From 1619250ba57b6ee02e4479f84af388f254117753 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 10 Jun 2026 05:37:08 +0200 Subject: [PATCH 01/13] Updated parser --- crates/codegen/src/top_level.rs | 2 +- crates/parser/src/ast/top_level.rs | 2 +- crates/parser/src/error.rs | 1 + crates/parser/src/grammar.pest | 6 +++--- crates/parser/src/lib.rs | 28 ++++++++++++++++++++++++--- crates/parser/src/parser/items/mod.rs | 2 +- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/crates/codegen/src/top_level.rs b/crates/codegen/src/top_level.rs index 1b2ff11..1af2afa 100644 --- a/crates/codegen/src/top_level.rs +++ b/crates/codegen/src/top_level.rs @@ -177,7 +177,7 @@ impl GenRust for TopLevelKind { Self::Import(vis, path) => { cg.addln(&format!("{}use {};", vis.get_rust(), path.get_rust())) } - Self::Mod(vis, id) => cg.addln(&format!("{}mod {};", vis.get_rust(), id.get_rust())), + Self::DeclareModule(_, _) => {} Self::FunctionDecl(decl) => decl.gen_rust(ctx, cg), Self::ImplDecl(impl_) => impl_.gen_rust(ctx, cg), Self::StructDecl { diff --git a/crates/parser/src/ast/top_level.rs b/crates/parser/src/ast/top_level.rs index 521fb97..be4a4b3 100644 --- a/crates/parser/src/ast/top_level.rs +++ b/crates/parser/src/ast/top_level.rs @@ -28,7 +28,7 @@ pub struct TopLevel(pub Spanned, pub Vec); pub enum TopLevelKind { ModAttribute, Import(Visibility, Path), - Mod(Visibility, Identifier), + DeclareModule(Visibility, Identifier), ImplDecl(ImplDecl), TraitDecl { visibility: Visibility, diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index 1ad1b05..592fa7e 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -24,6 +24,7 @@ pub struct AstError<'a, T> { pub enum ErrorCode { InvalidStatement, AstGenBug, + Module, } impl From> for ParseError<'_, T> { diff --git a/crates/parser/src/grammar.pest b/crates/parser/src/grammar.pest index 127d298..7877237 100644 --- a/crates/parser/src/grammar.pest +++ b/crates/parser/src/grammar.pest @@ -220,8 +220,8 @@ import = { visibility? ~ "use" ~ static_path ~ semicolon } -mod_package = { - visibility? ~ "mod" ~ identifier ~ semicolon +declare_module = { + visibility? ~ "module" ~ identifier ~ semicolon } // ====================================================== @@ -334,7 +334,7 @@ function_decl = { // ====================================================== top_level = { - (attributes ~ (import | impl_for_decl | impl_decl | trait_decl | struct_decl | enum_decl | class_decl | mod_package | function_decl)) + (attributes ~ (import | impl_for_decl | impl_decl | trait_decl | struct_decl | enum_decl | class_decl | declare_module | function_decl)) | mod_attribute } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 9e89836..80e6d3e 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,4 +1,4 @@ -use pest::Parser; +use pest::{Parser, Span}; use pest_derive::Parser; pub mod ast; @@ -14,7 +14,9 @@ use crate::error::{IntoErr, ParseError}; #[grammar = "./src/grammar.pest"] pub struct MistParser; -pub fn parse<'a>(source: &'a str) -> Result, ParseError<'a, Vec>> { +pub fn parse<'a>( + source: &'a str, +) -> Result<((Visibility, Identifier), Vec), ParseError<'a, Vec>> { let mut pairs = MistParser::parse(Rule::program, source)?; let mut statements = vec![]; @@ -27,8 +29,28 @@ pub fn parse<'a>(source: &'a str) -> Result, ParseError<'a, Vec;`".to_string(), + recovered: None, + })); + } + } else { + return Err(ParseError::Ast(error::AstError { + span: Span::new("", 0, 0).unwrap(), + error_code: error::ErrorCode::Module, + error_message: "Module must be declared, use `module <>;`".to_string(), + recovered: None, + })); + }; + match analyzer.build(statements) { - Ok(v) => Ok(v), + Ok(v) => Ok((m, v)), Err(e) => Err(ParseError::Ast(e)), } } diff --git a/crates/parser/src/parser/items/mod.rs b/crates/parser/src/parser/items/mod.rs index ebaf0a4..58c3bd2 100644 --- a/crates/parser/src/parser/items/mod.rs +++ b/crates/parser/src/parser/items/mod.rs @@ -93,7 +93,7 @@ impl<'a> TryFrom> for TopLevelKind { fields: collect_recovered(inner), }), - Rule::mod_package => ast_expr!(TopLevelKind::Mod( + Rule::declare_module => ast_expr!(TopLevelKind::DeclareModule( Visibility::try_from(&mut inner), inner.next().unwrap().try_into(), )), From 14e88e2e4d5ff0df167ef4d5cbea0b6679afb601 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 10 Jun 2026 05:44:58 +0200 Subject: [PATCH 02/13] Fixed API --- crates/analyzer/src/transpiler.rs | 6 ++++-- crates/mist-api/src/modules.mist | 4 ++-- crates/mist-api/src/transpiler.mist | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index 66a316c..a0e90f3 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -133,7 +133,9 @@ 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))?; + let ast = parser_result + .map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))? + .1; let mut gc = mist_codegen::RustCodegen::new(); let output = gc.generate(ast); @@ -147,7 +149,7 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { pub fn transpile_text<'a>(source: &'a str) -> Result>> { let mut gc = mist_codegen::RustCodegen::new(); - Ok(gc.generate(mist_parser::parse(&source)?)) + Ok(gc.generate(mist_parser::parse(&source)?.1)) } 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 d79d3b6..2232dd3 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -7,7 +7,7 @@ use std::process; pub class Module { pub name String; pub path PathBuf; - pub children Vec; + pub children Vec>; pub constructor(mist_path PathBuf) { self.children = Vec::new(); @@ -74,7 +74,7 @@ pub class Module { } } -pub fn master_package(mist_path PathBuf) Module { +pub fn master_package(mist_path PathBuf) Box { let mut root_mod = Module::new(mist_path.clone()); let parent_dir = mist_path.parent() diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 4291e89..21c92a4 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -88,7 +88,7 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { let mut gc = mist_codegen::RustCodegen::new(); - let output = gc.generate(match (mist_parser::parse(&source)) { + let [[vis, api], ast] = match (mist_parser::parse(&source)) { Ok[ast] => ast; Err[e] => { match (e) { @@ -118,7 +118,9 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { } } } - }); + }; + + let output = gc.generate(ast); let res = fs::write(&output_file, format!("{mod_decl}{output}")); From e7f10720509953d5a682aba88ba61087066ec480 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 10 Jun 2026 05:48:51 +0200 Subject: [PATCH 03/13] Implemented tests --- mist-test/src/common/attribute_test.mist | 2 ++ mist-test/src/common/decl_test.mist | 2 ++ mist-test/src/common/expr_test.mist | 2 ++ mist-test/src/common/statement_test.mist | 2 ++ mist-test/src/common/types_test.mist | 2 ++ mist-test/src/items/attribute_test.mist | 2 ++ mist-test/src/items/class_test.mist | 2 ++ mist-test/src/items/enum_test.mist | 2 ++ mist-test/src/items/function_test.mist | 2 ++ mist-test/src/items/impl_test.mist | 2 ++ mist-test/src/items/struct_test.mist | 2 ++ mist-test/src/items/trait_test.mist | 2 ++ 12 files changed, 24 insertions(+) diff --git a/mist-test/src/common/attribute_test.mist b/mist-test/src/common/attribute_test.mist index 313a20d..09f76f2 100644 --- a/mist-test/src/common/attribute_test.mist +++ b/mist-test/src/common/attribute_test.mist @@ -1,3 +1,5 @@ +pub module attribute_test; + #[derive(Debug)] pub struct Debuggable { value i32, diff --git a/mist-test/src/common/decl_test.mist b/mist-test/src/common/decl_test.mist index 60eda7f..e9a2e09 100644 --- a/mist-test/src/common/decl_test.mist +++ b/mist-test/src/common/decl_test.mist @@ -1,3 +1,5 @@ +pub module decl_test; + pub fn test_let_basic() { let x = 42; assert_eq!(x, 42); diff --git a/mist-test/src/common/expr_test.mist b/mist-test/src/common/expr_test.mist index a7decf1..c7a7cee 100644 --- a/mist-test/src/common/expr_test.mist +++ b/mist-test/src/common/expr_test.mist @@ -1,3 +1,5 @@ +pub module expr_test; + pub fn test_literals() { assert_eq!(42, 42); assert_eq!(3.14, 3.14); diff --git a/mist-test/src/common/statement_test.mist b/mist-test/src/common/statement_test.mist index b26ca96..8829946 100644 --- a/mist-test/src/common/statement_test.mist +++ b/mist-test/src/common/statement_test.mist @@ -1,3 +1,5 @@ +pub module statement_test; + pub fn test_var_decl() { let x = 42; assert_eq!(x, 42); diff --git a/mist-test/src/common/types_test.mist b/mist-test/src/common/types_test.mist index 62779b0..8edf714 100644 --- a/mist-test/src/common/types_test.mist +++ b/mist-test/src/common/types_test.mist @@ -1,3 +1,5 @@ +pub module types_test; + pub fn test_integer_type() { let x i32 = 42; assert_eq!(x, 42); diff --git a/mist-test/src/items/attribute_test.mist b/mist-test/src/items/attribute_test.mist index 313a20d..09f76f2 100644 --- a/mist-test/src/items/attribute_test.mist +++ b/mist-test/src/items/attribute_test.mist @@ -1,3 +1,5 @@ +pub module attribute_test; + #[derive(Debug)] pub struct Debuggable { value i32, diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index db6b7ec..d50ff9c 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -1,3 +1,5 @@ +pub module class_test; + pub class Animal { pub name String; pub val T; diff --git a/mist-test/src/items/enum_test.mist b/mist-test/src/items/enum_test.mist index 8bcdd0b..a005f46 100644 --- a/mist-test/src/items/enum_test.mist +++ b/mist-test/src/items/enum_test.mist @@ -1,3 +1,5 @@ +pub module enum_test; + enum OptionInt { None, Some[i32], diff --git a/mist-test/src/items/function_test.mist b/mist-test/src/items/function_test.mist index 7b051f0..8cbcb21 100644 --- a/mist-test/src/items/function_test.mist +++ b/mist-test/src/items/function_test.mist @@ -1,3 +1,5 @@ +pub module function_test; + fn greet() { let mut x = 1; x++; diff --git a/mist-test/src/items/impl_test.mist b/mist-test/src/items/impl_test.mist index 2c9240c..2c9b50a 100644 --- a/mist-test/src/items/impl_test.mist +++ b/mist-test/src/items/impl_test.mist @@ -1,3 +1,5 @@ +pub module impl_test; + struct Vector2 { x i32, y i32, diff --git a/mist-test/src/items/struct_test.mist b/mist-test/src/items/struct_test.mist index 7475688..bd83f6b 100644 --- a/mist-test/src/items/struct_test.mist +++ b/mist-test/src/items/struct_test.mist @@ -1,3 +1,5 @@ +pub module struct_test; + struct Point { x i32, y i32, diff --git a/mist-test/src/items/trait_test.mist b/mist-test/src/items/trait_test.mist index 55ce2c3..41fe093 100644 --- a/mist-test/src/items/trait_test.mist +++ b/mist-test/src/items/trait_test.mist @@ -1,3 +1,5 @@ +pub module trait_test; + trait Speak { fn speak(*self) String; } From cb3f1bcde14be63a32fbe0ba2521377d79842fe4 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 10 Jun 2026 05:54:19 +0200 Subject: [PATCH 04/13] Fixed where module needs to be placed --- crates/analyzer/src/transpiler.rs | 4 ++-- crates/mist-api/src/transpiler.mist | 4 +++- crates/parser/src/lib.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index a0e90f3..3c4a431 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -112,7 +112,7 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { let source = fs::read_to_string(path) .map_err(|e| format!("failed to read file {}: {}", path.display(), e))?; - let parser_result = mist_parser::parse(&source).map_err(|e| match e { + let parser_result = mist_parser::parse(&source, true).map_err(|e| match e { ParseError::Ast(e) => { // Using components carefully to prevent out-of-bounds or zero layout crashes let start_pos = e.span.start_pos().line_col(); @@ -149,7 +149,7 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { 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, true)?.1)) } fn should_skip(source: &Path, output: &Path) -> bool { diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 21c92a4..cd8bd03 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -88,7 +88,9 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { let mut gc = mist_codegen::RustCodegen::new(); - let [[vis, api], ast] = match (mist_parser::parse(&source)) { + let is_master = path.file_name().unwrap().to_str().unwrap() == "main.mist"; + + let [[vis, api], ast] = match (mist_parser::parse(&source, is_master)) { Ok[ast] => ast; Err[e] => { match (e) { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 80e6d3e..6ec7611 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -16,6 +16,7 @@ pub struct MistParser; pub fn parse<'a>( source: &'a str, + is_master: bool, ) -> Result<((Visibility, Identifier), Vec), ParseError<'a, Vec>> { let mut pairs = MistParser::parse(Rule::program, source)?; @@ -29,6 +30,13 @@ pub fn parse<'a>( } } + if is_master { + return match analyzer.build(statements) { + Ok(v) => Ok(((Visibility::Private, Identifier(String::new())), v)), + Err(e) => Err(ParseError::Ast(e)), + }; + } + let m = if let Some(v) = statements.get(0) { if let TopLevelKind::DeclareModule(vis, name) = &v.0.item { (vis.clone(), name.clone()) From 2256ef2a9f5508efc4680ce24ce5f7a71a5e6fdc Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 10:21:04 +0200 Subject: [PATCH 05/13] Parser --- crates/mist-api/src/transpiler.mist | 2 +- crates/parser/src/lib.rs | 41 ++++++++++++----------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index cd8bd03..90d57c8 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -90,7 +90,7 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { let is_master = path.file_name().unwrap().to_str().unwrap() == "main.mist"; - let [[vis, api], ast] = match (mist_parser::parse(&source, is_master)) { + let [[vis, name], ast] = match (mist_parser::parse(&source, is_master)) { Ok[ast] => ast; Err[e] => { match (e) { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 6ec7611..77e6bde 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,4 +1,4 @@ -use pest::{Parser, Span}; +use pest::Parser; use pest_derive::Parser; pub mod ast; @@ -16,8 +16,10 @@ pub struct MistParser; pub fn parse<'a>( source: &'a str, - is_master: bool, -) -> Result<((Visibility, Identifier), Vec), ParseError<'a, Vec>> { +) -> Result< + (Option<(Visibility, Identifier)>, Vec), + ParseError<'a, (Option<(Visibility, Identifier)>, Vec)>, +> { let mut pairs = MistParser::parse(Rule::program, source)?; let mut statements = vec![]; @@ -30,36 +32,27 @@ pub fn parse<'a>( } } - if is_master { - return match analyzer.build(statements) { - Ok(v) => Ok(((Visibility::Private, Identifier(String::new())), v)), - Err(e) => Err(ParseError::Ast(e)), - }; - } - let m = if let Some(v) = statements.get(0) { if let TopLevelKind::DeclareModule(vis, name) = &v.0.item { - (vis.clone(), name.clone()) + Some((vis.clone(), name.clone())) } else { - return Err(ParseError::Ast(error::AstError { - span: Span::new("", 0, 0).unwrap(), - error_code: error::ErrorCode::Module, - error_message: "Module must be declared, use `module <>;`".to_string(), - recovered: None, - })); + None } } else { - return Err(ParseError::Ast(error::AstError { - span: Span::new("", 0, 0).unwrap(), - error_code: error::ErrorCode::Module, - error_message: "Module must be declared, use `module <>;`".to_string(), - recovered: None, - })); + None }; match analyzer.build(statements) { Ok(v) => Ok((m, v)), - Err(e) => Err(ParseError::Ast(e)), + Err(e) => { + let rec = e.recovered.clone().map(move |r| (m, r)); + + let mut e2 = e.get(); + + e2.recovered = rec; + + Err(ParseError::Ast(e2)) + } } } From c37392046ace645026536178fc761dbc0bab6ae6 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 16:05:45 +0200 Subject: [PATCH 06/13] Working explicit module system --- crates/analyzer/src/transpiler.rs | 13 ++++-- crates/mist-api/src/transpiler.mist | 66 +++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index 3c4a431..347267a 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -3,7 +3,10 @@ use std::{ path::{Path, PathBuf}, }; -use mist_parser::{ast::TopLevel, error::ParseError}; +use mist_parser::{ + ast::{Identifier, TopLevel, Visibility}, + error::ParseError, +}; pub fn build(root: &PathBuf) { let src_dir = root.join("src"); @@ -112,7 +115,7 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { let source = fs::read_to_string(path) .map_err(|e| format!("failed to read file {}: {}", path.display(), e))?; - let parser_result = mist_parser::parse(&source, true).map_err(|e| match e { + let parser_result = mist_parser::parse(&source).map_err(|e| match e { ParseError::Ast(e) => { // Using components carefully to prevent out-of-bounds or zero layout crashes let start_pos = e.span.start_pos().line_col(); @@ -146,10 +149,12 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { Ok(()) } -pub fn transpile_text<'a>(source: &'a str) -> Result>> { +pub fn transpile_text<'a>( + source: &'a str, +) -> Result, Vec)>> { let mut gc = mist_codegen::RustCodegen::new(); - Ok(gc.generate(mist_parser::parse(&source, true)?.1)) + Ok(gc.generate(mist_parser::parse(&source)?.1)) } fn should_skip(source: &Path, output: &Path) -> bool { diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 90d57c8..ffa8355 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -6,6 +6,7 @@ use std::env; use mist_parser::error; use crate::modules; +use mist_codegen::GetRust; pub fn transpile() PathBuf { let start = time::Instant::now(); @@ -68,16 +69,16 @@ impl modules::Module { process::exit(1); } - } else { - transpile_file(&self.path, &output_file, &output); - } - format!("pub mod {};\n", self.name) + format!("pub mod {};\n", self.name) + } else { + transpile_file(&self.path, output_file, &output) + } } } -fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { - let source = match (fs::read_to_string(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; Err[e] => { eprintln!("error: failed to read file {}\n {}", path.display(), e); @@ -88,9 +89,7 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { let mut gc = mist_codegen::RustCodegen::new(); - let is_master = path.file_name().unwrap().to_str().unwrap() == "main.mist"; - - let [[vis, name], ast] = match (mist_parser::parse(&source, is_master)) { + let parsed = match (mist_parser::parse(&source)) { Ok[ast] => ast; Err[e] => { match (e) { @@ -122,18 +121,49 @@ fn transpile_file(path *PathBuf, output_file *PathBuf, mod_decl *str) { } }; - let output = gc.generate(ast); + if (let [Some[[vis, name]], ast] = parsed) { + let output = gc.generate(ast); - let res = fs::write(&output_file, format!("{mod_decl}{output}")); + if (let Some[ext] = output_file.extension().map(fn(e) -> e.to_owned())) { + let mut new_name = std::ffi::OsString::from(&name.0); - if (res.is_err()) { - eprintln!( - "error: failed to write output {}\n {}", - output_file.display(), - res.unwrap_err(), - ); + new_name.push("."); + new_name.push(ext); - process::exit(1); + 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 {};", vis.get_rust(), name.get_rust()) + } else { + let output = gc.generate(parsed.1); + + 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() } } From 74d53fff97a97e40d3a7ac71b6b42e28146d0adb Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 19:06:07 +0200 Subject: [PATCH 07/13] Mist.toml support --- Cargo.lock | 81 +++++++++++++++++++++++- crates/mist-api/Cargo.toml | 1 + crates/mist-api/src/transpiler.mist | 44 +++++++++++-- mist-test/Mist.toml | 2 +- mist-test/src/common/attribute_test.mist | 23 ------- 5 files changed, 120 insertions(+), 31 deletions(-) delete mode 100644 mist-test/src/common/attribute_test.mist diff --git a/Cargo.lock b/Cargo.lock index a75106e..b1403fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,7 +122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -136,7 +136,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -163,6 +163,12 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -274,6 +280,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "httparse" version = "1.10.1" @@ -383,6 +395,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + [[package]] name = "itoa" version = "1.0.18" @@ -464,6 +486,7 @@ dependencies = [ "mist-parser", "serde", "serde_json", + "toml", ] [[package]] @@ -711,6 +734,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "sha2" version = "0.10.9" @@ -859,6 +891,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tower" version = "0.4.13" @@ -1014,6 +1085,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + [[package]] name = "writeable" version = "0.6.3" diff --git a/crates/mist-api/Cargo.toml b/crates/mist-api/Cargo.toml index f2fcc01..06435b0 100644 --- a/crates/mist-api/Cargo.toml +++ b/crates/mist-api/Cargo.toml @@ -20,6 +20,7 @@ serde_json = "1.0.149" cargo_metadata = "0.23.1" mist-parser = { workspace = true } mist-codegen = { workspace = true } +toml = "1.1.2" [lib] name = "mist_api" diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index ffa8355..f983b42 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -1,4 +1,5 @@ use std::fs; +use std::path::Path; use std::path::PathBuf; use std::process; use std::time; @@ -8,20 +9,53 @@ use mist_parser::error; use crate::modules; use mist_codegen::GetRust; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct MistConfig { + packages Vec, +} +impl MistConfig { + pub fn is_package(*self, path *Path) bool { + for (pkg : &self.packages) { + if (path.ends_with(pkg)) { + return true; + } + } + + false + } +} + pub fn transpile() PathBuf { let start = time::Instant::now(); let root = env::current_dir().expect("Unable to find project root"); + let toml_path = root.join("Mist.toml"); let src = root.join("src"); let out = root.join(".mist/src"); - let master = modules::master_package(src.join("main.mist")); + let toml_content = fs::read_to_string(&toml_path) + .unwrap_or_else(fn(_) -> panic!("Could not read file at {:?}", toml_path)); - master.transpile(&out); + let mut config MistConfig = toml::from_str(&toml_content) + .unwrap_or_else(fn(_) -> panic!("Failed to parse Mist.toml from {:?}", toml_path)); + + for (pkg : &mut config.packages) { + *pkg = src.join(&*pkg); + } + + let master = modules::master_package( + config.packages.get(0) + .unwrap_or_else(fn() -> panic!("Mist.toml packages must contain at least one element, eg, 'src/main.mist'")) + .to_path_buf() + ); + + master.transpile(&out, &config); let elapsed = start.elapsed(); - + println!( "\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", elapsed @@ -31,7 +65,7 @@ pub fn transpile() PathBuf { } impl modules::Module { - fn transpile(*self, parent_dir *PathBuf) String { + fn transpile(*self, parent_dir *PathBuf, config *MistConfig) 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()) @@ -50,7 +84,7 @@ impl modules::Module { let mut output = String::new(); for (child : &self.children) { - output.push_str(&child.transpile(&dir)); + output.push_str(&child.transpile(&dir, config)); } if (self.path.is_dir()) { diff --git a/mist-test/Mist.toml b/mist-test/Mist.toml index 7a019d9..5c7ad70 100644 --- a/mist-test/Mist.toml +++ b/mist-test/Mist.toml @@ -1 +1 @@ -package = "main.mist" +packages = ["main.mist"] \ No newline at end of file diff --git a/mist-test/src/common/attribute_test.mist b/mist-test/src/common/attribute_test.mist deleted file mode 100644 index 09f76f2..0000000 --- a/mist-test/src/common/attribute_test.mist +++ /dev/null @@ -1,23 +0,0 @@ -pub module attribute_test; - -#[derive(Debug)] -pub struct Debuggable { - value i32, -} - -pub fn test_derive_attribute() { - let d = Debuggable { value: 42 }; - assert_eq!(d.value, 42); -} - -#[allow(unused_variables)] -pub fn test_allow_attribute() { - let x = 42; - let y = "unused"; - assert_eq!(x, 42); -} - -pub fn run_tests() { - test_derive_attribute(); - test_allow_attribute(); -} From a5a3d6566b068e10d7527da6c8648fc8cd822daf Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 19:34:37 +0200 Subject: [PATCH 08/13] Working package ignore --- crates/mist-api/src/modules.mist | 27 +++++++++++---------------- crates/mist-api/src/transpiler.mist | 11 +++++++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/crates/mist-api/src/modules.mist b/crates/mist-api/src/modules.mist index 2232dd3..e107248 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -3,13 +3,15 @@ use std::path::PathBuf; use std::fs; use std::process; +use crate::transpiler::MistConfig; + #[derive(Debug)] pub class Module { pub name String; pub path PathBuf; pub children Vec>; - pub constructor(mist_path PathBuf) { + pub constructor(mist_path PathBuf, config *MistConfig) { self.children = Vec::new(); self.name = mist_path.file_stem() .and_then(fn(s) -> s.to_str()) @@ -25,11 +27,15 @@ pub class Module { self.path = pkg_gate; } - self.scan_directory(&dir); + self.scan_directory(&dir, config); + } else if (config.is_package(&self.path)) { + let dir = self.path.parent().expect("Failed to get parent directory to scan").to_path_buf(); + + self.scan_directory(&dir, config); } } - fn scan_directory(*mut self, search_dir *Path) { + fn scan_directory(*mut self, search_dir *Path, config *MistConfig) { let entries = match (fs::read_dir(search_dir)) { Ok[entries] => entries; Err[e] => { @@ -49,11 +55,11 @@ pub class Module { let child_path = entry.path(); - if (child_path == self.path) { + if (child_path == self.path || child_path == config.packages[0]) { continue; } - self.children.push(Module::new(child_path)); + self.children.push(Module::new(child_path, config)); } } @@ -73,14 +79,3 @@ pub class Module { } } } - -pub fn master_package(mist_path PathBuf) Box { - let mut root_mod = Module::new(mist_path.clone()); - - let parent_dir = mist_path.parent() - .expect("Failed to get parent directory of root file"); - - root_mod.scan_directory(parent_dir); - - root_mod -} diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index f983b42..9c99d9b 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -13,7 +13,7 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct MistConfig { - packages Vec, + pub packages Vec, } impl MistConfig { pub fn is_package(*self, path *Path) bool { @@ -46,10 +46,11 @@ pub fn transpile() PathBuf { *pkg = src.join(&*pkg); } - let master = modules::master_package( + let master = modules::Module::new( config.packages.get(0) .unwrap_or_else(fn() -> panic!("Mist.toml packages must contain at least one element, eg, 'src/main.mist'")) - .to_path_buf() + .to_path_buf(), + &config ); master.transpile(&out, &config); @@ -84,7 +85,9 @@ impl modules::Module { let mut output = String::new(); for (child : &self.children) { - output.push_str(&child.transpile(&dir, config)); + if (!config.is_package(&child.path)) { + output.push_str(&child.transpile(&dir, config)); + } } if (self.path.is_dir()) { From 9ad253e91652fcb38d916f661e6f4a5e5a1ca415 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 20:17:29 +0200 Subject: [PATCH 09/13] Fixing --- crates/mist-api/src/modules.mist | 4 +-- crates/mist-api/src/transpiler.mist | 42 +++++++++++++++++++++++------ mist-test/Mist.toml | 3 ++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/crates/mist-api/src/modules.mist b/crates/mist-api/src/modules.mist index e107248..ca7c9f0 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -28,7 +28,7 @@ pub class Module { } self.scan_directory(&dir, config); - } else if (config.is_package(&self.path)) { + } else if (config.should_ignore(&self.path)) { let dir = self.path.parent().expect("Failed to get parent directory to scan").to_path_buf(); self.scan_directory(&dir, config); @@ -55,7 +55,7 @@ pub class Module { let child_path = entry.path(); - if (child_path == self.path || child_path == config.packages[0]) { + if (child_path == self.path || child_path == config.package) { continue; } diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 9c99d9b..7209e1a 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -12,11 +12,18 @@ use mist_codegen::GetRust; use serde::Deserialize; #[derive(Debug, Deserialize)] +#[serde(default)] pub struct MistConfig { + pub package PathBuf, pub packages Vec, + pub main_mods String } impl MistConfig { - pub fn is_package(*self, path *Path) bool { + pub fn should_ignore(*self, path *Path) bool { + if (path == self.package) { + return true; + } + for (pkg : &self.packages) { if (path.ends_with(pkg)) { return true; @@ -26,6 +33,15 @@ impl MistConfig { false } } +impl Default for MistConfig { + fn default() Self { + Self { + package: PathBuf::from("main.mist"), + packages: Vec::new(), + main_mods: String::new(), + } + } +} pub fn transpile() PathBuf { let start = time::Instant::now(); @@ -46,14 +62,14 @@ pub fn transpile() PathBuf { *pkg = src.join(&*pkg); } + config.package = src.join(&*config.package); + let master = modules::Module::new( - config.packages.get(0) - .unwrap_or_else(fn() -> panic!("Mist.toml packages must contain at least one element, eg, 'src/main.mist'")) - .to_path_buf(), + config.package.clone(), &config ); - master.transpile(&out, &config); + master.transpile(&out, &mut config); let elapsed = start.elapsed(); @@ -66,7 +82,7 @@ pub fn transpile() PathBuf { } impl modules::Module { - fn transpile(*self, parent_dir *PathBuf, config *MistConfig) String { + fn transpile(*self, parent_dir *PathBuf, config *mut MistConfig) 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()) @@ -85,11 +101,21 @@ impl modules::Module { let mut output = String::new(); for (child : &self.children) { - if (!config.is_package(&child.path)) { + if (!config.should_ignore(&child.path)) { output.push_str(&child.transpile(&dir, config)); } } + if (self.path == config.package) { + config.main_mods = output.clone(); + } else if (config.should_ignore(&self.path)) { + output.push_str(&config.main_mods) + } + + self.write_transpile(output, output_file) + } + + fn write_transpile(*self, output String, output_file PathBuf) String { if (self.path.is_dir()) { if (output.len() == 0) { return String::new(); @@ -184,7 +210,7 @@ fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String process::exit(1); } - format!("{}mod {};", vis.get_rust(), name.get_rust()) + format!("{}mod {};\n", vis.get_rust(), name.get_rust()) } else { let output = gc.generate(parsed.1); diff --git a/mist-test/Mist.toml b/mist-test/Mist.toml index 5c7ad70..c289d2a 100644 --- a/mist-test/Mist.toml +++ b/mist-test/Mist.toml @@ -1 +1,2 @@ -packages = ["main.mist"] \ No newline at end of file +package = "main.mist" +packages = ["yo.mist"] \ No newline at end of file From b547c7d93472ab6e8d9f0261057a09402c7542c0 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 20:18:49 +0200 Subject: [PATCH 10/13] removed yo --- mist-test/Mist.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mist-test/Mist.toml b/mist-test/Mist.toml index c289d2a..0ba36f0 100644 --- a/mist-test/Mist.toml +++ b/mist-test/Mist.toml @@ -1,2 +1,2 @@ package = "main.mist" -packages = ["yo.mist"] \ No newline at end of file +packages = [] \ No newline at end of file From da3923830d6b29ff068788e9e2ee8082240314c3 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 20:19:16 +0200 Subject: [PATCH 11/13] added analyzer --- Mist.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Mist.toml b/Mist.toml index 7a019d9..37b0e3f 100644 --- a/Mist.toml +++ b/Mist.toml @@ -1 +1,2 @@ package = "main.mist" +packages = ["analyzer.mist"] From c06eebe50b6facfd03436ba5a7bb53243b1673ff Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 20:22:23 +0200 Subject: [PATCH 12/13] Mist package test --- mist-test/src/common/package.mist | 3 +++ mist-test/src/main.mist | 1 + 2 files changed, 4 insertions(+) create mode 100644 mist-test/src/common/package.mist diff --git a/mist-test/src/common/package.mist b/mist-test/src/common/package.mist new file mode 100644 index 0000000..b2b6254 --- /dev/null +++ b/mist-test/src/common/package.mist @@ -0,0 +1,3 @@ +pub module common; + +pub fn test_package() {} diff --git a/mist-test/src/main.mist b/mist-test/src/main.mist index e2761cb..6bf0069 100644 --- a/mist-test/src/main.mist +++ b/mist-test/src/main.mist @@ -1,5 +1,6 @@ fn main() { println!("running common tests..."); + common::test_package(); common::expr_test::run_tests(); common::statement_test::run_tests(); common::decl_test::run_tests(); From 05fc8fa7f34df6463ec62076d53aca7dbc4e4345 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 11 Jun 2026 21:13:59 +0200 Subject: [PATCH 13/13] Fixed package gen --- crates/mist-api/src/modules.mist | 8 ++++++-- crates/mist-api/src/transpiler.mist | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/mist-api/src/modules.mist b/crates/mist-api/src/modules.mist index ca7c9f0..b0531c5 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -63,8 +63,12 @@ pub class Module { } } + pub fn is_package(*self, parent_dir *PathBuf) bool { + self.path.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() == Some("package.mist") + } + pub fn output_dir(*self, parent_dir *PathBuf) PathBuf { - if (self.path.is_dir() || Some("package.mist") == self.path.file_name().unwrap().to_str()) { + if (self.path.is_dir() || self.is_package(parent_dir)) { parent_dir.join(&self.name) } else { parent_dir.clone() @@ -72,7 +76,7 @@ pub class Module { } pub fn output_path(*self, parent_dir *PathBuf) PathBuf { - if (self.path.is_dir() || Some("package.mist") == self.path.file_name().unwrap().to_str()) { + if (self.path.is_dir() || self.is_package(parent_dir)) { parent_dir.join(&self.name).join("mod.rs") } else { parent_dir.join(&self.name).with_extension("rs") diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 7209e1a..82f497b 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -184,18 +184,20 @@ fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String } }; - if (let [Some[[vis, name]], ast] = parsed) { - let output = gc.generate(ast); + let output = gc.generate(parsed.1); - if (let Some[ext] = output_file.extension().map(fn(e) -> e.to_owned())) { - let mut new_name = std::ffi::OsString::from(&name.0); + 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); + new_name.push("."); + new_name.push(ext); - output_file.set_file_name(new_name); - } else { - output_file.set_file_name(&name.0); + 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}")); @@ -212,8 +214,6 @@ fn transpile_file(path *PathBuf, mut output_file PathBuf, mod_decl *str) String format!("{}mod {};\n", vis.get_rust(), name.get_rust()) } else { - let output = gc.generate(parsed.1); - let res = fs::write(&output_file, format!("{mod_decl}{output}")); if (res.is_err()) {