use crate::ast::*; use crate::lexer::{Token, TokenKind}; use crate::error::RuntimeError; pub struct Parser { tokens: Vec, current: usize, } impl Parser { pub fn new(tokens: Vec) -> Self { Self { tokens, current: 0 } } pub fn parse(&mut self) -> (Vec, Vec) { let mut statements = Vec::new(); let mut errors = Vec::new(); while !self.is_at_end() { match self.declaration() { Ok(stmt) => statements.push(stmt), Err(err) => { errors.push(err); self.synchronize(); } } } (statements, errors) } fn declaration(&mut self) -> Result { if self.match_kind(&[TokenKind::Let]) { self.let_declaration(true) } else if self.match_kind(&[TokenKind::Const]) { self.let_declaration(false) } else if self.match_kind(&[TokenKind::Fn]) { self.fn_declaration() } else { self.statement() } } fn statement(&mut self) -> Result { if self.match_kind(&[TokenKind::If]) { self.if_statement() } else if self.match_kind(&[TokenKind::While]) { self.while_statement() } else if self.match_kind(&[TokenKind::For]) { self.for_statement() } else if self.match_kind(&[TokenKind::Break]) { self.break_statement() } else if self.match_kind(&[TokenKind::Continue]) { self.continue_statement() } else if self.match_kind(&[TokenKind::Return]) { self.return_statement() } else if self.match_kind(&[TokenKind::LeftBrace]) { Ok(Stmt::Block(self.block()?)) } else { self.expression_statement() } } fn let_declaration(&mut self, mutable: bool) -> Result { let name = self.consume_ident("Expected variable name.")?; self.consume(TokenKind::Equal, "Expected '=' after variable name.")?; let initializer = self.expression()?; self.match_kind(&[TokenKind::Semicolon]); // 分号可选 Ok(Stmt::Let { name, initializer, mutable }) } fn fn_declaration(&mut self) -> Result { let name = self.consume_ident("Expected function name.")?; self.consume(TokenKind::LeftParen, "Expected '(' after function name.")?; let mut params = Vec::new(); if !self.check(&TokenKind::RightParen) { loop { params.push(self.consume_ident("Expected parameter name.")?); if !self.match_kind(&[TokenKind::Comma]) { break; } } } self.consume(TokenKind::RightParen, "Expected ')' after parameters.")?; self.consume(TokenKind::LeftBrace, "Expected '{' before function body.")?; let body = self.block()?; Ok(Stmt::Function { name, params, body }) } fn lambda_expr(&mut self) -> Result { self.consume(TokenKind::LeftParen, "Expected '(' after 'fn'.")?; let mut params = Vec::new(); if !self.check(&TokenKind::RightParen) { loop { params.push(self.consume_ident("Expected parameter name.")?); if !self.match_kind(&[TokenKind::Comma]) { break; } } } self.consume(TokenKind::RightParen, "Expected ')' after parameters.")?; self.consume(TokenKind::LeftBrace, "Expected '{' before function body.")?; let body = self.block()?; Ok(Expr::Lambda { params, body }) } fn if_statement(&mut self) -> Result { self.consume(TokenKind::LeftParen, "Expected '(' after 'if'.")?; let condition = self.expression()?; self.consume(TokenKind::RightParen, "Expected ')' after condition.")?; let then_branch = Box::new(self.statement()?); let else_branch = if self.match_kind(&[TokenKind::Else]) { Some(Box::new(self.statement()?)) } else { None }; Ok(Stmt::If { condition, then_branch, else_branch }) } fn while_statement(&mut self) -> Result { self.consume(TokenKind::LeftParen, "Expected '(' after 'while'.")?; let condition = self.expression()?; self.consume(TokenKind::RightParen, "Expected ')' after condition.")?; let body = Box::new(self.statement()?); Ok(Stmt::While { condition, body }) } fn for_statement(&mut self) -> Result { self.consume(TokenKind::LeftParen, "Expected '(' after 'for'.")?; // Detect for-in: for (Identifier in expr) body if let TokenKind::Identifier(var_name) = &self.peek().kind { let var_name = var_name.clone(); if matches!(self.peek_next().kind, TokenKind::In) { self.advance(); // consume identifier self.advance(); // consume 'in' let iterable = self.expression()?; self.consume(TokenKind::RightParen, "Expected ')' after for-in expression.")?; let body = Box::new(self.statement()?); return Ok(Stmt::ForIn { var_name, iterable, body }); } } let initializer = if self.match_kind(&[TokenKind::Semicolon]) { None } else if self.match_kind(&[TokenKind::Let]) { Some(self.let_declaration(true)?) } else { Some(self.expression_statement()?) }; let condition = if !self.check(&TokenKind::Semicolon) { Some(self.expression()?) } else { None }; self.consume(TokenKind::Semicolon, "Expected ';' after loop condition.")?; let step = if !self.check(&TokenKind::RightParen) { Some(self.expression()?) } else { None }; self.consume(TokenKind::RightParen, "Expected ')' after for clauses.")?; let body = Box::new(self.statement()?); Ok(Stmt::For { initializer: initializer.map(Box::new), condition, step, body }) } fn break_statement(&mut self) -> Result { self.match_kind(&[TokenKind::Semicolon]); // 分号可选 Ok(Stmt::Break) } fn continue_statement(&mut self) -> Result { self.match_kind(&[TokenKind::Semicolon]); // 分号可选 Ok(Stmt::Continue) } fn return_statement(&mut self) -> Result { let value = if !self.check(&TokenKind::Semicolon) && !self.check(&TokenKind::RightBrace) { Some(self.expression()?) } else { None }; self.match_kind(&[TokenKind::Semicolon]); // 分号可选 Ok(Stmt::Return(value)) } fn block(&mut self) -> Result, RuntimeError> { let mut statements = Vec::new(); while !self.check(&TokenKind::RightBrace) && !self.is_at_end() { statements.push(self.declaration()?); } self.consume(TokenKind::RightBrace, "Expected '}' after block.")?; Ok(statements) } fn expression_statement(&mut self) -> Result { let expr = self.expression()?; self.match_kind(&[TokenKind::Semicolon]); // 分号可选 Ok(Stmt::ExprStmt(expr)) } fn expression(&mut self) -> Result { self.assignment() } fn ternary(&mut self) -> Result { let expr = self.logical_or()?; if self.match_kind(&[TokenKind::Question]) { let then_branch = self.assignment()?; self.consume(TokenKind::Colon, "Expected ':' after then branch of ternary.")?; let else_branch = self.assignment()?; Ok(Expr::Ternary { condition: Box::new(expr), then_branch: Box::new(then_branch), else_branch: Box::new(else_branch), }) } else { Ok(expr) } } fn assignment(&mut self) -> Result { let expr = self.ternary()?; let op = if self.match_kind(&[TokenKind::Equal]) { Some(AssignOp::Equal) } else if self.match_kind(&[TokenKind::PlusEqual]) { Some(AssignOp::PlusEqual) } else if self.match_kind(&[TokenKind::MinusEqual]) { Some(AssignOp::MinusEqual) } else if self.match_kind(&[TokenKind::StarEqual]) { Some(AssignOp::StarEqual) } else if self.match_kind(&[TokenKind::SlashEqual]) { Some(AssignOp::SlashEqual) } else if self.match_kind(&[TokenKind::PercentEqual]) { Some(AssignOp::PercentEqual) } else { None }; if let Some(op) = op { let value = self.assignment()?; return match expr { Expr::Variable(name) => Ok(Expr::Assign { name, op, value: Box::new(value) }), Expr::Get { object, name } => Ok(Expr::Set { object, name, op, value: Box::new(value) }), Expr::IndexGet { array, index } => Ok(Expr::IndexSet { array, index, op, value: Box::new(value) }), _ => Err(RuntimeError::parse("Invalid assignment target.", self.previous().clone())), }; } Ok(expr) } fn logical_or(&mut self) -> Result { let mut expr = self.logical_and()?; while self.match_kind(&[TokenKind::OrOr]) { let right = self.logical_and()?; expr = Expr::Logical { left: Box::new(expr), op: LogicalOp::Or, right: Box::new(right), }; } Ok(expr) } fn logical_and(&mut self) -> Result { let mut expr = self.equality()?; while self.match_kind(&[TokenKind::AndAnd]) { let right = self.equality()?; expr = Expr::Logical { left: Box::new(expr), op: LogicalOp::And, right: Box::new(right), }; } Ok(expr) } fn equality(&mut self) -> Result { let mut expr = self.comparison()?; while self.match_kind(&[TokenKind::EqualEqual, TokenKind::BangEqual]) { let op = match self.previous().kind { TokenKind::EqualEqual => BinaryOp::Equal, TokenKind::BangEqual => BinaryOp::NotEqual, _ => unreachable!(), }; let right = self.comparison()?; expr = Expr::Binary { left: Box::new(expr), op, right: Box::new(right), }; } Ok(expr) } fn comparison(&mut self) -> Result { let mut expr = self.term()?; while self.match_kind(&[TokenKind::Greater, TokenKind::GreaterEqual, TokenKind::Less, TokenKind::LessEqual]) { let op = match self.previous().kind { TokenKind::Greater => BinaryOp::Greater, TokenKind::GreaterEqual => BinaryOp::GreaterEqual, TokenKind::Less => BinaryOp::Less, TokenKind::LessEqual => BinaryOp::LessEqual, _ => unreachable!(), }; let right = self.term()?; expr = Expr::Binary { left: Box::new(expr), op, right: Box::new(right), }; } Ok(expr) } fn term(&mut self) -> Result { let mut expr = self.factor()?; while self.match_kind(&[TokenKind::Plus, TokenKind::Minus]) { let op = match self.previous().kind { TokenKind::Plus => BinaryOp::Add, TokenKind::Minus => BinaryOp::Sub, _ => unreachable!(), }; let right = self.factor()?; expr = Expr::Binary { left: Box::new(expr), op, right: Box::new(right), }; } Ok(expr) } fn factor(&mut self) -> Result { let mut expr = self.unary()?; while self.match_kind(&[TokenKind::Star, TokenKind::Slash, TokenKind::Percent]) { let op = match self.previous().kind { TokenKind::Star => BinaryOp::Mul, TokenKind::Slash => BinaryOp::Div, TokenKind::Percent => BinaryOp::Mod, _ => unreachable!(), }; let right = self.unary()?; expr = Expr::Binary { left: Box::new(expr), op, right: Box::new(right), }; } Ok(expr) } fn unary(&mut self) -> Result { if self.match_kind(&[TokenKind::Bang, TokenKind::Minus]) { let op = match self.previous().kind { TokenKind::Bang => UnaryOp::Not, TokenKind::Minus => UnaryOp::Negate, _ => unreachable!(), }; let right = self.unary()?; return Ok(Expr::Unary { op, right: Box::new(right), }); } self.call() } fn call(&mut self) -> Result { let mut expr = self.primary()?; loop { if self.match_kind(&[TokenKind::LeftParen]) { let mut arguments = Vec::new(); if !self.check(&TokenKind::RightParen) { loop { arguments.push(self.expression()?); if !self.match_kind(&[TokenKind::Comma]) { break; } } } self.consume(TokenKind::RightParen, "Expected ')' after arguments.")?; expr = Expr::Call { callee: Box::new(expr), arguments, }; } else if self.match_kind(&[TokenKind::Dot]) { let name = self.consume_ident("Expected property name after '.'.")?; expr = Expr::Get { object: Box::new(expr), name, }; } else if self.match_kind(&[TokenKind::LeftBracket]) { let index = self.expression()?; self.consume(TokenKind::RightBracket, "Expected ']' after index.")?; expr = Expr::IndexGet { array: Box::new(expr), index: Box::new(index), }; } else { break; } } Ok(expr) } fn primary(&mut self) -> Result { // 布尔值 if self.match_kind(&[TokenKind::True]) { return Ok(Expr::Literal(Literal::Bool(true))); } if self.match_kind(&[TokenKind::False]) { return Ok(Expr::Literal(Literal::Bool(false))); } if self.match_kind(&[TokenKind::Nil]) { return Ok(Expr::Literal(Literal::Nil)); } // 数字 if let TokenKind::Number(n) = self.peek().kind { self.advance(); return Ok(Expr::Literal(Literal::Number(n))); } // 字符串 if let TokenKind::String(s) = &self.peek().kind { let s = s.clone(); self.advance(); return Ok(Expr::Literal(Literal::String(s))); } // 标识符(变量) if let TokenKind::Identifier(name) = &self.peek().kind { let name = name.clone(); self.advance(); return Ok(Expr::Variable(name)); } // 匿名函数(闭包): fn(params) { body } if self.match_kind(&[TokenKind::Fn]) { return self.lambda_expr(); } // 括号表达式 if self.match_kind(&[TokenKind::LeftParen]) { let expr = self.expression()?; self.consume(TokenKind::RightParen, "Expected ')' after expression.")?; return Ok(expr); } if self.match_kind(&[TokenKind::LeftBrace]) { return self.object_literal(); } if self.match_kind(&[TokenKind::LeftBracket]) { return self.array_literal(); } Err(RuntimeError::parse("Expected expression.", self.peek().clone())) } fn match_kind(&mut self, kinds: &[TokenKind]) -> bool { for kind in kinds { if self.check(kind) { self.advance(); return true; } } false } fn consume(&mut self, kind: TokenKind, msg: &str) -> Result<(), RuntimeError> { if self.check(&kind) { self.advance(); Ok(()) } else { Err(RuntimeError::parse(msg, self.peek().clone())) } } fn consume_ident(&mut self, msg: &str) -> Result { match &self.peek().kind { TokenKind::Identifier(name) => { let name = name.clone(); self.advance(); Ok(name) } _ => Err(RuntimeError::parse(msg, self.peek().clone())), } } fn check(&self, kind: &TokenKind) -> bool { if self.is_at_end() { return false; } std::mem::discriminant(&self.peek().kind) == std::mem::discriminant(kind) } fn advance(&mut self) -> &Token { if !self.is_at_end() { self.current += 1; } self.previous() } fn is_at_end(&self) -> bool { matches!(self.peek().kind, TokenKind::EOF) } fn peek(&self) -> &Token { &self.tokens[self.current] } fn peek_next(&self) -> &Token { &self.tokens[self.current + 1] } fn previous(&self) -> &Token { &self.tokens[self.current - 1] } fn object_literal(&mut self) -> Result { let mut properties = Vec::new(); if !self.check(&TokenKind::RightBrace) { loop { let name = self.consume_ident("Expected property name in object literal")?; self.consume(TokenKind::Colon, "Expected ':' after property name in object literal")?; let value = self.expression()?; properties.push((name, value)); if !self.match_kind(&[TokenKind::Comma]) { break; } } } self.consume(TokenKind::RightBrace, "Expected '}' after object literal")?; Ok(Expr::ObjectLiteral { properties }) } fn array_literal(&mut self) -> Result { let mut elements = Vec::new(); if !self.check(&TokenKind::RightBracket) { loop { elements.push(self.expression()?); if !self.match_kind(&[TokenKind::Comma]) { break; } } } self.consume(TokenKind::RightBracket, "Expected ']' after array literal")?; Ok(Expr::ArrayLiteral { elements }) } fn synchronize(&mut self) { // Always consume at least one token to guarantee forward progress, // preventing infinite loops when previous() is already a semicolon. if !self.is_at_end() { self.advance(); } while !self.is_at_end() { if self.current > 0 && matches!(self.previous().kind, TokenKind::Semicolon) { return; } match self.peek().kind { TokenKind::Let | TokenKind::Const | TokenKind::Fn | TokenKind::If | TokenKind::While | TokenKind::For | TokenKind::Return | TokenKind::Break | TokenKind::Continue => return, _ => { self.advance(); } } } } } // ======================================================================== // Tests // ======================================================================== #[cfg(test)] mod tests { use super::*; use crate::lexer::Lexer; /// Helper: lex + parse, return (stmts, errors). fn parse(input: &str) -> (Vec, Vec) { let (tokens, _) = Lexer::new(input).tokenize(); Parser::new(tokens).parse() } /// Helper: lex + parse single expression (wraps in `let _ = ;`). fn parse_expr(input: &str) -> Expr { let (stmts, errors) = parse(&format!("let __x = {};", input)); assert!(errors.is_empty(), "Parse errors: {:?}", errors); match &stmts[0] { Stmt::Let { initializer, .. } => initializer.clone(), _ => panic!("Expected Let statement"), } } /// Helper: lex + parse single statement. fn parse_stmt(input: &str) -> Stmt { let (stmts, errors) = parse(input); assert!(errors.is_empty(), "Parse errors: {:?}", errors); assert_eq!(stmts.len(), 1); stmts[0].clone() } // ----- literals ----- #[test] fn parse_number() { match parse_expr("42") { Expr::Literal(Literal::Number(42.0)) => {} e => panic!("Expected Number(42.0), got {:?}", e), } } #[test] fn parse_float() { match parse_expr("3.14") { Expr::Literal(Literal::Number(n)) => assert!((n - 3.14).abs() < 0.001), e => panic!("Expected Number, got {:?}", e), } } #[test] fn parse_string() { match parse_expr("\"hello\"") { Expr::Literal(Literal::String(s)) => assert_eq!(s, "hello"), e => panic!("Expected String, got {:?}", e), } } #[test] fn parse_bool_true() { match parse_expr("true") { Expr::Literal(Literal::Bool(true)) => {} e => panic!("Expected Bool(true), got {:?}", e), } } #[test] fn parse_bool_false() { match parse_expr("false") { Expr::Literal(Literal::Bool(false)) => {} e => panic!("Expected Bool(false), got {:?}", e), } } #[test] fn parse_nil() { match parse_expr("nil") { Expr::Literal(Literal::Nil) => {} e => panic!("Expected Nil, got {:?}", e), } } // ----- variables ----- #[test] fn parse_variable() { match parse_expr("x") { Expr::Variable(name) => assert_eq!(name, "x"), e => panic!("Expected Variable, got {:?}", e), } } // ----- unary ----- #[test] fn parse_negation() { match parse_expr("-5") { Expr::Unary { op: UnaryOp::Negate, right } => { match *right { Expr::Literal(Literal::Number(5.0)) => {} e => panic!("Expected Number(5), got {:?}", e), } } e => panic!("Expected Unary(Negate, Number), got {:?}", e), } } #[test] fn parse_not() { match parse_expr("!true") { Expr::Unary { op: UnaryOp::Not, right } => { match *right { Expr::Literal(Literal::Bool(true)) => {} e => panic!("Expected Bool(true), got {:?}", e), } } e => panic!("Expected Unary(Not, Bool), got {:?}", e), } } #[test] fn parse_double_negation() { match parse_expr("!!true") { Expr::Unary { op: UnaryOp::Not, right } => { match *right { Expr::Unary { op: UnaryOp::Not, .. } => {} e => panic!("Expected nested Unary, got {:?}", e), } } e => panic!("Expected Unary(Not, ...), got {:?}", e), } } // ----- binary precedence ----- #[test] fn parse_addition() { match parse_expr("1 + 2") { Expr::Binary { op: BinaryOp::Add, left, right } => { match (*left, *right) { (Expr::Literal(Literal::Number(1.0)), Expr::Literal(Literal::Number(2.0))) => {} e => panic!("Expected (1, 2), got {:?}", e), } } e => panic!("Expected Binary(Add), got {:?}", e), } } #[test] fn parse_multiplication_precedence() { // 1 + 2 * 3 => 1 + (2 * 3) match parse_expr("1 + 2 * 3") { Expr::Binary { op: BinaryOp::Add, left, right } => { match *left { Expr::Literal(Literal::Number(1.0)) => {} e => panic!("Expected left=1, got {:?}", e), } match *right { Expr::Binary { op: BinaryOp::Mul, .. } => {} e => panic!("Expected right=Binary(Mul), got {:?}", e), } } e => panic!("Expected Binary(Add), got {:?}", e), } } #[test] fn parse_modulo() { match parse_expr("7 % 3") { Expr::Binary { op: BinaryOp::Mod, .. } => {} e => panic!("Expected Binary(Mod), got {:?}", e), } } // ----- comparison ----- #[test] fn parse_comparison_chain() { // a < b == c match parse_expr("a < b == c") { Expr::Binary { op: BinaryOp::Equal, left, right } => { match *left { Expr::Binary { op: BinaryOp::Less, .. } => {} e => panic!("Expected left=Binary(Less), got {:?}", e), } match *right { Expr::Variable(name) => assert_eq!(name, "c"), e => panic!("Expected right=Variable(c), got {:?}", e), } } e => panic!("Expected Binary(Equal), got {:?}", e), } } // ----- logical ----- #[test] fn parse_logical_and() { match parse_expr("true && false") { Expr::Logical { op: LogicalOp::And, .. } => {} e => panic!("Expected Logical(And), got {:?}", e), } } #[test] fn parse_logical_or() { match parse_expr("true || false") { Expr::Logical { op: LogicalOp::Or, .. } => {} e => panic!("Expected Logical(Or), got {:?}", e), } } #[test] fn parse_logical_precedence() { // a || b && c => a || (b && c) match parse_expr("a || b && c") { Expr::Logical { op: LogicalOp::Or, left, right } => { match *left { Expr::Variable(name) => assert_eq!(name, "a"), e => panic!("Expected left=a, got {:?}", e), } match *right { Expr::Logical { op: LogicalOp::And, .. } => {} e => panic!("Expected right=Logical(And), got {:?}", e), } } e => panic!("Expected Logical(Or), got {:?}", e), } } // ----- ternary ----- #[test] fn parse_ternary_simple() { match parse_expr("a ? 1 : 2") { Expr::Ternary { condition, then_branch, else_branch } => { assert!(matches!(*condition, Expr::Variable(_))); assert!(matches!(*then_branch, Expr::Literal(Literal::Number(1.0)))); assert!(matches!(*else_branch, Expr::Literal(Literal::Number(2.0)))); } e => panic!("Expected Ternary, got {:?}", e), } } #[test] fn parse_ternary_nested_right_assoc() { // a ? b : c ? d : e → a ? b : (c ? d : e) match parse_expr("a ? b : c ? d : e") { Expr::Ternary { condition, then_branch, else_branch } => { assert!(matches!(*condition, Expr::Variable(_))); assert!(matches!(*then_branch, Expr::Variable(_))); assert!(matches!(*else_branch, Expr::Ternary { .. })); } e => panic!("Expected Ternary with nested Ternary else, got {:?}", e), } } #[test] fn parse_ternary_with_assignment_in_else() { // a ? b : c = d → a ? b : (c = d) match parse_expr("a ? b : c = 5") { Expr::Ternary { else_branch, .. } => { assert!(matches!(*else_branch, Expr::Assign { .. })); } e => panic!("Expected Ternary with Assign else, got {:?}", e), } } // ----- assignment ----- #[test] fn parse_simple_assignment() { match parse_expr("x = 5") { Expr::Assign { name, op: AssignOp::Equal, value } => { assert_eq!(name, "x"); match *value { Expr::Literal(Literal::Number(5.0)) => {} e => panic!("Expected Number(5), got {:?}", e), } } e => panic!("Expected Assign, got {:?}", e), } } #[test] fn parse_compound_assignment() { match parse_expr("x += 2") { Expr::Assign { name, op: AssignOp::PlusEqual, value } => { assert_eq!(name, "x"); match *value { Expr::Literal(Literal::Number(2.0)) => {} e => panic!("Expected Number(2), got {:?}", e), } } e => panic!("Expected Assign(PlusEqual), got {:?}", e), } } #[test] fn parse_all_compound_ops() { let ops = [ ("+=", AssignOp::PlusEqual), ("-=", AssignOp::MinusEqual), ("*=", AssignOp::StarEqual), ("/=", AssignOp::SlashEqual), ("%=", AssignOp::PercentEqual), ]; for (op_str, expected_op) in &ops { let expr = parse_expr(&format!("x {} 1", op_str)); match expr { Expr::Assign { op, .. } => { assert_eq!(std::mem::discriminant(&op), std::mem::discriminant(expected_op)); } e => panic!("Expected Assign for '{}', got {:?}", op_str, e), } } } // ----- property access ----- #[test] fn parse_property_get() { match parse_expr("obj.name") { Expr::Get { object, name } => { match *object { Expr::Variable(n) => assert_eq!(n, "obj"), e => panic!("Expected Variable(obj), got {:?}", e), } assert_eq!(name, "name"); } e => panic!("Expected Get, got {:?}", e), } } #[test] fn parse_property_set() { match parse_expr("obj.name = 5") { Expr::Set { object, name, op: AssignOp::Equal, value } => { match *object { Expr::Variable(n) => assert_eq!(n, "obj"), e => panic!("Expected Variable(obj), got {:?}", e), } assert_eq!(name, "name"); match *value { Expr::Literal(Literal::Number(5.0)) => {} e => panic!("Expected Number(5), got {:?}", e), } } e => panic!("Expected Set, got {:?}", e), } } #[test] fn parse_chained_property_access() { match parse_expr("a.b.c") { Expr::Get { object, name } => { assert_eq!(name, "c"); match *object { Expr::Get { name: ref inner, .. } => assert_eq!(inner, "b"), e => panic!("Expected chained Get, got {:?}", e), } } e => panic!("Expected Get, got {:?}", e), } } // ----- index access ----- #[test] fn parse_index_get() { match parse_expr("arr[0]") { Expr::IndexGet { array, index } => { match *array { Expr::Variable(n) => assert_eq!(n, "arr"), e => panic!("Expected Variable(arr), got {:?}", e), } match *index { Expr::Literal(Literal::Number(0.0)) => {} e => panic!("Expected Number(0), got {:?}", e), } } e => panic!("Expected IndexGet, got {:?}", e), } } #[test] fn parse_index_set() { match parse_expr("arr[i] = 42") { Expr::IndexSet { array, index, op: AssignOp::Equal, value } => { match *array { Expr::Variable(n) => assert_eq!(n, "arr"), e => panic!("Expected Variable(arr), got {:?}", e), } match *index { Expr::Variable(n) => assert_eq!(n, "i"), e => panic!("Expected Variable(i), got {:?}", e), } match *value { Expr::Literal(Literal::Number(42.0)) => {} e => panic!("Expected Number(42), got {:?}", e), } } e => panic!("Expected IndexSet, got {:?}", e), } } #[test] fn parse_nested_index() { match parse_expr("a[1][2]") { Expr::IndexGet { array, .. } => { match *array { Expr::IndexGet { .. } => {} e => panic!("Expected nested IndexGet, got {:?}", e), } } e => panic!("Expected IndexGet, got {:?}", e), } } // ----- function calls ----- #[test] fn parse_call_no_args() { match parse_expr("f()") { Expr::Call { callee, arguments } => { match *callee { Expr::Variable(n) => assert_eq!(n, "f"), e => panic!("Expected Variable(f), got {:?}", e), } assert!(arguments.is_empty()); } e => panic!("Expected Call, got {:?}", e), } } #[test] fn parse_call_with_args() { match parse_expr("add(1, 2, 3)") { Expr::Call { callee, arguments } => { match *callee { Expr::Variable(n) => assert_eq!(n, "add"), e => panic!("Expected Variable(add), got {:?}", e), } assert_eq!(arguments.len(), 3); } e => panic!("Expected Call, got {:?}", e), } } #[test] fn parse_chained_calls() { match parse_expr("f()()") { Expr::Call { callee, .. } => { match *callee { Expr::Call { callee, .. } => { match *callee { Expr::Variable(n) => assert_eq!(n, "f"), e => panic!("Expected Variable(f), got {:?}", e), } } e => panic!("Expected inner Call, got {:?}", e), } } e => panic!("Expected Call, got {:?}", e), } } #[test] fn parse_method_style_call() { // obj.method() match parse_expr("obj.method()") { Expr::Call { callee, .. } => { match *callee { Expr::Get { .. } => {} e => panic!("Expected Get, got {:?}", e), } } e => panic!("Expected Call, got {:?}", e), } } // ----- lambda ----- #[test] fn parse_simple_lambda() { match parse_expr("fn(x, y) { return x + y; }") { Expr::Lambda { params, body } => { assert_eq!(params, vec!["x", "y"]); assert_eq!(body.len(), 1); } e => panic!("Expected Lambda, got {:?}", e), } } #[test] fn parse_lambda_no_params() { match parse_expr("fn() { return 42; }") { Expr::Lambda { params, body } => { assert!(params.is_empty()); assert_eq!(body.len(), 1); } e => panic!("Expected Lambda, got {:?}", e), } } // ----- object literal ----- #[test] fn parse_empty_object() { match parse_expr("{}") { Expr::ObjectLiteral { properties } => assert!(properties.is_empty()), e => panic!("Expected ObjectLiteral, got {:?}", e), } } #[test] fn parse_object_with_properties() { match parse_expr("{ name: \"aster\", version: 1 }") { Expr::ObjectLiteral { properties } => { assert_eq!(properties.len(), 2); assert_eq!(properties[0].0, "name"); assert_eq!(properties[1].0, "version"); } e => panic!("Expected ObjectLiteral, got {:?}", e), } } // ----- array literal ----- #[test] fn parse_empty_array() { match parse_expr("[]") { Expr::ArrayLiteral { elements } => assert!(elements.is_empty()), e => panic!("Expected ArrayLiteral, got {:?}", e), } } #[test] fn parse_array_with_elements() { match parse_expr("[1, 2, 3]") { Expr::ArrayLiteral { elements } => assert_eq!(elements.len(), 3), e => panic!("Expected ArrayLiteral, got {:?}", e), } } #[test] fn parse_nested_arrays() { match parse_expr("[[1, 2], [3, 4]]") { Expr::ArrayLiteral { elements } => { assert_eq!(elements.len(), 2); for e in &elements { assert!(matches!(e, Expr::ArrayLiteral { .. })); } } e => panic!("Expected ArrayLiteral, got {:?}", e), } } // ----- statements ----- #[test] fn parse_let_statement() { match parse_stmt("let x = 5;") { Stmt::Let { name, initializer, .. } => { assert_eq!(name, "x"); assert!(matches!(initializer, Expr::Literal(Literal::Number(5.0)))); } e => panic!("Expected Let, got {:?}", e), } } #[test] fn parse_let_without_semicolon() { // Semicolons are optional match parse_stmt("let x = 5") { Stmt::Let { name, .. } => assert_eq!(name, "x"), e => panic!("Expected Let, got {:?}", e), } } #[test] fn parse_const_statement() { match parse_stmt("const x = 5;") { Stmt::Let { name, mutable, .. } => { assert_eq!(name, "x"); assert!(!mutable, "const should set mutable=false"); } e => panic!("Expected Let (const), got {:?}", e), } } #[test] fn parse_expression_statement() { match parse_stmt("x;") { Stmt::ExprStmt(Expr::Variable(name)) => assert_eq!(name, "x"), e => panic!("Expected ExprStmt(Variable), got {:?}", e), } } #[test] fn parse_if_statement() { match parse_stmt("if (x) { 1; }") { Stmt::If { condition, then_branch, else_branch } => { assert!(matches!(condition, Expr::Variable(_))); assert!(matches!(*then_branch, Stmt::Block(_))); assert!(else_branch.is_none()); } e => panic!("Expected If, got {:?}", e), } } #[test] fn parse_if_else_statement() { match parse_stmt("if (x) { 1; } else { 2; }") { Stmt::If { else_branch, .. } => { assert!(else_branch.is_some()); } e => panic!("Expected If with else, got {:?}", e), } } #[test] fn parse_while_statement() { match parse_stmt("while (x < 10) { x = x + 1; }") { Stmt::While { condition, body } => { assert!(matches!(condition, Expr::Binary { .. })); assert!(matches!(*body, Stmt::Block(_))); } e => panic!("Expected While, got {:?}", e), } } #[test] fn parse_for_statement() { match parse_stmt("for (let i = 0; i < 10; i = i + 1) { }") { Stmt::For { initializer, condition, step, body } => { assert!(initializer.is_some()); assert!(condition.is_some()); assert!(step.is_some()); assert!(matches!(*body, Stmt::Block(_))); } e => panic!("Expected For, got {:?}", e), } } #[test] fn parse_for_empty_clauses() { match parse_stmt("for (;;) { }") { Stmt::For { initializer, condition, step, .. } => { assert!(initializer.is_none()); assert!(condition.is_none()); assert!(step.is_none()); } e => panic!("Expected For, got {:?}", e), } } #[test] fn parse_for_in_array() { match parse_stmt("for (x in arr) { print(x); }") { Stmt::ForIn { var_name, iterable, body } => { assert_eq!(var_name, "x"); assert!(matches!(iterable, Expr::Variable(_))); assert!(matches!(*body, Stmt::Block(_))); } e => panic!("Expected ForIn, got {:?}", e), } } #[test] fn parse_for_in_with_object_literal() { match parse_stmt("for (k in {a:1,b:2}) { print(k); }") { Stmt::ForIn { var_name, iterable, .. } => { assert_eq!(var_name, "k"); assert!(matches!(iterable, Expr::ObjectLiteral { .. })); } e => panic!("Expected ForIn, got {:?}", e), } } #[test] fn parse_for_in_with_expression() { match parse_stmt("for (v in get_list()) { }") { Stmt::ForIn { var_name, .. } => { assert_eq!(var_name, "v"); } e => panic!("Expected ForIn, got {:?}", e), } } #[test] fn parse_for_in_distinct_from_c_style() { // "for (x = 0; ...)" is C-style for, not for-in match parse_stmt("for (x = 0; x < 10; x = x + 1) { }") { Stmt::For { .. } => {} e => panic!("Expected C-style For, got {:?}", e), } } #[test] fn parse_block() { match parse_stmt("{ let x = 1; x; }") { Stmt::Block(stmts) => assert_eq!(stmts.len(), 2), e => panic!("Expected Block, got {:?}", e), } } #[test] fn parse_return_statement() { match parse_stmt("return 42;") { Stmt::Return(Some(expr)) => { assert!(matches!(expr, Expr::Literal(Literal::Number(42.0)))); } e => panic!("Expected Return(42), got {:?}", e), } } #[test] fn parse_return_nil() { match parse_stmt("return;") { Stmt::Return(None) => {} e => panic!("Expected Return(None), got {:?}", e), } } #[test] fn parse_break_statement() { match parse_stmt("break;") { Stmt::Break => {} e => panic!("Expected Break, got {:?}", e), } } #[test] fn parse_continue_statement() { match parse_stmt("continue;") { Stmt::Continue => {} e => panic!("Expected Continue, got {:?}", e), } } #[test] fn parse_function_declaration() { match parse_stmt("fn add(a, b) { return a + b; }") { Stmt::Function { name, params, body } => { assert_eq!(name, "add"); assert_eq!(params, vec!["a", "b"]); assert_eq!(body.len(), 1); } e => panic!("Expected Function, got {:?}", e), } } #[test] fn parse_function_no_params() { match parse_stmt("fn greet() { print(\"hi\"); }") { Stmt::Function { name, params, .. } => { assert_eq!(name, "greet"); assert!(params.is_empty()); } e => panic!("Expected Function, got {:?}", e), } } // ----- error recovery ----- #[test] fn error_recovery_skips_past_semicolon() { // First statement is invalid, second should parse fine let (stmts, errors) = parse("let = 5; let y = 10;"); assert!(!errors.is_empty(), "Should have parse errors"); // Should have at least the second statement assert!(stmts.len() >= 1); // The second statement should be a valid Let for y match &stmts.last().unwrap() { Stmt::Let { name, .. } => assert_eq!(name, "y"), e => panic!("Expected Let(y), got {:?}", e), } } #[test] fn error_recovery_at_statement_boundary() { let (stmts, errors) = parse("let x = @@@; let y = 10;"); assert!(!errors.is_empty()); assert!(stmts.len() >= 1); } // ----- parse errors ----- #[test] fn error_invalid_assignment_target() { let (_stmts, errors) = parse("5 = 10;"); assert!(!errors.is_empty()); } #[test] fn error_missing_closing_paren() { let (_stmts, errors) = parse("if (x { 1; }"); assert!(!errors.is_empty()); } #[test] fn error_missing_semicolon_after_for_condition() { // for(;;){} is valid — test something actually invalid let (_stmts, _errors) = parse("for (let i = 0 i < 10) { }"); assert!(!_errors.is_empty()); } // ----- parenthesized expression ----- #[test] fn parse_parenthesized_expression() { match parse_expr("(1 + 2)") { Expr::Binary { op: BinaryOp::Add, .. } => {} e => panic!("Expected Binary(Add), got {:?}", e), } } #[test] fn parse_nested_parentheses() { match parse_expr("((42))") { Expr::Literal(Literal::Number(42.0)) => {} e => panic!("Expected Number(42), got {:?}", e), } } }