feat: parse function calls
This commit is contained in:
+43
-6
@@ -167,8 +167,18 @@ parse_block proc(state @mut State) ast.NodeId ! ParseError {
|
||||
|
||||
@hide:file
|
||||
parse_statement proc(state @mut State) ast.NodeId ! ParseError {
|
||||
if next_is(state, .ident) return try parse_statement_decl(state)
|
||||
return .unexpected_token
|
||||
if (next_is(state, .ident) and (
|
||||
next_n_is(state, 1, .ident) or
|
||||
next_n_is(state, 1, .double_colon) or
|
||||
next_n_is(state, 1, .colon_equal)
|
||||
)) return try parse_statement_decl(state)
|
||||
match current(state).kind {
|
||||
.ident, .int, .float, .string, .minus, .open_paren: {
|
||||
expression :: try parse_expr(state)
|
||||
return try ast.add_expression_stmt(&state.builder, expression)
|
||||
}
|
||||
else: return .unexpected_token
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
@@ -202,12 +212,12 @@ parse_type proc(state @mut State) ast.NodeId ! ParseError {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_expr proc(state @mut State) ast.NodeId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||
parse_expr proc(state @mut State) ast.NodeId ! ParseError {
|
||||
return try parse_expr_bp(state, BindingPowerScalar(0))
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||
parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! ParseError {
|
||||
token := current(state)
|
||||
left := if (prefix_binding_power(token.kind)) |right_bp| {
|
||||
state.next_token += 1
|
||||
@@ -225,6 +235,10 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! (me
|
||||
|
||||
while true {
|
||||
token = current(state)
|
||||
if (token.kind == .open_paren) {
|
||||
left = try parse_call(state, left)
|
||||
continue
|
||||
}
|
||||
if (infix_binding_power(token.kind)) |power| {
|
||||
if (power.left < min_bp) break
|
||||
state.next_token += 1
|
||||
@@ -246,7 +260,30 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! (me
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_primary proc(state @mut State) ast.NodeId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||
parse_call proc(state @mut State, callee ast.NodeId) ast.NodeId ! ParseError {
|
||||
_ = try consume(state, .open_paren)
|
||||
arguments arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator)
|
||||
defer arraylist.deinit(&arguments)
|
||||
|
||||
while !next_is(state, .close_paren) and !next_is(state, .newline) and
|
||||
!next_is(state, .close_curly) and !next_is(state, .eof)
|
||||
{
|
||||
argument :: try parse_expr(state)
|
||||
try arraylist.append(&arguments, argument)
|
||||
if (!next_is(state, .comma)) break
|
||||
state.next_token += 1
|
||||
}
|
||||
close :: try consume(state, .close_paren)
|
||||
return try ast.add_call(
|
||||
&state.builder,
|
||||
ast.Span{ start = ast.builder_span(&state.builder, callee).start, end = close.span.end },
|
||||
callee,
|
||||
arguments.items,
|
||||
)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_primary proc(state @mut State) ast.NodeId ! ParseError {
|
||||
token :: current(state)
|
||||
match token.kind {
|
||||
.open_paren: {
|
||||
@@ -284,7 +321,7 @@ parse_primary proc(state @mut State) ast.NodeId ! (mem.AllocError | diagnostics.
|
||||
else
|
||||
try diagnostics.add(state.diagnostics, .unexpected_token, token.span)
|
||||
|
||||
if (token.kind != .newline and token.kind != .close_paren and token.kind != .close_curly and token.kind != .eof) {
|
||||
if (token.kind != .newline and token.kind != .comma and token.kind != .close_paren and token.kind != .close_curly and token.kind != .eof) {
|
||||
state.next_token += 1
|
||||
}
|
||||
|
||||
|
||||
@@ -159,3 +159,103 @@ recovers_malformed_groups_and_preserves_following_statements test {
|
||||
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(scan_state.tokens.items, source, &diagnostic_store, mem.c_allocator)
|
||||
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(scan_state.tokens.items, source, &diagnostic_store, mem.c_allocator)
|
||||
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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user