From e7bfd23eb524a122fc5d32b1383b0a701410a27c Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 18:25:20 +0200 Subject: [PATCH 01/16] Class grammar --- parser/src/grammar.pest | 17 +++++++++++++++-- test/src/main.mist | 8 ++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 56e45ad..d86c135 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -19,8 +19,23 @@ var = { "var" } var_decl = { (var | type_expr) ~ mutable? ~ identifier } semicolon = _{ ";" } +// ================= CLASS ================= +class_constructor = { + export? ~ "constructor" ~ "(" ~ param_list? ~ ")" ~ block +} + +class_fields = { var_decl_statement* } + +class_methods = { function_decl* } + +class_decl = { + "class" ~ identifier ~ "{" ~ class_fields ~ class_constructor ~ class_methods ~ "}" +} + // ================= TOP LEVEL ================= +top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute | class_decl } + attribute = { "#" ~ "[" ~ meta ~ "]" } @@ -39,8 +54,6 @@ meta_list = { attributes = { attribute* } -top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute } - import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } field_list = { field ~ ("," ~ field)* ~ ","? } diff --git a/test/src/main.mist b/test/src/main.mist index fe1d604..258851d 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,7 +1,7 @@ -void main() { - var iter = vec!(1, 2, 3, 4, 5); +class MyClass { + String my_item; - for (i in iter) { - println!("{i}"); + constructor() { + } } From 1f7db7ff1a70692072f2abf041a80cbe35a4c7cc Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 18:30:23 +0200 Subject: [PATCH 02/16] Class AST --- parser/src/ast.rs | 6 ++++++ parser/src/grammar.pest | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index fb72ea9..0c8ba21 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -74,6 +74,12 @@ pub enum TopLevelKind { return_type: TypeExpr, body: Block, }, + ClassDecl { + export: bool, + name: String, + fields: Vec, + constructor: (ParamList, Block), + }, } #[derive(Debug, Clone, Serialize)] diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index d86c135..86d0f36 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -34,7 +34,7 @@ class_decl = { // ================= TOP LEVEL ================= -top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute | class_decl } +top_level = { (attributes ~ (import | function_decl | struct_decl | class_decl)) | mod_attribute } attribute = { "#" ~ "[" ~ meta ~ "]" From f01b9debe28e7b2b31629200bc122af996297e28 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 18:41:08 +0200 Subject: [PATCH 03/16] Class AST gen --- parser/src/ast.rs | 9 ++++++++- parser/src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 0c8ba21..7efafd3 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -78,10 +78,17 @@ pub enum TopLevelKind { export: bool, name: String, fields: Vec, - constructor: (ParamList, Block), + constructor: ClassConstructor, }, } +#[derive(Debug, Clone, Serialize)] +pub struct ClassConstructor { + pub export: bool, + pub params: ParamList, + pub body: Block, +} + #[derive(Debug, Clone, Serialize)] pub enum Postfix { FieldAccess(String), diff --git a/parser/src/lib.rs b/parser/src/lib.rs index ce1dd56..eaae013 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -210,6 +210,29 @@ impl From> for StatementBranch { } } +impl From> for ClassConstructor { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let mut inner = pair.into_inner(); + + let export = if let Some(first) = inner.peek() { + if first.as_rule() == Rule::export { + inner.next(); + true + } else { + false + } + } else { + false + }; + + Self { + export, + params: ParamList::from(inner.next().unwrap()), + body: Block::from(inner.next().unwrap()), + } + } +} + impl From> for TopLevelKind { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let rule = pair.as_rule(); @@ -271,6 +294,28 @@ impl From> for TopLevelKind { fields, } } + + Rule::class_decl => TopLevelKind::ClassDecl { + export: if let Some(first) = inner.peek() { + if first.as_rule() == Rule::export { + inner.next(); + true + } else { + false + } + } else { + false + }, + name: inner.next().unwrap().as_str().to_string(), + fields: inner + .next() + .unwrap() + .into_inner() + .map(VarDeclStmt::from) + .collect(), + constructor: ClassConstructor::from(inner.next().unwrap()), + }, + _ => unimplemented!("{rule:#?}"), } } From cf99ef58c39ce63455f12904f567e6dbf35d0fa2 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 18:58:14 +0200 Subject: [PATCH 04/16] Class codegen --- parser/src/lib.rs | 8 +++++++- src/codegen.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++ test/src/main.mist | 4 ++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index eaae013..559a0f6 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -225,9 +225,15 @@ impl From> for ClassConstructor { false }; + let params = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from(inner.next().unwrap()) + } else { + ParamList(Vec::new()) + }; + Self { export, - params: ParamList::from(inner.next().unwrap()), + params, body: Block::from(inner.next().unwrap()), } } diff --git a/src/codegen.rs b/src/codegen.rs index 3440a77..905de44 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -334,6 +334,56 @@ impl ToRust for TopLevelKind { cg.indent -= 1; cg.addln("}\n"); } + Self::ClassDecl { + export, + name, + fields, + constructor, + } => { + // Struct decl + let vis = if *export { "pub " } else { "" }; + cg.addln(&format!("{}struct {} {{", vis, name)); + cg.indent += 1; + + for field in fields { + let ty = field.decl.type_.clone().unwrap().get_rust(); + cg.add_indentedln(&format!("pub {}: {},", field.decl.name, ty)); + } + + cg.indent -= 1; + cg.addln("}\n"); + + // Constructor + cg.addln(&format!("impl {} {{", name)); + cg.indent += 1; + + let params_str = constructor + .params + .0 + .iter() + .map(VarDecl::get_rust) + .collect::>() + .join(", "); + + cg.add_indentedln(&format!( + "{}fn new({}) -> Self {{", + if constructor.export { "pub " } else { "" }, + params_str + )); + cg.indent += 1; + + cg.add_indentedln("let this: Self = unsafe { std::mem::zeroed() };"); + + constructor.body.to_rust(cg); + + cg.add_indentedln("this"); + + cg.indent -= 1; + cg.add_indentedln("}\n"); + + cg.indent -= 1; + cg.addln("}\n"); + } } } } diff --git a/test/src/main.mist b/test/src/main.mist index 258851d..87d8396 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -5,3 +5,7 @@ class MyClass { } } + +void main() { + var my_class = MyClass::new(); +} From efa9d51eb62c4bfd9f1d45bc710948d41848eb48 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 19:40:45 +0200 Subject: [PATCH 05/16] working class --- src/codegen.rs | 17 +++++++++++++++-- test/src/main.mist | 3 ++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 905de44..cb3b58f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -372,15 +372,28 @@ impl ToRust for TopLevelKind { )); cg.indent += 1; - cg.add_indentedln("let this: Self = unsafe { std::mem::zeroed() };"); + cg.add_indentedln("let mut this: Self = unsafe { std::mem::MaybeUninit::::zeroed().assume_init() };"); - constructor.body.to_rust(cg); + cg.add_indentedln("this.construct_class();"); cg.add_indentedln("this"); cg.indent -= 1; cg.add_indentedln("}\n"); + // Constructor function + cg.add_indentedln(&format!( + "{}fn construct_class(&mut self, {}) {{", + if constructor.export { "pub " } else { "" }, + params_str + )); + cg.indent += 1; + + constructor.body.to_rust(cg); + + cg.indent -= 1; + cg.add_indentedln("}\n"); + cg.indent -= 1; cg.addln("}\n"); } diff --git a/test/src/main.mist b/test/src/main.mist index 87d8396..e53f67d 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -2,10 +2,11 @@ class MyClass { String my_item; constructor() { - + self.my_item = "Hello World!".to_string(); } } void main() { var my_class = MyClass::new(); + println!("{}", my_class.my_item); } From 8bb5dc35842ecb977b0c24ffb9ce798a03f90c52 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 19:49:21 +0200 Subject: [PATCH 06/16] Class constructor param fix --- src/codegen.rs | 21 ++++++++++++++++++++- test/src/main.mist | 10 +++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index cb3b58f..1d3c873 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -374,7 +374,26 @@ impl ToRust for TopLevelKind { cg.add_indentedln("let mut this: Self = unsafe { std::mem::MaybeUninit::::zeroed().assume_init() };"); - cg.add_indentedln("this.construct_class();"); + for field in fields { + if let Some(init) = &field.init { + cg.add_indentedln(&format!( + "this.{} = {};", + field.decl.name, + init.get_rust() + )); + } + } + + cg.add_indentedln(&format!( + "this.construct_class({});", + constructor + .params + .0 + .iter() + .map(|e| e.name.to_string()) + .collect::>() + .join(", ") + )); cg.add_indentedln("this"); diff --git a/test/src/main.mist b/test/src/main.mist index e53f67d..a376c85 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,12 +1,12 @@ class MyClass { - String my_item; + String message; - constructor() { - self.my_item = "Hello World!".to_string(); + public constructor(str* name) { + self.message = format!("Hello {}!", name); } } void main() { - var my_class = MyClass::new(); - println!("{}", my_class.my_item); + var my_class = MyClass::new("World"); + println!("{}", my_class.message); } From 9e5d030b0c6a35528c445c6b933a518e00d460b3 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 20:11:43 +0200 Subject: [PATCH 07/16] Class constructor (optional) --- parser/src/ast.rs | 1 + parser/src/grammar.pest | 2 ++ parser/src/lib.rs | 1 + src/codegen.rs | 25 ++++++++++++++++++++++++- test/src/main.mist | 2 +- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 7efafd3..085a206 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -104,6 +104,7 @@ pub enum Prefix { Ref, RefMut, Deref, + New, } #[derive(Debug, Clone, Serialize)] diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 86d0f36..0b536c5 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -167,11 +167,13 @@ prefix = { deref_px | mut_ref_px | ref_px +| new_px } deref_px = { "*" } mut_ref_px = { "&" ~ mutable } ref_px = { "&" } +new_px = { "new" } expr = { prefix* ~ primary ~ postfix* diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 559a0f6..26f51f4 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -480,6 +480,7 @@ impl From> for Prefix { Rule::deref_px => Self::Deref, Rule::mut_ref_px => Self::RefMut, Rule::ref_px => Self::Ref, + Rule::new_px => Self::New, _ => unimplemented!("{pair:#?}"), } } diff --git a/src/codegen.rs b/src/codegen.rs index 1d3c873..865bf95 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -148,7 +148,12 @@ impl GetRust for Expression { initial, prefixes, postfixes, - } => prefixes.get_rust() + &initial.get_rust() + &postfixes.get_rust(), + } => { + prefixes.get_rust() + + &initial.get_rust() + + &Some(prefixes).get_rust() + + &postfixes.get_rust() + } } } } @@ -159,6 +164,7 @@ impl GetRust for Prefix { Self::Deref => "*", Self::Ref => "&", Self::RefMut => "&mut ", + Self::New => "", } .to_string() } @@ -170,6 +176,23 @@ impl GetRust for [Prefix] { } } +impl GetRust for Option<&Vec> { + fn get_rust(&self) -> String { + self.map(|prefixes| { + prefixes + .iter() + .last() + .map(|p| match p { + Prefix::New => "::new", + _ => "", + }) + .unwrap_or_default() + .to_string() + }) + .unwrap_or_default() + } +} + impl GetRust for Postfix { fn get_rust(&self) -> String { match self { diff --git a/test/src/main.mist b/test/src/main.mist index a376c85..9296bb1 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -7,6 +7,6 @@ class MyClass { } void main() { - var my_class = MyClass::new("World"); + var my_class = new MyClass("World"); println!("{}", my_class.message); } From 4837d39ab72605f33178c9301a376cfd02b391c7 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 20:30:15 +0200 Subject: [PATCH 08/16] Class method AST and grammar --- parser/src/ast.rs | 9 +++++++++ parser/src/grammar.pest | 10 ++++++++-- test/src/main.mist | 6 +++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 085a206..c946096 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -89,6 +89,15 @@ pub struct ClassConstructor { pub body: Block, } +#[derive(Debug, Clone, Serialize)] +pub struct ClassMethod { + export: bool, + name: String, + params: ParamList, + return_type: TypeExpr, + body: Block, +} + #[derive(Debug, Clone, Serialize)] pub enum Postfix { FieldAccess(String), diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 0b536c5..d92a46a 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -26,7 +26,13 @@ class_constructor = { class_fields = { var_decl_statement* } -class_methods = { function_decl* } +class_methods = { method* } + +self_param = { "self" ~ mutable? ~ deref_px? } + +method = { + export? ~ type_expr ~ identifier ~ "(" ~ self_param? ~ param_list? ~ ")" ~ block +} class_decl = { "class" ~ identifier ~ "{" ~ class_fields ~ class_constructor ~ class_methods ~ "}" @@ -167,7 +173,7 @@ prefix = { deref_px | mut_ref_px | ref_px -| new_px + | new_px } deref_px = { "*" } diff --git a/test/src/main.mist b/test/src/main.mist index 9296bb1..28009dc 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -4,9 +4,13 @@ class MyClass { public constructor(str* name) { self.message = format!("Hello {}!", name); } + + public void greet(self*) { + println!("{}", self.message); + } } void main() { var my_class = new MyClass("World"); - println!("{}", my_class.message); + my_class.greet(); } From 5b0cb74cee1c9662bff31a11ce764e50dbb00300 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 21:44:36 +0200 Subject: [PATCH 09/16] Class method AST gen --- parser/src/ast.rs | 11 +++--- parser/src/grammar.pest | 4 +- parser/src/lib.rs | 83 +++++++++++++++++++++++++++++++++++++++++ src/codegen.rs | 2 + 4 files changed, 92 insertions(+), 8 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index c946096..12d7b36 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -79,6 +79,7 @@ pub enum TopLevelKind { name: String, fields: Vec, constructor: ClassConstructor, + methods: Vec, }, } @@ -91,11 +92,11 @@ pub struct ClassConstructor { #[derive(Debug, Clone, Serialize)] pub struct ClassMethod { - export: bool, - name: String, - params: ParamList, - return_type: TypeExpr, - body: Block, + pub export: bool, + pub name: String, + pub params: ParamList, + pub return_type: TypeExpr, + pub body: Block, } #[derive(Debug, Clone, Serialize)] diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index d92a46a..a8b9643 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -26,8 +26,6 @@ class_constructor = { class_fields = { var_decl_statement* } -class_methods = { method* } - self_param = { "self" ~ mutable? ~ deref_px? } method = { @@ -35,7 +33,7 @@ method = { } class_decl = { - "class" ~ identifier ~ "{" ~ class_fields ~ class_constructor ~ class_methods ~ "}" + "class" ~ identifier ~ "{" ~ class_fields ~ class_constructor ~ method* ~ "}" } // ================= TOP LEVEL ================= diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 26f51f4..0a669e8 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -320,6 +320,7 @@ impl From> for TopLevelKind { .map(VarDeclStmt::from) .collect(), constructor: ClassConstructor::from(inner.next().unwrap()), + methods: inner.into_iter().map(ClassMethod::from).collect(), }, _ => unimplemented!("{rule:#?}"), @@ -594,3 +595,85 @@ impl From> for VarDecl { } } } + +impl From> for ClassMethod { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let mut inner = pair.into_inner(); + + let export = if let Some(first) = inner.peek() { + if first.as_rule() == Rule::export { + inner.next(); + true + } else { + false + } + } else { + false + }; + + let return_type = TypeExpr::from(inner.next().unwrap()); + + let name = inner.next().unwrap().as_str().to_string(); + let self_param = if inner.peek().unwrap().as_rule() == Rule::self_param { + let mut param = inner.next().unwrap().into_inner(); + let name = format!("self"); + + let mutable = param + .peek() + .map(|p| p.as_rule() == Rule::mutable) + .unwrap_or_default(); + + if mutable { + param.next(); + } + + let is_ref = param + .peek() + .map(|p| p.as_rule() == Rule::deref_px) + .unwrap_or_default(); + + if is_ref { + param.next(); + } + + Some(VarDecl { + mutable: mutable && !is_ref, + name, + type_: Some(TypeExpr( + TypeExprKind::Path(Path(vec![format!("Self")])), + if is_ref { + vec![if mutable { + TypePostfix::RefMut + } else { + TypePostfix::Ref + }] + } else { + Vec::new() + }, + )), + }) + } else { + None + }; + + let params = if inner.peek().unwrap().as_rule() == Rule::param_list { + let mut params = ParamList::from(inner.next().unwrap()); + if let Some(x) = self_param { + params.0.insert(0, x); + } + params + } else { + ParamList(self_param.into_iter().collect()) + }; + + let body = Block::from(inner.next().unwrap()); + + Self { + export, + name, + params, + return_type, + body, + } + } +} diff --git a/src/codegen.rs b/src/codegen.rs index 865bf95..e598004 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -362,7 +362,9 @@ impl ToRust for TopLevelKind { name, fields, constructor, + methods, } => { + dbg!(methods); // Struct decl let vis = if *export { "pub " } else { "" }; cg.addln(&format!("{}struct {} {{", vis, name)); From 557b6b77206eb30b728a712202193abe4e07f972 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 21:49:29 +0200 Subject: [PATCH 10/16] Class method codegen --- src/codegen.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index e598004..1525f28 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,5 +1,5 @@ use parser::ast::{ - Attribute, BinaryOp, Block, Expression, Literal, Path, Postfix, Prefix, Statement, + Attribute, BinaryOp, Block, ClassMethod, Expression, Literal, Path, Postfix, Prefix, Statement, StatementBranch, TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, }; @@ -364,7 +364,6 @@ impl ToRust for TopLevelKind { constructor, methods, } => { - dbg!(methods); // Struct decl let vis = if *export { "pub " } else { "" }; cg.addln(&format!("{}struct {} {{", vis, name)); @@ -438,6 +437,10 @@ impl ToRust for TopLevelKind { cg.indent -= 1; cg.add_indentedln("}\n"); + for method in methods { + method.to_rust(cg); + } + cg.indent -= 1; cg.addln("}\n"); } @@ -544,6 +547,32 @@ impl ToRust for Statement { } } +impl ToRust for ClassMethod { + fn to_rust(&self, cg: &mut RustCodegen) { + let vis = if self.export { "pub " } else { "" }; + + let params_str = self + .params + .0 + .iter() + .map(VarDecl::get_rust) + .collect::>() + .join(", "); + + cg.add_indentedln(&format!( + "{}fn {}({}) -> {} {{", + vis, + self.name, + params_str, + self.return_type.get_rust() + )); + cg.indent += 1; + self.body.to_rust(cg); + cg.indent -= 1; + cg.add_indentedln("}\n"); + } +} + impl GetRust for VarDecl { fn get_rust(&self) -> String { let mutability = if self.mutable { "mut " } else { "" }; From 5be6ecb44e5d0f91394d6cdecf481f02975f017b Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 21:54:43 +0200 Subject: [PATCH 11/16] Deduped methods --- parser/src/ast.rs | 18 +++++++++--------- parser/src/lib.rs | 38 ++++---------------------------------- src/codegen.rs | 39 +++++---------------------------------- 3 files changed, 18 insertions(+), 77 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 12d7b36..7e4dc1b 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -15,6 +15,12 @@ pub enum TypePostfix { RefMut, } +#[derive(Debug, Clone, Serialize)] +pub enum Visibility { + Public, + Private, +} + #[derive(Debug, Clone, Serialize)] pub enum Attribute { /// #[test] @@ -67,19 +73,13 @@ pub enum TopLevelKind { name: String, fields: FieldList, }, - FunctionDecl { - export: bool, - name: String, - params: ParamList, - return_type: TypeExpr, - body: Block, - }, + FunctionDecl(FunctionDecl), ClassDecl { export: bool, name: String, fields: Vec, constructor: ClassConstructor, - methods: Vec, + methods: Vec, }, } @@ -91,7 +91,7 @@ pub struct ClassConstructor { } #[derive(Debug, Clone, Serialize)] -pub struct ClassMethod { +pub struct FunctionDecl { pub export: bool, pub name: String, pub params: ParamList, diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 0a669e8..b39ea1d 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -242,42 +242,12 @@ impl From> for ClassConstructor { impl From> for TopLevelKind { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let rule = pair.as_rule(); - let mut inner = pair.into_inner(); + let mut inner = pair.clone().into_inner(); match rule { Rule::import => TopLevelKind::Include(Path::from(inner.next().unwrap())), - Rule::function_decl => { - let export = if let Some(first) = inner.peek() { - if first.as_rule() == Rule::export { - inner.next(); - true - } else { - false - } - } else { - false - }; - - let return_type = TypeExpr::from(inner.next().unwrap()); - - let name = inner.next().unwrap().as_str().to_string(); - let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from(inner.next().unwrap()) - } else { - ParamList(Vec::new()) - }; - - let body = Block::from(inner.next().unwrap()); - - TopLevelKind::FunctionDecl { - export, - name, - params, - return_type, - body, - } - } + Rule::function_decl => TopLevelKind::FunctionDecl(FunctionDecl::from(pair)), Rule::struct_decl => { let export = if let Some(first) = inner.peek() { @@ -320,7 +290,7 @@ impl From> for TopLevelKind { .map(VarDeclStmt::from) .collect(), constructor: ClassConstructor::from(inner.next().unwrap()), - methods: inner.into_iter().map(ClassMethod::from).collect(), + methods: inner.into_iter().map(FunctionDecl::from).collect(), }, _ => unimplemented!("{rule:#?}"), @@ -596,7 +566,7 @@ impl From> for VarDecl { } } -impl From> for ClassMethod { +impl From> for FunctionDecl { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let mut inner = pair.into_inner(); diff --git a/src/codegen.rs b/src/codegen.rs index 1525f28..cbbd3a8 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,7 +1,7 @@ use parser::ast::{ - Attribute, BinaryOp, Block, ClassMethod, Expression, Literal, Path, Postfix, Prefix, Statement, - StatementBranch, TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, - VarDecl, VarDeclStmt, + Attribute, BinaryOp, Block, Expression, FunctionDecl, Literal, Path, Postfix, Prefix, + Statement, StatementBranch, TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, + VarAssignStmt, VarDecl, VarDeclStmt, }; // --------------------------------------------------------------------------- @@ -310,7 +310,7 @@ impl ToRust for TopLevelKind { Self::Include(path) => { cg.addln(&format!("use {};", path.get_rust())); } - + Self::FunctionDecl(decl) => decl.to_rust(cg), Self::StructDecl { export, name, @@ -328,35 +328,6 @@ impl ToRust for TopLevelKind { cg.indent -= 1; cg.addln("}\n"); } - - Self::FunctionDecl { - export, - name, - params, - return_type, - body, - } => { - let vis = if *export { "pub " } else { "" }; - - let params_str = params - .0 - .iter() - .map(VarDecl::get_rust) - .collect::>() - .join(", "); - - cg.addln(&format!( - "{}fn {}({}) -> {} {{", - vis, - name, - params_str, - return_type.get_rust() - )); - cg.indent += 1; - body.to_rust(cg); - cg.indent -= 1; - cg.addln("}\n"); - } Self::ClassDecl { export, name, @@ -547,7 +518,7 @@ impl ToRust for Statement { } } -impl ToRust for ClassMethod { +impl ToRust for FunctionDecl { fn to_rust(&self, cg: &mut RustCodegen) { let vis = if self.export { "pub " } else { "" }; From ef1585f9fb053dfdf64588769594cc3757669301 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 22:00:29 +0200 Subject: [PATCH 12/16] Visibility And Mutability AST --- parser/src/ast.rs | 10 +++++----- parser/src/lib.rs | 39 +++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 7e4dc1b..2b7a613 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -1,7 +1,7 @@ use serde::Serialize; #[derive(Debug, Clone, Serialize)] -pub struct FieldList(pub Vec<(String, bool, TypeExpr)>); +pub struct FieldList(pub Vec<(String, Visibility, TypeExpr)>); #[derive(Debug, Clone, Serialize)] pub struct ParamList(pub Vec); @@ -69,13 +69,13 @@ pub enum TopLevelKind { ModAttribute, Include(Path), StructDecl { - export: bool, + export: Visibility, name: String, fields: FieldList, }, FunctionDecl(FunctionDecl), ClassDecl { - export: bool, + export: Visibility, name: String, fields: Vec, constructor: ClassConstructor, @@ -85,14 +85,14 @@ pub enum TopLevelKind { #[derive(Debug, Clone, Serialize)] pub struct ClassConstructor { - pub export: bool, + pub export: Visibility, pub params: ParamList, pub body: Block, } #[derive(Debug, Clone, Serialize)] pub struct FunctionDecl { - pub export: bool, + pub export: Visibility, pub name: String, pub params: ParamList, pub return_type: TypeExpr, diff --git a/parser/src/lib.rs b/parser/src/lib.rs index b39ea1d..ec2605c 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -546,12 +546,8 @@ impl From> for VarDecl { Some(TypeExpr::from(pair)) } }); - let mutable = if inner.peek().unwrap().as_rule() == Rule::mutable { - inner.next(); - true - } else { - false - }; + let mutable = listen_rule(&mut inner, Rule::mutable); + let name = inner.next().unwrap().as_str().to_string(); VarDecl { @@ -588,23 +584,9 @@ impl From> for FunctionDecl { let mut param = inner.next().unwrap().into_inner(); let name = format!("self"); - let mutable = param - .peek() - .map(|p| p.as_rule() == Rule::mutable) - .unwrap_or_default(); + let mutable = listen_rule(&mut param, Rule::mutable); - if mutable { - param.next(); - } - - let is_ref = param - .peek() - .map(|p| p.as_rule() == Rule::deref_px) - .unwrap_or_default(); - - if is_ref { - param.next(); - } + let is_ref = listen_rule(&mut param, Rule::mutable); Some(VarDecl { mutable: mutable && !is_ref, @@ -647,3 +629,16 @@ impl From> for FunctionDecl { } } } + +pub fn listen_rule(pairs: &mut pest::iterators::Pairs<'_, Rule>, rule: Rule) -> bool { + let consumed = pairs + .peek() + .map(|p| p.as_rule() == rule) + .unwrap_or_default(); + + if consumed { + pairs.next(); + } + + consumed +} From a6b6b21bf30898d80a4ec3baefefb62ef2a82705 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 22:13:36 +0200 Subject: [PATCH 13/16] Visibility --- parser/src/ast.rs | 8 +++--- parser/src/lib.rs | 69 +++++++++++++---------------------------------- src/codegen.rs | 30 ++++++++++++--------- 3 files changed, 41 insertions(+), 66 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 2b7a613..5a75b81 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -69,13 +69,13 @@ pub enum TopLevelKind { ModAttribute, Include(Path), StructDecl { - export: Visibility, + visibility: Visibility, name: String, fields: FieldList, }, FunctionDecl(FunctionDecl), ClassDecl { - export: Visibility, + visibility: Visibility, name: String, fields: Vec, constructor: ClassConstructor, @@ -85,14 +85,14 @@ pub enum TopLevelKind { #[derive(Debug, Clone, Serialize)] pub struct ClassConstructor { - pub export: Visibility, + pub visibility: Visibility, pub params: ParamList, pub body: Block, } #[derive(Debug, Clone, Serialize)] pub struct FunctionDecl { - pub export: Visibility, + pub visibility: Visibility, pub name: String, pub params: ParamList, pub return_type: TypeExpr, diff --git a/parser/src/lib.rs b/parser/src/lib.rs index ec2605c..9e3ff91 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -96,15 +96,10 @@ impl From> for FieldList { .into_inner() .map(|p| { let mut param_inner = p.into_inner(); - let export = if param_inner.peek().unwrap().as_rule() == Rule::export { - param_inner.next().unwrap(); - true - } else { - false - }; + let visibility = Visibility::from(&mut param_inner); let param_type = TypeExpr::from(param_inner.next().unwrap()); let param_name = param_inner.next().unwrap().as_str().to_string(); - (param_name, export, param_type) + (param_name, visibility, param_type) }) .collect(); @@ -214,16 +209,7 @@ impl From> for ClassConstructor { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let mut inner = pair.into_inner(); - let export = if let Some(first) = inner.peek() { - if first.as_rule() == Rule::export { - inner.next(); - true - } else { - false - } - } else { - false - }; + let visibility = Visibility::from(&mut inner); let params = if inner.peek().unwrap().as_rule() == Rule::param_list { ParamList::from(inner.next().unwrap()) @@ -232,7 +218,7 @@ impl From> for ClassConstructor { }; Self { - export, + visibility, params, body: Block::from(inner.next().unwrap()), } @@ -250,38 +236,20 @@ impl From> for TopLevelKind { Rule::function_decl => TopLevelKind::FunctionDecl(FunctionDecl::from(pair)), Rule::struct_decl => { - let export = if let Some(first) = inner.peek() { - if first.as_rule() == Rule::export { - inner.next(); - true - } else { - false - } - } else { - false - }; + let visibility = Visibility::from(&mut inner); let name = inner.next().unwrap().as_str().to_string(); let fields_pair = inner.next().unwrap(); let fields = FieldList::from(fields_pair); TopLevelKind::StructDecl { - export, + visibility, name, fields, } } Rule::class_decl => TopLevelKind::ClassDecl { - export: if let Some(first) = inner.peek() { - if first.as_rule() == Rule::export { - inner.next(); - true - } else { - false - } - } else { - false - }, + visibility: Visibility::from(&mut inner), name: inner.next().unwrap().as_str().to_string(), fields: inner .next() @@ -566,16 +534,7 @@ impl From> for FunctionDecl { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let mut inner = pair.into_inner(); - let export = if let Some(first) = inner.peek() { - if first.as_rule() == Rule::export { - inner.next(); - true - } else { - false - } - } else { - false - }; + let visibility = Visibility::from(&mut inner); let return_type = TypeExpr::from(inner.next().unwrap()); @@ -621,7 +580,7 @@ impl From> for FunctionDecl { let body = Block::from(inner.next().unwrap()); Self { - export, + visibility, name, params, return_type, @@ -630,6 +589,16 @@ impl From> for FunctionDecl { } } +impl From<&mut pest::iterators::Pairs<'_, Rule>> for Visibility { + fn from(pairs: &mut pest::iterators::Pairs<'_, Rule>) -> Self { + if listen_rule(pairs, Rule::export) { + Visibility::Public + } else { + Visibility::Private + } + } +} + pub fn listen_rule(pairs: &mut pest::iterators::Pairs<'_, Rule>, rule: Rule) -> bool { let consumed = pairs .peek() diff --git a/src/codegen.rs b/src/codegen.rs index cbbd3a8..463e448 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,7 +1,7 @@ use parser::ast::{ Attribute, BinaryOp, Block, Expression, FunctionDecl, Literal, Path, Postfix, Prefix, Statement, StatementBranch, TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, - VarAssignStmt, VarDecl, VarDeclStmt, + VarAssignStmt, VarDecl, VarDeclStmt, Visibility, }; // --------------------------------------------------------------------------- @@ -312,12 +312,11 @@ impl ToRust for TopLevelKind { } Self::FunctionDecl(decl) => decl.to_rust(cg), Self::StructDecl { - export, + visibility, name, fields, } => { - let vis = if *export { "pub " } else { "" }; - cg.addln(&format!("{}struct {} {{", vis, name)); + cg.addln(&format!("{}struct {} {{", visibility.get_rust(), name)); cg.indent += 1; for (field_name, _, ty) in &fields.0 { @@ -329,15 +328,14 @@ impl ToRust for TopLevelKind { cg.addln("}\n"); } Self::ClassDecl { - export, + visibility, name, fields, constructor, methods, } => { // Struct decl - let vis = if *export { "pub " } else { "" }; - cg.addln(&format!("{}struct {} {{", vis, name)); + cg.addln(&format!("{}struct {} {{", visibility.get_rust(), name)); cg.indent += 1; for field in fields { @@ -362,7 +360,7 @@ impl ToRust for TopLevelKind { cg.add_indentedln(&format!( "{}fn new({}) -> Self {{", - if constructor.export { "pub " } else { "" }, + constructor.visibility.get_rust(), params_str )); cg.indent += 1; @@ -398,7 +396,7 @@ impl ToRust for TopLevelKind { // Constructor function cg.add_indentedln(&format!( "{}fn construct_class(&mut self, {}) {{", - if constructor.export { "pub " } else { "" }, + constructor.visibility.get_rust(), params_str )); cg.indent += 1; @@ -520,8 +518,6 @@ impl ToRust for Statement { impl ToRust for FunctionDecl { fn to_rust(&self, cg: &mut RustCodegen) { - let vis = if self.export { "pub " } else { "" }; - let params_str = self .params .0 @@ -532,7 +528,7 @@ impl ToRust for FunctionDecl { cg.add_indentedln(&format!( "{}fn {}({}) -> {} {{", - vis, + self.visibility.get_rust(), self.name, params_str, self.return_type.get_rust() @@ -573,6 +569,16 @@ impl GetRust for TypePostfix { } } +impl GetRust for Visibility { + fn get_rust(&self) -> String { + match self { + Visibility::Public => "pub ", + Visibility::Private => "", + } + .to_string() + } +} + pub fn get_static_type_path(path: &Path) -> String { let rust_path = path.get_rust(); From 2b1069d2e8d23755dae85fc053414b272d551fc3 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 22:15:12 +0200 Subject: [PATCH 14/16] Fix self with ref --- parser/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 9e3ff91..9bd3207 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -545,7 +545,7 @@ impl From> for FunctionDecl { let mutable = listen_rule(&mut param, Rule::mutable); - let is_ref = listen_rule(&mut param, Rule::mutable); + let is_ref = listen_rule(&mut param, Rule::deref_px); Some(VarDecl { mutable: mutable && !is_ref, From d95805750275f45c16766887cb176d168da0f426 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 22:34:30 +0200 Subject: [PATCH 15/16] De duped --- parser/src/lib.rs | 71 ++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 9bd3207..ebc85b3 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -211,11 +211,9 @@ impl From> for ClassConstructor { let visibility = Visibility::from(&mut inner); - let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from(inner.next().unwrap()) - } else { - ParamList(Vec::new()) - }; + let params = consume_rule(&mut inner, Rule::param_list) + .map(ParamList::from) + .unwrap_or_else(|| ParamList(Vec::new())); Self { visibility, @@ -394,18 +392,8 @@ impl From> for Expression { } Rule::primary => Expression::from(inner.next().unwrap()), Rule::static_path => Expression::Path(Path::from(pair)), - Rule::integer => { - Expression::Literal(Literal::Int(pair.as_str().parse::().unwrap())) - } - Rule::float => { - Expression::Literal(Literal::Float(pair.as_str().parse::().unwrap())) - } - Rule::boolean => { - Expression::Literal(Literal::Bool(pair.as_str().parse::().unwrap())) - } - Rule::string_lit => Expression::Literal(Literal::String(inner.as_str().to_string())), - Rule::tuple => { - Expression::Literal(Literal::Tuple(inner.map(Expression::from).collect())) + Rule::integer | Rule::float | Rule::boolean | Rule::string_lit | Rule::tuple => { + Expression::Literal(Literal::from(pair)) } _ => unimplemented!("{rule:#?}"), } @@ -539,15 +527,15 @@ impl From> for FunctionDecl { let return_type = TypeExpr::from(inner.next().unwrap()); let name = inner.next().unwrap().as_str().to_string(); - let self_param = if inner.peek().unwrap().as_rule() == Rule::self_param { - let mut param = inner.next().unwrap().into_inner(); + let self_param = consume_rule(&mut inner, Rule::self_param).map(|param| { + let mut param_inner = param.into_inner(); let name = format!("self"); - let mutable = listen_rule(&mut param, Rule::mutable); + let mutable = listen_rule(&mut param_inner, Rule::mutable); - let is_ref = listen_rule(&mut param, Rule::deref_px); + let is_ref = listen_rule(&mut param_inner, Rule::deref_px); - Some(VarDecl { + VarDecl { mutable: mutable && !is_ref, name, type_: Some(TypeExpr( @@ -562,20 +550,21 @@ impl From> for FunctionDecl { Vec::new() }, )), - }) - } else { - None - }; - - let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - let mut params = ParamList::from(inner.next().unwrap()); - if let Some(x) = self_param { - params.0.insert(0, x); } - params - } else { - ParamList(self_param.into_iter().collect()) - }; + }); + + let params = consume_rule(&mut inner, Rule::param_list) + .map({ + let self_param = self_param.clone(); + |params_pair| { + let mut params = ParamList::from(params_pair); + if let Some(x) = self_param { + params.0.insert(0, x); + } + params + } + }) + .unwrap_or_else(|| ParamList(self_param.into_iter().collect())); let body = Block::from(inner.next().unwrap()); @@ -611,3 +600,15 @@ pub fn listen_rule(pairs: &mut pest::iterators::Pairs<'_, Rule>, rule: Rule) -> consumed } + +pub fn consume_rule<'a>( + pairs: &mut pest::iterators::Pairs<'a, Rule>, + rule: Rule, +) -> Option> { + let consumed = pairs + .peek() + .map(|p| p.as_rule() == rule) + .unwrap_or_default(); + + if consumed { pairs.next() } else { None } +} From 3211b6e6ec557afb472878ab774916f828a0871c Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 22:44:38 +0200 Subject: [PATCH 16/16] Improved expression prefix --- parser/src/grammar.pest | 4 +++- parser/src/lib.rs | 13 ++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index a8b9643..9bc04cc 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -179,6 +179,8 @@ mut_ref_px = { "&" ~ mutable } ref_px = { "&" } new_px = { "new" } +prefix_list = { prefix* } + expr = { - prefix* ~ primary ~ postfix* + prefix_list ~ primary ~ postfix* } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index ebc85b3..d4e2c27 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -368,15 +368,10 @@ impl From> for Expression { match rule { Rule::expr => { - let mut prefixes = Vec::new(); - - while inner - .peek() - .map(|v| v.as_rule() == Rule::prefix) - .unwrap_or_default() - { - prefixes.push(Prefix::from(inner.next().unwrap())); - } + let prefixes: Vec = inner + .next() + .map(|p| p.into_inner().into_iter().map(Prefix::from).collect()) + .unwrap_or_default(); let exp = Expression::from(inner.next().unwrap());