use nxc_frontend::{ BinaryOp, ExprKind, Item, Lexer, Literal, Parser, StmtKind, UnaryOp, }; fn parse(source: &str) -> nxc_frontend::ParseResult { let lexed = Lexer::new(source).lex(); assert!( lexed.diagnostics.is_empty(), "unexpected lexer diagnostics: {:?}", lexed.diagnostics ); Parser::new(lexed.tokens).parse_module() } #[test] fn parses_function_with_if_else_and_returns() { let source = "\ fn classify(value: Int) -> Int: if value > 10: return 1 else: return 0 "; let parsed = parse(source); assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics); assert_eq!(parsed.module.items.len(), 1); let Item::Function(function) = &parsed.module.items[0] else { panic!("expected function item"); }; assert_eq!(function.name, "classify"); assert_eq!(function.params.len(), 1); assert_eq!(function.body.statements.len(), 1); let StmtKind::If(if_stmt) = &function.body.statements[0].kind else { panic!("expected if statement"); }; match &if_stmt.condition.kind { ExprKind::Binary { op, .. } => assert_eq!(*op, BinaryOp::Greater), other => panic!("unexpected condition: {other:?}"), } assert!(if_stmt.else_block.is_some()); } #[test] fn respects_binary_operator_precedence() { let source = "\ fn main() -> Int: return 1 + 2 * 3 == 7 || false "; let parsed = parse(source); assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics); let Item::Function(function) = &parsed.module.items[0] else { panic!("expected function item"); }; let StmtKind::Return(Some(expr)) = &function.body.statements[0].kind else { panic!("expected return statement"); }; let ExprKind::Binary { left, op, right } = &expr.kind else { panic!("expected binary expression"); }; assert_eq!(*op, BinaryOp::LogicalOr); let ExprKind::Binary { left: equality_left, op: equality_op, right: equality_right, } = &left.kind else { panic!("expected equality expression"); }; assert_eq!(*equality_op, BinaryOp::Equal); let ExprKind::Binary { left: add_left, op: add_op, right: add_right, } = &equality_left.kind else { panic!("expected additive expression"); }; assert_eq!(*add_op, BinaryOp::Add); match &add_left.kind { ExprKind::Literal(Literal::Integer(1)) => {} other => panic!("unexpected left additive operand: {other:?}"), } let ExprKind::Binary { left: mul_left, op: mul_op, right: mul_right, } = &add_right.kind else { panic!("expected multiplicative expression"); }; assert_eq!(*mul_op, BinaryOp::Multiply); match &mul_left.kind { ExprKind::Literal(Literal::Integer(2)) => {} other => panic!("unexpected left multiplicative operand: {other:?}"), } match &mul_right.kind { ExprKind::Literal(Literal::Integer(3)) => {} other => panic!("unexpected right multiplicative operand: {other:?}"), } match &equality_right.kind { ExprKind::Literal(Literal::Integer(7)) => {} other => panic!("unexpected equality right operand: {other:?}"), } match &right.kind { ExprKind::Literal(Literal::Bool(false)) => {} other => panic!("unexpected logical-or right operand: {other:?}"), } } #[test] fn parses_grouping_unary_and_calls() { let source = "\ fn main() -> Int: return -(compute(1, 2) + 3) "; let parsed = parse(source); assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics); let Item::Function(function) = &parsed.module.items[0] else { panic!("expected function item"); }; let StmtKind::Return(Some(expr)) = &function.body.statements[0].kind else { panic!("expected return statement"); }; let ExprKind::Unary { op, expr: inner } = &expr.kind else { panic!("expected unary expression"); }; assert_eq!(*op, UnaryOp::Negate); let ExprKind::Group(grouped) = &inner.kind else { panic!("expected grouped expression"); }; let ExprKind::Binary { left, op, .. } = &grouped.kind else { panic!("expected additive expression"); }; assert_eq!(*op, BinaryOp::Add); let ExprKind::Call { args, .. } = &left.kind else { panic!("expected call expression"); }; assert_eq!(args.len(), 2); } #[test] fn recovers_and_reports_syntax_errors() { let source = "\ fn broken(value: Int) -> Int let x = 1 struct Config: port: Int "; let parsed = parse(source); assert!(!parsed.diagnostics.is_empty()); assert_eq!(parsed.module.items.len(), 1); let Item::Struct(struct_decl) = &parsed.module.items[0] else { panic!("expected recovered struct declaration"); }; assert_eq!(struct_decl.name, "Config"); }