From bd262f98f8b9b5bc616f481c8597e9030da64f52 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 14:38:37 +0200 Subject: [PATCH 01/11] If statement expression --- semantic/src/scope.rs | 75 +++++++++++++++++++++++++++++++------------ test/src/main.ms | 15 +++++++++ 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index 4ad0655..f7b638e 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -82,29 +82,44 @@ impl LocalScope { } } + pub fn with_statement(self: &Arc, statement: &mut Statement) { + match statement { + Statement::Block(b) => self.clone().with_block(b), + Statement::VarDecl { name, init, .. } => { + if let Some(init) = init { + let var_type = self.get_type_from_expr(init).unwrap(); + + self.variables.lock().unwrap().insert( + name.clone(), + Arc::new(VarRef { + export: false, + name: name.to_string(), + var_type, + }), + ); + } + } + Statement::Expression(e) => { + self.get_type_from_expr(e); + } + Statement::If { + condition, + then_branch, + else_branch, + } => { + self.get_type_from_expr(condition); + self.clone().with_statement(then_branch); + if let Some(else_branch) = else_branch { + self.clone().with_statement(else_branch); + } + } + _ => {} + } + } + pub fn with_block(self: &Arc, block: &mut parser::ast::Block) { for statement in &mut block.0 { - match statement { - Statement::Block(b) => self.clone().with_block(b), - Statement::VarDecl { name, init, .. } => { - if let Some(init) = init { - let var_type = self.get_type_from_expr(init).unwrap(); - - self.variables.lock().unwrap().insert( - name.clone(), - Arc::new(VarRef { - export: false, - name: name.to_string(), - var_type, - }), - ); - } - } - Statement::Expression(e) => { - self.get_type_from_expr(e); - } - _ => {} - } + self.with_statement(statement); } } @@ -137,6 +152,24 @@ impl LocalScope { } _ => unimplemented!(), }, + Postfix::Binary(op, right) => match op { + parser::ast::BinaryOp::Equal + | parser::ast::BinaryOp::NotEqual + | parser::ast::BinaryOp::GreaterThan + | parser::ast::BinaryOp::LessThan + | parser::ast::BinaryOp::GreaterThanOrEqual + | parser::ast::BinaryOp::LessThanOrEqual => { + self.get_type_from_expr(right)?; + current_type = Arc::new(TypeRef::Name("bool".to_string())); + } + parser::ast::BinaryOp::Plus + | parser::ast::BinaryOp::Minus + | parser::ast::BinaryOp::Multiply + | parser::ast::BinaryOp::Divide + | parser::ast::BinaryOp::Modulo => { + self.get_type_from_expr(right)?; + } + }, _ => unimplemented!(), } } diff --git a/test/src/main.ms b/test/src/main.ms index d994a44..0fc1fcc 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -3,5 +3,20 @@ package main; import "fmt"; function main() { + doSomething(); + let a = doSomethingElse(); + + if (a + 1 == 67 + 1) { + fmt.println(a); + } +} + +function doSomething() { fmt.println("hello"); } + +function doSomethingElse(): int { + fmt.println("world"); + + return 67; +} From 27a772dad8b4bff0a5e91c9d25bc948ae8e3d11c Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 15:03:34 +0200 Subject: [PATCH 02/11] Mist Script --- parser/src/lib.rs | 389 +----------------- .../src/{grammar.pest => script_grammar.pest} | 0 parser/src/script_parser.rs | 388 +++++++++++++++++ src/compiler.rs | 9 +- src/main.rs | 76 +--- test/mist.json | 3 +- 6 files changed, 400 insertions(+), 465 deletions(-) rename parser/src/{grammar.pest => script_grammar.pest} (100%) create mode 100644 parser/src/script_parser.rs diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 2dbe131..c9fb5fe 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -1,389 +1,2 @@ -use std::collections::HashMap; - -use pest::Parser; -use pest_derive::Parser; - pub mod ast; -use ast::*; - -#[derive(Parser)] -#[grammar = "./src/grammar.pest"] -pub struct MistParser; - -// convenience alias for pest errors -pub type ParseError = pest::error::Error; - -pub fn parse(source: &str) -> Result, ParseError> { - let mut pairs = MistParser::parse(Rule::program, source)?; - - let mut statements = vec![]; - - for pair in pairs.next().unwrap().into_inner() { - if let Some(stmt) = TopLevel::from_pair(pair) { - statements.push(stmt); - } - } - - 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 ParamList { - pub fn from_pair(export_allowed: bool, pair: pest::iterators::Pair) -> Self { - let params = pair - .into_inner() - .map(|p| { - let mut param_inner = p.into_inner(); - let export = - if export_allowed && param_inner.peek().unwrap().as_rule() == Rule::export { - param_inner.next().unwrap(); - true - } else { - false - }; - let param_name = param_inner.next().unwrap().as_str().to_string(); - let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); - (param_name, (export, param_type)) - }) - .collect(); - - ParamList(params) - } -} - -impl TopLevel { - pub fn from_pair(pair: pest::iterators::Pair) -> Option { - match pair.as_rule() { - Rule::import => { - let path = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Import(path)) - } - Rule::package => { - let name = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Package(name)) - } - 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 = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from_pair(false, inner.next().unwrap()) - } else { - ParamList(HashMap::new()) - }; - 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 - }; - - let body = Block::from_pair(inner.next().unwrap()); - - Some(TopLevel::FunctionDecl { - export, - name, - params, - return_type, - body, - }) - } - - 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(true, fields_pair); - - Some(TopLevel::StructDecl { - export, - name, - fields, - }) - } - - Rule::EOI => None, - _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), - } - } -} - -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)) - } - - 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(Expression::from_pair); - - 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 => { - 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())); - - (kind, name, init_expr) - } - _ => 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(parse_var_assign_no_semicolon); - let body = Statement::from_pair(inner.next().unwrap()); - - Statement::For { - init, - condition, - update: update.map(Box::new), - 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() - ), - } - } -} - -impl Expression { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { - match pair.as_rule() { - 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::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(); - 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 {:?}", - pair.as_rule() - ), - } - } -} - -impl Postfix { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { - match pair.as_rule() { - 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, - "%" => BinaryOp::Modulo, - "==" => BinaryOp::Equal, - "!=" => BinaryOp::NotEqual, - "<" => BinaryOp::LessThan, - ">" => BinaryOp::GreaterThan, - "<=" => BinaryOp::LessThanOrEqual, - ">=" => BinaryOp::GreaterThanOrEqual, - - _ => { - 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()), - } - } -} - -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 } -} +pub mod script_parser; diff --git a/parser/src/grammar.pest b/parser/src/script_grammar.pest similarity index 100% rename from parser/src/grammar.pest rename to parser/src/script_grammar.pest diff --git a/parser/src/script_parser.rs b/parser/src/script_parser.rs new file mode 100644 index 0000000..f580d4b --- /dev/null +++ b/parser/src/script_parser.rs @@ -0,0 +1,388 @@ +use std::collections::HashMap; + +use pest::Parser; +use pest_derive::Parser; + +use crate::ast::*; + +#[derive(Parser)] +#[grammar = "./src/script_grammar.pest"] +pub struct MistParser; + +// convenience alias for pest errors +pub type ParseError = pest::error::Error; + +pub fn parse(source: &str) -> Result, ParseError> { + let mut pairs = MistParser::parse(Rule::program, source)?; + + let mut statements = vec![]; + + for pair in pairs.next().unwrap().into_inner() { + if let Some(stmt) = TopLevel::from_pair(pair) { + statements.push(stmt); + } + } + + 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 ParamList { + pub fn from_pair(export_allowed: bool, pair: pest::iterators::Pair) -> Self { + let params = pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let export = + if export_allowed && param_inner.peek().unwrap().as_rule() == Rule::export { + param_inner.next().unwrap(); + true + } else { + false + }; + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); + (param_name, (export, param_type)) + }) + .collect(); + + ParamList(params) + } +} + +impl TopLevel { + pub fn from_pair(pair: pest::iterators::Pair) -> Option { + match pair.as_rule() { + Rule::import => { + let path = pair.into_inner().next().unwrap().as_str().to_string(); + Some(TopLevel::Import(path)) + } + Rule::package => { + let name = pair.into_inner().next().unwrap().as_str().to_string(); + Some(TopLevel::Package(name)) + } + 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 = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from_pair(false, inner.next().unwrap()) + } else { + ParamList(HashMap::new()) + }; + 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 + }; + + let body = Block::from_pair(inner.next().unwrap()); + + Some(TopLevel::FunctionDecl { + export, + name, + params, + return_type, + body, + }) + } + + 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(true, fields_pair); + + Some(TopLevel::StructDecl { + export, + name, + fields, + }) + } + + Rule::EOI => None, + _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +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)) + } + + 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(Expression::from_pair); + + 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 => { + 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())); + + (kind, name, init_expr) + } + _ => 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(parse_var_assign_no_semicolon); + let body = Statement::from_pair(inner.next().unwrap()); + + Statement::For { + init, + condition, + update: update.map(Box::new), + 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() + ), + } + } +} + +impl Expression { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + 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::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(); + 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 {:?}", + pair.as_rule() + ), + } + } +} + +impl Postfix { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + 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, + "%" => BinaryOp::Modulo, + "==" => BinaryOp::Equal, + "!=" => BinaryOp::NotEqual, + "<" => BinaryOp::LessThan, + ">" => BinaryOp::GreaterThan, + "<=" => BinaryOp::LessThanOrEqual, + ">=" => BinaryOp::GreaterThanOrEqual, + + _ => { + 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()), + } + } +} + +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 } +} diff --git a/src/compiler.rs b/src/compiler.rs index c7cbbe9..fb5621f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -6,6 +6,7 @@ use serde::Deserialize; struct Config { entry: String, out_dir: String, + script: bool, } pub fn build() { @@ -37,7 +38,13 @@ pub fn build() { println!(" → parsing..."); - let mut ast = match parser::parse(&source) { + let parser_result = if config.script { + parser::script_parser::parse(&source) + } else { + parser::script_parser::parse(&source) + }; + + let mut ast = match parser_result { Ok(ast) => ast, Err(e) => { eprintln!("error: parse failed\n{}", e); diff --git a/src/main.rs b/src/main.rs index 6331f9c..2632a49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,6 @@ -pub mod compiler; - -use std::fs; -use std::path::PathBuf; use std::process; -use semantic::walk_ast; +pub mod compiler; fn main() { let args: Vec = std::env::args().collect(); @@ -22,20 +18,6 @@ fn main() { } compiler::build(); } - "check" => { - if args.len() < 3 { - eprintln!("error: expected a file path\n usage: mist check "); - process::exit(1); - } - 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")); } @@ -50,62 +32,6 @@ fn main() { } } -fn cmd_check(path: &str) { - let source = read_ms_file(path); - match parser::parse(&source) { - Ok(mut ast) => { - println!("parse: ok"); - - // walk_ast(semantic::scope::Scope::from_top(&ast), &mut ast); - - println!("{:#?}", ast) - } - Err(e) => { - eprintln!("parse error:\n{}", e); - process::exit(1); - } - } -} - -fn cmd_parse(path: &str) { - let source = read_ms_file(path); - match parser::parse(&source) { - Ok(mut ast) => { - // walk_ast(semantic::scope::Scope::from_top(&ast), &mut ast); - - fs::write("output.json", serde_json::to_string_pretty(&ast).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); - - if !pb.exists() { - eprintln!("error: file '{}' not found", path); - process::exit(1); - } - - if pb.extension().and_then(|e| e.to_str()) != Some("ms") { - eprintln!("error: expected a .ms file, got '{}'", path); - process::exit(1); - } - - fs::read_to_string(&pb).unwrap_or_else(|e| { - eprintln!("error: could not read '{}': {}", path, e); - process::exit(1); - }) -} - fn print_usage() { println!("mist - the mist compiler"); println!(); diff --git a/test/mist.json b/test/mist.json index c639516..6a2d242 100644 --- a/test/mist.json +++ b/test/mist.json @@ -1,4 +1,5 @@ { "entry": "src/main.ms", - "out_dir": "build" + "out_dir": "build", + "script": true } \ No newline at end of file From 1ee3ae317e8a7c47ddf9c531b1980d15db995511 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 15:16:15 +0200 Subject: [PATCH 03/11] Improved implementations --- parser/src/script_parser.rs | 111 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/parser/src/script_parser.rs b/parser/src/script_parser.rs index f580d4b..b9becff 100644 --- a/parser/src/script_parser.rs +++ b/parser/src/script_parser.rs @@ -7,18 +7,18 @@ use crate::ast::*; #[derive(Parser)] #[grammar = "./src/script_grammar.pest"] -pub struct MistParser; +pub struct MistScriptParser; // convenience alias for pest errors pub type ParseError = pest::error::Error; pub fn parse(source: &str) -> Result, ParseError> { - let mut pairs = MistParser::parse(Rule::program, source)?; + let mut pairs = MistScriptParser::parse(Rule::program, source)?; let mut statements = vec![]; for pair in pairs.next().unwrap().into_inner() { - if let Some(stmt) = TopLevel::from_pair(pair) { + if let Ok(stmt) = TopLevel::try_from(pair) { statements.push(stmt); } } @@ -26,12 +26,12 @@ pub fn parse(source: &str) -> Result, ParseError> { Ok(statements) } -impl TypeExpr { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for TypeExpr { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { Rule::type_expr => { let inner = pair.into_inner().next().unwrap(); - TypeExpr::from_pair(inner) + TypeExpr::from(inner) } Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), _ => unimplemented!("TypeExpr parsing not implemented yet"), @@ -39,8 +39,8 @@ impl TypeExpr { } } -impl ParamList { - pub fn from_pair(export_allowed: bool, pair: pest::iterators::Pair) -> Self { +impl From<(bool, pest::iterators::Pair<'_, Rule>)> for ParamList { + fn from((export_allowed, pair): (bool, pest::iterators::Pair)) -> Self { let params = pair .into_inner() .map(|p| { @@ -53,7 +53,7 @@ impl ParamList { false }; let param_name = param_inner.next().unwrap().as_str().to_string(); - let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); + let param_type = TypeExpr::from(param_inner.next().unwrap()); (param_name, (export, param_type)) }) .collect(); @@ -62,16 +62,17 @@ impl ParamList { } } -impl TopLevel { - pub fn from_pair(pair: pest::iterators::Pair) -> Option { +impl TryFrom> for TopLevel { + type Error = (); + fn try_from(pair: pest::iterators::Pair) -> Result { match pair.as_rule() { Rule::import => { let path = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Import(path)) + Ok(TopLevel::Import(path)) } Rule::package => { let name = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Package(name)) + Ok(TopLevel::Package(name)) } Rule::function_decl => { let mut inner = pair.into_inner(); @@ -88,13 +89,13 @@ impl TopLevel { }; let name = inner.next().unwrap().as_str().to_string(); let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from_pair(false, inner.next().unwrap()) + ParamList::from((false, inner.next().unwrap())) } else { ParamList(HashMap::new()) }; let return_type = if let Some(next) = inner.peek() { if next.as_rule() == Rule::type_expr { - Some(TypeExpr::from_pair(inner.next().unwrap())) + Some(TypeExpr::from(inner.next().unwrap())) } else { None } @@ -102,9 +103,9 @@ impl TopLevel { None }; - let body = Block::from_pair(inner.next().unwrap()); + let body = Block::from(inner.next().unwrap()); - Some(TopLevel::FunctionDecl { + Ok(TopLevel::FunctionDecl { export, name, params, @@ -127,30 +128,30 @@ impl TopLevel { }; let name = inner.next().unwrap().as_str().to_string(); let fields_pair = inner.next().unwrap(); - let fields = ParamList::from_pair(true, fields_pair); + let fields = ParamList::from((true, fields_pair)); - Some(TopLevel::StructDecl { + Ok(TopLevel::StructDecl { export, name, fields, }) } - Rule::EOI => None, + Rule::EOI => Err(()), _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), } } } -impl Block { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Block { + fn from(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() + pair.into_inner().map(Statement::from).collect() } else { - vec![Statement::from_pair(pair)] + vec![Statement::from(pair)] } }) .collect(); @@ -158,20 +159,20 @@ impl Block { } } -impl Statement { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Statement { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { Rule::statement => { let inner = pair.into_inner().next().unwrap(); - Statement::from_pair(inner) + Statement::from(inner) } Rule::expr_stmt => { let expr_pair = pair.into_inner().next().unwrap(); - Statement::Expression(Expression::from_pair(expr_pair)) + Statement::Expression(Expression::from(expr_pair)) } - Rule::block => Statement::Block(Block::from_pair(pair.into_inner().next().unwrap())), + Rule::block => Statement::Block(Block::from(pair.into_inner().next().unwrap())), Rule::var_decl => { let mut inner = pair.into_inner(); @@ -179,7 +180,7 @@ impl Statement { let kind_pair = inner.next().unwrap(); // let/const/var let name_pair = inner.next().unwrap(); // identifier - let init = inner.next().map(Expression::from_pair); + let init = inner.next().map(Expression::from); let kind = match kind_pair.as_str() { "let" => VarKind::Let, @@ -198,7 +199,7 @@ impl Statement { Rule::return_stmt => { let mut inner = pair.into_inner(); - let expr = inner.next().map(Expression::from_pair); + let expr = inner.next().map(Expression::from); Statement::Return(expr) } @@ -210,10 +211,10 @@ impl Statement { 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 condition = Expression::from(inner.next().unwrap()); + let then_branch = Statement::from(inner.next().unwrap()); - let else_branch = inner.next().map(Statement::from_pair); + let else_branch = inner.next().map(Statement::from); Statement::If { condition, @@ -225,8 +226,8 @@ impl Statement { 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()); + let condition = Expression::from(inner.next().unwrap()); + let body = Statement::from(inner.next().unwrap()); Statement::While { condition, @@ -253,7 +254,7 @@ impl Statement { let name = it.next().unwrap().as_str().to_string(); let init_expr = it .next() - .map(|e| Expression::from_pair(e.into_inner().next().unwrap())); + .map(|e| Expression::from(e.into_inner().next().unwrap())); (kind, name, init_expr) } @@ -264,9 +265,9 @@ impl Statement { }) .unwrap(); - let condition = inner.next().map(Expression::from_pair); + let condition = inner.next().map(Expression::from); let update = inner.next().map(parse_var_assign_no_semicolon); - let body = Statement::from_pair(inner.next().unwrap()); + let body = Statement::from(inner.next().unwrap()); Statement::For { init, @@ -278,8 +279,8 @@ impl Statement { 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()); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); Statement::VarAssign { target, value } } @@ -292,23 +293,23 @@ impl Statement { } } -impl Expression { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Expression { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { Rule::expr => { let mut inner = pair.into_inner(); - let exp = Expression::from_pair(inner.next().unwrap()); + let exp = Expression::from(inner.next().unwrap()); if inner.len() > 0 { Expression::Postfix { initial: Box::new(exp), - postfixes: inner.map(|p| Postfix::from_pair(p)).collect(), + postfixes: inner.map(|p| Postfix::from(p)).collect(), } } else { exp } } - Rule::primary => Expression::from_pair(pair.into_inner().next().unwrap()), + Rule::primary => Expression::from(pair.into_inner().next().unwrap()), Rule::identifier => Expression::Identifier(pair.as_str().to_string()), Rule::integer => { let value = pair.as_str().parse::().unwrap(); @@ -335,21 +336,19 @@ impl Expression { } } -impl Postfix { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Postfix { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::postfix => Postfix::from_pair(pair.into_inner().next().unwrap()), + Rule::postfix => Postfix::from(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::call_px => Postfix::Call(pair.into_inner().map(Expression::from).collect()), - Rule::index_px => { - Postfix::Index(Expression::from_pair(pair.into_inner().next().unwrap())) - } + Rule::index_px => Postfix::Index(Expression::from(pair.into_inner().next().unwrap())), Rule::binary_px => { let mut inner = pair.into_inner(); @@ -371,7 +370,7 @@ impl Postfix { unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) } }; - Postfix::Binary(op, Expression::from_pair(inner.next().unwrap())) + Postfix::Binary(op, Expression::from(inner.next().unwrap())) } _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), @@ -381,8 +380,8 @@ 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()); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); Statement::VarAssign { target, value } } From 2576c77466f291ea7fddcfc1d3b379e45dee5375 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 15:21:42 +0200 Subject: [PATCH 04/11] Adding mist system --- parser/src/grammar.pest | 120 +++++++++++++ parser/src/lib.rs | 388 ++++++++++++++++++++++++++++++++++++++++ src/compiler.rs | 4 +- 3 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 parser/src/grammar.pest diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest new file mode 100644 index 0000000..4cc1a94 --- /dev/null +++ b/parser/src/grammar.pest @@ -0,0 +1,120 @@ +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } + +program = { SOI ~ top_level* ~ EOI } + +// primitives +identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } +integer = @{ ASCII_DIGIT+ } +float = @{ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ } +boolean = { "true" | "false" } +string_lit = { "\"" ~ inner_str ~ "\"" } +inner_str = @{ (!"\"" ~ ANY)* } + +// ================= TOP LEVEL ================= + +top_level = _{ import | function_decl | struct_decl | package } + +package = { "package" ~ identifier ~ ";" } + +export = { "export" } + +import = { "import" ~ string_lit ~ ";" } + +param_list = { param ~ ("," ~ param)* ~ ","? } +param = { export? ~ identifier ~ ":" ~ type_expr } + +struct_decl = { + export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" +} + +function_decl = { + export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block +} + +// ================= STATEMENTS ================= + +block = { "{" ~ statement_list ~ "}" } + +statement_list = { statement* } + +statement = _{ + expr_stmt + | var_decl + | var_assign + | 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_kind = { "let" | "const" | "var" } + +var_decl = { + var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" +} + +var_assign = { + expr ~ "=" ~ expr ~ ";" +} + +// --- control flow --- + +if_stmt = { + "if" ~ "(" ~ expr ~ ")" ~ statement ~ ("else" ~ statement)? +} + +while_stmt = { + "while" ~ "(" ~ expr ~ ")" ~ statement +} + +for_stmt = { + "for" ~ "(" ~ var_decl ~ expr ~ ";" ~ var_assign_no_semicolon ~ ")" ~ statement +} + +var_assign_no_semicolon = { + expr ~ "=" ~ expr +} + +// ================= EXPRESSIONS ================= +type_expr = { identifier } + +primary = { + integer + | float + | boolean + | string_lit + | identifier +} + +postfix = { + 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/parser/src/lib.rs b/parser/src/lib.rs index c9fb5fe..1d29ba5 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -1,2 +1,390 @@ +use std::collections::HashMap; + +use pest::Parser; +use pest_derive::Parser; + pub mod ast; pub mod script_parser; + +use ast::*; + +#[derive(Parser)] +#[grammar = "./src/grammar.pest"] +pub struct MistParser; + +// convenience alias for pest errors +pub type ParseError = pest::error::Error; + +pub fn parse(source: &str) -> Result, ParseError> { + let mut pairs = MistParser::parse(Rule::program, source)?; + + let mut statements = vec![]; + + for pair in pairs.next().unwrap().into_inner() { + if let Ok(stmt) = TopLevel::try_from(pair) { + statements.push(stmt); + } + } + + Ok(statements) +} + +impl From> for TypeExpr { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::type_expr => { + let inner = pair.into_inner().next().unwrap(); + TypeExpr::from(inner) + } + Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), + _ => unimplemented!("TypeExpr parsing not implemented yet"), + } + } +} + +impl From<(bool, pest::iterators::Pair<'_, Rule>)> for ParamList { + fn from((export_allowed, pair): (bool, pest::iterators::Pair)) -> Self { + let params = pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let export = + if export_allowed && param_inner.peek().unwrap().as_rule() == Rule::export { + param_inner.next().unwrap(); + true + } else { + false + }; + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from(param_inner.next().unwrap()); + (param_name, (export, param_type)) + }) + .collect(); + + ParamList(params) + } +} + +impl TryFrom> for TopLevel { + type Error = (); + fn try_from(pair: pest::iterators::Pair) -> Result { + match pair.as_rule() { + Rule::import => { + let path = pair.into_inner().next().unwrap().as_str().to_string(); + Ok(TopLevel::Import(path)) + } + Rule::package => { + let name = pair.into_inner().next().unwrap().as_str().to_string(); + Ok(TopLevel::Package(name)) + } + 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 = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from((false, inner.next().unwrap())) + } else { + ParamList(HashMap::new()) + }; + let return_type = if let Some(next) = inner.peek() { + if next.as_rule() == Rule::type_expr { + Some(TypeExpr::from(inner.next().unwrap())) + } else { + None + } + } else { + None + }; + + let body = Block::from(inner.next().unwrap()); + + Ok(TopLevel::FunctionDecl { + export, + name, + params, + return_type, + body, + }) + } + + 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((true, fields_pair)); + + Ok(TopLevel::StructDecl { + export, + name, + fields, + }) + } + + Rule::EOI => Err(()), + _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +impl From> for Block { + fn from(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).collect() + } else { + vec![Statement::from(pair)] + } + }) + .collect(); + Block(statements) + } +} + +impl From> for Statement { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::statement => { + let inner = pair.into_inner().next().unwrap(); + Statement::from(inner) + } + + Rule::expr_stmt => { + let expr_pair = pair.into_inner().next().unwrap(); + Statement::Expression(Expression::from(expr_pair)) + } + + Rule::block => Statement::Block(Block::from(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(Expression::from); + + 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); + + 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(inner.next().unwrap()); + let then_branch = Statement::from(inner.next().unwrap()); + + let else_branch = inner.next().map(Statement::from); + + 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(inner.next().unwrap()); + let body = Statement::from(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 => { + 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(e.into_inner().next().unwrap())); + + (kind, name, init_expr) + } + _ => unimplemented!( + "For loop init parsing not implemented yet: {:?}", + p.as_rule() + ), + }) + .unwrap(); + + let condition = inner.next().map(Expression::from); + let update = inner.next().map(parse_var_assign_no_semicolon); + let body = Statement::from(inner.next().unwrap()); + + Statement::For { + init, + condition, + update: update.map(Box::new), + body: Box::new(body), + } + } + + Rule::var_assign => { + let mut inner = pair.into_inner(); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); + + Statement::VarAssign { target, value } + } + + _ => unimplemented!( + "Statement parsing not implemented yet: {:?}", + pair.as_rule() + ), + } + } +} + +impl From> for Expression { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::expr => { + let mut inner = pair.into_inner(); + let exp = Expression::from(inner.next().unwrap()); + + if inner.len() > 0 { + Expression::Postfix { + initial: Box::new(exp), + postfixes: inner.map(|p| Postfix::from(p)).collect(), + } + } else { + exp + } + } + Rule::primary => Expression::from(pair.into_inner().next().unwrap()), + 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 {:?}", + pair.as_rule() + ), + } + } +} + +impl From> for Postfix { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::postfix => Postfix::from(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).collect()), + + Rule::index_px => Postfix::Index(Expression::from(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, + "%" => BinaryOp::Modulo, + "==" => BinaryOp::Equal, + "!=" => BinaryOp::NotEqual, + "<" => BinaryOp::LessThan, + ">" => BinaryOp::GreaterThan, + "<=" => BinaryOp::LessThanOrEqual, + ">=" => BinaryOp::GreaterThanOrEqual, + + _ => { + unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) + } + }; + Postfix::Binary(op, Expression::from(inner.next().unwrap())) + } + + _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +fn parse_var_assign_no_semicolon(pair: pest::iterators::Pair) -> Statement { + let mut inner = pair.into_inner(); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); + + Statement::VarAssign { target, value } +} diff --git a/src/compiler.rs b/src/compiler.rs index fb5621f..fa33867 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -39,9 +39,9 @@ pub fn build() { println!(" → parsing..."); let parser_result = if config.script { - parser::script_parser::parse(&source) + parser::script_parser::parse(&source).map_err(|e| e.to_string()) } else { - parser::script_parser::parse(&source) + parser::parse(&source).map_err(|e| e.to_string()) }; let mut ast = match parser_result { From 4f2d1d36640b05602b820cb499da299d5e6169b4 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 16:23:59 +0200 Subject: [PATCH 05/11] Working c like parsing --- parser/src/grammar.pest | 6 +++--- parser/src/lib.rs | 34 +++++++++++++++++++++------------- test/mist.json | 2 +- test/src/main.ms | 19 ++++--------------- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 4cc1a94..40d5351 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -17,19 +17,19 @@ top_level = _{ import | function_decl | struct_decl | package } package = { "package" ~ identifier ~ ";" } -export = { "export" } +export = { "public" } import = { "import" ~ string_lit ~ ";" } param_list = { param ~ ("," ~ param)* ~ ","? } -param = { export? ~ identifier ~ ":" ~ type_expr } +param = { export? ~ type_expr ~ identifier } struct_decl = { export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" } function_decl = { - export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block + export? ~ type_expr ~ identifier ~ "(" ~ param_list? ~ ")" ~ block } // ================= STATEMENTS ================= diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 1d29ba5..fb8e1ea 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -29,16 +29,22 @@ pub fn parse(source: &str) -> Result, ParseError> { Ok(statements) } -impl From> for TypeExpr { - fn from(pair: pest::iterators::Pair) -> Self { - match pair.as_rule() { +impl TryFrom> for TypeExpr { + type Error = (); + + fn try_from(pair: pest::iterators::Pair<'_, Rule>) -> Result { + if pair.as_str() == "void" { + return Err(()); + } + + Ok(match pair.as_rule() { Rule::type_expr => { let inner = pair.into_inner().next().unwrap(); - TypeExpr::from(inner) + TypeExpr::try_from(inner)? } Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), _ => unimplemented!("TypeExpr parsing not implemented yet"), - } + }) } } @@ -55,8 +61,8 @@ impl From<(bool, pest::iterators::Pair<'_, Rule>)> for ParamList { } else { false }; + let param_type = TypeExpr::try_from(param_inner.next().unwrap()).unwrap(); let param_name = param_inner.next().unwrap().as_str().to_string(); - let param_type = TypeExpr::from(param_inner.next().unwrap()); (param_name, (export, param_type)) }) .collect(); @@ -90,15 +96,10 @@ impl TryFrom> for TopLevel { } else { false }; - let name = inner.next().unwrap().as_str().to_string(); - let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from((false, inner.next().unwrap())) - } else { - ParamList(HashMap::new()) - }; + let return_type = if let Some(next) = inner.peek() { if next.as_rule() == Rule::type_expr { - Some(TypeExpr::from(inner.next().unwrap())) + TypeExpr::try_from(inner.next().unwrap()).ok() } else { None } @@ -106,6 +107,13 @@ impl TryFrom> for TopLevel { None }; + let name = inner.next().unwrap().as_str().to_string(); + let params = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from((false, inner.next().unwrap())) + } else { + ParamList(HashMap::new()) + }; + let body = Block::from(inner.next().unwrap()); Ok(TopLevel::FunctionDecl { diff --git a/test/mist.json b/test/mist.json index 6a2d242..104ad8f 100644 --- a/test/mist.json +++ b/test/mist.json @@ -1,5 +1,5 @@ { "entry": "src/main.ms", "out_dir": "build", - "script": true + "script": false } \ No newline at end of file diff --git a/test/src/main.ms b/test/src/main.ms index 0fc1fcc..e1170da 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -2,21 +2,10 @@ package main; import "fmt"; -function main() { - doSomething(); - let a = doSomethingElse(); - - if (a + 1 == 67 + 1) { - fmt.println(a); - } +void main() { + test(67); } -function doSomething() { - fmt.println("hello"); -} - -function doSomethingElse(): int { - fmt.println("world"); - - return 67; +void test(int x) { + fmt.println("Hello" + x); } From 040be1df88457e4783cf7b719133daabc3562e37 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 20:28:39 +0200 Subject: [PATCH 06/11] Working variables --- semantic/src/scope.rs | 12 ++++++++++-- test/src/main.ms | 7 ++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index f7b638e..69c2a53 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -38,6 +38,10 @@ impl Scope { Scope::Local(l) => l.parent.next_var_idx(), } } + + pub fn get_name(&self, export: bool) -> String { + format!("{}{}", if export { 'V' } else { 'v' }, self.next_var_idx()) + } } #[derive(Debug)] @@ -89,11 +93,15 @@ impl LocalScope { if let Some(init) = init { let var_type = self.get_type_from_expr(init).unwrap(); + let var_name = name.clone(); + + *name = self.parent.get_name(false); + self.variables.lock().unwrap().insert( - name.clone(), + var_name, Arc::new(VarRef { export: false, - name: name.to_string(), + name: name.clone(), var_type, }), ); diff --git a/test/src/main.ms b/test/src/main.ms index e1170da..0efc119 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -3,9 +3,6 @@ package main; import "fmt"; void main() { - test(67); -} - -void test(int x) { - fmt.println("Hello" + x); + let a = 0; + let b = a + 10; } From 753f8501c02525a57e597ec7cecc03d1057f937b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 20:48:13 +0200 Subject: [PATCH 07/11] Improving compiler --- src/compiler.rs | 108 +++++++++++++++++++++++++---------------------- test/mist.json | 5 +-- test/src/main.ms | 5 +-- 3 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index fa33867..c4071a8 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -4,9 +4,8 @@ use serde::Deserialize; #[derive(Deserialize)] struct Config { - entry: String, - out_dir: String, - script: bool, + src: String, + output: String, } pub fn build() { @@ -22,61 +21,68 @@ pub fn build() { // 2. load config let config = load_config(&root); - let entry_path = root.join(&config.entry); - let out_dir = root.join(&config.out_dir); + let src = root.join(&config.src); - println!(" → entry: {}", entry_path.display()); + for entry in fs::read_dir(src).unwrap() { + if let Ok(entry) = entry { + let entry_path = root.join(&entry.path()); + let out_dir = root.join(&config.output); + let file_name = entry.file_name(); + let file_name = file_name.to_string_lossy(); - // 3. read entry file - let source = match fs::read_to_string(&entry_path) { - Ok(s) => s, - Err(e) => { - eprintln!("error: failed to read entry file\n {}", e); - process::exit(1); + let script = if file_name.ends_with(".ms") { + true + } else if file_name.ends_with(".mist") { + false + } else { + continue; + }; + + // 3. read entry file + let source = match fs::read_to_string(&entry_path) { + Ok(s) => s, + Err(e) => { + eprintln!("error: failed to read entry file\n {}", e); + process::exit(1); + } + }; + + let parser_result = if script { + parser::script_parser::parse(&source).map_err(|e| e.to_string()) + } else { + parser::parse(&source).map_err(|e| e.to_string()) + }; + + let mut ast = match parser_result { + Ok(ast) => ast, + Err(e) => { + eprintln!("error: parse failed\n{}", e); + process::exit(1); + } + }; + + semantic::walk_ast(semantic::scope::Scope::from_top(&root, &ast), &mut ast); + + let mut gc = codegen::GoCodegen::new(); + let output = gc.generate(&ast); + + if let Err(e) = fs::create_dir_all(&out_dir) { + eprintln!("error: failed to create build dir\n {}", e); + process::exit(1); + } + + let out_file = + out_dir.join(file_name.replace(if script { ".ms" } else { ".mist" }, ".go")); + + if let Err(e) = fs::write(&out_file, output) { + eprintln!("error: failed to write output\n {}", e); + process::exit(1); + } } - }; - - println!(" → parsing..."); - - let parser_result = if config.script { - parser::script_parser::parse(&source).map_err(|e| e.to_string()) - } else { - parser::parse(&source).map_err(|e| e.to_string()) - }; - - let mut ast = match parser_result { - Ok(ast) => ast, - Err(e) => { - eprintln!("error: parse failed\n{}", e); - process::exit(1); - } - }; - - println!(" → type checking..."); - - semantic::walk_ast(semantic::scope::Scope::from_top(&root, &ast), &mut ast); - - println!(" → generating Go code..."); - - let mut gc = codegen::GoCodegen::new(); - let output = gc.generate(&ast); - - // 4. ensure build dir - if let Err(e) = fs::create_dir_all(&out_dir) { - eprintln!("error: failed to create build dir\n {}", e); - process::exit(1); - } - - let out_file = out_dir.join("main.go"); - - if let Err(e) = fs::write(&out_file, output) { - eprintln!("error: failed to write output\n {}", e); - process::exit(1); } let elapsed = start.elapsed(); - println!(" ✓ built {}", out_file.display()); println!("build finished in {:.2?}", elapsed); } diff --git a/test/mist.json b/test/mist.json index 104ad8f..6632b01 100644 --- a/test/mist.json +++ b/test/mist.json @@ -1,5 +1,4 @@ { - "entry": "src/main.ms", - "out_dir": "build", - "script": false + "src": "src", + "output": "build" } \ No newline at end of file diff --git a/test/src/main.ms b/test/src/main.ms index 0efc119..0591883 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -2,7 +2,6 @@ package main; import "fmt"; -void main() { - let a = 0; - let b = a + 10; +function main() { + } From 4118e4b562ad6006c6efed2a6e63ff16ba3dadd0 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 20:49:29 +0200 Subject: [PATCH 08/11] Mist --- test/src/{main.ms => main.mist} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/src/{main.ms => main.mist} (64%) diff --git a/test/src/main.ms b/test/src/main.mist similarity index 64% rename from test/src/main.ms rename to test/src/main.mist index 0591883..31511e8 100644 --- a/test/src/main.ms +++ b/test/src/main.mist @@ -2,6 +2,6 @@ package main; import "fmt"; -function main() { +void main() { } From 872fdf09e6050acc2ebf6ee2246c4c9badcda66b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 21:03:08 +0200 Subject: [PATCH 09/11] C like var declerations --- codegen/src/lib.rs | 16 ++++++++++++++-- parser/src/ast.rs | 1 + parser/src/grammar.pest | 4 ++-- parser/src/lib.rs | 19 ++++++------------- parser/src/script_parser.rs | 1 + test/src/main.mist | 3 ++- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4b01aef..42c462e 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -142,7 +142,12 @@ impl GoCodegen { self.indent -= 1; self.add_indentedln("}\n"); } - Statement::VarDecl { kind, name, init } => { + Statement::VarDecl { + kind, + name, + init, + type_, + } => { let go_kind = match kind { VarKind::Let | VarKind::Const => "var", VarKind::Var => "var", @@ -151,7 +156,14 @@ impl GoCodegen { .as_ref() .map(|e| format!(" = {}", self.generate_expression(e))) .unwrap_or_else(|| "".to_string()); - self.add_indentedln(&format!("{} {}{};\n", go_kind, name, init_expr)); + let type_expr = type_ + .clone() + .map(|t| self.translate_type(&t)) + .unwrap_or_default(); + self.add_indentedln(&format!( + "{} {} {}{};\n", + go_kind, name, type_expr, init_expr + )); } Statement::VarAssign { target, value } => { self.add_indentedln(&format!( diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 0313478..3c0f3d1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -67,6 +67,7 @@ pub enum Statement { kind: VarKind, name: String, init: Option, + type_: Option, }, VarAssign { diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 40d5351..058b782 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -63,10 +63,10 @@ continue_stmt = { "continue" ~ ";" } // --- variable declarations --- -var_decl_kind = { "let" | "const" | "var" } +// var_decl_kind = { "let" | "const" | "var" } var_decl = { - var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" + type_expr ~ identifier ~ ("=" ~ expr)? ~ ";" } var_assign = { diff --git a/parser/src/lib.rs b/parser/src/lib.rs index fb8e1ea..bd9c374 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -43,7 +43,7 @@ impl TryFrom> for TypeExpr { TypeExpr::try_from(inner)? } Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), - _ => unimplemented!("TypeExpr parsing not implemented yet"), + _ => unimplemented!("{pair:#?}"), }) } } @@ -188,22 +188,15 @@ impl From> for Statement { 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 type_ = inner.next().map(TypeExpr::try_from).unwrap().ok(); + let name = inner.next().unwrap().as_str().to_string(); let init = inner.next().map(Expression::from); - 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(), + kind: VarKind::Var, + name: name.as_str().to_string(), init, + type_, } } diff --git a/parser/src/script_parser.rs b/parser/src/script_parser.rs index b9becff..cff3b6f 100644 --- a/parser/src/script_parser.rs +++ b/parser/src/script_parser.rs @@ -193,6 +193,7 @@ impl From> for Statement { kind, name: name_pair.as_str().to_string(), init, + type_: None, } } diff --git a/test/src/main.mist b/test/src/main.mist index 31511e8..9a686bb 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -3,5 +3,6 @@ package main; import "fmt"; void main() { - + string test = "Hello, World"; + fmt.println(test); } From aedf80822c550841b542f35ad12246139355c5b7 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 21:12:14 +0200 Subject: [PATCH 10/11] Fix call args --- semantic/src/scope.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index 69c2a53..f920fff 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -153,9 +153,11 @@ impl LocalScope { } _ => unimplemented!(), }, - Postfix::Call(_args) => match &*current_type { + Postfix::Call(args) => match &*current_type { TypeRef::Function(s) => { - // TODO: arg checking + for arg in args { + self.get_type_from_expr(arg); + } current_type = s.return_type.clone()?; } _ => unimplemented!(), From b4edb33cbca2175767579d46ed8a6346c28bcfd6 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 21:47:11 +0200 Subject: [PATCH 11/11] Fix params --- semantic/src/lib.rs | 5 ---- semantic/src/scope.rs | 60 ++++++++++++++++++++++++++----------------- test/src/main.mist | 7 ++--- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/semantic/src/lib.rs b/semantic/src/lib.rs index 9af6dfa..340e2ea 100644 --- a/semantic/src/lib.rs +++ b/semantic/src/lib.rs @@ -26,11 +26,6 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { *name = rf.name.clone(); } - match &*rf.var_type { - hir::TypeRef::Function(f) => walk_param_list(&f.params, params), - _ => unimplemented!(), - } - let scope = LocalScope::new(top_scope.clone()); scope.with_params(params); diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index f920fff..78e88dc 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -59,15 +59,16 @@ impl LocalScope { } pub fn get_reference(&self, name: &String) -> Option> { - let rf = self - .variables + self.variables .lock() .unwrap() .get(name) .cloned() - .or_else(|| self.parent.get_reference(name)); + .or_else(|| self.parent.get_reference(name)) + } - match rf { + pub fn get_type_reference(&self, name: &String) -> Option> { + match self.get_reference(name) { Some(v) => Some(v), None => { let var_ref = Arc::new(VarRef { @@ -121,6 +122,11 @@ impl LocalScope { self.clone().with_statement(else_branch); } } + Statement::Return(expr) => { + if let Some(expr) = expr { + self.get_type_from_expr(expr); + } + } _ => {} } } @@ -193,19 +199,19 @@ impl LocalScope { ) -> Option> { match expr { ast::Expression::IntLiteral(_) => self - .get_reference(&"int".to_string()) + .get_type_reference(&"int".to_string()) .map(|r| r.var_type.clone()), ast::Expression::FloatLiteral(_) => self - .get_reference(&"float".to_string()) + .get_type_reference(&"float".to_string()) .map(|r| r.var_type.clone()), ast::Expression::BoolLiteral(_) => self - .get_reference(&"bool".to_string()) + .get_type_reference(&"bool".to_string()) .map(|r| r.var_type.clone()), ast::Expression::StringLiteral(_) => self - .get_reference(&"string".to_string()) + .get_type_reference(&"string".to_string()) .map(|r| r.var_type.clone()), ast::Expression::Identifier(id) => { @@ -220,24 +226,32 @@ impl LocalScope { } } - pub fn with_params(self: &Arc, param_list: &ParamList) { - for (param_name, (export, type_expr)) in ¶m_list.0 { - match type_expr { + pub fn with_params(self: &Arc, param_list: &mut ParamList) { + let mut new_param_list = HashMap::new(); + + for (param_name, (export, type_expr)) in param_list.0.drain() { + match &type_expr { parser::ast::TypeExpr::Identifier(id) => { - if let Some(var_type) = - self.parent.get_reference(id).map(|r| r.var_type.clone()) - { - self.variables.lock().unwrap().insert( - param_name.clone(), - Arc::new(VarRef { - export: *export, - var_type, - name: param_name.clone(), - }), - ); - } + let new_name = self.parent.get_name(export); + + self.variables.lock().unwrap().insert( + param_name.clone(), + Arc::new(VarRef { + export: export, + var_type: self + .parent + .get_reference(id) + .map(|r| r.var_type.clone()) + .unwrap(), + name: new_name.clone(), + }), + ); + + new_param_list.insert(new_name, (export, type_expr)); } } } + + param_list.0 = new_param_list; } } diff --git a/test/src/main.mist b/test/src/main.mist index 9a686bb..7bc9895 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,8 +1,5 @@ package main; -import "fmt"; - -void main() { - string test = "Hello, World"; - fmt.println(test); +int test(int a, int b) { + return a + b; }