feat: lower typed programs and interpret IR

This commit is contained in:
2026-10-09 22:47:05 +02:00
parent bc318a29ad
commit 98f3a04daf
20 changed files with 2328 additions and 85 deletions
+20
View File
@@ -179,6 +179,7 @@ parse_statement proc(state @mut State) ast.NodeId ! ParseError {
return try parse_statement_decl(state)
}
match current(state).kind {
.return: return try parse_return(state)
.ident, .int, .float, .string, .minus, .open_paren: {
expression :: try parse_expr(state)
return try ast.add_expression_stmt(&state.builder, expression)
@@ -187,6 +188,25 @@ parse_statement proc(state @mut State) ast.NodeId ! ParseError {
}
}
@hide:file
parse_return proc(state @mut State) ast.NodeId ! ParseError {
keyword :: try consume(state, .return)
if (next_is(state, .newline) or next_is(state, .close_curly) or next_is(state, .eof)) {
return try ast.add_return_stmt(&state.builder, keyword.span, null)
}
value :: try parse_expr(state)
if (!next_is(state, .newline) and !next_is(state, .close_curly) and !next_is(state, .eof)) {
return .unexpected_token
}
# include consumed grouping punctuation, which has no separate expression node.
end :: state.tokens[state.next_token - 1].span.end
return try ast.add_return_stmt(
&state.builder,
ast.Span{ start = keyword.span.start, end = end },
value,
)
}
@hide:file
parse_statement_decl proc(state @mut State) ast.NodeId ! ParseError {
name :: try consume(state, .ident)
+45
View File
@@ -285,3 +285,48 @@ recovers_malformed_calls_and_preserves_siblings test {
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))
}