diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index 347267a..66a316c 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -3,10 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use mist_parser::{ - ast::{Identifier, TopLevel, Visibility}, - error::ParseError, -}; +use mist_parser::{ast::TopLevel, error::ParseError}; pub fn build(root: &PathBuf) { 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 - let ast = parser_result - .map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))? - .1; + let ast = parser_result.map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))?; let mut gc = mist_codegen::RustCodegen::new(); let output = gc.generate(ast); @@ -149,12 +144,10 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { Ok(()) } -pub fn transpile_text<'a>( - source: &'a str, -) -> Result, Vec)>> { +pub fn transpile_text<'a>(source: &'a str) -> Result>> { 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 { diff --git a/crates/mist-api/src/modules.mist b/crates/mist-api/src/modules.mist index a6ad128..ea1c3c4 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -77,11 +77,17 @@ pub class Module { } } - pub fn output_path(*self, parent_dir *PathBuf) PathBuf { - if self.path.is_dir() || self.is_package() { - parent_dir.join(&self.name).join("mod.rs") + pub fn output_path(*self, parent_dir *PathBuf, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>) PathBuf { + let name = if let Some[[_, name]] = curr_mod_decl { + &name.0 } 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") } } } diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 37551fa..ba66a5f 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -84,7 +84,7 @@ pub fn transpile() PathBuf { } 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") { let file_name = self.path.file_name() .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"); - return String::new(); + return (true, String::new()); } - let dir = self.output_dir(&parent_dir); - let output_file = self.output_path(&parent_dir); + let dir = self.output_dir(parent_dir); let _ = fs::create_dir_all(&dir); let mut output = String::new(); + let mut child_changed = false; + for child in &self.children { 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) } - 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() { + let output_file = self.output_path(parent_dir, &None); + if output.len() == 0 { - return String::new(); + return (true, String::new()); } let res = fs::write(&output_file, output); @@ -135,26 +143,66 @@ impl modules::Module { process::exit(1); } - format!("pub mod {};\n", self.name) + (false, format!("pub mod {};\n", self.name)) } else { - transpile_file(&self.path, output_file, &output) + let source = match fs::read_to_string(&self.path) { + Ok[s] => s; + Err[e] => { + eprintln!("error: failed to read file {}\n {}", self.path.display(), e); + + 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 transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String { - let source = match fs::read_to_string(&path) { - Ok[s] => s; - Err[e] => { - eprintln!("error: failed to read file {}\n {}", path.display(), e); +fn is_source_newer(source *Path, output *Path) std::io::Result { + if !output.exists() { + return Ok(true); + } - process::exit(1); + 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 parsed = match mist_parser::parse(&source) { + let parsed = match mist_parser::parse(source) { Ok[ast] => ast; Err[e] => { match e { @@ -186,49 +234,18 @@ 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); + let res = fs::write(&output_file, format!("{mod_decl}{output}")); - new_name.push("."); - new_name.push(ext); + if res.is_err() { + eprintln!( + "error: failed to write output {}\n {}", + output_file.display(), + res.unwrap_err(), + ); - 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}")); - - if res.is_err() { - eprintln!( - "error: failed to write output {}\n {}", - output_file.display(), - res.unwrap_err(), - ); - - 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() + process::exit(1); } } diff --git a/crates/parser/src/grammar.pest b/crates/parser/src/grammar.pest index a486537..aa2a8fc 100644 --- a/crates/parser/src/grammar.pest +++ b/crates/parser/src/grammar.pest @@ -72,6 +72,10 @@ program = { SOI ~ top_level* ~ EOI } +module_program = { + SOI ~ top_level? +} + // ====================================================== // MODIFIERS // ====================================================== diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 77e6bde..ea39710 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -14,12 +14,7 @@ use crate::error::{IntoErr, ParseError}; #[grammar = "./src/grammar.pest"] pub struct MistParser; -pub fn parse<'a>( - source: &'a str, -) -> Result< - (Option<(Visibility, Identifier)>, Vec), - ParseError<'a, (Option<(Visibility, Identifier)>, Vec)>, -> { +pub fn parse<'a>(source: &'a str) -> Result, ParseError<'a, Vec>> { let mut pairs = MistParser::parse(Rule::program, source)?; 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) { - Ok(v) => Ok((m, v)), + Ok(v) => Ok(v), Err(e) => { - let rec = e.recovered.clone().map(move |r| (m, r)); + let rec = e.recovered.clone(); let mut e2 = e.get(); @@ -56,6 +41,30 @@ pub fn parse<'a>( } } +pub fn parse_module<'a>( + source: &'a str, +) -> Result, 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_rules! ast_ensure { ($pair:expr, $rule:expr $(, $rules:expr)* => $body:block) => {