From 6c31221cee78ffc8623802489b2b6104f66e082a Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 13 Apr 2026 14:10:29 +0200 Subject: [PATCH] Parser --- src/parser/grammar.pest | 43 +++++++ src/parser/mod.rs | 274 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 316 insertions(+), 1 deletion(-) diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index e69de29..9841b2a 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -0,0 +1,43 @@ +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } + +program = { SOI ~ (import_decl | function_decl | struct_decl | class_decl)* ~ 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 +import_decl = { "import" ~ string_lit } +struct_decl = { "struct" ~ identifier ~ "{" ~ struct_field* ~ "}" } +class_decl = { "class" ~ identifier ~ "{" ~ (struct_field | function_decl)* ~ "}" } +function_decl = { "fn" ~ identifier ~ "(" ~ param_list? ~ ")" ~ ("->" ~ type_expr)? ~ block } + +struct_field = { identifier ~ ":" ~ type_expr } +param_list = { param ~ ("," ~ param)* } +param = { identifier ~ ":" ~ type_expr } + +// statements +block = { "{" ~ statement* ~ "}" } +statement = _{ let_stmt | return_stmt | if_stmt | for_stmt | expression } + +let_stmt = { "let" ~ identifier ~ (":" ~ type_expr)? ~ "=" ~ expression } +return_stmt = { "return" ~ expression? } +if_stmt = { "if" ~ expression ~ block ~ ("else" ~ block)? } +for_stmt = { "for" ~ identifier ~ "in" ~ expression ~ block } + +// expressions — broken into precedence layers, no cycles +expression = { primary ~ (access_chain)* } +access_chain = _{ field_access | call_suffix } +field_access = { "." ~ identifier } +call_suffix = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" } +primary = _{ float | integer | string_lit | boolean | identifier } + +// types — optional is a suffix, not a recursive wrapper +type_expr = { base_type ~ "?"? } +base_type = _{ array_type | identifier } +array_type = { "[" ~ type_expr ~ "]" } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 18dd6db..3f20605 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,7 +1,279 @@ +use pest::Parser; +use pest::iterators::Pair; use pest_derive::Parser; pub mod ast; +use ast::*; #[derive(Parser)] -#[grammar = "./src/parser/grammar.pest"] // relative to src +#[grammar = "./src/parser/grammar.pest"] pub struct MistParser; + +// convenience alias for pest errors +pub type ParseError = pest::error::Error; + +pub fn parse(source: &str) -> Result { + let pairs = MistParser::parse(Rule::program, source)?; + let mut statements = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::function_decl => statements.push(TopLevel::Function(parse_function(pair))), + Rule::struct_decl => statements.push(TopLevel::Struct(parse_struct(pair))), + Rule::class_decl => statements.push(TopLevel::Class(parse_class(pair))), + Rule::import_decl => statements.push(TopLevel::Import(parse_import(pair))), + Rule::EOI => {} + _ => {} + } + } + + 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 => 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 { + // let span = span_of(&pair); + match pair.as_rule() { + Rule::expression => { + let mut inner = pair.into_inner(); + let mut expr = parse_primary(inner.next().unwrap()); + + // chain field accesses and calls as left-to-right suffixes + 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 + } + _ => parse_primary(pair), + } +} + +fn parse_primary(pair: Pair) -> Expression { + let span = span_of(&pair); + match pair.as_rule() { + 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::identifier => Expression::Identifier(pair.as_str().to_string(), span), + _ => 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 + } +}