diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-22 02:59:58 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-22 03:00:08 +0000 |
| commit | bb44db8e61a40d4fcd31a87140f3c61aed551a2c (patch) | |
| tree | 7a79a68508e78e7c8f10913fa98a47075b962db5 /src | |
| parent | f912e071eaaa362b369626d7312589d4c9ac311b (diff) | |
This one is big, it solves several bugs that were not detected.
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/codegen.c | 291 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 50 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 102 |
3 files changed, 396 insertions, 47 deletions
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 457594f..fa65aef 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -11,7 +11,7 @@ #include "zprep_plugin.h" // Helper: emit a single pattern condition (either a value, or a range) -static void emit_single_pattern_cond(const char *pat, int id, FILE *out) +static void emit_single_pattern_cond(const char *pat, int id, int is_ptr, FILE *out) { // Check for range pattern: "start..end" or "start..=end" char *range_incl = strstr(pat, "..="); @@ -25,7 +25,14 @@ static void emit_single_pattern_cond(const char *pat, int id, FILE *out) strncpy(start, pat, start_len); start[start_len] = 0; char *end = xstrdup(range_incl + 3); - fprintf(out, "(_m_%d >= %s && _m_%d <= %s)", id, start, id, end); + if (is_ptr) + { + fprintf(out, "(*_m_%d >= %s && *_m_%d <= %s)", id, start, id, end); + } + else + { + fprintf(out, "(_m_%d >= %s && _m_%d <= %s)", id, start, id, end); + } free(start); free(end); } @@ -37,29 +44,58 @@ static void emit_single_pattern_cond(const char *pat, int id, FILE *out) strncpy(start, pat, start_len); start[start_len] = 0; char *end = xstrdup(range_excl + 2); - fprintf(out, "(_m_%d >= %s && _m_%d < %s)", id, start, id, end); + if (is_ptr) + { + fprintf(out, "(*_m_%d >= %s && *_m_%d < %s)", id, start, id, end); + } + else + { + fprintf(out, "(_m_%d >= %s && _m_%d < %s)", id, start, id, end); + } free(start); free(end); } else if (pat[0] == '"') { - // String pattern - fprintf(out, "strcmp(_m_%d, %s) == 0", id, pat); + // String pattern - string comparison, _m is char* or similar + if (is_ptr) + { + fprintf(out, "strcmp(*_m_%d, %s) == 0", id, pat); + } + else + { + fprintf(out, "strcmp(_m_%d, %s) == 0", id, pat); + } } else if (pat[0] == '\'') { // Char literal pattern - fprintf(out, "_m_%d == %s", id, pat); + if (is_ptr) + { + fprintf(out, "*_m_%d == %s", id, pat); + } + else + { + fprintf(out, "_m_%d == %s", id, pat); + } } else { // Numeric or simple pattern - fprintf(out, "_m_%d == %s", id, pat); + if (is_ptr) + { + fprintf(out, "*_m_%d == %s", id, pat); + } + else + { + fprintf(out, "_m_%d == %s", id, pat); + } } } // Helper: emit condition for a pattern (may contain OR patterns with '|') -static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int id, FILE *out) +static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int id, int is_ptr, + FILE *out) { // Check if pattern contains '|' for OR patterns if (strchr(pattern, '|')) @@ -81,11 +117,18 @@ static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int EnumVariantReg *reg = find_enum_variant(ctx, part); if (reg) { - fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + if (is_ptr) + { + fprintf(out, "_m_%d->tag == %d", id, reg->tag_id); + } + else + { + fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + } } else { - emit_single_pattern_cond(part, id, out); + emit_single_pattern_cond(part, id, is_ptr, out); } first = 0; part = strtok_r(NULL, "|", &saveptr); @@ -99,11 +142,18 @@ static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int EnumVariantReg *reg = find_enum_variant(ctx, pattern); if (reg) { - fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + if (is_ptr) + { + fprintf(out, "_m_%d->tag == %d", id, reg->tag_id); + } + else + { + fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + } } else { - emit_single_pattern_cond(pattern, id, out); + emit_single_pattern_cond(pattern, id, is_ptr, out); } } } @@ -120,18 +170,52 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0); fprintf(out, "({ "); - emit_auto_type(ctx, node->match_stmt.expr, node->token, out); - fprintf(out, " _m_%d = ", id); - if (is_self) + + // Check if any case uses ref binding - only take address if needed + int has_ref_binding = 0; + ASTNode *ref_check = node->match_stmt.cases; + while (ref_check) { - fprintf(out, "*("); + if (ref_check->match_case.is_ref) + { + has_ref_binding = 1; + break; + } + ref_check = ref_check->next; } - codegen_expression(ctx, node->match_stmt.expr, out); + + int is_lvalue_opt = (node->match_stmt.expr->type == NODE_EXPR_VAR || + node->match_stmt.expr->type == NODE_EXPR_MEMBER || + node->match_stmt.expr->type == NODE_EXPR_INDEX); + if (is_self) { - fprintf(out, ")"); + fprintf(out, "ZC_AUTO _m_%d = ", id); + codegen_expression(ctx, node->match_stmt.expr, out); + fprintf(out, "; "); + } + else if (has_ref_binding && is_lvalue_opt) + { + // Take address for ref bindings + fprintf(out, "ZC_AUTO _m_%d = &", id); + codegen_expression(ctx, node->match_stmt.expr, out); + fprintf(out, "; "); + } + else if (has_ref_binding) + { + // Non-lvalue with ref binding: create temporary + emit_auto_type(ctx, node->match_stmt.expr, node->token, out); + fprintf(out, " _temp_%d = ", id); + codegen_expression(ctx, node->match_stmt.expr, out); + fprintf(out, "; ZC_AUTO _m_%d = &_temp_%d; ", id, id); + } + else + { + // No ref bindings: store value directly (not pointer) + fprintf(out, "ZC_AUTO _m_%d = ", id); + codegen_expression(ctx, node->match_stmt.expr, out); + fprintf(out, "; "); } - fprintf(out, "; "); if (is_expr) { @@ -208,11 +292,11 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (strcmp(c->match_case.pattern, "Some") == 0) { - fprintf(out, "_m_%d.is_some", id); + fprintf(out, "_m_%d->is_some", id); } else if (strcmp(c->match_case.pattern, "None") == 0) { - fprintf(out, "!_m_%d.is_some", id); + fprintf(out, "!_m_%d->is_some", id); } else { @@ -223,11 +307,11 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (strcmp(c->match_case.pattern, "Ok") == 0) { - fprintf(out, "_m_%d.is_ok", id); + fprintf(out, "_m_%d->is_ok", id); } else if (strcmp(c->match_case.pattern, "Err") == 0) { - fprintf(out, "!_m_%d.is_ok", id); + fprintf(out, "!_m_%d->is_ok", id); } else { @@ -237,7 +321,7 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, else { // Use helper for OR patterns, range patterns, and simple patterns - emit_pattern_condition(ctx, c->match_case.pattern, id, out); + emit_pattern_condition(ctx, c->match_case.pattern, id, has_ref_binding, out); } fprintf(out, ") { "); if (c->match_case.binding_name) @@ -261,10 +345,17 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (c->match_case.is_ref) { - fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name, id); + // _m is pointer when has_ref_binding, use -> + fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name, id); + } + else if (has_ref_binding) + { + // _m is pointer, use -> but don't take address + fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name, id); } else { + // _m is value, use . fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); } } @@ -279,12 +370,12 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (c->match_case.is_ref) { - fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id, + fprintf(out, "__typeof__(&_m_%d->val) %s = &_m_%d->val; ", id, c->match_case.binding_name, id); } else { - fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, + fprintf(out, "__typeof__(_m_%d->val) %s = _m_%d->val; ", id, c->match_case.binding_name, id); } } @@ -292,11 +383,19 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (c->match_case.is_ref) { - fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name, + // _m is pointer when has_ref_binding, use -> + fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name, + id); + } + else if (has_ref_binding) + { + // _m is pointer, use -> but don't take address + fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name, id); } else { + // _m is value, use . fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); } @@ -308,12 +407,12 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (c->match_case.is_ref) { - fprintf(out, "__typeof__(&_m_%d.err) %s = &_m_%d.err; ", id, + fprintf(out, "__typeof__(&_m_%d->err) %s = &_m_%d->err; ", id, c->match_case.binding_name, id); } else { - fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id, + fprintf(out, "__typeof__(_m_%d->err) %s = _m_%d->err; ", id, c->match_case.binding_name, id); } } @@ -321,11 +420,19 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (c->match_case.is_ref) { - fprintf(out, "ZC_AUTO %s = &_m_%d.err; ", c->match_case.binding_name, + // _m is pointer when has_ref_binding, use -> + fprintf(out, "ZC_AUTO %s = &_m_%d->err; ", c->match_case.binding_name, + id); + } + else if (has_ref_binding) + { + // _m is pointer, use -> but don't take address + fprintf(out, "ZC_AUTO %s = _m_%d->err; ", c->match_case.binding_name, id); } else { + // _m is value, use . fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name, id); } @@ -343,16 +450,23 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { f = c->match_case.pattern; } - // Generic struct destructuring (e.g. MyStruct_Variant) + // Generic struct destructuring (for example, MyStruct_Variant) // Assuming data union or accessible field. - // Original: _m_%d.data.%s if (c->match_case.is_ref) { - fprintf(out, "ZC_AUTO %s = &_m_%d.data.%s; ", c->match_case.binding_name, id, + // _m is pointer when has_ref_binding, use -> + fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", c->match_case.binding_name, id, + f); + } + else if (has_ref_binding) + { + // _m is pointer, use -> but don't take address + fprintf(out, "ZC_AUTO %s = _m_%d->data.%s; ", c->match_case.binding_name, id, f); } else { + // _m is value, use . fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f); } } @@ -521,16 +635,85 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { fprintf(out, "(!"); } - fprintf(out, "%s__eq(&", base); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, ", &"); - codegen_expression(ctx, node->binary.right, out); + fprintf(out, "%s__eq(", base); + + if (node->binary.left->type == NODE_EXPR_VAR || + node->binary.left->type == NODE_EXPR_INDEX || + node->binary.left->type == NODE_EXPR_MEMBER) + { + fprintf(out, "&"); + codegen_expression(ctx, node->binary.left, out); + } + else + { + fprintf(out, "({ ZC_AUTO _t = "); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, "; &_t; })"); + } + + fprintf(out, ", "); + + if (node->binary.right->type == NODE_EXPR_VAR || + node->binary.right->type == NODE_EXPR_INDEX || + node->binary.right->type == NODE_EXPR_MEMBER) + { + fprintf(out, "&"); + codegen_expression(ctx, node->binary.right, out); + } + else + { + fprintf(out, "({ ZC_AUTO _t = "); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, "; &_t; })"); + } + fprintf(out, ")"); if (strcmp(node->binary.op, "!=") == 0) { fprintf(out, ")"); } } + else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 || + strcmp(t1, "const char*") == 0)) + { + // Check if comparing to NULL - don't use strcmp for NULL comparisons + int is_null_compare = 0; + if (node->binary.right->type == NODE_EXPR_VAR && + strcmp(node->binary.right->var_ref.name, "NULL") == 0) + { + is_null_compare = 1; + } + else if (node->binary.left->type == NODE_EXPR_VAR && + strcmp(node->binary.left->var_ref.name, "NULL") == 0) + { + is_null_compare = 1; + } + + if (is_null_compare) + { + // Direct pointer comparison for NULL + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); + } + else + { + fprintf(out, "(strcmp("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, ", "); + codegen_expression(ctx, node->binary.right, out); + if (strcmp(node->binary.op, "==") == 0) + { + fprintf(out, ") == 0)"); + } + else + { + fprintf(out, ") != 0)"); + } + } + } else { fprintf(out, "("); @@ -943,7 +1126,18 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else { codegen_expression(ctx, node->member.target, out); - fprintf(out, "%s%s", node->member.is_pointer_access ? "->" : ".", node->member.field); + // Verify actual type instead of trusting is_pointer_access flag + char *lt = infer_type(ctx, node->member.target); + int actually_ptr = 0; + if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) + { + actually_ptr = 1; + } + if (lt) + { + free(lt); + } + fprintf(out, "%s%s", actually_ptr ? "->" : ".", node->member.field); } break; case NODE_EXPR_INDEX: @@ -1647,6 +1841,20 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "}\n"); break; + case NODE_ASSERT: + fprintf(out, "assert("); + codegen_expression(ctx, node->assert_stmt.condition, out); + if (node->assert_stmt.message) + { + fprintf(out, ", %s", node->assert_stmt.message); + } + else + { + fprintf(out, ", \"Assertion failed\""); + } + fprintf(out, ");\n"); + break; + case NODE_DEFER: if (defer_count < MAX_DEFER) { @@ -1783,7 +1991,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) } { char *tname = NULL; - if (node->type_info) + if (node->type_info && + (!node->var_decl.init_expr || node->var_decl.init_expr->type != NODE_AWAIT)) { tname = codegen_type_to_string(node->type_info); } @@ -1792,7 +2001,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) tname = node->var_decl.type_str; } - if (tname) + if (tname && strcmp(tname, "void*") != 0 && strcmp(tname, "unknown") != 0) { char *clean_type = tname; if (strncmp(clean_type, "struct ", 7) == 0) diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index f712ca4..e490789 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -26,8 +26,20 @@ void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, con (void)ctx; char *bracket = strchr(type_str, '['); + char *generic = strchr(type_str, '<'); - if (bracket) + 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); + + if (bracket) + { + fprintf(out, "%s", bracket); + } + } + else if (bracket) { int base_len = bracket - type_str; fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket); @@ -116,7 +128,8 @@ char *infer_type(ParserContext *ctx, ASTNode *node) { return NULL; } - if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0) + if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0 && + strcmp(node->resolved_type, "void*") != 0) { return node->resolved_type; } @@ -155,6 +168,16 @@ char *infer_type(ParserContext *ctx, ASTNode *node) { if (sig->is_async) { + if (sig->ret_type) + { + char *inner = codegen_type_to_string(sig->ret_type); + if (inner) + { + char *buf = xmalloc(strlen(inner) + 10); + sprintf(buf, "Async<%s>", inner); + return buf; + } + } return "Async"; } if (sig->ret_type) @@ -365,7 +388,28 @@ char *infer_type(ParserContext *ctx, ASTNode *node) if (node->type == NODE_AWAIT) { // Infer underlying type T from await Async<T> - // If it's a direct call await foo(), we know T from foo's signature. + // Check operand type for Generics <T> + char *op_type = infer_type(ctx, node->unary.operand); + if (op_type) + { + char *start = strchr(op_type, '<'); + if (start) + { + start++; // Skip < + char *end = strrchr(op_type, '>'); + if (end && end > start) + { + int len = end - start; + char *extracted = xmalloc(len + 1); + strncpy(extracted, start, len); + extracted[len] = 0; + return extracted; + } + } + } + + // Fallback: If it's a direct call await foo(), we can lookup signature even if generic + // syntax wasn't used if (node->unary.operand->type == NODE_EXPR_CALL && node->unary.operand->call.callee->type == NODE_EXPR_VAR) { diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index ff931f5..9888701 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -1,12 +1,24 @@ - #include "../zen/zen_facts.h" #include "parser.h" #include <ctype.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +extern ASTNode *global_user_structs; + +// Helper to check if a type is a struct type +int is_struct_type(ParserContext *ctx, const char *type_name) +{ + if (!type_name) + { + return 0; + } + return find_struct_def(ctx, type_name) != NULL; +} + Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name); int is_type_copy(ParserContext *ctx, Type *t) @@ -1700,7 +1712,54 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) if (fi.type == TOK_RBRACE) { - is_struct_init = 1; + // Empty struct init often conflicts with block start (e.g. if x == y {}) + // We allow it only if we verify 'acc' is a struct name. + if (find_struct_def(ctx, acc)) + { + is_struct_init = 1; + } + else + { + // Fallback: Check if it's a generic instantiation (e.g. Optional_T) + // We check if 'acc' starts with any known struct name followed by '_' + StructRef *sr = ctx->parsed_structs_list; + while (sr) + { + if (sr->node && sr->node->type == NODE_STRUCT) + { + size_t len = strlen(sr->node->strct.name); + if (strncmp(acc, sr->node->strct.name, len) == 0 && acc[len] == '_') + { + is_struct_init = 1; + break; + } + } + sr = sr->next; + } + + if (!is_struct_init && global_user_structs) + { + ASTNode *gn = global_user_structs; + while (gn) + { + if (gn->type == NODE_STRUCT) + { + size_t len = strlen(gn->strct.name); + if (strncmp(acc, gn->strct.name, len) == 0 && acc[len] == '_') + { + is_struct_init = 1; + break; + } + } + gn = gn->next; + } + } + + if (!is_struct_init) + { + is_struct_init = 0; + } + } } else if (fi.type == TOK_IDENT) { @@ -2010,7 +2069,28 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) Type *async_type = type_new(TYPE_STRUCT); async_type->name = xstrdup("Async"); node->type_info = async_type; - node->resolved_type = xstrdup("Async"); + + if (sig->ret_type) + { + char *inner = type_to_string(sig->ret_type); + if (inner) + { + char buf[512]; + snprintf(buf, 511, "Async<%s>", inner); + node->resolved_type = xstrdup(buf); + async_type->name = xstrdup(buf); // HACK: Persist generic info in name + free(inner); + } + else + { + node->resolved_type = xstrdup("Async"); + } + } + else + { + node->resolved_type = xstrdup("Async"); + } + node->type_info = async_type; } else if (sig->ret_type) { @@ -4423,6 +4503,22 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) sig->arg_types[1]->kind == TYPE_POINTER) { Type *rt = rhs->type_info; + + // If rhs is a variable reference without type_info, look it up + if (!rt && rhs->type == NODE_EXPR_VAR) + { + Symbol *sym = find_symbol_entry(ctx, rhs->var_ref.name); + if (sym && sym->type_info) + { + rt = sym->type_info; + rhs->type_info = rt; + if (sym->type_name) + { + rhs->resolved_type = xstrdup(sym->type_name); + } + } + } + int is_rhs_ptr = (rt && rt->kind == TYPE_POINTER); if (!is_rhs_ptr) // Need pointer, have value { |
