From ed4bbfd8cf4a72fdf4a5d6cba94d537cab340356 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Thu, 22 Jan 2026 22:40:43 +0000 Subject: Removing some duplicates and dissecting codegen/parser. --- src/codegen/codegen.c | 2939 +++++++---------------------- src/codegen/codegen.h | 3 + src/codegen/codegen_decl.c | 90 +- src/codegen/codegen_main.c | 16 +- src/codegen/codegen_stmt.c | 1671 +++++++++++++++++ src/codegen/codegen_utils.c | 20 + src/constants.h | 46 + src/parser/parser_decl.c | 802 ++++++++ src/parser/parser_stmt.c | 4353 +++++++++++++------------------------------ src/parser/parser_struct.c | 943 ++++++++++ 10 files changed, 5482 insertions(+), 5401 deletions(-) create mode 100644 src/codegen/codegen_stmt.c create mode 100644 src/constants.h create mode 100644 src/parser/parser_decl.c create mode 100644 src/parser/parser_struct.c (limited to 'src') diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index a43c902..3a93f91 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1,6 +1,7 @@ #include "codegen.h" #include "zprep.h" +#include "../constants.h" #include #include #include @@ -10,870 +11,334 @@ #include "ast.h" #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, int is_ptr, FILE *out) +// Emit literal expression (int, float, string, char) +static void codegen_literal_expr(ASTNode *node, FILE *out) { - // Check for range pattern: "start..end" or "start..=end" - char *range_incl = strstr(pat, "..="); - char *range_excl = strstr(pat, ".."); - - if (range_incl) - { - // Inclusive range: start..=end -> _m_id >= start && _m_id <= end - int start_len = (int)(range_incl - pat); - char *start = xmalloc(start_len + 1); - strncpy(start, pat, start_len); - start[start_len] = 0; - char *end = xstrdup(range_incl + 3); - 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 (range_excl) + if (node->literal.type_kind == TOK_STRING) { - // Exclusive range: start..end -> _m_id >= start && _m_id < end - int start_len = (int)(range_excl - pat); - char *start = xmalloc(start_len + 1); - strncpy(start, pat, start_len); - start[start_len] = 0; - char *end = xstrdup(range_excl + 2); - 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); + fprintf(out, "\"%s\"", node->literal.string_val); } - else if (pat[0] == '"') + else if (node->literal.type_kind == TOK_CHAR) { - // 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); - } + fprintf(out, "%s", node->literal.string_val); } - else if (pat[0] == '\'') + else if (node->literal.type_kind == 1) // float { - // Char literal pattern - if (is_ptr) - { - fprintf(out, "*_m_%d == %s", id, pat); - } - else - { - fprintf(out, "_m_%d == %s", id, pat); - } + fprintf(out, "%f", node->literal.float_val); } - else + else // int { - // Numeric or simple pattern - if (is_ptr) + if (node->literal.int_val > 9223372036854775807ULL) { - fprintf(out, "*_m_%d == %s", id, pat); + fprintf(out, "%lluULL", (unsigned long long)node->literal.int_val); } else { - fprintf(out, "_m_%d == %s", id, pat); + fprintf(out, "%llu", (unsigned long long)node->literal.int_val); } } } -// Helper: emit condition for a pattern (may contain OR patterns with '|') -static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int id, int is_ptr, - FILE *out) +// Emit variable reference expression +static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) { - // Check if pattern contains '|' for OR patterns - if (strchr(pattern, '|')) - { - // Split by '|' and emit OR conditions - char *pattern_copy = xstrdup(pattern); - char *saveptr; - char *part = strtok_r(pattern_copy, "|", &saveptr); - int first = 1; - fprintf(out, "("); - while (part) - { - if (!first) - { - fprintf(out, " || "); - } + (void)ctx; // May be used for context lookup in future - // Check if part is an enum variant - EnumVariantReg *reg = find_enum_variant(ctx, part); - if (reg) - { - 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, is_ptr, out); - } - first = 0; - part = strtok_r(NULL, "|", &saveptr); - } - fprintf(out, ")"); - free(pattern_copy); - } - else + if (g_current_lambda) { - // Single pattern (may be a range) - EnumVariantReg *reg = find_enum_variant(ctx, pattern); - if (reg) + for (int i = 0; i < g_current_lambda->lambda.num_captures; i++) { - if (is_ptr) - { - fprintf(out, "_m_%d->tag == %d", id, reg->tag_id); - } - else + if (strcmp(node->var_ref.name, g_current_lambda->lambda.captured_vars[i]) == 0) { - fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + fprintf(out, "ctx->%s", node->var_ref.name); + return; } } - else - { - emit_single_pattern_cond(pattern, id, is_ptr, out); - } - } -} - -// static function for internal use. -static char *g_current_func_ret_type = NULL; -static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result) -{ - int id = tmp_counter++; - int is_self = (node->match_stmt.expr->type == NODE_EXPR_VAR && - strcmp(node->match_stmt.expr->var_ref.name, "self") == 0); - - char *ret_type = infer_type(ctx, node); - int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0); - - fprintf(out, "({ "); - - // 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) - { - if (ref_check->match_case.is_ref) - { - has_ref_binding = 1; - break; - } - ref_check = ref_check->next; - } - - 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, "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, "; "); - } - - if (is_expr) - { - fprintf(out, "%s _r_%d; ", ret_type, id); } - char *expr_type = infer_type(ctx, node->match_stmt.expr); - int is_option = (expr_type && strncmp(expr_type, "Option_", 7) == 0); - int is_result = (expr_type && strncmp(expr_type, "Result_", 7) == 0); - - char *enum_name = NULL; - ASTNode *chk = node->match_stmt.cases; - int has_wildcard = 0; - while (chk) + if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0) { - if (strcmp(chk->match_case.pattern, "_") == 0) - { - has_wildcard = 1; - } - else if (!enum_name) + if (node->var_ref.suggestion) { - EnumVariantReg *reg = find_enum_variant(ctx, chk->match_case.pattern); - if (reg) - { - enum_name = reg->enum_name; - } + char msg[256]; + sprintf(msg, "Undefined variable '%s'", node->var_ref.name); + char help[256]; + sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion); + zwarn_at(node->token, "%s\n = help: %s", msg, help); } - chk = chk->next; } + fprintf(out, "%s", node->var_ref.name); +} - if (enum_name && !has_wildcard) +// Emit lambda expression +static void codegen_lambda_expr(ASTNode *node, FILE *out) +{ + if (node->lambda.num_captures > 0) { - // Iterate through all registered variants for this enum - EnumVariantReg *v = ctx->enum_variants; - while (v) + fprintf(out, + "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct " + "Lambda_%d_Ctx));\n", + node->lambda.lambda_id, node->lambda.lambda_id); + for (int i = 0; i < node->lambda.num_captures; i++) { - if (strcmp(v->enum_name, enum_name) == 0) + fprintf(out, "ctx->%s = ", node->lambda.captured_vars[i]); + int found = 0; + if (g_current_lambda) { - int covered = 0; - ASTNode *c2 = node->match_stmt.cases; - while (c2) + for (int k = 0; k < g_current_lambda->lambda.num_captures; k++) { - if (strcmp(c2->match_case.pattern, v->variant_name) == 0) + if (strcmp(node->lambda.captured_vars[i], + g_current_lambda->lambda.captured_vars[k]) == 0) { - covered = 1; + fprintf(out, "ctx->%s", node->lambda.captured_vars[i]); + found = 1; break; } - c2 = c2->next; - } - if (!covered) - { - zwarn_at(node->token, "Non-exhaustive match: Missing variant '%s'", - v->variant_name); } } - v = v->next; + if (!found) + { + fprintf(out, "%s", node->lambda.captured_vars[i]); + } + fprintf(out, ";\n"); } + fprintf(out, "(z_closure_T){.func = _lambda_%d, .ctx = ctx}; })", node->lambda.lambda_id); } + else + { + fprintf(out, "((z_closure_T){.func = (void*)_lambda_%d, .ctx = NULL})", + node->lambda.lambda_id); + } +} - ASTNode *c = node->match_stmt.cases; - int first = 1; - while (c) +void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) +{ + if (!node) { - if (!first) - { - fprintf(out, " else "); - } - fprintf(out, "if ("); - if (strcmp(c->match_case.pattern, "_") == 0) - { - fprintf(out, "1"); - } - else if (is_option) - { - if (strcmp(c->match_case.pattern, "Some") == 0) - { - fprintf(out, "_m_%d->is_some", id); - } - else if (strcmp(c->match_case.pattern, "None") == 0) - { - fprintf(out, "!_m_%d->is_some", id); - } - else - { - fprintf(out, "1"); - } - } - else if (is_result) + return; + } + switch (node->type) + { + case NODE_MATCH: + codegen_match_internal(ctx, node, out, 1); + break; + case NODE_EXPR_BINARY: + if (strncmp(node->binary.op, "??", 2) == 0 && strlen(node->binary.op) == 2) { - if (strcmp(c->match_case.pattern, "Ok") == 0) - { - fprintf(out, "_m_%d->is_ok", id); - } - else if (strcmp(c->match_case.pattern, "Err") == 0) - { - fprintf(out, "!_m_%d->is_ok", id); - } - else - { - fprintf(out, "1"); - } + fprintf(out, "({ "); + emit_auto_type(ctx, node->binary.left, node->token, out); + fprintf(out, " _l = ("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, "); _l ? _l : ("); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, "); })"); } - else + else if (strcmp(node->binary.op, "?\?=") == 0) { - // Use helper for OR patterns, range patterns, and simple patterns - emit_pattern_condition(ctx, c->match_case.pattern, id, has_ref_binding, out); + fprintf(out, "({ if (!("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, ")) "); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " = ("); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, "); "); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, "; })"); } - fprintf(out, ") { "); - if (c->match_case.binding_name) + else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0)) { - if (is_option) + char *t1 = infer_type(ctx, node->binary.left); + + int is_ptr = 0; + if (t1) { - if (strstr(g_config.cc, "tcc")) - { - if (c->match_case.is_ref) - { - 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, - c->match_case.binding_name, id); - } - } - else + char *check = t1; + int depth = 0; + while (depth++ < 10) { - if (c->match_case.is_ref) + if (strchr(check, '*')) { - // _m is pointer when has_ref_binding, use -> - fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name, id); + is_ptr = 1; + break; } - else if (has_ref_binding) + int resolved = 0; + ASTNode *alias = global_user_structs; + if (alias) { - // _m is pointer, use -> but don't take address - fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name, id); + while (alias) + { + if (alias->type == NODE_TYPE_ALIAS && + strcmp(check, alias->type_alias.alias) == 0) + { + check = alias->type_alias.original_type; + resolved = 1; + break; + } + alias = alias->next; + } } - else + if (!resolved) { - // _m is value, use . - fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); + break; } } } - else if (is_result) // FIX: Changed 'if' to 'else if' to match original logic structure - // if needed, but original code had implicit fallthrough checks? No, - // checks match pattern. + + int is_basic = IS_BASIC_TYPE(t1); + + ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; + if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && + !is_ptr) { - if (strcmp(c->match_case.pattern, "Ok") == 0) + char *base = t1; + if (strncmp(base, "struct ", 7) == 0) { - if (strstr(g_config.cc, "tcc")) - { - if (c->match_case.is_ref) - { - 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, - c->match_case.binding_name, id); - } - } - else - { - if (c->match_case.is_ref) - { - // _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); - } - } + base += 7; } - else + + if (strcmp(node->binary.op, "!=") == 0) { - if (strstr(g_config.cc, "tcc")) - { - if (c->match_case.is_ref) - { - 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, - c->match_case.binding_name, id); - } - } - else - { - if (c->match_case.is_ref) - { - // _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); - } - } + fprintf(out, "(!"); } - } - else - { - char *f = strrchr(c->match_case.pattern, '_'); - if (f) + 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) { - f++; + fprintf(out, "&"); + codegen_expression(ctx, node->binary.left, out); } else { - f = c->match_case.pattern; - } - // Generic struct destructuring (for example, MyStruct_Variant) - // Assuming data union or accessible field. - if (c->match_case.is_ref) - { - // _m is pointer when has_ref_binding, use -> - fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", c->match_case.binding_name, id, - f); + fprintf(out, "({ ZC_AUTO _t = "); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, "; &_t; })"); } - else if (has_ref_binding) + + fprintf(out, ", "); + + if (node->binary.right->type == NODE_EXPR_VAR || + node->binary.right->type == NODE_EXPR_INDEX || + node->binary.right->type == NODE_EXPR_MEMBER) { - // _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); + fprintf(out, "&"); + codegen_expression(ctx, node->binary.right, out); } else { - // _m is value, use . - fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f); + fprintf(out, "({ ZC_AUTO _t = "); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, "; &_t; })"); } - } - } - // Check if body is a string literal (should auto-print). - ASTNode *body = c->match_case.body; - int is_string_literal = (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == 2); - - if (is_expr) - { - fprintf(out, "_r_%d = ", id); - if (is_string_literal) - { - codegen_node_single(ctx, body, out); + fprintf(out, ")"); + if (strcmp(node->binary.op, "!=") == 0) + { + fprintf(out, ")"); + } } - else + else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 || + strcmp(t1, "const char*") == 0)) { - if (body->type == NODE_BLOCK) + // 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 { - int saved = defer_count; - fprintf(out, "({ "); - ASTNode *stmt = body->block.statements; - while (stmt) + 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) { - codegen_node_single(ctx, stmt, out); - stmt = stmt->next; + fprintf(out, ") == 0)"); } - for (int i = defer_count - 1; i >= saved; i--) + else { - codegen_node_single(ctx, defer_stack[i], out); + fprintf(out, ") != 0)"); } - defer_count = saved; - fprintf(out, " })"); } - else - { - codegen_node_single(ctx, body, out); - } - } - fprintf(out, ";"); - } - else - { - if (is_string_literal) - { - fprintf(out, "({ printf(\"%%s\", "); - codegen_expression(ctx, body, out); - fprintf(out, "); printf(\"\\n\"); 0; })"); } else { - codegen_node_single(ctx, body, out); + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); } } - - fprintf(out, " }"); - first = 0; - c = c->next; - } - - if (is_expr) - { - fprintf(out, " _r_%d; })", id); - } - else - { - fprintf(out, " })"); - } -} - -// Emit literal expression (int, float, string, char) -static void codegen_literal_expr(ASTNode *node, FILE *out) -{ - if (node->literal.type_kind == TOK_STRING) - { - fprintf(out, "\"%s\"", node->literal.string_val); - } - else if (node->literal.type_kind == TOK_CHAR) - { - fprintf(out, "%s", node->literal.string_val); - } - else if (node->literal.type_kind == 1) // float - { - fprintf(out, "%f", node->literal.float_val); - } - else // int - { - if (node->literal.int_val > 9223372036854775807ULL) - { - fprintf(out, "%lluULL", (unsigned long long)node->literal.int_val); - } else { - fprintf(out, "%llu", (unsigned long long)node->literal.int_val); - } - } -} - -// Emit variable reference expression -static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) -{ - (void)ctx; // May be used for context lookup in future - - if (g_current_lambda) - { - for (int i = 0; i < g_current_lambda->lambda.num_captures; i++) - { - if (strcmp(node->var_ref.name, g_current_lambda->lambda.captured_vars[i]) == 0) - { - fprintf(out, "ctx->%s", node->var_ref.name); - return; - } + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); } - } - - if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0) + break; + case NODE_EXPR_VAR: + codegen_var_expr(ctx, node, out); + break; + case NODE_LAMBDA: + codegen_lambda_expr(node, out); + break; + case NODE_EXPR_LITERAL: + codegen_literal_expr(node, out); + break; + case NODE_EXPR_CALL: { - if (node->var_ref.suggestion) + if (node->call.callee->type == NODE_EXPR_MEMBER) { - char msg[256]; - sprintf(msg, "Undefined variable '%s'", node->var_ref.name); - char help[256]; - sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion); - zwarn_at(node->token, "%s\n = help: %s", msg, help); - } - } - fprintf(out, "%s", node->var_ref.name); -} + ASTNode *target = node->call.callee->member.target; + char *method = node->call.callee->member.field; -// Emit lambda expression -static void codegen_lambda_expr(ASTNode *node, FILE *out) -{ - if (node->lambda.num_captures > 0) - { - fprintf(out, - "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct " - "Lambda_%d_Ctx));\n", - node->lambda.lambda_id, node->lambda.lambda_id); - for (int i = 0; i < node->lambda.num_captures; i++) - { - fprintf(out, "ctx->%s = ", node->lambda.captured_vars[i]); - int found = 0; - if (g_current_lambda) + if (strcmp(method, "len") == 0) { - for (int k = 0; k < g_current_lambda->lambda.num_captures; k++) + if (target->type_info && target->type_info->kind == TYPE_ARRAY) { - if (strcmp(node->lambda.captured_vars[i], - g_current_lambda->lambda.captured_vars[k]) == 0) + if (target->type_info->array_size > 0) { - fprintf(out, "ctx->%s", node->lambda.captured_vars[i]); - found = 1; - break; + fprintf(out, "%d", target->type_info->array_size); } + else + { + codegen_expression(ctx, target, out); + fprintf(out, ".len"); + } + return; } } - if (!found) + + char *type = infer_type(ctx, target); + if (type) { - fprintf(out, "%s", node->lambda.captured_vars[i]); - } - fprintf(out, ";\n"); - } - fprintf(out, "(z_closure_T){.func = _lambda_%d, .ctx = ctx}; })", node->lambda.lambda_id); - } - else - { - fprintf(out, "((z_closure_T){.func = (void*)_lambda_%d, .ctx = NULL})", - node->lambda.lambda_id); - } -} - -void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) -{ - if (!node) - { - return; - } - switch (node->type) - { - case NODE_MATCH: - codegen_match_internal(ctx, node, out, 1); - break; - case NODE_EXPR_BINARY: - if (strncmp(node->binary.op, "??", 2) == 0 && strlen(node->binary.op) == 2) - { - fprintf(out, "({ "); - emit_auto_type(ctx, node->binary.left, node->token, out); - fprintf(out, " _l = ("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, "); _l ? _l : ("); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, "); })"); - } - else if (strcmp(node->binary.op, "?\?=") == 0) - { - fprintf(out, "({ if (!("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, ")) "); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, " = ("); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, "); "); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, "; })"); - } - else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0)) - { - char *t1 = infer_type(ctx, node->binary.left); - - int is_ptr = 0; - if (t1) - { - char *check = t1; - int depth = 0; - while (depth++ < 10) - { - if (strchr(check, '*')) - { - is_ptr = 1; - break; - } - int resolved = 0; - ASTNode *alias = global_user_structs; - if (alias) - { - while (alias) - { - if (alias->type == NODE_TYPE_ALIAS && - strcmp(check, alias->type_alias.alias) == 0) - { - check = alias->type_alias.original_type; - resolved = 1; - break; - } - alias = alias->next; - } - } - if (!resolved) - { - break; - } - } - } - - int is_basic = 0; - if (t1) - { - is_basic = (strcmp(t1, "int") == 0 || strcmp(t1, "bool") == 0 || - strcmp(t1, "char") == 0 || strcmp(t1, "void") == 0 || - strcmp(t1, "float") == 0 || strcmp(t1, "double") == 0 || - strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 || - strcmp(t1, "ssize_t") == 0 || strcmp(t1, "__auto_type") == 0); - } - - ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; - if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && - !is_ptr) - { - char *base = t1; - if (strncmp(base, "struct ", 7) == 0) - { - base += 7; - } - - if (strcmp(node->binary.op, "!=") == 0) - { - fprintf(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, "("); - 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, "("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, " %s ", node->binary.op); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, ")"); - } - break; - case NODE_EXPR_VAR: - codegen_var_expr(ctx, node, out); - break; - case NODE_LAMBDA: - codegen_lambda_expr(node, out); - break; - case NODE_EXPR_LITERAL: - codegen_literal_expr(node, out); - break; - case NODE_EXPR_CALL: - { - if (node->call.callee->type == NODE_EXPR_MEMBER) - { - ASTNode *target = node->call.callee->member.target; - char *method = node->call.callee->member.field; - - if (strcmp(method, "len") == 0) - { - if (target->type_info && target->type_info->kind == TYPE_ARRAY) - { - if (target->type_info->array_size > 0) - { - fprintf(out, "%d", target->type_info->array_size); - } - else - { - codegen_expression(ctx, target, out); - fprintf(out, ".len"); - } - return; - } - } - - char *type = infer_type(ctx, target); - if (type) - { - char *clean = xstrdup(type); - char *ptr = strchr(clean, '*'); - if (ptr) - { - *ptr = 0; - } + char *clean = xstrdup(type); + char *ptr = strchr(clean, '*'); + if (ptr) + { + *ptr = 0; + } char *base = clean; if (strncmp(base, "struct ", 7) == 0) @@ -982,1632 +447,596 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) return; } } - if (node->call.callee->type == NODE_EXPR_VAR) - { - ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); - if (def && def->type == NODE_STRUCT) - { - fprintf(out, "(struct %s){0}", node->call.callee->var_ref.name); - return; - } - } - - if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION) - { - fprintf(out, "({ z_closure_T _c = "); - codegen_expression(ctx, node->call.callee, out); - fprintf(out, "; "); - - Type *ft = node->call.callee->type_info; - char *ret = codegen_type_to_string(ft->inner); - if (strcmp(ret, "string") == 0) - { - free(ret); - ret = xstrdup("char*"); - } - - fprintf(out, "((%s (*)(void*", ret); - for (int i = 0; i < ft->arg_count; i++) - { - char *as = codegen_type_to_string(ft->args[i]); - fprintf(out, ", %s", as); - free(as); - } - if (ft->is_varargs) - { - fprintf(out, ", ..."); - } - fprintf(out, "))_c.func)(_c.ctx"); - - ASTNode *arg = node->call.args; - while (arg) - { - fprintf(out, ", "); - codegen_expression(ctx, arg, out); - arg = arg->next; - } - fprintf(out, "); })"); - free(ret); - break; - } - - codegen_expression(ctx, node->call.callee, out); - fprintf(out, "("); - - if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR) - { - char *fn_name = node->call.callee->var_ref.name; - FuncSig *sig = find_func(ctx, fn_name); - - if (sig && sig->arg_types) - { - for (int p = 0; p < sig->total_args; p++) - { - ASTNode *arg = node->call.args; - - for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next) - { - if (node->call.arg_names[i] && p < node->call.arg_count) - { - - // For now, emit in order provided... - } - } - } - } - - ASTNode *arg = node->call.args; - int first = 1; - while (arg) - { - if (!first) - { - fprintf(out, ", "); - } - first = 0; - codegen_expression(ctx, arg, out); - arg = arg->next; - } - } - else - { - FuncSig *sig = NULL; - if (node->call.callee->type == NODE_EXPR_VAR) - { - sig = find_func(ctx, node->call.callee->var_ref.name); - } - - ASTNode *arg = node->call.args; - int arg_idx = 0; - while (arg) - { - int handled = 0; - if (sig && arg_idx < sig->total_args) - { - Type *param_t = sig->arg_types[arg_idx]; - Type *arg_t = arg->type_info; - - if (param_t && param_t->kind == TYPE_ARRAY && param_t->array_size == 0 && - arg_t && arg_t->kind == TYPE_ARRAY && arg_t->array_size > 0) - { - char *inner = type_to_string(param_t->inner); - fprintf(out, "(Slice_%s){.data = ", inner); - codegen_expression(ctx, arg, out); - fprintf(out, ", .len = %d, .cap = %d}", arg_t->array_size, - arg_t->array_size); - free(inner); - handled = 1; - } - } - - if (!handled) - { - codegen_expression(ctx, arg, out); - } - - if (arg->next) - { - fprintf(out, ", "); - } - arg = arg->next; - arg_idx++; - } - } - fprintf(out, ")"); - break; - } - case NODE_EXPR_MEMBER: - if (strcmp(node->member.field, "len") == 0) - { - if (node->member.target->type_info) - { - if (node->member.target->type_info->kind == TYPE_ARRAY) - { - if (node->member.target->type_info->array_size > 0) - { - fprintf(out, "%d", node->member.target->type_info->array_size); - break; - } - } - } - } - - if (node->member.is_pointer_access == 2) - { - fprintf(out, "({ "); - emit_auto_type(ctx, node->member.target, node->token, out); - fprintf(out, " _t = ("); - codegen_expression(ctx, node->member.target, out); - fprintf(out, "); _t ? _t->%s : 0; })", node->member.field); - } - else - { - codegen_expression(ctx, node->member.target, out); - // 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: - { - int is_slice_struct = 0; - if (node->index.array->type_info) - { - if (node->index.array->type_info->kind == TYPE_ARRAY && - node->index.array->type_info->array_size == 0) - { - is_slice_struct = 1; - } - } - if (node->index.array->resolved_type) - { - if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) - { - is_slice_struct = 1; - } - } - - if (is_slice_struct) - { - if (node->index.array->type == NODE_EXPR_VAR) - { - codegen_expression(ctx, node->index.array, out); - fprintf(out, ".data[_z_check_bounds("); - codegen_expression(ctx, node->index.index, out); - fprintf(out, ", "); - codegen_expression(ctx, node->index.array, out); - fprintf(out, ".len)]"); - } - else - { - codegen_expression(ctx, node->index.array, out); - fprintf(out, ".data["); - codegen_expression(ctx, node->index.index, out); - fprintf(out, "]"); - } - } - else - { - int fixed_size = -1; - if (node->index.array->type_info && node->index.array->type_info->kind == TYPE_ARRAY) - { - fixed_size = node->index.array->type_info->array_size; - } - - codegen_expression(ctx, node->index.array, out); - fprintf(out, "["); - if (fixed_size > 0) - { - fprintf(out, "_z_check_bounds("); - } - codegen_expression(ctx, node->index.index, out); - if (fixed_size > 0) - { - fprintf(out, ", %d)", fixed_size); - } - fprintf(out, "]"); - } - } - break; - case NODE_EXPR_SLICE: - { - int known_size = -1; - int is_slice_struct = 0; - if (node->slice.array->type_info) - { - if (node->slice.array->type_info->kind == TYPE_ARRAY) - { - known_size = node->slice.array->type_info->array_size; - if (known_size == 0) - { - is_slice_struct = 1; - } - } - } - - char *tname = "unknown"; - if (node->type_info && node->type_info->inner) - { - tname = codegen_type_to_string(node->type_info->inner); - } - - fprintf(out, "({ "); - emit_auto_type(ctx, node->slice.array, node->token, out); - fprintf(out, " _arr = "); - codegen_expression(ctx, node->slice.array, out); - fprintf(out, "; int _start = "); - if (node->slice.start) - { - codegen_expression(ctx, node->slice.start, out); - } - else - { - fprintf(out, "0"); - } - fprintf(out, "; int _len = "); - - if (node->slice.end) - { - codegen_expression(ctx, node->slice.end, out); - fprintf(out, " - _start; "); - } - else - { - if (known_size > 0) - { - fprintf(out, "%d - _start; ", known_size); - } - else if (is_slice_struct) - { - fprintf(out, "_arr.len - _start; "); - } - else - { - fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; "); - } - } - - if (is_slice_struct) - { - fprintf(out, - "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = " - "_len }; })", - tname); - } - else - { - fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", - tname); - } - break; - } - case NODE_BLOCK: - { - int saved = defer_count; - fprintf(out, "({ "); - codegen_walker(ctx, node->block.statements, out); - for (int i = defer_count - 1; i >= saved; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } - defer_count = saved; - fprintf(out, " })"); - break; - } - case NODE_TRY: - { - char *type_name = "Result"; - if (g_current_func_ret_type) - { - type_name = g_current_func_ret_type; - } - else if (node->try_stmt.expr->type_info && node->try_stmt.expr->type_info->name) - { - type_name = node->try_stmt.expr->type_info->name; - } - - if (strcmp(type_name, "__auto_type") == 0 || strcmp(type_name, "unknown") == 0) - { - type_name = "Result"; - } - - char *search_name = type_name; - if (strncmp(search_name, "struct ", 7) == 0) - { - search_name += 7; - } - - int is_enum = 0; - StructRef *er = ctx->parsed_enums_list; - while (er) - { - if (er->node && er->node->type == NODE_ENUM && - strcmp(er->node->enm.name, search_name) == 0) - { - is_enum = 1; - break; - } - er = er->next; - } - if (!is_enum) - { - ASTNode *ins = ctx->instantiated_structs; - while (ins) - { - if (ins->type == NODE_ENUM && strcmp(ins->enm.name, search_name) == 0) - { - is_enum = 1; - break; - } - ins = ins->next; - } - } - - fprintf(out, "({ "); - emit_auto_type(ctx, node->try_stmt.expr, node->token, out); - fprintf(out, " _try = "); - codegen_expression(ctx, node->try_stmt.expr, out); - - if (is_enum) - { - fprintf(out, - "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); " - "_try.data.Ok; })", - search_name, search_name); - } - else - { - fprintf(out, - "; if (!_try.is_ok) return %s__Err(_try.err); " - "_try.val; })", - search_name); - } - break; - } - case NODE_RAW_STMT: - fprintf(out, "%s", node->raw_stmt.content); - break; - case NODE_PLUGIN: - { - // Plugin registry - declare external plugins - ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name); - - if (found) - { - ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", - .current_line = node->line, - .out = out, - .hoist_out = ctx->hoist_out}; - found->fn(node->plugin_stmt.body, &api); - } - else - { - fprintf(out, "/* Unknown plugin: %s */\n", node->plugin_stmt.plugin_name); - } - break; - } - case NODE_EXPR_UNARY: - if (node->unary.op && strcmp(node->unary.op, "&_rval") == 0) - { - fprintf(out, "({ "); - emit_auto_type(ctx, node->unary.operand, node->token, out); - fprintf(out, " _t = ("); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, "); &_t; })"); - } - else if (node->unary.op && strcmp(node->unary.op, "?") == 0) - { - fprintf(out, "({ "); - emit_auto_type(ctx, node->unary.operand, node->token, out); - fprintf(out, " _t = ("); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, "); if (_t.tag != 0) return _t; _t.data.Ok; })"); - } - else if (node->unary.op && strcmp(node->unary.op, "_post++") == 0) - { - fprintf(out, "("); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, "++)"); - } - else if (node->unary.op && strcmp(node->unary.op, "_post--") == 0) - { - fprintf(out, "("); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, "--)"); - } - else - { - fprintf(out, "(%s", node->unary.op); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, ")"); - } - break; - case NODE_EXPR_CAST: - fprintf(out, "(%s)(", node->cast.target_type); - codegen_expression(ctx, node->cast.expr, out); - fprintf(out, ")"); - break; - case NODE_EXPR_SIZEOF: - if (node->size_of.target_type) - { - fprintf(out, "sizeof(%s)", node->size_of.target_type); - } - else - { - fprintf(out, "sizeof("); - codegen_expression(ctx, node->size_of.expr, out); - fprintf(out, ")"); - } - break; - case NODE_TYPEOF: - if (node->size_of.target_type) - { - fprintf(out, "typeof(%s)", node->size_of.target_type); - } - else - { - fprintf(out, "typeof("); - codegen_expression(ctx, node->size_of.expr, out); - fprintf(out, ")"); - } - break; - - case NODE_REFLECTION: - { - Type *t = node->reflection.target_type; - if (node->reflection.kind == 0) - { // @type_name - char *s = codegen_type_to_string(t); - fprintf(out, "\"%s\"", s); - free(s); - } - else - { // @fields - if (t->kind != TYPE_STRUCT || !t->name) - { - fprintf(out, "((void*)0)"); - break; - } - char *sname = t->name; - // Find definition - ASTNode *def = find_struct_def(ctx, sname); - if (!def) - { - fprintf(out, "((void*)0)"); - break; - } - - fprintf(out, - "({ static struct { char *name; char *type; unsigned long offset; } " - "_fields_%s[] = {", - sname); - ASTNode *f = def->strct.fields; - while (f) - { - if (f->type == NODE_FIELD) - { - fprintf(out, "{ \"%s\", \"%s\", __builtin_offsetof(struct %s, %s) }, ", - f->field.name, f->field.type, sname, f->field.name); - } - f = f->next; - } - fprintf(out, "{ 0 } }; (void*)_fields_%s; })", sname); - } - break; - } - case NODE_EXPR_STRUCT_INIT: - { - const char *struct_name = node->struct_init.struct_name; - if (strcmp(struct_name, "Self") == 0 && g_current_impl_type) - { - struct_name = g_current_impl_type; - } - - int is_zen_struct = 0; - StructRef *sr = ctx->parsed_structs_list; - while (sr) - { - if (sr->node && sr->node->type == NODE_STRUCT && - strcmp(sr->node->strct.name, struct_name) == 0) - { - is_zen_struct = 1; - break; - } - sr = sr->next; - } - - if (is_zen_struct) - { - fprintf(out, "(struct %s){", struct_name); - } - else - { - fprintf(out, "(%s){", struct_name); - } - ASTNode *f = node->struct_init.fields; - while (f) - { - fprintf(out, ".%s = ", f->var_decl.name); - codegen_expression(ctx, f->var_decl.init_expr, out); - if (f->next) - { - fprintf(out, ", "); - } - f = f->next; - } - fprintf(out, "}"); - break; - } - case NODE_EXPR_ARRAY_LITERAL: - fprintf(out, "{"); - ASTNode *elem = node->array_literal.elements; - int first = 1; - while (elem) - { - if (!first) - { - fprintf(out, ", "); - } - codegen_expression(ctx, elem, out); - elem = elem->next; - first = 0; - } - fprintf(out, "}"); - break; - case NODE_TERNARY: - fprintf(out, "(("); - codegen_expression(ctx, node->ternary.cond, out); - fprintf(out, ") ? ("); - codegen_expression(ctx, node->ternary.true_expr, out); - fprintf(out, ") : ("); - codegen_expression(ctx, node->ternary.false_expr, out); - fprintf(out, "))"); - break; - case NODE_AWAIT: - { - char *ret_type = "void*"; - int free_ret = 0; - if (node->type_info) - { - char *t = codegen_type_to_string(node->type_info); - if (t) - { - ret_type = t; - free_ret = 1; - } - } - else if (node->resolved_type) - { - ret_type = node->resolved_type; - } - - if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) - { - char *inf = infer_type(ctx, node); - if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0) - { - if (free_ret) - { - free(ret_type); - } - ret_type = inf; - free_ret = 0; + if (node->call.callee->type == NODE_EXPR_VAR) + { + ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); + if (def && def->type == NODE_STRUCT) + { + fprintf(out, "(struct %s){0}", node->call.callee->var_ref.name); + return; } } - int needs_long_cast = 0; - int returns_struct = 0; - if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 && - strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0) + if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION) { - if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 && - strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 && - strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 && - strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 && - strncmp(ret_type, "uint", 4) != 0 && strncmp(ret_type, "int", 3) != 0) + fprintf(out, "({ z_closure_T _c = "); + codegen_expression(ctx, node->call.callee, out); + fprintf(out, "; "); + + Type *ft = node->call.callee->type_info; + char *ret = codegen_type_to_string(ft->inner); + if (strcmp(ret, "string") == 0) { - returns_struct = 1; + free(ret); + ret = xstrdup("char*"); } - else + + fprintf(out, "((%s (*)(void*", ret); + for (int i = 0; i < ft->arg_count; i++) { - needs_long_cast = 1; + char *as = codegen_type_to_string(ft->args[i]); + fprintf(out, ", %s", as); + free(as); } - if (strncmp(ret_type, "struct", 6) == 0) + if (ft->is_varargs) { - returns_struct = 1; + fprintf(out, ", ..."); } - } + fprintf(out, "))_c.func)(_c.ctx"); - fprintf(out, "({ Async _a = "); - codegen_expression(ctx, node->unary.operand, out); - fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); "); - if (strcmp(ret_type, "void") == 0) - { - fprintf(out, "})"); - } - else - { - if (returns_struct) + ASTNode *arg = node->call.args; + while (arg) { - fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type, ret_type); + fprintf(out, ", "); + codegen_expression(ctx, arg, out); + arg = arg->next; } - else + fprintf(out, "); })"); + free(ret); + break; + } + + codegen_expression(ctx, node->call.callee, out); + fprintf(out, "("); + + if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR) + { + char *fn_name = node->call.callee->var_ref.name; + FuncSig *sig = find_func(ctx, fn_name); + + if (sig && sig->arg_types) { - if (needs_long_cast) + for (int p = 0; p < sig->total_args; p++) { - fprintf(out, "(%s)(long)_r; })", ret_type); + ASTNode *arg = node->call.args; + + for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next) + { + if (node->call.arg_names[i] && p < node->call.arg_count) + { + + // For now, emit in order provided... + } + } } - else + } + + ASTNode *arg = node->call.args; + int first = 1; + while (arg) + { + if (!first) { - fprintf(out, "(%s)_r; })", ret_type); + fprintf(out, ", "); } + first = 0; + codegen_expression(ctx, arg, out); + arg = arg->next; } } - if (free_ret) - { - free(ret_type); - } - break; - } - default: - break; - } -} - -void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) -{ - if (!node) - { - return; - } - switch (node->type) - { - case NODE_MATCH: - codegen_match_internal(ctx, node, out, 0); // 0 = statement context - fprintf(out, ";\n"); - break; - case NODE_FUNCTION: - if (!node->func.body) - { - break; - } - - if (node->func.is_async) + else { - fprintf(out, "struct %s_Args {\n", node->func.name); - char *args_copy = xstrdup(node->func.args); - char *token = strtok(args_copy, ","); - int arg_count = 0; - char **arg_names = xmalloc(32 * sizeof(char *)); + FuncSig *sig = NULL; + if (node->call.callee->type == NODE_EXPR_VAR) + { + sig = find_func(ctx, node->call.callee->var_ref.name); + } - while (token) + ASTNode *arg = node->call.args; + int arg_idx = 0; + while (arg) { - while (*token == ' ') + int handled = 0; + if (sig && arg_idx < sig->total_args) { - token++; // trim leading + Type *param_t = sig->arg_types[arg_idx]; + Type *arg_t = arg->type_info; + + if (param_t && param_t->kind == TYPE_ARRAY && param_t->array_size == 0 && + arg_t && arg_t->kind == TYPE_ARRAY && arg_t->array_size > 0) + { + char *inner = type_to_string(param_t->inner); + fprintf(out, "(Slice_%s){.data = ", inner); + codegen_expression(ctx, arg, out); + fprintf(out, ", .len = %d, .cap = %d}", arg_t->array_size, + arg_t->array_size); + free(inner); + handled = 1; + } } - char *last_space = strrchr(token, ' '); - if (last_space) + + if (!handled) { - *last_space = 0; - char *type = token; - char *name = last_space + 1; - fprintf(out, "%s %s;\n", type, name); + codegen_expression(ctx, arg, out); + } - arg_names[arg_count++] = xstrdup(name); + if (arg->next) + { + fprintf(out, ", "); } - token = strtok(NULL, ","); + arg = arg->next; + arg_idx++; } - free(args_copy); - fprintf(out, "};\n"); - - fprintf(out, "void* _runner_%s(void* _args)\n", node->func.name); - fprintf(out, "{\n"); - fprintf(out, " struct %s_Args* args = (struct %s_Args*)_args;\n", node->func.name, - node->func.name); - - // Determine mechanism: struct/large-type? -> malloc; primitive -> cast - int returns_struct = 0; - char *rt = node->func.ret_type; - if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) + } + fprintf(out, ")"); + break; + } + case NODE_EXPR_MEMBER: + if (strcmp(node->member.field, "len") == 0) + { + if (node->member.target->type_info) { - if (strstr(rt, "*") == NULL && strcmp(rt, "string") != 0 && - strcmp(rt, "int") != 0 && strcmp(rt, "bool") != 0 && strcmp(rt, "char") != 0 && - strcmp(rt, "float") != 0 && strcmp(rt, "double") != 0 && - strcmp(rt, "long") != 0 && strcmp(rt, "usize") != 0 && - strcmp(rt, "isize") != 0 && strncmp(rt, "uint", 4) != 0 && - strncmp(rt, "int", 3) != 0) + if (node->member.target->type_info->kind == TYPE_ARRAY) { - returns_struct = 1; + if (node->member.target->type_info->array_size > 0) + { + fprintf(out, "%d", node->member.target->type_info->array_size); + break; + } } } + } - // Call Impl - if (returns_struct) + if (node->member.is_pointer_access == 2) + { + fprintf(out, "({ "); + emit_auto_type(ctx, node->member.target, node->token, out); + fprintf(out, " _t = ("); + codegen_expression(ctx, node->member.target, out); + fprintf(out, "); _t ? _t->%s : 0; })", node->member.field); + } + else + { + codegen_expression(ctx, node->member.target, out); + // 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, "*"))) { - fprintf(out, " %s *res_ptr = malloc(sizeof(%s));\n", rt, rt); - fprintf(out, " *res_ptr = "); + actually_ptr = 1; } - else if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) + if (lt) { - fprintf(out, " %s res = ", rt); + free(lt); } - else + fprintf(out, "%s%s", actually_ptr ? "->" : ".", node->member.field); + } + break; + case NODE_EXPR_INDEX: + { + int is_slice_struct = 0; + if (node->index.array->type_info) + { + if (node->index.array->type_info->kind == TYPE_ARRAY && + node->index.array->type_info->array_size == 0) { - fprintf(out, " "); + is_slice_struct = 1; } - - fprintf(out, "_impl_%s(", node->func.name); - for (int i = 0; i < arg_count; i++) + } + if (node->index.array->resolved_type) + { + if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) { - fprintf(out, "%sargs->%s", i > 0 ? ", " : "", arg_names[i]); + is_slice_struct = 1; } - fprintf(out, ");\n"); - fprintf(out, " free(args);\n"); + } - if (returns_struct) + if (is_slice_struct) + { + if (node->index.array->type == NODE_EXPR_VAR) { - fprintf(out, " return (void*)res_ptr;\n"); + codegen_expression(ctx, node->index.array, out); + fprintf(out, ".data[_z_check_bounds("); + codegen_expression(ctx, node->index.index, out); + fprintf(out, ", "); + codegen_expression(ctx, node->index.array, out); + fprintf(out, ".len)]"); } - else if (strcmp(rt, "void") != 0) + else { - fprintf(out, " return (void*)(long)res;\n"); + codegen_expression(ctx, node->index.array, out); + fprintf(out, ".data["); + codegen_expression(ctx, node->index.index, out); + fprintf(out, "]"); } - else + } + else + { + int fixed_size = -1; + if (node->index.array->type_info && node->index.array->type_info->kind == TYPE_ARRAY) { - fprintf(out, " return NULL;\n"); + fixed_size = node->index.array->type_info->array_size; } - fprintf(out, "}\n"); - fprintf(out, "%s _impl_%s(%s)\n", node->func.ret_type, node->func.name, - node->func.args); - fprintf(out, "{\n"); - defer_count = 0; - codegen_walker(ctx, node->func.body, out); - for (int i = defer_count - 1; i >= 0; i--) + codegen_expression(ctx, node->index.array, out); + fprintf(out, "["); + if (fixed_size > 0) { - codegen_node_single(ctx, defer_stack[i], out); + fprintf(out, "_z_check_bounds("); } - fprintf(out, "}\n"); - - // 4. Define Public Wrapper (Spawns Thread) - fprintf(out, "Async %s(%s)\n", node->func.name, node->func.args); - fprintf(out, "{\n"); - fprintf(out, " struct %s_Args* args = malloc(sizeof(struct %s_Args));\n", - node->func.name, node->func.name); - for (int i = 0; i < arg_count; i++) + codegen_expression(ctx, node->index.index, out); + if (fixed_size > 0) { - fprintf(out, " args->%s = %s;\n", arg_names[i], arg_names[i]); + fprintf(out, ", %d)", fixed_size); } - - fprintf(out, " pthread_t th;\n"); - fprintf(out, " pthread_create(&th, NULL, _runner_%s, args);\n", node->func.name); - fprintf(out, " return (Async){.thread=th, .result=NULL};\n"); - fprintf(out, "}\n"); - - break; + fprintf(out, "]"); } - - defer_count = 0; - fprintf(out, "\n"); - - // Emit GCC attributes before function + } + break; + case NODE_EXPR_SLICE: + { + int known_size = -1; + int is_slice_struct = 0; + if (node->slice.array->type_info) { - int has_attrs = node->func.constructor || node->func.destructor || - node->func.noinline || node->func.unused || node->func.weak || - node->func.cold || node->func.hot || node->func.noreturn || - node->func.pure || node->func.section; - if (has_attrs) + if (node->slice.array->type_info->kind == TYPE_ARRAY) { - fprintf(out, "__attribute__(("); - int first = 1; -#define EMIT_ATTR(cond, name) \ - if (cond) \ - { \ - if (!first) \ - fprintf(out, ", "); \ - fprintf(out, name); \ - first = 0; \ - } - EMIT_ATTR(node->func.constructor, "constructor"); - EMIT_ATTR(node->func.destructor, "destructor"); - EMIT_ATTR(node->func.noinline, "noinline"); - EMIT_ATTR(node->func.unused, "unused"); - EMIT_ATTR(node->func.weak, "weak"); - EMIT_ATTR(node->func.cold, "cold"); - EMIT_ATTR(node->func.hot, "hot"); - EMIT_ATTR(node->func.noreturn, "noreturn"); - EMIT_ATTR(node->func.pure, "pure"); - if (node->func.section) + known_size = node->slice.array->type_info->array_size; + if (known_size == 0) { - if (!first) - { - fprintf(out, ", "); - } - fprintf(out, "section(\"%s\")", node->func.section); + is_slice_struct = 1; } -#undef EMIT_ATTR - fprintf(out, ")) "); } } - if (node->func.is_inline) - { - fprintf(out, "inline "); - } - emit_func_signature(out, node, NULL); - fprintf(out, "\n"); - fprintf(out, "{\n"); - char *prev_ret = g_current_func_ret_type; - g_current_func_ret_type = node->func.ret_type; - codegen_walker(ctx, node->func.body, out); - for (int i = defer_count - 1; i >= 0; i--) + char *tname = "unknown"; + if (node->type_info && node->type_info->inner) { - codegen_node_single(ctx, defer_stack[i], out); + tname = codegen_type_to_string(node->type_info->inner); } - g_current_func_ret_type = prev_ret; - 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, "({ "); + emit_auto_type(ctx, node->slice.array, node->token, out); + fprintf(out, " _arr = "); + codegen_expression(ctx, node->slice.array, out); + fprintf(out, "; int _start = "); + if (node->slice.start) { - fprintf(out, ", %s", node->assert_stmt.message); + codegen_expression(ctx, node->slice.start, out); } else { - fprintf(out, ", \"Assertion failed\""); - } - fprintf(out, ");\n"); - break; - - case NODE_DEFER: - if (defer_count < MAX_DEFER) - { - defer_stack[defer_count++] = node->defer_stmt.stmt; + fprintf(out, "0"); } - break; - case NODE_IMPL: - g_current_impl_type = node->impl.struct_name; - codegen_walker(ctx, node->impl.methods, out); - g_current_impl_type = NULL; - break; - case NODE_IMPL_TRAIT: - g_current_impl_type = node->impl_trait.target_type; - codegen_walker(ctx, node->impl_trait.methods, out); + fprintf(out, "; int _len = "); - if (strcmp(node->impl_trait.trait_name, "Drop") == 0) + if (node->slice.end) { - char *tname = node->impl_trait.target_type; - fprintf(out, "\n// RAII Glue\n"); - fprintf(out, "void %s__Drop_glue(%s *self) {\n", tname, tname); - fprintf(out, " %s__Drop_drop(self);\n", tname); - fprintf(out, "}\n"); + codegen_expression(ctx, node->slice.end, out); + fprintf(out, " - _start; "); } - g_current_impl_type = NULL; - break; - case NODE_DESTRUCT_VAR: - { - int id = tmp_counter++; - fprintf(out, " "); - emit_auto_type(ctx, node->destruct.init_expr, node->token, out); - fprintf(out, " _tmp_%d = ", id); - codegen_expression(ctx, node->destruct.init_expr, out); - fprintf(out, ";\n"); - - if (node->destruct.is_guard) + else { - // var Some(val) = opt else ... - char *variant = node->destruct.guard_variant; - char *check = "val"; // field to access - - if (strcmp(variant, "Some") == 0) - { - fprintf(out, " if (!_tmp_%d.is_some) {\n", id); - } - else if (strcmp(variant, "Ok") == 0) + if (known_size > 0) { - fprintf(out, " if (!_tmp_%d.is_ok) {\n", id); + fprintf(out, "%d - _start; ", known_size); } - else if (strcmp(variant, "Err") == 0) + else if (is_slice_struct) { - fprintf(out, " if (_tmp_%d.is_ok) {\n", id); // Err if NOT ok - check = "err"; + fprintf(out, "_arr.len - _start; "); } else { - // Generic guard? Assume .is_variant present? - fprintf(out, " if (!_tmp_%d.is_%s) {\n", id, variant); + fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; "); } + } - // Else block - codegen_walker(ctx, node->destruct.else_block->block.statements, out); - fprintf(out, " }\n"); - - // Bind value - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, check, - node->destruct.names[0], id, check); - } - else - { - fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[0], id, - check); - } + if (is_slice_struct) + { + fprintf(out, + "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = " + "_len }; })", + tname); } else { - for (int i = 0; i < node->destruct.count; i++) - { - if (node->destruct.is_struct_destruct) - { - char *field = node->destruct.field_names ? node->destruct.field_names[i] - : node->destruct.names[i]; - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, field, - node->destruct.names[i], id, field); - } - else - { - fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[i], - id, field); - } - } - else - { - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, " __typeof__(_tmp_%d.v%d) %s = _tmp_%d.v%d;\n", id, i, - node->destruct.names[i], id, i); - } - else - { - fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n", node->destruct.names[i], - id, i); - } - } - } + fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", + tname); } break; } case NODE_BLOCK: { int saved = defer_count; - fprintf(out, " {\n"); + fprintf(out, "({ "); codegen_walker(ctx, node->block.statements, out); for (int i = defer_count - 1; i >= saved; i--) { codegen_node_single(ctx, defer_stack[i], out); } defer_count = saved; - fprintf(out, " }\n"); + fprintf(out, " })"); break; } - case NODE_VAR_DECL: - fprintf(out, " "); - if (node->var_decl.is_static) + case NODE_TRY: + { + char *type_name = "Result"; + if (g_current_func_ret_type) { - fprintf(out, "static "); + type_name = g_current_func_ret_type; } - if (node->var_decl.is_autofree) + else if (node->try_stmt.expr->type_info && node->try_stmt.expr->type_info->name) { - fprintf(out, "__attribute__((cleanup(_z_autofree_impl))) "); + type_name = node->try_stmt.expr->type_info->name; } - { - char *tname = NULL; - 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); - } - else if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0) - { - tname = node->var_decl.type_str; - } - - if (tname && strcmp(tname, "void*") != 0 && strcmp(tname, "unknown") != 0) - { - char *clean_type = tname; - if (strncmp(clean_type, "struct ", 7) == 0) - { - clean_type += 7; - } - - ASTNode *def = find_struct_def(ctx, clean_type); - if (def && def->type_info && def->type_info->traits.has_drop) - { - fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type); - } - - // Emit Variable with Type - emit_var_decl_type(ctx, out, tname, node->var_decl.name); - add_symbol(ctx, node->var_decl.name, tname, node->type_info); - if (node->var_decl.init_expr) - { - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - } - fprintf(out, ";\n"); - - if (node->type_info) - { - free(tname); // Free if allocated by codegen_type_to_string - } - } - else - { - // Inference Fallback - char *inferred = NULL; - if (node->var_decl.init_expr) - { - inferred = infer_type(ctx, node->var_decl.init_expr); - } - - if (inferred && strcmp(inferred, "__auto_type") != 0) - { - char *clean_type = inferred; - if (strncmp(clean_type, "struct ", 7) == 0) - { - clean_type += 7; - } - - ASTNode *def = find_struct_def(ctx, clean_type); - if (def && def->type_info && def->type_info->traits.has_drop) - { - fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type); - } - - emit_var_decl_type(ctx, out, inferred, node->var_decl.name); - add_symbol(ctx, node->var_decl.name, inferred, NULL); - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - fprintf(out, ";\n"); - } - else - { - emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); - fprintf(out, " %s", node->var_decl.name); - - if (inferred) - { - add_symbol(ctx, node->var_decl.name, inferred, NULL); - } - - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - fprintf(out, ";\n"); - } - } - } - break; - case NODE_CONST: - fprintf(out, " const "); - if (node->var_decl.type_str) - { - fprintf(out, "%s %s", node->var_decl.type_str, node->var_decl.name); - } - else - { - emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); - fprintf(out, " %s", node->var_decl.name); - } - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - fprintf(out, ";\n"); - break; - case NODE_FIELD: - if (node->field.bit_width > 0) + if (strcmp(type_name, "__auto_type") == 0 || strcmp(type_name, "unknown") == 0) { - fprintf(out, " %s %s : %d;\n", node->field.type, node->field.name, - node->field.bit_width); + type_name = "Result"; } - else + + char *search_name = type_name; + if (strncmp(search_name, "struct ", 7) == 0) { - fprintf(out, " "); - emit_var_decl_type(ctx, out, node->field.type, node->field.name); - fprintf(out, ";\n"); + search_name += 7; } - break; - case NODE_IF: - fprintf(out, "if ("); - codegen_expression(ctx, node->if_stmt.condition, out); - fprintf(out, ") "); - codegen_node_single(ctx, node->if_stmt.then_body, out); - if (node->if_stmt.else_body) + + int is_enum = 0; + StructRef *er = ctx->parsed_enums_list; + while (er) { - fprintf(out, " else "); - codegen_node_single(ctx, node->if_stmt.else_body, out); + if (er->node && er->node->type == NODE_ENUM && + strcmp(er->node->enm.name, search_name) == 0) + { + is_enum = 1; + break; + } + er = er->next; } - break; - case NODE_UNLESS: - fprintf(out, "if (!("); - codegen_expression(ctx, node->unless_stmt.condition, out); - fprintf(out, ")) "); - codegen_node_single(ctx, node->unless_stmt.body, out); - break; - case NODE_GUARD: - fprintf(out, "if (!("); - codegen_expression(ctx, node->guard_stmt.condition, out); - fprintf(out, ")) "); - codegen_node_single(ctx, node->guard_stmt.body, out); - break; - case NODE_WHILE: - { - loop_defer_boundary[loop_depth++] = defer_count; - fprintf(out, "while ("); - codegen_expression(ctx, node->while_stmt.condition, out); - fprintf(out, ") "); - codegen_node_single(ctx, node->while_stmt.body, out); - loop_depth--; - break; - } - case NODE_FOR: - { - loop_defer_boundary[loop_depth++] = defer_count; - fprintf(out, "for ("); - if (node->for_stmt.init) + if (!is_enum) { - if (node->for_stmt.init->type == NODE_VAR_DECL) + ASTNode *ins = ctx->instantiated_structs; + while (ins) { - ASTNode *v = node->for_stmt.init; - if (v->var_decl.type_str && strcmp(v->var_decl.type_str, "__auto_type") != 0) - { - fprintf(out, "%s %s = (%s)(", v->var_decl.type_str, v->var_decl.name, - v->var_decl.type_str); - codegen_expression(ctx, v->var_decl.init_expr, out); - fprintf(out, ")"); - } - else + if (ins->type == NODE_ENUM && strcmp(ins->enm.name, search_name) == 0) { - emit_auto_type(ctx, v->var_decl.init_expr, v->token, out); - fprintf(out, " %s = ", v->var_decl.name); - codegen_expression(ctx, v->var_decl.init_expr, out); + is_enum = 1; + break; } - } - else - { - codegen_expression(ctx, node->for_stmt.init, out); + ins = ins->next; } } - fprintf(out, "; "); - if (node->for_stmt.condition) + + fprintf(out, "({ "); + emit_auto_type(ctx, node->try_stmt.expr, node->token, out); + fprintf(out, " _try = "); + codegen_expression(ctx, node->try_stmt.expr, out); + + if (is_enum) { - codegen_expression(ctx, node->for_stmt.condition, out); + fprintf(out, + "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); " + "_try.data.Ok; })", + search_name, search_name); } - fprintf(out, "; "); - if (node->for_stmt.step) + else { - codegen_expression(ctx, node->for_stmt.step, out); + fprintf(out, + "; if (!_try.is_ok) return %s__Err(_try.err); " + "_try.val; })", + search_name); } - fprintf(out, ") "); - codegen_node_single(ctx, node->for_stmt.body, out); - loop_depth--; break; } - case NODE_BREAK: - // Run defers from current scope down to loop boundary before breaking - if (loop_depth > 0) - { - int boundary = loop_defer_boundary[loop_depth - 1]; - for (int i = defer_count - 1; i >= boundary; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } - } - if (node->break_stmt.target_label) + case NODE_RAW_STMT: + fprintf(out, "%s", node->raw_stmt.content); + break; + case NODE_PLUGIN: + { + // Plugin registry - declare external plugins + ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name); + + if (found) { - fprintf(out, "goto __break_%s;\n", node->break_stmt.target_label); + ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", + .current_line = node->line, + .out = out, + .hoist_out = ctx->hoist_out}; + found->fn(node->plugin_stmt.body, &api); } else { - fprintf(out, "break;\n"); + fprintf(out, "/* Unknown plugin: %s */\n", node->plugin_stmt.plugin_name); } break; - case NODE_CONTINUE: - // Run defers from current scope down to loop boundary before continuing - if (loop_depth > 0) + } + case NODE_EXPR_UNARY: + if (node->unary.op && strcmp(node->unary.op, "&_rval") == 0) { - int boundary = loop_defer_boundary[loop_depth - 1]; - for (int i = defer_count - 1; i >= boundary; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } + fprintf(out, "({ "); + emit_auto_type(ctx, node->unary.operand, node->token, out); + fprintf(out, " _t = ("); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, "); &_t; })"); } - if (node->continue_stmt.target_label) + else if (node->unary.op && strcmp(node->unary.op, "?") == 0) { - fprintf(out, "goto __continue_%s;\n", node->continue_stmt.target_label); + fprintf(out, "({ "); + emit_auto_type(ctx, node->unary.operand, node->token, out); + fprintf(out, " _t = ("); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, "); if (_t.tag != 0) return _t; _t.data.Ok; })"); } - else + else if (node->unary.op && strcmp(node->unary.op, "_post++") == 0) { - fprintf(out, "continue;\n"); + fprintf(out, "("); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, "++)"); } - break; - case NODE_GOTO: - if (node->goto_stmt.goto_expr) + else if (node->unary.op && strcmp(node->unary.op, "_post--") == 0) { - // Computed goto: goto *expr; - fprintf(out, "goto *("); - codegen_expression(ctx, node->goto_stmt.goto_expr, out); - fprintf(out, ");\n"); + fprintf(out, "("); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, "--)"); } else { - fprintf(out, "goto %s;\n", node->goto_stmt.label_name); + fprintf(out, "(%s", node->unary.op); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, ")"); } break; - case NODE_LABEL: - fprintf(out, "%s:;\n", node->label_stmt.label_name); - break; - case NODE_DO_WHILE: - { - loop_defer_boundary[loop_depth++] = defer_count; - fprintf(out, "do "); - codegen_node_single(ctx, node->do_while_stmt.body, out); - fprintf(out, " while ("); - codegen_expression(ctx, node->do_while_stmt.condition, out); - fprintf(out, ");\n"); - loop_depth--; - break; - } - // Loop constructs: loop, repeat, for-in - case NODE_LOOP: - { - // loop { ... } => while (1) { ... } - loop_defer_boundary[loop_depth++] = defer_count; - fprintf(out, "while (1) "); - codegen_node_single(ctx, node->loop_stmt.body, out); - loop_depth--; - break; - } - case NODE_REPEAT: - { - loop_defer_boundary[loop_depth++] = defer_count; - fprintf(out, "for (int _rpt_i = 0; _rpt_i < (%s); _rpt_i++) ", node->repeat_stmt.count); - codegen_node_single(ctx, node->repeat_stmt.body, out); - loop_depth--; - break; - } - case NODE_FOR_RANGE: - { - // Track loop entry for defer boundary - loop_defer_boundary[loop_depth++] = defer_count; - - fprintf(out, "for ("); - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, "__typeof__(("); - codegen_expression(ctx, node->for_range.start, out); - fprintf(out, ")) %s = ", node->for_range.var_name); - } - else - { - fprintf(out, "ZC_AUTO %s = ", node->for_range.var_name); - } - codegen_expression(ctx, node->for_range.start, out); - if (node->for_range.is_inclusive) + case NODE_EXPR_CAST: + fprintf(out, "(%s)(", node->cast.target_type); + codegen_expression(ctx, node->cast.expr, out); + fprintf(out, ")"); + break; + case NODE_EXPR_SIZEOF: + if (node->size_of.target_type) { - fprintf(out, "; %s <= ", node->for_range.var_name); + fprintf(out, "sizeof(%s)", node->size_of.target_type); } else { - fprintf(out, "; %s < ", node->for_range.var_name); + fprintf(out, "sizeof("); + codegen_expression(ctx, node->size_of.expr, out); + fprintf(out, ")"); } - codegen_expression(ctx, node->for_range.end, out); - fprintf(out, "; %s", node->for_range.var_name); - if (node->for_range.step) + break; + case NODE_TYPEOF: + if (node->size_of.target_type) { - fprintf(out, " += %s) ", node->for_range.step); + fprintf(out, "typeof(%s)", node->size_of.target_type); } else { - fprintf(out, "++) "); + fprintf(out, "typeof("); + codegen_expression(ctx, node->size_of.expr, out); + fprintf(out, ")"); } - codegen_node_single(ctx, node->for_range.body, out); - - loop_depth--; break; - } - case NODE_ASM: - { - int is_extended = (node->asm_stmt.num_outputs > 0 || node->asm_stmt.num_inputs > 0 || - node->asm_stmt.num_clobbers > 0); - if (node->asm_stmt.is_volatile) - { - fprintf(out, " __asm__ __volatile__("); + case NODE_REFLECTION: + { + Type *t = node->reflection.target_type; + if (node->reflection.kind == 0) + { // @type_name + char *s = codegen_type_to_string(t); + fprintf(out, "\"%s\"", s); + free(s); } else - { - fprintf(out, " __asm__("); - } - - char *code = node->asm_stmt.code; - char *transformed = xmalloc(strlen(code) * 3); // Generous buffer - char *dst = transformed; - - for (char *p = code; *p; p++) - { - if (*p == '{') + { // @fields + if (t->kind != TYPE_STRUCT || !t->name) { - // Find matching } - char *end = strchr(p + 1, '}'); - if (end) - { - // Extract variable name - int var_len = end - p - 1; - char var_name[64]; - strncpy(var_name, p + 1, var_len); - var_name[var_len] = 0; - - // Find variable index - int idx = -1; - - // Check outputs first - for (int i = 0; i < node->asm_stmt.num_outputs; i++) - { - if (strcmp(node->asm_stmt.outputs[i], var_name) == 0) - { - idx = i; - break; - } - } - - // Then check inputs - if (idx == -1) - { - for (int i = 0; i < node->asm_stmt.num_inputs; i++) - { - if (strcmp(node->asm_stmt.inputs[i], var_name) == 0) - { - idx = node->asm_stmt.num_outputs + i; - break; - } - } - } - - if (idx >= 0) - { - // Replace with %N - dst += sprintf(dst, "%%%d", idx); - } - else - { - // Variable not found - error or keep as-is? - dst += sprintf(dst, "{%s}", var_name); - } - - p = end; // Skip past } - } - else - { - *dst++ = *p; - } + fprintf(out, "((void*)0)"); + break; + } + char *sname = t->name; + // Find definition + ASTNode *def = find_struct_def(ctx, sname); + if (!def) + { + fprintf(out, "((void*)0)"); + break; } - else if (*p == '%') + + fprintf(out, + "({ static struct { char *name; char *type; unsigned long offset; } " + "_fields_%s[] = {", + sname); + ASTNode *f = def->strct.fields; + while (f) { - if (is_extended) - { - *dst++ = '%'; - *dst++ = '%'; - } - else + if (f->type == NODE_FIELD) { - *dst++ = '%'; + fprintf(out, "{ \"%s\", \"%s\", __builtin_offsetof(struct %s, %s) }, ", + f->field.name, f->field.type, sname, f->field.name); } + f = f->next; } - else - { - *dst++ = *p; - } + fprintf(out, "{ 0 } }; (void*)_fields_%s; })", sname); } - *dst = 0; - - fprintf(out, "\""); - for (char *p = transformed; *p; p++) + break; + } + case NODE_EXPR_STRUCT_INIT: + { + const char *struct_name = node->struct_init.struct_name; + if (strcmp(struct_name, "Self") == 0 && g_current_impl_type) { - if (*p == '\n') - { - fprintf(out, "\\n\"\n \""); - } - else if (*p == '"') - { - fprintf(out, "\\\""); - } - else if (*p == '\\') - { - fprintf(out, "\\\\"); - } - else - { - fputc(*p, out); - } + struct_name = g_current_impl_type; } - fprintf(out, "\\n\""); - if (node->asm_stmt.num_outputs > 0) + int is_zen_struct = 0; + StructRef *sr = ctx->parsed_structs_list; + while (sr) { - fprintf(out, "\n : "); - for (int i = 0; i < node->asm_stmt.num_outputs; i++) + if (sr->node && sr->node->type == NODE_STRUCT && + strcmp(sr->node->strct.name, struct_name) == 0) { - if (i > 0) - { - fprintf(out, ", "); - } - - // Determine constraint - char *mode = node->asm_stmt.output_modes[i]; - if (strcmp(mode, "out") == 0) - { - fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]); - } - else if (strcmp(mode, "inout") == 0) - { - fprintf(out, "\"+r\"(%s)", node->asm_stmt.outputs[i]); - } - else - { - fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]); - } + is_zen_struct = 1; + break; } + sr = sr->next; } - if (node->asm_stmt.num_inputs > 0) + if (is_zen_struct) { - fprintf(out, "\n : "); - for (int i = 0; i < node->asm_stmt.num_inputs; i++) - { - if (i > 0) - { - fprintf(out, ", "); - } - fprintf(out, "\"r\"(%s)", node->asm_stmt.inputs[i]); - } + fprintf(out, "(struct %s){", struct_name); } - else if (node->asm_stmt.num_outputs > 0) + else { - fprintf(out, "\n : "); + fprintf(out, "(%s){", struct_name); } - - if (node->asm_stmt.num_clobbers > 0) + ASTNode *f = node->struct_init.fields; + while (f) { - fprintf(out, "\n : "); - for (int i = 0; i < node->asm_stmt.num_clobbers; i++) + fprintf(out, ".%s = ", f->var_decl.name); + codegen_expression(ctx, f->var_decl.init_expr, out); + if (f->next) { - if (i > 0) - { - fprintf(out, ", "); - } - fprintf(out, "\"%s\"", node->asm_stmt.clobbers[i]); + fprintf(out, ", "); } + f = f->next; } - - fprintf(out, ");\n"); + fprintf(out, "}"); break; } - case NODE_RETURN: - { - int has_defers = (defer_count > func_defer_boundary); - int handled = 0; - - if (node->ret.value && node->ret.value->type == NODE_EXPR_VAR) - { - char *tname = infer_type(ctx, node->ret.value); - if (tname) - { - char *clean = tname; - if (strncmp(clean, "struct ", 7) == 0) - { - clean += 7; - } - - ASTNode *def = find_struct_def(ctx, clean); - if (def && def->type_info && def->type_info->traits.has_drop) - { - fprintf(out, " return ({ "); - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, "__typeof__("); - codegen_expression(ctx, node->ret.value, out); - fprintf(out, ")"); - } - else - { - fprintf(out, "__auto_type"); - } - fprintf(out, " _z_ret_mv = "); - codegen_expression(ctx, node->ret.value, out); - fprintf(out, "; memset(&"); - codegen_expression(ctx, node->ret.value, out); - fprintf(out, ", 0, sizeof(_z_ret_mv)); "); - // Run defers before returning - for (int i = defer_count - 1; i >= func_defer_boundary; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } - fprintf(out, "_z_ret_mv; });\n"); - handled = 1; - } - free(tname); - } - } - - if (!handled) + case NODE_EXPR_ARRAY_LITERAL: + fprintf(out, "{"); + ASTNode *elem = node->array_literal.elements; + int first = 1; + while (elem) { - if (has_defers && node->ret.value) - { - // Save return value, run defers, then return - fprintf(out, " { "); - emit_auto_type(ctx, node->ret.value, node->token, out); - fprintf(out, " _z_ret = "); - codegen_expression(ctx, node->ret.value, out); - fprintf(out, "; "); - for (int i = defer_count - 1; i >= func_defer_boundary; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } - fprintf(out, "return _z_ret; }\n"); - } - else if (has_defers) - { - // No return value, just run defers - for (int i = defer_count - 1; i >= func_defer_boundary; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } - fprintf(out, " return;\n"); - } - else + if (!first) { - // No defers, simple return - fprintf(out, " return "); - codegen_expression(ctx, node->ret.value, out); - fprintf(out, ";\n"); + fprintf(out, ", "); } + codegen_expression(ctx, elem, out); + elem = elem->next; + first = 0; } + fprintf(out, "}"); break; - } - case NODE_EXPR_MEMBER: - { - codegen_expression(ctx, node->member.target, out); - char *lt = infer_type(ctx, node->member.target); - if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) - { - fprintf(out, "->%s", node->member.field); - } - else - { - fprintf(out, ".%s", node->member.field); - } - if (lt) - { - free(lt); - } - break; - } - case NODE_REPL_PRINT: - { - fprintf(out, "{ "); - emit_auto_type(ctx, node->repl_print.expr, node->token, out); - fprintf(out, " _zval = ("); - codegen_expression(ctx, node->repl_print.expr, out); - fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, " - "\"\\n\"); }\n"); + case NODE_TERNARY: + fprintf(out, "(("); + codegen_expression(ctx, node->ternary.cond, out); + fprintf(out, ") ? ("); + codegen_expression(ctx, node->ternary.true_expr, out); + fprintf(out, ") : ("); + codegen_expression(ctx, node->ternary.false_expr, out); + fprintf(out, "))"); break; - } case NODE_AWAIT: { char *ret_type = "void*"; @@ -2626,8 +1055,6 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) ret_type = node->resolved_type; } - // Fallback: If type is still Async/void* (likely from Future type, not - // Result type), try to infer if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) { char *inf = infer_type(ctx, node); @@ -2638,7 +1065,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) free(ret_type); } ret_type = inf; - free_ret = 0; // infer_type ownership ambiguous, avoid double free + free_ret = 0; } } @@ -2659,7 +1086,6 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) { needs_long_cast = 1; } - if (strncmp(ret_type, "struct", 6) == 0) { returns_struct = 1; @@ -2671,13 +1097,12 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); "); if (strcmp(ret_type, "void") == 0) { - fprintf(out, "})"); // result unused + fprintf(out, "})"); } else { if (returns_struct) { - // Dereference and free fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type, ret_type); } else @@ -2696,101 +1121,9 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) { free(ret_type); } - fprintf(out, ";\n"); // Statement terminator - break; - } - case NODE_EXPR_LITERAL: - // String literal statement should auto-print - if (node->literal.type_kind == 2 || node->literal.type_kind == TOK_STRING) - { - fprintf(out, " printf(\"%%s\\n\", "); - codegen_expression(ctx, node, out); - fprintf(out, ");\n"); - } - else - { - // Non-string literals as statements - just evaluate - codegen_expression(ctx, node, out); - fprintf(out, ";\n"); - } - break; - case NODE_CUDA_LAUNCH: - { - // Emit CUDA kernel launch: kernel<<>>(args); - ASTNode *call = node->cuda_launch.call; - - // Get kernel name from callee - if (call->call.callee->type == NODE_EXPR_VAR) - { - fprintf(out, " %s<<<", call->call.callee->var_ref.name); - } - else - { - fprintf(out, " "); - codegen_expression(ctx, call->call.callee, out); - fprintf(out, "<<<"); - } - - // Grid dimension - codegen_expression(ctx, node->cuda_launch.grid, out); - fprintf(out, ", "); - - // Block dimension - codegen_expression(ctx, node->cuda_launch.block, out); - - // Optional shared memory size - if (node->cuda_launch.shared_mem || node->cuda_launch.stream) - { - fprintf(out, ", "); - if (node->cuda_launch.shared_mem) - { - codegen_expression(ctx, node->cuda_launch.shared_mem, out); - } - else - { - fprintf(out, "0"); - } - } - - // Optional CUDA stream - if (node->cuda_launch.stream) - { - fprintf(out, ", "); - codegen_expression(ctx, node->cuda_launch.stream, out); - } - - fprintf(out, ">>>("); - - // Arguments - ASTNode *arg = call->call.args; - int first = 1; - while (arg) - { - if (!first) - { - fprintf(out, ", "); - } - codegen_expression(ctx, arg, out); - first = 0; - arg = arg->next; - } - - fprintf(out, ");\n"); break; } default: - codegen_expression(ctx, node, out); - fprintf(out, ";\n"); break; } } - -// Walks AST nodes and generates code. -void codegen_walker(ParserContext *ctx, ASTNode *node, FILE *out) -{ - while (node) - { - codegen_node_single(ctx, node, out); - node = node->next; - } -} diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index caf7c0c..0a1cbaf 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -12,6 +12,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_walker(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out); +void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result); // Utility functions (codegen_utils.c). char *infer_type(ParserContext *ctx, ASTNode *node); @@ -24,6 +25,7 @@ const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); char *codegen_type_to_string(Type *t); void emit_func_signature(FILE *out, ASTNode *func, const char *name_override); +char *strip_template_suffix(const char *name); // Declaration emission (codegen_decl.c). void emit_preamble(ParserContext *ctx, FILE *out); @@ -47,6 +49,7 @@ extern int tmp_counter; extern int defer_count; extern ASTNode *defer_stack[]; extern ASTNode *g_current_lambda; +extern char *g_current_func_ret_type; // Defer boundary tracking for proper defer execution on break/continue/return #define MAX_DEFER 1024 diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index e7bd3f1..4e6db6a 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -7,42 +7,44 @@ #include #include -// Emit C preamble with standard includes and type definitions. +static void emit_freestanding_preamble(FILE *out) +{ + fputs("#include \n#include \n#include " + "\n#include \n", + out); + fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); + fputs("typedef size_t usize;\ntypedef char* string;\n", out); + fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " + "int16_t\n#define U16 uint16_t\n", + out); + fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " + "int64_t\n#define U64 " + "uint64_t\n", + out); + fputs("#define F32 float\n#define F64 double\n", out); + fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " + "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " + "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " + "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " + "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " + "char*: \"%s\", void*: \"%p\")\n", + out); + fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); + + fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out); + fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return " + "NULL; }\n", + out); + fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out); + fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n", out); + fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n", out); +} + void emit_preamble(ParserContext *ctx, FILE *out) { if (g_config.is_freestanding) { - // Freestanding preamble. - // It actually needs more work, but yk. - fputs("#include \n#include \n#include " - "\n#include \n", - out); - fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); - fputs("typedef size_t usize;\ntypedef char* string;\n", out); - fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " - "int16_t\n#define U16 uint16_t\n", - out); - fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " - "int64_t\n#define U64 " - "uint64_t\n", - out); - fputs("#define F32 float\n#define F64 double\n", out); - fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " - "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " - "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " - "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " - "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " - "char*: \"%s\", void*: \"%p\")\n", - out); - fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); - - fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out); - fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return " - "NULL; }\n", - out); - fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out); - fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n", out); - fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n", out); + emit_freestanding_preamble(out); } else { @@ -705,13 +707,9 @@ void emit_protos(ASTNode *node, FILE *out) } else { - char *lt = strchr(sname, '<'); - if (lt) + char *buf = strip_template_suffix(sname); + if (buf) { - int len = lt - sname; - char *buf = xmalloc(len + 1); - strncpy(buf, sname, len); - buf[len] = 0; def = find_struct_def_codegen(g_parser_ctx, buf); if (def && def->strct.is_template) { @@ -782,13 +780,9 @@ void emit_protos(ASTNode *node, FILE *out) } else { - char *lt = strchr(sname, '<'); - if (lt) + char *buf = strip_template_suffix(sname); + if (buf) { - int len = lt - sname; - char *buf = xmalloc(len + 1); - strncpy(buf, sname, len); - buf[len] = 0; def = find_struct_def_codegen(g_parser_ctx, buf); if (def && def->strct.is_template) { @@ -896,13 +890,9 @@ void emit_impl_vtables(ParserContext *ctx, FILE *out) } else { - char *lt = strchr(strct, '<'); - if (lt) + char *buf = strip_template_suffix(strct); + if (buf) { - int len = lt - strct; - char *buf = xmalloc(len + 1); - strncpy(buf, strct, len); - buf[len] = 0; def = find_struct_def_codegen(ctx, buf); if (def && def->strct.is_template) { diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index 97abfc7..7363e61 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -577,13 +577,9 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) } else { - char *lt = strchr(sname, '<'); - if (lt) + char *buf = strip_template_suffix(sname); + if (buf) { - int len = lt - sname; - char *buf = xmalloc(len + 1); - strncpy(buf, sname, len); - buf[len] = 0; def = find_struct_def_codegen(ctx, buf); if (def && def->strct.is_template) { @@ -623,13 +619,9 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) } else { - char *lt = strchr(sname, '<'); - if (lt) + char *buf = strip_template_suffix(sname); + if (buf) { - int len = lt - sname; - char *buf = xmalloc(len + 1); - strncpy(buf, sname, len); - buf[len] = 0; def = find_struct_def_codegen(ctx, buf); if (def && def->strct.is_template) { diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c new file mode 100644 index 0000000..983130c --- /dev/null +++ b/src/codegen/codegen_stmt.c @@ -0,0 +1,1671 @@ + +#include "codegen.h" +#include "zprep.h" +#include "../constants.h" +#include +#include +#include +#include + +#include "../plugins/plugin_manager.h" +#include "ast.h" +#include "zprep_plugin.h" + +char *g_current_func_ret_type = NULL; + +// Helper: emit a single pattern condition (either a value, or a range) +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, "..="); + char *range_excl = strstr(pat, ".."); + + if (range_incl) + { + // Inclusive range: start..=end -> _m_id >= start && _m_id <= end + int start_len = (int)(range_incl - pat); + char *start = xmalloc(start_len + 1); + strncpy(start, pat, start_len); + start[start_len] = 0; + char *end = xstrdup(range_incl + 3); + 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 (range_excl) + { + // Exclusive range: start..end -> _m_id >= start && _m_id < end + int start_len = (int)(range_excl - pat); + char *start = xmalloc(start_len + 1); + strncpy(start, pat, start_len); + start[start_len] = 0; + char *end = xstrdup(range_excl + 2); + 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 - 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 + if (is_ptr) + { + fprintf(out, "*_m_%d == %s", id, pat); + } + else + { + fprintf(out, "_m_%d == %s", id, pat); + } + } + else + { + // Numeric or simple pattern + 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, int is_ptr, + FILE *out) +{ + // Check if pattern contains '|' for OR patterns + if (strchr(pattern, '|')) + { + // Split by '|' and emit OR conditions + char *pattern_copy = xstrdup(pattern); + char *saveptr; + char *part = strtok_r(pattern_copy, "|", &saveptr); + int first = 1; + fprintf(out, "("); + while (part) + { + if (!first) + { + fprintf(out, " || "); + } + + // Check if part is an enum variant + EnumVariantReg *reg = find_enum_variant(ctx, part); + if (reg) + { + 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, is_ptr, out); + } + first = 0; + part = strtok_r(NULL, "|", &saveptr); + } + fprintf(out, ")"); + free(pattern_copy); + } + else + { + // Single pattern (may be a range) + EnumVariantReg *reg = find_enum_variant(ctx, pattern); + if (reg) + { + 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, is_ptr, out); + } + } +} + +void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result) +{ + int id = tmp_counter++; + int is_self = (node->match_stmt.expr->type == NODE_EXPR_VAR && + strcmp(node->match_stmt.expr->var_ref.name, "self") == 0); + + char *ret_type = infer_type(ctx, node); + int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0); + + fprintf(out, "({ "); + + // 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) + { + if (ref_check->match_case.is_ref) + { + has_ref_binding = 1; + break; + } + ref_check = ref_check->next; + } + + 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, "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, "; "); + } + + if (is_expr) + { + fprintf(out, "%s _r_%d; ", ret_type, id); + } + + char *expr_type = infer_type(ctx, node->match_stmt.expr); + int is_option = (expr_type && strncmp(expr_type, "Option_", 7) == 0); + int is_result = (expr_type && strncmp(expr_type, "Result_", 7) == 0); + + char *enum_name = NULL; + ASTNode *chk = node->match_stmt.cases; + int has_wildcard = 0; + while (chk) + { + if (strcmp(chk->match_case.pattern, "_") == 0) + { + has_wildcard = 1; + } + else if (!enum_name) + { + EnumVariantReg *reg = find_enum_variant(ctx, chk->match_case.pattern); + if (reg) + { + enum_name = reg->enum_name; + } + } + chk = chk->next; + } + + if (enum_name && !has_wildcard) + { + // Iterate through all registered variants for this enum + EnumVariantReg *v = ctx->enum_variants; + while (v) + { + if (strcmp(v->enum_name, enum_name) == 0) + { + int covered = 0; + ASTNode *c2 = node->match_stmt.cases; + while (c2) + { + if (strcmp(c2->match_case.pattern, v->variant_name) == 0) + { + covered = 1; + break; + } + c2 = c2->next; + } + if (!covered) + { + zwarn_at(node->token, "Non-exhaustive match: Missing variant '%s'", + v->variant_name); + } + } + v = v->next; + } + } + + ASTNode *c = node->match_stmt.cases; + int first = 1; + while (c) + { + if (!first) + { + fprintf(out, " else "); + } + fprintf(out, "if ("); + if (strcmp(c->match_case.pattern, "_") == 0) + { + fprintf(out, "1"); + } + else if (is_option) + { + if (strcmp(c->match_case.pattern, "Some") == 0) + { + fprintf(out, "_m_%d->is_some", id); + } + else if (strcmp(c->match_case.pattern, "None") == 0) + { + fprintf(out, "!_m_%d->is_some", id); + } + else + { + fprintf(out, "1"); + } + } + else if (is_result) + { + if (strcmp(c->match_case.pattern, "Ok") == 0) + { + fprintf(out, "_m_%d->is_ok", id); + } + else if (strcmp(c->match_case.pattern, "Err") == 0) + { + fprintf(out, "!_m_%d->is_ok", id); + } + else + { + fprintf(out, "1"); + } + } + else + { + // Use helper for OR patterns, range patterns, and simple patterns + emit_pattern_condition(ctx, c->match_case.pattern, id, has_ref_binding, out); + } + fprintf(out, ") { "); + if (c->match_case.binding_name) + { + if (is_option) + { + if (strstr(g_config.cc, "tcc")) + { + if (c->match_case.is_ref) + { + 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, + c->match_case.binding_name, id); + } + } + else + { + if (c->match_case.is_ref) + { + // _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); + } + } + } + else if (is_result) // FIX: Changed 'if' to 'else if' to match original logic structure + // if needed, but original code had implicit fallthrough checks? No, + // checks match pattern. + { + if (strcmp(c->match_case.pattern, "Ok") == 0) + { + if (strstr(g_config.cc, "tcc")) + { + if (c->match_case.is_ref) + { + 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, + c->match_case.binding_name, id); + } + } + else + { + if (c->match_case.is_ref) + { + // _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); + } + } + } + else + { + if (strstr(g_config.cc, "tcc")) + { + if (c->match_case.is_ref) + { + 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, + c->match_case.binding_name, id); + } + } + else + { + if (c->match_case.is_ref) + { + // _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); + } + } + } + } + else + { + char *f = strrchr(c->match_case.pattern, '_'); + if (f) + { + f++; + } + else + { + f = c->match_case.pattern; + } + // Generic struct destructuring (for example, MyStruct_Variant) + // Assuming data union or accessible field. + if (c->match_case.is_ref) + { + // _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); + } + } + } + + // Check if body is a string literal (should auto-print). + ASTNode *body = c->match_case.body; + int is_string_literal = (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == 2); + + if (is_expr) + { + fprintf(out, "_r_%d = ", id); + if (is_string_literal) + { + codegen_node_single(ctx, body, out); + } + else + { + if (body->type == NODE_BLOCK) + { + int saved = defer_count; + fprintf(out, "({ "); + ASTNode *stmt = body->block.statements; + while (stmt) + { + codegen_node_single(ctx, stmt, out); + stmt = stmt->next; + } + for (int i = defer_count - 1; i >= saved; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + defer_count = saved; + fprintf(out, " })"); + } + else + { + codegen_node_single(ctx, body, out); + } + } + fprintf(out, ";"); + } + else + { + if (is_string_literal) + { + fprintf(out, "({ printf(\"%%s\", "); + codegen_expression(ctx, body, out); + fprintf(out, "); printf(\"\\n\"); 0; })"); + } + else + { + codegen_node_single(ctx, body, out); + } + } + + fprintf(out, " }"); + first = 0; + c = c->next; + } + + if (is_expr) + { + fprintf(out, " _r_%d; })", id); + } + else + { + fprintf(out, " })"); + } +} +void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) +{ + if (!node) + { + return; + } + switch (node->type) + { + case NODE_MATCH: + codegen_match_internal(ctx, node, out, 0); // 0 = statement context + fprintf(out, ";\n"); + break; + case NODE_FUNCTION: + if (!node->func.body) + { + break; + } + + if (node->func.is_async) + { + fprintf(out, "struct %s_Args {\n", node->func.name); + char *args_copy = xstrdup(node->func.args); + char *token = strtok(args_copy, ","); + int arg_count = 0; + char **arg_names = xmalloc(32 * sizeof(char *)); + + while (token) + { + while (*token == ' ') + { + token++; // trim leading + } + char *last_space = strrchr(token, ' '); + if (last_space) + { + *last_space = 0; + char *type = token; + char *name = last_space + 1; + fprintf(out, "%s %s;\n", type, name); + + arg_names[arg_count++] = xstrdup(name); + } + token = strtok(NULL, ","); + } + free(args_copy); + fprintf(out, "};\n"); + + fprintf(out, "void* _runner_%s(void* _args)\n", node->func.name); + fprintf(out, "{\n"); + fprintf(out, " struct %s_Args* args = (struct %s_Args*)_args;\n", node->func.name, + node->func.name); + + // Determine mechanism: struct/large-type? -> malloc; primitive -> cast + int returns_struct = 0; + char *rt = node->func.ret_type; + if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) + { + if (strstr(rt, "*") == NULL && strcmp(rt, "string") != 0 && + strcmp(rt, "int") != 0 && strcmp(rt, "bool") != 0 && strcmp(rt, "char") != 0 && + strcmp(rt, "float") != 0 && strcmp(rt, "double") != 0 && + strcmp(rt, "long") != 0 && strcmp(rt, "usize") != 0 && + strcmp(rt, "isize") != 0 && strncmp(rt, "uint", 4) != 0 && + strncmp(rt, "int", 3) != 0) + { + returns_struct = 1; + } + } + + // Call Impl + if (returns_struct) + { + fprintf(out, " %s *res_ptr = malloc(sizeof(%s));\n", rt, rt); + fprintf(out, " *res_ptr = "); + } + else if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) + { + fprintf(out, " %s res = ", rt); + } + else + { + fprintf(out, " "); + } + + fprintf(out, "_impl_%s(", node->func.name); + for (int i = 0; i < arg_count; i++) + { + fprintf(out, "%sargs->%s", i > 0 ? ", " : "", arg_names[i]); + } + fprintf(out, ");\n"); + fprintf(out, " free(args);\n"); + + if (returns_struct) + { + fprintf(out, " return (void*)res_ptr;\n"); + } + else if (strcmp(rt, "void") != 0) + { + fprintf(out, " return (void*)(long)res;\n"); + } + else + { + fprintf(out, " return NULL;\n"); + } + fprintf(out, "}\n"); + + fprintf(out, "%s _impl_%s(%s)\n", node->func.ret_type, node->func.name, + node->func.args); + fprintf(out, "{\n"); + defer_count = 0; + codegen_walker(ctx, node->func.body, out); + for (int i = defer_count - 1; i >= 0; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + fprintf(out, "}\n"); + + // 4. Define Public Wrapper (Spawns Thread) + fprintf(out, "Async %s(%s)\n", node->func.name, node->func.args); + fprintf(out, "{\n"); + fprintf(out, " struct %s_Args* args = malloc(sizeof(struct %s_Args));\n", + node->func.name, node->func.name); + for (int i = 0; i < arg_count; i++) + { + fprintf(out, " args->%s = %s;\n", arg_names[i], arg_names[i]); + } + + fprintf(out, " pthread_t th;\n"); + fprintf(out, " pthread_create(&th, NULL, _runner_%s, args);\n", node->func.name); + fprintf(out, " return (Async){.thread=th, .result=NULL};\n"); + fprintf(out, "}\n"); + + break; + } + + defer_count = 0; + fprintf(out, "\n"); + + // Emit GCC attributes before function + { + int has_attrs = node->func.constructor || node->func.destructor || + node->func.noinline || node->func.unused || node->func.weak || + node->func.cold || node->func.hot || node->func.noreturn || + node->func.pure || node->func.section; + if (has_attrs) + { + fprintf(out, "__attribute__(("); + int first = 1; +#define EMIT_ATTR(cond, name) \ + if (cond) \ + { \ + if (!first) \ + fprintf(out, ", "); \ + fprintf(out, name); \ + first = 0; \ + } + EMIT_ATTR(node->func.constructor, "constructor"); + EMIT_ATTR(node->func.destructor, "destructor"); + EMIT_ATTR(node->func.noinline, "noinline"); + EMIT_ATTR(node->func.unused, "unused"); + EMIT_ATTR(node->func.weak, "weak"); + EMIT_ATTR(node->func.cold, "cold"); + EMIT_ATTR(node->func.hot, "hot"); + EMIT_ATTR(node->func.noreturn, "noreturn"); + EMIT_ATTR(node->func.pure, "pure"); + if (node->func.section) + { + if (!first) + { + fprintf(out, ", "); + } + fprintf(out, "section(\"%s\")", node->func.section); + } +#undef EMIT_ATTR + fprintf(out, ")) "); + } + } + + if (node->func.is_inline) + { + fprintf(out, "inline "); + } + emit_func_signature(out, node, NULL); + fprintf(out, "\n"); + fprintf(out, "{\n"); + char *prev_ret = g_current_func_ret_type; + g_current_func_ret_type = node->func.ret_type; + codegen_walker(ctx, node->func.body, out); + for (int i = defer_count - 1; i >= 0; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + g_current_func_ret_type = prev_ret; + 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) + { + defer_stack[defer_count++] = node->defer_stmt.stmt; + } + break; + case NODE_IMPL: + g_current_impl_type = node->impl.struct_name; + codegen_walker(ctx, node->impl.methods, out); + g_current_impl_type = NULL; + break; + case NODE_IMPL_TRAIT: + g_current_impl_type = node->impl_trait.target_type; + codegen_walker(ctx, node->impl_trait.methods, out); + + if (strcmp(node->impl_trait.trait_name, "Drop") == 0) + { + char *tname = node->impl_trait.target_type; + fprintf(out, "\n// RAII Glue\n"); + fprintf(out, "void %s__Drop_glue(%s *self) {\n", tname, tname); + fprintf(out, " %s__Drop_drop(self);\n", tname); + fprintf(out, "}\n"); + } + g_current_impl_type = NULL; + break; + case NODE_DESTRUCT_VAR: + { + int id = tmp_counter++; + fprintf(out, " "); + emit_auto_type(ctx, node->destruct.init_expr, node->token, out); + fprintf(out, " _tmp_%d = ", id); + codegen_expression(ctx, node->destruct.init_expr, out); + fprintf(out, ";\n"); + + if (node->destruct.is_guard) + { + // var Some(val) = opt else ... + char *variant = node->destruct.guard_variant; + char *check = "val"; // field to access + + if (strcmp(variant, "Some") == 0) + { + fprintf(out, " if (!_tmp_%d.is_some) {\n", id); + } + else if (strcmp(variant, "Ok") == 0) + { + fprintf(out, " if (!_tmp_%d.is_ok) {\n", id); + } + else if (strcmp(variant, "Err") == 0) + { + fprintf(out, " if (_tmp_%d.is_ok) {\n", id); // Err if NOT ok + check = "err"; + } + else + { + // Generic guard? Assume .is_variant present? + fprintf(out, " if (!_tmp_%d.is_%s) {\n", id, variant); + } + + // Else block + codegen_walker(ctx, node->destruct.else_block->block.statements, out); + fprintf(out, " }\n"); + + // Bind value + if (strstr(g_config.cc, "tcc")) + { + fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, check, + node->destruct.names[0], id, check); + } + else + { + fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[0], id, + check); + } + } + else + { + for (int i = 0; i < node->destruct.count; i++) + { + if (node->destruct.is_struct_destruct) + { + char *field = node->destruct.field_names ? node->destruct.field_names[i] + : node->destruct.names[i]; + if (strstr(g_config.cc, "tcc")) + { + fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, field, + node->destruct.names[i], id, field); + } + else + { + fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[i], + id, field); + } + } + else + { + if (strstr(g_config.cc, "tcc")) + { + fprintf(out, " __typeof__(_tmp_%d.v%d) %s = _tmp_%d.v%d;\n", id, i, + node->destruct.names[i], id, i); + } + else + { + fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n", node->destruct.names[i], + id, i); + } + } + } + } + break; + } + case NODE_BLOCK: + { + int saved = defer_count; + fprintf(out, " {\n"); + codegen_walker(ctx, node->block.statements, out); + for (int i = defer_count - 1; i >= saved; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + defer_count = saved; + fprintf(out, " }\n"); + break; + } + case NODE_VAR_DECL: + fprintf(out, " "); + if (node->var_decl.is_static) + { + fprintf(out, "static "); + } + if (node->var_decl.is_autofree) + { + fprintf(out, "__attribute__((cleanup(_z_autofree_impl))) "); + } + { + char *tname = NULL; + 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); + } + else if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0) + { + tname = node->var_decl.type_str; + } + + if (tname && strcmp(tname, "void*") != 0 && strcmp(tname, "unknown") != 0) + { + char *clean_type = tname; + if (strncmp(clean_type, "struct ", 7) == 0) + { + clean_type += 7; + } + + ASTNode *def = find_struct_def(ctx, clean_type); + if (def && def->type_info && def->type_info->traits.has_drop) + { + fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type); + } + + // Emit Variable with Type + emit_var_decl_type(ctx, out, tname, node->var_decl.name); + add_symbol(ctx, node->var_decl.name, tname, node->type_info); + + if (node->var_decl.init_expr) + { + fprintf(out, " = "); + codegen_expression(ctx, node->var_decl.init_expr, out); + } + fprintf(out, ";\n"); + + if (node->type_info) + { + free(tname); // Free if allocated by codegen_type_to_string + } + } + else + { + // Inference Fallback + char *inferred = NULL; + if (node->var_decl.init_expr) + { + inferred = infer_type(ctx, node->var_decl.init_expr); + } + + if (inferred && strcmp(inferred, "__auto_type") != 0) + { + char *clean_type = inferred; + if (strncmp(clean_type, "struct ", 7) == 0) + { + clean_type += 7; + } + + ASTNode *def = find_struct_def(ctx, clean_type); + if (def && def->type_info && def->type_info->traits.has_drop) + { + fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type); + } + + emit_var_decl_type(ctx, out, inferred, node->var_decl.name); + add_symbol(ctx, node->var_decl.name, inferred, NULL); + fprintf(out, " = "); + codegen_expression(ctx, node->var_decl.init_expr, out); + fprintf(out, ";\n"); + } + else + { + emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); + fprintf(out, " %s", node->var_decl.name); + + if (inferred) + { + add_symbol(ctx, node->var_decl.name, inferred, NULL); + } + + fprintf(out, " = "); + codegen_expression(ctx, node->var_decl.init_expr, out); + fprintf(out, ";\n"); + } + } + } + break; + case NODE_CONST: + fprintf(out, " const "); + if (node->var_decl.type_str) + { + fprintf(out, "%s %s", node->var_decl.type_str, node->var_decl.name); + } + else + { + emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); + fprintf(out, " %s", node->var_decl.name); + } + fprintf(out, " = "); + codegen_expression(ctx, node->var_decl.init_expr, out); + fprintf(out, ";\n"); + break; + case NODE_FIELD: + if (node->field.bit_width > 0) + { + fprintf(out, " %s %s : %d;\n", node->field.type, node->field.name, + node->field.bit_width); + } + else + { + fprintf(out, " "); + emit_var_decl_type(ctx, out, node->field.type, node->field.name); + fprintf(out, ";\n"); + } + break; + case NODE_IF: + fprintf(out, "if ("); + codegen_expression(ctx, node->if_stmt.condition, out); + fprintf(out, ") "); + codegen_node_single(ctx, node->if_stmt.then_body, out); + if (node->if_stmt.else_body) + { + fprintf(out, " else "); + codegen_node_single(ctx, node->if_stmt.else_body, out); + } + break; + case NODE_UNLESS: + fprintf(out, "if (!("); + codegen_expression(ctx, node->unless_stmt.condition, out); + fprintf(out, ")) "); + codegen_node_single(ctx, node->unless_stmt.body, out); + break; + case NODE_GUARD: + fprintf(out, "if (!("); + codegen_expression(ctx, node->guard_stmt.condition, out); + fprintf(out, ")) "); + codegen_node_single(ctx, node->guard_stmt.body, out); + break; + case NODE_WHILE: + { + loop_defer_boundary[loop_depth++] = defer_count; + fprintf(out, "while ("); + codegen_expression(ctx, node->while_stmt.condition, out); + fprintf(out, ") "); + codegen_node_single(ctx, node->while_stmt.body, out); + loop_depth--; + break; + } + case NODE_FOR: + { + loop_defer_boundary[loop_depth++] = defer_count; + fprintf(out, "for ("); + if (node->for_stmt.init) + { + if (node->for_stmt.init->type == NODE_VAR_DECL) + { + ASTNode *v = node->for_stmt.init; + if (v->var_decl.type_str && strcmp(v->var_decl.type_str, "__auto_type") != 0) + { + fprintf(out, "%s %s = (%s)(", v->var_decl.type_str, v->var_decl.name, + v->var_decl.type_str); + codegen_expression(ctx, v->var_decl.init_expr, out); + fprintf(out, ")"); + } + else + { + emit_auto_type(ctx, v->var_decl.init_expr, v->token, out); + fprintf(out, " %s = ", v->var_decl.name); + codegen_expression(ctx, v->var_decl.init_expr, out); + } + } + else + { + codegen_expression(ctx, node->for_stmt.init, out); + } + } + fprintf(out, "; "); + if (node->for_stmt.condition) + { + codegen_expression(ctx, node->for_stmt.condition, out); + } + fprintf(out, "; "); + if (node->for_stmt.step) + { + codegen_expression(ctx, node->for_stmt.step, out); + } + fprintf(out, ") "); + codegen_node_single(ctx, node->for_stmt.body, out); + loop_depth--; + break; + } + case NODE_BREAK: + // Run defers from current scope down to loop boundary before breaking + if (loop_depth > 0) + { + int boundary = loop_defer_boundary[loop_depth - 1]; + for (int i = defer_count - 1; i >= boundary; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + } + if (node->break_stmt.target_label) + { + fprintf(out, "goto __break_%s;\n", node->break_stmt.target_label); + } + else + { + fprintf(out, "break;\n"); + } + break; + case NODE_CONTINUE: + // Run defers from current scope down to loop boundary before continuing + if (loop_depth > 0) + { + int boundary = loop_defer_boundary[loop_depth - 1]; + for (int i = defer_count - 1; i >= boundary; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + } + if (node->continue_stmt.target_label) + { + fprintf(out, "goto __continue_%s;\n", node->continue_stmt.target_label); + } + else + { + fprintf(out, "continue;\n"); + } + break; + case NODE_GOTO: + if (node->goto_stmt.goto_expr) + { + // Computed goto: goto *expr; + fprintf(out, "goto *("); + codegen_expression(ctx, node->goto_stmt.goto_expr, out); + fprintf(out, ");\n"); + } + else + { + fprintf(out, "goto %s;\n", node->goto_stmt.label_name); + } + break; + case NODE_LABEL: + fprintf(out, "%s:;\n", node->label_stmt.label_name); + break; + case NODE_DO_WHILE: + { + loop_defer_boundary[loop_depth++] = defer_count; + fprintf(out, "do "); + codegen_node_single(ctx, node->do_while_stmt.body, out); + fprintf(out, " while ("); + codegen_expression(ctx, node->do_while_stmt.condition, out); + fprintf(out, ");\n"); + loop_depth--; + break; + } + // Loop constructs: loop, repeat, for-in + case NODE_LOOP: + { + // loop { ... } => while (1) { ... } + loop_defer_boundary[loop_depth++] = defer_count; + fprintf(out, "while (1) "); + codegen_node_single(ctx, node->loop_stmt.body, out); + loop_depth--; + break; + } + case NODE_REPEAT: + { + loop_defer_boundary[loop_depth++] = defer_count; + fprintf(out, "for (int _rpt_i = 0; _rpt_i < (%s); _rpt_i++) ", node->repeat_stmt.count); + codegen_node_single(ctx, node->repeat_stmt.body, out); + loop_depth--; + break; + } + case NODE_FOR_RANGE: + { + // Track loop entry for defer boundary + loop_defer_boundary[loop_depth++] = defer_count; + + fprintf(out, "for ("); + if (strstr(g_config.cc, "tcc")) + { + fprintf(out, "__typeof__(("); + codegen_expression(ctx, node->for_range.start, out); + fprintf(out, ")) %s = ", node->for_range.var_name); + } + else + { + fprintf(out, "ZC_AUTO %s = ", node->for_range.var_name); + } + codegen_expression(ctx, node->for_range.start, out); + if (node->for_range.is_inclusive) + { + fprintf(out, "; %s <= ", node->for_range.var_name); + } + else + { + fprintf(out, "; %s < ", node->for_range.var_name); + } + codegen_expression(ctx, node->for_range.end, out); + fprintf(out, "; %s", node->for_range.var_name); + if (node->for_range.step) + { + fprintf(out, " += %s) ", node->for_range.step); + } + else + { + fprintf(out, "++) "); + } + codegen_node_single(ctx, node->for_range.body, out); + + loop_depth--; + break; + } + case NODE_ASM: + { + int is_extended = (node->asm_stmt.num_outputs > 0 || node->asm_stmt.num_inputs > 0 || + node->asm_stmt.num_clobbers > 0); + + if (node->asm_stmt.is_volatile) + { + fprintf(out, " __asm__ __volatile__("); + } + else + { + fprintf(out, " __asm__("); + } + + char *code = node->asm_stmt.code; + char *transformed = xmalloc(strlen(code) * 3); // Generous buffer + char *dst = transformed; + + for (char *p = code; *p; p++) + { + if (*p == '{') + { + // Find matching } + char *end = strchr(p + 1, '}'); + if (end) + { + // Extract variable name + int var_len = end - p - 1; + char var_name[64]; + strncpy(var_name, p + 1, var_len); + var_name[var_len] = 0; + + // Find variable index + int idx = -1; + + // Check outputs first + for (int i = 0; i < node->asm_stmt.num_outputs; i++) + { + if (strcmp(node->asm_stmt.outputs[i], var_name) == 0) + { + idx = i; + break; + } + } + + // Then check inputs + if (idx == -1) + { + for (int i = 0; i < node->asm_stmt.num_inputs; i++) + { + if (strcmp(node->asm_stmt.inputs[i], var_name) == 0) + { + idx = node->asm_stmt.num_outputs + i; + break; + } + } + } + + if (idx >= 0) + { + // Replace with %N + dst += sprintf(dst, "%%%d", idx); + } + else + { + // Variable not found - error or keep as-is? + dst += sprintf(dst, "{%s}", var_name); + } + + p = end; // Skip past } + } + else + { + *dst++ = *p; + } + } + else if (*p == '%') + { + if (is_extended) + { + *dst++ = '%'; + *dst++ = '%'; + } + else + { + *dst++ = '%'; + } + } + else + { + *dst++ = *p; + } + } + *dst = 0; + + fprintf(out, "\""); + for (char *p = transformed; *p; p++) + { + if (*p == '\n') + { + fprintf(out, "\\n\"\n \""); + } + else if (*p == '"') + { + fprintf(out, "\\\""); + } + else if (*p == '\\') + { + fprintf(out, "\\\\"); + } + else + { + fputc(*p, out); + } + } + fprintf(out, "\\n\""); + + if (node->asm_stmt.num_outputs > 0) + { + fprintf(out, "\n : "); + for (int i = 0; i < node->asm_stmt.num_outputs; i++) + { + if (i > 0) + { + fprintf(out, ", "); + } + + // Determine constraint + char *mode = node->asm_stmt.output_modes[i]; + if (strcmp(mode, "out") == 0) + { + fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]); + } + else if (strcmp(mode, "inout") == 0) + { + fprintf(out, "\"+r\"(%s)", node->asm_stmt.outputs[i]); + } + else + { + fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]); + } + } + } + + if (node->asm_stmt.num_inputs > 0) + { + fprintf(out, "\n : "); + for (int i = 0; i < node->asm_stmt.num_inputs; i++) + { + if (i > 0) + { + fprintf(out, ", "); + } + fprintf(out, "\"r\"(%s)", node->asm_stmt.inputs[i]); + } + } + else if (node->asm_stmt.num_outputs > 0) + { + fprintf(out, "\n : "); + } + + if (node->asm_stmt.num_clobbers > 0) + { + fprintf(out, "\n : "); + for (int i = 0; i < node->asm_stmt.num_clobbers; i++) + { + if (i > 0) + { + fprintf(out, ", "); + } + fprintf(out, "\"%s\"", node->asm_stmt.clobbers[i]); + } + } + + fprintf(out, ");\n"); + break; + } + case NODE_RETURN: + { + int has_defers = (defer_count > func_defer_boundary); + int handled = 0; + + if (node->ret.value && node->ret.value->type == NODE_EXPR_VAR) + { + char *tname = infer_type(ctx, node->ret.value); + if (tname) + { + char *clean = tname; + if (strncmp(clean, "struct ", 7) == 0) + { + clean += 7; + } + + ASTNode *def = find_struct_def(ctx, clean); + if (def && def->type_info && def->type_info->traits.has_drop) + { + fprintf(out, " return ({ "); + if (strstr(g_config.cc, "tcc")) + { + fprintf(out, "__typeof__("); + codegen_expression(ctx, node->ret.value, out); + fprintf(out, ")"); + } + else + { + fprintf(out, "__auto_type"); + } + fprintf(out, " _z_ret_mv = "); + codegen_expression(ctx, node->ret.value, out); + fprintf(out, "; memset(&"); + codegen_expression(ctx, node->ret.value, out); + fprintf(out, ", 0, sizeof(_z_ret_mv)); "); + // Run defers before returning + for (int i = defer_count - 1; i >= func_defer_boundary; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + fprintf(out, "_z_ret_mv; });\n"); + handled = 1; + } + free(tname); + } + } + + if (!handled) + { + if (has_defers && node->ret.value) + { + // Save return value, run defers, then return + fprintf(out, " { "); + emit_auto_type(ctx, node->ret.value, node->token, out); + fprintf(out, " _z_ret = "); + codegen_expression(ctx, node->ret.value, out); + fprintf(out, "; "); + for (int i = defer_count - 1; i >= func_defer_boundary; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + fprintf(out, "return _z_ret; }\n"); + } + else if (has_defers) + { + // No return value, just run defers + for (int i = defer_count - 1; i >= func_defer_boundary; i--) + { + codegen_node_single(ctx, defer_stack[i], out); + } + fprintf(out, " return;\n"); + } + else + { + // No defers, simple return + fprintf(out, " return "); + codegen_expression(ctx, node->ret.value, out); + fprintf(out, ";\n"); + } + } + break; + } + case NODE_EXPR_MEMBER: + { + codegen_expression(ctx, node->member.target, out); + char *lt = infer_type(ctx, node->member.target); + if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) + { + fprintf(out, "->%s", node->member.field); + } + else + { + fprintf(out, ".%s", node->member.field); + } + if (lt) + { + free(lt); + } + break; + } + case NODE_REPL_PRINT: + { + fprintf(out, "{ "); + emit_auto_type(ctx, node->repl_print.expr, node->token, out); + fprintf(out, " _zval = ("); + codegen_expression(ctx, node->repl_print.expr, out); + fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, " + "\"\\n\"); }\n"); + break; + } + case NODE_AWAIT: + { + char *ret_type = "void*"; + int free_ret = 0; + if (node->type_info) + { + char *t = codegen_type_to_string(node->type_info); + if (t) + { + ret_type = t; + free_ret = 1; + } + } + else if (node->resolved_type) + { + ret_type = node->resolved_type; + } + + // Fallback: If type is still Async/void* (likely from Future type, not + // Result type), try to infer + if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) + { + char *inf = infer_type(ctx, node); + if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0) + { + if (free_ret) + { + free(ret_type); + } + ret_type = inf; + free_ret = 0; // infer_type ownership ambiguous, avoid double free + } + } + + int needs_long_cast = 0; + int returns_struct = 0; + if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 && + strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0) + { + if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 && + strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 && + strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 && + strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 && + strncmp(ret_type, "uint", 4) != 0 && strncmp(ret_type, "int", 3) != 0) + { + returns_struct = 1; + } + else + { + needs_long_cast = 1; + } + + if (strncmp(ret_type, "struct", 6) == 0) + { + returns_struct = 1; + } + } + + fprintf(out, "({ Async _a = "); + codegen_expression(ctx, node->unary.operand, out); + fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); "); + if (strcmp(ret_type, "void") == 0) + { + fprintf(out, "})"); // result unused + } + else + { + if (returns_struct) + { + // Dereference and free + fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type, ret_type); + } + else + { + if (needs_long_cast) + { + fprintf(out, "(%s)(long)_r; })", ret_type); + } + else + { + fprintf(out, "(%s)_r; })", ret_type); + } + } + } + if (free_ret) + { + free(ret_type); + } + fprintf(out, ";\n"); // Statement terminator + break; + } + case NODE_EXPR_LITERAL: + // String literal statement should auto-print + if (node->literal.type_kind == 2 || node->literal.type_kind == TOK_STRING) + { + fprintf(out, " printf(\"%%s\\n\", "); + codegen_expression(ctx, node, out); + fprintf(out, ");\n"); + } + else + { + // Non-string literals as statements - just evaluate + codegen_expression(ctx, node, out); + fprintf(out, ";\n"); + } + break; + case NODE_CUDA_LAUNCH: + { + // Emit CUDA kernel launch: kernel<<>>(args); + ASTNode *call = node->cuda_launch.call; + + // Get kernel name from callee + if (call->call.callee->type == NODE_EXPR_VAR) + { + fprintf(out, " %s<<<", call->call.callee->var_ref.name); + } + else + { + fprintf(out, " "); + codegen_expression(ctx, call->call.callee, out); + fprintf(out, "<<<"); + } + + // Grid dimension + codegen_expression(ctx, node->cuda_launch.grid, out); + fprintf(out, ", "); + + // Block dimension + codegen_expression(ctx, node->cuda_launch.block, out); + + // Optional shared memory size + if (node->cuda_launch.shared_mem || node->cuda_launch.stream) + { + fprintf(out, ", "); + if (node->cuda_launch.shared_mem) + { + codegen_expression(ctx, node->cuda_launch.shared_mem, out); + } + else + { + fprintf(out, "0"); + } + } + + // Optional CUDA stream + if (node->cuda_launch.stream) + { + fprintf(out, ", "); + codegen_expression(ctx, node->cuda_launch.stream, out); + } + + fprintf(out, ">>>("); + + // Arguments + ASTNode *arg = call->call.args; + int first = 1; + while (arg) + { + if (!first) + { + fprintf(out, ", "); + } + codegen_expression(ctx, arg, out); + first = 0; + arg = arg->next; + } + + fprintf(out, ");\n"); + break; + } + default: + codegen_expression(ctx, node, out); + fprintf(out, ";\n"); + break; + } +} + +// Walks AST nodes and generates code. +void codegen_walker(ParserContext *ctx, ASTNode *node, FILE *out) +{ + while (node) + { + codegen_node_single(ctx, node, out); + node = node->next; + } +} diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index e490789..fe580bf 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -20,6 +20,26 @@ int loop_defer_boundary[MAX_LOOP_DEPTH]; int loop_depth = 0; int func_defer_boundary = 0; +// Strip template suffix from a type name (for example, "MyStruct" -> "MyStruct") +// Returns newly allocated string, caller must free. +char *strip_template_suffix(const char *name) +{ + if (!name) + { + return NULL; + } + char *lt = strchr(name, '<'); + if (lt) + { + int len = lt - name; + char *buf = xmalloc(len + 1); + strncpy(buf, name, len); + buf[len] = 0; + return buf; + } + 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) { diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..15bb19f --- /dev/null +++ b/src/constants.h @@ -0,0 +1,46 @@ + +#ifndef ZEN_CONSTANTS_H +#define ZEN_CONSTANTS_H + +// Buffer sizes +#define MAX_TYPE_NAME_LEN 256 +#define MAX_FUNC_NAME_LEN 512 +#define MAX_ERROR_MSG_LEN 1024 +#define MAX_MANGLED_NAME_LEN 512 +#define MAX_PATH_LEN 4096 + +// Type checking helpers +#define IS_INT_TYPE(t) ((t) && strcmp((t), "int") == 0) +#define IS_BOOL_TYPE(t) ((t) && strcmp((t), "bool") == 0) +#define IS_CHAR_TYPE(t) ((t) && strcmp((t), "char") == 0) +#define IS_VOID_TYPE(t) ((t) && strcmp((t), "void") == 0) +#define IS_FLOAT_TYPE(t) ((t) && strcmp((t), "float") == 0) +#define IS_DOUBLE_TYPE(t) ((t) && strcmp((t), "double") == 0) +#define IS_USIZE_TYPE(t) ((t) && (strcmp((t), "usize") == 0 || strcmp((t), "size_t") == 0)) +#define IS_STRING_TYPE(t) \ + ((t) && \ + (strcmp((t), "string") == 0 || strcmp((t), "char*") == 0 || strcmp((t), "const char*") == 0)) + +// Composite type checks +#define IS_BASIC_TYPE(t) \ + ((t) && (IS_INT_TYPE(t) || IS_BOOL_TYPE(t) || IS_CHAR_TYPE(t) || IS_VOID_TYPE(t) || \ + IS_FLOAT_TYPE(t) || IS_DOUBLE_TYPE(t) || IS_USIZE_TYPE(t) || \ + strcmp((t), "ssize_t") == 0 || strcmp((t), "__auto_type") == 0)) + +#define IS_NUMERIC_TYPE(t) \ + ((t) && (IS_INT_TYPE(t) || IS_FLOAT_TYPE(t) || IS_DOUBLE_TYPE(t) || IS_USIZE_TYPE(t))) + +// Pointer type check +#define IS_PTR_TYPE(t) ((t) && strchr((t), '*') != NULL) + +// Struct prefix check +#define IS_STRUCT_PREFIX(t) ((t) && strncmp((t), "struct ", 7) == 0) +#define STRIP_STRUCT_PREFIX(t) (IS_STRUCT_PREFIX(t) ? ((t) + 7) : (t)) + +// Generic type checks +#define IS_OPTION_TYPE(t) ((t) && strncmp((t), "Option_", 7) == 0) +#define IS_RESULT_TYPE(t) ((t) && strncmp((t), "Result_", 7) == 0) +#define IS_VEC_TYPE(t) ((t) && strncmp((t), "Vec_", 4) == 0) +#define IS_SLICE_TYPE(t) ((t) && strncmp((t), "Slice_", 6) == 0) + +#endif // ZEN_CONSTANTS_H diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c new file mode 100644 index 0000000..63ec329 --- /dev/null +++ b/src/parser/parser_decl.c @@ -0,0 +1,802 @@ + +#include "parser.h" +#include +#include +#include +#include +#include + +#include "../ast/ast.h" +#include "../plugins/plugin_manager.h" +#include "../zen/zen_facts.h" +#include "zprep_plugin.h" +#include "../codegen/codegen.h" + +ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) +{ + lexer_next(l); // eat 'fn' + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); + + if (is_async) + { + ctx->has_async = 1; + } + + // Check for C reserved word conflict + if (is_c_reserved_word(name)) + { + warn_c_reserved_word(name_tok, name); + } + + char *gen_param = NULL; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + + char buf[1024]; + buf[0] = 0; + + while (1) + { + Token gt = lexer_next(l); + if (gt.type != TOK_IDENT) + { + zpanic_at(gt, "Expected generic parameter name"); + } + char *s = token_strdup(gt); + + if (strlen(buf) + strlen(s) + 2 >= sizeof(buf)) + { + zpanic_at(gt, "Too many generic parameters"); + } + + if (buf[0]) + { + strcat(buf, ","); + } + strcat(buf, s); + free(s); + + if (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + continue; + } + break; + } + + if (lexer_next(l).type != TOK_RANGLE) + { + zpanic_at(lexer_peek(l), "Expected >"); + } + gen_param = xstrdup(buf); + } + + // Register generic parameters so type parsing recognizes them + int saved_generic_count = ctx->known_generics_count; + if (gen_param) + { + char *tmp = xstrdup(gen_param); + char *tok = strtok(tmp, ","); + while (tok) + { + register_generic(ctx, tok); + tok = strtok(NULL, ","); + } + free(tmp); + } + + enter_scope(ctx); + char **defaults; + int count; + Type **arg_types; + char **param_names; + int is_varargs = 0; + + char *args = + parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); + + char *ret = "void"; + Type *ret_type_obj = type_new(TYPE_VOID); + + if (strcmp(name, "main") == 0) + { + ret = "int"; + ret_type_obj = type_new(TYPE_INT); + } + + if (lexer_peek(l).type == TOK_ARROW) + { + lexer_next(l); + ret_type_obj = parse_type_formal(ctx, l); + ret = type_to_string(ret_type_obj); + } + + extern char *curr_func_ret; + curr_func_ret = ret; + + // Auto-prefix function name if in module context + // Don't prefix generic templates or functions inside impl blocks (already + // mangled) + if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) + { + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); + free(name); + name = prefixed_name; + } + + // Register if concrete (Global functions only) + if (!gen_param && !ctx->current_impl_struct) + { + register_func(ctx, name, count, defaults, arg_types, ret_type_obj, is_varargs, is_async, + name_tok); + // Note: must_use is set after return by caller (parser_core.c) + } + + ASTNode *body = NULL; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); // consume ; + } + else + { + body = parse_block(ctx, l); + } + + // Check for unused parameters + // The current scope contains arguments (since parse_block creates a new child + // scope for body) Only check if we parsed a body (not a prototype) function + if (body && ctx->current_scope) + { + Symbol *sym = ctx->current_scope->symbols; + while (sym) + { + // Check if unused and not prefixed with '_' (conventional ignore) + // also ignore 'self' as it is often mandated by traits + if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "self") != 0 && + strcmp(name, "main") != 0) + { + warn_unused_parameter(sym->decl_token, sym->name, name); + } + sym = sym->next; + } + } + + exit_scope(ctx); + + // Restore generic count to unregister function-scoped generics + ctx->known_generics_count = saved_generic_count; + + curr_func_ret = NULL; + + ASTNode *node = ast_create(NODE_FUNCTION); + node->token = name_tok; // Save definition location + node->func.name = name; + node->func.args = args; + node->func.ret_type = ret; + node->func.body = body; + + node->func.arg_types = arg_types; + node->func.param_names = param_names; + node->func.arg_count = count; + node->func.defaults = defaults; + node->func.ret_type_info = ret_type_obj; + node->func.is_varargs = is_varargs; + + if (gen_param) + { + register_func_template(ctx, name, gen_param, node); + return NULL; + } + if (!ctx->current_impl_struct) + { + add_to_func_list(ctx, node); + } + return node; +} + +char *patch_self_args(const char *args, const char *struct_name) +{ + if (!args) + { + return NULL; + } + char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10); + + // Check if it starts with "void* self" + if (strncmp(args, "void* self", 10) == 0) + { + sprintf(new_args, "%s* self%s", struct_name, args + 10); + } + else + { + strcpy(new_args, args); + } + return new_args; +} +// Helper for Value-Returning Defer +static void replace_it_with_var(ASTNode *node, char *var_name) +{ + if (!node) + { + return; + } + if (node->type == NODE_EXPR_VAR) + { + if (strcmp(node->var_ref.name, "it") == 0) + { + // Replace 'it' with var_name + node->var_ref.name = xstrdup(var_name); + } + } + else if (node->type == NODE_EXPR_CALL) + { + replace_it_with_var(node->call.callee, var_name); + ASTNode *arg = node->call.args; + while (arg) + { + replace_it_with_var(arg, var_name); + arg = arg->next; + } + } + else if (node->type == NODE_EXPR_MEMBER) + { + replace_it_with_var(node->member.target, var_name); + } + else if (node->type == NODE_EXPR_BINARY) + { + replace_it_with_var(node->binary.left, var_name); + replace_it_with_var(node->binary.right, var_name); + } + else if (node->type == NODE_EXPR_UNARY) + { + replace_it_with_var(node->unary.operand, var_name); + } + else if (node->type == NODE_BLOCK) + { + ASTNode *s = node->block.statements; + while (s) + { + replace_it_with_var(s, var_name); + s = s->next; + } + } +} + +ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat 'var' + + // Destructuring: var {x, y} = ... + if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN) + { + int is_struct = (lexer_peek(l).type == TOK_LBRACE); + lexer_next(l); + char **names = xmalloc(16 * sizeof(char *)); + int count = 0; + while (1) + { + Token t = lexer_next(l); + char *nm = token_strdup(t); + // UPDATE: Pass NULL to add_symbol + names[count++] = nm; + add_symbol(ctx, nm, "unknown", NULL); + Token next = lexer_next(l); + if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN)) + { + break; + } + if (next.type != TOK_COMMA) + { + zpanic_at(next, "Expected comma"); + } + } + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected ="); + } + ASTNode *init = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = names; + n->destruct.count = count; + n->destruct.init_expr = init; + n->destruct.is_struct_destruct = is_struct; + return n; + } + + // Normal Declaration OR Named Struct Destructuring + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); + + // Check for Struct Destructuring: var Point { x, y } + if (lexer_peek(l).type == TOK_LBRACE) + { + lexer_next(l); // eat { + char **names = xmalloc(16 * sizeof(char *)); + char **fields = xmalloc(16 * sizeof(char *)); + int count = 0; + + while (1) + { + // Parse field:name or just name + Token t = lexer_next(l); + char *ident = token_strdup(t); + + if (lexer_peek(l).type == TOK_COLON) + { + // field: var_name + lexer_next(l); // eat : + Token v = lexer_next(l); + fields[count] = ident; + names[count] = token_strdup(v); + } + else + { + // Shorthand: field (implies var name = field) + fields[count] = ident; + names[count] = ident; // Share pointer or duplicate? duplicate safer if we free + } + // Register symbol for variable + add_symbol(ctx, names[count], "unknown", NULL); + + count++; + + Token next = lexer_next(l); + if (next.type == TOK_RBRACE) + { + break; + } + if (next.type != TOK_COMMA) + { + zpanic_at(next, "Expected comma in struct pattern"); + } + } + + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected ="); + } + ASTNode *init = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = names; + n->destruct.field_names = fields; + n->destruct.count = count; + n->destruct.init_expr = init; + n->destruct.is_struct_destruct = 1; + n->destruct.struct_name = name; // "Point" + return n; + } + + // Check for Guard Pattern: var Some(val) = opt else { ... } + if (lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); // eat ( + Token val_tok = lexer_next(l); + char *val_name = token_strdup(val_tok); + + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); + } + + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); + } + + ASTNode *init = parse_expression(ctx, l); + + Token t = lexer_next(l); + if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) + { + zpanic_at(t, "Expected 'else' in guard statement"); + } + + ASTNode *else_blk; + if (lexer_peek(l).type == TOK_LBRACE) + { + else_blk = parse_block(ctx, l); + } + else + { + else_blk = ast_create(NODE_BLOCK); + else_blk->block.statements = parse_statement(ctx, l); + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = xmalloc(sizeof(char *)); + n->destruct.names[0] = val_name; + n->destruct.count = 1; + n->destruct.init_expr = init; + n->destruct.is_guard = 1; + n->destruct.guard_variant = name; + n->destruct.else_block = else_blk; + + add_symbol(ctx, val_name, "unknown", NULL); + + return n; + } + + char *type = NULL; + Type *type_obj = NULL; // --- NEW: Formal Type Object --- + + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); + // Hybrid Parse: Get Object AND String + type_obj = parse_type_formal(ctx, l); + type = type_to_string(type_obj); + } + + ASTNode *init = NULL; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); + + // Peek for special initializers + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) + { + init = parse_embed(ctx, l); + + if (!type && init->type_info) + { + type = type_to_string(init->type_info); + } + if (!type) + { + register_slice(ctx, "char"); + type = xstrdup("Slice_char"); + } + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else if (next.type == TOK_LBRACKET && type && strncmp(type, "Slice_", 6) == 0) + { + char *code = parse_array_literal(ctx, l, type); + init = ast_create(NODE_RAW_STMT); + init->raw_stmt.content = code; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else if (next.type == TOK_LPAREN && type && strncmp(type, "Tuple_", 6) == 0) + { + char *code = parse_tuple_literal(ctx, l, type); + init = ast_create(NODE_RAW_STMT); + init->raw_stmt.content = code; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else + { + init = parse_expression(ctx, l); + } + + if (init && type) + { + char *rhs_type = init->resolved_type; + if (!rhs_type && init->type_info) + { + rhs_type = type_to_string(init->type_info); + } + + if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) + { + // Strip stars to get struct names + char target_struct[256]; + strcpy(target_struct, type); + target_struct[strlen(target_struct) - 1] = 0; + char source_struct[256]; + strcpy(source_struct, rhs_type); + source_struct[strlen(source_struct) - 1] = 0; + + ASTNode *def = find_struct_def(ctx, source_struct); + + if (def && def->strct.parent && strcmp(def->strct.parent, target_struct) == 0) + { + // Create Cast Node + ASTNode *cast = ast_create(NODE_EXPR_CAST); + cast->cast.target_type = xstrdup(type); + cast->cast.expr = init; + cast->type_info = type_obj; // Inherit formal type + + init = cast; // Replace init with cast + } + } + } + + // ** Type Inference Logic ** + if (!type && init) + { + if (init->type_info) + { + type_obj = init->type_info; + type = type_to_string(type_obj); + } + else if (init->type == NODE_EXPR_SLICE) + { + zpanic_at(init->token, "Slice Node has NO Type Info!"); + } + // Fallbacks for literals + else if (init->type == NODE_EXPR_LITERAL) + { + if (init->literal.type_kind == 0) + { + type = xstrdup("int"); + type_obj = type_new(TYPE_INT); + } + else if (init->literal.type_kind == 1) + { + type = xstrdup("float"); + type_obj = type_new(TYPE_FLOAT); + } + else if (init->literal.type_kind == 2) + { + type = xstrdup("string"); + type_obj = type_new(TYPE_STRING); + } + } + else if (init->type == NODE_EXPR_STRUCT_INIT) + { + type = xstrdup(init->struct_init.struct_name); + type_obj = type_new(TYPE_STRUCT); + type_obj->name = xstrdup(type); + } + } + } + + if (!type && !init) + { + zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); + } + + // Register in symbol table with actual token + add_symbol_with_token(ctx, name, type, type_obj, name_tok); + + // NEW: Capture Const Integer Values + if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == 0) + { + Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct + if (s) + { + s->is_const_value = 1; + s->const_int_val = init->literal.int_val; + } + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_VAR_DECL); + n->token = name_tok; // Save location + n->var_decl.name = name; + n->var_decl.type_str = type; + n->type_info = type_obj; + + // Auto-construct Trait Object + if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY && + strcmp(init->unary.op, "&") == 0 && init->unary.operand->type == NODE_EXPR_VAR) + { + char *var_ref_name = init->unary.operand->var_ref.name; + char *struct_type = find_symbol_type(ctx, var_ref_name); + if (struct_type) + { + char *code = xmalloc(512); + sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type, var_ref_name, struct_type, + type); + ASTNode *wrapper = ast_create(NODE_RAW_STMT); + wrapper->raw_stmt.content = code; + init = wrapper; + } + } + + n->var_decl.init_expr = init; + + // Move Semantics Logic for Initialization + check_move_usage(ctx, init, init ? init->token : name_tok); + if (init && init->type == NODE_EXPR_VAR) + { + Type *t = find_symbol_type_info(ctx, init->var_ref.name); + if (!t) + { + Symbol *s = find_symbol_entry(ctx, init->var_ref.name); + if (s) + { + t = s->type_info; + } + } + if (!is_type_copy(ctx, t)) + { + Symbol *s = find_symbol_entry(ctx, init->var_ref.name); + if (s) + { + s->is_moved = 1; + } + } + } + + // Global detection: Either no scope (yet) OR root scope (no parent) + if (!ctx->current_scope || !ctx->current_scope->parent) + { + add_to_global_list(ctx, n); + } + + // Check for 'defer' (Value-Returning Defer) + // Only capture if it is NOT a block defer (defer { ... }) + // If it is a block defer, we leave it for the next parse_statement call. + if (lexer_peek(l).type == TOK_DEFER) + { + Lexer lookahead = *l; + lexer_next(&lookahead); // Eat defer + if (lexer_peek(&lookahead).type != TOK_LBRACE) + { + // Proceed to consume + lexer_next(l); // eat defer (real) + + // Parse the defer expression/statement + // Usually defer close(it); + // We parse expression. + ASTNode *expr = parse_expression(ctx, l); + + // Handle "it" substitution + replace_it_with_var(expr, name); + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *d = ast_create(NODE_DEFER); + d->defer_stmt.stmt = expr; + + // Chain it: var_decl -> defer + n->next = d; + } + } + + return n; +} + +ASTNode *parse_const(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat const + Token n = lexer_next(l); + + char *type_str = NULL; + Type *type_obj = NULL; + + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); + // Hybrid Parse + type_obj = parse_type_formal(ctx, l); + type_str = type_to_string(type_obj); + } + + char *ns = token_strdup(n); + if (!type_obj) + { + type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object + } + type_obj->is_const = 1; + add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj); + + ASTNode *i = 0; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); + + // Check for constant integer literal + if (lexer_peek(l).type == TOK_INT) + { + Token val_tok = lexer_peek(l); + int val = atoi(token_strdup(val_tok)); // quick check + + Symbol *s = find_symbol_entry(ctx, ns); + if (s) + { + s->is_const_value = 1; + s->const_int_val = val; + + if (!s->type_name || strcmp(s->type_name, "unknown") == 0) + { + if (s->type_name) + { + free(s->type_name); + } + s->type_name = xstrdup("int"); + if (s->type_info) + { + free(s->type_info); + } + s->type_info = type_new(TYPE_INT); + } + } + } + + if (lexer_peek(l).type == TOK_LPAREN && type_str && strncmp(type_str, "Tuple_", 6) == 0) + { + char *code = parse_tuple_literal(ctx, l, type_str); + i = ast_create(NODE_RAW_STMT); + i->raw_stmt.content = code; + } + else + { + i = parse_expression(ctx, l); + } + } + else + { + lexer_next(l); + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *o = ast_create(NODE_CONST); + o->var_decl.name = ns; + o->var_decl.type_str = type_str; + o->var_decl.init_expr = i; + + if (!ctx->current_scope || !ctx->current_scope->parent) + { + add_to_global_list(ctx, o); + } + + return o; +} + +ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // consume 'type' or 'alias' + Token n = lexer_next(l); + if (n.type != TOK_IDENT) + { + zpanic_at(n, "Expected identifier for type alias"); + } + + lexer_next(l); // consume '=' + + char *o = parse_type(ctx, l); + + lexer_next(l); // consume ';' (parse_type doesn't consume it? parse_type calls parse_type_formal + // which doesn't consume ;?) + // Note: parse_type_stmt usually expects ; but parse_type just parses type expression. + // Check previous implementation: it had lexer_next(l) at end. This assumes ;. + + ASTNode *node = ast_create(NODE_TYPE_ALIAS); + node->type_alias.alias = xmalloc(n.len + 1); + strncpy(node->type_alias.alias, n.start, n.len); + node->type_alias.alias[n.len] = 0; + node->type_alias.original_type = o; + + register_type_alias(ctx, node->type_alias.alias, o); + + return node; +} diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 67045ea..5eefb81 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -12,7 +12,7 @@ #include "zprep_plugin.h" #include "../codegen/codegen.h" -static char *curr_func_ret = NULL; +char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); static void check_assignment_condition(ASTNode *cond) @@ -31,211 +31,6 @@ static void check_assignment_condition(ASTNode *cond) } } -ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) -{ - lexer_next(l); // eat 'fn' - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); - - if (is_async) - { - ctx->has_async = 1; - } - - // Check for C reserved word conflict - if (is_c_reserved_word(name)) - { - warn_c_reserved_word(name_tok, name); - } - - char *gen_param = NULL; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - - char buf[1024]; - buf[0] = 0; - - while (1) - { - Token gt = lexer_next(l); - if (gt.type != TOK_IDENT) - { - zpanic_at(gt, "Expected generic parameter name"); - } - char *s = token_strdup(gt); - - if (strlen(buf) + strlen(s) + 2 >= sizeof(buf)) - { - zpanic_at(gt, "Too many generic parameters"); - } - - if (buf[0]) - { - strcat(buf, ","); - } - strcat(buf, s); - free(s); - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - continue; - } - break; - } - - if (lexer_next(l).type != TOK_RANGLE) - { - zpanic_at(lexer_peek(l), "Expected >"); - } - gen_param = xstrdup(buf); - } - - // Register generic parameters so type parsing recognizes them - int saved_generic_count = ctx->known_generics_count; - if (gen_param) - { - char *tmp = xstrdup(gen_param); - char *tok = strtok(tmp, ","); - while (tok) - { - register_generic(ctx, tok); - tok = strtok(NULL, ","); - } - free(tmp); - } - - enter_scope(ctx); - char **defaults; - int count; - Type **arg_types; - char **param_names; - int is_varargs = 0; - - char *args = - parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); - - char *ret = "void"; - Type *ret_type_obj = type_new(TYPE_VOID); - - if (strcmp(name, "main") == 0) - { - ret = "int"; - ret_type_obj = type_new(TYPE_INT); - } - - if (lexer_peek(l).type == TOK_ARROW) - { - lexer_next(l); - ret_type_obj = parse_type_formal(ctx, l); - ret = type_to_string(ret_type_obj); - } - - extern char *curr_func_ret; - curr_func_ret = ret; - - // Auto-prefix function name if in module context - // Don't prefix generic templates or functions inside impl blocks (already - // mangled) - if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) - { - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); - free(name); - name = prefixed_name; - } - - // Register if concrete (Global functions only) - if (!gen_param && !ctx->current_impl_struct) - { - register_func(ctx, name, count, defaults, arg_types, ret_type_obj, is_varargs, is_async, - name_tok); - // Note: must_use is set after return by caller (parser_core.c) - } - - ASTNode *body = NULL; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); // consume ; - } - else - { - body = parse_block(ctx, l); - } - - // Check for unused parameters - // The current scope contains arguments (since parse_block creates a new child - // scope for body) Only check if we parsed a body (not a prototype) function - if (body && ctx->current_scope) - { - Symbol *sym = ctx->current_scope->symbols; - while (sym) - { - // Check if unused and not prefixed with '_' (conventional ignore) - // also ignore 'self' as it is often mandated by traits - if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "self") != 0 && - strcmp(name, "main") != 0) - { - warn_unused_parameter(sym->decl_token, sym->name, name); - } - sym = sym->next; - } - } - - exit_scope(ctx); - - // Restore generic count to unregister function-scoped generics - ctx->known_generics_count = saved_generic_count; - - curr_func_ret = NULL; - - ASTNode *node = ast_create(NODE_FUNCTION); - node->token = name_tok; // Save definition location - node->func.name = name; - node->func.args = args; - node->func.ret_type = ret; - node->func.body = body; - - node->func.arg_types = arg_types; - node->func.param_names = param_names; - node->func.arg_count = count; - node->func.defaults = defaults; - node->func.ret_type_info = ret_type_obj; - node->func.is_varargs = is_varargs; - - if (gen_param) - { - register_func_template(ctx, name, gen_param, node); - return NULL; - } - if (!ctx->current_impl_struct) - { - add_to_func_list(ctx, node); - } - return node; -} - -char *patch_self_args(const char *args, const char *struct_name) -{ - if (!args) - { - return NULL; - } - char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10); - - // Check if it starts with "void* self" - if (strncmp(args, "void* self", 10) == 0) - { - sprintf(new_args, "%s* self%s", struct_name, args + 10); - } - else - { - strcpy(new_args, args); - } - return new_args; -} - ASTNode *parse_match(ParserContext *ctx, Lexer *l) { init_builtins(); @@ -956,3273 +751,1759 @@ ASTNode *parse_assert(ParserContext *ctx, Lexer *l) return n; } -// Helper for Value-Returning Defer -static void replace_it_with_var(ASTNode *node, char *var_name) +ASTNode *parse_return(ParserContext *ctx, Lexer *l) { - if (!node) - { - return; - } - if (node->type == NODE_EXPR_VAR) - { - if (strcmp(node->var_ref.name, "it") == 0) - { - // Replace 'it' with var_name - node->var_ref.name = xstrdup(var_name); - } - } - else if (node->type == NODE_EXPR_CALL) - { - replace_it_with_var(node->call.callee, var_name); - ASTNode *arg = node->call.args; - while (arg) - { - replace_it_with_var(arg, var_name); - arg = arg->next; - } - } - else if (node->type == NODE_EXPR_MEMBER) - { - replace_it_with_var(node->member.target, var_name); - } - else if (node->type == NODE_EXPR_BINARY) - { - replace_it_with_var(node->binary.left, var_name); - replace_it_with_var(node->binary.right, var_name); - } - else if (node->type == NODE_EXPR_UNARY) - { - replace_it_with_var(node->unary.operand, var_name); - } - else if (node->type == NODE_BLOCK) + Token return_token = lexer_next(l); // eat 'return' + + // Error if return is used inside a defer block + if (ctx->in_defer_block) { - ASTNode *s = node->block.statements; - while (s) - { - replace_it_with_var(s, var_name); - s = s->next; - } + zpanic_at(return_token, "'return' is not allowed inside a 'defer' block"); } -} -ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'var' + ASTNode *n = ast_create(NODE_RETURN); + n->token = return_token; + + int handled = 0; - // Destructuring: var {x, y} = ... - if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN) + // 1. Check for Tuple Literal Return: return (a, b); + // Condition: Function returns Tuple_..., starts with '(', and contains ',' at + // top level + if (curr_func_ret && strncmp(curr_func_ret, "Tuple_", 6) == 0 && + lexer_peek(l).type == TOK_LPAREN) { - int is_struct = (lexer_peek(l).type == TOK_LBRACE); - lexer_next(l); - char **names = xmalloc(16 * sizeof(char *)); - int count = 0; + + // Peek ahead to distinguish "(expr)" from "(a, b)" + int is_tuple_lit = 0; + int depth = 0; + + // Just scan tokens manually using a temp lexer to be safe + Lexer temp_l = *l; + while (1) { - Token t = lexer_next(l); - char *nm = token_strdup(t); - // UPDATE: Pass NULL to add_symbol - names[count++] = nm; - add_symbol(ctx, nm, "unknown", NULL); - Token next = lexer_next(l); - if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN)) + Token t = lexer_next(&temp_l); + if (t.type == TOK_EOF) { break; } - if (next.type != TOK_COMMA) + if (t.type == TOK_SEMICOLON) { - zpanic_at(next, "Expected comma"); + break; // Safety break } - } - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected ="); - } - ASTNode *init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = names; - n->destruct.count = count; - n->destruct.init_expr = init; - n->destruct.is_struct_destruct = is_struct; - return n; - } - - // Normal Declaration OR Named Struct Destructuring - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); - // Check for Struct Destructuring: var Point { x, y } - if (lexer_peek(l).type == TOK_LBRACE) - { - lexer_next(l); // eat { - char **names = xmalloc(16 * sizeof(char *)); - char **fields = xmalloc(16 * sizeof(char *)); - int count = 0; - - while (1) - { - // Parse field:name or just name - Token t = lexer_next(l); - char *ident = token_strdup(t); - - if (lexer_peek(l).type == TOK_COLON) + if (t.type == TOK_LPAREN) { - // field: var_name - lexer_next(l); // eat : - Token v = lexer_next(l); - fields[count] = ident; - names[count] = token_strdup(v); + depth++; } - else + if (t.type == TOK_RPAREN) { - // Shorthand: field (implies var name = field) - fields[count] = ident; - names[count] = ident; // Share pointer or duplicate? duplicate safer if we free + depth--; + if (depth == 0) + { + break; // End of potential tuple + } } - // Register symbol for variable - add_symbol(ctx, names[count], "unknown", NULL); - - count++; - Token next = lexer_next(l); - if (next.type == TOK_RBRACE) + // If we find a comma at depth 1 (inside the first parens), it's a tuple + // literal! + if (depth == 1 && t.type == TOK_COMMA) { + is_tuple_lit = 1; break; } - if (next.type != TOK_COMMA) - { - zpanic_at(next, "Expected comma in struct pattern"); - } } - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected ="); - } - ASTNode *init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) + if (is_tuple_lit) { - lexer_next(l); + char *code = parse_tuple_literal(ctx, l, curr_func_ret); + ASTNode *raw = ast_create(NODE_RAW_STMT); + raw->raw_stmt.content = code; + n->ret.value = raw; + handled = 1; } - - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = names; - n->destruct.field_names = fields; - n->destruct.count = count; - n->destruct.init_expr = init; - n->destruct.is_struct_destruct = 1; - n->destruct.struct_name = name; // "Point" - return n; } - - // Check for Guard Pattern: var Some(val) = opt else { ... } - if (lexer_peek(l).type == TOK_LPAREN) + // 2. Check for Array Literal Return: return [a, b]; + else if (curr_func_ret && strncmp(curr_func_ret, "Slice_", 6) == 0 && + lexer_peek(l).type == TOK_LBRACKET) { - lexer_next(l); // eat ( - Token val_tok = lexer_next(l); - char *val_name = token_strdup(val_tok); - - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); - } - - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); - } - - ASTNode *init = parse_expression(ctx, l); - - Token t = lexer_next(l); - if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) - { - zpanic_at(t, "Expected 'else' in guard statement"); - } + char *code = parse_array_literal(ctx, l, curr_func_ret); + ASTNode *raw = ast_create(NODE_RAW_STMT); + raw->raw_stmt.content = code; + n->ret.value = raw; + handled = 1; + } - ASTNode *else_blk; - if (lexer_peek(l).type == TOK_LBRACE) + // 3. Standard Expression Return + if (!handled) + { + if (lexer_peek(l).type == TOK_SEMICOLON) { - else_blk = parse_block(ctx, l); + n->ret.value = NULL; } else { - else_blk = ast_create(NODE_BLOCK); - else_blk->block.statements = parse_statement(ctx, l); - } + n->ret.value = parse_expression(ctx, l); + check_move_usage(ctx, n->ret.value, n->ret.value ? n->ret.value->token : lexer_peek(l)); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); + // Note: Returning a non-Copy variable effectively moves it out. + // We could mark it as moved, but scope ends anyway. + // The critical part is checking we aren't returning an ALREADY moved value. } - - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = xmalloc(sizeof(char *)); - n->destruct.names[0] = val_name; - n->destruct.count = 1; - n->destruct.init_expr = init; - n->destruct.is_guard = 1; - n->destruct.guard_variant = name; - n->destruct.else_block = else_blk; - - add_symbol(ctx, val_name, "unknown", NULL); - - return n; } - char *type = NULL; - Type *type_obj = NULL; // --- NEW: Formal Type Object --- - - if (lexer_peek(l).type == TOK_COLON) + if (lexer_peek(l).type == TOK_SEMICOLON) { lexer_next(l); - // Hybrid Parse: Get Object AND String - type_obj = parse_type_formal(ctx, l); - type = type_to_string(type_obj); } + return n; +} - ASTNode *init = NULL; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) - { - lexer_next(l); - - // Peek for special initializers - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) - { - init = parse_embed(ctx, l); +ASTNode *parse_if(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat if + ASTNode *cond = parse_expression(ctx, l); + check_assignment_condition(cond); - if (!type && init->type_info) - { - type = type_to_string(init->type_info); - } - if (!type) - { - register_slice(ctx, "char"); - type = xstrdup("Slice_char"); - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else if (next.type == TOK_LBRACKET && type && strncmp(type, "Slice_", 6) == 0) - { - char *code = parse_array_literal(ctx, l, type); - init = ast_create(NODE_RAW_STMT); - init->raw_stmt.content = code; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else if (next.type == TOK_LPAREN && type && strncmp(type, "Tuple_", 6) == 0) - { - char *code = parse_tuple_literal(ctx, l, type); - init = ast_create(NODE_RAW_STMT); - init->raw_stmt.content = code; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else - { - init = parse_expression(ctx, l); - } - - if (init && type) - { - char *rhs_type = init->resolved_type; - if (!rhs_type && init->type_info) - { - rhs_type = type_to_string(init->type_info); - } - - if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) - { - // Strip stars to get struct names - char target_struct[256]; - strcpy(target_struct, type); - target_struct[strlen(target_struct) - 1] = 0; - char source_struct[256]; - strcpy(source_struct, rhs_type); - source_struct[strlen(source_struct) - 1] = 0; - - ASTNode *def = find_struct_def(ctx, source_struct); - - if (def && def->strct.parent && strcmp(def->strct.parent, target_struct) == 0) - { - // Create Cast Node - ASTNode *cast = ast_create(NODE_EXPR_CAST); - cast->cast.target_type = xstrdup(type); - cast->cast.expr = init; - cast->type_info = type_obj; // Inherit formal type - - init = cast; // Replace init with cast - } - } - } - - // ** Type Inference Logic ** - if (!type && init) - { - if (init->type_info) - { - type_obj = init->type_info; - type = type_to_string(type_obj); - } - else if (init->type == NODE_EXPR_SLICE) - { - zpanic_at(init->token, "Slice Node has NO Type Info!"); - } - // Fallbacks for literals - else if (init->type == NODE_EXPR_LITERAL) - { - if (init->literal.type_kind == 0) - { - type = xstrdup("int"); - type_obj = type_new(TYPE_INT); - } - else if (init->literal.type_kind == 1) - { - type = xstrdup("float"); - type_obj = type_new(TYPE_FLOAT); - } - else if (init->literal.type_kind == 2) - { - type = xstrdup("string"); - type_obj = type_new(TYPE_STRING); - } - } - else if (init->type == NODE_EXPR_STRUCT_INIT) - { - type = xstrdup(init->struct_init.struct_name); - type_obj = type_new(TYPE_STRUCT); - type_obj->name = xstrdup(type); - } - } - } - - if (!type && !init) + ASTNode *then_b = NULL; + if (lexer_peek(l).type == TOK_LBRACE) { - zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); + then_b = parse_block(ctx, l); } - - // Register in symbol table with actual token - add_symbol_with_token(ctx, name, type, type_obj, name_tok); - - // NEW: Capture Const Integer Values - if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == 0) + else { - Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct - if (s) - { - s->is_const_value = 1; - s->const_int_val = init->literal.int_val; - } + // Single statement: Wrap in scope + block + enter_scope(ctx); + ASTNode *s = parse_statement(ctx, l); + exit_scope(ctx); + then_b = ast_create(NODE_BLOCK); + then_b->block.statements = s; } - if (lexer_peek(l).type == TOK_SEMICOLON) + ASTNode *else_b = NULL; + skip_comments(l); + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "else", 4) == 0) { lexer_next(l); - } - - ASTNode *n = ast_create(NODE_VAR_DECL); - n->token = name_tok; // Save location - n->var_decl.name = name; - n->var_decl.type_str = type; - n->type_info = type_obj; - - // Auto-construct Trait Object - if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY && - strcmp(init->unary.op, "&") == 0 && init->unary.operand->type == NODE_EXPR_VAR) - { - char *var_ref_name = init->unary.operand->var_ref.name; - char *struct_type = find_symbol_type(ctx, var_ref_name); - if (struct_type) - { - char *code = xmalloc(512); - sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type, var_ref_name, struct_type, - type); - ASTNode *wrapper = ast_create(NODE_RAW_STMT); - wrapper->raw_stmt.content = code; - init = wrapper; - } - } - - n->var_decl.init_expr = init; - - // Move Semantics Logic for Initialization - check_move_usage(ctx, init, init ? init->token : name_tok); - if (init && init->type == NODE_EXPR_VAR) - { - Type *t = find_symbol_type_info(ctx, init->var_ref.name); - if (!t) + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0) { - Symbol *s = find_symbol_entry(ctx, init->var_ref.name); - if (s) - { - t = s->type_info; - } + else_b = parse_if(ctx, l); } - if (!is_type_copy(ctx, t)) + else if (lexer_peek(l).type == TOK_LBRACE) { - Symbol *s = find_symbol_entry(ctx, init->var_ref.name); - if (s) - { - s->is_moved = 1; - } + else_b = parse_block(ctx, l); } - } - - // Global detection: Either no scope (yet) OR root scope (no parent) - if (!ctx->current_scope || !ctx->current_scope->parent) - { - add_to_global_list(ctx, n); - } - - // Check for 'defer' (Value-Returning Defer) - // Only capture if it is NOT a block defer (defer { ... }) - // If it is a block defer, we leave it for the next parse_statement call. - if (lexer_peek(l).type == TOK_DEFER) - { - Lexer lookahead = *l; - lexer_next(&lookahead); // Eat defer - if (lexer_peek(&lookahead).type != TOK_LBRACE) + else { - // Proceed to consume - lexer_next(l); // eat defer (real) - - // Parse the defer expression/statement - // Usually defer close(it); - // We parse expression. - ASTNode *expr = parse_expression(ctx, l); - - // Handle "it" substitution - replace_it_with_var(expr, name); - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *d = ast_create(NODE_DEFER); - d->defer_stmt.stmt = expr; - - // Chain it: var_decl -> defer - n->next = d; + // Single statement else + enter_scope(ctx); + ASTNode *s = parse_statement(ctx, l); + exit_scope(ctx); + else_b = ast_create(NODE_BLOCK); + else_b->block.statements = s; } } - + ASTNode *n = ast_create(NODE_IF); + n->if_stmt.condition = cond; + n->if_stmt.then_body = then_b; + n->if_stmt.else_body = else_b; return n; } -ASTNode *parse_const(ParserContext *ctx, Lexer *l) +ASTNode *parse_while(ParserContext *ctx, Lexer *l) { - lexer_next(l); // eat const - Token n = lexer_next(l); - - char *type_str = NULL; - Type *type_obj = NULL; + lexer_next(l); + ASTNode *cond = parse_expression(ctx, l); + check_assignment_condition(cond); - if (lexer_peek(l).type == TOK_COLON) + // Zen: While(true) + if ((cond->type == NODE_EXPR_LITERAL && cond->literal.type_kind == TOK_INT && + strcmp(cond->literal.string_val, "1") == 0) || + (cond->type == NODE_EXPR_VAR && strcmp(cond->var_ref.name, "true") == 0)) { - lexer_next(l); - // Hybrid Parse - type_obj = parse_type_formal(ctx, l); - type_str = type_to_string(type_obj); + zen_trigger_at(TRIGGER_WHILE_TRUE, cond->token); } - - char *ns = token_strdup(n); - if (!type_obj) + ASTNode *body; + if (lexer_peek(l).type == TOK_LBRACE) + { + body = parse_block(ctx, l); + } + else { - type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object + body = parse_statement(ctx, l); } - type_obj->is_const = 1; - add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj); + ASTNode *n = ast_create(NODE_WHILE); + n->while_stmt.condition = cond; + n->while_stmt.body = body; + return n; +} + +ASTNode *parse_for(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); - ASTNode *i = 0; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + // Range Loop: for i in 0..10 + if (lexer_peek(l).type == TOK_IDENT) { - lexer_next(l); + int saved_pos = l->pos; + Token var = lexer_next(l); + Token in_tok = lexer_next(l); - // Check for constant integer literal - if (lexer_peek(l).type == TOK_INT) + if (in_tok.type == TOK_IDENT && strncmp(in_tok.start, "in", 2) == 0) { - Token val_tok = lexer_peek(l); - int val = atoi(token_strdup(val_tok)); // quick check - - Symbol *s = find_symbol_entry(ctx, ns); - if (s) + ASTNode *start_expr = parse_expression(ctx, l); + // Check for Range Loop (.. or ..= or ..<) + TokenType next_tok = lexer_peek(l).type; + if (next_tok == TOK_DOTDOT || next_tok == TOK_DOTDOT_LT || next_tok == TOK_DOTDOT_EQ) { - s->is_const_value = 1; - s->const_int_val = val; + int is_inclusive = 0; + if (next_tok == TOK_DOTDOT || next_tok == TOK_DOTDOT_LT) + { + lexer_next(l); // consume .. or ..< + } + else if (next_tok == TOK_DOTDOT_EQ) + { + is_inclusive = 1; + lexer_next(l); // consume ..= + } - if (!s->type_name || strcmp(s->type_name, "unknown") == 0) + if (1) // Block to keep scope for variables { - if (s->type_name) + ASTNode *end_expr = parse_expression(ctx, l); + + ASTNode *n = ast_create(NODE_FOR_RANGE); + n->for_range.var_name = xmalloc(var.len + 1); + strncpy(n->for_range.var_name, var.start, var.len); + n->for_range.var_name[var.len] = 0; + n->for_range.start = start_expr; + n->for_range.end = end_expr; + n->for_range.is_inclusive = is_inclusive; + + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "step", 4) == 0) { - free(s->type_name); + lexer_next(l); + Token s_tok = lexer_next(l); + char *sval = xmalloc(s_tok.len + 1); + strncpy(sval, s_tok.start, s_tok.len); + sval[s_tok.len] = 0; + n->for_range.step = sval; } - s->type_name = xstrdup("int"); - if (s->type_info) + else { - free(s->type_info); + n->for_range.step = NULL; } - s->type_info = type_new(TYPE_INT); - } - } - } - if (lexer_peek(l).type == TOK_LPAREN && type_str && strncmp(type_str, "Tuple_", 6) == 0) - { - char *code = parse_tuple_literal(ctx, l, type_str); - i = ast_create(NODE_RAW_STMT); - i->raw_stmt.content = code; - } - else - { - i = parse_expression(ctx, l); - } - } - else - { - lexer_next(l); - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *o = ast_create(NODE_CONST); - o->var_decl.name = ns; - o->var_decl.type_str = type_str; - o->var_decl.init_expr = i; - - if (!ctx->current_scope || !ctx->current_scope->parent) - { - add_to_global_list(ctx, o); - } + enter_scope(ctx); + add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT)); - return o; -} + if (lexer_peek(l).type == TOK_LBRACE) + { + n->for_range.body = parse_block(ctx, l); + } + else + { + n->for_range.body = parse_statement(ctx, l); + } + exit_scope(ctx); -ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // consume 'type' or 'alias' - Token n = lexer_next(l); - if (n.type != TOK_IDENT) - { - zpanic_at(n, "Expected identifier for type alias"); - } + return n; + } + } + else + { + // Iterator Loop: for x in obj + // Desugar to: + /* + { + var __it = obj.iterator(); + while (true) { + var __opt = __it.next(); + if (__opt.is_none()) break; + var x = __opt.unwrap(); + + } + } + */ - lexer_next(l); // consume '=' + char *var_name = xmalloc(var.len + 1); + strncpy(var_name, var.start, var.len); + var_name[var.len] = 0; - char *o = parse_type(ctx, l); + ASTNode *obj_expr = start_expr; + char *iter_method = "iterator"; - lexer_next(l); // consume ';' (parse_type doesn't consume it? parse_type calls parse_type_formal - // which doesn't consume ;?) - // Note: parse_type_stmt usually expects ; but parse_type just parses type expression. - // Check previous implementation: it had lexer_next(l) at end. This assumes ;. + // Check for reference iteration: for x in &vec + if (obj_expr->type == NODE_EXPR_UNARY && obj_expr->unary.op && + strcmp(obj_expr->unary.op, "&") == 0) + { + obj_expr = obj_expr->unary.operand; + iter_method = "iter_ref"; + } - ASTNode *node = ast_create(NODE_TYPE_ALIAS); - node->type_alias.alias = xmalloc(n.len + 1); - strncpy(node->type_alias.alias, n.start, n.len); - node->type_alias.alias[n.len] = 0; - node->type_alias.original_type = o; + // var __it = obj.iterator(); + ASTNode *it_decl = ast_create(NODE_VAR_DECL); + it_decl->var_decl.name = xstrdup("__it"); + it_decl->var_decl.type_str = NULL; // inferred - register_type_alias(ctx, node->type_alias.alias, o); + // obj.iterator() or obj.iter_ref() + ASTNode *call_iter = ast_create(NODE_EXPR_CALL); + ASTNode *memb_iter = ast_create(NODE_EXPR_MEMBER); + memb_iter->member.target = obj_expr; + memb_iter->member.field = xstrdup(iter_method); + call_iter->call.callee = memb_iter; + call_iter->call.args = NULL; + call_iter->call.arg_count = 0; - return node; -} + it_decl->var_decl.init_expr = call_iter; -ASTNode *parse_return(ParserContext *ctx, Lexer *l) -{ - Token return_token = lexer_next(l); // eat 'return' + // while(true) + ASTNode *while_loop = ast_create(NODE_WHILE); + ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL); + true_lit->literal.type_kind = TOK_INT; // Treated as bool in conditions + true_lit->literal.int_val = 1; + true_lit->literal.string_val = xstrdup("1"); + while_loop->while_stmt.condition = true_lit; - // Error if return is used inside a defer block - if (ctx->in_defer_block) - { - zpanic_at(return_token, "'return' is not allowed inside a 'defer' block"); + ASTNode *loop_body = ast_create(NODE_BLOCK); + ASTNode *stmts_head = NULL; + ASTNode *stmts_tail = NULL; + +#define APPEND_STMT(node) \ + if (!stmts_head) \ + { \ + stmts_head = node; \ + stmts_tail = node; \ + } \ + else \ + { \ + stmts_tail->next = node; \ + stmts_tail = node; \ } - ASTNode *n = ast_create(NODE_RETURN); - n->token = return_token; + // var __opt = __it.next(); + ASTNode *opt_decl = ast_create(NODE_VAR_DECL); + opt_decl->var_decl.name = xstrdup("__opt"); + opt_decl->var_decl.type_str = NULL; - int handled = 0; + // __it.next() + ASTNode *call_next = ast_create(NODE_EXPR_CALL); + ASTNode *memb_next = ast_create(NODE_EXPR_MEMBER); + ASTNode *it_ref = ast_create(NODE_EXPR_VAR); + it_ref->var_ref.name = xstrdup("__it"); + memb_next->member.target = it_ref; + memb_next->member.field = xstrdup("next"); + call_next->call.callee = memb_next; - // 1. Check for Tuple Literal Return: return (a, b); - // Condition: Function returns Tuple_..., starts with '(', and contains ',' at - // top level - if (curr_func_ret && strncmp(curr_func_ret, "Tuple_", 6) == 0 && - lexer_peek(l).type == TOK_LPAREN) - { + opt_decl->var_decl.init_expr = call_next; + APPEND_STMT(opt_decl); - // Peek ahead to distinguish "(expr)" from "(a, b)" - int is_tuple_lit = 0; - int depth = 0; + // __opt.is_none() + ASTNode *call_is_none = ast_create(NODE_EXPR_CALL); + ASTNode *memb_is_none = ast_create(NODE_EXPR_MEMBER); + ASTNode *opt_ref1 = ast_create(NODE_EXPR_VAR); + opt_ref1->var_ref.name = xstrdup("__opt"); + memb_is_none->member.target = opt_ref1; + memb_is_none->member.field = xstrdup("is_none"); + call_is_none->call.callee = memb_is_none; - // Just scan tokens manually using a temp lexer to be safe - Lexer temp_l = *l; + ASTNode *break_stmt = ast_create(NODE_BREAK); - while (1) - { - Token t = lexer_next(&temp_l); - if (t.type == TOK_EOF) - { - break; - } - if (t.type == TOK_SEMICOLON) - { - break; // Safety break - } + ASTNode *if_break = ast_create(NODE_IF); + if_break->if_stmt.condition = call_is_none; + if_break->if_stmt.then_body = break_stmt; + APPEND_STMT(if_break); - if (t.type == TOK_LPAREN) - { - depth++; - } - if (t.type == TOK_RPAREN) - { - depth--; - if (depth == 0) + // var = __opt.unwrap(); + ASTNode *user_var_decl = ast_create(NODE_VAR_DECL); + user_var_decl->var_decl.name = var_name; + user_var_decl->var_decl.type_str = NULL; + + // __opt.unwrap() + ASTNode *call_unwrap = ast_create(NODE_EXPR_CALL); + ASTNode *memb_unwrap = ast_create(NODE_EXPR_MEMBER); + ASTNode *opt_ref2 = ast_create(NODE_EXPR_VAR); + opt_ref2->var_ref.name = xstrdup("__opt"); + memb_unwrap->member.target = opt_ref2; + memb_unwrap->member.field = xstrdup("unwrap"); + call_unwrap->call.callee = memb_unwrap; + + user_var_decl->var_decl.init_expr = call_unwrap; + APPEND_STMT(user_var_decl); + + // User Body + enter_scope(ctx); + add_symbol(ctx, var_name, NULL, NULL); + + ASTNode *user_body_node; + if (lexer_peek(l).type == TOK_LBRACE) { - break; // End of potential tuple + user_body_node = parse_block(ctx, l); } - } + else + { + ASTNode *stmt = parse_statement(ctx, l); + ASTNode *blk = ast_create(NODE_BLOCK); + blk->block.statements = stmt; + user_body_node = blk; + } + exit_scope(ctx); - // If we find a comma at depth 1 (inside the first parens), it's a tuple - // literal! - if (depth == 1 && t.type == TOK_COMMA) - { - is_tuple_lit = 1; - break; - } - } + // Append user body statements to our loop body + APPEND_STMT(user_body_node); - if (is_tuple_lit) - { - char *code = parse_tuple_literal(ctx, l, curr_func_ret); - ASTNode *raw = ast_create(NODE_RAW_STMT); - raw->raw_stmt.content = code; - n->ret.value = raw; - handled = 1; + loop_body->block.statements = stmts_head; + while_loop->while_stmt.body = loop_body; + + // Wrap entire thing in a block to scope _it + ASTNode *outer_block = ast_create(NODE_BLOCK); + it_decl->next = while_loop; + outer_block->block.statements = it_decl; + + return outer_block; + } } + l->pos = saved_pos; // Restore } - // 2. Check for Array Literal Return: return [a, b]; - else if (curr_func_ret && strncmp(curr_func_ret, "Slice_", 6) == 0 && - lexer_peek(l).type == TOK_LBRACKET) + + // C-Style For Loop + enter_scope(ctx); + if (lexer_peek(l).type == TOK_LPAREN) { - char *code = parse_array_literal(ctx, l, curr_func_ret); - ASTNode *raw = ast_create(NODE_RAW_STMT); - raw->raw_stmt.content = code; - n->ret.value = raw; - handled = 1; + lexer_next(l); } - // 3. Standard Expression Return - if (!handled) + ASTNode *init = NULL; + if (lexer_peek(l).type != TOK_SEMICOLON) { - if (lexer_peek(l).type == TOK_SEMICOLON) + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "var", 3) == 0) { - n->ret.value = NULL; + init = parse_var_decl(ctx, l); } else { - n->ret.value = parse_expression(ctx, l); - check_move_usage(ctx, n->ret.value, n->ret.value ? n->ret.value->token : lexer_peek(l)); - - // Note: Returning a non-Copy variable effectively moves it out. - // We could mark it as moved, but scope ends anyway. - // The critical part is checking we aren't returning an ALREADY moved value. + init = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } } } + else + { + lexer_next(l); + } + ASTNode *cond = NULL; + if (lexer_peek(l).type != TOK_SEMICOLON) + { + cond = parse_expression(ctx, l); + } + else + { + // Empty condition = true + ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL); + true_lit->literal.type_kind = 0; + true_lit->literal.int_val = 1; + cond = true_lit; + } if (lexer_peek(l).type == TOK_SEMICOLON) { lexer_next(l); } - return n; -} -ASTNode *parse_if(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat if - ASTNode *cond = parse_expression(ctx, l); - check_assignment_condition(cond); + ASTNode *step = NULL; + if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) + { + step = parse_expression(ctx, l); + } - ASTNode *then_b = NULL; + if (lexer_peek(l).type == TOK_RPAREN) + { + lexer_next(l); + } + + ASTNode *body; if (lexer_peek(l).type == TOK_LBRACE) { - then_b = parse_block(ctx, l); + body = parse_block(ctx, l); } else { - // Single statement: Wrap in scope + block - enter_scope(ctx); - ASTNode *s = parse_statement(ctx, l); - exit_scope(ctx); - then_b = ast_create(NODE_BLOCK); - then_b->block.statements = s; + body = parse_statement(ctx, l); } + exit_scope(ctx); - ASTNode *else_b = NULL; - skip_comments(l); - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "else", 4) == 0) - { - lexer_next(l); - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0) - { - else_b = parse_if(ctx, l); - } - else if (lexer_peek(l).type == TOK_LBRACE) - { - else_b = parse_block(ctx, l); - } - else - { - // Single statement else - enter_scope(ctx); - ASTNode *s = parse_statement(ctx, l); - exit_scope(ctx); - else_b = ast_create(NODE_BLOCK); - else_b->block.statements = s; - } - } - ASTNode *n = ast_create(NODE_IF); - n->if_stmt.condition = cond; - n->if_stmt.then_body = then_b; - n->if_stmt.else_body = else_b; + ASTNode *n = ast_create(NODE_FOR); + n->for_stmt.init = init; + n->for_stmt.condition = cond; + n->for_stmt.step = step; + n->for_stmt.body = body; return n; } -ASTNode *parse_while(ParserContext *ctx, Lexer *l) +char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target, + char ***used_syms, int *count) { - lexer_next(l); - ASTNode *cond = parse_expression(ctx, l); - check_assignment_condition(cond); - - // Zen: While(true) - if ((cond->type == NODE_EXPR_LITERAL && cond->literal.type_kind == TOK_INT && - strcmp(cond->literal.string_val, "1") == 0) || - (cond->type == NODE_EXPR_VAR && strcmp(cond->var_ref.name, "true") == 0)) - { - zen_trigger_at(TRIGGER_WHILE_TRUE, cond->token); - } - ASTNode *body; - if (lexer_peek(l).type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else - { - body = parse_statement(ctx, l); - } - ASTNode *n = ast_create(NODE_WHILE); - n->while_stmt.condition = cond; - n->while_stmt.body = body; - return n; -} + char *gen = xmalloc(8192); + strcpy(gen, "({ "); -ASTNode *parse_for(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); + char *s = xstrdup(content); + char *cur = s; - // Range Loop: for i in 0..10 - if (lexer_peek(l).type == TOK_IDENT) - { - int saved_pos = l->pos; - Token var = lexer_next(l); - Token in_tok = lexer_next(l); - - if (in_tok.type == TOK_IDENT && strncmp(in_tok.start, "in", 2) == 0) - { - ASTNode *start_expr = parse_expression(ctx, l); - // Check for Range Loop (.. or ..= or ..<) - TokenType next_tok = lexer_peek(l).type; - if (next_tok == TOK_DOTDOT || next_tok == TOK_DOTDOT_LT || next_tok == TOK_DOTDOT_EQ) - { - int is_inclusive = 0; - if (next_tok == TOK_DOTDOT || next_tok == TOK_DOTDOT_LT) - { - lexer_next(l); // consume .. or ..< - } - else if (next_tok == TOK_DOTDOT_EQ) - { - is_inclusive = 1; - lexer_next(l); // consume ..= - } - - if (1) // Block to keep scope for variables - { - ASTNode *end_expr = parse_expression(ctx, l); - - ASTNode *n = ast_create(NODE_FOR_RANGE); - n->for_range.var_name = xmalloc(var.len + 1); - strncpy(n->for_range.var_name, var.start, var.len); - n->for_range.var_name[var.len] = 0; - n->for_range.start = start_expr; - n->for_range.end = end_expr; - n->for_range.is_inclusive = is_inclusive; - - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "step", 4) == 0) - { - lexer_next(l); - Token s_tok = lexer_next(l); - char *sval = xmalloc(s_tok.len + 1); - strncpy(sval, s_tok.start, s_tok.len); - sval[s_tok.len] = 0; - n->for_range.step = sval; - } - else - { - n->for_range.step = NULL; - } - - enter_scope(ctx); - add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT)); - - if (lexer_peek(l).type == TOK_LBRACE) - { - n->for_range.body = parse_block(ctx, l); - } - else - { - n->for_range.body = parse_statement(ctx, l); - } - exit_scope(ctx); - - return n; - } - } - else - { - // Iterator Loop: for x in obj - // Desugar to: - /* - { - var __it = obj.iterator(); - while (true) { - var __opt = __it.next(); - if (__opt.is_none()) break; - var x = __opt.unwrap(); - - } - } - */ - - char *var_name = xmalloc(var.len + 1); - strncpy(var_name, var.start, var.len); - var_name[var.len] = 0; - - ASTNode *obj_expr = start_expr; - char *iter_method = "iterator"; - - // Check for reference iteration: for x in &vec - if (obj_expr->type == NODE_EXPR_UNARY && obj_expr->unary.op && - strcmp(obj_expr->unary.op, "&") == 0) - { - obj_expr = obj_expr->unary.operand; - iter_method = "iter_ref"; - } - - // var __it = obj.iterator(); - ASTNode *it_decl = ast_create(NODE_VAR_DECL); - it_decl->var_decl.name = xstrdup("__it"); - it_decl->var_decl.type_str = NULL; // inferred - - // obj.iterator() or obj.iter_ref() - ASTNode *call_iter = ast_create(NODE_EXPR_CALL); - ASTNode *memb_iter = ast_create(NODE_EXPR_MEMBER); - memb_iter->member.target = obj_expr; - memb_iter->member.field = xstrdup(iter_method); - call_iter->call.callee = memb_iter; - call_iter->call.args = NULL; - call_iter->call.arg_count = 0; - - it_decl->var_decl.init_expr = call_iter; - - // while(true) - ASTNode *while_loop = ast_create(NODE_WHILE); - ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL); - true_lit->literal.type_kind = TOK_INT; // Treated as bool in conditions - true_lit->literal.int_val = 1; - true_lit->literal.string_val = xstrdup("1"); - while_loop->while_stmt.condition = true_lit; - - ASTNode *loop_body = ast_create(NODE_BLOCK); - ASTNode *stmts_head = NULL; - ASTNode *stmts_tail = NULL; - -#define APPEND_STMT(node) \ - if (!stmts_head) \ - { \ - stmts_head = node; \ - stmts_tail = node; \ - } \ - else \ - { \ - stmts_tail->next = node; \ - stmts_tail = node; \ - } - - // var __opt = __it.next(); - ASTNode *opt_decl = ast_create(NODE_VAR_DECL); - opt_decl->var_decl.name = xstrdup("__opt"); - opt_decl->var_decl.type_str = NULL; - - // __it.next() - ASTNode *call_next = ast_create(NODE_EXPR_CALL); - ASTNode *memb_next = ast_create(NODE_EXPR_MEMBER); - ASTNode *it_ref = ast_create(NODE_EXPR_VAR); - it_ref->var_ref.name = xstrdup("__it"); - memb_next->member.target = it_ref; - memb_next->member.field = xstrdup("next"); - call_next->call.callee = memb_next; - - opt_decl->var_decl.init_expr = call_next; - APPEND_STMT(opt_decl); - - // __opt.is_none() - ASTNode *call_is_none = ast_create(NODE_EXPR_CALL); - ASTNode *memb_is_none = ast_create(NODE_EXPR_MEMBER); - ASTNode *opt_ref1 = ast_create(NODE_EXPR_VAR); - opt_ref1->var_ref.name = xstrdup("__opt"); - memb_is_none->member.target = opt_ref1; - memb_is_none->member.field = xstrdup("is_none"); - call_is_none->call.callee = memb_is_none; - - ASTNode *break_stmt = ast_create(NODE_BREAK); - - ASTNode *if_break = ast_create(NODE_IF); - if_break->if_stmt.condition = call_is_none; - if_break->if_stmt.then_body = break_stmt; - APPEND_STMT(if_break); - - // var = __opt.unwrap(); - ASTNode *user_var_decl = ast_create(NODE_VAR_DECL); - user_var_decl->var_decl.name = var_name; - user_var_decl->var_decl.type_str = NULL; - - // __opt.unwrap() - ASTNode *call_unwrap = ast_create(NODE_EXPR_CALL); - ASTNode *memb_unwrap = ast_create(NODE_EXPR_MEMBER); - ASTNode *opt_ref2 = ast_create(NODE_EXPR_VAR); - opt_ref2->var_ref.name = xstrdup("__opt"); - memb_unwrap->member.target = opt_ref2; - memb_unwrap->member.field = xstrdup("unwrap"); - call_unwrap->call.callee = memb_unwrap; - - user_var_decl->var_decl.init_expr = call_unwrap; - APPEND_STMT(user_var_decl); - - // User Body - enter_scope(ctx); - add_symbol(ctx, var_name, NULL, NULL); - - ASTNode *user_body_node; - if (lexer_peek(l).type == TOK_LBRACE) - { - user_body_node = parse_block(ctx, l); - } - else - { - ASTNode *stmt = parse_statement(ctx, l); - ASTNode *blk = ast_create(NODE_BLOCK); - blk->block.statements = stmt; - user_body_node = blk; - } - exit_scope(ctx); - - // Append user body statements to our loop body - APPEND_STMT(user_body_node); - - loop_body->block.statements = stmts_head; - while_loop->while_stmt.body = loop_body; - - // Wrap entire thing in a block to scope _it - ASTNode *outer_block = ast_create(NODE_BLOCK); - it_decl->next = while_loop; - outer_block->block.statements = it_decl; - - return outer_block; - } - } - l->pos = saved_pos; // Restore - } - - // C-Style For Loop - enter_scope(ctx); - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - } - - ASTNode *init = NULL; - if (lexer_peek(l).type != TOK_SEMICOLON) - { - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "var", 3) == 0) - { - init = parse_var_decl(ctx, l); - } - else - { - init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - } - else - { - lexer_next(l); - } - - ASTNode *cond = NULL; - if (lexer_peek(l).type != TOK_SEMICOLON) - { - cond = parse_expression(ctx, l); - } - else - { - // Empty condition = true - ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL); - true_lit->literal.type_kind = 0; - true_lit->literal.int_val = 1; - cond = true_lit; - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *step = NULL; - if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) - { - step = parse_expression(ctx, l); - } - - if (lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); - } - - ASTNode *body; - if (lexer_peek(l).type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else - { - body = parse_statement(ctx, l); - } - exit_scope(ctx); - - ASTNode *n = ast_create(NODE_FOR); - n->for_stmt.init = init; - n->for_stmt.condition = cond; - n->for_stmt.step = step; - n->for_stmt.body = body; - return n; -} - -char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target, - char ***used_syms, int *count) -{ - char *gen = xmalloc(8192); - strcpy(gen, "({ "); - - char *s = xstrdup(content); - char *cur = s; - - while (*cur) + while (*cur) { // 1. Find text before the next '{' char *brace = cur; while (*brace && *brace != '{') { - brace++; - } - - if (brace > cur) - { - // Append text literal - char buf[256]; - sprintf(buf, "fprintf(%s, \"%%s\", \"", target); - strcat(gen, buf); - strncat(gen, cur, brace - cur); - strcat(gen, "\"); "); - } - - if (*brace == 0) - { - break; - } - - // 2. Handle {expression} - char *p = brace + 1; - char *colon = NULL; - int depth = 1; - while (*p && depth > 0) - { - if (*p == '{') - { - depth++; - } - if (*p == '}') - { - depth--; - } - if (depth == 1 && *p == ':' && !colon) - { - if (*(p + 1) == ':') - { - p++; - } - else - { - colon = p; - } - } - if (depth == 0) - { - break; - } - p++; - } - - *p = 0; // Terminate expression - char *expr = brace + 1; - - // Unescape \" to " in the expression code to ensure correct parsing - char *read = expr; - char *write = expr; - while (*read) - { - if (*read == '\\' && *(read + 1) == '"') - { - *write = '"'; - read += 2; - write++; - } - else - { - *write = *read; - read++; - write++; - } - } - *write = 0; - char *fmt = NULL; - if (colon) - { - *colon = 0; - fmt = colon + 1; - } - - char *clean_expr = expr; - while (*clean_expr == ' ') - { - clean_expr++; // Skip leading spaces - } - - // Analyze usage & Type Check for to_string() - char *final_expr = xstrdup(clean_expr); - - // Use final_expr in usage analysis if needed, but mainly for symbol tracking - { - Lexer lex; - lexer_init(&lex, clean_expr); // Scan original for symbols - Token t; - while ((t = lexer_next(&lex)).type != TOK_EOF) - { - if (t.type == TOK_IDENT) - { - char *name = token_strdup(t); - Symbol *sym = find_symbol_entry(ctx, name); - if (sym) - { - sym->is_used = 1; - } - - if (used_syms && count) - { - *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1)); - (*used_syms)[*count] = name; - (*count)++; - } - else - { - free(name); - } - } - } - } - - expr = final_expr; - char *allocated_expr = NULL; - clean_expr = final_expr; - - int skip_rewrite = 0; - - // Check if struct and has to_string (Robust Logic) - { - Lexer lex; - lexer_init(&lex, clean_expr); - // Parse using temporary lexer to check type - ASTNode *expr_node = parse_expression(ctx, &lex); - - if (expr_node && expr_node->type_info) - { - Type *t = expr_node->type_info; - char *struct_name = NULL; - int is_ptr = 0; - - if (t->kind == TYPE_STRUCT) - { - struct_name = t->name; - } - else if (t->kind == TYPE_POINTER && t->inner && t->inner->kind == TYPE_STRUCT) - { - struct_name = t->inner->name; - is_ptr = 1; - } - - if (struct_name) - { - char mangled[256]; - sprintf(mangled, "%s__to_string", struct_name); - if (find_func(ctx, mangled)) - { - char *inner_wrapped = xmalloc(strlen(clean_expr) + 5); - sprintf(inner_wrapped, "#{%s}", clean_expr); - char *inner_c = rewrite_expr_methods(ctx, inner_wrapped); - free(inner_wrapped); - - // Now wrap in to_string call using C99 compound literal for safety - char *new_expr = xmalloc(strlen(inner_c) + strlen(mangled) + 64); - if (is_ptr) - { - sprintf(new_expr, "%s(%s)", mangled, inner_c); - } - else - { - sprintf(new_expr, "%s(({ %s _z_tmp = (%s); &_z_tmp; }))", mangled, - struct_name, inner_c); - } - - if (expr != s) - { - free(expr); // Free if explicitly allocated - } - expr = new_expr; - skip_rewrite = 1; // Don't rewrite again on the C99 syntax - } - } - } - } - - // Rewrite the expression to handle pointer access (header_ptr.magic -> - // header_ptr->magic) - char *rw_expr; - if (skip_rewrite) - { - rw_expr = xstrdup(expr); - } - else - { - char *wrapped_expr = xmalloc(strlen(expr) + 5); - sprintf(wrapped_expr, "#{%s}", expr); - rw_expr = rewrite_expr_methods(ctx, wrapped_expr); - free(wrapped_expr); - } - - if (fmt) - { - // Explicit format: {x:%.2f} - char buf[128]; - sprintf(buf, "fprintf(%s, \"%%", target); - strcat(gen, buf); - strcat(gen, fmt); - strcat(gen, "\", "); - strcat(gen, rw_expr); // Use rewritten expr - strcat(gen, "); "); - } - else - { - // Auto-detect format based on type if possible - const char *format_spec = NULL; - char *inferred_type = find_symbol_type(ctx, clean_expr); // Simple variable lookup - - // Basic Type Mappings - if (inferred_type) - { - if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0 || - strcmp(inferred_type, "bool") == 0) - { - format_spec = "%d"; - } - else if (strcmp(inferred_type, "long") == 0 || strcmp(inferred_type, "i64") == 0 || - strcmp(inferred_type, "isize") == 0) - { - format_spec = "%ld"; - } - else if (strcmp(inferred_type, "usize") == 0 || strcmp(inferred_type, "u64") == 0) - { - format_spec = "%lu"; - } - else if (strcmp(inferred_type, "float") == 0 || strcmp(inferred_type, "f32") == 0 || - strcmp(inferred_type, "double") == 0) - { - format_spec = "%f"; - } - else if (strcmp(inferred_type, "char") == 0 || strcmp(inferred_type, "byte") == 0) - { - format_spec = "%c"; - } - else if (strcmp(inferred_type, "string") == 0 || - strcmp(inferred_type, "str") == 0 || - (inferred_type[strlen(inferred_type) - 1] == '*' && - strstr(inferred_type, "char"))) - { - format_spec = "%s"; - } - else if (strstr(inferred_type, "*")) - { - format_spec = "%p"; // Pointer - } - } - - // Check for Literals if variable lookup failed - if (!format_spec) - { - if (isdigit(clean_expr[0]) || clean_expr[0] == '-') - { - format_spec = "%d"; // Naive integer guess (could be float) - } - else if (clean_expr[0] == '"') - { - format_spec = "%s"; - } - else if (clean_expr[0] == '\'') - { - format_spec = "%c"; - } - } - - if (format_spec) - { - char buf[128]; - sprintf(buf, "fprintf(%s, \"", target); - strcat(gen, buf); - strcat(gen, format_spec); - strcat(gen, "\", "); - strcat(gen, rw_expr); - strcat(gen, "); "); - } - else - { - // Fallback to runtime macro - char buf[128]; - sprintf(buf, "fprintf(%s, _z_str(", target); - strcat(gen, buf); - strcat(gen, rw_expr); - strcat(gen, "), "); - strcat(gen, rw_expr); - strcat(gen, "); "); - } - } - - free(rw_expr); // Don't forget to free! - if (allocated_expr) - { - free(allocated_expr); // Don't forget to free the auto-generated call! - } - - cur = p + 1; - } - - if (newline) - { - char buf[128]; - sprintf(buf, "fprintf(%s, \"\\n\"); ", target); - strcat(gen, buf); - } - else - { - strcat(gen, "fflush(stdout); "); - } - - strcat(gen, "0; })"); - - free(s); - return gen; -} - -ASTNode *parse_macro_call(ParserContext *ctx, Lexer *l, char *macro_name) -{ - Token start_tok = lexer_peek(l); - if (lexer_peek(l).type != TOK_OP || lexer_peek(l).start[0] != '!') - { - return NULL; - } - lexer_next(l); // consume ! - - // Expect { - if (lexer_peek(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected { after macro invocation"); - } - lexer_next(l); // consume { - - // Collect body until } - char *body = xmalloc(8192); - body[0] = '\0'; - int body_len = 0; - int depth = 1; - int last_line = start_tok.line; - - while (depth > 0) - { - Token t = lexer_peek(l); - if (t.type == TOK_EOF) - { - zpanic_at(t, "Unexpected EOF in macro block"); - } - - if (t.type == TOK_LBRACE) - { - depth++; - } - if (t.type == TOK_RBRACE) - { - depth--; - } - - if (depth > 0) - { - if (body_len + t.len + 2 < 8192) - { - // Preserve newlines - if (t.line > last_line) - { - body[body_len] = '\n'; - body[body_len + 1] = 0; - body_len++; - } - else - { - body[body_len] = ' '; - body[body_len + 1] = 0; - body_len++; - } - - strncat(body, t.start, t.len); - body_len += t.len; - } - } - - last_line = t.line; - lexer_next(l); - } - - // Resolve plugin name - const char *plugin_name = resolve_plugin(ctx, macro_name); - if (!plugin_name) - { - char err[256]; - snprintf(err, sizeof(err), "Unknown plugin: %s (did you forget 'import plugin \"%s\"'?)", - macro_name, macro_name); - zpanic_at(start_tok, err); - } - - // Find Plugin Definition - // Verify plugin exists - ZPlugin *found = zptr_find_plugin(plugin_name); - - if (!found) - { - char err[256]; - snprintf(err, sizeof(err), "Plugin implementation not found: %s", plugin_name); - zpanic_at(start_tok, err); - } - - // Execute Plugin Immediately (Expansion) - FILE *capture = tmpfile(); - if (!capture) - { - zpanic_at(start_tok, "Failed to create capture buffer for plugin expansion"); - } - - ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", - .current_line = start_tok.line, - .out = capture, - .hoist_out = ctx->hoist_out}; - - found->fn(body, &api); - - // Read captured output - long len = ftell(capture); - rewind(capture); - char *expanded_code = xmalloc(len + 1); - fread(expanded_code, 1, len, capture); - expanded_code[len] = 0; - fclose(capture); - free(body); - - // Create Raw Statement/Expression Node - ASTNode *n = ast_create(NODE_RAW_STMT); - n->line = start_tok.line; - n->raw_stmt.content = expanded_code; - - return n; -} - -ASTNode *parse_statement(ParserContext *ctx, Lexer *l) -{ - Token tk = lexer_peek(l); - - ASTNode *s = NULL; - - if (tk.type == TOK_SEMICOLON) - { - lexer_next(l); - ASTNode *nop = ast_create(NODE_BLOCK); // Empty block as NOP - nop->block.statements = NULL; - return nop; - } - - if (tk.type == TOK_PREPROC) - { - lexer_next(l); // consume token - char *content = xmalloc(tk.len + 2); - strncpy(content, tk.start, tk.len); - content[tk.len] = '\n'; // Ensure newline - content[tk.len + 1] = 0; - ASTNode *s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = content; - return s; - } - - if (tk.type == TOK_STRING || tk.type == TOK_FSTRING) - { - Lexer lookahead = *l; - lexer_next(&lookahead); - TokenType next_type = lexer_peek(&lookahead).type; - - if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT) - { - Token t = lexer_next(l); // consume string - - char *inner = xmalloc(t.len); - // Strip quotes - if (t.type == TOK_FSTRING) - { - strncpy(inner, t.start + 2, t.len - 3); - inner[t.len - 3] = 0; - } - else - { - strncpy(inner, t.start + 1, t.len - 2); - inner[t.len - 2] = 0; - } - - // ; means println (end of line), .. means print (continuation) - int is_ln = (next_type == TOK_SEMICOLON); - char **used_syms = NULL; - int used_count = 0; - char *code = process_printf_sugar(ctx, inner, is_ln, "stdout", &used_syms, &used_count); - - if (next_type == TOK_SEMICOLON) - { - lexer_next(l); // consume ; - } - else if (next_type == TOK_DOTDOT) - { - lexer_next(l); // consume .. - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); // consume optional ; - } - } - - ASTNode *n = ast_create(NODE_RAW_STMT); - n->raw_stmt.content = code; - n->raw_stmt.used_symbols = used_syms; - n->raw_stmt.used_symbol_count = used_count; - free(inner); - return n; - } - } - - // Block - if (tk.type == TOK_LBRACE) - { - return parse_block(ctx, l); - } - - // Keywords / Special - if (tk.type == TOK_TRAIT) - { - return parse_trait(ctx, l); - } - if (tk.type == TOK_IMPL) - { - return parse_impl(ctx, l); - } - if (tk.type == TOK_AUTOFREE) - { - lexer_next(l); - if (lexer_peek(l).type != TOK_IDENT || strncmp(lexer_peek(l).start, "var", 3) != 0) - { - zpanic_at(lexer_peek(l), "Expected 'var' after autofree"); - } - s = parse_var_decl(ctx, l); - s->var_decl.is_autofree = 1; - // Mark symbol as autofree to suppress unused variable warning - Symbol *sym = find_symbol_entry(ctx, s->var_decl.name); - if (sym) - { - sym->is_autofree = 1; - } - return s; - } - if (tk.type == TOK_TEST) - { - return parse_test(ctx, l); - } - if (tk.type == TOK_COMPTIME) - { - char *src = run_comptime_block(ctx, l); - Lexer new_l; - lexer_init(&new_l, src); - ASTNode *head = NULL, *tail = NULL; - - while (lexer_peek(&new_l).type != TOK_EOF) - { - ASTNode *s = parse_statement(ctx, &new_l); - if (!s) - { - break; - } - if (!head) - { - head = s; - } - else - { - tail->next = s; - } - tail = s; - while (tail->next) - { - tail = tail->next; - } - } - - if (head && !head->next) - { - return head; - } - - ASTNode *b = ast_create(NODE_BLOCK); - b->block.statements = head; - return b; - } - if (tk.type == TOK_ASSERT) - { - return parse_assert(ctx, l); - } - if (tk.type == TOK_DEFER) - { - return parse_defer(ctx, l); - } - if (tk.type == TOK_ASM) - { - return parse_asm(ctx, l); - } - - // Identifiers (Keywords or Expressions) - if (tk.type == TOK_IDENT) - { - // Check for macro invocation: identifier! { code } - Lexer lookahead = *l; - lexer_next(&lookahead); - Token exclaim = lexer_peek(&lookahead); - lexer_next(&lookahead); - Token lbrace = lexer_peek(&lookahead); - if (exclaim.type == TOK_OP && exclaim.len == 1 && exclaim.start[0] == '!' && - lbrace.type == TOK_LBRACE) - { - // This is a macro invocation - char *macro_name = token_strdup(tk); - lexer_next(l); // consume identifier - - ASTNode *n = parse_macro_call(ctx, l, macro_name); - free(macro_name); - return n; - } - - // Check for raw blocks - if (strncmp(tk.start, "raw", 3) == 0 && tk.len == 3) - { - lexer_next(l); // eat raw - if (lexer_peek(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected { after raw"); - } - lexer_next(l); // eat { - - const char *start = l->src + l->pos; - int depth = 1; - while (depth > 0) - { - Token t = lexer_next(l); - if (t.type == TOK_EOF) - { - zpanic_at(t, "Unexpected EOF in raw block"); - } - if (t.type == TOK_LBRACE) - { - depth++; - } - if (t.type == TOK_RBRACE) - { - depth--; - } - } - const char *end = l->src + l->pos - 1; - size_t len = end - start; - - char *content = xmalloc(len + 1); - memcpy(content, start, len); - content[len] = 0; - - ASTNode *s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = content; - return s; - } - - // Check for plugin blocks - if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) - { - lexer_next(l); // consume 'plugin' - return parse_plugin(ctx, l); - } - - if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) - { - return parse_var_decl(ctx, l); - } - - // Static local variable: static var x = 0; - if (strncmp(tk.start, "static", 6) == 0 && tk.len == 6) - { - lexer_next(l); // eat 'static' - Token next = lexer_peek(l); - if (strncmp(next.start, "var", 3) == 0 && next.len == 3) - { - ASTNode *v = parse_var_decl(ctx, l); - v->var_decl.is_static = 1; - return v; - } - zpanic_at(next, "Expected 'var' after 'static'"); - } - if (strncmp(tk.start, "const", 5) == 0 && tk.len == 5) - { - return parse_const(ctx, l); - } - if (strncmp(tk.start, "return", 6) == 0 && tk.len == 6) - { - return parse_return(ctx, l); - } - if (strncmp(tk.start, "if", 2) == 0 && tk.len == 2) - { - return parse_if(ctx, l); - } - if (strncmp(tk.start, "while", 5) == 0 && tk.len == 5) - { - return parse_while(ctx, l); - } - if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3) - { - return parse_for(ctx, l); - } - if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5) - { - return parse_match(ctx, l); - } - - // Break with optional label: break; or break 'outer; - if (strncmp(tk.start, "break", 5) == 0 && tk.len == 5) - { - Token break_token = lexer_next(l); - - // Error if break is used inside a defer block - if (ctx->in_defer_block) - { - zpanic_at(break_token, "'break' is not allowed inside a 'defer' block"); - } - - ASTNode *n = ast_create(NODE_BREAK); - n->token = break_token; - n->break_stmt.target_label = NULL; - // Check for 'label - if (lexer_peek(l).type == TOK_CHAR) - { - Token label_tok = lexer_next(l); - // Extract label name (strip quotes) - char *label = xmalloc(label_tok.len); - strncpy(label, label_tok.start + 1, label_tok.len - 2); - label[label_tok.len - 2] = 0; - n->break_stmt.target_label = label; - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - return n; - } - - // Continue with optional label - if (strncmp(tk.start, "continue", 8) == 0 && tk.len == 8) - { - Token continue_token = lexer_next(l); - - // Error if continue is used inside a defer block - if (ctx->in_defer_block) - { - zpanic_at(continue_token, "'continue' is not allowed inside a 'defer' block"); - } - - ASTNode *n = ast_create(NODE_CONTINUE); - n->token = continue_token; - n->continue_stmt.target_label = NULL; - if (lexer_peek(l).type == TOK_CHAR) - { - Token label_tok = lexer_next(l); - char *label = xmalloc(label_tok.len); - strncpy(label, label_tok.start + 1, label_tok.len - 2); - label[label_tok.len - 2] = 0; - n->continue_stmt.target_label = label; - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - return n; - } - - if (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4) - { - return parse_loop(ctx, l); - } - if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6) - { - return parse_repeat(ctx, l); + brace++; } - if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6) + + if (brace > cur) { - return parse_unless(ctx, l); + // Append text literal + char buf[256]; + sprintf(buf, "fprintf(%s, \"%%s\", \"", target); + strcat(gen, buf); + strncat(gen, cur, brace - cur); + strcat(gen, "\"); "); } - if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5) + + if (*brace == 0) { - return parse_guard(ctx, l); + break; } - // CUDA launch: launch kernel(args) with { grid: X, block: Y }; - if (strncmp(tk.start, "launch", 6) == 0 && tk.len == 6) + // 2. Handle {expression} + char *p = brace + 1; + char *colon = NULL; + int depth = 1; + while (*p && depth > 0) { - Token launch_tok = lexer_next(l); // eat 'launch' - - // Parse the kernel call expression - ASTNode *call = parse_expression(ctx, l); - if (!call || call->type != NODE_EXPR_CALL) - { - zpanic_at(launch_tok, "Expected kernel call after 'launch'"); - } - - // Expect 'with' - Token with_tok = lexer_peek(l); - if (with_tok.type != TOK_IDENT || strncmp(with_tok.start, "with", 4) != 0 || - with_tok.len != 4) + if (*p == '{') { - zpanic_at(with_tok, "Expected 'with' after kernel call in launch statement"); + depth++; } - lexer_next(l); // eat 'with' - - // Expect '{' for configuration block - if (lexer_peek(l).type != TOK_LBRACE) + if (*p == '}') { - zpanic_at(lexer_peek(l), "Expected '{' after 'with' in launch statement"); + depth--; } - lexer_next(l); // eat '{' - - ASTNode *grid = NULL; - ASTNode *block = NULL; - ASTNode *shared_mem = NULL; - ASTNode *stream = NULL; - - // Parse configuration fields - while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_EOF) + if (depth == 1 && *p == ':' && !colon) { - Token field_name = lexer_next(l); - if (field_name.type != TOK_IDENT) - { - zpanic_at(field_name, "Expected field name in launch configuration"); - } - - // Expect ':' - if (lexer_peek(l).type != TOK_COLON) - { - zpanic_at(lexer_peek(l), "Expected ':' after field name"); - } - lexer_next(l); // eat ':' - - // Parse value expression - ASTNode *value = parse_expression(ctx, l); - - // Assign to appropriate field - if (strncmp(field_name.start, "grid", 4) == 0 && field_name.len == 4) - { - grid = value; - } - else if (strncmp(field_name.start, "block", 5) == 0 && field_name.len == 5) - { - block = value; - } - else if (strncmp(field_name.start, "shared_mem", 10) == 0 && field_name.len == 10) - { - shared_mem = value; - } - else if (strncmp(field_name.start, "stream", 6) == 0 && field_name.len == 6) + if (*(p + 1) == ':') { - stream = value; + p++; } else { - zpanic_at(field_name, "Unknown launch configuration field (expected: grid, " - "block, shared_mem, stream)"); - } - - // Optional comma - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); + colon = p; } } - - // Expect '}' - if (lexer_peek(l).type != TOK_RBRACE) + if (depth == 0) { - zpanic_at(lexer_peek(l), "Expected '}' to close launch configuration"); + break; } - lexer_next(l); // eat '}' + p++; + } - // Expect ';' - if (lexer_peek(l).type == TOK_SEMICOLON) + *p = 0; // Terminate expression + char *expr = brace + 1; + + // Unescape \" to " in the expression code to ensure correct parsing + char *read = expr; + char *write = expr; + while (*read) + { + if (*read == '\\' && *(read + 1) == '"') { - lexer_next(l); + *write = '"'; + read += 2; + write++; } - - // Require at least grid and block - if (!grid || !block) + else { - zpanic_at(launch_tok, "Launch configuration requires at least 'grid' and 'block'"); + *write = *read; + read++; + write++; } - - ASTNode *n = ast_create(NODE_CUDA_LAUNCH); - n->cuda_launch.call = call; - n->cuda_launch.grid = grid; - n->cuda_launch.block = block; - n->cuda_launch.shared_mem = shared_mem; - n->cuda_launch.stream = stream; - n->token = launch_tok; - return n; + } + *write = 0; + char *fmt = NULL; + if (colon) + { + *colon = 0; + fmt = colon + 1; } - // Do-while loop: do { body } while condition; - if (strncmp(tk.start, "do", 2) == 0 && tk.len == 2) + char *clean_expr = expr; + while (*clean_expr == ' ') { - lexer_next(l); // eat 'do' - ASTNode *body = parse_block(ctx, l); + clean_expr++; // Skip leading spaces + } - // Expect 'while' - Token while_tok = lexer_peek(l); - if (while_tok.type != TOK_IDENT || strncmp(while_tok.start, "while", 5) != 0 || - while_tok.len != 5) + // Analyze usage & Type Check for to_string() + char *final_expr = xstrdup(clean_expr); + + // Use final_expr in usage analysis if needed, but mainly for symbol tracking + { + Lexer lex; + lexer_init(&lex, clean_expr); // Scan original for symbols + Token t; + while ((t = lexer_next(&lex)).type != TOK_EOF) { - zpanic_at(while_tok, "Expected 'while' after do block"); + if (t.type == TOK_IDENT) + { + char *name = token_strdup(t); + Symbol *sym = find_symbol_entry(ctx, name); + if (sym) + { + sym->is_used = 1; + } + + if (used_syms && count) + { + *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1)); + (*used_syms)[*count] = name; + (*count)++; + } + else + { + free(name); + } + } } - lexer_next(l); // eat 'while' + } - ASTNode *cond = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) + expr = final_expr; + char *allocated_expr = NULL; + clean_expr = final_expr; + + int skip_rewrite = 0; + + // Check if struct and has to_string (Robust Logic) + { + Lexer lex; + lexer_init(&lex, clean_expr); + // Parse using temporary lexer to check type + ASTNode *expr_node = parse_expression(ctx, &lex); + + if (expr_node && expr_node->type_info) { - lexer_next(l); - } + Type *t = expr_node->type_info; + char *struct_name = NULL; + int is_ptr = 0; - ASTNode *n = ast_create(NODE_DO_WHILE); - n->do_while_stmt.body = body; - n->do_while_stmt.condition = cond; - n->do_while_stmt.loop_label = NULL; - return n; + if (t->kind == TYPE_STRUCT) + { + struct_name = t->name; + } + else if (t->kind == TYPE_POINTER && t->inner && t->inner->kind == TYPE_STRUCT) + { + struct_name = t->inner->name; + is_ptr = 1; + } + + if (struct_name) + { + char mangled[256]; + sprintf(mangled, "%s__to_string", struct_name); + if (find_func(ctx, mangled)) + { + char *inner_wrapped = xmalloc(strlen(clean_expr) + 5); + sprintf(inner_wrapped, "#{%s}", clean_expr); + char *inner_c = rewrite_expr_methods(ctx, inner_wrapped); + free(inner_wrapped); + + // Now wrap in to_string call using C99 compound literal for safety + char *new_expr = xmalloc(strlen(inner_c) + strlen(mangled) + 64); + if (is_ptr) + { + sprintf(new_expr, "%s(%s)", mangled, inner_c); + } + else + { + sprintf(new_expr, "%s(({ %s _z_tmp = (%s); &_z_tmp; }))", mangled, + struct_name, inner_c); + } + + if (expr != s) + { + free(expr); // Free if explicitly allocated + } + expr = new_expr; + skip_rewrite = 1; // Don't rewrite again on the C99 syntax + } + } + } } - if (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) + // Rewrite the expression to handle pointer access (header_ptr.magic -> + // header_ptr->magic) + char *rw_expr; + if (skip_rewrite) { - return parse_defer(ctx, l); + rw_expr = xstrdup(expr); } - - // Goto statement: goto label_name; OR goto *expr; (computed goto) - if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4) + else { - Token goto_tok = lexer_next(l); // eat 'goto' - - // Error if goto is used inside a defer block - if (ctx->in_defer_block) - { - zpanic_at(goto_tok, "'goto' is not allowed inside a 'defer' block"); - } + char *wrapped_expr = xmalloc(strlen(expr) + 5); + sprintf(wrapped_expr, "#{%s}", expr); + rw_expr = rewrite_expr_methods(ctx, wrapped_expr); + free(wrapped_expr); + } - Token next = lexer_peek(l); + if (fmt) + { + // Explicit format: {x:%.2f} + char buf[128]; + sprintf(buf, "fprintf(%s, \"%%", target); + strcat(gen, buf); + strcat(gen, fmt); + strcat(gen, "\", "); + strcat(gen, rw_expr); // Use rewritten expr + strcat(gen, "); "); + } + else + { + // Auto-detect format based on type if possible + const char *format_spec = NULL; + char *inferred_type = find_symbol_type(ctx, clean_expr); // Simple variable lookup - // Computed goto: goto *ptr; - if (next.type == TOK_OP && next.start[0] == '*') + // Basic Type Mappings + if (inferred_type) { - lexer_next(l); // eat '*' - ASTNode *target = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) + if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0 || + strcmp(inferred_type, "bool") == 0) { - lexer_next(l); + format_spec = "%d"; } - - ASTNode *n = ast_create(NODE_GOTO); - n->goto_stmt.label_name = NULL; - n->goto_stmt.goto_expr = target; - n->token = goto_tok; - return n; - } - - // Regular goto - Token label = lexer_next(l); - if (label.type != TOK_IDENT) - { - zpanic_at(label, "Expected label name after goto"); - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - ASTNode *n = ast_create(NODE_GOTO); - n->goto_stmt.label_name = token_strdup(label); - n->token = goto_tok; - zen_trigger_at(TRIGGER_GOTO, goto_tok); - return n; - } - - // Label detection: identifier followed by : (but not ::) - { - Lexer lookahead = *l; - Token ident = lexer_next(&lookahead); - Token maybe_colon = lexer_peek(&lookahead); - if (maybe_colon.type == TOK_COLON) - { - // Check it's not :: (double colon for namespaces) - lexer_next(&lookahead); - Token after_colon = lexer_peek(&lookahead); - if (after_colon.type != TOK_COLON) + else if (strcmp(inferred_type, "long") == 0 || strcmp(inferred_type, "i64") == 0 || + strcmp(inferred_type, "isize") == 0) { - // This is a label! - lexer_next(l); // eat identifier - lexer_next(l); // eat : - ASTNode *n = ast_create(NODE_LABEL); - n->label_stmt.label_name = token_strdup(ident); - n->token = ident; - return n; + format_spec = "%ld"; + } + else if (strcmp(inferred_type, "usize") == 0 || strcmp(inferred_type, "u64") == 0) + { + format_spec = "%lu"; + } + else if (strcmp(inferred_type, "float") == 0 || strcmp(inferred_type, "f32") == 0 || + strcmp(inferred_type, "double") == 0) + { + format_spec = "%f"; + } + else if (strcmp(inferred_type, "char") == 0 || strcmp(inferred_type, "byte") == 0) + { + format_spec = "%c"; + } + else if (strcmp(inferred_type, "string") == 0 || + strcmp(inferred_type, "str") == 0 || + (inferred_type[strlen(inferred_type) - 1] == '*' && + strstr(inferred_type, "char"))) + { + format_spec = "%s"; + } + else if (strstr(inferred_type, "*")) + { + format_spec = "%p"; // Pointer } } - } - - if ((strncmp(tk.start, "print", 5) == 0 && tk.len == 5) || - (strncmp(tk.start, "println", 7) == 0 && tk.len == 7) || - (strncmp(tk.start, "eprint", 6) == 0 && tk.len == 6) || - (strncmp(tk.start, "eprintln", 8) == 0 && tk.len == 8)) - { - - // Revert: User requested print without newline - int is_ln = (tk.len == 7 || tk.len == 8); - // int is_ln = (tk.len == 7 || tk.len == 8); - int is_err = (tk.start[0] == 'e'); - char *target = is_err ? "stderr" : "stdout"; - - lexer_next(l); // eat keyword - Token t = lexer_next(l); - if (t.type != TOK_STRING && t.type != TOK_FSTRING) + // Check for Literals if variable lookup failed + if (!format_spec) { - zpanic_at(t, "Expected string literal after print/eprint"); + if (isdigit(clean_expr[0]) || clean_expr[0] == '-') + { + format_spec = "%d"; // Naive integer guess (could be float) + } + else if (clean_expr[0] == '"') + { + format_spec = "%s"; + } + else if (clean_expr[0] == '\'') + { + format_spec = "%c"; + } } - char *inner = xmalloc(t.len); - if (t.type == TOK_FSTRING) + if (format_spec) { - strncpy(inner, t.start + 2, t.len - 3); - inner[t.len - 3] = 0; + char buf[128]; + sprintf(buf, "fprintf(%s, \"", target); + strcat(gen, buf); + strcat(gen, format_spec); + strcat(gen, "\", "); + strcat(gen, rw_expr); + strcat(gen, "); "); } else { - strncpy(inner, t.start + 1, t.len - 2); - inner[t.len - 2] = 0; - } - - char **used_syms = NULL; - int used_count = 0; - char *code = process_printf_sugar(ctx, inner, is_ln, target, &used_syms, &used_count); - free(inner); - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); + // Fallback to runtime macro + char buf[128]; + sprintf(buf, "fprintf(%s, _z_str(", target); + strcat(gen, buf); + strcat(gen, rw_expr); + strcat(gen, "), "); + strcat(gen, rw_expr); + strcat(gen, "); "); } - - ASTNode *n = ast_create(NODE_RAW_STMT); - n->raw_stmt.content = code; - n->raw_stmt.used_symbols = used_syms; - n->raw_stmt.used_symbol_count = used_count; - return n; } - } - - // Default: Expression Statement - s = parse_expression(ctx, l); - - int has_semi = 0; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - has_semi = 1; - } - // Auto-print in REPL: If no semicolon (implicit expr at block end) - // and not an assignment, print it. - if (ctx->is_repl && s && !has_semi) - { - int is_assign = 0; - if (s->type == NODE_EXPR_BINARY) + free(rw_expr); // Don't forget to free! + if (allocated_expr) { - char *op = s->binary.op; - if (strcmp(op, "=") == 0 || - (strlen(op) > 1 && op[strlen(op) - 1] == '=' && strcmp(op, "==") != 0 && - strcmp(op, "!=") != 0 && strcmp(op, "<=") != 0 && strcmp(op, ">=") != 0)) - { - is_assign = 1; - } + free(allocated_expr); // Don't forget to free the auto-generated call! } - if (!is_assign) - { - ASTNode *print_node = ast_create(NODE_REPL_PRINT); - print_node->repl_print.expr = s; - // Preserve line info - print_node->line = s->line; - print_node->token = s->token; - return print_node; - } + cur = p + 1; } - if (s) + if (newline) { - s->line = tk.line; + char buf[128]; + sprintf(buf, "fprintf(%s, \"\\n\"); ", target); + strcat(gen, buf); } - - // Check for discarded must_use result - if (s && s->type == NODE_EXPR_CALL) + else { - ASTNode *callee = s->call.callee; - if (callee && callee->type == NODE_EXPR_VAR) - { - FuncSig *sig = find_func(ctx, callee->var_ref.name); - if (sig && sig->must_use) - { - zwarn_at(tk, "Ignoring return value of function marked @must_use"); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET - "Use the result or explicitly discard with `_ = ...`\n"); - } - } + strcat(gen, "fflush(stdout); "); } - return s; + strcat(gen, "0; })"); + + free(s); + return gen; } -ASTNode *parse_block(ParserContext *ctx, Lexer *l) +ASTNode *parse_macro_call(ParserContext *ctx, Lexer *l, char *macro_name) { - lexer_next(l); // eat '{' - enter_scope(ctx); - ASTNode *head = 0, *tail = 0; + Token start_tok = lexer_peek(l); + if (lexer_peek(l).type != TOK_OP || lexer_peek(l).start[0] != '!') + { + return NULL; + } + lexer_next(l); // consume ! - int unreachable = 0; + // Expect { + if (lexer_peek(l).type != TOK_LBRACE) + { + zpanic_at(lexer_peek(l), "Expected { after macro invocation"); + } + lexer_next(l); // consume { - while (1) + // Collect body until } + char *body = xmalloc(8192); + body[0] = '\0'; + int body_len = 0; + int depth = 1; + int last_line = start_tok.line; + + while (depth > 0) { - skip_comments(l); - Token tk = lexer_peek(l); - if (tk.type == TOK_RBRACE) + Token t = lexer_peek(l); + if (t.type == TOK_EOF) { - lexer_next(l); - break; + zpanic_at(t, "Unexpected EOF in macro block"); } - if (tk.type == TOK_EOF) + if (t.type == TOK_LBRACE) { - break; + depth++; } - - if (unreachable == 1) + if (t.type == TOK_RBRACE) { - warn_unreachable_code(tk); - unreachable = 2; // Warned once, don't spam + depth--; } - if (tk.type == TOK_COMPTIME) + if (depth > 0) { - // lexer_next(l); // don't eat here, run_comptime_block expects it - char *src = run_comptime_block(ctx, l); - Lexer new_l; - lexer_init(&new_l, src); - // Parse statements from the generated source - while (lexer_peek(&new_l).type != TOK_EOF) + if (body_len + t.len + 2 < 8192) { - ASTNode *s = parse_statement(ctx, &new_l); - if (!s) - { - break; // EOF or error handling dependency - } - - // Link - if (!head) + // Preserve newlines + if (t.line > last_line) { - head = s; + body[body_len] = '\n'; + body[body_len + 1] = 0; + body_len++; } else { - tail->next = s; - } - tail = s; - while (tail->next) - { - tail = tail->next; + body[body_len] = ' '; + body[body_len + 1] = 0; + body_len++; } + + strncat(body, t.start, t.len); + body_len += t.len; } - continue; } - ASTNode *s = parse_statement(ctx, l); - if (s) + last_line = t.line; + lexer_next(l); + } + + // Resolve plugin name + const char *plugin_name = resolve_plugin(ctx, macro_name); + if (!plugin_name) + { + char err[256]; + snprintf(err, sizeof(err), "Unknown plugin: %s (did you forget 'import plugin \"%s\"'?)", + macro_name, macro_name); + zpanic_at(start_tok, err); + } + + // Find Plugin Definition + // Verify plugin exists + ZPlugin *found = zptr_find_plugin(plugin_name); + + if (!found) + { + char err[256]; + snprintf(err, sizeof(err), "Plugin implementation not found: %s", plugin_name); + zpanic_at(start_tok, err); + } + + // Execute Plugin Immediately (Expansion) + FILE *capture = tmpfile(); + if (!capture) + { + zpanic_at(start_tok, "Failed to create capture buffer for plugin expansion"); + } + + ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", + .current_line = start_tok.line, + .out = capture, + .hoist_out = ctx->hoist_out}; + + found->fn(body, &api); + + // Read captured output + long len = ftell(capture); + rewind(capture); + char *expanded_code = xmalloc(len + 1); + fread(expanded_code, 1, len, capture); + expanded_code[len] = 0; + fclose(capture); + free(body); + + // Create Raw Statement/Expression Node + ASTNode *n = ast_create(NODE_RAW_STMT); + n->line = start_tok.line; + n->raw_stmt.content = expanded_code; + + return n; +} + +ASTNode *parse_statement(ParserContext *ctx, Lexer *l) +{ + Token tk = lexer_peek(l); + + ASTNode *s = NULL; + + if (tk.type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *nop = ast_create(NODE_BLOCK); // Empty block as NOP + nop->block.statements = NULL; + return nop; + } + + if (tk.type == TOK_PREPROC) + { + lexer_next(l); // consume token + char *content = xmalloc(tk.len + 2); + strncpy(content, tk.start, tk.len); + content[tk.len] = '\n'; // Ensure newline + content[tk.len + 1] = 0; + ASTNode *s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + return s; + } + + if (tk.type == TOK_STRING || tk.type == TOK_FSTRING) + { + Lexer lookahead = *l; + lexer_next(&lookahead); + TokenType next_type = lexer_peek(&lookahead).type; + + if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT) { - if (!head) + Token t = lexer_next(l); // consume string + + char *inner = xmalloc(t.len); + // Strip quotes + if (t.type == TOK_FSTRING) { - head = s; + strncpy(inner, t.start + 2, t.len - 3); + inner[t.len - 3] = 0; } else { - tail->next = s; + strncpy(inner, t.start + 1, t.len - 2); + inner[t.len - 2] = 0; } - tail = s; - while (tail->next) + + // ; means println (end of line), .. means print (continuation) + int is_ln = (next_type == TOK_SEMICOLON); + char **used_syms = NULL; + int used_count = 0; + char *code = process_printf_sugar(ctx, inner, is_ln, "stdout", &used_syms, &used_count); + + if (next_type == TOK_SEMICOLON) { - tail = tail->next; // Handle chains (e.g. var decl + defer) + lexer_next(l); // consume ; } - - // Check for control flow interruption - if (s->type == NODE_RETURN || s->type == NODE_BREAK || s->type == NODE_CONTINUE) + else if (next_type == TOK_DOTDOT) { - if (unreachable == 0) + lexer_next(l); // consume .. + if (lexer_peek(l).type == TOK_SEMICOLON) { - unreachable = 1; + lexer_next(l); // consume optional ; } } + + ASTNode *n = ast_create(NODE_RAW_STMT); + n->raw_stmt.content = code; + n->raw_stmt.used_symbols = used_syms; + n->raw_stmt.used_symbol_count = used_count; + free(inner); + return n; } } - // Check for unused variables in this block scope - if (ctx->current_scope && !ctx->is_repl) + // Block + if (tk.type == TOK_LBRACE) { - Symbol *sym = ctx->current_scope->symbols; - while (sym) - { - // Skip special names and already warned - if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "it") != 0 && - strcmp(sym->name, "self") != 0) - { - // Skip autofree variables (used implicitly for cleanup) - if (sym->is_autofree) - { - sym = sym->next; - continue; - } - - // RAII: Don't warn if type implements Drop (it is used implicitly) - int has_drop = (sym->type_info && sym->type_info->traits.has_drop); - if (!has_drop && sym->type_info && sym->type_info->name) - { - ASTNode *def = find_struct_def(ctx, sym->type_info->name); - if (def && def->type_info && def->type_info->traits.has_drop) - { - has_drop = 1; - } - } + return parse_block(ctx, l); + } - if (!has_drop) - { - warn_unused_variable(sym->decl_token, sym->name); - } - } - sym = sym->next; + // Keywords / Special + if (tk.type == TOK_TRAIT) + { + return parse_trait(ctx, l); + } + if (tk.type == TOK_IMPL) + { + return parse_impl(ctx, l); + } + if (tk.type == TOK_AUTOFREE) + { + lexer_next(l); + if (lexer_peek(l).type != TOK_IDENT || strncmp(lexer_peek(l).start, "var", 3) != 0) + { + zpanic_at(lexer_peek(l), "Expected 'var' after autofree"); + } + s = parse_var_decl(ctx, l); + s->var_decl.is_autofree = 1; + // Mark symbol as autofree to suppress unused variable warning + Symbol *sym = find_symbol_entry(ctx, s->var_decl.name); + if (sym) + { + sym->is_autofree = 1; } + return s; } - - exit_scope(ctx); - ASTNode *b = ast_create(NODE_BLOCK); - b->block.statements = head; - return b; -} - -// Trait Parsing -ASTNode *parse_trait(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat trait - Token n = lexer_next(l); - if (n.type != TOK_IDENT) + if (tk.type == TOK_TEST) { - zpanic_at(n, "Expected trait name"); + return parse_test(ctx, l); } - char *name = xmalloc(n.len + 1); - strncpy(name, n.start, n.len); - name[n.len] = 0; - - // Generics - char **generic_params = NULL; - int generic_count = 0; - if (lexer_peek(l).type == TOK_LANGLE) + if (tk.type == TOK_COMPTIME) { - lexer_next(l); // eat < - generic_params = xmalloc(sizeof(char *) * 8); // simplified - while (1) + char *src = run_comptime_block(ctx, l); + Lexer new_l; + lexer_init(&new_l, src); + ASTNode *head = NULL, *tail = NULL; + + while (lexer_peek(&new_l).type != TOK_EOF) { - Token p = lexer_next(l); - if (p.type != TOK_IDENT) + ASTNode *s = parse_statement(ctx, &new_l); + if (!s) { - zpanic_at(p, "Expected generic parameter name"); + break; } - generic_params[generic_count] = xmalloc(p.len + 1); - strncpy(generic_params[generic_count], p.start, p.len); - generic_params[generic_count][p.len] = 0; - generic_count++; - - Token sep = lexer_peek(l); - if (sep.type == TOK_COMMA) + if (!head) { - lexer_next(l); - continue; + head = s; } - else if (sep.type == TOK_RANGLE) + else { - lexer_next(l); - break; + tail->next = s; } - else + tail = s; + while (tail->next) { - zpanic_at(sep, "Expected , or > in generic params"); + tail = tail->next; } } - } - if (generic_count > 0) - { - for (int i = 0; i < generic_count; i++) + if (head && !head->next) { - register_generic(ctx, generic_params[i]); + return head; } - } - lexer_next(l); // eat { + ASTNode *b = ast_create(NODE_BLOCK); + b->block.statements = head; + return b; + } + if (tk.type == TOK_ASSERT) + { + return parse_assert(ctx, l); + } + if (tk.type == TOK_DEFER) + { + return parse_defer(ctx, l); + } + if (tk.type == TOK_ASM) + { + return parse_asm(ctx, l); + } - ASTNode *methods = NULL, *tail = NULL; - while (1) + // Identifiers (Keywords or Expressions) + if (tk.type == TOK_IDENT) { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) + // Check for macro invocation: identifier! { code } + Lexer lookahead = *l; + lexer_next(&lookahead); + Token exclaim = lexer_peek(&lookahead); + lexer_next(&lookahead); + Token lbrace = lexer_peek(&lookahead); + if (exclaim.type == TOK_OP && exclaim.len == 1 && exclaim.start[0] == '!' && + lbrace.type == TOK_LBRACE) { - lexer_next(l); - break; + // This is a macro invocation + char *macro_name = token_strdup(tk); + lexer_next(l); // consume identifier + + ASTNode *n = parse_macro_call(ctx, l, macro_name); + free(macro_name); + return n; + } + + // Check for raw blocks + if (strncmp(tk.start, "raw", 3) == 0 && tk.len == 3) + { + lexer_next(l); // eat raw + if (lexer_peek(l).type != TOK_LBRACE) + { + zpanic_at(lexer_peek(l), "Expected { after raw"); + } + lexer_next(l); // eat { + + const char *start = l->src + l->pos; + int depth = 1; + while (depth > 0) + { + Token t = lexer_next(l); + if (t.type == TOK_EOF) + { + zpanic_at(t, "Unexpected EOF in raw block"); + } + if (t.type == TOK_LBRACE) + { + depth++; + } + if (t.type == TOK_RBRACE) + { + depth--; + } + } + const char *end = l->src + l->pos - 1; + size_t len = end - start; + + char *content = xmalloc(len + 1); + memcpy(content, start, len); + content[len] = 0; + + ASTNode *s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + return s; } - // Parse method signature: fn name(args...) -> ret; - Token ft = lexer_next(l); - if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) + // Check for plugin blocks + if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) { - zpanic_at(ft, "Expected fn in trait"); + lexer_next(l); // consume 'plugin' + return parse_plugin(ctx, l); } - Token mn = lexer_next(l); - char *mname = xmalloc(mn.len + 1); - strncpy(mname, mn.start, mn.len); - mname[mn.len] = 0; - - char **defaults = NULL; - int arg_count = 0; - Type **arg_types = NULL; - char **param_names = NULL; - int is_varargs = 0; - char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, - &is_varargs); - - char *ret = xstrdup("void"); - if (lexer_peek(l).type == TOK_ARROW) + if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) { - lexer_next(l); - char *rt = parse_type(ctx, l); - free(ret); - ret = rt; + return parse_var_decl(ctx, l); } - if (lexer_peek(l).type == TOK_SEMICOLON) + // Static local variable: static var x = 0; + if (strncmp(tk.start, "static", 6) == 0 && tk.len == 6) { - lexer_next(l); - ASTNode *m = ast_create(NODE_FUNCTION); - m->func.param_names = param_names; - m->func.name = mname; - m->func.args = args; - m->func.ret_type = ret; - m->func.body = NULL; - if (!methods) - { - methods = m; - } - else + lexer_next(l); // eat 'static' + Token next = lexer_peek(l); + if (strncmp(next.start, "var", 3) == 0 && next.len == 3) { - tail->next = m; + ASTNode *v = parse_var_decl(ctx, l); + v->var_decl.is_static = 1; + return v; } - tail = m; + zpanic_at(next, "Expected 'var' after 'static'"); } - else + if (strncmp(tk.start, "const", 5) == 0 && tk.len == 5) + { + return parse_const(ctx, l); + } + if (strncmp(tk.start, "return", 6) == 0 && tk.len == 6) { - // Default implementation? Not supported yet. - zpanic_at(lexer_peek(l), "Trait methods must end with ; for now"); + return parse_return(ctx, l); } - } - - ASTNode *n_node = ast_create(NODE_TRAIT); - n_node->trait.name = name; - n_node->trait.methods = methods; - n_node->trait.generic_params = generic_params; - n_node->trait.generic_param_count = generic_count; - register_trait(name); - return n_node; -} - -ASTNode *parse_impl(ParserContext *ctx, Lexer *l) -{ - - lexer_next(l); // eat impl - Token t1 = lexer_next(l); - char *name1 = token_strdup(t1); - - char *gen_param = NULL; - // Check for on the struct name - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - Token gt = lexer_next(l); - gen_param = token_strdup(gt); - if (lexer_next(l).type != TOK_RANGLE) + if (strncmp(tk.start, "if", 2) == 0 && tk.len == 2) { - zpanic_at(lexer_peek(l), "Expected >"); + return parse_if(ctx, l); } - } - - // Check for "for" (Trait impl) - Token pk = lexer_peek(l); - if (pk.type == TOK_FOR || - (pk.type == TOK_IDENT && strncmp(pk.start, "for", 3) == 0 && pk.len == 3)) - { - if (pk.type != TOK_FOR) + if (strncmp(tk.start, "while", 5) == 0 && tk.len == 5) { - lexer_next(l); + return parse_while(ctx, l); } - else + if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3) { - lexer_next(l); // eat for + return parse_for(ctx, l); + } + if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5) + { + return parse_match(ctx, l); } - Token t2 = lexer_next(l); - char *name2 = token_strdup(t2); - - register_impl(ctx, name1, name2); - // RAII: Check for "Drop" trait implementation - if (strcmp(name1, "Drop") == 0) + // Break with optional label: break; or break 'outer; + if (strncmp(tk.start, "break", 5) == 0 && tk.len == 5) { - Symbol *s = find_symbol_entry(ctx, name2); - if (s && s->type_info) - { - s->type_info->traits.has_drop = 1; - } - else + Token break_token = lexer_next(l); + + // Error if break is used inside a defer block + if (ctx->in_defer_block) { - // Try finding struct definition - ASTNode *def = find_struct_def(ctx, name2); - if (def && def->type_info) - { - def->type_info->traits.has_drop = 1; - } + zpanic_at(break_token, "'break' is not allowed inside a 'defer' block"); } - } - // Iterator: Check for "Iterable" trait implementation - else if (strcmp(name1, "Iterable") == 0) - { - Symbol *s = find_symbol_entry(ctx, name2); - if (s && s->type_info) + ASTNode *n = ast_create(NODE_BREAK); + n->token = break_token; + n->break_stmt.target_label = NULL; + // Check for 'label + if (lexer_peek(l).type == TOK_CHAR) { - s->type_info->traits.has_iterable = 1; + Token label_tok = lexer_next(l); + // Extract label name (strip quotes) + char *label = xmalloc(label_tok.len); + strncpy(label, label_tok.start + 1, label_tok.len - 2); + label[label_tok.len - 2] = 0; + n->break_stmt.target_label = label; } - else + if (lexer_peek(l).type == TOK_SEMICOLON) { - // Try finding struct definition - ASTNode *def = find_struct_def(ctx, name2); - if (def && def->type_info) - { - def->type_info->traits.has_iterable = 1; - } + lexer_next(l); } + return n; } - ctx->current_impl_struct = name2; // Set context to prevent duplicate emission and prefixing - - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - while (1) + // Continue with optional label + if (strncmp(tk.start, "continue", 8) == 0 && tk.len == 8) { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + Token continue_token = lexer_next(l); + + // Error if continue is used inside a defer block + if (ctx->in_defer_block) { - ASTNode *f = parse_function(ctx, l, 0); - // Mangle: Type_Trait_Method - char *mangled = xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4); - sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name2); - free(f->func.args); - f->func.args = na; - - // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, - f->func.ret_type_info, f->func.is_varargs, f->func.is_async, - f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; + zpanic_at(continue_token, "'continue' is not allowed inside a 'defer' block"); } - else if (lexer_peek(l).type == TOK_ASYNC) + + ASTNode *n = ast_create(NODE_CONTINUE); + n->token = continue_token; + n->continue_stmt.target_label = NULL; + if (lexer_peek(l).type == TOK_CHAR) { - lexer_next(l); // eat async - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - // Mangle: Type_Trait_Method - char *mangled = - xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 5); - sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name2); - free(f->func.args); - f->func.args = na; - - // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - f->func.is_async, f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } + Token label_tok = lexer_next(l); + char *label = xmalloc(label_tok.len); + strncpy(label, label_tok.start + 1, label_tok.len - 2); + label[label_tok.len - 2] = 0; + n->continue_stmt.target_label = label; } - else + if (lexer_peek(l).type == TOK_SEMICOLON) { lexer_next(l); } + return n; } - ctx->current_impl_struct = NULL; // Restore context - ASTNode *n = ast_create(NODE_IMPL_TRAIT); - n->impl_trait.trait_name = name1; - n->impl_trait.target_type = name2; - n->impl_trait.methods = h; - add_to_impl_list(ctx, n); - - // If target struct is generic, register this impl as a template - ASTNode *def = find_struct_def(ctx, name2); - if (def && ((def->type == NODE_STRUCT && def->strct.is_template) || - (def->type == NODE_ENUM && def->enm.is_template))) + + if (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4) { - const char *gp = "T"; - if (def->type == NODE_STRUCT && def->strct.generic_param_count > 0) - { - gp = def->strct.generic_params[0]; - } - // TODO: Enum generic params support if needed - register_impl_template(ctx, name2, gp, n); + return parse_loop(ctx, l); } - - return n; - } - else - { - // Regular impl Struct (impl Box or impl Box) - - // Auto-prefix struct name if in module context - if (ctx->current_module_prefix && !gen_param) + if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6) { - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name1) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name1); - free(name1); - name1 = prefixed_name; + return parse_repeat(ctx, l); + } + if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6) + { + return parse_unless(ctx, l); + } + if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5) + { + return parse_guard(ctx, l); } - ctx->current_impl_struct = name1; // For patch_self_args inside parse_function - - if (gen_param) + // CUDA launch: launch kernel(args) with { grid: X, block: Y }; + if (strncmp(tk.start, "launch", 6) == 0 && tk.len == 6) { - // GENERIC IMPL TEMPLATE: impl Box - if (lexer_next(l).type != TOK_LBRACE) + Token launch_tok = lexer_next(l); // eat 'launch' + + // Parse the kernel call expression + ASTNode *call = parse_expression(ctx, l); + if (!call || call->type != NODE_EXPR_CALL) { - zpanic_at(lexer_peek(l), "Expected {"); + zpanic_at(launch_tok, "Expected kernel call after 'launch'"); } - char *full_struct_name = xmalloc(strlen(name1) + strlen(gen_param) + 3); - sprintf(full_struct_name, "%s<%s>", name1, gen_param); - ASTNode *h = 0, *tl = 0; - ctx->current_impl_methods = NULL; - while (1) + // Expect 'with' + Token with_tok = lexer_peek(l); + if (with_tok.type != TOK_IDENT || strncmp(with_tok.start, "with", 4) != 0 || + with_tok.len != 4) { - ctx->current_impl_methods = h; - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 0); - // Standard Mangle for template: Box_method - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - // Update args string - char *na = patch_self_args(f->func.args, full_struct_name); - free(f->func.args); - f->func.args = na; - - // Manual Type construction for self: Foo* - if (f->func.arg_count > 0 && f->func.param_names && - strcmp(f->func.param_names[0], "self") == 0) - { - Type *t_struct = type_new(TYPE_STRUCT); - t_struct->name = xstrdup(name1); - t_struct->arg_count = 1; - t_struct->args = xmalloc(sizeof(Type *)); - t_struct->args[0] = type_new(TYPE_GENERIC); - t_struct->args[0]->name = xstrdup(gen_param); + zpanic_at(with_tok, "Expected 'with' after kernel call in launch statement"); + } + lexer_next(l); // eat 'with' - Type *t_ptr = type_new(TYPE_POINTER); - t_ptr->inner = t_struct; + // Expect '{' for configuration block + if (lexer_peek(l).type != TOK_LBRACE) + { + zpanic_at(lexer_peek(l), "Expected '{' after 'with' in launch statement"); + } + lexer_next(l); // eat '{' - f->func.arg_types[0] = t_ptr; - } + ASTNode *grid = NULL; + ASTNode *block = NULL; + ASTNode *shared_mem = NULL; + ASTNode *stream = NULL; - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else if (lexer_peek(l).type == TOK_ASYNC) + // Parse configuration fields + while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_EOF) + { + Token field_name = lexer_next(l); + if (field_name.type != TOK_IDENT) { - lexer_next(l); // eat async - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - char *na = patch_self_args(f->func.args, full_struct_name); - free(f->func.args); - f->func.args = na; - - if (f->func.arg_count > 0 && f->func.param_names && - strcmp(f->func.param_names[0], "self") == 0) - { - Type *t_struct = type_new(TYPE_STRUCT); - t_struct->name = xstrdup(name1); - t_struct->arg_count = 1; - t_struct->args = xmalloc(sizeof(Type *)); - t_struct->args[0] = type_new(TYPE_GENERIC); - t_struct->args[0]->name = xstrdup(gen_param); + zpanic_at(field_name, "Expected field name in launch configuration"); + } - Type *t_ptr = type_new(TYPE_POINTER); - t_ptr->inner = t_struct; + // Expect ':' + if (lexer_peek(l).type != TOK_COLON) + { + zpanic_at(lexer_peek(l), "Expected ':' after field name"); + } + lexer_next(l); // eat ':' - f->func.arg_types[0] = t_ptr; - } + // Parse value expression + ASTNode *value = parse_expression(ctx, l); - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } - else + // Assign to appropriate field + if (strncmp(field_name.start, "grid", 4) == 0 && field_name.len == 4) { - lexer_next(l); + grid = value; } - } - free(full_struct_name); - // Register Template - ASTNode *n = ast_create(NODE_IMPL); - n->impl.struct_name = name1; - n->impl.methods = h; - register_impl_template(ctx, name1, gen_param, n); - ctx->current_impl_struct = NULL; - return NULL; // Do not emit generic template - } - else - { - // REGULAR IMPL - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - while (1) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) + else if (strncmp(field_name.start, "block", 5) == 0 && field_name.len == 5) { - lexer_next(l); - break; + block = value; } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + else if (strncmp(field_name.start, "shared_mem", 10) == 0 && field_name.len == 10) { - ASTNode *f = parse_function(ctx, l, 0); - - // Standard Mangle: Struct_method - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - char *na = patch_self_args(f->func.args, name1); - free(f->func.args); - f->func.args = na; - - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, - f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; + shared_mem = value; } - else if (lexer_peek(l).type == TOK_ASYNC) + else if (strncmp(field_name.start, "stream", 6) == 0 && field_name.len == 6) { - lexer_next(l); - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name1); - free(f->func.args); - f->func.args = na; - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - 1, f->token); - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } + stream = value; } else + { + zpanic_at(field_name, "Unknown launch configuration field (expected: grid, " + "block, shared_mem, stream)"); + } + + // Optional comma + if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); } } - ctx->current_impl_struct = NULL; - ASTNode *n = ast_create(NODE_IMPL); - n->impl.struct_name = name1; - n->impl.methods = h; - add_to_impl_list(ctx, n); - return n; - } - } -} - -ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) -{ - - lexer_next(l); // eat struct or union - Token n = lexer_next(l); - char *name = token_strdup(n); - - // Generic Params or - char **gps = NULL; - int gp_count = 0; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - while (1) - { - Token g = lexer_next(l); - gps = realloc(gps, sizeof(char *) * (gp_count + 1)); - gps[gp_count++] = token_strdup(g); - Token next = lexer_peek(l); - if (next.type == TOK_COMMA) + // Expect '}' + if (lexer_peek(l).type != TOK_RBRACE) { - lexer_next(l); // eat , + zpanic_at(lexer_peek(l), "Expected '}' to close launch configuration"); } - else if (next.type == TOK_RANGLE) + lexer_next(l); // eat '}' + + // Expect ';' + if (lexer_peek(l).type == TOK_SEMICOLON) { - lexer_next(l); // eat > - break; + lexer_next(l); } - else + + // Require at least grid and block + if (!grid || !block) { - zpanic_at(next, "Expected ',' or '>' in generic parameter list"); + zpanic_at(launch_tok, "Launch configuration requires at least 'grid' and 'block'"); } - } - register_generic(ctx, name); - } - // Check for prototype (forward declaration) - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - ASTNode *n = ast_create(NODE_STRUCT); - n->strct.name = name; - n->strct.is_template = (gp_count > 0); - n->strct.generic_params = gps; - n->strct.generic_param_count = gp_count; - n->strct.is_union = is_union; - n->strct.fields = NULL; - n->strct.is_incomplete = 1; + ASTNode *n = ast_create(NODE_CUDA_LAUNCH); + n->cuda_launch.call = call; + n->cuda_launch.grid = grid; + n->cuda_launch.block = block; + n->cuda_launch.shared_mem = shared_mem; + n->cuda_launch.stream = stream; + n->token = launch_tok; + return n; + } - return n; - } + // Do-while loop: do { body } while condition; + if (strncmp(tk.start, "do", 2) == 0 && tk.len == 2) + { + lexer_next(l); // eat 'do' + ASTNode *body = parse_block(ctx, l); - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; + // Expect 'while' + Token while_tok = lexer_peek(l); + if (while_tok.type != TOK_IDENT || strncmp(while_tok.start, "while", 5) != 0 || + while_tok.len != 5) + { + zpanic_at(while_tok, "Expected 'while' after do block"); + } + lexer_next(l); // eat 'while' - // Temp storage for used structs - char **temp_used_structs = NULL; - int temp_used_count = 0; + ASTNode *cond = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } - while (1) - { - skip_comments(l); - Token t = lexer_peek(l); - // printf("DEBUG: parse_struct loop seeing '%.*s'\n", t.len, t.start); - if (t.type == TOK_RBRACE) - { - lexer_next(l); - break; + ASTNode *n = ast_create(NODE_DO_WHILE); + n->do_while_stmt.body = body; + n->do_while_stmt.condition = cond; + n->do_while_stmt.loop_label = NULL; + return n; } - if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA) + + if (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) { - lexer_next(l); - continue; + return parse_defer(ctx, l); } - // Handle 'use' (Struct Embedding) - if (t.type == TOK_USE) + // Goto statement: goto label_name; OR goto *expr; (computed goto) + if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4) { - lexer_next(l); // eat use - - // Check for named use: use name: Type; - Token t1 = lexer_peek(l); - Token t2 = lexer_peek2(l); + Token goto_tok = lexer_next(l); // eat 'goto' - if (t1.type == TOK_IDENT && t2.type == TOK_COLON) + // Error if goto is used inside a defer block + if (ctx->in_defer_block) { - // Named use -> Composition (Add field, don't flatten) - Token field_name = lexer_next(l); - lexer_next(l); // eat : - char *field_type_str = parse_type(ctx, l); - expect(l, TOK_SEMICOLON, "Expected ;"); + zpanic_at(goto_tok, "'goto' is not allowed inside a 'defer' block"); + } - ASTNode *nf = ast_create(NODE_FIELD); - nf->field.name = token_strdup(field_name); - nf->field.type = field_type_str; + Token next = lexer_peek(l); - if (!h) - { - h = nf; - } - else + // Computed goto: goto *ptr; + if (next.type == TOK_OP && next.start[0] == '*') + { + lexer_next(l); // eat '*' + ASTNode *target = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) { - tl->next = nf; + lexer_next(l); } - tl = nf; - continue; + + ASTNode *n = ast_create(NODE_GOTO); + n->goto_stmt.label_name = NULL; + n->goto_stmt.goto_expr = target; + n->token = goto_tok; + return n; + } + + // Regular goto + Token label = lexer_next(l); + if (label.type != TOK_IDENT) + { + zpanic_at(label, "Expected label name after goto"); } - - // Normal use -> Mixin (Flatten) - // Parse the type (e.g. Header) - Type *use_type = parse_type_formal(ctx, l); - char *use_name = type_to_string(use_type); - - expect(l, TOK_SEMICOLON, "Expected ; after use"); - - // Find the definition and COPY fields - ASTNode *def = find_struct_def(ctx, use_name); - if (!def && is_known_generic(ctx, use_type->name)) + if (lexer_peek(l).type == TOK_SEMICOLON) { - // Try to force instantiation if not found? - // For now, rely on parse_type having triggered instantiation. - char *mangled = - type_to_string(use_type); // This works if type_to_string returns mangled name - def = find_struct_def(ctx, mangled); - free(mangled); + lexer_next(l); } + ASTNode *n = ast_create(NODE_GOTO); + n->goto_stmt.label_name = token_strdup(label); + n->token = goto_tok; + zen_trigger_at(TRIGGER_GOTO, goto_tok); + return n; + } - if (def && def->type == NODE_STRUCT) + // Label detection: identifier followed by : (but not ::) + { + Lexer lookahead = *l; + Token ident = lexer_next(&lookahead); + Token maybe_colon = lexer_peek(&lookahead); + if (maybe_colon.type == TOK_COLON) { - if (!temp_used_structs) - { - temp_used_structs = xmalloc(sizeof(char *) * 8); - } - temp_used_structs[temp_used_count++] = xstrdup(use_name); - - ASTNode *f = def->strct.fields; - while (f) + // Check it's not :: (double colon for namespaces) + lexer_next(&lookahead); + Token after_colon = lexer_peek(&lookahead); + if (after_colon.type != TOK_COLON) { - ASTNode *nf = ast_create(NODE_FIELD); - nf->field.name = xstrdup(f->field.name); - nf->field.type = xstrdup(f->field.type); - if (!h) - { - h = nf; - } - else - { - tl->next = nf; - } - tl = nf; - f = f->next; + // This is a label! + lexer_next(l); // eat identifier + lexer_next(l); // eat : + ASTNode *n = ast_create(NODE_LABEL); + n->label_stmt.label_name = token_strdup(ident); + n->token = ident; + return n; } } - else - { - // If definition not found (e.g. user struct defined later), we can't - // embed fields yet. Compiler limitation: 'use' requires struct to be - // defined before. Fallback: Emit a placeholder field so compilation - // doesn't crash, but layout will be wrong. printf("Warning: Could not - // find struct '%s' for embedding.\n", use_name); - } - free(use_name); - continue; } - // --------------------------------------- - if (t.type == TOK_IDENT) + if ((strncmp(tk.start, "print", 5) == 0 && tk.len == 5) || + (strncmp(tk.start, "println", 7) == 0 && tk.len == 7) || + (strncmp(tk.start, "eprint", 6) == 0 && tk.len == 6) || + (strncmp(tk.start, "eprintln", 8) == 0 && tk.len == 8)) { - Token f_name = lexer_next(l); - expect(l, TOK_COLON, "Expected :"); - char *f_type = parse_type(ctx, l); - ASTNode *f = ast_create(NODE_FIELD); - f->field.name = token_strdup(f_name); - f->field.type = f_type; - f->field.bit_width = 0; + // Revert: User requested print without newline + int is_ln = (tk.len == 7 || tk.len == 8); + // int is_ln = (tk.len == 7 || tk.len == 8); + int is_err = (tk.start[0] == 'e'); + char *target = is_err ? "stderr" : "stdout"; + + lexer_next(l); // eat keyword - // Optional bit width: name: type : 3 - if (lexer_peek(l).type == TOK_COLON) + Token t = lexer_next(l); + if (t.type != TOK_STRING && t.type != TOK_FSTRING) { - lexer_next(l); // eat : - Token width_tok = lexer_next(l); - if (width_tok.type != TOK_INT) - { - zpanic_at(width_tok, "Expected bit width integer"); - } - f->field.bit_width = atoi(token_strdup(width_tok)); + zpanic_at(t, "Expected string literal after print/eprint"); } - if (!h) + char *inner = xmalloc(t.len); + if (t.type == TOK_FSTRING) { - h = f; + strncpy(inner, t.start + 2, t.len - 3); + inner[t.len - 3] = 0; } else { - tl->next = f; + strncpy(inner, t.start + 1, t.len - 2); + inner[t.len - 2] = 0; } - tl = f; - if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA) + char **used_syms = NULL; + int used_count = 0; + char *code = process_printf_sugar(ctx, inner, is_ln, target, &used_syms, &used_count); + free(inner); + + if (lexer_peek(l).type == TOK_SEMICOLON) { lexer_next(l); } - } - else - { - lexer_next(l); - } - } - - ASTNode *node = ast_create(NODE_STRUCT); - add_to_struct_list(ctx, node); - // Auto-prefix struct name if in module context - if (ctx->current_module_prefix && gp_count == 0) - { // Don't prefix generic templates - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); - free(name); - name = prefixed_name; + ASTNode *n = ast_create(NODE_RAW_STMT); + n->raw_stmt.content = code; + n->raw_stmt.used_symbols = used_syms; + n->raw_stmt.used_symbol_count = used_count; + return n; + } } - node->strct.name = name; + // Default: Expression Statement + s = parse_expression(ctx, l); - // Initialize Type Info so we can track traits (like Drop) - node->type_info = type_new(TYPE_STRUCT); - node->type_info->name = xstrdup(name); - if (gp_count > 0) + int has_semi = 0; + if (lexer_peek(l).type == TOK_SEMICOLON) { - node->type_info->kind = TYPE_GENERIC; - // TODO: track generic params + lexer_next(l); + has_semi = 1; } - node->strct.fields = h; - node->strct.generic_params = gps; - node->strct.generic_param_count = gp_count; - node->strct.is_union = is_union; - node->strct.used_structs = temp_used_structs; - node->strct.used_struct_count = temp_used_count; - - if (gp_count > 0) + // Auto-print in REPL: If no semicolon (implicit expr at block end) + // and not an assignment, print it. + if (ctx->is_repl && s && !has_semi) { - node->strct.is_template = 1; - register_template(ctx, name, node); + int is_assign = 0; + if (s->type == NODE_EXPR_BINARY) + { + char *op = s->binary.op; + if (strcmp(op, "=") == 0 || + (strlen(op) > 1 && op[strlen(op) - 1] == '=' && strcmp(op, "==") != 0 && + strcmp(op, "!=") != 0 && strcmp(op, "<=") != 0 && strcmp(op, ">=") != 0)) + { + is_assign = 1; + } + } + + if (!is_assign) + { + ASTNode *print_node = ast_create(NODE_REPL_PRINT); + print_node->repl_print.expr = s; + // Preserve line info + print_node->line = s->line; + print_node->token = s->token; + return print_node; + } } - // Register definition for 'use' lookups and LSP - if (gp_count == 0) + if (s) { - register_struct_def(ctx, name, node); + s->line = tk.line; } - return node; -} - -Type *parse_type_obj(ParserContext *ctx, Lexer *l) -{ - // Parse the base type (int, U32, MyStruct, etc.) - Type *t = parse_type_base(ctx, l); - - // Handle Pointers - while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') + // Check for discarded must_use result + if (s && s->type == NODE_EXPR_CALL) { - lexer_next(l); // eat * - // Wrap the current type in a Pointer type - Type *ptr = type_new(TYPE_POINTER); - ptr->inner = t; - t = ptr; + ASTNode *callee = s->call.callee; + if (callee && callee->type == NODE_EXPR_VAR) + { + FuncSig *sig = find_func(ctx, callee->var_ref.name); + if (sig && sig->must_use) + { + zwarn_at(tk, "Ignoring return value of function marked @must_use"); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Use the result or explicitly discard with `_ = ...`\n"); + } + } } - return t; + return s; } -ASTNode *parse_enum(ParserContext *ctx, Lexer *l) +ASTNode *parse_block(ParserContext *ctx, Lexer *l) { - lexer_next(l); - Token n = lexer_next(l); - - // 1. Check for Generic - char *gp = NULL; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - Token g = lexer_next(l); - gp = token_strdup(g); - lexer_next(l); // eat > - register_generic(ctx, n.start ? token_strdup(n) : "anon"); - } - - lexer_next(l); // eat { + lexer_next(l); // eat '{' + enter_scope(ctx); + ASTNode *head = 0, *tail = 0; - ASTNode *h = 0, *tl = 0; - int v = 0; - char *ename = token_strdup(n); // Store enum name + int unreachable = 0; while (1) { skip_comments(l); - Token t = lexer_peek(l); - if (t.type == TOK_RBRACE) + Token tk = lexer_peek(l); + if (tk.type == TOK_RBRACE) { lexer_next(l); break; } - if (t.type == TOK_COMMA) + + if (tk.type == TOK_EOF) { - lexer_next(l); - continue; + break; } - if (t.type == TOK_IDENT) + if (unreachable == 1) { - Token vt = lexer_next(l); - char *vname = token_strdup(vt); + warn_unreachable_code(tk); + unreachable = 2; // Warned once, don't spam + } - // 2. Parse Payload Type (Ok(int)) - Type *payload = NULL; - if (lexer_peek(l).type == TOK_LPAREN) + if (tk.type == TOK_COMPTIME) + { + // lexer_next(l); // don't eat here, run_comptime_block expects it + char *src = run_comptime_block(ctx, l); + Lexer new_l; + lexer_init(&new_l, src); + // Parse statements from the generated source + while (lexer_peek(&new_l).type != TOK_EOF) { - lexer_next(l); - payload = parse_type_obj(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) + ASTNode *s = parse_statement(ctx, &new_l); + if (!s) { - zpanic_at(lexer_peek(l), "Expected )"); + break; // EOF or error handling dependency } - } - - ASTNode *va = ast_create(NODE_ENUM_VARIANT); - va->variant.name = vname; - va->variant.tag_id = v++; // Use tag_id instead of value - va->variant.payload = payload; // Store Type* - - // Register Variant (Mangled name to avoid collisions: Result_Ok) - char mangled[256]; - sprintf(mangled, "%s_%s", ename, vname); - register_enum_variant(ctx, ename, mangled, va->variant.tag_id); - // Handle explicit assignment: Ok = 5 - if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=') - { - lexer_next(l); - va->variant.tag_id = atoi(lexer_next(l).start); - v = va->variant.tag_id + 1; + // Link + if (!head) + { + head = s; + } + else + { + tail->next = s; + } + tail = s; + while (tail->next) + { + tail = tail->next; + } } + continue; + } - if (!h) + ASTNode *s = parse_statement(ctx, l); + if (s) + { + if (!head) { - h = va; + head = s; } else { - tl->next = va; + tail->next = s; + } + tail = s; + while (tail->next) + { + tail = tail->next; // Handle chains (e.g. var decl + defer) } - tl = va; - } - else - { - lexer_next(l); - } - } - // Auto-prefix enum name if in module context - if (ctx->current_module_prefix && !gp) - { // Don't prefix generic templates - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename); - free(ename); - ename = prefixed_name; + // Check for control flow interruption + if (s->type == NODE_RETURN || s->type == NODE_BREAK || s->type == NODE_CONTINUE) + { + if (unreachable == 0) + { + unreachable = 1; + } + } + } } - ASTNode *node = ast_create(NODE_ENUM); - node->enm.name = ename; + // Check for unused variables in this block scope + if (ctx->current_scope && !ctx->is_repl) + { + Symbol *sym = ctx->current_scope->symbols; + while (sym) + { + // Skip special names and already warned + if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "it") != 0 && + strcmp(sym->name, "self") != 0) + { + // Skip autofree variables (used implicitly for cleanup) + if (sym->is_autofree) + { + sym = sym->next; + continue; + } - node->enm.variants = h; - node->enm.generic_param = gp; // 3. Store generic param + // RAII: Don't warn if type implements Drop (it is used implicitly) + int has_drop = (sym->type_info && sym->type_info->traits.has_drop); + if (!has_drop && sym->type_info && sym->type_info->name) + { + ASTNode *def = find_struct_def(ctx, sym->type_info->name); + if (def && def->type_info && def->type_info->traits.has_drop) + { + has_drop = 1; + } + } - if (gp) - { - node->enm.is_template = 1; - register_template(ctx, node->enm.name, node); + if (!has_drop) + { + warn_unused_variable(sym->decl_token, sym->name); + } + } + sym = sym->next; + } } - add_to_enum_list(ctx, node); // Register globally - - return node; + exit_scope(ctx); + ASTNode *b = ast_create(NODE_BLOCK); + b->block.statements = head; + return b; } + ASTNode *parse_include(ParserContext *ctx, Lexer *l) { lexer_next(l); // eat 'include' diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c new file mode 100644 index 0000000..600d60d --- /dev/null +++ b/src/parser/parser_struct.c @@ -0,0 +1,943 @@ + +#include "parser.h" +#include +#include +#include +#include +#include + +#include "../ast/ast.h" +#include "../plugins/plugin_manager.h" +#include "../zen/zen_facts.h" +#include "zprep_plugin.h" +#include "../codegen/codegen.h" + +// Trait Parsing +ASTNode *parse_trait(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat trait + Token n = lexer_next(l); + if (n.type != TOK_IDENT) + { + zpanic_at(n, "Expected trait name"); + } + char *name = xmalloc(n.len + 1); + strncpy(name, n.start, n.len); + name[n.len] = 0; + + // Generics + char **generic_params = NULL; + int generic_count = 0; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + generic_params = xmalloc(sizeof(char *) * 8); // simplified + while (1) + { + Token p = lexer_next(l); + if (p.type != TOK_IDENT) + { + zpanic_at(p, "Expected generic parameter name"); + } + generic_params[generic_count] = xmalloc(p.len + 1); + strncpy(generic_params[generic_count], p.start, p.len); + generic_params[generic_count][p.len] = 0; + generic_count++; + + Token sep = lexer_peek(l); + if (sep.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + else if (sep.type == TOK_RANGLE) + { + lexer_next(l); + break; + } + else + { + zpanic_at(sep, "Expected , or > in generic params"); + } + } + } + + if (generic_count > 0) + { + for (int i = 0; i < generic_count; i++) + { + register_generic(ctx, generic_params[i]); + } + } + + lexer_next(l); // eat { + + ASTNode *methods = NULL, *tail = NULL; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + + // Parse method signature: fn name(args...) -> ret; + Token ft = lexer_next(l); + if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) + { + zpanic_at(ft, "Expected fn in trait"); + } + + Token mn = lexer_next(l); + char *mname = xmalloc(mn.len + 1); + strncpy(mname, mn.start, mn.len); + mname[mn.len] = 0; + + char **defaults = NULL; + int arg_count = 0; + Type **arg_types = NULL; + char **param_names = NULL; + int is_varargs = 0; + char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, + &is_varargs); + + char *ret = xstrdup("void"); + if (lexer_peek(l).type == TOK_ARROW) + { + lexer_next(l); + char *rt = parse_type(ctx, l); + free(ret); + ret = rt; + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *m = ast_create(NODE_FUNCTION); + m->func.param_names = param_names; + m->func.name = mname; + m->func.args = args; + m->func.ret_type = ret; + m->func.body = NULL; + if (!methods) + { + methods = m; + } + else + { + tail->next = m; + } + tail = m; + } + else + { + // Default implementation? Not supported yet. + zpanic_at(lexer_peek(l), "Trait methods must end with ; for now"); + } + } + + ASTNode *n_node = ast_create(NODE_TRAIT); + n_node->trait.name = name; + n_node->trait.methods = methods; + n_node->trait.generic_params = generic_params; + n_node->trait.generic_param_count = generic_count; + register_trait(name); + return n_node; +} + +ASTNode *parse_impl(ParserContext *ctx, Lexer *l) +{ + + lexer_next(l); // eat impl + Token t1 = lexer_next(l); + char *name1 = token_strdup(t1); + + char *gen_param = NULL; + // Check for on the struct name + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + Token gt = lexer_next(l); + gen_param = token_strdup(gt); + if (lexer_next(l).type != TOK_RANGLE) + { + zpanic_at(lexer_peek(l), "Expected >"); + } + } + + // Check for "for" (Trait impl) + Token pk = lexer_peek(l); + if (pk.type == TOK_FOR || + (pk.type == TOK_IDENT && strncmp(pk.start, "for", 3) == 0 && pk.len == 3)) + { + if (pk.type != TOK_FOR) + { + lexer_next(l); + } + else + { + lexer_next(l); // eat for + } + Token t2 = lexer_next(l); + char *name2 = token_strdup(t2); + + register_impl(ctx, name1, name2); + + // RAII: Check for "Drop" trait implementation + if (strcmp(name1, "Drop") == 0) + { + Symbol *s = find_symbol_entry(ctx, name2); + if (s && s->type_info) + { + s->type_info->traits.has_drop = 1; + } + else + { + // Try finding struct definition + ASTNode *def = find_struct_def(ctx, name2); + if (def && def->type_info) + { + def->type_info->traits.has_drop = 1; + } + } + } + + // Iterator: Check for "Iterable" trait implementation + else if (strcmp(name1, "Iterable") == 0) + { + Symbol *s = find_symbol_entry(ctx, name2); + if (s && s->type_info) + { + s->type_info->traits.has_iterable = 1; + } + else + { + // Try finding struct definition + ASTNode *def = find_struct_def(ctx, name2); + if (def && def->type_info) + { + def->type_info->traits.has_iterable = 1; + } + } + } + + ctx->current_impl_struct = name2; // Set context to prevent duplicate emission and prefixing + + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + // Mangle: Type_Trait_Method + char *mangled = xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4); + sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name2); + free(f->func.args); + f->func.args = na; + + // Register function for lookup + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, + f->func.ret_type_info, f->func.is_varargs, f->func.is_async, + f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); // eat async + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + // Mangle: Type_Trait_Method + char *mangled = + xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 5); + sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name2); + free(f->func.args); + f->func.args = na; + + // Register function for lookup + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + f->func.is_async, f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + ctx->current_impl_struct = NULL; // Restore context + ASTNode *n = ast_create(NODE_IMPL_TRAIT); + n->impl_trait.trait_name = name1; + n->impl_trait.target_type = name2; + n->impl_trait.methods = h; + add_to_impl_list(ctx, n); + + // If target struct is generic, register this impl as a template + ASTNode *def = find_struct_def(ctx, name2); + if (def && ((def->type == NODE_STRUCT && def->strct.is_template) || + (def->type == NODE_ENUM && def->enm.is_template))) + { + const char *gp = "T"; + if (def->type == NODE_STRUCT && def->strct.generic_param_count > 0) + { + gp = def->strct.generic_params[0]; + } + // TODO: Enum generic params support if needed + register_impl_template(ctx, name2, gp, n); + } + + return n; + } + else + { + // Regular impl Struct (impl Box or impl Box) + + // Auto-prefix struct name if in module context + if (ctx->current_module_prefix && !gen_param) + { + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name1) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name1); + free(name1); + name1 = prefixed_name; + } + + ctx->current_impl_struct = name1; // For patch_self_args inside parse_function + + if (gen_param) + { + // GENERIC IMPL TEMPLATE: impl Box + if (lexer_next(l).type != TOK_LBRACE) + { + zpanic_at(lexer_peek(l), "Expected {"); + } + char *full_struct_name = xmalloc(strlen(name1) + strlen(gen_param) + 3); + sprintf(full_struct_name, "%s<%s>", name1, gen_param); + + ASTNode *h = 0, *tl = 0; + ctx->current_impl_methods = NULL; + while (1) + { + ctx->current_impl_methods = h; + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + // Standard Mangle for template: Box_method + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + // Update args string + char *na = patch_self_args(f->func.args, full_struct_name); + free(f->func.args); + f->func.args = na; + + // Manual Type construction for self: Foo* + if (f->func.arg_count > 0 && f->func.param_names && + strcmp(f->func.param_names[0], "self") == 0) + { + Type *t_struct = type_new(TYPE_STRUCT); + t_struct->name = xstrdup(name1); + t_struct->arg_count = 1; + t_struct->args = xmalloc(sizeof(Type *)); + t_struct->args[0] = type_new(TYPE_GENERIC); + t_struct->args[0]->name = xstrdup(gen_param); + + Type *t_ptr = type_new(TYPE_POINTER); + t_ptr->inner = t_struct; + + f->func.arg_types[0] = t_ptr; + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); // eat async + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, full_struct_name); + free(f->func.args); + f->func.args = na; + + if (f->func.arg_count > 0 && f->func.param_names && + strcmp(f->func.param_names[0], "self") == 0) + { + Type *t_struct = type_new(TYPE_STRUCT); + t_struct->name = xstrdup(name1); + t_struct->arg_count = 1; + t_struct->args = xmalloc(sizeof(Type *)); + t_struct->args[0] = type_new(TYPE_GENERIC); + t_struct->args[0]->name = xstrdup(gen_param); + + Type *t_ptr = type_new(TYPE_POINTER); + t_ptr->inner = t_struct; + + f->func.arg_types[0] = t_ptr; + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + free(full_struct_name); + // Register Template + ASTNode *n = ast_create(NODE_IMPL); + n->impl.struct_name = name1; + n->impl.methods = h; + register_impl_template(ctx, name1, gen_param, n); + ctx->current_impl_struct = NULL; + return NULL; // Do not emit generic template + } + else + { + // REGULAR IMPL + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + + // Standard Mangle: Struct_method + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, + f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + 1, f->token); + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + ctx->current_impl_struct = NULL; + ASTNode *n = ast_create(NODE_IMPL); + n->impl.struct_name = name1; + n->impl.methods = h; + add_to_impl_list(ctx, n); + return n; + } + } +} + +ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) +{ + + lexer_next(l); // eat struct or union + Token n = lexer_next(l); + char *name = token_strdup(n); + + // Generic Params or + char **gps = NULL; + int gp_count = 0; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + while (1) + { + Token g = lexer_next(l); + gps = realloc(gps, sizeof(char *) * (gp_count + 1)); + gps[gp_count++] = token_strdup(g); + + Token next = lexer_peek(l); + if (next.type == TOK_COMMA) + { + lexer_next(l); // eat , + } + else if (next.type == TOK_RANGLE) + { + lexer_next(l); // eat > + break; + } + else + { + zpanic_at(next, "Expected ',' or '>' in generic parameter list"); + } + } + register_generic(ctx, name); + } + + // Check for prototype (forward declaration) + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *n = ast_create(NODE_STRUCT); + n->strct.name = name; + n->strct.is_template = (gp_count > 0); + n->strct.generic_params = gps; + n->strct.generic_param_count = gp_count; + n->strct.is_union = is_union; + n->strct.fields = NULL; + n->strct.is_incomplete = 1; + + return n; + } + + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + + // Temp storage for used structs + char **temp_used_structs = NULL; + int temp_used_count = 0; + + while (1) + { + skip_comments(l); + Token t = lexer_peek(l); + // printf("DEBUG: parse_struct loop seeing '%.*s'\n", t.len, t.start); + if (t.type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + + // Handle 'use' (Struct Embedding) + if (t.type == TOK_USE) + { + lexer_next(l); // eat use + + // Check for named use: use name: Type; + Token t1 = lexer_peek(l); + Token t2 = lexer_peek2(l); + + if (t1.type == TOK_IDENT && t2.type == TOK_COLON) + { + // Named use -> Composition (Add field, don't flatten) + Token field_name = lexer_next(l); + lexer_next(l); // eat : + char *field_type_str = parse_type(ctx, l); + expect(l, TOK_SEMICOLON, "Expected ;"); + + ASTNode *nf = ast_create(NODE_FIELD); + nf->field.name = token_strdup(field_name); + nf->field.type = field_type_str; + + if (!h) + { + h = nf; + } + else + { + tl->next = nf; + } + tl = nf; + continue; + } + + // Normal use -> Mixin (Flatten) + // Parse the type (e.g. Header) + Type *use_type = parse_type_formal(ctx, l); + char *use_name = type_to_string(use_type); + + expect(l, TOK_SEMICOLON, "Expected ; after use"); + + // Find the definition and COPY fields + ASTNode *def = find_struct_def(ctx, use_name); + if (!def && is_known_generic(ctx, use_type->name)) + { + // Try to force instantiation if not found? + // For now, rely on parse_type having triggered instantiation. + char *mangled = + type_to_string(use_type); // This works if type_to_string returns mangled name + def = find_struct_def(ctx, mangled); + free(mangled); + } + + if (def && def->type == NODE_STRUCT) + { + if (!temp_used_structs) + { + temp_used_structs = xmalloc(sizeof(char *) * 8); + } + temp_used_structs[temp_used_count++] = xstrdup(use_name); + + ASTNode *f = def->strct.fields; + while (f) + { + ASTNode *nf = ast_create(NODE_FIELD); + nf->field.name = xstrdup(f->field.name); + nf->field.type = xstrdup(f->field.type); + if (!h) + { + h = nf; + } + else + { + tl->next = nf; + } + tl = nf; + f = f->next; + } + } + else + { + // If definition not found (e.g. user struct defined later), we can't + // embed fields yet. Compiler limitation: 'use' requires struct to be + // defined before. Fallback: Emit a placeholder field so compilation + // doesn't crash, but layout will be wrong. printf("Warning: Could not + // find struct '%s' for embedding.\n", use_name); + } + free(use_name); + continue; + } + // --------------------------------------- + + if (t.type == TOK_IDENT) + { + Token f_name = lexer_next(l); + expect(l, TOK_COLON, "Expected :"); + char *f_type = parse_type(ctx, l); + + ASTNode *f = ast_create(NODE_FIELD); + f->field.name = token_strdup(f_name); + f->field.type = f_type; + f->field.bit_width = 0; + + // Optional bit width: name: type : 3 + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); // eat : + Token width_tok = lexer_next(l); + if (width_tok.type != TOK_INT) + { + zpanic_at(width_tok, "Expected bit width integer"); + } + f->field.bit_width = atoi(token_strdup(width_tok)); + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + + if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + } + } + else + { + lexer_next(l); + } + } + + ASTNode *node = ast_create(NODE_STRUCT); + add_to_struct_list(ctx, node); + + // Auto-prefix struct name if in module context + if (ctx->current_module_prefix && gp_count == 0) + { // Don't prefix generic templates + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); + free(name); + name = prefixed_name; + } + + node->strct.name = name; + + // Initialize Type Info so we can track traits (like Drop) + node->type_info = type_new(TYPE_STRUCT); + node->type_info->name = xstrdup(name); + if (gp_count > 0) + { + node->type_info->kind = TYPE_GENERIC; + // TODO: track generic params + } + + node->strct.fields = h; + node->strct.generic_params = gps; + node->strct.generic_param_count = gp_count; + node->strct.is_union = is_union; + node->strct.used_structs = temp_used_structs; + node->strct.used_struct_count = temp_used_count; + + if (gp_count > 0) + { + node->strct.is_template = 1; + register_template(ctx, name, node); + } + + // Register definition for 'use' lookups and LSP + if (gp_count == 0) + { + register_struct_def(ctx, name, node); + } + + return node; +} + +Type *parse_type_obj(ParserContext *ctx, Lexer *l) +{ + // Parse the base type (int, U32, MyStruct, etc.) + Type *t = parse_type_base(ctx, l); + + // Handle Pointers + while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') + { + lexer_next(l); // eat * + // Wrap the current type in a Pointer type + Type *ptr = type_new(TYPE_POINTER); + ptr->inner = t; + t = ptr; + } + + return t; +} + +ASTNode *parse_enum(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); + Token n = lexer_next(l); + + // 1. Check for Generic + char *gp = NULL; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + Token g = lexer_next(l); + gp = token_strdup(g); + lexer_next(l); // eat > + register_generic(ctx, n.start ? token_strdup(n) : "anon"); + } + + lexer_next(l); // eat { + + ASTNode *h = 0, *tl = 0; + int v = 0; + char *ename = token_strdup(n); // Store enum name + + while (1) + { + skip_comments(l); + Token t = lexer_peek(l); + if (t.type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (t.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + + if (t.type == TOK_IDENT) + { + Token vt = lexer_next(l); + char *vname = token_strdup(vt); + + // 2. Parse Payload Type (Ok(int)) + Type *payload = NULL; + if (lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); + payload = parse_type_obj(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected )"); + } + } + + ASTNode *va = ast_create(NODE_ENUM_VARIANT); + va->variant.name = vname; + va->variant.tag_id = v++; // Use tag_id instead of value + va->variant.payload = payload; // Store Type* + + // Register Variant (Mangled name to avoid collisions: Result_Ok) + char mangled[256]; + sprintf(mangled, "%s_%s", ename, vname); + register_enum_variant(ctx, ename, mangled, va->variant.tag_id); + + // Handle explicit assignment: Ok = 5 + if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=') + { + lexer_next(l); + va->variant.tag_id = atoi(lexer_next(l).start); + v = va->variant.tag_id + 1; + } + + if (!h) + { + h = va; + } + else + { + tl->next = va; + } + tl = va; + } + else + { + lexer_next(l); + } + } + + // Auto-prefix enum name if in module context + if (ctx->current_module_prefix && !gp) + { // Don't prefix generic templates + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename); + free(ename); + ename = prefixed_name; + } + + ASTNode *node = ast_create(NODE_ENUM); + node->enm.name = ename; + + node->enm.variants = h; + node->enm.generic_param = gp; // 3. Store generic param + + if (gp) + { + node->enm.is_template = 1; + register_template(ctx, node->enm.name, node); + } + + add_to_enum_list(ctx, node); // Register globally + + return node; +} -- cgit v1.2.3