From 2576c77466f291ea7fddcfc1d3b379e45dee5375 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 22 Apr 2026 15:21:42 +0200 Subject: [PATCH] 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 {