aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-09-28 15:24:28 +0900
committernsfisis <nsfisis@gmail.com>2025-09-28 15:24:28 +0900
commit1b208f7f0a3b7d6b72c2a431d9fcd6e18fe76f5d (patch)
tree4902e2907f39cb0ae05439365e70d5628396a6fc
parent235969b792bf11e8b7927318cf01b5ef5705177b (diff)
downloadducc-1b208f7f0a3b7d6b72c2a431d9fcd6e18fe76f5d.tar.gz
ducc-1b208f7f0a3b7d6b72c2a431d9fcd6e18fe76f5d.tar.zst
ducc-1b208f7f0a3b7d6b72c2a431d9fcd6e18fe76f5d.zip
feat: support using function as a value
-rw-r--r--src/ast.h1
-rw-r--r--src/codegen.c7
-rw-r--r--src/parse.c9
-rw-r--r--tests/test_function_pointers.sh18
4 files changed, 31 insertions, 4 deletions
diff --git a/src/ast.h b/src/ast.h
index 5f4367d..c5171d7 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -102,6 +102,7 @@ typedef enum {
AstNodeKind_enum_member,
AstNodeKind_expr_stmt,
AstNodeKind_for_stmt,
+ AstNodeKind_func,
AstNodeKind_func_call,
AstNodeKind_func_decl,
AstNodeKind_func_def,
diff --git a/src/codegen.c b/src/codegen.c
index 36f854d..34f6c23 100644
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -439,6 +439,11 @@ static void codegen_gvar(CodeGen* g, AstNode* ast, GenMode gen_mode) {
}
}
+static void codegen_func_ref(CodeGen* g, AstNode* ast) {
+ fprintf(g->out, " lea rax, %s[rip]\n", ast->name);
+ fprintf(g->out, " push rax\n");
+}
+
static void codegen_composite_expr(CodeGen* g, AstNode* ast) {
// Standard C does not have composite expression, but ducc internally has.
for (int i = 0; i < ast->node_len; ++i) {
@@ -478,6 +483,8 @@ static void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) {
codegen_lvar(g, ast, gen_mode);
} else if (ast->kind == AstNodeKind_gvar) {
codegen_gvar(g, ast, gen_mode);
+ } else if (ast->kind == AstNodeKind_func) {
+ codegen_func_ref(g, ast);
} else if (ast->kind == AstNodeKind_list) {
codegen_composite_expr(g, ast);
} else {
diff --git a/src/parse.c b/src/parse.c
index dbbb61d..80c7aac 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -406,7 +406,14 @@ static AstNode* parse_primary_expr(Parser* p) {
if (gvar_idx == -1) {
int enum_member_idx = find_enum_member(p, name);
if (enum_member_idx == -1) {
- fatal_error("undefined variable: %s", name);
+ int func_idx = find_func(p, name);
+ if (func_idx == -1) {
+ fatal_error("undefined variable: %s", name);
+ }
+ AstNode* e = ast_new(AstNodeKind_func);
+ e->name = name;
+ e->ty = p->funcs.data[func_idx].ty;
+ return e;
}
int enum_idx = enum_member_idx / 1000;
int n = enum_member_idx % 1000;
diff --git a/tests/test_function_pointers.sh b/tests/test_function_pointers.sh
index c97a087..cddc623 100644
--- a/tests/test_function_pointers.sh
+++ b/tests/test_function_pointers.sh
@@ -1,12 +1,24 @@
cat <<'EOF' > expected
+a
+h
+g
EOF
test_diff <<'EOF'
-int* f(int a);
-int (*f)(int a);
+int* f1(int a);
+int (*f2)(int a);
extern int atexit (void (*) (void));
extern int atexit (void (*fn) (void));
-int main() {}
+int printf(const char*, ...);
+
+void g() { printf("g\n"); }
+void h() { printf("h\n"); }
+
+int main() {
+ atexit(g);
+ atexit(h);
+ printf("a\n");
+}
EOF