import "@std/mem" import "@std/testing" import "@source/diagnostics" import "@source/strpool" import "@source/lexer" import "@source/ast" parses_expression_precedence_through_tree_views test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: 1 + 2 * 3\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(1, ast.package_decl_count(&tree)) declaration :: ast.package_decl(&tree, 0) statement :: ast.statement_decl(&tree, declaration) try testing.expect_equal("value", ast.source_text(&tree, statement.name)) try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, statement.initializer)) add :: ast.binary_expr(&tree, statement.initializer) try testing.expect_equal(ast.BinaryOperator.add, add.operator) try testing.expect_equal(ast.ExprKind.literal_int, ast.expr_kind(&tree, add.left)) try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, add.right)) multiply :: ast.binary_expr(&tree, add.right) try testing.expect_equal(ast.BinaryOperator.multiply, multiply.operator) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } parses_grouped_expression_precedence test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: a + ((b - c)) * 2 / 3\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(1, ast.package_decl_count(&tree)) statement :: ast.statement_decl(&tree, ast.package_decl(&tree, 0)) add :: ast.binary_expr(&tree, statement.initializer) try testing.expect_equal(ast.BinaryOperator.add, add.operator) try testing.expect_equal("a", ast.source_text(&tree, ast.node_span(&tree, add.left))) divide :: ast.binary_expr(&tree, add.right) try testing.expect_equal(ast.BinaryOperator.divide, divide.operator) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, divide.right))) multiply :: ast.binary_expr(&tree, divide.left) try testing.expect_equal(ast.BinaryOperator.multiply, multiply.operator) try testing.expect_equal("2", ast.source_text(&tree, ast.node_span(&tree, multiply.right))) subtract :: ast.binary_expr(&tree, multiply.left) try testing.expect_equal(ast.BinaryOperator.subtract, subtract.operator) try testing.expect_equal("b", ast.source_text(&tree, ast.node_span(&tree, subtract.left))) try testing.expect_equal("c", ast.source_text(&tree, ast.node_span(&tree, subtract.right))) } parses_function_structure_through_tree_views test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `grouped proc(a, b T, c U) void { ` value int :: 1 `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) declaration :: ast.package_decl(&tree, 0) try testing.expect_equal(ast.DeclKind.function, ast.decl_kind(&tree, declaration)) function :: ast.function_decl(&tree, declaration) try testing.expect_equal("grouped", ast.source_text(&tree, function.name)) try testing.expect_equal(3, ast.function_param_count(&tree, declaration)) first :: ast.param(&tree, ast.function_param(&tree, declaration, 0)) second :: ast.param(&tree, ast.function_param(&tree, declaration, 1)) third :: ast.param(&tree, ast.function_param(&tree, declaration, 2)) try testing.expect_equal("a", ast.source_text(&tree, first.name)) try testing.expect_equal("b", ast.source_text(&tree, second.name)) try testing.expect_equal("c", ast.source_text(&tree, third.name)) try testing.expect_equal(first.type, second.type) try testing.expect(first.type != third.type) try testing.expect_equal(1, ast.block_statement_count(&tree, function.body)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } recovers_invalid_declaration_and_preserves_sibling test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "@ broken\nvalid :: 1\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) invalid_decl :: ast.package_decl(&tree, 0) valid_decl :: ast.package_decl(&tree, 1) try testing.expect_equal(ast.DeclKind.invalid, ast.decl_kind(&tree, invalid_decl)) try testing.expect_equal(ast.DeclKind.statement, ast.decl_kind(&tree, valid_decl)) invalid_view :: ast.invalid(&tree, invalid_decl) try testing.expect(diagnostics.get(&diagnostic_store, invalid_view.diagnostic) != null) valid_statement :: ast.statement_decl(&tree, valid_decl) try testing.expect_equal("valid", ast.source_text(&tree, valid_statement.name)) } recovers_malformed_groups_and_preserves_following_statements test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` empty :: () ` operand :: (1 + ) ` missing :: (2 ` valid :: (3) `} `after :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, ast.package_decl_count(&tree)) function :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) try testing.expect_equal(4, ast.block_statement_count(&tree, function.body)) empty :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 0)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, empty.initializer)) operand :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 1)) add :: ast.binary_expr(&tree, operand.initializer) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, add.right)) missing :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 2)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, missing.initializer)) valid :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 3)) try testing.expect_equal(ast.ExprKind.literal_int, ast.expr_kind(&tree, valid.initializer)) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, valid.initializer))) after :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("after", ast.source_text(&tree, after.name)) try testing.expect_equal("4", ast.source_text(&tree, ast.node_span(&tree, after.initializer))) } parses_call_statements_and_postfix_precedence test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` ping() ` result int := -factory()(add(1, 2 * 3), (4 + 5),) + 6 ` (handler)(result) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) function :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) try testing.expect_equal(3, ast.block_statement_count(&tree, function.body)) ping_stmt :: ast.block_statement(&tree, function.body, 0) try testing.expect_equal(ast.StmtKind.expression, ast.stmt_kind(&tree, ping_stmt)) ping :: ast.statement_expr(&tree, ping_stmt) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, ping)) try testing.expect_equal(0, ast.call_arg_count(&tree, ping)) try testing.expect_equal( "ping", ast.source_text(&tree, ast.node_span(&tree, ast.call_expr(&tree, ping).callee)), ) try testing.expect_equal("ping()", ast.source_text(&tree, ast.call_expr(&tree, ping).span)) result :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 1)) try testing.expect_equal(ast.Mutability.variable, result.mutability) add :: ast.binary_expr(&tree, result.initializer) try testing.expect_equal(ast.BinaryOperator.add, add.operator) try testing.expect_equal("6", ast.source_text(&tree, ast.node_span(&tree, add.right))) negate :: ast.unary_expr(&tree, add.left) call :: negate.operand try testing.expect_equal(2, ast.call_arg_count(&tree, call)) factory :: ast.call_expr(&tree, call).callee try testing.expect_equal(0, ast.call_arg_count(&tree, factory)) try testing.expect_equal( "factory", ast.source_text(&tree, ast.node_span(&tree, ast.call_expr(&tree, factory).callee)), ) nested :: ast.call_arg(&tree, call, 0) try testing.expect_equal(2, ast.call_arg_count(&tree, nested)) try testing.expect_equal( "add", ast.source_text(&tree, ast.node_span(&tree, ast.call_expr(&tree, nested).callee)), ) try testing.expect_equal( "1", ast.source_text(&tree, ast.node_span(&tree, ast.call_arg(&tree, nested, 0))), ) multiply :: ast.binary_expr(&tree, ast.call_arg(&tree, nested, 1)) try testing.expect_equal(ast.BinaryOperator.multiply, multiply.operator) try testing.expect_equal("2", ast.source_text(&tree, ast.node_span(&tree, multiply.left))) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, multiply.right))) grouped :: ast.binary_expr(&tree, ast.call_arg(&tree, call, 1)) try testing.expect_equal(ast.BinaryOperator.add, grouped.operator) try testing.expect_equal("4", ast.source_text(&tree, ast.node_span(&tree, grouped.left))) try testing.expect_equal("5", ast.source_text(&tree, ast.node_span(&tree, grouped.right))) handler :: ast.statement_expr(&tree, ast.block_statement(&tree, function.body, 2)) try testing.expect_equal( "handler", ast.source_text(&tree, ast.node_span(&tree, ast.call_expr(&tree, handler).callee)), ) try testing.expect_equal(1, ast.call_arg_count(&tree, handler)) try testing.expect_equal( "result", ast.source_text(&tree, ast.node_span(&tree, ast.call_arg(&tree, handler, 0))), ) } recovers_malformed_calls_and_preserves_siblings test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` bad(1 2) ` good() ` missing(1 ` after :: 3 ` holes(, 2) `} `tail :: f() scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, ast.package_decl_count(&tree)) function :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) try testing.expect_equal(5, ast.block_statement_count(&tree, function.body)) for ([0, 2]) |index| { statement :: ast.block_statement(&tree, function.body, usize(index)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, statement)) try testing.expect( diagnostics.get(&diagnostic_store, ast.invalid(&tree, statement).diagnostic) != null, ) } good :: ast.statement_expr(&tree, ast.block_statement(&tree, function.body, 1)) try testing.expect_equal( "good", ast.source_text(&tree, ast.node_span(&tree, ast.call_expr(&tree, good).callee)), ) after :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 3)) try testing.expect_equal("after", ast.source_text(&tree, after.name)) holes :: ast.statement_expr(&tree, ast.block_statement(&tree, function.body, 4)) try testing.expect_equal(2, ast.call_arg_count(&tree, holes)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, ast.call_arg(&tree, holes, 0))) try testing.expect_equal( "2", ast.source_text(&tree, ast.node_span(&tree, ast.call_arg(&tree, holes, 1))), ) tail :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("tail", ast.source_text(&tree, tail.name)) try testing.expect_equal(0, ast.call_arg_count(&tree, tail.initializer)) } parses_return_boundaries_and_recovers_trailing_tokens test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `return 1 `main proc() void { ` return ` 2 ` return (3 + 4) ` return 5 stray ` after :: 6 ` return } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) try testing.expect_equal(ast.DeclKind.invalid, ast.decl_kind(&tree, ast.package_decl(&tree, 0))) body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body try testing.expect_equal(6, ast.block_statement_count(&tree, body)) first :: ast.block_statement(&tree, body, 0) try testing.expect_equal(ast.StmtKind.return_stmt, ast.stmt_kind(&tree, first)) try testing.expect(ast.return_stmt(&tree, first).value == null) try testing.expect_equal("return", ast.source_text(&tree, ast.return_stmt(&tree, first).span)) try testing.expect_equal(ast.StmtKind.expression, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1))) grouped :: ast.return_stmt(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal("return (3 + 4)", ast.source_text(&tree, grouped.span)) if (grouped.value) |value| { try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, value)) } else { return .expectation_failed } try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 3))) after :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 4)) try testing.expect_equal("after", ast.source_text(&tree, after.name)) last :: ast.return_stmt(&tree, ast.block_statement(&tree, body, 5)) try testing.expect(last.value == null) try testing.expect_equal("return", ast.source_text(&tree, last.span)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) } parses_boolean_comparison_precedence_and_associativity test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `value :: -f() * 2 + 3 < 4 <= 5 > 6 >= 7 == true != false scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) different :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer) try testing.expect_equal(ast.BinaryOperator.not_equal, different.operator) try testing.expect_equal(ast.ExprKind.literal_bool, ast.expr_kind(&tree, different.right)) try testing.expect_equal("false", ast.source_text(&tree, ast.node_span(&tree, different.right))) equal :: ast.binary_expr(&tree, different.left) try testing.expect_equal(ast.BinaryOperator.equal, equal.operator) try testing.expect_equal(ast.ExprKind.literal_bool, ast.expr_kind(&tree, equal.right)) relation := equal.left operators [4]ast.BinaryOperator :: [.greater_equal, .greater, .less_equal, .less] for (operators) |operator| { binary :: ast.binary_expr(&tree, relation) try testing.expect_equal(operator, binary.operator) relation = binary.left } addition :: ast.binary_expr(&tree, relation) try testing.expect_equal(ast.BinaryOperator.add, addition.operator) product :: ast.binary_expr(&tree, addition.left) try testing.expect_equal(ast.BinaryOperator.multiply, product.operator) negate :: ast.unary_expr(&tree, product.left) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, negate.operand)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } parses_factorial_and_canonical_conditional_arms test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `factorial proc(n int) int { ` if (n <= 1) return 1 else return n * factorial(n - 1) `} `main proc() int { ` if true { ` local :: factorial(5) ` if (false) return 0 else return local ` } ` else if ready() return 2 else { return 3 } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) factorial :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) try testing.expect_equal(1, ast.block_statement_count(&tree, factorial.body)) guard :: ast.if_stmt(&tree, ast.block_statement(&tree, factorial.body, 0)) try testing.expect_equal("n <= 1", ast.source_text(&tree, ast.node_span(&tree, guard.condition))) try testing.expect_equal(1, ast.block_statement_count(&tree, guard.then_block)) try testing.expect_equal("return 1", ast.source_text(&tree, ast.return_stmt(&tree, ast.block_statement(&tree, guard.then_block, 0)).span)) try testing.expect_equal(1, ast.block_statement_count(&tree, guard.else_block?)) try testing.expect_equal(ast.StmtKind.return_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, guard.else_block?, 0))) main :: ast.function_decl(&tree, ast.package_decl(&tree, 1)) outer :: ast.if_stmt(&tree, ast.block_statement(&tree, main.body, 0)) try testing.expect_equal(2, ast.block_statement_count(&tree, outer.then_block)) try testing.expect_equal(ast.StmtKind.declaration, ast.stmt_kind(&tree, ast.block_statement(&tree, outer.then_block, 0))) nested :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.then_block, 1)) try testing.expect(nested.else_block != null) try testing.expect_equal(1, ast.block_statement_count(&tree, outer.else_block?)) chain :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.else_block?, 0)) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, chain.condition)) try testing.expect(chain.else_block != null) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } attaches_dangling_else_to_nearest_if test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "main proc() int { if (true) if (false) return 1 else return 2 else return 3 }" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body outer :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 0)) inner :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.then_block, 0)) inner_return :: ast.return_stmt(&tree, ast.block_statement(&tree, inner.else_block?, 0)) outer_return :: ast.return_stmt(&tree, ast.block_statement(&tree, outer.else_block?, 0)) try testing.expect_equal("2", ast.source_text(&tree, ast.node_span(&tree, inner_return.value?))) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, outer_return.value?))) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } recovers_conditional_conditions_and_arms_without_losing_siblings test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` if { good() } else { other() } ` if (true) else return ` if broken( { kept() } ` after :: 7 ` return 1 else return 2 ` final :: 8 `} `tail :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(6, ast.block_statement_count(&tree, body)) first :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, first.condition)) condition_diagnostic :: diagnostics.get(&diagnostic_store, ast.invalid(&tree, first.condition).diagnostic)? try testing.expect_equal("{", ast.source_text(&tree, condition_diagnostic.span)) try testing.expect_equal(1, ast.block_statement_count(&tree, first.then_block)) second :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 1)) bad_arm :: ast.block_statement(&tree, second.then_block, 0) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, bad_arm)) arm_diagnostic :: diagnostics.get(&diagnostic_store, ast.invalid(&tree, bad_arm).diagnostic)? try testing.expect_equal("else", ast.source_text(&tree, arm_diagnostic.span)) try testing.expect(second.else_block != null) third :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal(1, ast.block_statement_count(&tree, third.then_block)) after :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 3)) try testing.expect_equal("after", ast.source_text(&tree, after.name)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 4))) final :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 5)) try testing.expect_equal("final", ast.source_text(&tree, final.name)) try testing.expect_equal(4, diagnostics.count(&diagnostic_store)) } recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() int { ` if true return 1 else return 2 ` if (true junk) { kept() } else { other() } ` sibling :: 3 ` if (false) } `tail :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) first :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, first.condition)) guard_error :: diagnostics.get(&diagnostic_store, ast.invalid(&tree, first.condition).diagnostic)? try testing.expect_equal("return", ast.source_text(&tree, guard_error.span)) try testing.expect(first.else_block != null) second :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 1)) try testing.expect_equal(1, ast.block_statement_count(&tree, second.then_block)) try testing.expect(second.else_block != null) sibling :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal("sibling", ast.source_text(&tree, sibling.name)) last :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 3)) missing :: ast.block_statement(&tree, last.then_block, 0) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, missing)) arm_error :: diagnostics.get(&diagnostic_store, ast.invalid(&tree, missing).diagnostic)? try testing.expect_equal("}", ast.source_text(&tree, arm_error.span)) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) } parses_iterative_factorial_assignments_and_loop test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `factorial proc(n int) int { ` remaining := n ` product := 1 ` while remaining > 1 { ` product = product * remaining ` remaining = remaining - 1 ` } ` return product `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) loop_id :: ast.block_statement(&tree, body, 2) try testing.expect_equal(ast.StmtKind.while_stmt, ast.stmt_kind(&tree, loop_id)) loop :: ast.while_stmt(&tree, loop_id) try testing.expect_equal(ast.BinaryOperator.greater, ast.binary_expr(&tree, loop.condition).operator) try testing.expect_equal(2, ast.block_statement_count(&tree, loop.body)) update_id :: ast.block_statement(&tree, loop.body, 0) try testing.expect_equal(ast.StmtKind.assignment_stmt, ast.stmt_kind(&tree, update_id)) update :: ast.assignment_stmt(&tree, update_id) try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, update.target)) try testing.expect_equal("product", ast.source_text(&tree, ast.node_span(&tree, update.target))) try testing.expect_equal(ast.BinaryOperator.multiply, ast.binary_expr(&tree, update.value).operator) decrement :: ast.assignment_stmt(&tree, ast.block_statement(&tree, loop.body, 1)) try testing.expect_equal(ast.BinaryOperator.subtract, ast.binary_expr(&tree, decrement.value).operator) } parses_assignment_precedence_grouped_span_and_rhs_boundaries test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` value = (1 + 2 * 3) ` if (true) value = 4 else value = 5 ` while ready() value = value - 1 ` value = 6 } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal("value = (1 + 2 * 3)", ast.source_text(&tree, assignment.span)) add :: ast.binary_expr(&tree, assignment.value) try testing.expect_equal(ast.BinaryOperator.add, add.operator) try testing.expect_equal(ast.BinaryOperator.multiply, ast.binary_expr(&tree, add.right).operator) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 1)) first :: ast.assignment_stmt(&tree, ast.block_statement(&tree, conditional.then_block, 0)) second :: ast.assignment_stmt(&tree, ast.block_statement(&tree, conditional.else_block?, 0)) try testing.expect_equal("4", ast.source_text(&tree, ast.node_span(&tree, first.value))) try testing.expect_equal("5", ast.source_text(&tree, ast.node_span(&tree, second.value))) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, loop.condition)) try testing.expect_equal(1, ast.block_statement_count(&tree, loop.body)) try testing.expect_equal(ast.StmtKind.assignment_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, 0))) last :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 3)) try testing.expect_equal("6", ast.source_text(&tree, ast.node_span(&tree, last.value))) } parses_nested_loops_and_preserves_nearest_if_else test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` if (true) while (false) if (true) x = 1 else x = 2 else x = 3 ` while (true) while (false) x = 4 ` while (true) { ` while false { x = 5 } ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(3, ast.block_statement_count(&tree, body)) outer :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 0)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, outer.then_block, 0)) inner :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 0)) inner_else :: ast.assignment_stmt(&tree, ast.block_statement(&tree, inner.else_block?, 0)) outer_else :: ast.assignment_stmt(&tree, ast.block_statement(&tree, outer.else_block?, 0)) try testing.expect_equal("2", ast.source_text(&tree, ast.node_span(&tree, inner_else.value))) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, outer_else.value))) braceless :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 1)) nested :: ast.while_stmt(&tree, ast.block_statement(&tree, braceless.body, 0)) try testing.expect_equal(1, ast.block_statement_count(&tree, nested.body)) braced :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 2)) child :: ast.while_stmt(&tree, ast.block_statement(&tree, braced.body, 0)) try testing.expect_equal(ast.ExprKind.literal_bool, ast.expr_kind(&tree, child.condition)) try testing.expect_equal(1, ast.block_statement_count(&tree, child.body)) } recovers_loop_guards_bodies_and_assignments_without_losing_braces test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` while { x = 1 } ` while true x = 2 ` while broken( { x = 3 } ` x = ` x = 1 junk ` if (true) x = else x = 4 ` kept :: 7 ` while (false) } `tail :: 8 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect(diagnostics.count(&diagnostic_store) > 0) try testing.expect_equal(2, ast.package_decl_count(&tree)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(8, ast.block_statement_count(&tree, body)) for 0..3 |index| { loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, usize(index))) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, loop.condition)) try testing.expect_equal(1, ast.block_statement_count(&tree, loop.body)) try testing.expect_equal(ast.StmtKind.assignment_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, 0))) } missing :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 3)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, missing.value)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 4))) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 5)) missing_arm :: ast.assignment_stmt(&tree, ast.block_statement(&tree, conditional.then_block, 0)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, missing_arm.value)) try testing.expect(conditional.else_block != null) kept :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 6)) try testing.expect_equal("kept", ast.source_text(&tree, kept.name)) last :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 7)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, last.body, 0))) tail :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("tail", ast.source_text(&tree, tail.name)) } rejects_top_level_assignment_loop_else_and_assignment_expressions test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `x = 1 `main proc() void { ` while (true) x = 2 else x = 3 ` x = y = 4 ` kept :: 5 `} `tail :: 6 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect(diagnostics.count(&diagnostic_store) > 0) try testing.expect_equal(3, ast.package_decl_count(&tree)) try testing.expect_equal(ast.DeclKind.invalid, ast.decl_kind(&tree, ast.package_decl(&tree, 0))) body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, 0))) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1))) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 2))) kept :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 3)) try testing.expect_equal("kept", ast.source_text(&tree, kept.name)) } recovers_nested_braces_in_malformed_loop_body_and_assignment test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` while (true) @ { ` bad() ` } ` x = { ` bad() ` } ` kept :: 7 `} `tail :: 8 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect(diagnostics.count(&diagnostic_store) > 0) try testing.expect_equal(2, ast.package_decl_count(&tree)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(3, ast.block_statement_count(&tree, body)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, 0))) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1))) kept :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal("kept", ast.source_text(&tree, kept.name)) tail :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("tail", ast.source_text(&tree, tail.name)) } parses_logical_precedence_and_associativity test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: !probe() == a + b * c < d and e and f or g or h\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) value :: ast.statement_decl(&tree, ast.package_decl(&tree, 0)) outer_or :: ast.binary_expr(&tree, value.initializer) try testing.expect_equal(ast.BinaryOperator.logical_or, outer_or.operator) try testing.expect_equal("h", ast.source_text(&tree, ast.node_span(&tree, outer_or.right))) inner_or :: ast.binary_expr(&tree, outer_or.left) try testing.expect_equal(ast.BinaryOperator.logical_or, inner_or.operator) outer_and :: ast.binary_expr(&tree, inner_or.left) try testing.expect_equal(ast.BinaryOperator.logical_and, outer_and.operator) try testing.expect_equal("f", ast.source_text(&tree, ast.node_span(&tree, outer_and.right))) inner_and :: ast.binary_expr(&tree, outer_and.left) try testing.expect_equal(ast.BinaryOperator.logical_and, inner_and.operator) equality :: ast.binary_expr(&tree, inner_and.left) try testing.expect_equal(ast.BinaryOperator.equal, equality.operator) logical_not :: ast.unary_expr(&tree, equality.left) try testing.expect_equal(ast.UnaryOperator.logical_not, logical_not.operator) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, logical_not.operand)) try testing.expect_equal("!probe()", ast.source_text(&tree, logical_not.span)) comparison :: ast.binary_expr(&tree, equality.right) try testing.expect_equal(ast.BinaryOperator.less, comparison.operator) addition :: ast.binary_expr(&tree, comparison.left) try testing.expect_equal(ast.BinaryOperator.add, addition.operator) multiply :: ast.binary_expr(&tree, addition.right) try testing.expect_equal(ast.BinaryOperator.multiply, multiply.operator) } parses_right_associative_mixed_prefix_operators test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: !-!probe()\nother :: -!false\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) value :: ast.statement_decl(&tree, ast.package_decl(&tree, 0)) first :: ast.unary_expr(&tree, value.initializer) second :: ast.unary_expr(&tree, first.operand) third :: ast.unary_expr(&tree, second.operand) try testing.expect_equal(ast.UnaryOperator.logical_not, first.operator) try testing.expect_equal(ast.UnaryOperator.negate, second.operator) try testing.expect_equal(ast.UnaryOperator.logical_not, third.operator) try testing.expect_equal(ast.ExprKind.call, ast.expr_kind(&tree, third.operand)) try testing.expect_equal("!-!probe()", ast.source_text(&tree, first.span)) try testing.expect_equal("-!probe()", ast.source_text(&tree, second.span)) other :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) negate :: ast.unary_expr(&tree, other.initializer) try testing.expect_equal(ast.UnaryOperator.negate, negate.operator) try testing.expect_equal(ast.UnaryOperator.logical_not, ast.unary_expr(&tree, negate.operand).operator) } parses_logical_expressions_in_all_expression_positions test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `global :: ( ` false ` and absent `) `main proc() bool { ` value := ( ` true ` or absent ` ) ` value = ( ` ! ` value ` and ready( ` ) ` ) ` !value ` if ( ` !value ` or ready() ` ) { value = false } ` while ( ` value ` and !done() ` ) { value = false } ` return ( ` choose( ` ( ` value ` or false ` ), ` nested( ` !value ` and true ` ), ` ) ` ) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) global :: ast.statement_decl(&tree, ast.package_decl(&tree, 0)) global_and :: ast.binary_expr(&tree, global.initializer) try testing.expect_equal(ast.BinaryOperator.logical_and, global_and.operator) try testing.expect_equal("absent", ast.source_text(&tree, ast.node_span(&tree, global_and.right))) function :: ast.function_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal(6, ast.block_statement_count(&tree, function.body)) local :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 0)) try testing.expect_equal(ast.BinaryOperator.logical_or, ast.binary_expr(&tree, local.initializer).operator) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, function.body, 1)) try testing.expect_equal(ast.BinaryOperator.logical_and, ast.binary_expr(&tree, assignment.value).operator) expression :: ast.statement_expr(&tree, ast.block_statement(&tree, function.body, 2)) try testing.expect_equal(ast.UnaryOperator.logical_not, ast.unary_expr(&tree, expression).operator) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, function.body, 3)) try testing.expect_equal(ast.BinaryOperator.logical_or, ast.binary_expr(&tree, conditional.condition).operator) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, function.body, 4)) try testing.expect_equal(ast.BinaryOperator.logical_and, ast.binary_expr(&tree, loop.condition).operator) returned :: ast.return_stmt(&tree, ast.block_statement(&tree, function.body, 5)).value? try testing.expect_equal(2, ast.call_arg_count(&tree, returned)) first_argument :: ast.call_arg(&tree, returned, 0) try testing.expect_equal(ast.BinaryOperator.logical_or, ast.binary_expr(&tree, first_argument).operator) nested :: ast.call_arg(&tree, returned, 1) nested_argument :: ast.call_arg(&tree, nested, 0) try testing.expect_equal(ast.BinaryOperator.logical_and, ast.binary_expr(&tree, nested_argument).operator) } multiline_group_errors_preserve_statement_boundaries test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` value := ( ` 1 + ` next int :: 2 ` value = 3 ` return ( ` true `} `tail :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, ast.package_decl_count(&tree)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) next :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 1)) try testing.expect_equal("next", ast.source_text(&tree, next.name)) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 2)) try testing.expect_equal("3", ast.source_text(&tree, ast.node_span(&tree, assignment.value))) try testing.expect_equal(ast.StmtKind.return_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 3))) tail :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("tail", ast.source_text(&tree, tail.name)) } recovers_missing_logical_operands_and_preserves_enclosing_nodes test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() bool { ` first :: true and ` second :: true and or false ` third :: choose(!, false or) ` if ! { return false } ` return true or } `after :: true scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(6, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, ast.package_decl_count(&tree)) function :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) try testing.expect_equal(5, ast.block_statement_count(&tree, function.body)) first :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 0)) first_and :: ast.binary_expr(&tree, first.initializer) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, first_and.right)) second :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 1)) second_or :: ast.binary_expr(&tree, second.initializer) try testing.expect_equal(ast.BinaryOperator.logical_or, second_or.operator) second_and :: ast.binary_expr(&tree, second_or.left) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, second_and.right)) try testing.expect_equal(ast.ExprKind.literal_bool, ast.expr_kind(&tree, second_or.right)) third :: ast.statement_decl(&tree, ast.block_statement(&tree, function.body, 2)) try testing.expect_equal(2, ast.call_arg_count(&tree, third.initializer)) missing_prefix :: ast.unary_expr(&tree, ast.call_arg(&tree, third.initializer, 0)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, missing_prefix.operand)) missing_rhs :: ast.binary_expr(&tree, ast.call_arg(&tree, third.initializer, 1)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, missing_rhs.right)) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, function.body, 3)) try testing.expect_equal(1, ast.block_statement_count(&tree, conditional.then_block)) returned :: ast.return_stmt(&tree, ast.block_statement(&tree, function.body, 4)).value? try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, ast.binary_expr(&tree, returned).right)) after :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) try testing.expect_equal("after", ast.source_text(&tree, after.name)) } parses_leaf_loop_controls_and_nearest_else test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` while (true) { ` if (true) if (false) break else continue ` while (false) continue ` break ` } ` while (false) if (true) break else continue `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(2, ast.block_statement_count(&tree, body)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(3, ast.block_statement_count(&tree, loop.body)) outer_if :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 0)) try testing.expect(outer_if.else_block == null) inner_if :: ast.if_stmt(&tree, ast.block_statement(&tree, outer_if.then_block, 0)) transfer :: ast.block_statement(&tree, inner_if.then_block, 0) try testing.expect_equal(ast.StmtKind.break_stmt, ast.stmt_kind(&tree, transfer)) try testing.expect_equal("break", ast.source_text(&tree, ast.node_span(&tree, transfer))) if (inner_if.else_block) |other| { alternate :: ast.block_statement(&tree, other, 0) try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(&tree, alternate)) try testing.expect_equal("continue", ast.source_text(&tree, ast.node_span(&tree, alternate))) } else return .expectation_failed nested :: ast.while_stmt(&tree, ast.block_statement(&tree, loop.body, 1)) try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, nested.body, 0))) braceless :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 1)) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, braceless.body, 0)) try testing.expect(conditional.else_block != null) } rejects_control_tails_and_preserves_following_declarations test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `break `continue `bad :: break `bad_continue :: continue `main proc() void { ` while (true) { ` break 1 ` first :: 1 ` continue label ` second :: 2 ` if (true) break label else continue ` if (true) continue 2 else break ` break 3 } ` after :: 3 `} `last :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(9, diagnostics.count(&diagnostic_store)) try testing.expect_equal(6, ast.package_decl_count(&tree)) for (usize(0)..usize(2)) |index| try testing.expect_equal(ast.DeclKind.invalid, ast.decl_kind(&tree, ast.package_decl(&tree, index))) for (usize(2)..usize(4)) |index| { declaration :: ast.package_decl(&tree, index) try testing.expect_equal(ast.DeclKind.statement, ast.decl_kind(&tree, declaration)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, ast.statement_decl(&tree, declaration).initializer)) } body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body try testing.expect_equal(2, ast.block_statement_count(&tree, body)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 0)) try testing.expect_equal(7, ast.block_statement_count(&tree, loop.body)) for ([usize(1), usize(3)]) |index| { try testing.expect_equal(ast.StmtKind.declaration, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, index))) } conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 4)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, conditional.then_block, 0))) if (conditional.else_block) |other| { try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, other, 0))) } else return .expectation_failed try testing.expect_equal(ast.StmtKind.declaration, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1))) try testing.expect_equal(ast.DeclKind.statement, ast.decl_kind(&tree, ast.package_decl(&tree, 5))) } preserves_control_bodies_after_malformed_guards test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` while (true break ` while predicate( continue ` while (true) { continue } ` after :: 1 `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body try testing.expect_equal(4, ast.block_statement_count(&tree, body)) expected [3]ast.StmtKind :: [.break_stmt, .continue_stmt, .continue_stmt] for (expected) |kind, index| { loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, index)) try testing.expect_equal(kind, ast.stmt_kind(&tree, ast.block_statement(&tree, loop.body, 0))) if (index < 2) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, loop.condition)) } try testing.expect_equal(ast.StmtKind.declaration, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 3))) }