From ec1864334cba1685a9fbddb16c52ca9fe3de75db Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 25 May 2026 16:06:04 +0200 Subject: [PATCH] Bootstrapped transpiler in mist --- src/_header.rs | 1 - src/transpiler.mist | 162 ++++++++++++++++++++++++++++++++++++++++++ src/transpiler.rs | 167 -------------------------------------------- 3 files changed, 162 insertions(+), 168 deletions(-) create mode 100644 src/transpiler.mist delete mode 100644 src/transpiler.rs diff --git a/src/_header.rs b/src/_header.rs index 749d126..6f096e5 100644 --- a/src/_header.rs +++ b/src/_header.rs @@ -1,4 +1,3 @@ pub mod builder; -pub mod transpiler; fn main() {} diff --git a/src/transpiler.mist b/src/transpiler.mist new file mode 100644 index 0000000..d7ffc2a --- /dev/null +++ b/src/transpiler.mist @@ -0,0 +1,162 @@ +use ; +use ; +use ; +use ; +use ; +use ; + +use ; + +pub PathBuf build(bool force) { + var start = time::Instant::now(); + + var root = env::current_dir().expect("Unable to find project root"); + + PathBuf src = root.join("src"); + PathBuf out = root.join(".mist/src"); + + build_dir(&root, &src, &src, &out, force); + + var elapsed = start.elapsed(); + + println!( + "\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", + elapsed + ); + + root +} + +void build_dir(Path* root, Path* base_src, Path* current, Path* out, bool force) { + var entries = match (fs::read_dir(current)) { + Ok[entries] => entries; + Err[e] => { + eprintln!( + "error: failed to read directory {}\n {}", + current.display(), + e + ); + + process::exit(1); + } + }; + + for (entry : entries) { + var entry = match (entry) { + Ok[entry] => entry; + Err[e] => { + eprintln!("error: failed to read directory entry\n {}", e); + + process::exit(1); + } + }; + + PathBuf path = entry.path(); + + if (path.is_dir()) { + build_dir(root, base_src, &path, out, force); + continue; + } + + Path* relative = path.strip_prefix(base_src).expect("Failed to strip base src from path"); + + if (path.extension().and_then(|var e| e.to_str()) != Some("mist")) { + PathBuf dest = out.join(relative); + + Option parent = dest.parent(); + + if (parent.is_some()) { + fs::create_dir_all(parent.unwrap()).expect("failed to create output directory"); + } + + if (!force && should_skip(&path, &dest)) { + continue + } + + fs::copy(&path, dest).expect("Failed to copy non-mist file"); + + continue; + } + + PathBuf output_file = out.join(relative).with_extension("rs"); + + if (!force && should_skip(&path, &output_file)) { + continue; + } + + Option parent = output_file.parent(); + + if (parent.is_some()) { + fs::create_dir_all(parent.unwrap()).expect("failed to create output directory"); + } + + String source = match (fs::read_to_string(&path)) { + Ok[s] => s; + Err[e] => { + eprintln!("error: failed to read file {}\n {}", path.display(), e); + + process::exit(1); + } + }; + + var mut gc = mist_codegen::RustCodegen::new(); + + String output = gc.generate(match (mist_parser::parse(&source)) { + Ok[ast] => ast; + Err[e] => { + match (e) { + error::ParseError::Ast[e] => { + var start_pos = e.span.start_pos().line_col(); + + var span = e.span.as_str(); + + eprintln!( + "\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}", + path.as_os_str().display(), + start_pos.0, + start_pos.1, + e.error_message, + span, + if span.ends_with("\n") { "" } else { "\n" }, + "^".repeat(span.trim().len()), + ); + + process::exit(1); + } + + error::ParseError::PreAst[e] => { + eprintln!("error: parse failed in {}\n{}", path.display(), e); + + process::exit(1); + } + } + } + }); + + var res = fs::write(&output_file, output); + + if (res.is_err()) { + eprintln!( + "error: failed to write output {}\n {}", + output_file.display(), + res.unwrap_err(), + ); + + process::exit(1); + } + } +} + +bool should_skip(Path* source, Path* output) { + var src_time = fs::metadata(source) + .ok() + .and_then(|var v| v.modified().ok()); + + var out_time = fs::metadata(output) + .ok() + .and_then(|var v| v.modified().ok()); + + out_time + .map(|var out_time| out_time >= src_time.unwrap()) + .unwrap_or_default() +} diff --git a/src/transpiler.rs b/src/transpiler.rs deleted file mode 100644 index 051e305..0000000 --- a/src/transpiler.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::{ - fs, - path::{Path, PathBuf}, - process, - time::Instant, -}; - -use mist_parser::{ast::TopLevel, error::ParseError}; - -pub fn build(force: bool) -> PathBuf { - let start = Instant::now(); - - let root = std::env::current_dir() - .ok() - .expect("Unable to find project root"); - - let src_dir = root.join("src"); - let out_dir = root.join(".mist/src"); - - build_dir(&root, &src_dir, &src_dir, &out_dir, force); - - let elapsed = start.elapsed(); - - println!( - "\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", - elapsed - ); - - root -} - -fn build_dir(root: &Path, base_src: &Path, current_dir: &Path, out_dir: &Path, force: bool) { - let entries = match fs::read_dir(current_dir) { - Ok(entries) => entries, - Err(e) => { - eprintln!( - "error: failed to read directory {}\n {}", - current_dir.display(), - e - ); - - process::exit(1); - } - }; - - for entry in entries { - let entry = match entry { - Ok(entry) => entry, - Err(e) => { - eprintln!("error: failed to read directory entry\n {}", e); - - process::exit(1); - } - }; - - let path = entry.path(); - - // recurse into nested directories - if path.is_dir() { - build_dir(root, base_src, &path, out_dir, force); - continue; - } - - let relative = path.strip_prefix(base_src).unwrap(); - - // Handle non-mist files with a cache check - if path.extension().and_then(|e| e.to_str()) != Some("mist") { - let dest_path = out_dir.join(relative); - - // Create parent directories for static assets if needed - if let Some(parent) = dest_path.parent() { - let _ = fs::create_dir_all(parent); - } - - if !force && should_skip(&path, &dest_path) { - continue; - } - - fs::copy(&path, dest_path).expect("Failed to copy non-mist file"); - continue; - } - - let output_path = out_dir.join(relative).with_extension("rs"); - - // Cache layer: Skip if the generated .rs file is newer than the .mist source - if !force && should_skip(&path, &output_path) { - continue; - } - - // create parent directories - if let Some(parent) = output_path.parent() { - if let Err(e) = fs::create_dir_all(parent) { - eprintln!( - "error: failed to create output directory {}\n {}", - parent.display(), - e - ); - - process::exit(1); - } - } - - // read source - let source = match fs::read_to_string(&path) { - Ok(s) => s, - Err(e) => { - eprintln!("error: failed to read file {}\n {}", path.display(), e); - - process::exit(1); - } - }; - - let output = match transpile_file(&source) { - Ok(ast) => ast, - Err(e) => { - match e { - ParseError::Ast(e) => { - let start_pos = e.span.start_pos().line_col(); - - let span = e.span.as_str(); - - eprintln!( - "\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}", - path.as_os_str().display(), - start_pos.0, - start_pos.1, - e.error_message, - span, - if span.ends_with("\n") { "" } else { "\n" }, - "^".repeat(span.trim().len()), - ) - } - ParseError::PreAst(e) => { - eprintln!("error: parse failed in {}\n{}", path.display(), e) - } - } - - process::exit(1); - } - }; - - if let Err(e) = fs::write(&output_path, output) { - eprintln!( - "error: failed to write output {}\n {}", - output_path.display(), - e - ); - - process::exit(1); - } - } -} - -pub fn transpile_file<'a>(source: &'a str) -> Result>> { - let mut gc = mist_codegen::RustCodegen::new(); - - Ok(gc.generate(mist_parser::parse(&source)?)) -} - -fn should_skip(source: &Path, output: &Path) -> bool { - if let (Ok(src_meta), Ok(out_meta)) = (fs::metadata(source), fs::metadata(output)) { - if let (Ok(src_time), Ok(out_time)) = (src_meta.modified(), out_meta.modified()) { - return out_time >= src_time; - } - } - false -}