use std::fmt; use crate::{ ast::{ BinaryOp, Block, ElseBranch, Expr, ExprKind, FieldDef, FuncDef, Param, Program, Stmt, StmtKind, StructDef, StructField, TopLevelDef, TopLevelDefKind, Type, UnaryOp, }, lexer::Lexer, token::{Span, Token, TokenKind}, }; // ── Parse error ─────────────────────────────────────────────────────────────── #[derive(Debug, Clone)] pub struct ParseError { pub span: Span, pub message: String, } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "error at {}: {}", self.span, self.message) } } // ── Binding powers ───────────────────────────────────────────────────────────── // // Returns `(left_bp, right_bp)` for infix operators. // left_bp < right_bp → left-associative // left_bp > right_bp → right-associative (none here) // // NOTE: comparison operators (==, !=, <, >, <=, >=) are not listed in the // GRAMMAR.ebnf precedence table but appear in examples; placed between // bitwise-AND (50) and additive (60) at 55. fn infix_bp(kind: TokenKind) -> Option<(u8, u8)> { let bp = match kind { // Assignment: lowest precedence, right-associative (left_bp == right_bp). // `a = b = c` → `a = (b = c)`. TokenKind::Eq => (2, 2), TokenKind::Or => (10, 11), TokenKind::And => (20, 21), TokenKind::Pipe => (30, 31), TokenKind::Caret => (40, 41), TokenKind::Amp => (50, 51), TokenKind::EqEq | TokenKind::BangEq | TokenKind::Lt | TokenKind::Gt | TokenKind::LtEq | TokenKind::GtEq => (55, 56), TokenKind::Plus | TokenKind::Minus => (60, 61), TokenKind::Star | TokenKind::Slash | TokenKind::Percent => (70, 71), // Postfix: `.`, `[`, `(` — handled separately in parse_led, bp listed // here only so callers can detect them as infix/postfix operators. TokenKind::Dot | TokenKind::LBracket | TokenKind::LParen => (90, 91), _ => return None, }; Some(bp) } // Returns the right binding power for prefix operators. fn prefix_bp(kind: TokenKind) -> Option { match kind { TokenKind::Bang | TokenKind::Tilde | TokenKind::Minus | TokenKind::Star | TokenKind::Amp => Some(80), _ => None, } } fn token_to_unary_op(kind: TokenKind) -> UnaryOp { match kind { TokenKind::Minus => UnaryOp::Neg, TokenKind::Bang => UnaryOp::Not, TokenKind::Tilde => UnaryOp::BitNot, TokenKind::Star => UnaryOp::Deref, TokenKind::Amp => UnaryOp::AddrOf, _ => unreachable!("not a unary op: {:?}", kind), } } fn token_to_binary_op(kind: TokenKind) -> BinaryOp { match kind { TokenKind::Or => BinaryOp::Or, TokenKind::And => BinaryOp::And, TokenKind::Pipe => BinaryOp::BitOr, TokenKind::Caret => BinaryOp::BitXor, TokenKind::Amp => BinaryOp::BitAnd, TokenKind::EqEq => BinaryOp::Eq, TokenKind::BangEq => BinaryOp::Ne, TokenKind::Lt => BinaryOp::Lt, TokenKind::Gt => BinaryOp::Gt, TokenKind::LtEq => BinaryOp::Le, TokenKind::GtEq => BinaryOp::Ge, TokenKind::Plus => BinaryOp::Add, TokenKind::Minus => BinaryOp::Sub, TokenKind::Star => BinaryOp::Mul, TokenKind::Slash => BinaryOp::Div, TokenKind::Percent => BinaryOp::Rem, TokenKind::Eq => BinaryOp::Assign, _ => unreachable!("not a binary op: {:?}", kind), } } // ── Parser ───────────────────────────────────────────────────────────────────── pub struct Parser<'src> { tokens: Vec>, pos: usize, pub errors: Vec, } impl<'src> Parser<'src> { pub fn new(src: &'src str) -> Self { let tokens = Lexer::new(src).tokenize(); Self { tokens, pos: 0, errors: Vec::new(), } } // ── Token access ────────────────────────────────────────────────────────── fn current(&self) -> Token<'src> { self.tokens[self.pos] } /// Advance past the current token and return it. fn advance(&mut self) -> Token<'src> { let tok = self.current(); if tok.kind != TokenKind::Eof { self.pos += 1; } tok } /// Consume the current token if it matches `kind`; otherwise record an /// error and return a zero-width dummy token at the current position /// so that parsing can continue (missing-token insertion). fn expect(&mut self, kind: TokenKind) -> Token<'src> { let tok = self.current(); if tok.kind == kind { self.advance() } else { let span = Span::new(tok.span.start, tok.span.start); self.errors.push(ParseError { span, message: format!("expected {}, found {}", kind, tok.kind), }); Token::new(kind, span, "") } } /// Skip tokens until we reach a natural statement boundary, so that /// subsequent statements can still be parsed cleanly. /// /// Stops *before* statement-starting keywords and `}` (so the caller can /// handle them), and stops *after* consuming a `;`. fn synchronize(&mut self) { loop { match self.current().kind { // Stop before these — they begin the next statement or close a block. TokenKind::Eof | TokenKind::RCurly | TokenKind::Let | TokenKind::Return | TokenKind::If | TokenKind::While | TokenKind::Loop | TokenKind::Break | TokenKind::Continue => break, // Consume the `;` and stop — it terminates the current statement. TokenKind::Semicolon => { self.advance(); break; } _ => { self.advance(); } } } } // ── Public API ──────────────────────────────────────────────────────────── /// Parse a type annotation. pub fn parse_type(&mut self) -> Type { let tok = self.advance(); match tok.kind { // Primitive types TokenKind::U8 => Type::U8, TokenKind::U16 => Type::U16, TokenKind::U32 => Type::U32, TokenKind::U64 => Type::U64, TokenKind::I8 => Type::I8, TokenKind::I16 => Type::I16, TokenKind::I32 => Type::I32, TokenKind::I64 => Type::I64, TokenKind::F32 => Type::F32, TokenKind::F64 => Type::F64, TokenKind::Bool => Type::Bool, TokenKind::Char => Type::Char, // Named type (user-defined struct, etc.) TokenKind::Ident => Type::Named(tok.text.to_owned(), tok.span), // Pointer: `*opaque` or `*` TokenKind::Star => { if self.current().kind == TokenKind::Opaque { self.advance(); Type::OpaquePointer } else { Type::Pointer(Box::new(self.parse_type())) } } // Array: `[type; INT_LIT]` TokenKind::LBracket => { let elem = self.parse_type(); self.expect(TokenKind::Semicolon); let size_tok = self.expect(TokenKind::IntLit); self.expect(TokenKind::RBracket); Type::Array { elem: Box::new(elem), size: size_tok.text.to_owned(), } } // Error — insert recovery placeholder _ => { self.errors.push(ParseError { span: tok.span, message: format!("expected type, found {}", tok.kind), }); Type::Error } } } /// Parse a block: `{ stmt* }`. pub fn parse_block(&mut self) -> Block { let open = self.expect(TokenKind::LCurly); let mut stmts = Vec::new(); loop { if matches!(self.current().kind, TokenKind::RCurly | TokenKind::Eof) { break; } stmts.push(self.parse_stmt()); } let close = self.expect(TokenKind::RCurly); Block { stmts, span: open.span.cover(close.span), } } /// Parse a single statement. /// /// **Recovery policy** /// - *Missing-token insertion*: `expect()` handles single missing tokens /// (e.g. a forgotten `;`) by inserting a zero-width dummy — no tokens /// are skipped and no error cascades. /// - *Synchronization*: tokens that can never start a statement or /// expression trigger `synchronize()`, which skips forward until the /// next statement boundary to prevent cascading errors. pub fn parse_stmt(&mut self) -> Stmt { let tok = self.current(); match tok.kind { TokenKind::Let => self.parse_let_stmt(), TokenKind::Return => self.parse_return_stmt(), TokenKind::If => self.parse_if_stmt(), TokenKind::While => self.parse_while_stmt(), TokenKind::Loop => self.parse_loop_stmt(), TokenKind::Break => { let kw = self.advance(); let semi = self.expect(TokenKind::Semicolon); Stmt { kind: StmtKind::Break, span: kw.span.cover(semi.span), } } TokenKind::Continue => { let kw = self.advance(); let semi = self.expect(TokenKind::Semicolon); Stmt { kind: StmtKind::Continue, span: kw.span.cover(semi.span), } } TokenKind::LCurly => { let block = self.parse_block(); let span = block.span; Stmt { kind: StmtKind::Block(block), span, } } // Tokens that cannot start any statement or expression. // Synchronize to prevent cascading errors. TokenKind::RCurly | TokenKind::RParen | TokenKind::RBracket | TokenKind::Else | TokenKind::Comma | TokenKind::Arrow | TokenKind::Fn | TokenKind::Struct | TokenKind::Eof => { self.errors.push(ParseError { span: tok.span, message: format!("unexpected {} in statement position", tok.kind), }); self.synchronize(); Stmt { kind: StmtKind::Error, span: tok.span, } } // Anything else is an expression statement. _ => self.parse_expr_stmt(), } } /// Parse a single expression. /// /// `allow_struct_literals` controls whether a bare `Ident { … }` is /// parsed as a struct literal. Pass `false` in `if`/`while` conditions /// so that `{` is not consumed as a struct body. pub fn parse_expr(&mut self, allow_struct_literals: bool) -> Expr { self.pratt(0, allow_struct_literals) } // ── Statement helpers ───────────────────────────────────────────────────── fn parse_let_stmt(&mut self) -> Stmt { let start = self.advance(); // consume `let` let mutable = if self.current().kind == TokenKind::Mut { self.advance(); true } else { false }; let name_tok = self.expect(TokenKind::Ident); let ty = if self.current().kind == TokenKind::Colon { self.advance(); Some(self.parse_type()) } else { None }; let init = if self.current().kind == TokenKind::Eq { self.advance(); Some(self.parse_expr(true)) } else { None }; let semi = self.expect(TokenKind::Semicolon); Stmt { kind: StmtKind::Let { mutable, name: name_tok.text.to_owned(), name_span: name_tok.span, ty, init, }, span: start.span.cover(semi.span), } } fn parse_return_stmt(&mut self) -> Stmt { let kw = self.advance(); // consume `return` // LL(1): `;` → unit return; anything else → parse expression let value = if self.current().kind != TokenKind::Semicolon { Some(self.parse_expr(true)) } else { None }; let semi = self.expect(TokenKind::Semicolon); Stmt { kind: StmtKind::Return(value), span: kw.span.cover(semi.span), } } fn parse_if_stmt(&mut self) -> Stmt { let kw = self.advance(); // consume `if` // Condition: expr_ns (no struct literals at outermost level) let cond = self.parse_expr(false); let then_block = self.parse_block(); let else_branch = if self.current().kind == TokenKind::Else { self.advance(); // consume `else` if self.current().kind == TokenKind::If { let nested = self.parse_if_stmt(); Some(ElseBranch::If(Box::new(nested))) } else { Some(ElseBranch::Block(self.parse_block())) } } else { None }; let end_span = match &else_branch { Some(ElseBranch::If(s)) => s.span, Some(ElseBranch::Block(b)) => b.span, None => then_block.span, }; Stmt { kind: StmtKind::If { cond, then_block, else_branch, }, span: kw.span.cover(end_span), } } fn parse_while_stmt(&mut self) -> Stmt { let kw = self.advance(); // consume `while` let cond = self.parse_expr(false); // no struct literals in condition let body = self.parse_block(); let span = kw.span.cover(body.span); Stmt { kind: StmtKind::While { cond, body }, span, } } fn parse_loop_stmt(&mut self) -> Stmt { let kw = self.advance(); // consume `loop` let body = self.parse_block(); let span = kw.span.cover(body.span); Stmt { kind: StmtKind::Loop { body }, span, } } fn parse_expr_stmt(&mut self) -> Stmt { let expr = self.parse_expr(true); let semi = self.expect(TokenKind::Semicolon); let span = expr.span.cover(semi.span); Stmt { kind: StmtKind::Expr(expr), span, } } // ── Pratt core ──────────────────────────────────────────────────────────── fn pratt(&mut self, min_bp: u8, allow_struct_lit: bool) -> Expr { let mut lhs = self.parse_nud(allow_struct_lit); loop { let op_tok = self.current(); // Struct literal: `Ident {` — only when the flag is set, and only // when the lhs is a bare identifier. if allow_struct_lit && op_tok.kind == TokenKind::LCurly && matches!(lhs.kind, ExprKind::Ident(_)) && min_bp == 0 { lhs = self.parse_struct_lit(lhs); continue; } let (l_bp, r_bp) = match infix_bp(op_tok.kind) { Some(bp) => bp, None => break, }; if l_bp < min_bp { break; } lhs = self.parse_led(lhs, op_tok, r_bp, allow_struct_lit); } lhs } // ── Null denotation (prefix / primary) ─────────────────────────────────── fn parse_nud(&mut self, allow_struct_lit: bool) -> Expr { let tok = self.advance(); match tok.kind { // Literals TokenKind::IntLit => Expr::new(ExprKind::IntLit(tok.text.to_owned()), tok.span), TokenKind::FloatLit => Expr::new(ExprKind::FloatLit(tok.text.to_owned()), tok.span), TokenKind::StringLit => Expr::new(ExprKind::StringLit(tok.text.to_owned()), tok.span), TokenKind::CharLit => Expr::new(ExprKind::CharLit(tok.text.to_owned()), tok.span), TokenKind::True => Expr::new(ExprKind::Bool(true), tok.span), TokenKind::False => Expr::new(ExprKind::Bool(false), tok.span), // Identifier TokenKind::Ident => Expr::new(ExprKind::Ident(tok.text.to_owned()), tok.span), // Prefix unary kind if prefix_bp(kind).is_some() => { let r_bp = prefix_bp(kind).unwrap(); let op = token_to_unary_op(kind); let operand = self.pratt(r_bp, allow_struct_lit); let span = tok.span.cover(operand.span); Expr::new( ExprKind::Unary { op, op_span: tok.span, expr: Box::new(operand), }, span, ) } // Grouped expression TokenKind::LParen => { // Inside parentheses struct literals are always allowed. let inner = self.pratt(0, true); let close = self.expect(TokenKind::RParen); let span = tok.span.cover(close.span); Expr::new(ExprKind::Group(Box::new(inner)), span) } // Error recovery _ => { self.errors.push(ParseError { span: tok.span, message: format!("unexpected token {} in expression", tok.kind), }); Expr::new(ExprKind::Error, tok.span) } } } // ── Left denotation (infix / postfix) ──────────────────────────────────── fn parse_led( &mut self, lhs: Expr, op_tok: Token<'src>, r_bp: u8, allow_struct_lit: bool, ) -> Expr { // Consume the operator token. self.advance(); match op_tok.kind { // Field access: `expr.field` TokenKind::Dot => { let field_tok = self.expect(TokenKind::Ident); let span = lhs.span.cover(field_tok.span); Expr::new( ExprKind::Field { expr: Box::new(lhs), field: field_tok.text.to_owned(), field_span: field_tok.span, }, span, ) } // Index: `expr[index]` TokenKind::LBracket => { // Inside brackets struct literals are always allowed. let index = self.pratt(0, true); let close = self.expect(TokenKind::RBracket); let span = lhs.span.cover(close.span); Expr::new( ExprKind::Index { expr: Box::new(lhs), index: Box::new(index), }, span, ) } // Call: `expr(args…)` TokenKind::LParen => { let (args, close_span) = self.parse_arg_list(); let span = lhs.span.cover(close_span); Expr::new( ExprKind::Call { callee: Box::new(lhs), args, }, span, ) } // Binary operator kind => { let op = token_to_binary_op(kind); let rhs = self.pratt(r_bp, allow_struct_lit); let span = lhs.span.cover(rhs.span); Expr::new( ExprKind::Binary { op, op_span: op_tok.span, lhs: Box::new(lhs), rhs: Box::new(rhs), }, span, ) } } } // ── Struct literal ──────────────────────────────────────────────────────── /// Called after we have already parsed the leading `Ident` as `lhs` and /// the current token is `{`. fn parse_struct_lit(&mut self, name_expr: Expr) -> Expr { let (name, name_span) = match name_expr.kind { ExprKind::Ident(ref s) => (s.clone(), name_expr.span), _ => unreachable!(), }; self.advance(); // consume `{` let fields = self.parse_struct_field_list(); let close = self.expect(TokenKind::RCurly); let span = name_span.cover(close.span); Expr::new( ExprKind::StructLit { name, name_span, fields, }, span, ) } fn parse_struct_field_list(&mut self) -> Vec { let mut fields = Vec::new(); loop { if matches!(self.current().kind, TokenKind::RCurly | TokenKind::Eof) { break; } fields.push(self.parse_struct_field()); if self.current().kind == TokenKind::Comma { self.advance(); } else { break; } } fields } fn parse_struct_field(&mut self) -> StructField { let name_tok = self.expect(TokenKind::Ident); self.expect(TokenKind::Colon); // Struct literals allowed inside field values. let value = self.pratt(0, true); StructField { name: name_tok.text.to_owned(), name_span: name_tok.span, value, } } // ── Argument list ───────────────────────────────────────────────────────── // ── Top-level definitions ───────────────────────────────────────────────── /// Parse an entire source file as a `Program`. pub fn parse_program(&mut self) -> Program { let start = self.current().span; let mut defs = Vec::new(); loop { if self.current().kind == TokenKind::Eof { break; } defs.push(self.parse_top_level_def()); } let span = start.cover(self.current().span); Program { defs, span } } /// Parse one top-level definition (`fn` or `struct`). pub fn parse_top_level_def(&mut self) -> TopLevelDef { let tok = self.current(); match tok.kind { TokenKind::Fn => self.parse_func_def(), TokenKind::Struct => self.parse_struct_def(), _ => { self.errors.push(ParseError { span: tok.span, message: format!("expected `fn` or `struct`, found {}", tok.kind), }); self.synchronize_top_level(); TopLevelDef { kind: TopLevelDefKind::Error, span: tok.span, } } } } /// Skip tokens until the next top-level boundary (`fn`, `struct`, or EOF). fn synchronize_top_level(&mut self) { loop { match self.current().kind { TokenKind::Eof | TokenKind::Fn | TokenKind::Struct => break, _ => { self.advance(); } } } } fn parse_func_def(&mut self) -> TopLevelDef { let kw = self.advance(); // consume `fn` let name_tok = self.expect(TokenKind::Ident); self.expect(TokenKind::LParen); let params = self.parse_param_list(); self.expect(TokenKind::RParen); let ret_ty = if self.current().kind == TokenKind::Arrow { self.advance(); Some(self.parse_type()) } else { None }; let body = self.parse_block(); let span = kw.span.cover(body.span); TopLevelDef { kind: TopLevelDefKind::Func(FuncDef { name: name_tok.text.to_owned(), name_span: name_tok.span, params, ret_ty, body, }), span, } } fn parse_param_list(&mut self) -> Vec { let mut params = Vec::new(); loop { if matches!(self.current().kind, TokenKind::RParen | TokenKind::Eof) { break; } params.push(self.parse_param()); if self.current().kind == TokenKind::Comma { self.advance(); } else { break; } } params } fn parse_param(&mut self) -> Param { let mutable = if self.current().kind == TokenKind::Mut { self.advance(); true } else { false }; let name_tok = self.expect(TokenKind::Ident); self.expect(TokenKind::Colon); let ty = self.parse_type(); Param { mutable, name: name_tok.text.to_owned(), name_span: name_tok.span, ty, } } fn parse_struct_def(&mut self) -> TopLevelDef { let kw = self.advance(); // consume `struct` let name_tok = self.expect(TokenKind::Ident); self.expect(TokenKind::LCurly); let fields = self.parse_field_def_list(); let close = self.expect(TokenKind::RCurly); let span = kw.span.cover(close.span); TopLevelDef { kind: TopLevelDefKind::Struct(StructDef { name: name_tok.text.to_owned(), name_span: name_tok.span, fields, }), span, } } fn parse_field_def_list(&mut self) -> Vec { let mut fields = Vec::new(); loop { if matches!(self.current().kind, TokenKind::RCurly | TokenKind::Eof) { break; } fields.push(self.parse_field_def()); if self.current().kind == TokenKind::Comma { self.advance(); } else { break; } } fields } fn parse_field_def(&mut self) -> FieldDef { let name_tok = self.expect(TokenKind::Ident); self.expect(TokenKind::Colon); let ty = self.parse_type(); FieldDef { name: name_tok.text.to_owned(), name_span: name_tok.span, ty, } } /// Parse `arg, arg, …` up to `)`. The opening `(` has already been /// consumed by `parse_led`. Returns `(args, close_span)`. fn parse_arg_list(&mut self) -> (Vec, Span) { let mut args = Vec::new(); loop { if matches!(self.current().kind, TokenKind::RParen | TokenKind::Eof) { break; } // Struct literals allowed inside argument lists. args.push(self.pratt(0, true)); if self.current().kind == TokenKind::Comma { self.advance(); } else { break; } } let close = self.expect(TokenKind::RParen); (args, close.span) } } // ── Tests ────────────────────────────────────────────────────────────────────── #[cfg(test)] mod tests { use super::*; use crate::ast::{ElseBranch, ExprKind, StmtKind, TopLevelDefKind, Type}; // ── Expression test helpers ─────────────────────────────────────────────── fn parse(src: &str) -> Expr { Parser::new(src).parse_expr(true) } fn parse_no_struct(src: &str) -> Expr { Parser::new(src).parse_expr(false) } // ── Statement test helpers ──────────────────────────────────────────────── fn stmt(src: &str) -> Stmt { Parser::new(src).parse_stmt() } fn parse_type_str(src: &str) -> Type { Parser::new(src).parse_type() } // ── Expression tests ────────────────────────────────────────────────────── #[test] fn int_literal() { let expr = parse("42"); assert!(matches!(expr.kind, ExprKind::IntLit(ref s) if s == "42")); } #[test] fn float_literal() { let expr = parse("3.14"); assert!(matches!(expr.kind, ExprKind::FloatLit(ref s) if s == "3.14")); } #[test] fn bool_literals() { assert!(matches!(parse("true").kind, ExprKind::Bool(true))); assert!(matches!(parse("false").kind, ExprKind::Bool(false))); } #[test] fn ident() { let expr = parse("foo"); assert!(matches!(expr.kind, ExprKind::Ident(ref s) if s == "foo")); } #[test] fn unary_neg() { let expr = parse("-42"); assert!(matches!( expr.kind, ExprKind::Unary { op: UnaryOp::Neg, .. } )); } #[test] fn unary_not() { let expr = parse("!x"); assert!(matches!( expr.kind, ExprKind::Unary { op: UnaryOp::Not, .. } )); } #[test] fn binary_add() { let expr = parse("a + b"); assert!(matches!( expr.kind, ExprKind::Binary { op: BinaryOp::Add, .. } )); } #[test] fn binary_precedence() { // `a + b * c` should parse as `a + (b * c)` let expr = parse("a + b * c"); match &expr.kind { ExprKind::Binary { op: BinaryOp::Add, lhs, rhs, .. } => { assert!(matches!(lhs.kind, ExprKind::Ident(ref s) if s == "a")); assert!(matches!( rhs.kind, ExprKind::Binary { op: BinaryOp::Mul, .. } )); } _ => panic!("expected binary add, got {:?}", expr.kind), } } #[test] fn comparison() { let expr = parse("a == b"); assert!(matches!( expr.kind, ExprKind::Binary { op: BinaryOp::Eq, .. } )); } #[test] fn logical_and_or() { // `a or b and c` → `a or (b and c)` (and binds tighter) let expr = parse("a or b and c"); match &expr.kind { ExprKind::Binary { op: BinaryOp::Or, rhs, .. } => { assert!(matches!( rhs.kind, ExprKind::Binary { op: BinaryOp::And, .. } )); } _ => panic!("expected or at top level"), } } #[test] fn grouped_expr() { let expr = parse("(a + b)"); assert!(matches!(expr.kind, ExprKind::Group(_))); } #[test] fn field_access() { let expr = parse("foo.bar"); assert!(matches!(expr.kind, ExprKind::Field { ref field, .. } if field == "bar")); } #[test] fn index_expr() { let expr = parse("arr[0]"); assert!(matches!(expr.kind, ExprKind::Index { .. })); } #[test] fn call_no_args() { let expr = parse("foo()"); match &expr.kind { ExprKind::Call { args, .. } => assert!(args.is_empty()), _ => panic!("expected call"), } } #[test] fn call_with_args() { let expr = parse("foo(1, 2, 3)"); match &expr.kind { ExprKind::Call { args, .. } => assert_eq!(args.len(), 3), _ => panic!("expected call"), } } #[test] fn struct_literal() { let expr = parse("Foo { x: 1, y: 2 }"); match &expr.kind { ExprKind::StructLit { name, fields, .. } => { assert_eq!(name, "Foo"); assert_eq!(fields.len(), 2); } _ => panic!("expected struct literal, got {:?}", expr.kind), } } #[test] fn struct_literal_disabled() { // With allow_struct_literals=false, `Foo { ... }` should NOT be a // struct literal — the Ident is parsed alone and `{` is left unconsumed. let expr = parse_no_struct("Foo { x: 1 }"); assert!(matches!(expr.kind, ExprKind::Ident(ref s) if s == "Foo")); } #[test] fn chained_field_access() { let expr = parse("a.b.c"); match &expr.kind { ExprKind::Field { expr: inner, field, .. } => { assert_eq!(field, "c"); assert!(matches!(inner.kind, ExprKind::Field { ref field, .. } if field == "b")); } _ => panic!("expected field access"), } } #[test] fn assignment_expr() { let expr = parse("a = b"); assert!(matches!( expr.kind, ExprKind::Binary { op: BinaryOp::Assign, .. } )); } #[test] fn assignment_right_associative() { // `a = b = c` → `a = (b = c)` let expr = parse("a = b = c"); match &expr.kind { ExprKind::Binary { op: BinaryOp::Assign, rhs, .. } => { assert!(matches!( rhs.kind, ExprKind::Binary { op: BinaryOp::Assign, .. } )); } _ => panic!("expected assignment"), } } #[test] fn assignment_stmt() { let s = stmt("a = b + 1;"); match &s.kind { StmtKind::Expr(e) => assert!(matches!( e.kind, ExprKind::Binary { op: BinaryOp::Assign, .. } )), _ => panic!("expected expr stmt with assignment"), } } #[test] fn deref_and_addrof() { assert!(matches!( parse("*p").kind, ExprKind::Unary { op: UnaryOp::Deref, .. } )); assert!(matches!( parse("&x").kind, ExprKind::Unary { op: UnaryOp::AddrOf, .. } )); } // ── Type tests ──────────────────────────────────────────────────────────── #[test] fn type_primitives() { assert!(matches!(parse_type_str("u8"), Type::U8)); assert!(matches!(parse_type_str("u16"), Type::U16)); assert!(matches!(parse_type_str("u32"), Type::U32)); assert!(matches!(parse_type_str("u64"), Type::U64)); assert!(matches!(parse_type_str("i8"), Type::I8)); assert!(matches!(parse_type_str("i16"), Type::I16)); assert!(matches!(parse_type_str("i32"), Type::I32)); assert!(matches!(parse_type_str("i64"), Type::I64)); assert!(matches!(parse_type_str("f32"), Type::F32)); assert!(matches!(parse_type_str("f64"), Type::F64)); assert!(matches!(parse_type_str("bool"), Type::Bool)); assert!(matches!(parse_type_str("char"), Type::Char)); } #[test] fn type_named() { assert!(matches!(parse_type_str("Foo"), Type::Named(ref s, _) if s == "Foo")); } #[test] fn type_pointer() { assert!(matches!(parse_type_str("*i32"), Type::Pointer(_))); } #[test] fn type_opaque_pointer() { assert!(matches!(parse_type_str("*opaque"), Type::OpaquePointer)); } #[test] fn type_array() { assert!( matches!(parse_type_str("[i32; 10]"), Type::Array { ref size, .. } if size == "10") ); } #[test] fn type_nested_pointer() { // `**i32` → Pointer(Pointer(I32)) assert!(matches!(parse_type_str("**i32"), Type::Pointer(_))); } // ── Statement tests ─────────────────────────────────────────────────────── #[test] fn let_basic() { let s = stmt("let x = 5;"); match &s.kind { StmtKind::Let { mutable, name, ty, init, .. } => { assert!(!mutable); assert_eq!(name, "x"); assert!(ty.is_none()); assert!(matches!( init.as_ref().unwrap().kind, ExprKind::IntLit(ref v) if v == "5" )); } _ => panic!("expected let"), } } #[test] fn let_mut() { assert!(matches!( stmt("let mut x = 5;").kind, StmtKind::Let { mutable: true, .. } )); } #[test] fn let_with_type() { let s = stmt("let x: i32 = 0;"); assert!(matches!( s.kind, StmtKind::Let { ty: Some(Type::I32), .. } )); } #[test] fn let_type_only() { let s = stmt("let x: bool;"); assert!(matches!( s.kind, StmtKind::Let { ty: Some(Type::Bool), init: None, .. } )); } #[test] fn return_unit() { assert!(matches!(stmt("return;").kind, StmtKind::Return(None))); } #[test] fn return_value() { assert!(matches!( stmt("return x + 1;").kind, StmtKind::Return(Some(_)) )); } #[test] fn if_no_else() { assert!(matches!( stmt("if x < 10 { foo(); }").kind, StmtKind::If { else_branch: None, .. } )); } #[test] fn if_else() { let s = stmt("if x { a(); } else { b(); }"); assert!(matches!( s.kind, StmtKind::If { else_branch: Some(ElseBranch::Block(_)), .. } )); } #[test] fn if_else_if() { let s = stmt("if a { } else if b { }"); assert!(matches!( s.kind, StmtKind::If { else_branch: Some(ElseBranch::If(_)), .. } )); } #[test] fn while_stmt() { assert!(matches!( stmt("while n > 0 { n = n - 1; }").kind, StmtKind::While { .. } )); } #[test] fn loop_stmt() { assert!(matches!( stmt("loop { break; }").kind, StmtKind::Loop { .. } )); } #[test] fn break_stmt() { assert!(matches!(stmt("break;").kind, StmtKind::Break)); } #[test] fn continue_stmt() { assert!(matches!(stmt("continue;").kind, StmtKind::Continue)); } #[test] fn block_stmt() { assert!(matches!(stmt("{ let x = 1; }").kind, StmtKind::Block(_))); } #[test] fn expr_stmt_call() { let s = stmt("foo(1, 2);"); match &s.kind { StmtKind::Expr(e) => assert!(matches!(e.kind, ExprKind::Call { .. })), _ => panic!("expected expr stmt"), } } #[test] fn nested_blocks() { // Blocks containing other blocks parse without panic let s = stmt("{ { let x = 1; } }"); match &s.kind { StmtKind::Block(outer) => { assert_eq!(outer.stmts.len(), 1); assert!(matches!(outer.stmts[0].kind, StmtKind::Block(_))); } _ => panic!("expected block"), } } // ── Recovery tests ──────────────────────────────────────────────────────── #[test] fn missing_semicolon_records_error() { // `let x = 5` with no `;` should record exactly one error let mut p = Parser::new("let x = 5"); p.parse_stmt(); assert!(!p.errors.is_empty(), "expected at least one error"); } #[test] fn stray_token_synchronizes_to_next_stmt() { // `,` cannot start a statement; parser should synchronize so that // the following `let` still parses correctly. let mut p = Parser::new(", let x = 1;"); let s1 = p.parse_stmt(); let s2 = p.parse_stmt(); assert!( matches!(s1.kind, StmtKind::Error), "first stmt should be Error" ); assert!( matches!(s2.kind, StmtKind::Let { .. }), "second stmt should be Let" ); } #[test] fn missing_let_name_inserts_dummy() { // `let = 5;` — missing name, but a dummy is inserted and parsing // continues; we expect errors but no panic. let mut p = Parser::new("let = 5;"); let s = p.parse_stmt(); assert!(!p.errors.is_empty()); // Even with the error, we should still get a Let node back. assert!(matches!(s.kind, StmtKind::Let { .. })); } #[test] fn if_condition_no_struct_literal() { // `if Foo { x: 1 } { }` — `Foo` is the condition (no struct literal // allowed), `{ x: 1 }` is an unexpected block; `{ }` is the body. // The important thing is that this doesn't panic. let mut p = Parser::new("if Foo { }"); let s = p.parse_stmt(); assert!(matches!(s.kind, StmtKind::If { .. })); } // ── Function definition tests ───────────────────────────────────────────── fn top(src: &str) -> TopLevelDef { Parser::new(src).parse_top_level_def() } #[test] fn func_def_empty() { let d = top("fn foo() { }"); match &d.kind { TopLevelDefKind::Func(f) => { assert_eq!(f.name, "foo"); assert!(f.params.is_empty()); assert!(f.ret_ty.is_none()); } _ => panic!("expected func def"), } } #[test] fn func_def_with_return_type() { let d = top("fn answer() -> i32 { return 42; }"); match &d.kind { TopLevelDefKind::Func(f) => { assert!(matches!(f.ret_ty, Some(Type::I32))); } _ => panic!("expected func def"), } } #[test] fn func_def_params() { let d = top("fn add(a: i32, b: i32) -> i32 { return a + b; }"); match &d.kind { TopLevelDefKind::Func(f) => { assert_eq!(f.params.len(), 2); assert_eq!(f.params[0].name, "a"); assert!(!f.params[0].mutable); assert!(matches!(f.params[0].ty, Type::I32)); assert_eq!(f.params[1].name, "b"); } _ => panic!("expected func def"), } } #[test] fn func_def_mut_param() { let d = top("fn inc(mut n: i32) -> i32 { n = n + 1; return n; }"); match &d.kind { TopLevelDefKind::Func(f) => { assert!(f.params[0].mutable); } _ => panic!("expected func def"), } } #[test] fn func_def_pointer_param() { let d = top("fn foo(p: *i32) { }"); match &d.kind { TopLevelDefKind::Func(f) => { assert!(matches!(f.params[0].ty, Type::Pointer(_))); } _ => panic!("expected func def"), } } // ── Struct definition tests ─────────────────────────────────────────────── #[test] fn struct_def_empty() { let d = top("struct Empty { }"); match &d.kind { TopLevelDefKind::Struct(s) => { assert_eq!(s.name, "Empty"); assert!(s.fields.is_empty()); } _ => panic!("expected struct def"), } } #[test] fn struct_def_with_fields() { let d = top("struct Point { x: f64, y: f64 }"); match &d.kind { TopLevelDefKind::Struct(s) => { assert_eq!(s.name, "Point"); assert_eq!(s.fields.len(), 2); assert_eq!(s.fields[0].name, "x"); assert!(matches!(s.fields[0].ty, Type::F64)); assert_eq!(s.fields[1].name, "y"); } _ => panic!("expected struct def"), } } #[test] fn struct_def_named_field_type() { let d = top("struct Node { value: i32, next: *Node }"); match &d.kind { TopLevelDefKind::Struct(s) => { assert!(matches!(s.fields[1].ty, Type::Pointer(_))); } _ => panic!("expected struct def"), } } // ── Program tests ───────────────────────────────────────────────────────── fn program(src: &str) -> Program { Parser::new(src).parse_program() } #[test] fn program_empty() { let p = program(""); assert!(p.defs.is_empty()); } #[test] fn program_single_func() { let p = program("fn main() { }"); assert_eq!(p.defs.len(), 1); assert!(matches!(p.defs[0].kind, TopLevelDefKind::Func(_))); } #[test] fn program_struct_and_func() { let src = "struct Point { x: f64, y: f64 } fn main() { }"; let p = program(src); assert_eq!(p.defs.len(), 2); assert!(matches!(p.defs[0].kind, TopLevelDefKind::Struct(_))); assert!(matches!(p.defs[1].kind, TopLevelDefKind::Func(_))); } #[test] fn program_multiple_funcs() { let src = "fn foo() { } fn bar() -> i32 { return 1; } fn baz(x: bool) { }"; let p = program(src); assert_eq!(p.defs.len(), 3); } // ── Top-level recovery tests ────────────────────────────────────────────── #[test] fn top_level_stray_token_synchronizes() { // A stray token should produce Error and then the next definition // should still parse correctly. let mut p = Parser::new("42 fn foo() { }"); let d1 = p.parse_top_level_def(); let d2 = p.parse_top_level_def(); assert!(matches!(d1.kind, TopLevelDefKind::Error)); assert!(matches!(d2.kind, TopLevelDefKind::Func(_))); assert!(!p.errors.is_empty()); } #[test] fn top_level_missing_func_name_inserts_dummy() { // `fn () { }` — missing name; expect() inserts a dummy, no panic. let mut p = Parser::new("fn () { }"); let d = p.parse_top_level_def(); assert!(!p.errors.is_empty()); assert!(matches!(d.kind, TopLevelDefKind::Func(_))); } }