From a9c9c03fd3276f959b6ee3717881043eb062ccd4 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:12:46 +0200 Subject: [PATCH 01/17] Re writing --- src/compiler.rs | 5 +- src/gogen/mod.rs | 458 ++++++++++++++++++++-------------------- src/parser/ast.rs | 193 ++--------------- src/parser/grammar.pest | 142 +------------ src/parser/mod.rs | 332 +---------------------------- 5 files changed, 264 insertions(+), 866 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 7091a9a..136aed5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -41,7 +41,7 @@ pub fn build() { let ast = match parser::parse(&source) { Ok(ast) => { - println!(" ✓ parsed {} items", ast.statements.len()); + println!(" ✓ parsed {} items", ast.len()); ast } Err(e) => { @@ -52,7 +52,8 @@ pub fn build() { println!(" → generating Go code..."); - let output = gogen::generate(&ast); + // let output = gogen::generate(&ast); + let output = String::from("// TODO: implement code generation\n"); // 4. ensure build dir if let Err(e) = fs::create_dir_all(&out_dir) { diff --git a/src/gogen/mod.rs b/src/gogen/mod.rs index 4402e36..d41f710 100644 --- a/src/gogen/mod.rs +++ b/src/gogen/mod.rs @@ -1,283 +1,283 @@ -use crate::parser::ast::{ - BinOperator, Class, Expression, Function, Program, Statement, Struct, TopLevel, TypeExpr, -}; +// use crate::parser::ast::{ +// BinOperator, Class, Expression, Function, Statement, Struct, TopLevel, TopLevel, TypeExpr, +// }; -pub fn generate(program: &Program) -> String { - let mut out = String::new(); +// pub fn generate(program: &TopLevel) -> String { +// let mut out = String::new(); - out.push_str("package main\n\n"); +// out.push_str("package main\n\n"); - // imports - let mut imports = vec![]; - for item in &program.statements { - if let TopLevel::Import(i) = item { - imports.push(i.path.clone()); - } - } +// // imports +// let mut imports = vec![]; +// for item in &program.statements { +// if let TopLevel::Import(i) = item { +// imports.push(i.path.clone()); +// } +// } - if !imports.is_empty() { - out.push_str("import (\n"); - for imp in imports { - out.push_str(&format!(" {}\n", imp)); - } - out.push_str(")\n\n"); - } +// if !imports.is_empty() { +// out.push_str("import (\n"); +// for imp in imports { +// out.push_str(&format!(" {}\n", imp)); +// } +// out.push_str(")\n\n"); +// } - // rest - for item in &program.statements { - match item { - TopLevel::Function(f) => out.push_str(&gen_function(f)), - TopLevel::Struct(s) => out.push_str(&gen_struct(s)), - TopLevel::Class(c) => out.push_str(&gen_class(c)), - TopLevel::Import(_) => {} - } - out.push('\n'); - } +// // rest +// for item in &program.statements { +// match item { +// TopLevel::Function(f) => out.push_str(&gen_function(f)), +// TopLevel::Struct(s) => out.push_str(&gen_struct(s)), +// TopLevel::Class(c) => out.push_str(&gen_class(c)), +// TopLevel::Import(_) => {} +// } +// out.push('\n'); +// } - out -} +// out +// } -fn gen_struct(s: &Struct) -> String { - let mut out = format!("type {} struct {{\n", s.name); +// fn gen_struct(s: &Struct) -> String { +// let mut out = format!("type {} struct {{\n", s.name); - for field in &s.fields { - out.push_str(&format!( - " {} {}\n", - capitalize(&field.name), - gen_type(&field.type_expr) - )); - } +// for field in &s.fields { +// out.push_str(&format!( +// " {} {}\n", +// capitalize(&field.name), +// gen_type(&field.type_expr) +// )); +// } - out.push_str("}\n"); - out -} +// out.push_str("}\n"); +// out +// } -fn gen_class(c: &Class) -> String { - let mut out = String::new(); +// fn gen_class(c: &Class) -> String { +// let mut out = String::new(); - // struct - out.push_str(&format!("type {} struct {{\n", c.name)); - for field in &c.fields { - out.push_str(&format!( - " {} {}\n", - capitalize(&field.name), - gen_type(&field.type_expr) - )); - } - out.push_str("}\n\n"); +// // struct +// out.push_str(&format!("type {} struct {{\n", c.name)); +// for field in &c.fields { +// out.push_str(&format!( +// " {} {}\n", +// capitalize(&field.name), +// gen_type(&field.type_expr) +// )); +// } +// out.push_str("}\n\n"); - // methods - for method in &c.methods { - out.push_str(&gen_method(c, method)); - out.push('\n'); - } +// // methods +// for method in &c.methods { +// out.push_str(&gen_method(c, method)); +// out.push('\n'); +// } - out -} +// out +// } -fn gen_function(f: &Function) -> String { - let mut out = format!("func {}(", f.name); +// fn gen_function(f: &Function) -> String { +// let mut out = format!("func {}(", f.name); - // params - for (i, p) in f.params.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); - } +// // params +// for (i, p) in f.params.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); +// } - out.push(')'); +// out.push(')'); - // return - if let Some(ret) = &f.return_type { - out.push_str(&format!(" {}", gen_type(ret))); - } +// // return +// if let Some(ret) = &f.return_type { +// out.push_str(&format!(" {}", gen_type(ret))); +// } - out.push_str(" {\n"); +// out.push_str(" {\n"); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str("}\n"); - out -} +// out.push_str("}\n"); +// out +// } -fn gen_method(class: &Class, f: &Function) -> String { - let mut out = format!("func (self *{}) {}(", class.name, f.name); +// fn gen_method(class: &Class, f: &Function) -> String { +// let mut out = format!("func (self *{}) {}(", class.name, f.name); - for (i, p) in f.params.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); - } +// for (i, p) in f.params.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); +// } - out.push(')'); +// out.push(')'); - if let Some(ret) = &f.return_type { - out.push_str(&format!(" {}", gen_type(ret))); - } +// if let Some(ret) = &f.return_type { +// out.push_str(&format!(" {}", gen_type(ret))); +// } - out.push_str(" {\n"); +// out.push_str(" {\n"); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str("}\n"); +// out.push_str("}\n"); - out -} +// out +// } -fn gen_statement(stmt: &Statement) -> String { - match stmt { - Statement::Let(s) => { - let mut out = format!(" {} := {}", s.name, gen_expr(&s.value)); +// fn gen_statement(stmt: &Statement) -> String { +// match stmt { +// Statement::Let(s) => { +// let mut out = format!(" {} := {}", s.name, gen_expr(&s.value)); - out.push_str(";\n"); - out - } +// out.push_str(";\n"); +// out +// } - Statement::Return(r) => match &r.value { - Some(v) => format!(" return {};\n", gen_expr(v)), - None => " return;\n".to_string(), - }, +// Statement::Return(r) => match &r.value { +// Some(v) => format!(" return {};\n", gen_expr(v)), +// None => " return;\n".to_string(), +// }, - Statement::Expression(e) => { - format!(" {};\n", gen_expr(e)) - } +// Statement::Expression(e) => { +// format!(" {};\n", gen_expr(e)) +// } - Statement::If(i) => { - let mut out = format!(" if {} {{\n", gen_expr(&i.condition)); +// Statement::If(i) => { +// let mut out = format!(" if {} {{\n", gen_expr(&i.condition)); - for stmt in &i.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &i.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str(" }"); +// out.push_str(" }"); - if let Some(else_body) = &i.else_body { - out.push_str(" else {\n"); - for stmt in else_body { - out.push_str(&gen_statement(stmt)); - } - out.push_str(" }"); - } +// if let Some(else_body) = &i.else_body { +// out.push_str(" else {\n"); +// for stmt in else_body { +// out.push_str(&gen_statement(stmt)); +// } +// out.push_str(" }"); +// } - out.push('\n'); - out - } +// out.push('\n'); +// out +// } - Statement::For(f) => { - let mut out = format!( - " for _, {} := range {} {{\n", - f.var, - gen_expr(&f.iterator) - ); +// Statement::For(f) => { +// let mut out = format!( +// " for _, {} := range {} {{\n", +// f.var, +// gen_expr(&f.iterator) +// ); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str(" }\n"); - out - } - } -} +// out.push_str(" }\n"); +// out +// } +// } +// } -fn gen_expr(expr: &Expression) -> String { - match expr { - Expression::Identifier(name, _) => name.clone(), - Expression::Integer(v, _) => v.to_string(), - Expression::Float(v, _) => v.to_string(), - Expression::StringLit(s, _) => format!("\"{}\"", s), - Expression::Bool(b, _) => b.to_string(), +// fn gen_expr(expr: &Expression) -> String { +// match expr { +// Expression::Identifier(name, _) => name.clone(), +// Expression::Integer(v, _) => v.to_string(), +// Expression::Float(v, _) => v.to_string(), +// Expression::StringLit(s, _) => format!("\"{}\"", s), +// Expression::Bool(b, _) => b.to_string(), - Expression::BinaryOp(b) => format!( - "{} {} {}", - gen_expr(&b.left), - op_to_str(&b.op), - gen_expr(&b.right) - ), +// Expression::BinaryOp(b) => format!( +// "{} {} {}", +// gen_expr(&b.left), +// op_to_str(&b.op), +// gen_expr(&b.right) +// ), - Expression::Call(c) => { - let args = c.args.iter().map(gen_expr).collect::>().join(", "); - format!("{}({})", gen_expr(&c.callee), args) - } +// Expression::Call(c) => { +// let args = c.args.iter().map(gen_expr).collect::>().join(", "); +// format!("{}({})", gen_expr(&c.callee), args) +// } - Expression::FieldAccess(f) => { - format!("{}.{}", gen_expr(&f.object), capitalize(&f.field)) - } +// Expression::FieldAccess(f) => { +// format!("{}.{}", gen_expr(&f.object), capitalize(&f.field)) +// } - Expression::StructInit(s) => { - let mut out = format!("{}{{", s.name); +// Expression::StructInit(s) => { +// let mut out = format!("{}{{", s.name); - for (i, (name, val)) in s.fields.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{}: {}", capitalize(name), gen_expr(val))); - } +// for (i, (name, val)) in s.fields.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{}: {}", capitalize(name), gen_expr(val))); +// } - out.push('}'); - out - } +// out.push('}'); +// out +// } - Expression::ArrayLiteral(arr) => { - let elems = arr - .elements - .iter() - .map(gen_expr) - .collect::>() - .join(", "); - format!("[]any{{{}}}", elems) // simple version - } +// Expression::ArrayLiteral(arr) => { +// let elems = arr +// .elements +// .iter() +// .map(gen_expr) +// .collect::>() +// .join(", "); +// format!("[]any{{{}}}", elems) // simple version +// } - _ => todo!(), - } -} +// _ => todo!(), +// } +// } -fn gen_type(t: &TypeExpr) -> String { - match t { - TypeExpr::Named(n) => match n.as_str() { - "int" => "int".into(), - "float" => "float64".into(), - "string" => "string".into(), - _ => n.clone(), - }, +// fn gen_type(t: &TypeExpr) -> String { +// match t { +// TypeExpr::Named(n) => match n.as_str() { +// "int" => "int".into(), +// "float" => "float64".into(), +// "string" => "string".into(), +// _ => n.clone(), +// }, - TypeExpr::Array(inner) => { - format!("[]{}", gen_type(inner)) - } +// TypeExpr::Array(inner) => { +// format!("[]{}", gen_type(inner)) +// } - TypeExpr::Optional(inner) => { - format!("*{}", gen_type(inner)) // pointer for optional - } - } -} +// TypeExpr::Optional(inner) => { +// format!("*{}", gen_type(inner)) // pointer for optional +// } +// } +// } -fn capitalize(s: &str) -> String { - let mut chars = s.chars(); - match chars.next() { - Some(c) => c.to_uppercase().collect::() + chars.as_str(), - None => String::new(), - } -} +// fn capitalize(s: &str) -> String { +// let mut chars = s.chars(); +// match chars.next() { +// Some(c) => c.to_uppercase().collect::() + chars.as_str(), +// None => String::new(), +// } +// } -fn op_to_str(op: &BinOperator) -> &'static str { - match op { - BinOperator::Add => "+", - BinOperator::Sub => "-", - BinOperator::Mul => "*", - BinOperator::Div => "/", - BinOperator::Eq => "==", - BinOperator::NotEq => "!=", - BinOperator::Lt => "<", - BinOperator::Gt => ">", - BinOperator::LtEq => "<=", - BinOperator::GtEq => ">=", - BinOperator::And => "&&", - BinOperator::Or => "||", - } -} +// fn op_to_str(op: &BinOperator) -> &'static str { +// match op { +// BinOperator::Add => "+", +// BinOperator::Sub => "-", +// BinOperator::Mul => "*", +// BinOperator::Div => "/", +// BinOperator::Eq => "==", +// BinOperator::NotEq => "!=", +// BinOperator::Lt => "<", +// BinOperator::Gt => ">", +// BinOperator::LtEq => "<=", +// BinOperator::GtEq => ">=", +// BinOperator::And => "&&", +// BinOperator::Or => "||", +// } +// } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 057fac0..e0de358 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,187 +1,32 @@ #[derive(Debug, Clone)] -pub struct Span { - pub start: usize, - pub end: usize, -} - -#[derive(Debug, Clone)] -pub struct Program { - pub statements: Vec, +pub enum TypeExpr { + Identifier(String), } #[derive(Debug, Clone)] pub enum TopLevel { - Function(Function), - Struct(Struct), - Class(Class), - Import(Import), -} - -#[derive(Debug, Clone)] -pub struct Function { - pub name: String, - pub params: Vec, - pub return_type: Option, - pub body: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Param { - pub name: String, - pub type_expr: TypeExpr, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Struct { - pub name: String, - pub fields: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct StructField { - pub name: String, - pub type_expr: TypeExpr, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Class { - pub name: String, - pub fields: Vec, - pub methods: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Import { - pub path: String, - pub span: Span, + Import(String), + StructDecl { + name: String, + fields: Vec<(String, TypeExpr)>, + }, + FunctionDecl { + name: String, + params: Vec<(String, TypeExpr)>, + return_type: Option, + body: Vec, + }, } #[derive(Debug, Clone)] pub enum Statement { - Let(LetStatement), - Return(ReturnStatement), - Expression(Expression), - If(IfStatement), - For(ForStatement), + Expression(String), } -#[derive(Debug, Clone)] -pub struct LetStatement { - pub name: String, - pub type_expr: Option, - pub value: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct ReturnStatement { - pub value: Option, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct IfStatement { - pub condition: Expression, - pub body: Vec, - pub else_body: Option>, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct ForStatement { - pub var: String, - pub iterator: Expression, - pub body: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] pub enum Expression { - Identifier(String, Span), - Integer(i64, Span), - Float(f64, Span), - StringLit(String, Span), - Bool(bool, Span), - BinaryOp(Box), - UnaryOp(Box), - Call(Box), - FieldAccess(Box), - StructInit(Box), - ArrayLiteral(Box), -} - -#[derive(Debug, Clone)] -pub struct BinaryOp { - pub left: Expression, - pub op: BinOperator, - pub right: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum BinOperator { - Add, - Sub, - Mul, - Div, - Eq, - NotEq, - Lt, - Gt, - LtEq, - GtEq, - And, - Or, -} - -#[derive(Debug, Clone)] -pub struct UnaryOp { - pub op: UnaryOperator, - pub expr: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum UnaryOperator { - Neg, - Not, -} - -#[derive(Debug, Clone)] -pub struct CallExpr { - pub callee: Expression, - pub args: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct FieldAccess { - pub object: Expression, - pub field: String, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct StructInit { - pub name: String, - pub fields: Vec<(String, Expression)>, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum TypeExpr { - Named(String), - Array(Box), - Optional(Box), -} - -#[derive(Debug, Clone)] -pub struct ArrayLiteral { - pub elements: Vec, - pub span: Span, + Identifier(String), + IntLiteral(i64), + FloatLiteral(f64), + BoolLiteral(bool), + StringLiteral(String), } diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 8a59ca2..b8c4418 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -2,7 +2,7 @@ WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } program = { SOI ~ item* ~ EOI } -item = _{ import_decl | function_decl | struct_decl | class_decl } +item = _{ import_decl | function_decl | struct_decl } // primitives identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } @@ -12,152 +12,26 @@ boolean = { "true" | "false" } string_lit = { "\"" ~ inner_str ~ "\"" } inner_str = @{ (!"\"" ~ ANY)* } -// ================= TYPE SCRIPT STYLE CHANGES ================= +// ================= TOP LEVEL ================= -// import becomes TS-like (still identical structure) import_decl = { "import" ~ string_lit ~ ";" } -// struct stays, but feels like TS "type object shape" -struct_decl = { +struct_field = { identifier ~ ":" ~ type_expr ~ ";" } +struct_decl = { "struct" ~ identifier ~ "{" ~ struct_field* ~ "}" } -// class already TS-like — just improved consistency -class_decl = { - "class" ~ identifier ~ "{" ~ class_member* ~ "}" -} - -class_member = _{ struct_field | function_decl } - -// function keyword changed feel: fn → function +param_list = { param ~ ("," ~ param)* ~ ","? } +param = { identifier ~ ":" ~ type_expr } function_decl = { "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ ("->" ~ type_expr)? ~ block } -// struct fields: TS-style semicolon consistency (already good) -struct_field = { identifier ~ ":" ~ type_expr ~ ";" } - -// params: TS optional typing style feel (unchanged structure) -param_list = { param ~ ("," ~ param)* ~ ","? } -param = { identifier ~ ":" ~ type_expr } - // ================= STATEMENTS ================= block = { "{" ~ statement* ~ "}" } -statement = _{ - let_stmt - | return_stmt - | if_stmt - | for_stmt - | assign_stmt - | expression_stmt -} - -// expression statements: TS allows no semicolon in real life, -// but we keep your structure -expression_stmt = { expression ~ ";" } - -// assignment unchanged (JS/TS feel already) -assign_stmt = { identifier ~ "=" ~ expression ~ ";" } - -// let → const/let feel (still your rule, just TS naming) -let_stmt = { - "const" ~ identifier ~ (":" ~ type_expr)? ~ "=" ~ expression ~ ";" -} - -// return: TS-like (no change except feel consistency) -return_stmt = { "return" ~ expression? ~ ";" } - -// if: add TS-style parentheses (MAJOR feel upgrade, same structure) -if_stmt = { - "if" ~ "(" ~ expression ~ ")" ~ block ~ ("else" ~ block)? -} - -// for: TS-like "of" -for_stmt = { - "for" ~ identifier ~ "of" ~ expression ~ block -} - -// ================= TYPES (KEEP STRUCTURE, TS FEEL) ================= - -type_expr = { base_type ~ "?"? } -base_type = _{ array_type | identifier } -array_type = { "[" ~ type_expr ~ "]" } +statement = _{ "idk" } // ================= EXPRESSIONS ================= - -expression = { term ~ (bin_op ~ term)* } - -bin_op = _{ - eq - | neq - | lte - | gte - | lt - | gt - | and - | or - | add - | sub - | mul - | div -} - -add = { "+" } -sub = { "-" } -mul = { "*" } -div = { "/" } -eq = { "==" } -neq = { "!=" } -lte = { "<=" } -gte = { ">=" } -lt = { "<" } -gt = { ">" } -and = { "&&" } -or = { "||" } - -// ================= LITERALS ================= - -struct_literal = { - identifier ~ "{" ~ struct_init_list? ~ "}" -} - -struct_init_list = { - struct_init_field ~ ("," ~ struct_init_field)* ~ ","? -} - -struct_init_field = { - identifier ~ ":" ~ expression -} - -array_literal = { - "[" ~ (expression ~ ("," ~ expression)*)? ~ ","? ~ "]" -} - -// ================= CHAINING ================= - -term = { primary ~ (field_access | call_suffix)* } - -field_access = { "." ~ identifier } - -call_suffix = { - "(" ~ (expression ~ ("," ~ expression)*)? ~ ","? ~ ")" -} - -// ================= PRIMARY ================= - -primary = _{ - struct_literal - | array_literal - | float - | integer - | string_lit - | boolean - | self_kw - | null_kw - | identifier -} - -self_kw = { "this" } -null_kw = { "null" } +type_expr = _{ identifier } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a8ad54b..faaa5e7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,5 +1,4 @@ use pest::Parser; -use pest::iterators::Pair; use pest_derive::Parser; pub mod ast; @@ -12,7 +11,7 @@ pub struct MistParser; // convenience alias for pest errors pub type ParseError = pest::error::Error; -pub fn parse(source: &str) -> Result { +pub fn parse(source: &str) -> Result, ParseError> { let pairs = MistParser::parse(Rule::program, source)?; let mut statements = vec![]; @@ -24,12 +23,10 @@ pub fn parse(source: &str) -> Result { Rule::program => { for inner in pair.into_inner() { match inner.as_rule() { - Rule::function_decl => { - statements.push(TopLevel::Function(parse_function(inner))) + Rule::import_decl => { + let path = inner.into_inner().next().unwrap().as_str().to_string(); + statements.push(TopLevel::Import(path)); } - Rule::struct_decl => statements.push(TopLevel::Struct(parse_struct(inner))), - Rule::class_decl => statements.push(TopLevel::Class(parse_class(inner))), - Rule::import_decl => statements.push(TopLevel::Import(parse_import(inner))), Rule::EOI => {} _ => {} } @@ -40,324 +37,5 @@ pub fn parse(source: &str) -> Result { } } - Ok(Program { statements }) -} - -fn span_of(pair: &Pair) -> Span { - let s = pair.as_span(); - Span { - start: s.start(), - end: s.end(), - } -} - -fn parse_function(pair: Pair) -> Function { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - - let name = inner.next().unwrap().as_str().to_string(); - - let mut params = vec![]; - let mut return_type = None; - let mut body = vec![]; - - for part in inner { - match part.as_rule() { - Rule::param_list => params = parse_param_list(part), - Rule::type_expr => return_type = Some(parse_type_expr(part)), - Rule::block => body = parse_block(part), - _ => {} - } - } - - Function { - name, - params, - return_type, - body, - span, - } -} - -fn parse_param_list(pair: Pair) -> Vec { - pair.into_inner() - .map(|p| { - let span = span_of(&p); - let mut inner = p.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let type_expr = parse_type_expr(inner.next().unwrap()); - Param { - name, - type_expr, - span, - } - }) - .collect() -} - -fn parse_struct(pair: Pair) -> Struct { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let fields = inner.map(|f| parse_struct_field(f)).collect(); - Struct { name, fields, span } -} - -fn parse_struct_field(pair: Pair) -> StructField { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let type_expr = parse_type_expr(inner.next().unwrap()); - StructField { - name, - type_expr, - span, - } -} - -fn parse_class(pair: Pair) -> Class { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let mut fields = vec![]; - let mut methods = vec![]; - - for part in inner { - match part.as_rule() { - Rule::struct_field => fields.push(parse_struct_field(part)), - Rule::function_decl => methods.push(parse_function(part)), - _ => {} - } - } - - Class { - name, - fields, - methods, - span, - } -} - -fn parse_import(pair: Pair) -> Import { - let span = span_of(&pair); - let path = pair.into_inner().next().unwrap().as_str().to_string(); - Import { path, span } -} - -fn parse_block(pair: Pair) -> Vec { - pair.into_inner() - .filter_map(|p| parse_statement(p)) - .collect() -} - -fn parse_statement(pair: Pair) -> Option { - match pair.as_rule() { - Rule::let_stmt => Some(Statement::Let(parse_let(pair))), - Rule::return_stmt => Some(Statement::Return(parse_return(pair))), - Rule::if_stmt => Some(Statement::If(parse_if(pair))), - Rule::for_stmt => Some(Statement::For(parse_for(pair))), - Rule::expression_stmt => { - let expr = pair.into_inner().next().unwrap(); - Some(Statement::Expression(parse_expression(expr))) - } - Rule::expression => Some(Statement::Expression(parse_expression(pair))), - _ => None, - } -} - -fn parse_let(pair: Pair) -> LetStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - - // peek ahead — next is either a type or an expression - let next = inner.next().unwrap(); - let (type_expr, value) = if next.as_rule() == Rule::type_expr { - ( - Some(parse_type_expr(next)), - parse_expression(inner.next().unwrap()), - ) - } else { - (None, parse_expression(next)) - }; - - LetStatement { - name, - type_expr, - value, - span, - } -} - -fn parse_return(pair: Pair) -> ReturnStatement { - let span = span_of(&pair); - let value = pair.into_inner().next().map(|p| parse_expression(p)); - ReturnStatement { value, span } -} - -fn parse_if(pair: Pair) -> IfStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let condition = parse_expression(inner.next().unwrap()); - let body = parse_block(inner.next().unwrap()); - let else_body = inner.next().map(|p| parse_block(p)); - IfStatement { - condition, - body, - else_body, - span, - } -} - -fn parse_for(pair: Pair) -> ForStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let var = inner.next().unwrap().as_str().to_string(); - let iterator = parse_expression(inner.next().unwrap()); - let body = parse_block(inner.next().unwrap()); - ForStatement { - var, - iterator, - body, - span, - } -} - -fn parse_expression(pair: Pair) -> Expression { - match pair.as_rule() { - Rule::expression => { - let mut inner = pair.into_inner(); - let mut expr = parse_term(inner.next().unwrap()); - - // consume pairs of (bin_op, term) - while let Some(op_pair) = inner.next() { - let right = parse_term(inner.next().unwrap()); - let span = span_of(&op_pair); - let op = match op_pair.as_rule() { - Rule::add => BinOperator::Add, - Rule::sub => BinOperator::Sub, - Rule::mul => BinOperator::Mul, - Rule::div => BinOperator::Div, - Rule::eq => BinOperator::Eq, - Rule::neq => BinOperator::NotEq, - Rule::lt => BinOperator::Lt, - Rule::gt => BinOperator::Gt, - Rule::lte => BinOperator::LtEq, - Rule::gte => BinOperator::GtEq, - Rule::and => BinOperator::And, - Rule::or => BinOperator::Or, - _ => unreachable!(), - }; - expr = Expression::BinaryOp(Box::new(BinaryOp { - left: expr, - op, - right, - span, - })); - } - - expr - } - _ => parse_term(pair), - } -} - -fn parse_term(pair: Pair) -> Expression { - let mut inner = pair.into_inner(); - let mut expr = parse_primary(inner.next().unwrap()); - - for part in inner { - let span = span_of(&part); - match part.as_rule() { - Rule::field_access => { - let field = part.into_inner().next().unwrap().as_str().to_string(); - expr = Expression::FieldAccess(Box::new(FieldAccess { - object: expr, - field, - span, - })); - } - Rule::call_suffix => { - let args = part.into_inner().map(|p| parse_expression(p)).collect(); - expr = Expression::Call(Box::new(CallExpr { - callee: expr, - args, - span, - })); - } - _ => {} - } - } - - expr -} - -fn parse_primary(pair: Pair) -> Expression { - let span = span_of(&pair); - - match pair.as_rule() { - Rule::struct_literal => { - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - - inner = inner.next().unwrap().into_inner(); - - // println!("{inner:#?}"); - - let mut fields = vec![]; - for field in inner { - let mut f_inner = field.into_inner(); - let field_name = f_inner.next().unwrap().as_str().to_string(); - let value = parse_expression(f_inner.next().unwrap()); - - fields.push((field_name, value)); - } - - Expression::StructInit(Box::new(StructInit { name, fields, span })) - } - - Rule::array_literal => { - let elements = pair.into_inner().map(|p| parse_expression(p)).collect(); - - Expression::ArrayLiteral(Box::new(ArrayLiteral { elements, span })) - } - - Rule::integer => Expression::Integer(pair.as_str().parse().unwrap(), span), - Rule::float => Expression::Float(pair.as_str().parse().unwrap(), span), - - Rule::string_lit => { - Expression::StringLit(pair.into_inner().next().unwrap().as_str().to_string(), span) - } - - Rule::boolean => Expression::Bool(pair.as_str() == "true", span), - - Rule::self_kw => Expression::Identifier("self".to_string(), span), - Rule::null_kw => Expression::Identifier("null".to_string(), span), - Rule::identifier => Expression::Identifier(pair.as_str().to_string(), span), - - Rule::term => parse_term(pair), - - _ => unreachable!("unexpected primary rule: {:?}", pair.as_rule()), - } -} - -fn parse_type_expr(pair: Pair) -> TypeExpr { - let mut inner = pair.into_inner(); - let base = inner.next().unwrap(); - - let base_type = match base.as_rule() { - Rule::array_type => { - let inner_type = parse_type_expr(base.into_inner().next().unwrap()); - TypeExpr::Array(Box::new(inner_type)) - } - Rule::identifier => TypeExpr::Named(base.as_str().to_string()), - _ => unreachable!(), - }; - - // if a "?" suffix was present, wrap in Optional - if inner.next().is_some() { - TypeExpr::Optional(Box::new(base_type)) - } else { - base_type - } + Ok(statements) } From 511895d0f84c7d709dfb3f6aefb29a0d509b05b7 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:23:37 +0200 Subject: [PATCH 02/17] Testing simple grammar --- .gitignore | 3 ++- src/main.rs | 27 +++++++++++++++++++++++++++ src/parser/ast.rs | 13 ++++++++++--- src/parser/grammar.pest | 11 +++-------- test/src/main.ms | 8 +------- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 7e6aa69..7811c03 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ target /target -/test/build \ No newline at end of file +/test/build +/test/output.json \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 56fd64a..d2d17ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,13 @@ fn main() { } cmd_check(&args[2]); } + "parse" => { + if args.len() < 3 { + eprintln!("error: expected a file path\n usage: mist check "); + process::exit(1); + } + cmd_parse(&args[2]); + } "version" | "--version" | "-v" => { println!("mist {}", env!("CARGO_PKG_VERSION")); } @@ -61,6 +68,26 @@ fn cmd_check(path: &str) { } } +fn cmd_parse(path: &str) { + let source = read_ms_file(path); + match parser::parse(&source) { + Ok(output) => { + fs::write( + "output.json", + serde_json::to_string_pretty(&output).unwrap(), + ) + .unwrap_or_else(|e| { + eprintln!("error: could not write output.json: {}", e); + process::exit(1); + }); + } + Err(e) => { + eprintln!("parse error:\n{}", e); + process::exit(1); + } + } +} + fn read_ms_file(path: &str) -> String { let pb = PathBuf::from(path); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index e0de358..80e3ad8 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,9 +1,11 @@ -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] pub enum TypeExpr { Identifier(String), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub enum TopLevel { Import(String), StructDecl { @@ -18,15 +20,20 @@ pub enum TopLevel { }, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub enum Statement { Expression(String), } +#[derive(Debug, Clone, Serialize)] pub enum Expression { Identifier(String), IntLiteral(i64), FloatLiteral(f64), BoolLiteral(bool), StringLiteral(String), + FunctionCall { + callee: Box, + args: Vec, + }, } diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index b8c4418..23f246c 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -2,7 +2,7 @@ WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } program = { SOI ~ item* ~ EOI } -item = _{ import_decl | function_decl | struct_decl } +item = _{ import_decl | function_decl } // primitives identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } @@ -16,20 +16,15 @@ inner_str = @{ (!"\"" ~ ANY)* } import_decl = { "import" ~ string_lit ~ ";" } -struct_field = { identifier ~ ":" ~ type_expr ~ ";" } -struct_decl = { - "struct" ~ identifier ~ "{" ~ struct_field* ~ "}" -} - param_list = { param ~ ("," ~ param)* ~ ","? } param = { identifier ~ ":" ~ type_expr } function_decl = { - "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ ("->" ~ type_expr)? ~ block + "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block } // ================= STATEMENTS ================= -block = { "{" ~ statement* ~ "}" } +block = { "{" ~ "}" } statement = _{ "idk" } diff --git a/test/src/main.ms b/test/src/main.ms index ce922de..9e8fa56 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,9 +1,3 @@ import "fmt"; -function add(a: int, b: int) -> int { - return a + b; -} - -function main() { - fmt.Println(add(10, 10) / 2); -} \ No newline at end of file +function add(a: int, b: int): int {} From 5b01171ee4c6b3ddacb5ed05e172bb24a008c93e Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:28:15 +0200 Subject: [PATCH 03/17] Expression parsing --- src/parser/grammar.pest | 6 +++++- src/parser/mod.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 23f246c..53e39ea 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -14,12 +14,14 @@ inner_str = @{ (!"\"" ~ ANY)* } // ================= TOP LEVEL ================= +export = { "export"? } + import_decl = { "import" ~ string_lit ~ ";" } param_list = { param ~ ("," ~ param)* ~ ","? } param = { identifier ~ ":" ~ type_expr } function_decl = { - "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block + export ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block } // ================= STATEMENTS ================= @@ -30,3 +32,5 @@ statement = _{ "idk" } // ================= EXPRESSIONS ================= type_expr = _{ identifier } + +expr = { identifier | integer | float | boolean | string_lit } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index faaa5e7..0aa6a79 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -39,3 +39,32 @@ pub fn parse(source: &str) -> Result, ParseError> { Ok(statements) } + +impl Expression { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::expr => { + let inner = pair.into_inner().next().unwrap(); + Expression::from_pair(inner) + } + Rule::identifier => Expression::Identifier(pair.as_str().to_string()), + Rule::integer => { + let value = pair.as_str().parse::().unwrap(); + Expression::IntLiteral(value) + } + Rule::float => { + let value = pair.as_str().parse::().unwrap(); + Expression::FloatLiteral(value) + } + Rule::boolean => { + let value = pair.as_str().parse::().unwrap(); + Expression::BoolLiteral(value) + } + Rule::string_lit => { + let inner_str = pair.into_inner().next().unwrap().as_str(); + Expression::StringLiteral(inner_str.to_string()) + } + _ => unimplemented!("Expression parsing not implemented yet"), + } + } +} From 9a5511018303ac1790c527ad400cc2fe187d7d87 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:38:52 +0200 Subject: [PATCH 04/17] Simple parser --- src/parser/grammar.pest | 15 ++++--- src/parser/mod.rs | 92 +++++++++++++++++++++++++++++++++-------- test/src/main.ms | 4 +- 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 53e39ea..032281a 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -1,8 +1,7 @@ WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } -program = { SOI ~ item* ~ EOI } -item = _{ import_decl | function_decl } +program = { SOI ~ top_level* ~ EOI } // primitives identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } @@ -14,9 +13,11 @@ inner_str = @{ (!"\"" ~ ANY)* } // ================= TOP LEVEL ================= +top_level = _{ import | function_decl } + export = { "export"? } -import_decl = { "import" ~ string_lit ~ ";" } +import = { "import" ~ string_lit ~ ";" } param_list = { param ~ ("," ~ param)* ~ ","? } param = { identifier ~ ":" ~ type_expr } @@ -26,9 +27,13 @@ function_decl = { // ================= STATEMENTS ================= -block = { "{" ~ "}" } +block = { "{" ~ statement_list ~ "}" } -statement = _{ "idk" } +statement_list = { statement* } + +statement = _{ expr_stmt } + +expr_stmt = { expr ~ ";" } // ================= EXPRESSIONS ================= type_expr = _{ identifier } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0aa6a79..1b3f177 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -16,30 +16,86 @@ pub fn parse(source: &str) -> Result, ParseError> { let mut statements = vec![]; - // pairs is an iterator over the top-level program pair - // we need to get its inner children for pair in pairs { - match pair.as_rule() { - Rule::program => { - for inner in pair.into_inner() { - match inner.as_rule() { - Rule::import_decl => { - let path = inner.into_inner().next().unwrap().as_str().to_string(); - statements.push(TopLevel::Import(path)); - } - Rule::EOI => {} - _ => {} - } - } - } - Rule::EOI => {} - _ => {} - } + statements.push(TopLevel::from_pair(pair)); } Ok(statements) } +impl TypeExpr { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::type_expr => { + let inner = pair.into_inner().next().unwrap(); + TypeExpr::from_pair(inner) + } + Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), + _ => unimplemented!("TypeExpr parsing not implemented yet"), + } + } +} + +impl TopLevel { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::program => { + let inner = pair.into_inner().next().unwrap(); + TopLevel::from_pair(inner) + } + Rule::import => { + let path = pair.into_inner().next().unwrap().as_str().to_string(); + TopLevel::Import(path) + } + Rule::function_decl => { + 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 name = inner.next().unwrap().as_str().to_string(); + let params_pair = inner.next().unwrap(); + let params = if params_pair.as_rule() == Rule::param_list { + params_pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); + (param_name, param_type) + }) + .collect() + } else { + vec![] + }; + let return_type = if let Some(next) = inner.peek() { + if next.as_rule() == Rule::type_expr { + Some(TypeExpr::from_pair(inner.next().unwrap())) + } else { + None + } + } else { + None + }; + // For now, we'll just ignore the function body and return an empty vector + TopLevel::FunctionDecl { + name, + params, + return_type, + body: vec![], + } + } + _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + impl Expression { pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { diff --git a/test/src/main.ms b/test/src/main.ms index 9e8fa56..bcc1da0 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,3 +1,5 @@ import "fmt"; -function add(a: int, b: int): int {} +function add(a: int, b: int): int { + hello; +} From d04fd4d2b24c30a323f7b2bba01588385dfe1a2e Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:45:41 +0200 Subject: [PATCH 05/17] Working function parsing --- src/parser/ast.rs | 4 ++++ src/parser/mod.rs | 23 ++++++++++++----------- test/src/main.ms | 4 +--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 80e3ad8..fb249e2 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,11 +1,13 @@ use serde::Serialize; #[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] pub enum TypeExpr { Identifier(String), } #[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] pub enum TopLevel { Import(String), StructDecl { @@ -21,11 +23,13 @@ pub enum TopLevel { } #[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] pub enum Statement { Expression(String), } #[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] pub enum Expression { Identifier(String), IntLiteral(i64), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1b3f177..02c4a55 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12,12 +12,14 @@ pub struct MistParser; pub type ParseError = pest::error::Error; pub fn parse(source: &str) -> Result, ParseError> { - let pairs = MistParser::parse(Rule::program, source)?; + let mut pairs = MistParser::parse(Rule::program, source)?; let mut statements = vec![]; - for pair in pairs { - statements.push(TopLevel::from_pair(pair)); + for pair in pairs.next().unwrap().into_inner() { + if let Some(stmt) = TopLevel::from_pair(pair) { + statements.push(stmt); + } } Ok(statements) @@ -37,15 +39,11 @@ impl TypeExpr { } impl TopLevel { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { + pub fn from_pair(pair: pest::iterators::Pair) -> Option { match pair.as_rule() { - Rule::program => { - let inner = pair.into_inner().next().unwrap(); - TopLevel::from_pair(inner) - } Rule::import => { let path = pair.into_inner().next().unwrap().as_str().to_string(); - TopLevel::Import(path) + Some(TopLevel::Import(path)) } Rule::function_decl => { let mut inner = pair.into_inner(); @@ -83,14 +81,17 @@ impl TopLevel { } else { None }; + // For now, we'll just ignore the function body and return an empty vector - TopLevel::FunctionDecl { + Some(TopLevel::FunctionDecl { name, params, return_type, body: vec![], - } + }) } + + Rule::EOI => None, _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), } } diff --git a/test/src/main.ms b/test/src/main.ms index bcc1da0..9e8fa56 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,5 +1,3 @@ import "fmt"; -function add(a: int, b: int): int { - hello; -} +function add(a: int, b: int): int {} From 00569da11d4320d4616a2b2260cd5858c56fb833 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:58:51 +0200 Subject: [PATCH 06/17] Struct decleration --- src/parser/ast.rs | 7 ++++-- src/parser/grammar.pest | 11 +++++++--- src/parser/mod.rs | 47 ++++++++++++++++++++++++++++++++--------- test/src/main.ms | 5 +++++ 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index fb249e2..55dc6bf 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -12,11 +12,11 @@ pub enum TopLevel { Import(String), StructDecl { name: String, - fields: Vec<(String, TypeExpr)>, + fields: ParamList, }, FunctionDecl { name: String, - params: Vec<(String, TypeExpr)>, + params: ParamList, return_type: Option, body: Vec, }, @@ -41,3 +41,6 @@ pub enum Expression { args: Vec, }, } + +#[derive(Debug, Clone, Serialize)] +pub struct ParamList(pub Vec<(String, TypeExpr)>); diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 032281a..4fac1c4 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -13,14 +13,19 @@ inner_str = @{ (!"\"" ~ ANY)* } // ================= TOP LEVEL ================= -top_level = _{ import | function_decl } +top_level = _{ import | function_decl | struct_decl } export = { "export"? } import = { "import" ~ string_lit ~ ";" } -param_list = { param ~ ("," ~ param)* ~ ","? } -param = { identifier ~ ":" ~ type_expr } +param_list = { param ~ ("," ~ param)* ~ ","? } +param = { identifier ~ ":" ~ type_expr } + +struct_decl = { + export ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" +} + function_decl = { export ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 02c4a55..935f198 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -38,6 +38,22 @@ impl TypeExpr { } } +impl ParamList { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + let params = pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); + (param_name, param_type) + }) + .collect(); + + ParamList(params) + } +} + impl TopLevel { pub fn from_pair(pair: pest::iterators::Pair) -> Option { match pair.as_rule() { @@ -60,17 +76,9 @@ impl TopLevel { let name = inner.next().unwrap().as_str().to_string(); let params_pair = inner.next().unwrap(); let params = if params_pair.as_rule() == Rule::param_list { - params_pair - .into_inner() - .map(|p| { - let mut param_inner = p.into_inner(); - let param_name = param_inner.next().unwrap().as_str().to_string(); - let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); - (param_name, param_type) - }) - .collect() + ParamList::from_pair(params_pair) } else { - vec![] + ParamList(vec![]) }; let return_type = if let Some(next) = inner.peek() { if next.as_rule() == Rule::type_expr { @@ -91,6 +99,25 @@ impl TopLevel { }) } + Rule::struct_decl => { + 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 name = inner.next().unwrap().as_str().to_string(); + let fields_pair = inner.next().unwrap(); + let fields = ParamList::from_pair(fields_pair); + + Some(TopLevel::StructDecl { name, fields }) + } + Rule::EOI => None, _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), } diff --git a/test/src/main.ms b/test/src/main.ms index 9e8fa56..4cdfd6a 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,3 +1,8 @@ import "fmt"; +struct Point { + x: int, + y: int +} + function add(a: int, b: int): int {} From 2aef4c20be23b94525e075e9a41f6dbfae3aac47 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 02:59:45 +0200 Subject: [PATCH 07/17] Export parsing --- src/parser/ast.rs | 2 ++ src/parser/mod.rs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 55dc6bf..28939c3 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -11,10 +11,12 @@ pub enum TypeExpr { pub enum TopLevel { Import(String), StructDecl { + export: bool, name: String, fields: ParamList, }, FunctionDecl { + export: bool, name: String, params: ParamList, return_type: Option, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 935f198..ead59f9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -92,6 +92,7 @@ impl TopLevel { // For now, we'll just ignore the function body and return an empty vector Some(TopLevel::FunctionDecl { + export, name, params, return_type, @@ -115,7 +116,11 @@ impl TopLevel { let fields_pair = inner.next().unwrap(); let fields = ParamList::from_pair(fields_pair); - Some(TopLevel::StructDecl { name, fields }) + Some(TopLevel::StructDecl { + export, + name, + fields, + }) } Rule::EOI => None, From 9ced12933a83e8eedeceb2fe1f3006b1e888f4b7 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 03:22:35 +0200 Subject: [PATCH 08/17] Body parsing --- src/parser/ast.rs | 7 +++++-- src/parser/grammar.pest | 8 ++++---- src/parser/mod.rs | 41 +++++++++++++++++++++++++++++++++++++++-- test/src/main.ms | 4 +++- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 28939c3..3978138 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -20,14 +20,14 @@ pub enum TopLevel { name: String, params: ParamList, return_type: Option, - body: Vec, + body: Block, }, } #[derive(Debug, Clone, Serialize)] #[serde(tag = "type", content = "value")] pub enum Statement { - Expression(String), + Expression(Expression), } #[derive(Debug, Clone, Serialize)] @@ -46,3 +46,6 @@ pub enum Expression { #[derive(Debug, Clone, Serialize)] pub struct ParamList(pub Vec<(String, TypeExpr)>); + +#[derive(Debug, Clone, Serialize)] +pub struct Block(pub Vec); diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 4fac1c4..9c9afd5 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -15,7 +15,7 @@ inner_str = @{ (!"\"" ~ ANY)* } top_level = _{ import | function_decl | struct_decl } -export = { "export"? } +export = { "export" } import = { "import" ~ string_lit ~ ";" } @@ -23,11 +23,11 @@ param_list = { param ~ ("," ~ param)* ~ ","? } param = { identifier ~ ":" ~ type_expr } struct_decl = { - export ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" + export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" } function_decl = { - export ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block + export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block } // ================= STATEMENTS ================= @@ -41,6 +41,6 @@ statement = _{ expr_stmt } expr_stmt = { expr ~ ";" } // ================= EXPRESSIONS ================= -type_expr = _{ identifier } +type_expr = { identifier } expr = { identifier | integer | float | boolean | string_lit } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ead59f9..9477df1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -63,6 +63,7 @@ impl TopLevel { } Rule::function_decl => { let mut inner = pair.into_inner(); + let export = if let Some(first) = inner.peek() { if first.as_rule() == Rule::export { inner.next(); @@ -90,13 +91,14 @@ impl TopLevel { None }; - // For now, we'll just ignore the function body and return an empty vector + let body = Block::from_pair(inner.next().unwrap()); + Some(TopLevel::FunctionDecl { export, name, params, return_type, - body: vec![], + body, }) } @@ -129,6 +131,41 @@ impl TopLevel { } } +impl Block { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + let statements = pair + .into_inner() + .flat_map(|pair| { + if pair.as_rule() == Rule::statement_list { + pair.into_inner().map(Statement::from_pair).collect() + } else { + vec![Statement::from_pair(pair)] + } + }) + .collect(); + Block(statements) + } +} + +impl Statement { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::statement => { + let inner = pair.into_inner().next().unwrap(); + Statement::from_pair(inner) + } + Rule::expr_stmt => { + let expr_pair = pair.into_inner().next().unwrap(); + Statement::Expression(Expression::from_pair(expr_pair)) + } + _ => unimplemented!( + "Statement parsing not implemented yet: {:?}", + pair.as_rule() + ), + } + } +} + impl Expression { pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { diff --git a/test/src/main.ms b/test/src/main.ms index 4cdfd6a..3932a50 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -5,4 +5,6 @@ struct Point { y: int } -function add(a: int, b: int): int {} +function add(a: int, b: int): int { + do_something; +} From a914892fc1719c8952ce559d2d24e9aed93eb9cf Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 03:31:13 +0200 Subject: [PATCH 09/17] Function call with var path --- src/parser/grammar.pest | 19 ++++++++++++++++++- src/parser/mod.rs | 19 +++++++++++++++++-- test/src/main.ms | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 9c9afd5..8c62804 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -43,4 +43,21 @@ expr_stmt = { expr ~ ";" } // ================= EXPRESSIONS ================= type_expr = { identifier } -expr = { identifier | integer | float | boolean | string_lit } +var_path = { identifier ~ ("." ~ identifier)* } + +term = { + integer + | float + | boolean + | string_lit + | var_path +} + +func_call = { + var_path ~ "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" +} + +expr = { + func_call + | term +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9477df1..22721b4 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -169,10 +169,11 @@ impl Statement { impl Expression { pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::expr => { + Rule::expr | Rule::term => { let inner = pair.into_inner().next().unwrap(); Expression::from_pair(inner) } + Rule::var_path => Expression::Identifier(pair.as_str().to_string()), Rule::identifier => Expression::Identifier(pair.as_str().to_string()), Rule::integer => { let value = pair.as_str().parse::().unwrap(); @@ -190,7 +191,21 @@ impl Expression { let inner_str = pair.into_inner().next().unwrap().as_str(); Expression::StringLiteral(inner_str.to_string()) } - _ => unimplemented!("Expression parsing not implemented yet"), + + Rule::func_call => { + let mut inner = pair.into_inner(); + let callee = Expression::from_pair(inner.next().unwrap()); + let args = inner.map(Expression::from_pair).collect(); + Expression::FunctionCall { + callee: Box::new(callee), + args, + } + } + + _ => unimplemented!( + "Expression parsing not implemented yet {:?}", + pair.as_rule() + ), } } } diff --git a/test/src/main.ms b/test/src/main.ms index 3932a50..eb93632 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -6,5 +6,5 @@ struct Point { } function add(a: int, b: int): int { - do_something; + idk.do_something(); } From 6c46c1f0bb7c4574fb5bda6d6d6fec350e39b91b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 04:14:46 +0200 Subject: [PATCH 10/17] Postfix --- src/parser/ast.rs | 13 +++++++++--- src/parser/grammar.pest | 14 ++++++------ src/parser/mod.rs | 47 +++++++++++++++++++++++++++++------------ test/src/main.ms | 2 +- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 3978138..9e63b3b 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -38,12 +38,19 @@ pub enum Expression { FloatLiteral(f64), BoolLiteral(bool), StringLiteral(String), - FunctionCall { - callee: Box, - args: Vec, + Postfix { + initial: Box, + postfixes: Vec, }, } +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum Postfix { + FieldAccess(String), + FunctionCall(Vec), +} + #[derive(Debug, Clone, Serialize)] pub struct ParamList(pub Vec<(String, TypeExpr)>); diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 8c62804..5258991 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -43,21 +43,19 @@ expr_stmt = { expr ~ ";" } // ================= EXPRESSIONS ================= type_expr = { identifier } -var_path = { identifier ~ ("." ~ identifier)* } - -term = { +primary = { integer | float | boolean | string_lit - | var_path + | identifier } -func_call = { - var_path ~ "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" +postfix = { + ("." ~ identifier) + | ("(" ~ (expr ~ ("," ~ expr)*)? ~ ")") } expr = { - func_call - | term + primary ~ postfix* } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 22721b4..60af79d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -169,11 +169,20 @@ impl Statement { impl Expression { pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::expr | Rule::term => { - let inner = pair.into_inner().next().unwrap(); - Expression::from_pair(inner) + Rule::expr => { + let mut inner = pair.into_inner(); + let exp = Expression::from_pair(inner.next().unwrap()); + + if inner.len() > 0 { + Expression::Postfix { + initial: Box::new(exp), + postfixes: inner.map(|p| Postfix::from_pair(p)).collect(), + } + } else { + exp + } } - Rule::var_path => Expression::Identifier(pair.as_str().to_string()), + Rule::primary => Expression::from_pair(pair.into_inner().next().unwrap()), Rule::identifier => Expression::Identifier(pair.as_str().to_string()), Rule::integer => { let value = pair.as_str().parse::().unwrap(); @@ -192,16 +201,6 @@ impl Expression { Expression::StringLiteral(inner_str.to_string()) } - Rule::func_call => { - let mut inner = pair.into_inner(); - let callee = Expression::from_pair(inner.next().unwrap()); - let args = inner.map(Expression::from_pair).collect(); - Expression::FunctionCall { - callee: Box::new(callee), - args, - } - } - _ => unimplemented!( "Expression parsing not implemented yet {:?}", pair.as_rule() @@ -209,3 +208,23 @@ impl Expression { } } } + +impl Postfix { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::postfix => { + let inner = pair.into_inner(); + if let Some(field) = inner.peek() { + if field.as_rule() == Rule::identifier { + Postfix::FieldAccess(field.as_str().to_string()) + } else { + Postfix::FunctionCall(inner.map(|p| Expression::from_pair(p)).collect()) + } + } else { + Postfix::FunctionCall(vec![]) + } + } + _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), + } + } +} diff --git a/test/src/main.ms b/test/src/main.ms index eb93632..2e183dd 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -6,5 +6,5 @@ struct Point { } function add(a: int, b: int): int { - idk.do_something(); + idk.do_something(123, "abc"); } From 3223a8c85495617c080b91f12c59a3f5aec376f6 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 04:38:56 +0200 Subject: [PATCH 11/17] Statements --- src/parser/ast.rs | 71 +++++++++++++++++++++++--- src/parser/grammar.pest | 48 +++++++++++++++++- src/parser/mod.rs | 108 ++++++++++++++++++++++++++++++++++++++++ test/src/main.ms | 4 +- 4 files changed, 221 insertions(+), 10 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9e63b3b..35bfca5 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,5 +1,11 @@ use serde::Serialize; +#[derive(Debug, Clone, Serialize)] +pub struct ParamList(pub Vec<(String, TypeExpr)>); + +#[derive(Debug, Clone, Serialize)] +pub struct Block(pub Vec); + #[derive(Debug, Clone, Serialize)] #[serde(tag = "type", content = "value")] pub enum TypeExpr { @@ -24,10 +30,55 @@ pub enum TopLevel { }, } +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum Postfix { + FieldAccess(String), + FunctionCall(Vec), +} + #[derive(Debug, Clone, Serialize)] #[serde(tag = "type", content = "value")] pub enum Statement { + // expr; Expression(Expression), + + // { ... } + Block(Block), + + // let/const/var x = ... + VarDecl { + kind: VarKind, + name: String, + init: Option, + }, + + // if (...) stmt else stmt + If { + condition: Expression, + then_branch: Box, + else_branch: Option>, + }, + + // while (...) stmt + While { + condition: Expression, + body: Box, + }, + + // for (...) stmt + For { + init: Option, + condition: Option, + update: Option, + body: Box, + }, + + // return expr?; + Return(Option), + + Break, + Continue, } #[derive(Debug, Clone, Serialize)] @@ -45,14 +96,18 @@ pub enum Expression { } #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] -pub enum Postfix { - FieldAccess(String), - FunctionCall(Vec), +pub enum VarKind { + Let, + Const, + Var, } #[derive(Debug, Clone, Serialize)] -pub struct ParamList(pub Vec<(String, TypeExpr)>); - -#[derive(Debug, Clone, Serialize)] -pub struct Block(pub Vec); +pub enum ForInit { + VarDecl { + kind: VarKind, + name: String, + init: Option, + }, + Expr(Expression), +} diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 5258991..4723880 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -36,10 +36,56 @@ block = { "{" ~ statement_list ~ "}" } statement_list = { statement* } -statement = _{ expr_stmt } +statement = _{ + expr_stmt + | var_decl + | if_stmt + | while_stmt + | for_stmt + | return_stmt + | break_stmt + | continue_stmt + | block +} + +// --- basic statements --- expr_stmt = { expr ~ ";" } +return_stmt = { "return" ~ expr? ~ ";" } + +break_stmt = { "break" ~ ";" } + +continue_stmt = { "continue" ~ ";" } + +// --- variable declarations --- + +var_decl = { + ("let" | "const" | "var") ~ identifier ~ ("=" ~ expr)? ~ ";" +} + +// --- control flow --- + +if_stmt = { + "if" ~ "(" ~ expr ~ ")" ~ statement ~ ("else" ~ statement)? +} + +while_stmt = { + "while" ~ "(" ~ expr ~ ")" ~ statement +} + +for_stmt = { + "for" ~ "(" ~ (var_decl_no_semicolon | expr_stmt | ";") + ~ expr? ~ ";" + ~ expr? ~ ")" + ~ statement +} + +// helper (same as var_decl but no trailing ;) +var_decl_no_semicolon = { + ("let" | "const" | "var") ~ identifier ~ ("=" ~ expr)? +} + // ================= EXPRESSIONS ================= type_expr = { identifier } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 60af79d..f5e2e1e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -154,10 +154,118 @@ impl Statement { let inner = pair.into_inner().next().unwrap(); Statement::from_pair(inner) } + Rule::expr_stmt => { let expr_pair = pair.into_inner().next().unwrap(); Statement::Expression(Expression::from_pair(expr_pair)) } + + Rule::block => Statement::Block(Block::from_pair(pair.into_inner().next().unwrap())), + + Rule::var_decl => { + let mut inner = pair.into_inner(); + + let kind_pair = inner.next().unwrap(); // let/const/var + let name_pair = inner.next().unwrap(); // identifier + + let init = inner.next().map(|expr_pair| { + // expects "=" expr + Expression::from_pair(expr_pair.into_inner().next().unwrap()) + }); + + let kind = match kind_pair.as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, + _ => unreachable!(), + }; + + Statement::VarDecl { + kind, + name: name_pair.as_str().to_string(), + init, + } + } + + Rule::return_stmt => { + let mut inner = pair.into_inner(); + + let expr = inner.next().map(Expression::from_pair); + + Statement::Return(expr) + } + + Rule::break_stmt => Statement::Break, + + Rule::continue_stmt => Statement::Continue, + + Rule::if_stmt => { + let mut inner = pair.into_inner(); + + let condition = Expression::from_pair(inner.next().unwrap()); + let then_branch = Statement::from_pair(inner.next().unwrap()); + + let else_branch = inner.next().map(Statement::from_pair); + + Statement::If { + condition, + then_branch: Box::new(then_branch), + else_branch: else_branch.map(Box::new), + } + } + + Rule::while_stmt => { + let mut inner = pair.into_inner(); + + let condition = Expression::from_pair(inner.next().unwrap()); + let body = Statement::from_pair(inner.next().unwrap()); + + Statement::While { + condition, + body: Box::new(body), + } + } + + Rule::for_stmt => { + let mut inner = pair.into_inner(); + + let init = inner.next().map(|p| match p.as_rule() { + Rule::var_decl_no_semicolon => { + let mut it = p.into_inner(); + + let kind = match it.next().unwrap().as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, + _ => unreachable!(), + }; + + let name = it.next().unwrap().as_str().to_string(); + let init_expr = it + .next() + .map(|e| Expression::from_pair(e.into_inner().next().unwrap())); + + ForInit::VarDecl { + kind, + name, + init: init_expr, + } + } + _ => ForInit::Expr(Expression::from_pair(p)), + }); + + let condition = inner.next().map(Expression::from_pair); + let update = inner.next().map(Expression::from_pair); + let body = Statement::from_pair(inner.next().unwrap()); + + Statement::For { + init, + condition, + update, + body: Box::new(body), + } + } + _ => unimplemented!( "Statement parsing not implemented yet: {:?}", pair.as_rule() diff --git a/test/src/main.ms b/test/src/main.ms index 2e183dd..77c55b6 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -6,5 +6,7 @@ struct Point { } function add(a: int, b: int): int { - idk.do_something(123, "abc"); + if (x) { + if (y) return 1; + } } From 5a5258297759758adfed2604beb8e5a781ac6c12 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 04:47:53 +0200 Subject: [PATCH 12/17] Opertators --- src/parser/grammar.pest | 26 +++++++++++++------------- test/src/main.ms | 11 +---------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 4723880..a91c38d 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -37,15 +37,15 @@ block = { "{" ~ statement_list ~ "}" } statement_list = { statement* } statement = _{ - expr_stmt - | var_decl - | if_stmt - | while_stmt - | for_stmt - | return_stmt - | break_stmt - | continue_stmt - | block + expr_stmt + | var_decl + | if_stmt + | while_stmt + | for_stmt + | return_stmt + | break_stmt + | continue_stmt + | block } // --- basic statements --- @@ -75,10 +75,7 @@ while_stmt = { } for_stmt = { - "for" ~ "(" ~ (var_decl_no_semicolon | expr_stmt | ";") - ~ expr? ~ ";" - ~ expr? ~ ")" - ~ statement + "for" ~ "(" ~ (var_decl_no_semicolon | expr_stmt | ";") ~ expr? ~ ";" ~ expr? ~ ")" ~ statement } // helper (same as var_decl but no trailing ;) @@ -100,6 +97,9 @@ primary = { postfix = { ("." ~ identifier) | ("(" ~ (expr ~ ("," ~ expr)*)? ~ ")") + | ("[" ~ expr ~ "]") + | ("++" | "--") + | (("+" | "-" | "*" | "/" | "%") ~ primary) } expr = { diff --git a/test/src/main.ms b/test/src/main.ms index 77c55b6..2e358b8 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,12 +1,3 @@ -import "fmt"; - -struct Point { - x: int, - y: int -} - function add(a: int, b: int): int { - if (x) { - if (y) return 1; - } + itm.a + itm.b(); } From 7b8010abbb6e21febc30c737baaa35063bfb4aee Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 05:03:12 +0200 Subject: [PATCH 13/17] Working binary operations --- src/parser/ast.rs | 12 +++++++++++- src/parser/grammar.pest | 15 ++++++++++----- src/parser/mod.rs | 38 +++++++++++++++++++++++++++----------- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 35bfca5..af4f8b8 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -12,6 +12,14 @@ pub enum TypeExpr { Identifier(String), } +#[derive(Debug, Clone, Serialize)] +pub enum BinaryOp { + Plus, + Minus, + Multiply, + Divide, +} + #[derive(Debug, Clone, Serialize)] #[serde(tag = "type", content = "value")] pub enum TopLevel { @@ -34,7 +42,9 @@ pub enum TopLevel { #[serde(tag = "type", content = "value")] pub enum Postfix { FieldAccess(String), - FunctionCall(Vec), + Call(Vec), + Index(Expression), + Binary(BinaryOp, Expression), } #[derive(Debug, Clone, Serialize)] diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index a91c38d..08824db 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -95,13 +95,18 @@ primary = { } postfix = { - ("." ~ identifier) - | ("(" ~ (expr ~ ("," ~ expr)*)? ~ ")") - | ("[" ~ expr ~ "]") - | ("++" | "--") - | (("+" | "-" | "*" | "/" | "%") ~ primary) + field_px + | call_px + | index_px + | binary_px } +field_px = { "." ~ identifier } +call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } +index_px = { "[" ~ expr ~ "]" } +binary_px = { bin_op ~ expr } +bin_op = { "+" | "-" | "*" | "/" | "%" } + expr = { primary ~ postfix* } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f5e2e1e..6b14585 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -320,18 +320,34 @@ impl Expression { impl Postfix { pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::postfix => { - let inner = pair.into_inner(); - if let Some(field) = inner.peek() { - if field.as_rule() == Rule::identifier { - Postfix::FieldAccess(field.as_str().to_string()) - } else { - Postfix::FunctionCall(inner.map(|p| Expression::from_pair(p)).collect()) - } - } else { - Postfix::FunctionCall(vec![]) - } + Rule::postfix => Postfix::from_pair(pair.into_inner().next().unwrap()), + + Rule::field_px => { + let field_name = pair.into_inner().next().unwrap().as_str().to_string(); + Postfix::FieldAccess(field_name) } + + Rule::call_px => Postfix::Call(pair.into_inner().map(Expression::from_pair).collect()), + + Rule::index_px => { + Postfix::Index(Expression::from_pair(pair.into_inner().next().unwrap())) + } + + Rule::binary_px => { + let mut inner = pair.into_inner(); + let op_pair = inner.next().unwrap(); + let op = match op_pair.as_str() { + "+" => BinaryOp::Plus, + "-" => BinaryOp::Minus, + "*" => BinaryOp::Multiply, + "/" => BinaryOp::Divide, + _ => { + unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) + } + }; + Postfix::Binary(op, Expression::from_pair(inner.next().unwrap())) + } + _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), } } From b2fa1e24e118bdb68feb8d7d656880bba3bc3bb7 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 05:07:45 +0200 Subject: [PATCH 14/17] More binary operators --- src/parser/ast.rs | 7 +++++++ src/parser/grammar.pest | 2 +- src/parser/mod.rs | 8 ++++++++ test/src/main.ms | 16 +++++++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index af4f8b8..7fd5c76 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -18,6 +18,13 @@ pub enum BinaryOp { Minus, Multiply, Divide, + Modulo, + Equal, + NotEqual, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual, } #[derive(Debug, Clone, Serialize)] diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 08824db..7094766 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -105,7 +105,7 @@ field_px = { "." ~ identifier } call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } index_px = { "[" ~ expr ~ "]" } binary_px = { bin_op ~ expr } -bin_op = { "+" | "-" | "*" | "/" | "%" } +bin_op = { "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" } expr = { primary ~ postfix* diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6b14585..d98087d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -341,6 +341,14 @@ impl Postfix { "-" => BinaryOp::Minus, "*" => BinaryOp::Multiply, "/" => BinaryOp::Divide, + "%" => BinaryOp::Modulo, + "==" => BinaryOp::Equal, + "!=" => BinaryOp::NotEqual, + "<" => BinaryOp::LessThan, + ">" => BinaryOp::GreaterThan, + "<=" => BinaryOp::LessThanOrEqual, + ">=" => BinaryOp::GreaterThanOrEqual, + _ => { unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) } diff --git a/test/src/main.ms b/test/src/main.ms index 2e358b8..755f17d 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,3 +1,17 @@ function add(a: int, b: int): int { - itm.a + itm.b(); + let sum = 0; + + for (let i = 0; i < 10; i = i + 1) { + if (i == 5) { + continue; + } + + sum = sum + i; + } + + if (sum > 20) { + return sum; + } else { + return 0; + } } From b4faebd34ed314b8d324eb05fe6ea60198254533 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 05:57:32 +0200 Subject: [PATCH 15/17] Var decl kind --- src/parser/grammar.pest | 6 ++++-- test/src/main.ms | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 7094766..a20eb82 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -60,8 +60,10 @@ continue_stmt = { "continue" ~ ";" } // --- variable declarations --- +var_decl_kind = { "let" | "const" | "var" } + var_decl = { - ("let" | "const" | "var") ~ identifier ~ ("=" ~ expr)? ~ ";" + var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" } // --- control flow --- @@ -80,7 +82,7 @@ for_stmt = { // helper (same as var_decl but no trailing ;) var_decl_no_semicolon = { - ("let" | "const" | "var") ~ identifier ~ ("=" ~ expr)? + var_decl_kind ~ identifier ~ ("=" ~ expr)? } // ================= EXPRESSIONS ================= diff --git a/test/src/main.ms b/test/src/main.ms index 755f17d..977dd57 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,13 +1,11 @@ function add(a: int, b: int): int { let sum = 0; - for (let i = 0; i < 10; i = i + 1) { if (i == 5) { continue; } sum = sum + i; - } if (sum > 20) { return sum; From 43ce27c12e542bd569ca9a2306a405662537fb35 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 06:13:55 +0200 Subject: [PATCH 16/17] Fixing for loop --- src/parser/ast.rs | 17 +++++-------- src/parser/grammar.pest | 12 ++++++--- src/parser/mod.rs | 54 ++++++++++++++++++++++++----------------- test/src/main.ms | 2 ++ 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 7fd5c76..08e305d 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -70,6 +70,11 @@ pub enum Statement { init: Option, }, + VarAssign { + target: Expression, + value: Expression, + }, + // if (...) stmt else stmt If { condition: Expression, @@ -85,7 +90,7 @@ pub enum Statement { // for (...) stmt For { - init: Option, + init: (VarKind, String, Option), condition: Option, update: Option, body: Box, @@ -118,13 +123,3 @@ pub enum VarKind { Const, Var, } - -#[derive(Debug, Clone, Serialize)] -pub enum ForInit { - VarDecl { - kind: VarKind, - name: String, - init: Option, - }, - Expr(Expression), -} diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index a20eb82..ef15bad 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -39,6 +39,7 @@ statement_list = { statement* } statement = _{ expr_stmt | var_decl + | var_assign | if_stmt | while_stmt | for_stmt @@ -66,6 +67,10 @@ var_decl = { var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" } +var_assign = { + expr ~ "=" ~ expr ~ ";" +} + // --- control flow --- if_stmt = { @@ -77,12 +82,11 @@ while_stmt = { } for_stmt = { - "for" ~ "(" ~ (var_decl_no_semicolon | expr_stmt | ";") ~ expr? ~ ";" ~ expr? ~ ")" ~ statement + "for" ~ "(" ~ var_decl ~ expr ~ ";" ~ var_assign_no_semicolon ~ ")" ~ statement } -// helper (same as var_decl but no trailing ;) -var_decl_no_semicolon = { - var_decl_kind ~ identifier ~ ("=" ~ expr)? +var_assign_no_semicolon = { + expr ~ "=" ~ expr } // ================= EXPRESSIONS ================= diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d98087d..4fb42c3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -229,30 +229,32 @@ impl Statement { Rule::for_stmt => { let mut inner = pair.into_inner(); - let init = inner.next().map(|p| match p.as_rule() { - Rule::var_decl_no_semicolon => { - let mut it = p.into_inner(); + let init = inner + .next() + .map(|p| match p.as_rule() { + Rule::var_decl => { + let mut it = p.into_inner(); - let kind = match it.next().unwrap().as_str() { - "let" => VarKind::Let, - "const" => VarKind::Const, - "var" => VarKind::Var, - _ => unreachable!(), - }; + let kind = match it.next().unwrap().as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, + _ => unreachable!(), + }; - let name = it.next().unwrap().as_str().to_string(); - let init_expr = it - .next() - .map(|e| Expression::from_pair(e.into_inner().next().unwrap())); + let name = it.next().unwrap().as_str().to_string(); + let init_expr = it + .next() + .map(|e| Expression::from_pair(e.into_inner().next().unwrap())); - ForInit::VarDecl { - kind, - name, - init: init_expr, + (kind, name, init_expr) } - } - _ => ForInit::Expr(Expression::from_pair(p)), - }); + _ => unimplemented!( + "For loop init parsing not implemented yet: {:?}", + p.as_rule() + ), + }) + .unwrap(); let condition = inner.next().map(Expression::from_pair); let update = inner.next().map(Expression::from_pair); @@ -260,12 +262,20 @@ impl Statement { Statement::For { init, - condition, - update, + condition: None, + update: None, body: Box::new(body), } } + Rule::var_assign => { + let mut inner = pair.into_inner(); + let target = Expression::from_pair(inner.next().unwrap()); + let value = Expression::from_pair(inner.next().unwrap()); + + Statement::VarAssign { target, value } + } + _ => unimplemented!( "Statement parsing not implemented yet: {:?}", pair.as_rule() diff --git a/test/src/main.ms b/test/src/main.ms index 977dd57..755f17d 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,11 +1,13 @@ function add(a: int, b: int): int { let sum = 0; + for (let i = 0; i < 10; i = i + 1) { if (i == 5) { continue; } sum = sum + i; + } if (sum > 20) { return sum; From 77f1759b3bed5d723cf0d02f74c077084baa4c1d Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 14 Apr 2026 06:17:30 +0200 Subject: [PATCH 17/17] Working for loop --- src/parser/ast.rs | 2 +- src/parser/mod.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 08e305d..63d7e24 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -92,7 +92,7 @@ pub enum Statement { For { init: (VarKind, String, Option), condition: Option, - update: Option, + update: Option>, body: Box, }, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4fb42c3..c000532 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -257,13 +257,13 @@ impl Statement { .unwrap(); let condition = inner.next().map(Expression::from_pair); - let update = inner.next().map(Expression::from_pair); + let update = inner.next().map(parse_var_assign_no_semicolon); let body = Statement::from_pair(inner.next().unwrap()); Statement::For { init, - condition: None, - update: None, + condition, + update: update.map(Box::new), body: Box::new(body), } } @@ -370,3 +370,11 @@ impl Postfix { } } } + +fn parse_var_assign_no_semicolon(pair: pest::iterators::Pair) -> Statement { + let mut inner = pair.into_inner(); + let target = Expression::from_pair(inner.next().unwrap()); + let value = Expression::from_pair(inner.next().unwrap()); + + Statement::VarAssign { target, value } +}