Merge pull request #77 from mist-go/module-system-caching
Module system caching
This commit is contained in:
@@ -3,10 +3,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use mist_parser::{
|
use mist_parser::{ast::TopLevel, error::ParseError};
|
||||||
ast::{Identifier, TopLevel, Visibility},
|
|
||||||
error::ParseError,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn build(root: &PathBuf) {
|
pub fn build(root: &PathBuf) {
|
||||||
let src_dir = root.join("src");
|
let src_dir = root.join("src");
|
||||||
@@ -136,9 +133,7 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// If parsing fails, return the error string back gracefully so the LSP can show it
|
// If parsing fails, return the error string back gracefully so the LSP can show it
|
||||||
let ast = parser_result
|
let ast = parser_result.map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))?;
|
||||||
.map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))?
|
|
||||||
.1;
|
|
||||||
|
|
||||||
let mut gc = mist_codegen::RustCodegen::new();
|
let mut gc = mist_codegen::RustCodegen::new();
|
||||||
let output = gc.generate(ast);
|
let output = gc.generate(ast);
|
||||||
@@ -149,12 +144,10 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transpile_text<'a>(
|
pub fn transpile_text<'a>(source: &'a str) -> Result<String, ParseError<'a, Vec<TopLevel>>> {
|
||||||
source: &'a str,
|
|
||||||
) -> Result<String, ParseError<'a, (Option<(Visibility, Identifier)>, Vec<TopLevel>)>> {
|
|
||||||
let mut gc = mist_codegen::RustCodegen::new();
|
let mut gc = mist_codegen::RustCodegen::new();
|
||||||
|
|
||||||
Ok(gc.generate(mist_parser::parse(&source)?.1))
|
Ok(gc.generate(mist_parser::parse(&source)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_skip(source: &Path, output: &Path) -> bool {
|
fn should_skip(source: &Path, output: &Path) -> bool {
|
||||||
|
|||||||
@@ -77,11 +77,17 @@ pub class Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_path(*self, parent_dir *PathBuf) PathBuf {
|
pub fn output_path(*self, parent_dir *PathBuf, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>) PathBuf {
|
||||||
if self.path.is_dir() || self.is_package() {
|
let name = if let Some[[_, name]] = curr_mod_decl {
|
||||||
parent_dir.join(&self.name).join("mod.rs")
|
&name.0
|
||||||
} else {
|
} else {
|
||||||
parent_dir.join(&self.name).with_extension("rs")
|
&self.name
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.path.is_dir() || self.is_package() {
|
||||||
|
parent_dir.join(name).join("mod.rs")
|
||||||
|
} else {
|
||||||
|
parent_dir.join(name).with_extension("rs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ pub fn transpile() PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl modules::Module {
|
impl modules::Module {
|
||||||
fn transpile(*self, parent_dir *PathBuf, config *mut MistConfig) String {
|
fn transpile(*self, parent_dir *PathBuf, config *mut MistConfig) [bool, String] {
|
||||||
if !self.path.is_dir() && self.path.extension().and_then(fn(v) -> v.to_str()) != Some("mist") {
|
if !self.path.is_dir() && self.path.extension().and_then(fn(v) -> v.to_str()) != Some("mist") {
|
||||||
let file_name = self.path.file_name()
|
let file_name = self.path.file_name()
|
||||||
.and_then(fn(s) -> s.to_str())
|
.and_then(fn(s) -> s.to_str())
|
||||||
@@ -92,19 +92,25 @@ impl modules::Module {
|
|||||||
|
|
||||||
fs::copy(&self.path, parent_dir.join(&file_name)).expect("Failed to copy SideFile");
|
fs::copy(&self.path, parent_dir.join(&file_name)).expect("Failed to copy SideFile");
|
||||||
|
|
||||||
return String::new();
|
return (true, String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir = self.output_dir(&parent_dir);
|
let dir = self.output_dir(parent_dir);
|
||||||
let output_file = self.output_path(&parent_dir);
|
|
||||||
|
|
||||||
let _ = fs::create_dir_all(&dir);
|
let _ = fs::create_dir_all(&dir);
|
||||||
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let mut child_changed = false;
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
if !config.should_ignore(&child.path) {
|
if !config.should_ignore(&child.path) {
|
||||||
output.push_str(&child.transpile(&dir, config));
|
let out = child.transpile(&dir, config);
|
||||||
|
if out.0 {
|
||||||
|
child_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_str(&out.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,13 +120,15 @@ impl modules::Module {
|
|||||||
output.push_str(&config.main_mods)
|
output.push_str(&config.main_mods)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_transpile(output, output_file)
|
self.write_transpile(output, parent_dir, child_changed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_transpile(*self, output String, output_file PathBuf) String {
|
fn write_transpile(*self, output String, parent_dir *PathBuf, child_changed bool) [bool, String] {
|
||||||
if self.path.is_dir() {
|
if self.path.is_dir() {
|
||||||
|
let output_file = self.output_path(parent_dir, &None);
|
||||||
|
|
||||||
if output.len() == 0 {
|
if output.len() == 0 {
|
||||||
return String::new();
|
return (true, String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = fs::write(&output_file, output);
|
let res = fs::write(&output_file, output);
|
||||||
@@ -135,26 +143,66 @@ impl modules::Module {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("pub mod {};\n", self.name)
|
(false, format!("pub mod {};\n", self.name))
|
||||||
} else {
|
} else {
|
||||||
transpile_file(&self.path, output_file, &output)
|
let source = match fs::read_to_string(&self.path) {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String {
|
|
||||||
let source = match fs::read_to_string(&path) {
|
|
||||||
Ok[s] => s;
|
Ok[s] => s;
|
||||||
Err[e] => {
|
Err[e] => {
|
||||||
eprintln!("error: failed to read file {}\n {}", path.display(), e);
|
eprintln!("error: failed to read file {}\n {}", self.path.display(), e);
|
||||||
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let curr_mod_decl = mist_parser::parse_module(&source).expect("Failed parsing module");
|
||||||
|
|
||||||
|
let output_file = self.output_path(parent_dir, &curr_mod_decl);
|
||||||
|
|
||||||
|
let should_change = is_source_newer(&self.path, &output_file).expect("Failed to cache");
|
||||||
|
|
||||||
|
if should_change || child_changed {
|
||||||
|
transpile_file(&self.path, &source, output_file, &output, &curr_mod_decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
(should_change, if let Some[[vis, name]] = &curr_mod_decl {
|
||||||
|
format!("{}mod {};\n", vis.get_rust(), name.get_rust())
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_source_newer(source *Path, output *Path) std::io::Result<bool> {
|
||||||
|
if !output.exists() {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_time = fs::metadata(source)?.modified()?;
|
||||||
|
let output_time = fs::metadata(output)?.modified()?;
|
||||||
|
|
||||||
|
Ok(source_time > output_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transpile_file(path *PathBuf, source *str, mut output_file PathBuf, mod_decl *str, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>) {
|
||||||
|
if let Some[[_, name]] = &curr_mod_decl {
|
||||||
|
if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") {
|
||||||
|
if let Some[ext] = output_file.extension().map(fn(e) -> e.to_owned()) {
|
||||||
|
let mut new_name = std::ffi::OsString::from(&name.0);
|
||||||
|
|
||||||
|
new_name.push(".");
|
||||||
|
new_name.push(ext);
|
||||||
|
|
||||||
|
output_file.set_file_name(new_name);
|
||||||
|
} else {
|
||||||
|
output_file.set_file_name(&name.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut gc = mist_codegen::RustCodegen::new();
|
let mut gc = mist_codegen::RustCodegen::new();
|
||||||
|
|
||||||
let parsed = match mist_parser::parse(&source) {
|
let parsed = match mist_parser::parse(source) {
|
||||||
Ok[ast] => ast;
|
Ok[ast] => ast;
|
||||||
Err[e] => {
|
Err[e] => {
|
||||||
match e {
|
match e {
|
||||||
@@ -186,21 +234,7 @@ fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = gc.generate(parsed.1);
|
let output = gc.generate(parsed);
|
||||||
|
|
||||||
if let [Some[[vis, name]], _] = parsed {
|
|
||||||
if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") {
|
|
||||||
if let Some[ext] = output_file.extension().map(fn(e) -> e.to_owned()) {
|
|
||||||
let mut new_name = std::ffi::OsString::from(&name.0);
|
|
||||||
|
|
||||||
new_name.push(".");
|
|
||||||
new_name.push(ext);
|
|
||||||
|
|
||||||
output_file.set_file_name(new_name);
|
|
||||||
} else {
|
|
||||||
output_file.set_file_name(&name.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
||||||
|
|
||||||
@@ -213,22 +247,5 @@ fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String
|
|||||||
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("{}mod {};\n", vis.get_rust(), name.get_rust())
|
|
||||||
} else {
|
|
||||||
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
|
||||||
|
|
||||||
if res.is_err() {
|
|
||||||
eprintln!(
|
|
||||||
"error: failed to write output {}\n {}",
|
|
||||||
output_file.display(),
|
|
||||||
res.unwrap_err(),
|
|
||||||
);
|
|
||||||
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ program = {
|
|||||||
SOI ~ top_level* ~ EOI
|
SOI ~ top_level* ~ EOI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module_program = {
|
||||||
|
SOI ~ top_level?
|
||||||
|
}
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
// MODIFIERS
|
// MODIFIERS
|
||||||
// ======================================================
|
// ======================================================
|
||||||
|
|||||||
+27
-18
@@ -14,12 +14,7 @@ use crate::error::{IntoErr, ParseError};
|
|||||||
#[grammar = "./src/grammar.pest"]
|
#[grammar = "./src/grammar.pest"]
|
||||||
pub struct MistParser;
|
pub struct MistParser;
|
||||||
|
|
||||||
pub fn parse<'a>(
|
pub fn parse<'a>(source: &'a str) -> Result<Vec<TopLevel>, ParseError<'a, Vec<TopLevel>>> {
|
||||||
source: &'a str,
|
|
||||||
) -> Result<
|
|
||||||
(Option<(Visibility, Identifier)>, Vec<TopLevel>),
|
|
||||||
ParseError<'a, (Option<(Visibility, Identifier)>, Vec<TopLevel>)>,
|
|
||||||
> {
|
|
||||||
let mut pairs = MistParser::parse(Rule::program, source)?;
|
let mut pairs = MistParser::parse(Rule::program, source)?;
|
||||||
|
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
@@ -32,20 +27,10 @@ pub fn parse<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = if let Some(v) = statements.get(0) {
|
|
||||||
if let TopLevelKind::DeclareModule(vis, name) = &v.0.item {
|
|
||||||
Some((vis.clone(), name.clone()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
match analyzer.build(statements) {
|
match analyzer.build(statements) {
|
||||||
Ok(v) => Ok((m, v)),
|
Ok(v) => Ok(v),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let rec = e.recovered.clone().map(move |r| (m, r));
|
let rec = e.recovered.clone();
|
||||||
|
|
||||||
let mut e2 = e.get();
|
let mut e2 = e.get();
|
||||||
|
|
||||||
@@ -56,6 +41,30 @@ pub fn parse<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_module<'a>(
|
||||||
|
source: &'a str,
|
||||||
|
) -> Result<Option<(Visibility, Identifier)>, ParseError<'a, Option<(Visibility, Identifier)>>> {
|
||||||
|
let mut pairs = MistParser::parse(Rule::module_program, source)?;
|
||||||
|
|
||||||
|
if let Some(v) = pairs
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner()
|
||||||
|
.next()
|
||||||
|
.map(TopLevel::try_from)
|
||||||
|
.transpose()
|
||||||
|
.get()?
|
||||||
|
{
|
||||||
|
if let TopLevelKind::DeclareModule(vis, name) = &v.0.item {
|
||||||
|
Ok(Some((vis.clone(), name.clone())))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! ast_ensure {
|
macro_rules! ast_ensure {
|
||||||
($pair:expr, $rule:expr $(, $rules:expr)* => $body:block) => {
|
($pair:expr, $rule:expr $(, $rules:expr)* => $body:block) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user