Files
honey/source/parser/parse.test.hon
T

1086 lines
56 KiB
Plaintext

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)))
}