From 8b720543f538862796fec0ff6b7ea12cb140bf0f Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sun, 25 Jan 2026 14:38:28 +0000 Subject: Fix for #90 --- README.md | 19 +++++++++ src/ast/ast.c | 74 ++++++++++++++++++++++++++++++++++++ src/ast/ast.h | 1 + src/codegen/codegen.c | 3 +- src/codegen/codegen_utils.c | 68 ++++++++++++++++++++++++--------- src/parser/parser_type.c | 27 +++++++++++-- tests/functions/test_raw_func_ptr.zc | 27 +++++++++++++ 7 files changed, 195 insertions(+), 24 deletions(-) create mode 100644 tests/functions/test_raw_func_ptr.zc diff --git a/README.md b/README.md index 9159ea2..e87b8ea 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Join the discussion, share demos, ask questions, or report bugs in the official - [Const Arguments](#const-arguments) - [Default Arguments](#default-arguments) - [Lambdas (Closures)](#lambdas-closures) + - [Raw Function Pointers](#raw-function-pointers) - [Variadic Functions](#variadic-functions) - [5. Control Flow](#5-control-flow) - [Conditionals](#conditionals) @@ -319,6 +320,24 @@ var double = x -> x * factor; // Arrow syntax var full = fn(x: int) -> int { return x * factor; }; // Block syntax ``` +#### Raw Function Pointers +Zen C supports raw C function pointers using the `fn*` syntax. This allows seamless interop with C libraries that expect function pointers without closure overhead. + +```zc +// Function taking a raw function pointer +fn set_callback(cb: fn*(int)) { + cb(42); +} + +// Function returning a raw function pointer +fn get_callback() -> fn*(int) { + return my_handler; +} + +// Pointers to function pointers are supported (fn**) +var pptr: fn**(int) = &ptr; +``` + #### Variadic Functions Functions can accept a variable number of arguments using `...` and the `va_list` type. ```zc diff --git a/src/ast/ast.c b/src/ast/ast.c index b20d9c2..0799845 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -59,6 +59,8 @@ Type *type_new(TypeKind kind) t->args = NULL; t->arg_count = 0; t->is_const = 0; + t->is_explicit_struct = 0; + t->is_raw = 0; t->array_size = 0; return t; } @@ -283,6 +285,36 @@ static char *type_to_string_impl(Type *t) } case TYPE_FUNCTION: + if (t->is_raw) + { + // fn*(Args)->Ret + char *ret = type_to_string(t->inner); + char *res = xmalloc(strlen(ret) + 64); + sprintf(res, "fn*("); + + for (int i = 0; i < t->arg_count; i++) + { + if (i > 0) + { + char *tmp = xmalloc(strlen(res) + 3); + sprintf(tmp, "%s, ", res); + free(res); + res = tmp; + } + char *arg = type_to_string(t->args[i]); + char *tmp = xmalloc(strlen(res) + strlen(arg) + 1); + sprintf(tmp, "%s%s", res, arg); + free(res); + res = tmp; + free(arg); + } + char *tmp = xmalloc(strlen(res) + strlen(ret) + 5); // ) -> Ret + sprintf(tmp, "%s) -> %s", res, ret); + free(res); + res = tmp; + free(ret); + return res; + } if (t->inner) { free(type_to_string(t->inner)); @@ -410,6 +442,19 @@ static char *type_to_c_string_impl(Type *t) case TYPE_POINTER: { char *inner = type_to_c_string(t->inner); + char *ptr_token = strstr(inner, "(*"); + if (ptr_token) + { + long prefix_len = ptr_token - inner + 2; // "void (*" + char *res = xmalloc(strlen(inner) + 2); + strncpy(res, inner, prefix_len); + res[prefix_len] = 0; + strcat(res, "*"); + strcat(res, ptr_token + 2); + free(inner); + return res; + } + if (t->is_restrict) { char *res = xmalloc(strlen(inner) + 16); @@ -441,6 +486,35 @@ static char *type_to_c_string_impl(Type *t) } case TYPE_FUNCTION: + if (t->is_raw) + { + char *ret = type_to_c_string(t->inner); + char *res = xmalloc(strlen(ret) + 64); // heuristic start buffer + sprintf(res, "%s (*)(", ret); + + for (int i = 0; i < t->arg_count; i++) + { + if (i > 0) + { + char *tmp = xmalloc(strlen(res) + 3); + sprintf(tmp, "%s, ", res); + free(res); + res = tmp; + } + char *arg = type_to_c_string(t->args[i]); + char *tmp = xmalloc(strlen(res) + strlen(arg) + 1); + sprintf(tmp, "%s%s", res, arg); + free(res); + res = tmp; + free(arg); + } + char *tmp = xmalloc(strlen(res) + 2); + sprintf(tmp, "%s)", res); + free(res); + res = tmp; + free(ret); + return res; + } if (t->inner) { free(type_to_c_string(t->inner)); diff --git a/src/ast/ast.h b/src/ast/ast.h index 1614f3c..12d8f2b 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -64,6 +64,7 @@ typedef struct Type int arg_count; int is_const; int is_explicit_struct; // for example, "struct Foo" vs "Foo" + int is_raw; // Raw function pointer (fn*) union { int array_size; // For fixed-size arrays [T; N]. diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 689c4dc..7c58943 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -549,7 +549,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } } - if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION) + if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION && + !node->call.callee->type_info->is_raw) { fprintf(out, "({ z_closure_T _c = "); codegen_expression(ctx, node->call.callee, out); diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index cdb2110..6283bce 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -40,19 +40,33 @@ char *strip_template_suffix(const char *name) return xstrdup(name); } -// Helper to emit variable declarations with array types. -void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name) +// Helper to emit C declaration (handle arrays, function pointers correctly) +void emit_c_decl(FILE *out, const char *type_str, const char *name) { - (void)ctx; - char *bracket = strchr(type_str, '['); char *generic = strchr(type_str, '<'); + char *fn_ptr = strstr(type_str, "(*"); - if (generic && (!bracket || generic < bracket)) + if (fn_ptr) + { + char *end_paren = strchr(fn_ptr, ')'); + if (end_paren) + { + int prefix_len = end_paren - type_str; + fprintf(out, "%.*s%s%s", prefix_len, type_str, name, end_paren); + } + else + { + // Fallback if malformed (shouldn't happen) + int prefix_len = fn_ptr - type_str + 2; + fprintf(out, "%.*s%s%s", prefix_len, type_str, name, fn_ptr + 2); + } + } + else if (generic && (!bracket || generic < bracket)) { // Strip generic part for C output int base_len = generic - type_str; - fprintf(out, "%.*s %s", base_len, type_str, var_name); + fprintf(out, "%.*s %s", base_len, type_str, name); if (bracket) { @@ -62,14 +76,21 @@ void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, con else if (bracket) { int base_len = bracket - type_str; - fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket); + fprintf(out, "%.*s %s%s", base_len, type_str, name, bracket); } else { - fprintf(out, "%s %s", type_str, var_name); + fprintf(out, "%s %s", type_str, name); } } +// Helper to emit variable declarations with array types. +void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name) +{ + (void)ctx; + emit_c_decl(out, type_str, var_name); +} + // Find struct definition ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) { @@ -649,7 +670,20 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) ret_str = xstrdup("void"); } - fprintf(out, "%s %s(", ret_str, name_override ? name_override : func->func.name); + char *ret_suffix = NULL; + char *fn_ptr = strstr(ret_str, "(*)"); + + if (fn_ptr) + { + int prefix_len = fn_ptr - ret_str + 2; // Include "(*" + fprintf(out, "%.*s%s(", prefix_len, ret_str, + name_override ? name_override : func->func.name); + ret_suffix = fn_ptr + 2; + } + else + { + fprintf(out, "%s %s(", ret_str, name_override ? name_override : func->func.name); + } free(ret_str); // Args @@ -683,16 +717,7 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } // check if array type - char *bracket = strchr(type_str, '['); - if (bracket) - { - int base_len = bracket - type_str; - fprintf(out, "%.*s %s%s", base_len, type_str, name, bracket); - } - else - { - fprintf(out, "%s %s", type_str, name); - } + emit_c_decl(out, type_str, name); free(type_str); } if (func->func.is_varargs) @@ -705,6 +730,11 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } } fprintf(out, ")"); + + if (ret_suffix) + { + fprintf(out, "%s", ret_suffix); + } } // Invalidate a moved-from variable by zeroing it out to prevent double-free diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 5774571..0585baa 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -751,14 +751,31 @@ Type *parse_type_formal(ParserContext *ctx, Lexer *l) } } + Type *t = NULL; + // Example: fn(int, int) -> int if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0 && lexer_peek(l).len == 2) { lexer_next(l); // eat 'fn' + + int star_count = 0; + while (lexer_peek(l).type == TOK_OP && strncmp(lexer_peek(l).start, "*", 1) == 0) + { + lexer_next(l); + star_count++; + } + Type *fn_type = type_new(TYPE_FUNCTION); + fn_type->is_raw = (star_count > 0); fn_type->is_varargs = 0; + Type *wrapped = fn_type; + for (int i = 1; i < star_count; i++) + { + wrapped = type_new_ptr(wrapped); + } + expect(l, TOK_LPAREN, "Expected '(' for function type"); // Parse Arguments @@ -794,11 +811,13 @@ Type *parse_type_formal(ParserContext *ctx, Lexer *l) fn_type->inner = type_new(TYPE_VOID); } - return fn_type; + t = wrapped; + } + else + { + // Handles: int, Struct, Generic, [Slice], (Tuple) + t = parse_type_base(ctx, l); } - - // Handles: int, Struct, Generic, [Slice], (Tuple) - Type *t = parse_type_base(ctx, l); // Handles: T*, T**, etc. while (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '*') diff --git a/tests/functions/test_raw_func_ptr.zc b/tests/functions/test_raw_func_ptr.zc new file mode 100644 index 0000000..16ec7df --- /dev/null +++ b/tests/functions/test_raw_func_ptr.zc @@ -0,0 +1,27 @@ + +fn print_msg(msg: char*) { + puts(msg); +} + +fn run_callback(cb: fn*(char*), arg: char*) { + cb(arg); +} + +fn get_printer() -> fn*(char*) { + return print_msg; +} + +test "raw_func_ptr_basic" { + run_callback(print_msg, "Hello Raw Ptr"); +} + +test "raw_func_ptr_return_explicit" { + var p: fn*(char*) = get_printer(); + p("Returned Ptr Explicit works"); +} + +test "fn_ptr_ptr" { + var p: fn*(char*) = print_msg; + var pp: fn**(char*) = &p; + (*pp)("Double Pointer works"); +} -- cgit v1.2.3