aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-07 15:42:00 +0900
committernsfisis <nsfisis@gmail.com>2026-02-07 17:33:55 +0900
commitcdddf2422553f1f21c8d2c57cd382b8362dc80fb (patch)
tree3053026498552aecda2e4889c8455298ad70c8cb
parente1042a6373773830297dfd5718938c12f21ae624 (diff)
downloadducc-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.c6
-rw-r--r--src/ast.h4
-rw-r--r--src/codegen.c23
-rw-r--r--src/codegen_wasm.c9
-rw-r--r--src/parse.c42
-rw-r--r--tests/cli.sh3
-rw-r--r--tests/functions.c18
7 files changed, 83 insertions, 22 deletions
diff --git a/src/ast.c b/src/ast.c
index c2e1aba..eb478c5 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -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;
}
diff --git a/src/ast.h b/src/ast.h
index 3f9eb82..fe2f720 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -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));
}