From 06654b428db75f57e32cc39aba1aa6b017a09bcb Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 01:57:25 +0200 Subject: [PATCH 01/13] Fix parser --- .gitignore | 1 + parser/src/grammar.pest | 6 +-- test/src/main.mist | 90 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7811c03..2112842 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +*.exe # Generated by cargo mutants # Contains mutation testing data diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 406001c..f2da105 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -90,8 +90,8 @@ macro_inner_v = { tuple = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } primary = { - integer - | float + float + | integer | boolean | string_lit | static_path @@ -110,7 +110,7 @@ postfix = { field_px = { "." ~ identifier } call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } macro_call_px = { "!" ~ "(" ~ macro_inner_v* ~ ")" } -struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ "}" } +struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ ","? ~ "}" } index_px = { "[" ~ expr ~ "]" } binary_px = { bin_op ~ expr } bin_op = { "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" } diff --git a/test/src/main.mist b/test/src/main.mist index f5c3828..2b31e8e 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,3 +1,89 @@ -void main() { - (i32, i32, str*) my_tuple = (10, 20, "hello, world"); +#include +#include + +public struct Vec2 { + f32 x, + f32 y, +} + +public struct Body { + Vec2 pos, + Vec2 vel, + f32 mass, +} + +public struct World { + Body bodies, +} + +// ---------- Utility Functions ---------- + +public Vec2 add(Vec2 a, Vec2 b) { + return Vec2{ x: a.x + b.x, y: a.y + b.y }; +} + +public Vec2 scale(Vec2 v, f32 s) { + return Vec2{ x: v.x * s, y: v.y * s }; +} + +public f32 length(Vec2 v) { + return std::math::sqrt(v.x * v.x + v.y * v.y); +} + +// ---------- Physics Step ---------- + +public void integrate(Body mut* body, f32 dt) { + body.pos = add(body.pos, scale(body.vel, dt)); +} + +public void apply_gravity(Body mut* body) { + // simple constant gravity + body.vel.y = body.vel.y - 9.81; +} + +// ---------- Simulation ---------- + +public void simulate(World mut* world, i32 steps, f32 dt) { + i32 i = 0; + + while (i < steps) { + i = i + 1; + + // pretend we have an array-like world.bodies + i32 j = 0; + + while (j < 10) { + Body b = world.bodies[j]; + + apply_gravity(&mut b); + integrate(&mut b, dt); + + world.bodies[j] = b; + j = j + 1; + } + } +} + +// ---------- Debug / Logging ---------- + +public void debug_body(Body b) { + std::io::println!("Body -> pos(", b.pos.x, ", ", b.pos.y, ")"); +} + +// ---------- Entry Point ---------- + +public void main() { + Body player = Body{ + pos: Vec2{ x: 0.0, y: 10.0 }, + vel: Vec2{ x: 1.0, y: 0.0 }, + mass: 1.0, + }; + + World world = World{ + bodies: player + }; + + simulate(&mut world, 60, 0.016); + + debug_body(world.bodies[0]); } \ No newline at end of file From 7fe7fddea51a8584ce456c8c124a2c00d9eb9ccf Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 02:06:07 +0200 Subject: [PATCH 02/13] Fix float --- src/codegen.rs | 2 +- test/src/main.mist | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index db79b79..45cfcdd 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -110,7 +110,7 @@ impl GetRust for Expression { match self { Expression::Path(path) => path.get_rust(), Expression::IntLiteral(n) => n.to_string(), - Expression::FloatLiteral(n) => n.to_string(), + Expression::FloatLiteral(n) => format!("{n:?}"), Expression::BoolLiteral(b) => b.to_string(), Expression::StringLiteral(s) => format!("\"{s}\""), Expression::TupleLiteral(t) => { diff --git a/test/src/main.mist b/test/src/main.mist index 2b31e8e..6c83e92 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,6 +1,3 @@ -#include -#include - public struct Vec2 { f32 x, f32 y, @@ -27,7 +24,7 @@ public Vec2 scale(Vec2 v, f32 s) { } public f32 length(Vec2 v) { - return std::math::sqrt(v.x * v.x + v.y * v.y); + return (v.x * v.x + v.y * v.y).sqrt(); } // ---------- Physics Step ---------- @@ -67,7 +64,7 @@ public void simulate(World mut* world, i32 steps, f32 dt) { // ---------- Debug / Logging ---------- public void debug_body(Body b) { - std::io::println!("Body -> pos(", b.pos.x, ", ", b.pos.y, ")"); + println!("Body -> pos(", b.pos.x, ", ", b.pos.y, ")"); } // ---------- Entry Point ---------- From 7b6638d274bc74aab05da5814fbd087c93db758a Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 02:18:52 +0200 Subject: [PATCH 03/13] Attribute grammar --- parser/src/grammar.pest | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index f2da105..49a3421 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -19,7 +19,18 @@ var_decl = { type_expr ~ mutable? ~ identifier } // ================= TOP LEVEL ================= -top_level = _{ import | function_decl | struct_decl } +attribute_list = { sharp ~ "[" ~ attribute ~ ("," ~ attribute)* ~ "]" } + +attribute = { static_path ~ attribute_args? } + +attribute_args = { "(" ~ (attr_expr ~ ("," ~ attr_expr)*)? ~ ")" } + +attr_expr = { + (identifier ~ "=" ~ primary) + | primary +} + +top_level = _{ attribute_list* ~ (import | function_decl | struct_decl) } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } From 0af357df97b1a00bbf0d6056c4c333859ca14481 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 02:32:06 +0200 Subject: [PATCH 04/13] Attribute AST --- parser/src/ast.rs | 27 ++++++++++++++++++++++----- parser/src/lib.rs | 18 +++++++++++++----- src/codegen.rs | 24 ++++++++++++++++-------- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 5051a1d..cdaa12c 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -17,6 +17,27 @@ pub enum TypePostfix { RefMut, } +#[derive(Debug, Clone, Serialize)] +pub struct Attribute { + pub path: StaticPath, + pub args: Vec, +} + +#[derive(Debug, Clone, Serialize)] +pub enum AttributeArg { + Named(String, Literal), + Positional(Literal), +} + +#[derive(Debug, Clone, Serialize)] +pub enum Literal { + String(String), + Int(i64), + Float(f64), + Bool(bool), + Tuple(Vec), +} + #[derive(Debug, Clone, Serialize)] pub enum TypeExprKind { Path(StaticPath), @@ -137,12 +158,8 @@ pub struct ForStmt { #[derive(Debug, Clone, Serialize)] pub enum Expression { + Literal(Literal), Path(StaticPath), - IntLiteral(i64), - FloatLiteral(f64), - BoolLiteral(bool), - StringLiteral(String), - TupleLiteral(Vec), Fix { initial: Box, prefixes: Vec, diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 036e8cc..525280b 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -293,11 +293,19 @@ impl From> for Expression { } Rule::primary => Expression::from(inner.next().unwrap()), Rule::static_path => Expression::Path(StaticPath::from(pair)), - Rule::integer => Expression::IntLiteral(pair.as_str().parse::().unwrap()), - Rule::float => Expression::FloatLiteral(pair.as_str().parse::().unwrap()), - Rule::boolean => Expression::BoolLiteral(pair.as_str().parse::().unwrap()), - Rule::string_lit => Expression::StringLiteral(inner.as_str().to_string()), - Rule::tuple => Expression::TupleLiteral(inner.map(Expression::from).collect()), + 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())) + } _ => unimplemented!("{rule:#?}"), } } diff --git a/src/codegen.rs b/src/codegen.rs index 45cfcdd..94cd68e 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,5 +1,5 @@ use parser::ast::{ - BinaryOp, Block, Expression, IfStmt, Postfix, Prefix, Statement, StaticPath, TopLevel, + BinaryOp, Block, Expression, IfStmt, Literal, Postfix, Prefix, Statement, StaticPath, TopLevel, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, WhileStmt, }; @@ -105,15 +105,14 @@ impl GetRust for TypeExprKind { } } -impl GetRust for Expression { +impl GetRust for Literal { fn get_rust(&self) -> String { match self { - Expression::Path(path) => path.get_rust(), - Expression::IntLiteral(n) => n.to_string(), - Expression::FloatLiteral(n) => format!("{n:?}"), - Expression::BoolLiteral(b) => b.to_string(), - Expression::StringLiteral(s) => format!("\"{s}\""), - Expression::TupleLiteral(t) => { + Self::Int(n) => n.to_string(), + Self::Float(n) => format!("{n:?}"), + Self::Bool(b) => b.to_string(), + Self::String(s) => format!("\"{s}\""), + Self::Tuple(t) => { format!( "({})", t.iter() @@ -122,6 +121,15 @@ impl GetRust for Expression { .join(", ") ) } + } + } +} + +impl GetRust for Expression { + fn get_rust(&self) -> String { + match self { + Expression::Path(path) => path.get_rust(), + Expression::Literal(literal) => literal.get_rust(), Expression::Fix { initial, prefixes, From d9c20467a4b25a115cb4659d8f57c1f55d9d1874 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 02:46:32 +0200 Subject: [PATCH 05/13] Updated attr grammar --- parser/src/grammar.pest | 27 ++++++++----- test/src/main.mist | 89 ++--------------------------------------- 2 files changed, 21 insertions(+), 95 deletions(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 49a3421..3d74a98 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -19,18 +19,25 @@ var_decl = { type_expr ~ mutable? ~ identifier } // ================= TOP LEVEL ================= -attribute_list = { sharp ~ "[" ~ attribute ~ ("," ~ attribute)* ~ "]" } - -attribute = { static_path ~ attribute_args? } - -attribute_args = { "(" ~ (attr_expr ~ ("," ~ attr_expr)*)? ~ ")" } - -attr_expr = { - (identifier ~ "=" ~ primary) - | primary +attribute = { + "#" ~ "[" ~ (structured_style | list_style | key_value_style | simple_style) ~ "]" } -top_level = _{ attribute_list* ~ (import | function_decl | struct_decl) } +// Simple: #[test] +simple_style = { identifier } + +// Key-Value: #[crate_type = "lib"] +key_value_style = { identifier ~ "=" ~ primary } + +// #[derive(PartialEq, PartialOrd)] +list_style = { identifier ~ "(" ~ identifier ~ ("," ~ identifier)* ~ ","? ~ ")" } + +// #[link(name = "readline")] +structured_style = { identifier ~ "(" ~ pair ~ ("," ~ pair)* ~ ","? ~ ")" } + +pair = { identifier ~ "=" ~ primary } + +top_level = _{ attribute* ~ (import | function_decl | struct_decl) } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } diff --git a/test/src/main.mist b/test/src/main.mist index 6c83e92..352d958 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,86 +1,5 @@ -public struct Vec2 { - f32 x, - f32 y, -} - -public struct Body { - Vec2 pos, - Vec2 vel, - f32 mass, -} - -public struct World { - Body bodies, -} - -// ---------- Utility Functions ---------- - -public Vec2 add(Vec2 a, Vec2 b) { - return Vec2{ x: a.x + b.x, y: a.y + b.y }; -} - -public Vec2 scale(Vec2 v, f32 s) { - return Vec2{ x: v.x * s, y: v.y * s }; -} - -public f32 length(Vec2 v) { - return (v.x * v.x + v.y * v.y).sqrt(); -} - -// ---------- Physics Step ---------- - -public void integrate(Body mut* body, f32 dt) { - body.pos = add(body.pos, scale(body.vel, dt)); -} - -public void apply_gravity(Body mut* body) { - // simple constant gravity - body.vel.y = body.vel.y - 9.81; -} - -// ---------- Simulation ---------- - -public void simulate(World mut* world, i32 steps, f32 dt) { - i32 i = 0; - - while (i < steps) { - i = i + 1; - - // pretend we have an array-like world.bodies - i32 j = 0; - - while (j < 10) { - Body b = world.bodies[j]; - - apply_gravity(&mut b); - integrate(&mut b, dt); - - world.bodies[j] = b; - j = j + 1; - } - } -} - -// ---------- Debug / Logging ---------- - -public void debug_body(Body b) { - println!("Body -> pos(", b.pos.x, ", ", b.pos.y, ")"); -} - -// ---------- Entry Point ---------- - -public void main() { - Body player = Body{ - pos: Vec2{ x: 0.0, y: 10.0 }, - vel: Vec2{ x: 1.0, y: 0.0 }, - mass: 1.0, - }; - - World world = World{ - bodies: player - }; - - simulate(&mut world, 60, 0.016); - - debug_body(world.bodies[0]); +#[test] +public struct ApiCall { + i32 x, + i32 y, } \ No newline at end of file From f4a14789c3bc6d11cf190aeef63269453b3fa71e Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 03:17:33 +0200 Subject: [PATCH 06/13] Attribute parsing and ast --- parser/src/ast.rs | 33 ++++++++----- parser/src/grammar.pest | 2 +- parser/src/lib.rs | 103 +++++++++++++++++++++++++++++++++++----- 3 files changed, 114 insertions(+), 24 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index cdaa12c..e3e0bcf 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -20,22 +20,20 @@ pub enum TypePostfix { #[derive(Debug, Clone, Serialize)] pub struct Attribute { pub path: StaticPath, - pub args: Vec, + pub kind: MetaItemKind, } #[derive(Debug, Clone, Serialize)] -pub enum AttributeArg { - Named(String, Literal), - Positional(Literal), +pub enum MetaItemKind { + Word, + NameValue(Literal), + List(Vec), } #[derive(Debug, Clone, Serialize)] -pub enum Literal { - String(String), - Int(i64), - Float(f64), - Bool(bool), - Tuple(Vec), +pub enum NestedMetaItem { + Literal(Literal), + MetaItem(Attribute), } #[derive(Debug, Clone, Serialize)] @@ -67,7 +65,10 @@ pub enum BinaryOp { } #[derive(Debug, Clone, Serialize)] -pub enum TopLevel { +pub struct TopLevel(pub TopLevelKind, pub Vec); + +#[derive(Debug, Clone, Serialize)] +pub enum TopLevelKind { Include(StaticPath), StructDecl { export: bool, @@ -81,6 +82,7 @@ pub enum TopLevel { return_type: TypeExpr, body: Block, }, + EOI, } #[derive(Debug, Clone, Serialize)] @@ -166,3 +168,12 @@ pub enum Expression { postfixes: Vec, }, } + +#[derive(Debug, Clone, Serialize)] +pub enum Literal { + String(String), + Int(i64), + Float(f64), + Bool(bool), + Tuple(Vec), +} diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 3d74a98..b690bcf 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -37,7 +37,7 @@ structured_style = { identifier ~ "(" ~ pair ~ ("," ~ pair)* ~ ","? ~ ")" } pair = { identifier ~ "=" ~ primary } -top_level = _{ attribute* ~ (import | function_decl | struct_decl) } +top_level = { attribute* ~ (import | function_decl | struct_decl) } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 525280b..caddf9d 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -18,9 +18,7 @@ pub fn parse(source: &str) -> Result, ParseError> { let mut statements = vec![]; for pair in pairs.next().unwrap().into_inner() { - if let Ok(stmt) = TopLevel::try_from(pair) { - statements.push(stmt); - } + statements.push(TopLevel::from(pair)); } Ok(statements) @@ -120,14 +118,79 @@ impl From> for ParamList { } } -impl TryFrom> for TopLevel { - type Error = (); - fn try_from(pair: pest::iterators::Pair) -> Result { +impl From> for Attribute { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let rule = pair.as_rule(); + + // If we start at the top-level #[...], dive into the meta_item + if rule == Rule::attribute { + return Attribute::from(pair.into_inner().next().unwrap()); + } + + let mut inner = pair.into_inner(); + // Every style starts with an identifier (the path) + let path = StaticPath::from(inner.next().expect("Path identifier expected")); + + let kind = match rule { + Rule::simple_style => MetaItemKind::Word, + + Rule::key_value_style | Rule::pair => { + let lit = Literal::from(inner.next().unwrap()); + MetaItemKind::NameValue(lit) + } + + Rule::list_style => { + // derive(Debug, Clone) -> NestedMetaItem::MetaItem(Attribute { path: "Debug", kind: Word }) + let items = inner + .map(|p| { + NestedMetaItem::MetaItem(Attribute { + path: StaticPath::from(p), + kind: MetaItemKind::Word, + }) + }) + .collect(); + MetaItemKind::List(items) + } + + Rule::structured_style => { + // link(name = "readline") -> NestedMetaItem::MetaItem(Attribute { path: "name", kind: NameValue(...) }) + let items = inner + .map(|p| NestedMetaItem::MetaItem(Attribute::from(p))) + .collect(); + MetaItemKind::List(items) + } + + _ => unreachable!("Unexpected rule: {:?}", rule), + }; + + Attribute { path, kind } + } +} + +impl From> for TopLevel { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let mut inner = pair.into_inner(); + let mut attributes = Vec::new(); + + while inner + .peek() + .map(|v| v.as_rule() == Rule::attribute) + .unwrap_or_default() + { + attributes.push(Attribute::from(inner.next().unwrap())); + } + + TopLevel(inner.next().unwrap().into(), attributes) + } +} + +impl From> for TopLevelKind { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let rule = pair.as_rule(); let mut inner = pair.into_inner(); match rule { - Rule::import => Ok(TopLevel::Include(StaticPath::from(inner.next().unwrap()))), + Rule::import => TopLevelKind::Include(StaticPath::from(inner.next().unwrap())), Rule::function_decl => { let export = if let Some(first) = inner.peek() { @@ -152,13 +215,13 @@ impl TryFrom> for TopLevel { let body = Block::from(inner.next().unwrap()); - Ok(TopLevel::FunctionDecl { + TopLevelKind::FunctionDecl { export, name, params, return_type, body, - }) + } } Rule::struct_decl => { @@ -176,14 +239,14 @@ impl TryFrom> for TopLevel { let fields_pair = inner.next().unwrap(); let fields = FieldList::from(fields_pair); - Ok(TopLevel::StructDecl { + TopLevelKind::StructDecl { export, name, fields, - }) + } } - Rule::EOI => Err(()), + Rule::EOI => TopLevelKind::EOI, _ => unimplemented!("{rule:#?}"), } } @@ -262,6 +325,22 @@ impl From> for Statement { } } +impl From> for Literal { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let rule = pair.as_rule(); + let inner = pair.clone().into_inner(); + + match rule { + Rule::integer => Literal::Int(pair.as_str().parse::().unwrap()), + Rule::float => Literal::Float(pair.as_str().parse::().unwrap()), + Rule::boolean => Literal::Bool(pair.as_str().parse::().unwrap()), + Rule::string_lit => Literal::String(inner.as_str().to_string()), + Rule::tuple => Literal::Tuple(inner.map(Expression::from).collect()), + _ => unimplemented!("{rule:#?}"), + } + } +} + impl From> for Expression { fn from(pair: pest::iterators::Pair) -> Self { let rule = pair.as_rule(); From 1d00667b180b6756ee6b98d42c1a0a4b5c3abb0c Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:12:49 +0200 Subject: [PATCH 07/13] refactoring the grammar --- parser/src/grammar.pest | 13 +++++-------- src/codegen.rs | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index b690bcf..8c8aaa0 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -20,22 +20,19 @@ var_decl = { type_expr ~ mutable? ~ identifier } // ================= TOP LEVEL ================= attribute = { - "#" ~ "[" ~ (structured_style | list_style | key_value_style | simple_style) ~ "]" + "#" ~ "[" ~ (structured_style | list_style | pair_attr | simple_style) ~ "]" } // Simple: #[test] -simple_style = { identifier } - -// Key-Value: #[crate_type = "lib"] -key_value_style = { identifier ~ "=" ~ primary } +simple_style = { static_path } // #[derive(PartialEq, PartialOrd)] -list_style = { identifier ~ "(" ~ identifier ~ ("," ~ identifier)* ~ ","? ~ ")" } +list_style = { static_path ~ "(" ~ identifier ~ ("," ~ identifier)* ~ ","? ~ ")" } // #[link(name = "readline")] -structured_style = { identifier ~ "(" ~ pair ~ ("," ~ pair)* ~ ","? ~ ")" } +structured_style = { static_path ~ "(" ~ pair_attr ~ ("," ~ pair_attr)* ~ ","? ~ ")" } -pair = { identifier ~ "=" ~ primary } +pair_attr = { identifier ~ "=" ~ primary } top_level = { attribute* ~ (import | function_decl | struct_decl) } diff --git a/src/codegen.rs b/src/codegen.rs index 94cd68e..c37b6ea 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,6 +1,7 @@ use parser::ast::{ BinaryOp, Block, Expression, IfStmt, Literal, Postfix, Prefix, Statement, StaticPath, TopLevel, - TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, WhileStmt, + TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, + WhileStmt, }; // --------------------------------------------------------------------------- @@ -226,13 +227,19 @@ impl ToRust for Block { } impl ToRust for TopLevel { + fn to_rust(&self, cg: &mut RustCodegen) { + self.0.to_rust(cg); + } +} + +impl ToRust for TopLevelKind { fn to_rust(&self, cg: &mut RustCodegen) { match self { - TopLevel::Include(path) => { + Self::Include(path) => { cg.addln(&format!("use {};", path.get_rust())); } - TopLevel::StructDecl { + Self::StructDecl { export, name, fields, @@ -250,7 +257,7 @@ impl ToRust for TopLevel { cg.addln("}\n"); } - TopLevel::FunctionDecl { + Self::FunctionDecl { export, name, params, @@ -278,6 +285,7 @@ impl ToRust for TopLevel { cg.indent -= 1; cg.addln("}\n"); } + Self::EOI => {} } } } From ce7dc2b9ba54a20565db610b1533d0fb6258d5a3 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:30:24 +0200 Subject: [PATCH 08/13] Updated grammar and AST --- parser/src/ast.rs | 24 ++++++++++-------------- parser/src/grammar.pest | 17 +++++++---------- parser/src/lib.rs | 35 ++--------------------------------- 3 files changed, 19 insertions(+), 57 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index e3e0bcf..05a42f0 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -18,22 +18,18 @@ pub enum TypePostfix { } #[derive(Debug, Clone, Serialize)] -pub struct Attribute { - pub path: StaticPath, - pub kind: MetaItemKind, -} +pub enum Attribute { + /// #[test] + Path(StaticPath), -#[derive(Debug, Clone, Serialize)] -pub enum MetaItemKind { - Word, - NameValue(Literal), - List(Vec), -} + /// #[name = "value"] + NameValue { path: StaticPath, value: Literal }, -#[derive(Debug, Clone, Serialize)] -pub enum NestedMetaItem { - Literal(Literal), - MetaItem(Attribute), + /// #[derive(Clone, Copy)] + List { + path: StaticPath, + items: Vec, + }, } #[derive(Debug, Clone, Serialize)] diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 8c8aaa0..7b13504 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -20,19 +20,16 @@ var_decl = { type_expr ~ mutable? ~ identifier } // ================= TOP LEVEL ================= attribute = { - "#" ~ "[" ~ (structured_style | list_style | pair_attr | simple_style) ~ "]" + "#" ~ "[" ~ meta ~ "]" } -// Simple: #[test] -simple_style = { static_path } +meta = { + static_path ~ ("=" ~ primary | "(" ~ meta_list? ~ ")")? +} -// #[derive(PartialEq, PartialOrd)] -list_style = { static_path ~ "(" ~ identifier ~ ("," ~ identifier)* ~ ","? ~ ")" } - -// #[link(name = "readline")] -structured_style = { static_path ~ "(" ~ pair_attr ~ ("," ~ pair_attr)* ~ ","? ~ ")" } - -pair_attr = { identifier ~ "=" ~ primary } +meta_list = { + meta ~ ("," ~ meta)* ~ ","? +} top_level = { attribute* ~ (import | function_decl | struct_decl) } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index caddf9d..0e00713 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -128,42 +128,11 @@ impl From> for Attribute { } let mut inner = pair.into_inner(); - // Every style starts with an identifier (the path) - let path = StaticPath::from(inner.next().expect("Path identifier expected")); + let path = StaticPath::from(inner.next().unwrap()); let kind = match rule { - Rule::simple_style => MetaItemKind::Word, - - Rule::key_value_style | Rule::pair => { - let lit = Literal::from(inner.next().unwrap()); - MetaItemKind::NameValue(lit) - } - - Rule::list_style => { - // derive(Debug, Clone) -> NestedMetaItem::MetaItem(Attribute { path: "Debug", kind: Word }) - let items = inner - .map(|p| { - NestedMetaItem::MetaItem(Attribute { - path: StaticPath::from(p), - kind: MetaItemKind::Word, - }) - }) - .collect(); - MetaItemKind::List(items) - } - - Rule::structured_style => { - // link(name = "readline") -> NestedMetaItem::MetaItem(Attribute { path: "name", kind: NameValue(...) }) - let items = inner - .map(|p| NestedMetaItem::MetaItem(Attribute::from(p))) - .collect(); - MetaItemKind::List(items) - } - - _ => unreachable!("Unexpected rule: {:?}", rule), + _ => unimplemented!("{:?}", rule), }; - - Attribute { path, kind } } } From e421a81a3dc3b69760299e3f098f43a74c00004e Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:43:14 +0200 Subject: [PATCH 09/13] Working parser for attributes --- parser/src/ast.rs | 1 - parser/src/grammar.pest | 4 +- parser/src/lib.rs | 83 +++++++++++++++++++++++++++++------------ src/codegen.rs | 1 - 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 05a42f0..cbbc186 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -78,7 +78,6 @@ pub enum TopLevelKind { return_type: TypeExpr, body: Block, }, - EOI, } #[derive(Debug, Clone, Serialize)] diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 7b13504..5f73a47 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -31,7 +31,9 @@ meta_list = { meta ~ ("," ~ meta)* ~ ","? } -top_level = { attribute* ~ (import | function_decl | struct_decl) } +attributes = { attribute* } + +top_level = { attributes ~ (import | function_decl | struct_decl) } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 0e00713..8b988fc 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -18,7 +18,9 @@ pub fn parse(source: &str) -> Result, ParseError> { let mut statements = vec![]; for pair in pairs.next().unwrap().into_inner() { - statements.push(TopLevel::from(pair)); + if pair.as_rule() != Rule::EOI { + statements.push(TopLevel::from(pair)); + } } Ok(statements) @@ -120,36 +122,73 @@ impl From> for ParamList { impl From> for Attribute { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { - let rule = pair.as_rule(); + match pair.as_rule() { + Rule::attribute => { + // unwrap #[ ... ] + Attribute::from(pair.into_inner().next().unwrap()) + } - // If we start at the top-level #[...], dive into the meta_item - if rule == Rule::attribute { - return Attribute::from(pair.into_inner().next().unwrap()); + Rule::meta => { + let mut inner = pair.into_inner(); + + // first item is always the path + let path = StaticPath::from(inner.next().unwrap()); + + // check what comes next + match inner.next() { + None => { + // #[path] + Attribute::Path(path) + } + + Some(next) => match next.as_rule() { + Rule::primary => { + // #[path = literal] + Attribute::NameValue { + path, + value: Literal::from(next), + } + } + + Rule::meta_list => { + // #[path(...)] + let items = next.into_inner().map(Attribute::from).collect(); + + Attribute::List { path, items } + } + + _ => unreachable!("unexpected rule in meta: {:?}", next.as_rule()), + }, + } + } + + Rule::meta_list => { + // This case usually won't be hit directly, + // but it's nice to keep it safe if reused + let items = pair.into_inner().map(Attribute::from).collect::>(); + + // NOTE: this shouldn't normally construct an Attribute alone + // but you can panic or wrap depending on your design + panic!("meta_list should be handled inside meta: {:?}", items); + } + + _ => unreachable!("unexpected rule: {:?}", pair.as_rule()), } - - let mut inner = pair.into_inner(); - let path = StaticPath::from(inner.next().unwrap()); - - let kind = match rule { - _ => unimplemented!("{:?}", rule), - }; } } impl From> for TopLevel { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let mut inner = pair.into_inner(); - let mut attributes = Vec::new(); - while inner - .peek() - .map(|v| v.as_rule() == Rule::attribute) - .unwrap_or_default() - { - attributes.push(Attribute::from(inner.next().unwrap())); - } + let attributes = inner + .next() + .unwrap() + .into_inner() + .map(Attribute::from) + .collect::>(); - TopLevel(inner.next().unwrap().into(), attributes) + TopLevel(TopLevelKind::from(inner.next().unwrap()), attributes) } } @@ -214,8 +253,6 @@ impl From> for TopLevelKind { fields, } } - - Rule::EOI => TopLevelKind::EOI, _ => unimplemented!("{rule:#?}"), } } diff --git a/src/codegen.rs b/src/codegen.rs index c37b6ea..4e1e848 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -285,7 +285,6 @@ impl ToRust for TopLevelKind { cg.indent -= 1; cg.addln("}\n"); } - Self::EOI => {} } } } From 3f3498c33857914673b54ac7e8cd53322729548d Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:45:30 +0200 Subject: [PATCH 10/13] Fix primary --- parser/src/lib.rs | 3 ++- test/src/main.mist | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 8b988fc..a78b54a 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -334,9 +334,10 @@ impl From> for Statement { impl From> for Literal { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let rule = pair.as_rule(); - let inner = pair.clone().into_inner(); + let mut inner = pair.clone().into_inner(); match rule { + Rule::primary => Self::from(inner.next().unwrap()), Rule::integer => Literal::Int(pair.as_str().parse::().unwrap()), Rule::float => Literal::Float(pair.as_str().parse::().unwrap()), Rule::boolean => Literal::Bool(pair.as_str().parse::().unwrap()), diff --git a/test/src/main.mist b/test/src/main.mist index 352d958..0ebece7 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,4 +1,4 @@ -#[test] +#[derive(Debug)] public struct ApiCall { i32 x, i32 y, From 5c557596da3b05658a487a379a25eaa58a1373c8 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:46:22 +0200 Subject: [PATCH 11/13] Refactored Path --- parser/src/ast.rs | 19 ++++++++----------- parser/src/lib.rs | 12 ++++++------ src/codegen.rs | 6 +++--- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index cbbc186..7dc69b1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -20,22 +20,19 @@ pub enum TypePostfix { #[derive(Debug, Clone, Serialize)] pub enum Attribute { /// #[test] - Path(StaticPath), + Path(Path), /// #[name = "value"] - NameValue { path: StaticPath, value: Literal }, + NameValue { path: Path, value: Literal }, /// #[derive(Clone, Copy)] - List { - path: StaticPath, - items: Vec, - }, + List { path: Path, items: Vec }, } #[derive(Debug, Clone, Serialize)] pub enum TypeExprKind { - Path(StaticPath), - PathParams(StaticPath, Vec), + Path(Path), + PathParams(Path, Vec), Tuple(Vec), } @@ -43,7 +40,7 @@ pub enum TypeExprKind { pub struct TypeExpr(pub TypeExprKind, pub Vec); #[derive(Debug, Clone, Serialize)] -pub struct StaticPath(pub Vec); +pub struct Path(pub Vec); #[derive(Debug, Clone, Serialize)] pub enum BinaryOp { @@ -65,7 +62,7 @@ pub struct TopLevel(pub TopLevelKind, pub Vec); #[derive(Debug, Clone, Serialize)] pub enum TopLevelKind { - Include(StaticPath), + Include(Path), StructDecl { export: bool, name: String, @@ -156,7 +153,7 @@ pub struct ForStmt { #[derive(Debug, Clone, Serialize)] pub enum Expression { Literal(Literal), - Path(StaticPath), + Path(Path), Fix { initial: Box, prefixes: Vec, diff --git a/parser/src/lib.rs b/parser/src/lib.rs index a78b54a..cae599f 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -67,7 +67,7 @@ impl From> for TypeExprKind { match rule { Rule::tuple_type => TypeExprKind::Tuple(inner.map(TypeExpr::from).collect()), Rule::path_type => { - let path = StaticPath::from(inner.next().unwrap()); + let path = Path::from(inner.next().unwrap()); let params = inner.map(TypeExpr::from).collect::>(); if params.len() == 0 { @@ -81,11 +81,11 @@ impl From> for TypeExprKind { } } -impl From> for StaticPath { +impl From> for Path { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { match pair.as_rule() { Rule::static_path => { - StaticPath(pair.into_inner().map(|i| i.as_str().to_string()).collect()) + Path(pair.into_inner().map(|i| i.as_str().to_string()).collect()) } _ => unimplemented!("{pair:#?}"), } @@ -132,7 +132,7 @@ impl From> for Attribute { let mut inner = pair.into_inner(); // first item is always the path - let path = StaticPath::from(inner.next().unwrap()); + let path = Path::from(inner.next().unwrap()); // check what comes next match inner.next() { @@ -198,7 +198,7 @@ impl From> for TopLevelKind { let mut inner = pair.into_inner(); match rule { - Rule::import => TopLevelKind::Include(StaticPath::from(inner.next().unwrap())), + Rule::import => TopLevelKind::Include(Path::from(inner.next().unwrap())), Rule::function_decl => { let export = if let Some(first) = inner.peek() { @@ -378,7 +378,7 @@ impl From> for Expression { } } Rule::primary => Expression::from(inner.next().unwrap()), - Rule::static_path => Expression::Path(StaticPath::from(pair)), + Rule::static_path => Expression::Path(Path::from(pair)), Rule::integer => { Expression::Literal(Literal::Int(pair.as_str().parse::().unwrap())) } diff --git a/src/codegen.rs b/src/codegen.rs index 4e1e848..b6038d5 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,5 +1,5 @@ use parser::ast::{ - BinaryOp, Block, Expression, IfStmt, Literal, Postfix, Prefix, Statement, StaticPath, TopLevel, + BinaryOp, Block, Expression, IfStmt, Literal, Path, Postfix, Prefix, Statement, TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, WhileStmt, }; @@ -374,7 +374,7 @@ impl GetRust for VarDecl { } } -impl GetRust for StaticPath { +impl GetRust for Path { fn get_rust(&self) -> String { self.0.join("::") } @@ -389,7 +389,7 @@ impl GetRust for TypePostfix { } } -pub fn get_static_type_path(path: &StaticPath) -> String { +pub fn get_static_type_path(path: &Path) -> String { let rust_path = path.get_rust(); if rust_path == "void" { From ce5024789574f7a4350e34019c45aebe6ca60d39 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 14:53:07 +0200 Subject: [PATCH 12/13] Attribute codegen --- src/codegen.rs | 32 +++++++++++++++++++++++++++++--- test/src/main.mist | 8 +++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index b6038d5..94df494 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,7 +1,7 @@ use parser::ast::{ - BinaryOp, Block, Expression, IfStmt, Literal, Path, Postfix, Prefix, Statement, TopLevel, - TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, - WhileStmt, + Attribute, BinaryOp, Block, Expression, IfStmt, Literal, Path, Postfix, Prefix, Statement, + TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, + VarDeclStmt, WhileStmt, }; // --------------------------------------------------------------------------- @@ -228,10 +228,36 @@ impl ToRust for Block { impl ToRust for TopLevel { fn to_rust(&self, cg: &mut RustCodegen) { + for attr in &self.1 { + cg.addln(&format!("#[{}]", attr.get_rust())); + } + self.0.to_rust(cg); } } +impl GetRust for Attribute { + fn get_rust(&self) -> String { + match self { + Self::Path(path) => path.get_rust(), + Self::NameValue { path, value } => { + format!("{} = {}", path.get_rust(), value.get_rust()) + } + Self::List { path, items } => { + format!( + "{}({})", + path.get_rust(), + items + .iter() + .map(Attribute::get_rust) + .collect::>() + .join(", ") + ) + } + } + } +} + impl ToRust for TopLevelKind { fn to_rust(&self, cg: &mut RustCodegen) { match self { diff --git a/test/src/main.mist b/test/src/main.mist index 0ebece7..a9c3257 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -2,4 +2,10 @@ public struct ApiCall { i32 x, i32 y, -} \ No newline at end of file +} + +void main() { + ApiCall call = ApiCall { x: 10, y: 12 }; + + println!("{call:?}"); +} From 8793c5d69a39b457cfab4027c81099ae31a8f6b0 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Tue, 5 May 2026 15:08:38 +0200 Subject: [PATCH 13/13] Top level attributes --- parser/src/ast.rs | 1 + parser/src/grammar.pest | 6 +++++- parser/src/lib.rs | 12 ++++++++---- src/codegen.rs | 14 ++++++++++++-- test/src/main.mist | 2 ++ 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 7dc69b1..03a5bd1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -62,6 +62,7 @@ pub struct TopLevel(pub TopLevelKind, pub Vec); #[derive(Debug, Clone, Serialize)] pub enum TopLevelKind { + ModAttribute, Include(Path), StructDecl { export: bool, diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 5f73a47..59d1490 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -23,6 +23,10 @@ attribute = { "#" ~ "[" ~ meta ~ "]" } +mod_attribute = { + "#" ~ "!" ~ "[" ~ meta ~ "]" +} + meta = { static_path ~ ("=" ~ primary | "(" ~ meta_list? ~ ")")? } @@ -33,7 +37,7 @@ meta_list = { attributes = { attribute* } -top_level = { attributes ~ (import | function_decl | struct_decl) } +top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index cae599f..e2014f4 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -84,9 +84,7 @@ impl From> for TypeExprKind { impl From> for Path { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { match pair.as_rule() { - Rule::static_path => { - Path(pair.into_inner().map(|i| i.as_str().to_string()).collect()) - } + Rule::static_path => Path(pair.into_inner().map(|i| i.as_str().to_string()).collect()), _ => unimplemented!("{pair:#?}"), } } @@ -188,7 +186,13 @@ impl From> for TopLevel { .map(Attribute::from) .collect::>(); - TopLevel(TopLevelKind::from(inner.next().unwrap()), attributes) + TopLevel( + inner + .next() + .map(TopLevelKind::from) + .unwrap_or(TopLevelKind::ModAttribute), + attributes, + ) } } diff --git a/src/codegen.rs b/src/codegen.rs index 94df494..b4c4a35 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -228,8 +228,17 @@ impl ToRust for Block { impl ToRust for TopLevel { fn to_rust(&self, cg: &mut RustCodegen) { - for attr in &self.1 { - cg.addln(&format!("#[{}]", attr.get_rust())); + match &self.0 { + TopLevelKind::ModAttribute => { + for attr in &self.1 { + cg.addln(&format!("#![{}]", attr.get_rust())); + } + } + _ => { + for attr in &self.1 { + cg.addln(&format!("#[{}]", attr.get_rust())); + } + } } self.0.to_rust(cg); @@ -261,6 +270,7 @@ impl GetRust for Attribute { impl ToRust for TopLevelKind { fn to_rust(&self, cg: &mut RustCodegen) { match self { + Self::ModAttribute => {} Self::Include(path) => { cg.addln(&format!("use {};", path.get_rust())); } diff --git a/test/src/main.mist b/test/src/main.mist index a9c3257..40ba951 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,3 +1,5 @@ +#![allow(unused_mut)] + #[derive(Debug)] public struct ApiCall { i32 x,