diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-07 15:42:00 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-07 17:33:55 +0900 |
| commit | cdddf2422553f1f21c8d2c57cd382b8362dc80fb (patch) | |
| tree | 3053026498552aecda2e4889c8455298ad70c8cb | |
| parent | e1042a6373773830297dfd5718938c12f21ae624 (diff) | |
| download | ducc-cdddf2422553f1f21c8d2c57cd382b8362dc80fb.tar.gz ducc-cdddf2422553f1f21c8d2c57cd382b8362dc80fb.tar.zst ducc-cdddf2422553f1f21c8d2c57cd382b8362dc80fb.zip | |
feat: support function calls via function pointers
The two-pass parsing of function pointer declaration is referenced from chibicc:
https://github.com/rui314/chibicc
| -rw-r--r-- | src/ast.c | 6 | ||||
| -rw-r--r-- | src/ast.h | 4 | ||||
| -rw-r--r-- | src/codegen.c | 23 | ||||
| -rw-r--r-- | src/codegen_wasm.c | 9 | ||||
| -rw-r--r-- | src/parse.c | 42 | ||||
| -rw-r--r-- | tests/cli.sh | 3 | ||||
| -rw-r--r-- | tests/functions.c | 18 |
7 files changed, 83 insertions, 22 deletions
@@ -454,11 +454,11 @@ AstNode* ast_new_str_expr(int idx, Type* ty) { return e; } -AstNode* ast_new_func_call(const char* name, Type* ty) { +AstNode* ast_new_func_call(AstNode* func, AstNode* args) { AstNode* e = ast_new(AstNodeKind_func_call); e->as.func_call = calloc(1, sizeof(FuncCallNode)); - e->as.func_call->name = name; - e->ty = ty; + e->as.func_call->func = func; + e->as.func_call->args = args; return e; } @@ -188,7 +188,7 @@ typedef struct { } RefExprNode; typedef struct { - const char* name; + AstNode* func; AstNode* args; } FuncCallNode; @@ -387,7 +387,7 @@ AstNode* ast_new_cast_expr(AstNode* operand, Type* result_ty); AstNode* ast_new_logical_expr(int op, AstNode* lhs, AstNode* rhs); AstNode* ast_new_cond_expr(AstNode* cond, AstNode* then, AstNode* else_); AstNode* ast_new_str_expr(int idx, Type* ty); -AstNode* ast_new_func_call(const char* name, Type* ty); +AstNode* ast_new_func_call(AstNode* func, AstNode* args); AstNode* ast_new_func(const char* name, Type* ty); AstNode* ast_new_gvar(const char* name, Type* ty); AstNode* ast_new_lvar(const char* name, int stack_offset, Type* ty); diff --git a/src/codegen.c b/src/codegen.c index 5ac61ed..91baef8 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -398,9 +398,12 @@ static void codegen_args(CodeGen* g, AstNode* args) { } static void codegen_func_call(CodeGen* g, FuncCallNode* call) { - const char* func_name = call->name; + const char* func_name = NULL; + if (call->func->kind == AstNodeKind_func) { + func_name = call->func->as.func->name; + } - if (strcmp(func_name, "__ducc_va_start") == 0) { + if (func_name && strcmp(func_name, "__ducc_va_start") == 0) { fprintf(g->out, " # __ducc_va_start BEGIN\n"); AstNode* va_list_args = &call->args->as.list->items[0]; codegen_expr(g, va_list_args, GenMode_rval); @@ -428,7 +431,7 @@ static void codegen_func_call(CodeGen* g, FuncCallNode* call) { return; } - if (strcmp(func_name, "__ducc_va_arg") == 0) { + if (func_name && strcmp(func_name, "__ducc_va_arg") == 0) { fprintf(g->out, " # __ducc_va_arg BEGIN\n"); // Evaluate va_list argument (first argument) @@ -511,7 +514,12 @@ static void codegen_func_call(CodeGen* g, FuncCallNode* call) { fprintf(g->out, " sub rsp, 8\n"); codegen_args(g, args); fprintf(g->out, " mov rax, 0\n"); - fprintf(g->out, " call %s\n", func_name); + if (func_name) { + fprintf(g->out, " call %s\n", func_name); + } else { + codegen_expr(g, call->func, GenMode_rval); + fprintf(g->out, " call rax\n"); + } fprintf(g->out, " add rsp, 8\n"); fprintf(g->out, " jmp .Lend%d\n", label); @@ -519,7 +527,12 @@ static void codegen_func_call(CodeGen* g, FuncCallNode* call) { codegen_args(g, args); fprintf(g->out, " mov rax, 0\n"); - fprintf(g->out, " call %s\n", func_name); + if (func_name) { + fprintf(g->out, " call %s\n", func_name); + } else { + codegen_expr(g, call->func, GenMode_rval); + fprintf(g->out, " call rax\n"); + } fprintf(g->out, ".Lend%d:\n", label); // Pop pass-by-stack arguments. diff --git a/src/codegen_wasm.c b/src/codegen_wasm.c index cb48f38..01dfe42 100644 --- a/src/codegen_wasm.c +++ b/src/codegen_wasm.c @@ -62,12 +62,19 @@ static void codegen_lvar(CodeGen* g, LvarNode* lvar, GenMode) { } static void codegen_func_call(CodeGen* g, FuncCallNode* call) { + const char* func_name; + if (call->func->kind == AstNodeKind_func) { + func_name = call->func->as.func->name; + } else { + unimplemented(); + } + AstNode* args = call->args; for (int i = 0; i < args->as.list->len; ++i) { AstNode* arg = args->as.list->items + i; codegen_expr(g, arg, GenMode_rval); } - fprintf(g->out, " call $%s\n", call->name); + fprintf(g->out, " call $%s\n", func_name); } static void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) { diff --git a/src/parse.c b/src/parse.c index 6443de2..9083ecc 100644 --- a/src/parse.c +++ b/src/parse.c @@ -637,14 +637,6 @@ static AstNode* parse_primary_expr(Parser* p) { } else if (t->kind == TokenKind_ident) { const char* name = t->value.string; - if (peek_token(p)->kind == TokenKind_paren_l) { - int func_idx = find_func(p, name); - if (func_idx == -1) { - fatal_error("%s:%d: undefined function: %s", t->loc.filename, t->loc.line, name); - } - return ast_new_func_call(name, p->funcs.data[func_idx].ty->result); - } - int lvar_idx = find_lvar(p, name); if (lvar_idx == -1) { int gvar_idx = find_gvar(p, name); @@ -695,7 +687,12 @@ static AstNode* parse_postfix_expr(Parser* p) { if (consume_token_if(p, TokenKind_paren_l)) { AstNode* args = parse_argument_expr_list(p); expect(p, TokenKind_paren_r); - ret->as.func_call->args = args; + ret = ast_new_func_call(ret, args); + Type* func_type = ret->as.func_call->func->ty; + if (func_type->kind == TypeKind_ptr) { + func_type = func_type->base; + } + ret->ty = func_type->result; } else if (consume_token_if(p, TokenKind_bracket_l)) { AstNode* idx = parse_expr(p); expect(p, TokenKind_bracket_r); @@ -758,7 +755,12 @@ static AstNode* parse_unary_expr(Parser* p) { return ast_new_ref_expr(operand); } else if (consume_token_if(p, TokenKind_star)) { AstNode* operand = parse_cast_expr(p); - return ast_new_deref_expr(operand); + if (operand->ty->kind == TypeKind_func) { + // dereference operator against function pointers are no-op. + return operand; + } else { + return ast_new_deref_expr(operand); + } } else if (consume_token_if(p, TokenKind_plusplus)) { AstNode* operand = parse_unary_expr(p); return ast_new_assign_add_expr(operand, ast_new_int(1)); @@ -1228,8 +1230,28 @@ static AstNode* parse_direct_declarator(Parser* p, Type* ty) { decl = ast_new_declarator(parse_ident(p)->value.string, ty); } else if (peek_token(p)->kind == TokenKind_paren_l && !is_type_token(p, peek_token2(p))) { next_token(p); + // 1st pass: skip content inside parentheses. + int saved_pos = p->pos; + Type* dummy = type_new(TypeKind_void); + parse_declarator(p, dummy); + expect(p, TokenKind_paren_r); + // Parse suffixes. + while (1) { + if (peek_token(p)->kind == TokenKind_bracket_l) { + ty = parse_array_declarator_suffix(p, ty); + } else if (peek_token(p)->kind == TokenKind_paren_l) { + ty = parse_function_declarator_suffix(p, ty); + } else { + break; + } + } + // 2nd pass: re-parse inner content. + int end_pos = p->pos; + p->pos = saved_pos; decl = parse_declarator(p, ty); expect(p, TokenKind_paren_r); + p->pos = end_pos; + return ast_new_declarator(decl->as.declarator->name, decl->ty); } else { decl = ast_new_declarator(NULL, ty); } diff --git a/tests/cli.sh b/tests/cli.sh index fc8a481..2c6ac72 100644 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -45,8 +45,9 @@ int main(int argc, char** argv) { EOF # compile errors +# TODO: report as "undefined function" cat <<'EOF' > expected -main.c:2: undefined function: f +main.c:2: undefined variable: f EOF test_compile_error <<'EOF' int main() { diff --git a/tests/functions.c b/tests/functions.c index b5ffecb..eef9496 100644 --- a/tests/functions.c +++ b/tests/functions.c @@ -95,6 +95,10 @@ int f9(int select, int a, int b, int c, int d, S e, int f, int g) { } } +int f10() { + return 12345; +} + // recursive functions int fib(int n) { if (n <= 1) { @@ -136,6 +140,20 @@ int main() { ASSERT_EQ(7, f9(6, 1, 2, 3, 4, s, 7, 8)); ASSERT_EQ(8, f9(7, 1, 2, 3, 4, s, 7, 8)); + // function pointers + ASSERT_EQ(12345, (f10)()); + ASSERT_EQ(12345, (*f10)()); + ASSERT_EQ(12345, (**f10)()); + + int (*fp1)() = f10; + ASSERT_EQ(12345, fp1()); + int (*fp2)(int, int, int, int, int, int) = f; + ASSERT_EQ(1, fp2(1, 2, 3, 4, 5, 6)); + int (*fp3)(int, int, int, int, int, int) = f6; + ASSERT_EQ(6, fp3(1, 2, 3, 4, 5, 6)); + int (*fp4)(int, int, int, int, int, int, int, int, int, int, int) = f7; + ASSERT_EQ(7, fp4(6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + // recursive functions ASSERT_EQ(89, fib(10)); } |
