diff options
| -rw-r--r-- | src/analysis/typecheck.c | 494 | ||||
| -rw-r--r-- | src/analysis/typecheck.h | 14 | ||||
| -rw-r--r-- | src/ast/ast.c | 445 | ||||
| -rw-r--r-- | src/ast/ast.h | 975 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 3531 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 6 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 1519 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 902 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 782 | ||||
| -rw-r--r-- | src/lexer/token.c | 730 | ||||
| -rw-r--r-- | src/lsp/json_rpc.c | 283 | ||||
| -rw-r--r-- | src/lsp/lsp_analysis.c | 620 | ||||
| -rw-r--r-- | src/lsp/lsp_index.c | 329 | ||||
| -rw-r--r-- | src/lsp/lsp_index.h | 41 | ||||
| -rw-r--r-- | src/lsp/lsp_main.c | 90 | ||||
| -rw-r--r-- | src/main.c | 540 | ||||
| -rw-r--r-- | src/parser/parser.h | 429 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 995 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 6407 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 6899 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 1351 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 4528 | ||||
| -rw-r--r-- | src/plugins/plugin_manager.c | 135 | ||||
| -rw-r--r-- | src/repl/repl.c | 2415 | ||||
| -rw-r--r-- | src/utils/utils.c | 1342 | ||||
| -rw-r--r-- | src/zen/zen_facts.c | 259 | ||||
| -rw-r--r-- | src/zen/zen_facts.h | 29 | ||||
| -rw-r--r-- | src/zprep.h | 188 |
28 files changed, 16444 insertions, 19834 deletions
diff --git a/src/analysis/typecheck.c b/src/analysis/typecheck.c index 903cb75..a19393b 100644 --- a/src/analysis/typecheck.c +++ b/src/analysis/typecheck.c @@ -1,279 +1,327 @@ - #include "typecheck.h" +#include "../ast/ast.h" #include <stdio.h> #include <stdlib.h> #include <string.h> // ** Internal Helpers ** -static void tc_error(TypeChecker *tc, Token t, const char *msg) -{ - fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line, t.col, msg); - tc->error_count++; +static void tc_error(TypeChecker *tc, Token t, const char *msg) { + fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line, + t.col, msg); + tc->error_count++; } -static void tc_enter_scope(TypeChecker *tc) -{ - Scope *s = malloc(sizeof(Scope)); - s->symbols = NULL; - s->parent = tc->current_scope; - tc->current_scope = s; +static void tc_enter_scope(TypeChecker *tc) { + Scope *s = malloc(sizeof(Scope)); + s->symbols = NULL; + s->parent = tc->current_scope; + tc->current_scope = s; } -static void tc_exit_scope(TypeChecker *tc) -{ - if (!tc->current_scope) - { - return; - } - Scope *old = tc->current_scope; - tc->current_scope = old->parent; - - Symbol *sym = old->symbols; - while (sym) - { - Symbol *next = sym->next; - free(sym); - sym = next; - } - free(old); +static void tc_exit_scope(TypeChecker *tc) { + if (!tc->current_scope) { + return; + } + Scope *old = tc->current_scope; + tc->current_scope = old->parent; + + Symbol *sym = old->symbols; + while (sym) { + Symbol *next = sym->next; + free(sym); + sym = next; + } + free(old); } -static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type, Token t) -{ - Symbol *s = malloc(sizeof(Symbol)); - memset(s, 0, sizeof(Symbol)); - s->name = strdup(name); - s->type_info = type; - s->decl_token = t; - s->next = tc->current_scope->symbols; - tc->current_scope->symbols = s; +static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type, + Token t) { + Symbol *s = malloc(sizeof(Symbol)); + memset(s, 0, sizeof(Symbol)); + s->name = strdup(name); + s->type_info = type; + s->decl_token = t; + s->next = tc->current_scope->symbols; + tc->current_scope->symbols = s; } -static Symbol *tc_lookup(TypeChecker *tc, const char *name) -{ - Scope *s = tc->current_scope; - while (s) - { - Symbol *curr = s->symbols; - while (curr) - { - if (0 == strcmp(curr->name, name)) - { - return curr; - } - curr = curr->next; - } - s = s->parent; +static Symbol *tc_lookup(TypeChecker *tc, const char *name) { + Scope *s = tc->current_scope; + while (s) { + Symbol *curr = s->symbols; + while (curr) { + if (0 == strcmp(curr->name, name)) { + return curr; + } + curr = curr->next; } - return NULL; + s = s->parent; + } + return NULL; +} + +static int is_integer_type(Type *t) { + if (!t) + return 0; + // Check all integer kinds defined in TypeKind + return (t->kind >= TYPE_I8 && t->kind <= TYPE_U128) || t->kind == TYPE_INT || + t->kind == TYPE_UINT || t->kind == TYPE_USIZE || t->kind == TYPE_ISIZE; +} + +static int is_signed_integer(Type *t) { + if (!t) + return 0; + return (t->kind == TYPE_I8 || t->kind == TYPE_I16 || t->kind == TYPE_I32 || + t->kind == TYPE_I64 || t->kind == TYPE_I128 || t->kind == TYPE_INT || + t->kind == TYPE_ISIZE); +} + +// Check if node is a literal integer +// This allows '0' to be used with unsigned types without casting. +static int is_safe_integer_literal(ASTNode *node) { + if (!node || node->type != NODE_EXPR_LITERAL) + return 0; + + // In Zen C, string/char literals have string_val set. + if (node->literal.string_val != NULL) + return 0; + + // In codegen.c, type_kind == 1 represents floats + if (node->literal.type_kind == 1) + return 0; + + // If it's not a string, char, or float, it's an integer literal. + // Parsed integer literals (e.g. 123) are positive. + // Negative numbers are handled as UnaryOp(-, Literal(123)), so this + // safe check correctly returns false for negative literals. + return 1; } // ** Node Checkers ** static void check_node(TypeChecker *tc, ASTNode *node); -static void check_block(TypeChecker *tc, ASTNode *block) -{ - tc_enter_scope(tc); - ASTNode *stmt = block->block.statements; - while (stmt) - { - check_node(tc, stmt); - stmt = stmt->next; - } - tc_exit_scope(tc); +static void check_block(TypeChecker *tc, ASTNode *block) { + tc_enter_scope(tc); + ASTNode *stmt = block->block.statements; + while (stmt) { + check_node(tc, stmt); + stmt = stmt->next; + } + tc_exit_scope(tc); } -static int check_type_compatibility(TypeChecker *tc, Type *target, Type *value, Token t) -{ - if (!target || !value) - { - return 1; // Can't check - } +static int check_type_compatibility(TypeChecker *tc, Type *target, + ASTNode *value_expr, Token t) { + if (!target || !value_expr) + return 1; + + Type *value_type = value_expr->type_info; + if (!value_type) + return 1; // Can't check yet - // Simple equality check for now... This will be changed. - if (!type_eq(target, value)) - { - - // For now we have strict equality on structure. - - // In Zen C (like C), void* is generic. - if (TYPE_POINTER == target->kind && TYPE_VOID == target->inner->kind) - { - return 1; - } - if (TYPE_POINTER == value->kind && TYPE_VOID == value->inner->kind) - { - return 1; - } - - // Exception: integer promotion/demotion. - - if (target->kind >= TYPE_I8 && target->kind <= TYPE_U64 && value->kind >= TYPE_I8 && - value->kind <= TYPE_U64) - { - return 1; - } - - char *t_str = type_to_string(target); - char *v_str = type_to_string(value); - char msg[256]; - snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str); - tc_error(tc, t, msg); - free(t_str); - free(v_str); - return 0; + // 1. Exact Match + if (type_eq(target, value_type)) + return 1; + + // 2. Void Pointer Generics (void* matches any pointer) + if (target->kind == TYPE_POINTER && target->inner->kind == TYPE_VOID) + return 1; + if (value_type->kind == TYPE_POINTER && value_type->inner->kind == TYPE_VOID) + return 1; + + // 3. Integer Promotion / Safety Checks + if (is_integer_type(target) && is_integer_type(value_type)) { + int target_signed = is_signed_integer(target); + int value_signed = is_signed_integer(value_type); + + // A. Signed/Unsigned Mismatch + if (target_signed != value_signed) { + // ERGONOMIC FIX: Allow implicit conversion IF it is a safe positive + // literal e.g. 'usize x = 0;' or 'if (len > 0)' + if (is_safe_integer_literal(value_expr)) { + return 1; // Safe! + } + + // Otherwise, warn strict + char *t_str = type_to_string(target); + char *v_str = type_to_string(value_type); + char msg[256]; + snprintf(msg, 255, + "Sign mismatch: cannot implicitly convert '%s' to '%s' (use " + "cast or unsigned literal)", + v_str, t_str); + tc_error(tc, t, msg); + free(t_str); + free(v_str); + return 0; } + + // B. Size truncation could be checked here (optional) return 1; + } + + // 4. Default Failure + char *t_str = type_to_string(target); + char *v_str = type_to_string(value_type); + char msg[256]; + snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str); + tc_error(tc, t, msg); + free(t_str); + free(v_str); + return 0; } -static void check_var_decl(TypeChecker *tc, ASTNode *node) -{ - if (node->var_decl.init_expr) - { - check_node(tc, node->var_decl.init_expr); +static void check_var_decl(TypeChecker *tc, ASTNode *node) { + if (node->var_decl.init_expr) { + check_node(tc, node->var_decl.init_expr); - Type *decl_type = node->type_info; - Type *init_type = node->var_decl.init_expr->type_info; + Type *decl_type = node->type_info; - if (decl_type && init_type) - { - check_type_compatibility(tc, decl_type, init_type, node->token); - } + if (decl_type) { + // Pass the full expression node to allow literal checking + check_type_compatibility(tc, decl_type, node->var_decl.init_expr, + node->token); } + } - // If type is not explicit, we should ideally infer it from init_expr. - Type *t = node->type_info; - if (!t && node->var_decl.init_expr) - { - t = node->var_decl.init_expr->type_info; - } + Type *t = node->type_info; + if (!t && node->var_decl.init_expr) { + t = node->var_decl.init_expr->type_info; + } - tc_add_symbol(tc, node->var_decl.name, t, node->token); + tc_add_symbol(tc, node->var_decl.name, t, node->token); + node->type_info = t; } -static void check_function(TypeChecker *tc, ASTNode *node) -{ - // Just to suppress the warning. - (void)tc_error; +static void check_function(TypeChecker *tc, ASTNode *node) { + (void)tc_error; - tc->current_func = node; - tc_enter_scope(tc); + tc->current_func = node; + tc_enter_scope(tc); - for (int i = 0; i < node->func.arg_count; i++) - { - if (node->func.param_names && node->func.param_names[i]) - { - tc_add_symbol(tc, node->func.param_names[i], NULL, (Token){0}); - } + for (int i = 0; i < node->func.arg_count; i++) { + if (node->func.param_names && node->func.param_names[i]) { + // FIXED: use arg_types from struct, not param_types + Type *t = (node->func.arg_types) ? node->func.arg_types[i] : NULL; + tc_add_symbol(tc, node->func.param_names[i], t, (Token){0}); } + } - check_node(tc, node->func.body); + check_node(tc, node->func.body); - tc_exit_scope(tc); - tc->current_func = NULL; + tc_exit_scope(tc); + tc->current_func = NULL; } -static void check_expr_var(TypeChecker *tc, ASTNode *node) -{ - Symbol *sym = tc_lookup(tc, node->var_ref.name); - if (!sym) - { - // Check global functions/contexts if not found in locals - // This is a naive check. - // We really want to warn here if it's truly unknown. - } - if (sym && sym->type_info) - { - node->type_info = sym->type_info; - } +static void check_expr_var(TypeChecker *tc, ASTNode *node) { + Symbol *sym = tc_lookup(tc, node->var_ref.name); + if (sym && sym->type_info) { + node->type_info = sym->type_info; + } } -static void check_node(TypeChecker *tc, ASTNode *node) -{ - if (!node) - { - return; +static void check_node(TypeChecker *tc, ASTNode *node) { + if (!node) { + return; + } + + switch (node->type) { + case NODE_ROOT: + check_node(tc, node->root.children); + break; + case NODE_BLOCK: + check_block(tc, node); + break; + case NODE_VAR_DECL: + check_var_decl(tc, node); + break; + case NODE_FUNCTION: + check_function(tc, node); + break; + case NODE_EXPR_VAR: + check_expr_var(tc, node); + break; + case NODE_RETURN: + if (node->ret.value) { + check_node(tc, node->ret.value); + // Check return type matches function signature + if (tc->current_func && tc->current_func->type_info) { + check_type_compatibility(tc, tc->current_func->type_info, + node->ret.value, node->token); + } } + break; + + case NODE_IF: + check_node(tc, node->if_stmt.condition); + check_node(tc, node->if_stmt.then_body); + check_node(tc, node->if_stmt.else_body); + break; + case NODE_WHILE: + check_node(tc, node->while_stmt.condition); + check_node(tc, node->while_stmt.body); + break; + case NODE_FOR: + tc_enter_scope(tc); + check_node(tc, node->for_stmt.init); + check_node(tc, node->for_stmt.condition); + check_node(tc, node->for_stmt.step); + check_node(tc, node->for_stmt.body); + tc_exit_scope(tc); + break; - switch (node->type) - { - case NODE_ROOT: - check_node(tc, node->root.children); - break; - case NODE_BLOCK: - check_block(tc, node); - break; - case NODE_VAR_DECL: - check_var_decl(tc, node); - break; - case NODE_FUNCTION: - check_function(tc, node); - break; - case NODE_EXPR_VAR: - check_expr_var(tc, node); - break; - case NODE_RETURN: - if (node->ret.value) - { - check_node(tc, node->ret.value); - } - - break; - - // Control flow with nested nodes. - case NODE_IF: - check_node(tc, node->if_stmt.condition); - check_node(tc, node->if_stmt.then_body); - check_node(tc, node->if_stmt.else_body); - break; - case NODE_WHILE: - check_node(tc, node->while_stmt.condition); - check_node(tc, node->while_stmt.body); - break; - case NODE_FOR: - tc_enter_scope(tc); // For loop init variable is scoped - check_node(tc, node->for_stmt.init); - check_node(tc, node->for_stmt.condition); - check_node(tc, node->for_stmt.step); - check_node(tc, node->for_stmt.body); - tc_exit_scope(tc); - break; - case NODE_EXPR_BINARY: - check_node(tc, node->binary.left); - check_node(tc, node->binary.right); - break; - case NODE_EXPR_CALL: - check_node(tc, node->call.callee); - check_node(tc, node->call.args); - break; - default: - // Generic recursion for lists. - break; - } + case NODE_EXPR_BINARY: + check_node(tc, node->binary.left); + check_node(tc, node->binary.right); - if (node->next) - { - check_node(tc, node->next); + // Infer type from left operand + if (node->binary.left->type_info) { + node->type_info = node->binary.left->type_info; + + // Perform the comparison check using the robust compatibility logic + // This will trigger is_safe_integer_literal for cases like (usize > 0) + check_type_compatibility(tc, node->binary.left->type_info, + node->binary.right, node->token); + } + break; + + case NODE_EXPR_CALL: + check_node(tc, node->call.callee); + check_node(tc, node->call.args); + // Propagate return type + if (node->call.callee->type_info) { + node->type_info = node->call.callee->type_info; } + break; + + case NODE_EXPR_LITERAL: + // Literals usually have their type set during parsing/inference phases + break; + + default: + break; + } + + if (node->next) { + check_node(tc, node->next); + } } // ** Entry Point ** -int check_program(ParserContext *ctx, ASTNode *root) -{ - TypeChecker tc = {0}; - tc.pctx = ctx; +int check_program(ParserContext *ctx, ASTNode *root) { + TypeChecker tc = {0}; + tc.pctx = ctx; - printf("[TypeCheck] Starting semantic analysis...\n"); - check_node(&tc, root); + printf("[TypeCheck] Starting semantic analysis...\n"); + check_node(&tc, root); - if (tc.error_count > 0) - { - printf("[TypeCheck] Found %d errors.\n", tc.error_count); - return 1; - } - printf("[TypeCheck] Passed.\n"); - return 0; + if (tc.error_count > 0) { + printf("[TypeCheck] Found %d errors.\n", tc.error_count); + return 1; + } + printf("[TypeCheck] Passed.\n"); + return 0; } diff --git a/src/analysis/typecheck.h b/src/analysis/typecheck.h index fe51c4d..a680f3a 100644 --- a/src/analysis/typecheck.h +++ b/src/analysis/typecheck.h @@ -7,13 +7,13 @@ // Type Checker Context // Holds the state during the semantic analysis pass. // Unlike the parser, this focuses on semantic validity (types, definitions). -typedef struct TypeChecker -{ - ParserContext *pctx; // Reference to global parser context (for lookups) - Scope *current_scope; // Current lexical scope - ASTNode *current_func; // Current function being checked (for return type checks) - int error_count; // Number of errors found - int warning_count; // Number of recommendations/warnings +typedef struct TypeChecker { + ParserContext *pctx; // Reference to global parser context (for lookups) + Scope *current_scope; // Current lexical scope + ASTNode + *current_func; // Current function being checked (for return type checks) + int error_count; // Number of errors found + int warning_count; // Number of recommendations/warnings } TypeChecker; // Main Entry Point diff --git a/src/ast/ast.c b/src/ast/ast.c index f34370e..023180d 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -5,279 +5,242 @@ #include <stdlib.h> #include <string.h> -typedef struct TraitReg -{ - char *name; - struct TraitReg *next; +typedef struct TraitReg { + char *name; + struct TraitReg *next; } TraitReg; static TraitReg *registered_traits = NULL; -void register_trait(const char *name) -{ - TraitReg *r = xmalloc(sizeof(TraitReg)); - r->name = xstrdup(name); - r->next = registered_traits; - registered_traits = r; +void register_trait(const char *name) { + TraitReg *r = xmalloc(sizeof(TraitReg)); + r->name = xstrdup(name); + r->next = registered_traits; + registered_traits = r; } -int is_trait(const char *name) -{ - TraitReg *r = registered_traits; - while (r) - { - if (0 == strcmp(r->name, name)) - { - return 1; - } - r = r->next; +int is_trait(const char *name) { + TraitReg *r = registered_traits; + while (r) { + if (0 == strcmp(r->name, name)) { + return 1; } - return 0; + r = r->next; + } + return 0; } -ASTNode *ast_create(NodeType type) -{ - ASTNode *node = xmalloc(sizeof(ASTNode)); - memset(node, 0, sizeof(ASTNode)); - node->type = type; - return node; +ASTNode *ast_create(NodeType type) { + ASTNode *node = xmalloc(sizeof(ASTNode)); + memset(node, 0, sizeof(ASTNode)); + node->type = type; + return node; } -void ast_free(ASTNode *node) -{ - (void)node; - return; +void ast_free(ASTNode *node) { + (void)node; + return; } -Type *type_new(TypeKind kind) -{ - Type *t = xmalloc(sizeof(Type)); - t->kind = kind; - t->name = NULL; - t->inner = NULL; - t->args = NULL; - t->arg_count = 0; - t->is_const = 0; - t->array_size = 0; - return t; +Type *type_new(TypeKind kind) { + Type *t = xmalloc(sizeof(Type)); + t->kind = kind; + t->name = NULL; + t->inner = NULL; + t->args = NULL; + t->arg_count = 0; + t->is_const = 0; + t->array_size = 0; + return t; } -Type *type_new_ptr(Type *inner) -{ - Type *t = type_new(TYPE_POINTER); - t->inner = inner; - return t; +Type *type_new_ptr(Type *inner) { + Type *t = type_new(TYPE_POINTER); + t->inner = inner; + return t; } -int is_char_ptr(Type *t) -{ - // Handle both primitive char* and legacy struct char*. - if (TYPE_POINTER == t->kind && TYPE_CHAR == t->inner->kind) - { - return 1; - } - if (TYPE_POINTER == t->kind && TYPE_STRUCT == t->inner->kind && - 0 == strcmp(t->inner->name, "char")) - { - return 1; - } - return 0; +int is_char_ptr(Type *t) { + // Handle both primitive char* and legacy struct char*. + if (TYPE_POINTER == t->kind && TYPE_CHAR == t->inner->kind) { + return 1; + } + if (TYPE_POINTER == t->kind && TYPE_STRUCT == t->inner->kind && + 0 == strcmp(t->inner->name, "char")) { + return 1; + } + return 0; } -int is_integer_type(Type *t) -{ - if (!t) - { - return 0; - } - - return (t->kind == TYPE_INT || t->kind == TYPE_CHAR || t->kind == TYPE_BOOL || - t->kind == TYPE_I8 || t->kind == TYPE_U8 || t->kind == TYPE_I16 || - t->kind == TYPE_U16 || t->kind == TYPE_I32 || t->kind == TYPE_U32 || - t->kind == TYPE_I64 || t->kind == TYPE_U64 || t->kind == TYPE_USIZE || - t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || t->kind == TYPE_RUNE || - t->kind == TYPE_UINT || t->kind == TYPE_I128 || t->kind == TYPE_U128); +int is_integer_type(Type *t) { + if (!t) { + return 0; + } + + return (t->kind == TYPE_INT || t->kind == TYPE_CHAR || t->kind == TYPE_BOOL || + t->kind == TYPE_I8 || t->kind == TYPE_U8 || t->kind == TYPE_I16 || + t->kind == TYPE_U16 || t->kind == TYPE_I32 || t->kind == TYPE_U32 || + t->kind == TYPE_I64 || t->kind == TYPE_U64 || t->kind == TYPE_USIZE || + t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || + t->kind == TYPE_RUNE || t->kind == TYPE_UINT || + t->kind == TYPE_I128 || t->kind == TYPE_U128); } -int is_float_type(Type *t) -{ - if (!t) - { - return 0; - } +int is_float_type(Type *t) { + if (!t) { + return 0; + } - return (t->kind == TYPE_FLOAT || t->kind == TYPE_F32 || t->kind == TYPE_F64); + return (t->kind == TYPE_FLOAT || t->kind == TYPE_F32 || t->kind == TYPE_F64); } -int type_eq(Type *a, Type *b) -{ - if (!a || !b) - { - return 0; - } - - if (a == b) - { - return 1; - } - - // Lax integer matching (bool == int, char == i8, etc.). - if (is_integer_type(a) && is_integer_type(b)) - { - return 1; - } - - // Lax float matching. - if (is_float_type(a) && is_float_type(b)) - { - return 1; - } - - // String Literal vs char* - if (a->kind == TYPE_STRING && is_char_ptr(b)) - { - return 1; - } - - if (b->kind == TYPE_STRING && is_char_ptr(a)) - { - return 1; - } - - if (a->kind != b->kind) - { - return 0; - } +int type_eq(Type *a, Type *b) { + if (!a || !b) { + return 0; + } - if (a->kind == TYPE_STRUCT || a->kind == TYPE_GENERIC) - { - return 0 == strcmp(a->name, b->name); - } - if (a->kind == TYPE_POINTER || a->kind == TYPE_ARRAY) - { - return type_eq(a->inner, b->inner); - } + if (a == b) { + return 1; + } + // Lax integer matching (bool == int, char == i8, etc.). + if (is_integer_type(a) && is_integer_type(b)) { return 1; -} + } -char *type_to_string(Type *t) -{ - if (!t) - { - return xstrdup("void"); - } + // Lax float matching. + if (is_float_type(a) && is_float_type(b)) { + return 1; + } - switch (t->kind) - { - case TYPE_VOID: - return xstrdup("void"); - case TYPE_BOOL: - return xstrdup("bool"); - case TYPE_STRING: - return xstrdup("string"); - case TYPE_CHAR: - return xstrdup("char"); - case TYPE_I8: - return xstrdup("int8_t"); - case TYPE_U8: - return xstrdup("uint8_t"); - case TYPE_I16: - return xstrdup("int16_t"); - case TYPE_U16: - return xstrdup("uint16_t"); - case TYPE_I32: - return xstrdup("int32_t"); - case TYPE_U32: - return xstrdup("uint32_t"); - case TYPE_I64: - return xstrdup("int64_t"); - case TYPE_U64: - return xstrdup("uint64_t"); - case TYPE_F32: - return xstrdup("float"); - case TYPE_F64: - return xstrdup("double"); - case TYPE_USIZE: - return xstrdup("size_t"); - case TYPE_ISIZE: - return xstrdup("ptrdiff_t"); - case TYPE_BYTE: - return xstrdup("uint8_t"); - case TYPE_I128: - return xstrdup("__int128"); - case TYPE_U128: - return xstrdup("unsigned __int128"); - case TYPE_RUNE: - return xstrdup("int32_t"); - case TYPE_UINT: - return xstrdup("unsigned int"); - case TYPE_INT: - return xstrdup("int"); - case TYPE_FLOAT: - return xstrdup("float"); - - case TYPE_POINTER: - { - char *inner = type_to_string(t->inner); - if (t->is_restrict) - { - char *res = xmalloc(strlen(inner) + 16); - sprintf(res, "%s* __restrict", inner); - return res; - } - else - { - char *res = xmalloc(strlen(inner) + 2); - sprintf(res, "%s*", inner); - return res; - } - } + // String Literal vs char* + if (a->kind == TYPE_STRING && is_char_ptr(b)) { + return 1; + } - case TYPE_ARRAY: - { - char *inner = type_to_string(t->inner); + if (b->kind == TYPE_STRING && is_char_ptr(a)) { + return 1; + } - if (t->array_size > 0) - { - char *res = xmalloc(strlen(inner) + 20); - sprintf(res, "%s[%d]", inner, t->array_size); - return res; - } + if (a->kind != b->kind) { + return 0; + } - char *res = xmalloc(strlen(inner) + 7); - sprintf(res, "Slice_%s", inner); - return res; - } + if (a->kind == TYPE_STRUCT || a->kind == TYPE_GENERIC) { + return 0 == strcmp(a->name, b->name); + } + if (a->kind == TYPE_POINTER || a->kind == TYPE_ARRAY) { + return type_eq(a->inner, b->inner); + } - case TYPE_FUNCTION: - if (t->inner) - { - free(type_to_string(t->inner)); - } - - return xstrdup("z_closure_T"); - - case TYPE_STRUCT: - case TYPE_GENERIC: - { - if (t->arg_count > 0) - { - char *base = t->name; - char *arg = type_to_string(t->args[0]); - char *clean_arg = sanitize_mangled_name(arg); - - char *res = xmalloc(strlen(base) + strlen(clean_arg) + 2); - sprintf(res, "%s_%s", base, clean_arg); - - free(arg); - free(clean_arg); - return res; - } - return xstrdup(t->name); - } + return 1; +} - default: - return xstrdup("unknown"); - } +char *type_to_string(Type *t) { + if (!t) { + return xstrdup("void"); + } + + switch (t->kind) { + case TYPE_VOID: + return xstrdup("void"); + case TYPE_BOOL: + return xstrdup("bool"); + case TYPE_STRING: + return xstrdup("string"); + case TYPE_CHAR: + return xstrdup("char"); + case TYPE_I8: + return xstrdup("int8_t"); + case TYPE_U8: + return xstrdup("uint8_t"); + case TYPE_I16: + return xstrdup("int16_t"); + case TYPE_U16: + return xstrdup("uint16_t"); + case TYPE_I32: + return xstrdup("int32_t"); + case TYPE_U32: + return xstrdup("uint32_t"); + case TYPE_I64: + return xstrdup("int64_t"); + case TYPE_U64: + return xstrdup("uint64_t"); + case TYPE_F32: + return xstrdup("float"); + case TYPE_F64: + return xstrdup("double"); + case TYPE_USIZE: + return xstrdup("size_t"); + case TYPE_ISIZE: + return xstrdup("ptrdiff_t"); + case TYPE_BYTE: + return xstrdup("uint8_t"); + case TYPE_I128: + return xstrdup("__int128"); + case TYPE_U128: + return xstrdup("unsigned __int128"); + case TYPE_RUNE: + return xstrdup("int32_t"); + case TYPE_UINT: + return xstrdup("unsigned int"); + case TYPE_INT: + return xstrdup("int"); + case TYPE_FLOAT: + return xstrdup("float"); + + case TYPE_POINTER: { + char *inner = type_to_string(t->inner); + if (t->is_restrict) { + char *res = xmalloc(strlen(inner) + 16); + sprintf(res, "%s* __restrict", inner); + return res; + } else { + char *res = xmalloc(strlen(inner) + 2); + sprintf(res, "%s*", inner); + return res; + } + } + + case TYPE_ARRAY: { + char *inner = type_to_string(t->inner); + + if (t->array_size > 0) { + char *res = xmalloc(strlen(inner) + 20); + sprintf(res, "%s[%d]", inner, t->array_size); + return res; + } + + char *res = xmalloc(strlen(inner) + 7); + sprintf(res, "Slice_%s", inner); + return res; + } + + case TYPE_FUNCTION: + if (t->inner) { + free(type_to_string(t->inner)); + } + + return xstrdup("z_closure_T"); + + case TYPE_STRUCT: + case TYPE_GENERIC: { + if (t->arg_count > 0) { + char *base = t->name; + char *arg = type_to_string(t->args[0]); + char *clean_arg = sanitize_mangled_name(arg); + + char *res = xmalloc(strlen(base) + strlen(clean_arg) + 2); + sprintf(res, "%s_%s", base, clean_arg); + + free(arg); + free(clean_arg); + return res; + } + return xstrdup(t->name); + } + + default: + return xstrdup("unknown"); + } } diff --git a/src/ast/ast.h b/src/ast/ast.h index a5d3d0a..0fa2e24 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -11,531 +11,472 @@ typedef struct ASTNode ASTNode; // ** Formal Type System ** // Used for Generics, Type Inference, and robust pointer handling. -typedef enum -{ - TYPE_VOID, - TYPE_BOOL, - TYPE_CHAR, - TYPE_STRING, - TYPE_U0, - TYPE_I8, - TYPE_U8, - TYPE_I16, - TYPE_U16, - TYPE_I32, - TYPE_U32, - TYPE_I64, - TYPE_U64, - TYPE_I128, - TYPE_U128, - TYPE_F32, - TYPE_F64, - TYPE_INT, - TYPE_FLOAT, - TYPE_USIZE, - TYPE_ISIZE, - TYPE_BYTE, - TYPE_RUNE, - TYPE_UINT, - TYPE_STRUCT, - TYPE_ENUM, - TYPE_POINTER, - TYPE_ARRAY, - TYPE_FUNCTION, - TYPE_GENERIC, - TYPE_UNKNOWN +typedef enum { + TYPE_VOID, + TYPE_BOOL, + TYPE_CHAR, + TYPE_STRING, + TYPE_U0, + TYPE_I8, + TYPE_U8, + TYPE_I16, + TYPE_U16, + TYPE_I32, + TYPE_U32, + TYPE_I64, + TYPE_U64, + TYPE_I128, + TYPE_U128, + TYPE_F32, + TYPE_F64, + TYPE_INT, + TYPE_FLOAT, + TYPE_USIZE, + TYPE_ISIZE, + TYPE_BYTE, + TYPE_RUNE, + TYPE_UINT, + TYPE_STRUCT, + TYPE_ENUM, + TYPE_POINTER, + TYPE_ARRAY, + TYPE_FUNCTION, + TYPE_GENERIC, + TYPE_UNKNOWN } TypeKind; -typedef struct Type -{ - TypeKind kind; - char *name; // For STRUCT, GENERIC, ENUM. - struct Type *inner; // For POINTER, ARRAY. - struct Type **args; // For GENERIC args. - int arg_count; - int is_const; - union - { - int array_size; // For fixed-size arrays [T; N]. - int is_varargs; // For function types (...). - int is_restrict; // For restrict pointers. - int has_drop; // For RAII: does this type implement Drop? - }; +typedef struct Type { + TypeKind kind; + char *name; // For STRUCT, GENERIC, ENUM. + struct Type *inner; // For POINTER, ARRAY. + struct Type **args; // For GENERIC args. + int arg_count; + int is_const; + union { + int array_size; // For fixed-size arrays [T; N]. + int is_varargs; // For function types (...). + int is_restrict; // For restrict pointers. + int has_drop; // For RAII: does this type implement Drop? + }; } Type; // ** AST Node Types ** -typedef enum -{ - NODE_ROOT, - NODE_FUNCTION, - NODE_BLOCK, - NODE_RETURN, - NODE_VAR_DECL, - NODE_CONST, - NODE_TYPE_ALIAS, - NODE_IF, - NODE_WHILE, - NODE_FOR, - NODE_FOR_RANGE, - NODE_LOOP, - NODE_REPEAT, - NODE_UNLESS, - NODE_GUARD, - NODE_BREAK, - NODE_CONTINUE, - NODE_MATCH, - NODE_MATCH_CASE, - NODE_EXPR_BINARY, - NODE_EXPR_UNARY, - NODE_EXPR_LITERAL, - NODE_EXPR_VAR, - NODE_EXPR_CALL, - NODE_EXPR_MEMBER, - NODE_EXPR_INDEX, - NODE_EXPR_CAST, - NODE_EXPR_SIZEOF, - NODE_EXPR_STRUCT_INIT, - NODE_EXPR_ARRAY_LITERAL, - NODE_EXPR_SLICE, - NODE_STRUCT, - NODE_FIELD, - NODE_ENUM, - NODE_ENUM_VARIANT, - NODE_TRAIT, - NODE_IMPL, - NODE_IMPL_TRAIT, - NODE_INCLUDE, - NODE_RAW_STMT, - NODE_TEST, - NODE_ASSERT, - NODE_DEFER, - NODE_DESTRUCT_VAR, - NODE_TERNARY, - NODE_ASM, - NODE_LAMBDA, - NODE_PLUGIN, - NODE_GOTO, - NODE_LABEL, - NODE_DO_WHILE, - NODE_TYPEOF, - NODE_TRY, - NODE_REFLECTION, - NODE_AWAIT, - NODE_REPL_PRINT +typedef enum { + NODE_ROOT, + NODE_FUNCTION, + NODE_BLOCK, + NODE_RETURN, + NODE_VAR_DECL, + NODE_CONST, + NODE_TYPE_ALIAS, + NODE_IF, + NODE_WHILE, + NODE_FOR, + NODE_FOR_RANGE, + NODE_LOOP, + NODE_REPEAT, + NODE_UNLESS, + NODE_GUARD, + NODE_BREAK, + NODE_CONTINUE, + NODE_MATCH, + NODE_MATCH_CASE, + NODE_EXPR_BINARY, + NODE_EXPR_UNARY, + NODE_EXPR_LITERAL, + NODE_EXPR_VAR, + NODE_EXPR_CALL, + NODE_EXPR_MEMBER, + NODE_EXPR_INDEX, + NODE_EXPR_CAST, + NODE_EXPR_SIZEOF, + NODE_EXPR_STRUCT_INIT, + NODE_EXPR_ARRAY_LITERAL, + NODE_EXPR_SLICE, + NODE_STRUCT, + NODE_FIELD, + NODE_ENUM, + NODE_ENUM_VARIANT, + NODE_TRAIT, + NODE_IMPL, + NODE_IMPL_TRAIT, + NODE_INCLUDE, + NODE_RAW_STMT, + NODE_TEST, + NODE_ASSERT, + NODE_DEFER, + NODE_DESTRUCT_VAR, + NODE_TERNARY, + NODE_ASM, + NODE_LAMBDA, + NODE_PLUGIN, + NODE_GOTO, + NODE_LABEL, + NODE_DO_WHILE, + NODE_TYPEOF, + NODE_TRY, + NODE_REFLECTION, + NODE_AWAIT, + NODE_REPL_PRINT } NodeType; // ** AST Node Structure ** -struct ASTNode -{ - NodeType type; - ASTNode *next; - int line; // Source line number for debugging. - - // Type information. - char *resolved_type; // Legacy string representation (for example: "int", - // "User*"). > Yes, 'legacy' is a thing, this is the - // third iteration > of this project (for now). - Type *type_info; // Formal type object (for inference/generics). - Token token; - Token definition_token; // For LSP: Location where the symbol used in this - // node was defined. - - union - { - struct - { - ASTNode *children; - } root; - - struct - { - char *name; - char *args; // Legacy string args. - char *ret_type; // Legacy string return type. - ASTNode *body; - Type **arg_types; - char **defaults; - char **param_names; // Explicit parameter names. - int arg_count; - Type *ret_type_info; - int is_varargs; - int is_inline; - int must_use; // @must_use: warn if return value is discarded. - // GCC attributes - int noinline; // @noinline - int constructor; // @constructor - int destructor; // @destructor - int unused; // @unused - int weak; // @weak - int is_export; // @export (visibility default). - int cold; // @cold - int hot; // @hot - int noreturn; // @noreturn - int pure; // @pure - char *section; // @section("name") - int is_async; // async function - int is_comptime; // @comptime function - } func; - - struct - { - ASTNode *statements; - } block; - - struct - { - ASTNode *value; - } ret; - - struct - { - char *name; - char *type_str; - ASTNode *init_expr; - Type *type_info; - int is_autofree; - int is_mutable; - int is_static; - } var_decl; - - struct - { - char *name; - Type *payload; - int tag_id; - } variant; - - struct - { - char *name; - ASTNode *variants; - int is_template; - char *generic_param; - } enm; - - struct - { - char *alias; - char *original_type; - } type_alias; - - struct - { - ASTNode *condition; - ASTNode *then_body; - ASTNode *else_body; - } if_stmt; - - struct - { - ASTNode *condition; - ASTNode *body; - char *loop_label; - } while_stmt; - - struct - { - ASTNode *init; - ASTNode *condition; - ASTNode *step; - ASTNode *body; - char *loop_label; - } for_stmt; - - struct - { - char *var_name; - ASTNode *start; - ASTNode *end; - char *step; - ASTNode *body; - } for_range; - - struct - { - ASTNode *body; - char *loop_label; - } loop_stmt; - - struct - { - char *count; - ASTNode *body; - } repeat_stmt; - - struct - { - ASTNode *condition; - ASTNode *body; - } unless_stmt; - - struct - { - ASTNode *condition; - ASTNode *body; - } guard_stmt; - - struct - { - ASTNode *condition; - ASTNode *body; - char *loop_label; - } do_while_stmt; - - struct - { - ASTNode *expr; - ASTNode *cases; - } match_stmt; - - struct - { - char *pattern; - char *binding_name; - int is_destructuring; - ASTNode *guard; - ASTNode *body; - int is_default; - } match_case; - - struct - { - char *op; - ASTNode *left; - ASTNode *right; - } binary; - - struct - { - char *op; - ASTNode *operand; - } unary; - - struct - { - int type_kind; - unsigned long long int_val; - double float_val; - char *string_val; - } literal; - - struct - { - char *name; - char *suggestion; - } var_ref; - - struct - { - ASTNode *callee; - ASTNode *args; - char **arg_names; - int arg_count; - } call; - - struct - { - ASTNode *target; - char *field; - int is_pointer_access; - } member; - - struct - { - ASTNode *array; - ASTNode *index; - } index; - - struct - { - ASTNode *array; - ASTNode *start; - ASTNode *end; - } slice; - - struct - { - char *target_type; - ASTNode *expr; - } cast; - - struct - { - char *struct_name; - ASTNode *fields; - } struct_init; - - struct - { - ASTNode *elements; - int count; - } array_literal; - - struct - { - char *name; - ASTNode *fields; - int is_template; - char *generic_param; - char *parent; - int is_union; - int is_packed; // @packed attribute. - int align; // @align(N) attribute, 0 = default. - int is_incomplete; // Forward declaration (prototype) - } strct; - - struct - { - char *name; - char *type; - int bit_width; - } field; - - struct - { - char *name; - ASTNode *methods; - } trait; - - struct - { - char *struct_name; - ASTNode *methods; - } impl; - - struct - { - char *trait_name; - char *target_type; - ASTNode *methods; - } impl_trait; - - struct - { - char *path; - int is_system; - } include; - - struct - { - char *content; - char **used_symbols; - int used_symbol_count; - } raw_stmt; - - struct - { - char *name; - ASTNode *body; - } test_stmt; - - struct - { - ASTNode *condition; - char *message; - } assert_stmt; - - struct - { - ASTNode *stmt; - } defer_stmt; - - struct - { - char *plugin_name; - char *body; - } plugin_stmt; - - struct - { - char **names; - int count; - ASTNode *init_expr; - int is_struct_destruct; - char *struct_name; // "Point" (or NULL if unchecked/inferred). - char **field_names; // NULL if same as 'names', otherwise mapped. - int is_guard; - char *guard_variant; // "Some", "Ok". - ASTNode *else_block; - } destruct; - - struct - { - ASTNode *cond; - ASTNode *true_expr; - ASTNode *false_expr; - } ternary; - - struct - { - char *code; - int is_volatile; - char **outputs; - char **output_modes; - char **inputs; - char **clobbers; - int num_outputs; - int num_inputs; - int num_clobbers; - } asm_stmt; - - struct - { - char **param_names; - char **param_types; - char *return_type; - ASTNode *body; - int num_params; - int lambda_id; - int is_expression; - char **captured_vars; - char **captured_types; - int num_captures; - } lambda; - - struct - { - char *target_type; - ASTNode *expr; - } size_of; - - struct - { - char *label_name; - ASTNode *goto_expr; - } goto_stmt; - - struct - { - char *label_name; - } label_stmt; - - struct - { - char *target_label; - } break_stmt; - - struct - { - char *target_label; - } continue_stmt; - - struct - { - ASTNode *expr; - } try_stmt; - - struct - { - int kind; // 0=type_name, 1=fields. - Type *target_type; - } reflection; - - struct - { - ASTNode *expr; - } repl_print; - }; +struct ASTNode { + NodeType type; + ASTNode *next; + int line; // Source line number for debugging. + + // Type information. + char *resolved_type; // Legacy string representation (for example: "int", + // "User*"). > Yes, 'legacy' is a thing, this is the + // third iteration > of this project (for now). + Type *type_info; // Formal type object (for inference/generics). + Token token; + Token definition_token; // For LSP: Location where the symbol used in this + // node was defined. + + union { + struct { + ASTNode *children; + } root; + + struct { + char *name; + char *args; // Legacy string args. + char *ret_type; // Legacy string return type. + ASTNode *body; + Type **arg_types; + char **defaults; + char **param_names; // Explicit parameter names. + int arg_count; + Type *ret_type_info; + int is_varargs; + int is_inline; + int must_use; // @must_use: warn if return value is discarded. + // GCC attributes + int noinline; // @noinline + int constructor; // @constructor + int destructor; // @destructor + int unused; // @unused + int weak; // @weak + int is_export; // @export (visibility default). + int cold; // @cold + int hot; // @hot + int noreturn; // @noreturn + int pure; // @pure + char *section; // @section("name") + int is_async; // async function + int is_comptime; // @comptime function + } func; + + struct { + ASTNode *statements; + } block; + + struct { + ASTNode *value; + } ret; + + struct { + char *name; + char *type_str; + ASTNode *init_expr; + Type *type_info; + int is_autofree; + int is_mutable; + int is_static; + } var_decl; + + struct { + char *name; + Type *payload; + int tag_id; + } variant; + + struct { + char *name; + ASTNode *variants; + int is_template; + char *generic_param; + } enm; + + struct { + char *alias; + char *original_type; + } type_alias; + + struct { + ASTNode *condition; + ASTNode *then_body; + ASTNode *else_body; + } if_stmt; + + struct { + ASTNode *condition; + ASTNode *body; + char *loop_label; + } while_stmt; + + struct { + ASTNode *init; + ASTNode *condition; + ASTNode *step; + ASTNode *body; + char *loop_label; + } for_stmt; + + struct { + char *var_name; + ASTNode *start; + ASTNode *end; + char *step; + ASTNode *body; + } for_range; + + struct { + ASTNode *body; + char *loop_label; + } loop_stmt; + + struct { + char *count; + ASTNode *body; + } repeat_stmt; + + struct { + ASTNode *condition; + ASTNode *body; + } unless_stmt; + + struct { + ASTNode *condition; + ASTNode *body; + } guard_stmt; + + struct { + ASTNode *condition; + ASTNode *body; + char *loop_label; + } do_while_stmt; + + struct { + ASTNode *expr; + ASTNode *cases; + } match_stmt; + + struct { + char *pattern; + char *binding_name; + int is_destructuring; + ASTNode *guard; + ASTNode *body; + int is_default; + } match_case; + + struct { + char *op; + ASTNode *left; + ASTNode *right; + } binary; + + struct { + char *op; + ASTNode *operand; + } unary; + + struct { + int type_kind; + unsigned long long int_val; + double float_val; + char *string_val; + } literal; + + struct { + char *name; + char *suggestion; + } var_ref; + + struct { + ASTNode *callee; + ASTNode *args; + char **arg_names; + int arg_count; + } call; + + struct { + ASTNode *target; + char *field; + int is_pointer_access; + } member; + + struct { + ASTNode *array; + ASTNode *index; + } index; + + struct { + ASTNode *array; + ASTNode *start; + ASTNode *end; + } slice; + + struct { + char *target_type; + ASTNode *expr; + } cast; + + struct { + char *struct_name; + ASTNode *fields; + } struct_init; + + struct { + ASTNode *elements; + int count; + } array_literal; + + struct { + char *name; + ASTNode *fields; + int is_template; + char *generic_param; + char *parent; + int is_union; + int is_packed; // @packed attribute. + int align; // @align(N) attribute, 0 = default. + int is_incomplete; // Forward declaration (prototype) + } strct; + + struct { + char *name; + char *type; + int bit_width; + } field; + + struct { + char *name; + ASTNode *methods; + } trait; + + struct { + char *struct_name; + ASTNode *methods; + } impl; + + struct { + char *trait_name; + char *target_type; + ASTNode *methods; + } impl_trait; + + struct { + char *path; + int is_system; + } include; + + struct { + char *content; + char **used_symbols; + int used_symbol_count; + } raw_stmt; + + struct { + char *name; + ASTNode *body; + } test_stmt; + + struct { + ASTNode *condition; + char *message; + } assert_stmt; + + struct { + ASTNode *stmt; + } defer_stmt; + + struct { + char *plugin_name; + char *body; + } plugin_stmt; + + struct { + char **names; + int count; + ASTNode *init_expr; + int is_struct_destruct; + char *struct_name; // "Point" (or NULL if unchecked/inferred). + char **field_names; // NULL if same as 'names', otherwise mapped. + int is_guard; + char *guard_variant; // "Some", "Ok". + ASTNode *else_block; + } destruct; + + struct { + ASTNode *cond; + ASTNode *true_expr; + ASTNode *false_expr; + } ternary; + + struct { + char *code; + int is_volatile; + char **outputs; + char **output_modes; + char **inputs; + char **clobbers; + int num_outputs; + int num_inputs; + int num_clobbers; + } asm_stmt; + + struct { + char **param_names; + char **param_types; + char *return_type; + ASTNode *body; + int num_params; + int lambda_id; + int is_expression; + char **captured_vars; + char **captured_types; + int num_captures; + } lambda; + + struct { + char *target_type; + ASTNode *expr; + } size_of; + + struct { + char *label_name; + ASTNode *goto_expr; + } goto_stmt; + + struct { + char *label_name; + } label_stmt; + + struct { + char *target_label; + } break_stmt; + + struct { + char *target_label; + } continue_stmt; + + struct { + ASTNode *expr; + } try_stmt; + + struct { + int kind; // 0=type_name, 1=fields. + Type *target_type; + } reflection; + + struct { + ASTNode *expr; + } repl_print; + }; }; // ** Functions ** diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 86295e4..e3e1ac6 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -20,2073 +20,1652 @@ // 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); +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, "({ "); + emit_auto_type(ctx, node->match_stmt.expr, node->token, out); + fprintf(out, " _m_%d = ", id); + if (is_self) { + fprintf(out, "*("); + } + codegen_expression(ctx, node->match_stmt.expr, out); + if (is_self) { + fprintf(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; + } + } - fprintf(out, "({ "); - emit_auto_type(ctx, node->match_stmt.expr, node->token, out); - fprintf(out, " _m_%d = ", id); - if (is_self) - { - fprintf(out, "*("); + ASTNode *c = node->match_stmt.cases; + int first = 1; + while (c) { + if (!first) { + fprintf(out, " else "); } - codegen_expression(ctx, node->match_stmt.expr, out); - if (is_self) - { - fprintf(out, ")"); + 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 { + EnumVariantReg *reg = find_enum_variant(ctx, c->match_case.pattern); + if (reg) { + fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); + } else if (c->match_case.pattern[0] == '"') { + fprintf(out, "strcmp(_m_%d, %s) == 0", id, c->match_case.pattern); + } else if (isdigit(c->match_case.pattern[0]) || + c->match_case.pattern[0] == '-') { + // Numeric pattern + fprintf(out, "_m_%d == %s", id, c->match_case.pattern); + } else if (c->match_case.pattern[0] == '\'') { + // Char literal pattern + fprintf(out, "_m_%d == %s", id, c->match_case.pattern); + } else { + fprintf(out, "1"); + } + } + fprintf(out, ") { "); + if (c->match_case.binding_name) { + if (is_option) { + if (strstr(g_config.cc, "tcc")) { + fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, + c->match_case.binding_name, id); + } else { + fprintf(out, "__auto_type %s = _m_%d.val; ", + c->match_case.binding_name, id); + } + } + if (is_result) { + if (strcmp(c->match_case.pattern, "Ok") == 0) { + if (strstr(g_config.cc, "tcc")) { + fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, + c->match_case.binding_name, id); + } else { + fprintf(out, "__auto_type %s = _m_%d.val; ", + c->match_case.binding_name, id); + } + } else { + if (strstr(g_config.cc, "tcc")) { + fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id, + c->match_case.binding_name, id); + } else { + fprintf(out, "__auto_type %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; + } + fprintf(out, "__auto_type %s = _m_%d.data.%s; ", + c->match_case.binding_name, id, f); + } } - fprintf(out, "; "); - if (is_expr) - { - fprintf(out, "%s _r_%d; ", ret_type, id); + // 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); + } } - 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); + fprintf(out, " }"); + first = 0; + c = c->next; + } - 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 (is_expr) { + fprintf(out, " _r_%d; })", id); + } else { + fprintf(out, " })"); + } +} - 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); - } +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; } - v = v->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); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, ", "); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); + if (strcmp(node->binary.op, "!=") == 0) { + 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, ")"); + } + } 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: + 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; + } + } } - 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 - { - EnumVariantReg *reg = find_enum_variant(ctx, c->match_case.pattern); - if (reg) - { - fprintf(out, "_m_%d.tag == %d", id, reg->tag_id); - } - else if (c->match_case.pattern[0] == '"') - { - fprintf(out, "strcmp(_m_%d, %s) == 0", id, c->match_case.pattern); - } - else if (isdigit(c->match_case.pattern[0]) || c->match_case.pattern[0] == '-') - { - // Numeric pattern - fprintf(out, "_m_%d == %s", id, c->match_case.pattern); - } - else if (c->match_case.pattern[0] == '\'') - { - // Char literal pattern - fprintf(out, "_m_%d == %s", id, c->match_case.pattern); - } - else - { - fprintf(out, "1"); - } - } - fprintf(out, ") { "); - if (c->match_case.binding_name) - { - if (is_option) - { - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, - c->match_case.binding_name, id); - } - else - { - fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name, id); - } - } - if (is_result) - { - if (strcmp(c->match_case.pattern, "Ok") == 0) - { - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, - c->match_case.binding_name, id); - } - else - { - fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name, - id); - } - } - else - { - if (strstr(g_config.cc, "tcc")) - { - fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id, - c->match_case.binding_name, id); - } - else - { - fprintf(out, "__auto_type %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; - } - fprintf(out, "__auto_type %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 (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0) { + if (node->var_ref.suggestion) { + 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); - 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); - } + zwarn_at(node->token, "%s\n = help: %s", msg, help); + } + } + fprintf(out, "%s", node->var_ref.name); + break; + case NODE_LAMBDA: + 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) { + for (int k = 0; k < g_current_lambda->lambda.num_captures; k++) { + if (strcmp(node->lambda.captured_vars[i], + g_current_lambda->lambda.captured_vars[k]) == 0) { + fprintf(out, "ctx->%s", node->lambda.captured_vars[i]); + found = 1; + break; + } + } + } + if (!found) { + fprintf(out, "%s", node->lambda.captured_vars[i]); } - - fprintf(out, " }"); - first = 0; - c = c->next; + 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); + } + break; + case NODE_EXPR_LITERAL: + 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) { + fprintf(out, "%f", node->literal.float_val); } - if (is_expr) - { - fprintf(out, " _r_%d; })", id); + else { + 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); + } } - else - { - fprintf(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 *base = clean; + if (strncmp(base, "struct ", 7) == 0) { + base += 7; + } + + if (!strchr(type, '*') && target->type == NODE_EXPR_CALL) { + fprintf(out, "({ %s _t = ", type); + codegen_expression(ctx, target, out); + fprintf(out, "; %s_%s(&_t", base, method); + ASTNode *arg = node->call.args; + while (arg) { + fprintf(out, ", "); + codegen_expression(ctx, arg, out); + arg = arg->next; + } + fprintf(out, "); })"); + } else { + fprintf(out, "%s_%s(", base, method); + if (!strchr(type, '*')) { + fprintf(out, "&"); + } + codegen_expression(ctx, target, out); + ASTNode *arg = node->call.args; + while (arg) { + fprintf(out, ", "); + codegen_expression(ctx, arg, out); + arg = arg->next; + } + fprintf(out, ")"); + } + free(clean); + return; + } } -} - -void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) -{ - if (!node) - { + 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; + } } - 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); - } + 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 = 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 = 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; + } - 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); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, ", "); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, ")"); - if (strcmp(node->binary.op, "!=") == 0) - { - 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, ")"); - } - } - 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: - 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; - } - } - } + codegen_expression(ctx, node->call.callee, out); + fprintf(out, "("); - if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0) - { - if (node->var_ref.suggestion) - { - 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); + 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); - zwarn_at(node->token, "%s\n = help: %s", msg, help); - } - } - fprintf(out, "%s", node->var_ref.name); - break; - case NODE_LAMBDA: - 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) - { - for (int k = 0; k < g_current_lambda->lambda.num_captures; k++) - { - if (strcmp(node->lambda.captured_vars[i], - g_current_lambda->lambda.captured_vars[k]) == 0) - { - fprintf(out, "ctx->%s", node->lambda.captured_vars[i]); - found = 1; - break; - } - } - } - 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); - } - break; - case NODE_EXPR_LITERAL: - 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) - { - fprintf(out, "%f", node->literal.float_val); - } - - else - { - 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); - } - } - 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 *base = clean; - if (strncmp(base, "struct ", 7) == 0) - { - base += 7; - } - - if (!strchr(type, '*') && target->type == NODE_EXPR_CALL) - { - fprintf(out, "({ %s _t = ", type); - codegen_expression(ctx, target, out); - fprintf(out, "; %s_%s(&_t", base, method); - ASTNode *arg = node->call.args; - while (arg) - { - fprintf(out, ", "); - codegen_expression(ctx, arg, out); - arg = arg->next; - } - fprintf(out, "); })"); - } - else - { - fprintf(out, "%s_%s(", base, method); - if (!strchr(type, '*')) - { - fprintf(out, "&"); - } - codegen_expression(ctx, target, out); - ASTNode *arg = node->call.args; - while (arg) - { - fprintf(out, ", "); - codegen_expression(ctx, arg, out); - arg = arg->next; - } - fprintf(out, ")"); - } - free(clean); - 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 (sig && sig->arg_types) { + for (int p = 0; p < sig->total_args; p++) { + ASTNode *arg = node->call.args; - 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 = type_to_string(ft->inner); - if (strcmp(ret, "string") == 0) - { - free(ret); - ret = xstrdup("char*"); - } + 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) { - fprintf(out, "((%s (*)(void*", ret); - for (int i = 0; i < ft->arg_count; i++) - { - char *as = 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; + // For now, emit in order provided... } - 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 - { - ASTNode *arg = node->call.args; - while (arg) - { - codegen_expression(ctx, arg, out); - if (arg->next) - { - fprintf(out, ", "); - } - arg = arg->next; - } + ASTNode *arg = node->call.args; + int first = 1; + while (arg) { + if (!first) { + fprintf(out, ", "); } - fprintf(out, ")"); - break; + first = 0; + codegen_expression(ctx, arg, out); + arg = arg->next; + } + } else { + ASTNode *arg = node->call.args; + while (arg) { + codegen_expression(ctx, arg, out); + if (arg->next) { + fprintf(out, ", "); + } + arg = arg->next; + } } - 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); - fprintf(out, "%s%s", node->member.is_pointer_access ? "->" : ".", 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, "]"); - } + 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; + } } - 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, "]"); - } + 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); + fprintf(out, "%s%s", node->member.is_pointer_access ? "->" : ".", + node->member.field); } 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 = type_to_string(node->type_info->inner); - } + 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; + } + } - 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 (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; + } + } + } - 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; "); - } - } + char *tname = "unknown"; + if (node->type_info && node->type_info->inner) { + tname = type_to_string(node->type_info->inner); + } - 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; + 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"); } - 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; + 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; "); + } } - 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"; - } + 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; + } - char *search_name = type_name; - if (strncmp(search_name, "struct ", 7) == 0) - { - search_name += 7; - } + if (strcmp(type_name, "__auto_type") == 0 || + strcmp(type_name, "unknown") == 0) { + type_name = "Result"; + } - 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; - } - } + char *search_name = type_name; + if (strncmp(search_name, "struct ", 7) == 0) { + search_name += 7; + } - 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); - } + 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; } - 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; + 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; + } } - 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 = 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; + 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); } - 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; - } - fprintf(out, "(struct %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; + 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); } - 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_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 = type_to_string(t); + fprintf(out, "\"%s\"", s); + free(s); + } else { // @fields + if (t->kind != TYPE_STRUCT || !t->name) { + fprintf(out, "((void*)0)"); 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, "))"); + } + char *sname = t->name; + // Find definition + ASTNode *def = find_struct_def(ctx, sname); + if (!def) { + fprintf(out, "((void*)0)"); break; - case NODE_AWAIT: - { - char *ret_type = "void*"; - int free_ret = 0; - if (node->type_info) - { - char *t = type_to_string(node->type_info); - if (t) - { - ret_type = t; - free_ret = 1; - } - } - else if (node->resolved_type) - { - ret_type = node->resolved_type; - } + } + + 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; + } + fprintf(out, "(struct %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 = 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 (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; + } + } - 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; - } - } + 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, "})"); - } - else - { - if (returns_struct) - { - 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); - } - break; + 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) { + 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); + } + } } - default: - break; + if (free_ret) { + free(ret_type); } + break; + } + default: + break; + } } -void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) -{ - if (!node) - { - return; +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; } - 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]); - } + 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; + } - 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"); + defer_count = 0; + fprintf(out, "\n"); - break; + // 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%s%s\")", SEGMENT_NAME_PREFIX, + node->func.section, SEGMENT_NAME_SUFFIX); } - - 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%s%s\")", SEGMENT_NAME_PREFIX, node->func.section, SEGMENT_NAME_SUFFIX); - } #undef EMIT_ATTR - fprintf(out, ")) "); - } - } - - if (node->func.is_inline) - { - fprintf(out, "inline "); - } - fprintf(out, "%s %s(%s)\n", node->func.ret_type, node->func.name, node->func.args); - 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_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); - } + fprintf(out, ")) "); + } + } - // Else block - codegen_walker(ctx, node->destruct.else_block->block.statements, out); - fprintf(out, " }\n"); + if (node->func.is_inline) { + fprintf(out, "inline "); + } + fprintf(out, "%s %s(%s)\n", node->func.ret_type, node->func.name, + node->func.args); + 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; - // 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_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))) "); } - 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; + char *tname = NULL; + Type *tinfo = node->var_decl.type_info; + if (tinfo && tinfo->name) { + tname = tinfo->name; + } else if (node->var_decl.type_str && + strcmp(node->var_decl.type_str, "__auto_type") != 0) { + tname = node->var_decl.type_str; + } + + if (tname) { + ASTNode *def = find_struct_def(ctx, tname); + if (def && def->type_info && def->type_info->has_drop) { + fprintf(out, "__attribute__((cleanup(%s_Drop_glue))) ", tname); + } + } } - 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; - Type *tinfo = node->var_decl.type_info; - if (tinfo && tinfo->name) - { - tname = tinfo->name; - } - else if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0) - { - tname = node->var_decl.type_str; - } - - if (tname) - { - ASTNode *def = find_struct_def(ctx, tname); - if (def && def->type_info && def->type_info->has_drop) - { - fprintf(out, "__attribute__((cleanup(%s_Drop_glue))) ", tname); - } - } - } - if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0) - { - emit_var_decl_type(ctx, out, node->var_decl.type_str, node->var_decl.name); - add_symbol(ctx, node->var_decl.name, node->var_decl.type_str, node->var_decl.type_info); - if (node->var_decl.init_expr) - { - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - } - fprintf(out, ";\n"); - } - else - { - 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) - { - emit_var_decl_type(ctx, out, inferred, node->var_decl.name); - add_symbol(ctx, node->var_decl.name, inferred, NULL); - } - 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); - } - else - { - // Here we are cooked. - } - } - - 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); - } + if (node->var_decl.type_str && + strcmp(node->var_decl.type_str, "__auto_type") != 0) { + emit_var_decl_type(ctx, out, node->var_decl.type_str, + node->var_decl.name); + add_symbol(ctx, node->var_decl.name, node->var_decl.type_str, + node->var_decl.type_info); + if (node->var_decl.init_expr) { 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: - fprintf(out, "while ("); - codegen_expression(ctx, node->while_stmt.condition, out); - fprintf(out, ") "); - codegen_node_single(ctx, node->while_stmt.body, out); - break; - case NODE_FOR: - 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); - break; - case NODE_BREAK: - 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: - 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: - 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"); - break; - // Loop constructs: loop, repeat, for-in - case NODE_LOOP: - // loop { ... } => while (1) { ... } - fprintf(out, "while (1) "); - codegen_node_single(ctx, node->loop_stmt.body, out); - break; - case NODE_REPEAT: - 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); - break; - case NODE_FOR_RANGE: - 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, "__auto_type %s = ", node->for_range.var_name); - } - codegen_expression(ctx, node->for_range.start, out); - 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); - 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); + } + fprintf(out, ";\n"); + } else { + 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) { + emit_var_decl_type(ctx, out, inferred, node->var_decl.name); + add_symbol(ctx, node->var_decl.name, inferred, NULL); + } 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); + } else { + // Here we are cooked. + } + } + + 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: + fprintf(out, "while ("); + codegen_expression(ctx, node->while_stmt.condition, out); + fprintf(out, ") "); + codegen_node_single(ctx, node->while_stmt.body, out); + break; + case NODE_FOR: + 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); + break; + case NODE_BREAK: + 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: + 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: + 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"); + break; + // Loop constructs: loop, repeat, for-in + case NODE_LOOP: + // loop { ... } => while (1) { ... } + fprintf(out, "while (1) "); + codegen_node_single(ctx, node->loop_stmt.body, out); + break; + case NODE_REPEAT: + 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); + break; + case NODE_FOR_RANGE: + 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, "__auto_type %s = ", node->for_range.var_name); + } + codegen_expression(ctx, node->for_range.start, out); + 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); + 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__("); + } - 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\""); - 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_outputs > 0) { + fprintf(out, "\n : "); + for (int i = 0; i < node->asm_stmt.num_outputs; i++) { + if (i > 0) { + fprintf(out, ", "); } - 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]); - } + // 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]); } - else if (node->asm_stmt.num_outputs > 0) - { - fprintf(out, "\n : "); + } + } + + 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]); - } + 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; + fprintf(out, ");\n"); + break; + } + case NODE_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); } - case NODE_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; + if (lt) { + free(lt); } - 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; + 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 = type_to_string(node->type_info); + if (t) { + ret_type = t; + free_ret = 1; + } + } else if (node->resolved_type) { + ret_type = node->resolved_type; } - case NODE_AWAIT: - { - char *ret_type = "void*"; - int free_ret = 0; - if (node->type_info) - { - char *t = 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; - } + // 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 + } + } - if (strncmp(ret_type, "struct", 6) == 0) - { - returns_struct = 1; - } - } + 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; + 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); + } + } } - default: - codegen_expression(ctx, node, out); - fprintf(out, ";\n"); - break; + if (free_ret) { + free(ret_type); } + fprintf(out, ";\n"); // Statement terminator + 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; - } +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 4b66f99..4d13d65 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -16,9 +16,11 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out); // Utility functions (codegen_utils.c). char *infer_type(ParserContext *ctx, ASTNode *node); ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name); -char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name); +char *get_field_type_str(ParserContext *ctx, const char *struct_name, + const char *field_name); char *extract_call_args(const char *args); -void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name); +void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, + const char *var_name); char *replace_string_type(const char *args); const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 026824e..53c4eb2 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -8,912 +8,763 @@ #include <string.h> // Emit C preamble with standard includes and type definitions. -void emit_preamble(ParserContext *ctx, FILE *out) -{ - if (g_config.is_freestanding) - { - // Freestanding preamble. - // It actually needs more work, but yk. - fputs("#include <stddef.h>\n#include <stdint.h>\n#include " - "<stdbool.h>\n#include <stdarg.h>\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); - } - else - { - // Standard hosted preamble. - fputs("#include <stdio.h>\n#include <stdlib.h>\n#include " - "<stddef.h>\n#include <string.h>\n", - out); - fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n", out); - fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions - fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); - fputs("typedef size_t usize;\ntypedef char* string;\n", out); - if (ctx->has_async) - { - fputs("#include <pthread.h>\n", out); - fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); - } - fputs("typedef struct { void *func; void *ctx; } z_closure_T;\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); - - // Memory Mapping. - fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free " - "free\n#define " - "z_print printf\n", - out); - fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", " - "msg); exit(1); }\n", - out); - - fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { " - "z_free(*pp); *pp " - "= NULL; } }\n", - out); - fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, " - "\"Assertion failed: \" " - "__VA_ARGS__); exit(1); }\n", - out); - fputs("string _z_readln_raw() { " - "size_t cap = 64; size_t len = 0; " - "char *line = z_malloc(cap); " - "if(!line) return NULL; " - "int c; " - "while((c = fgetc(stdin)) != EOF) { " - "if(c == '\\n') break; " - "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); " - "if(!n) { z_free(line); return NULL; } line = n; } " - "line[len++] = c; } " - "if(len == 0 && c == EOF) { z_free(line); return NULL; } " - "line[len] = 0; return line; }\n", - out); - fputs("int _z_scan_helper(const char *fmt, ...) { char *l = " - "_z_readln_raw(); if(!l) return " - "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); " - "va_end(ap); " - "z_free(l); return r; }\n", - out); - - // REPL helpers: suppress/restore stdout. - fputs("int _z_orig_stdout = -1;\n", out); - fputs("void _z_suppress_stdout() {\n", out); - fputs(" fflush(stdout);\n", out); - fputs(" if (_z_orig_stdout == -1) _z_orig_stdout = dup(STDOUT_FILENO);\n", out); - fputs(" int nullfd = open(\"/dev/null\", O_WRONLY);\n", out); - fputs(" dup2(nullfd, STDOUT_FILENO);\n", out); - fputs(" close(nullfd);\n", out); - fputs("}\n", out); - fputs("void _z_restore_stdout() {\n", out); - fputs(" fflush(stdout);\n", out); - fputs(" if (_z_orig_stdout != -1) {\n", out); - fputs(" dup2(_z_orig_stdout, STDOUT_FILENO);\n", out); - fputs(" close(_z_orig_stdout);\n", out); - fputs(" _z_orig_stdout = -1;\n", out); - fputs(" }\n", out); - fputs("}\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 <stddef.h>\n#include <stdint.h>\n#include " + "<stdbool.h>\n#include <stdarg.h>\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); + } else { + // Standard hosted preamble. + fputs("#include <stdio.h>\n#include <stdlib.h>\n#include " + "<stddef.h>\n#include <string.h>\n", + out); + fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n", + out); + fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions + fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); + fputs("typedef size_t usize;\ntypedef char* string;\n", out); + if (ctx->has_async) { + fputs("#include <pthread.h>\n", out); + fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); } + fputs("typedef struct { void *func; void *ctx; } z_closure_T;\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); + + // Memory Mapping. + fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free " + "free\n#define " + "z_print printf\n", + out); + fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", " + "msg); exit(1); }\n", + out); + + fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { " + "z_free(*pp); *pp " + "= NULL; } }\n", + out); + fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, " + "\"Assertion failed: \" " + "__VA_ARGS__); exit(1); }\n", + out); + fputs("string _z_readln_raw() { " + "size_t cap = 64; size_t len = 0; " + "char *line = z_malloc(cap); " + "if(!line) return NULL; " + "int c; " + "while((c = fgetc(stdin)) != EOF) { " + "if(c == '\\n') break; " + "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); " + "if(!n) { z_free(line); return NULL; } line = n; } " + "line[len++] = c; } " + "if(len == 0 && c == EOF) { z_free(line); return NULL; } " + "line[len] = 0; return line; }\n", + out); + fputs("int _z_scan_helper(const char *fmt, ...) { char *l = " + "_z_readln_raw(); if(!l) return " + "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); " + "va_end(ap); " + "z_free(l); return r; }\n", + out); + + // REPL helpers: suppress/restore stdout. + fputs("int _z_orig_stdout = -1;\n", out); + fputs("void _z_suppress_stdout() {\n", out); + fputs(" fflush(stdout);\n", out); + fputs( + " if (_z_orig_stdout == -1) _z_orig_stdout = dup(STDOUT_FILENO);\n", + out); + fputs(" int nullfd = open(\"/dev/null\", O_WRONLY);\n", out); + fputs(" dup2(nullfd, STDOUT_FILENO);\n", out); + fputs(" close(nullfd);\n", out); + fputs("}\n", out); + fputs("void _z_restore_stdout() {\n", out); + fputs(" fflush(stdout);\n", out); + fputs(" if (_z_orig_stdout != -1) {\n", out); + fputs(" dup2(_z_orig_stdout, STDOUT_FILENO);\n", out); + fputs(" close(_z_orig_stdout);\n", out); + fputs(" _z_orig_stdout = -1;\n", out); + fputs(" }\n", out); + fputs("}\n", out); + } } // Emit includes and type aliases. -void emit_includes_and_aliases(ASTNode *node, FILE *out) -{ - while (node) - { - if (node->type == NODE_INCLUDE) - { - if (node->include.is_system) - { - fprintf(out, "#include <%s>\n", node->include.path); - } - else - { - fprintf(out, "#include \"%s\"\n", node->include.path); - } - } - else if (node->type == NODE_TYPE_ALIAS) - { - fprintf(out, "typedef %s %s;\n", node->type_alias.original_type, - node->type_alias.alias); - } - node = node->next; +void emit_includes_and_aliases(ASTNode *node, FILE *out) { + while (node) { + if (node->type == NODE_INCLUDE) { + if (node->include.is_system) { + fprintf(out, "#include <%s>\n", node->include.path); + } else { + fprintf(out, "#include \"%s\"\n", node->include.path); + } + } else if (node->type == NODE_TYPE_ALIAS) { + fprintf(out, "typedef %s %s;\n", node->type_alias.original_type, + node->type_alias.alias); } + node = node->next; + } } // Emit enum constructor prototypes -void emit_enum_protos(ASTNode *node, FILE *out) -{ - while (node) - { - if (node->type == NODE_ENUM && !node->enm.is_template) - { - ASTNode *v = node->enm.variants; - while (v) - { - if (v->variant.payload) - { - char *tstr = type_to_string(v->variant.payload); - fprintf(out, "%s %s_%s(%s v);\n", node->enm.name, node->enm.name, - v->variant.name, tstr); - free(tstr); - } - else - { - fprintf(out, "%s %s_%s();\n", node->enm.name, node->enm.name, v->variant.name); - } - v = v->next; - } +void emit_enum_protos(ASTNode *node, FILE *out) { + while (node) { + if (node->type == NODE_ENUM && !node->enm.is_template) { + ASTNode *v = node->enm.variants; + while (v) { + if (v->variant.payload) { + char *tstr = type_to_string(v->variant.payload); + fprintf(out, "%s %s_%s(%s v);\n", node->enm.name, node->enm.name, + v->variant.name, tstr); + free(tstr); + } else { + fprintf(out, "%s %s_%s();\n", node->enm.name, node->enm.name, + v->variant.name); } - node = node->next; + v = v->next; + } } + node = node->next; + } } // Emit lambda definitions. -void emit_lambda_defs(ParserContext *ctx, FILE *out) -{ - LambdaRef *cur = ctx->global_lambdas; - while (cur) - { - ASTNode *node = cur->node; - int saved_defer = defer_count; - defer_count = 0; - - if (node->lambda.num_captures > 0) - { - fprintf(out, "struct Lambda_%d_Ctx {\n", node->lambda.lambda_id); - for (int i = 0; i < node->lambda.num_captures; i++) - { - fprintf(out, " %s %s;\n", node->lambda.captured_types[i], - node->lambda.captured_vars[i]); - } - fprintf(out, "};\n"); - } +void emit_lambda_defs(ParserContext *ctx, FILE *out) { + LambdaRef *cur = ctx->global_lambdas; + while (cur) { + ASTNode *node = cur->node; + int saved_defer = defer_count; + defer_count = 0; + + if (node->lambda.num_captures > 0) { + fprintf(out, "struct Lambda_%d_Ctx {\n", node->lambda.lambda_id); + for (int i = 0; i < node->lambda.num_captures; i++) { + fprintf(out, " %s %s;\n", node->lambda.captured_types[i], + node->lambda.captured_vars[i]); + } + fprintf(out, "};\n"); + } - fprintf(out, "%s _lambda_%d(void* _ctx", node->lambda.return_type, node->lambda.lambda_id); + fprintf(out, "%s _lambda_%d(void* _ctx", node->lambda.return_type, + node->lambda.lambda_id); - for (int i = 0; i < node->lambda.num_params; i++) - { - fprintf(out, ", %s %s", node->lambda.param_types[i], node->lambda.param_names[i]); - } - fprintf(out, ") {\n"); + for (int i = 0; i < node->lambda.num_params; i++) { + fprintf(out, ", %s %s", node->lambda.param_types[i], + node->lambda.param_names[i]); + } + fprintf(out, ") {\n"); - if (node->lambda.num_captures > 0) - { - fprintf(out, " struct Lambda_%d_Ctx* ctx = (struct Lambda_%d_Ctx*)_ctx;\n", - node->lambda.lambda_id, node->lambda.lambda_id); - } + if (node->lambda.num_captures > 0) { + fprintf(out, + " struct Lambda_%d_Ctx* ctx = (struct Lambda_%d_Ctx*)_ctx;\n", + node->lambda.lambda_id, node->lambda.lambda_id); + } - g_current_lambda = node; - if (node->lambda.body && node->lambda.body->type == NODE_BLOCK) - { - codegen_walker(ctx, node->lambda.body->block.statements, out); - } - g_current_lambda = NULL; + g_current_lambda = node; + if (node->lambda.body && node->lambda.body->type == NODE_BLOCK) { + codegen_walker(ctx, node->lambda.body->block.statements, out); + } + g_current_lambda = NULL; - for (int i = defer_count - 1; i >= 0; i--) - { - codegen_node_single(ctx, defer_stack[i], out); - } + for (int i = defer_count - 1; i >= 0; i--) { + codegen_node_single(ctx, defer_stack[i], out); + } - fprintf(out, "}\n\n"); + fprintf(out, "}\n\n"); - defer_count = saved_defer; - cur = cur->next; - } + defer_count = saved_defer; + cur = cur->next; + } } // Emit struct and enum definitions. -void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out) -{ - while (node) - { - if (node->type == NODE_STRUCT && node->strct.is_template) - { - node = node->next; - continue; +void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out) { + while (node) { + if (node->type == NODE_STRUCT && node->strct.is_template) { + node = node->next; + continue; + } + if (node->type == NODE_ENUM && node->enm.is_template) { + node = node->next; + continue; + } + if (node->type == NODE_STRUCT) { + if (node->strct.is_incomplete) { + // Forward declaration - no body needed (typedef handles it) + node = node->next; + continue; + } + + if (node->strct.is_union) { + fprintf(out, "union %s {", node->strct.name); + } else { + fprintf(out, "struct %s {", node->strct.name); + } + fprintf(out, "\n"); + codegen_walker(ctx, node->strct.fields, out); + fprintf(out, "}"); + + if (node->strct.is_packed && node->strct.align) { + fprintf(out, " __attribute__((packed, aligned(%d)))", + node->strct.align); + } else if (node->strct.is_packed) { + fprintf(out, " __attribute__((packed))"); + } else if (node->strct.align) { + fprintf(out, " __attribute__((aligned(%d)))", node->strct.align); + } + fprintf(out, ";\n\n"); + } else if (node->type == NODE_ENUM) { + fprintf(out, "typedef enum { "); + ASTNode *v = node->enm.variants; + while (v) { + fprintf(out, "%s_%s_Tag, ", node->enm.name, v->variant.name); + v = v->next; + } + fprintf(out, "} %s_Tag;\n", node->enm.name); + fprintf(out, "struct %s { %s_Tag tag; union { ", node->enm.name, + node->enm.name); + v = node->enm.variants; + while (v) { + if (v->variant.payload) { + char *tstr = type_to_string(v->variant.payload); + fprintf(out, "%s %s; ", tstr, v->variant.name); + free(tstr); } - if (node->type == NODE_ENUM && node->enm.is_template) - { - node = node->next; - continue; + v = v->next; + } + fprintf(out, "} data; };\n\n"); + v = node->enm.variants; + while (v) { + if (v->variant.payload) { + char *tstr = type_to_string(v->variant.payload); + fprintf(out, + "%s %s_%s(%s v) { return (%s){.tag=%s_%s_Tag, " + ".data.%s=v}; }\n", + node->enm.name, node->enm.name, v->variant.name, tstr, + node->enm.name, node->enm.name, v->variant.name, + v->variant.name); + free(tstr); + } else { + fprintf(out, "%s %s_%s() { return (%s){.tag=%s_%s_Tag}; }\n", + node->enm.name, node->enm.name, v->variant.name, + node->enm.name, node->enm.name, v->variant.name); } - if (node->type == NODE_STRUCT) - { - if (node->strct.is_incomplete) - { - // Forward declaration - no body needed (typedef handles it) - node = node->next; - continue; - } + v = v->next; + } + } + node = node->next; + } +} - if (node->strct.is_union) - { - fprintf(out, "union %s {", node->strct.name); - } - else - { - fprintf(out, "struct %s {", node->strct.name); - } - fprintf(out, "\n"); - codegen_walker(ctx, node->strct.fields, out); - fprintf(out, "}"); +// Emit trait definitions. +void emit_trait_defs(ASTNode *node, FILE *out) { + while (node) { + if (node->type == NODE_TRAIT) { + fprintf(out, "typedef struct %s_VTable {\n", node->trait.name); + ASTNode *m = node->trait.methods; + while (m) { + fprintf(out, " %s (*%s)(", m->func.ret_type, + parse_original_method_name(m->func.name)); + int has_self = (m->func.args && strstr(m->func.args, "self")); + if (!has_self) { + fprintf(out, "void* self"); + } - if (node->strct.is_packed && node->strct.align) - { - fprintf(out, " __attribute__((packed, aligned(%d)))", node->strct.align); - } - else if (node->strct.is_packed) - { - fprintf(out, " __attribute__((packed))"); - } - else if (node->strct.align) - { - fprintf(out, " __attribute__((aligned(%d)))", node->strct.align); - } - fprintf(out, ";\n\n"); + if (m->func.args) { + if (!has_self) { + fprintf(out, ", "); + } + fprintf(out, "%s", m->func.args); } - else if (node->type == NODE_ENUM) - { - fprintf(out, "typedef enum { "); - ASTNode *v = node->enm.variants; - while (v) - { - fprintf(out, "%s_%s_Tag, ", node->enm.name, v->variant.name); - v = v->next; - } - fprintf(out, "} %s_Tag;\n", node->enm.name); - fprintf(out, "struct %s { %s_Tag tag; union { ", node->enm.name, node->enm.name); - v = node->enm.variants; - while (v) - { - if (v->variant.payload) - { - char *tstr = type_to_string(v->variant.payload); - fprintf(out, "%s %s; ", tstr, v->variant.name); - free(tstr); - } - v = v->next; - } - fprintf(out, "} data; };\n\n"); - v = node->enm.variants; - while (v) - { - if (v->variant.payload) - { - char *tstr = type_to_string(v->variant.payload); - fprintf(out, - "%s %s_%s(%s v) { return (%s){.tag=%s_%s_Tag, " - ".data.%s=v}; }\n", - node->enm.name, node->enm.name, v->variant.name, tstr, node->enm.name, - node->enm.name, v->variant.name, v->variant.name); - free(tstr); - } - else - { - fprintf(out, "%s %s_%s() { return (%s){.tag=%s_%s_Tag}; }\n", node->enm.name, - node->enm.name, v->variant.name, node->enm.name, node->enm.name, - v->variant.name); - } - v = v->next; - } + fprintf(out, ");\n"); + m = m->next; + } + fprintf(out, "} %s_VTable;\n", node->trait.name); + fprintf(out, "typedef struct %s { void *self; %s_VTable *vtable; } %s;\n", + node->trait.name, node->trait.name, node->trait.name); + + m = node->trait.methods; + while (m) { + const char *orig = parse_original_method_name(m->func.name); + fprintf(out, "%s %s_%s(%s* self", m->func.ret_type, node->trait.name, + orig, node->trait.name); + + int has_self = (m->func.args && strstr(m->func.args, "self")); + if (m->func.args) { + if (has_self) { + char *comma = strchr(m->func.args, ','); + if (comma) { + fprintf(out, ", %s", comma + 1); + } + } else { + fprintf(out, ", %s", m->func.args); + } } - node = node->next; - } -} + fprintf(out, ") {\n"); -// Emit trait definitions. -void emit_trait_defs(ASTNode *node, FILE *out) -{ - while (node) - { - if (node->type == NODE_TRAIT) - { - fprintf(out, "typedef struct %s_VTable {\n", node->trait.name); - ASTNode *m = node->trait.methods; - while (m) - { - fprintf(out, " %s (*%s)(", m->func.ret_type, - parse_original_method_name(m->func.name)); - int has_self = (m->func.args && strstr(m->func.args, "self")); - if (!has_self) - { - fprintf(out, "void* self"); - } - - if (m->func.args) - { - if (!has_self) - { - fprintf(out, ", "); - } - fprintf(out, "%s", m->func.args); - } - fprintf(out, ");\n"); - m = m->next; + fprintf(out, " return self->vtable->%s(self->self", orig); + + if (m->func.args) { + char *call_args = extract_call_args(m->func.args); + if (has_self) { + char *comma = strchr(call_args, ','); + if (comma) { + fprintf(out, ", %s", comma + 1); } - fprintf(out, "} %s_VTable;\n", node->trait.name); - fprintf(out, "typedef struct %s { void *self; %s_VTable *vtable; } %s;\n", - node->trait.name, node->trait.name, node->trait.name); - - m = node->trait.methods; - while (m) - { - const char *orig = parse_original_method_name(m->func.name); - fprintf(out, "%s %s_%s(%s* self", m->func.ret_type, node->trait.name, orig, - node->trait.name); - - int has_self = (m->func.args && strstr(m->func.args, "self")); - if (m->func.args) - { - if (has_self) - { - char *comma = strchr(m->func.args, ','); - if (comma) - { - fprintf(out, ", %s", comma + 1); - } - } - else - { - fprintf(out, ", %s", m->func.args); - } - } - fprintf(out, ") {\n"); - - fprintf(out, " return self->vtable->%s(self->self", orig); - - if (m->func.args) - { - char *call_args = extract_call_args(m->func.args); - if (has_self) - { - char *comma = strchr(call_args, ','); - if (comma) - { - fprintf(out, ", %s", comma + 1); - } - } - else - { - if (strlen(call_args) > 0) - { - fprintf(out, ", %s", call_args); - } - } - free(call_args); - } - fprintf(out, ");\n}\n"); - - m = m->next; + } else { + if (strlen(call_args) > 0) { + fprintf(out, ", %s", call_args); } - fprintf(out, "\n"); + } + free(call_args); } - node = node->next; + fprintf(out, ");\n}\n"); + + m = m->next; + } + fprintf(out, "\n"); } + node = node->next; + } } // Emit global variables -void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) -{ - while (node) - { - if (node->type == NODE_VAR_DECL || node->type == NODE_CONST) - { - if (node->type == NODE_CONST) - { - fprintf(out, "const "); - } - if (node->var_decl.type_str) - { - emit_var_decl_type(ctx, out, node->var_decl.type_str, node->var_decl.name); - } - else - { - 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) - { - emit_var_decl_type(ctx, out, inferred, node->var_decl.name); - } - else - { - emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); - fprintf(out, " %s", node->var_decl.name); - } - } - if (node->var_decl.init_expr) - { - fprintf(out, " = "); - codegen_expression(ctx, node->var_decl.init_expr, out); - } - fprintf(out, ";\n"); +void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) { + while (node) { + if (node->type == NODE_VAR_DECL || node->type == NODE_CONST) { + if (node->type == NODE_CONST) { + fprintf(out, "const "); + } + if (node->var_decl.type_str) { + emit_var_decl_type(ctx, out, node->var_decl.type_str, + node->var_decl.name); + } else { + char *inferred = NULL; + if (node->var_decl.init_expr) { + inferred = infer_type(ctx, node->var_decl.init_expr); } - node = node->next; + + if (inferred && strcmp(inferred, "__auto_type") != 0) { + emit_var_decl_type(ctx, out, inferred, node->var_decl.name); + } else { + emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); + fprintf(out, " %s", node->var_decl.name); + } + } + if (node->var_decl.init_expr) { + fprintf(out, " = "); + codegen_expression(ctx, node->var_decl.init_expr, out); + } + fprintf(out, ";\n"); } + node = node->next; + } } // Emit function prototypes -void emit_protos(ASTNode *node, FILE *out) -{ - ASTNode *f = node; - while (f) - { - if (f->type == NODE_FUNCTION) - { - if (f->func.is_async) - { - fprintf(out, "Async %s(%s);\n", f->func.name, f->func.args); - // Also emit _impl_ prototype - if (f->func.ret_type) - { - fprintf(out, "%s _impl_%s(%s);\n", f->func.ret_type, f->func.name, - f->func.args); - } - else - { - fprintf(out, "void _impl_%s(%s);\n", f->func.name, f->func.args); - } - } - else - { - fprintf(out, "%s %s(%s);\n", f->func.ret_type, f->func.name, f->func.args); - } +void emit_protos(ASTNode *node, FILE *out) { + ASTNode *f = node; + while (f) { + if (f->type == NODE_FUNCTION) { + if (f->func.is_async) { + fprintf(out, "Async %s(%s);\n", f->func.name, f->func.args); + // Also emit _impl_ prototype + if (f->func.ret_type) { + fprintf(out, "%s _impl_%s(%s);\n", f->func.ret_type, f->func.name, + f->func.args); + } else { + fprintf(out, "void _impl_%s(%s);\n", f->func.name, f->func.args); } - else if (f->type == NODE_IMPL) - { - char *sname = f->impl.struct_name; - if (!sname) - { - f = f->next; - continue; - } - - char *mangled = replace_string_type(sname); - ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); - int skip = 0; - if (def) - { - if (def->type == NODE_STRUCT && def->strct.is_template) - { - skip = 1; - } - else if (def->type == NODE_ENUM && def->enm.is_template) - { - skip = 1; - } - } - else - { - char *lt = strchr(sname, '<'); - if (lt) - { - 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) - { - skip = 1; - } - free(buf); - } - } - if (mangled) - { - free(mangled); - } - - if (skip) - { - f = f->next; - continue; - } - - ASTNode *m = f->impl.methods; - while (m) - { - char *fname = m->func.name; - char *proto = xmalloc(strlen(fname) + strlen(sname) + 2); - int slen = strlen(sname); - if (strncmp(fname, sname, slen) == 0 && fname[slen] == '_') - { - strcpy(proto, fname); - } - else - { - sprintf(proto, "%s_%s", sname, fname); - } - - if (m->func.is_async) - { - fprintf(out, "Async %s(%s);\n", proto, m->func.args); - } - else - { - fprintf(out, "%s %s(%s);\n", m->func.ret_type, proto, m->func.args); - } - - free(proto); - m = m->next; - } + } else { + fprintf(out, "%s %s(%s);\n", f->func.ret_type, f->func.name, + f->func.args); + } + } else if (f->type == NODE_IMPL) { + char *sname = f->impl.struct_name; + if (!sname) { + f = f->next; + continue; + } + + char *mangled = replace_string_type(sname); + ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); + int skip = 0; + if (def) { + if (def->type == NODE_STRUCT && def->strct.is_template) { + skip = 1; + } else if (def->type == NODE_ENUM && def->enm.is_template) { + skip = 1; } - else if (f->type == NODE_IMPL_TRAIT) - { - char *sname = f->impl_trait.target_type; - if (!sname) - { - f = f->next; - continue; - } + } else { + char *lt = strchr(sname, '<'); + if (lt) { + 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) { + skip = 1; + } + free(buf); + } + } + if (mangled) { + free(mangled); + } - char *mangled = replace_string_type(sname); - ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); - int skip = 0; - if (def) - { - if (def->strct.is_template) - { - skip = 1; - } - } - else - { - char *lt = strchr(sname, '<'); - if (lt) - { - 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) - { - skip = 1; - } - free(buf); - } - } - if (mangled) - { - free(mangled); - } + if (skip) { + f = f->next; + continue; + } + + ASTNode *m = f->impl.methods; + while (m) { + char *fname = m->func.name; + char *proto = xmalloc(strlen(fname) + strlen(sname) + 2); + int slen = strlen(sname); + if (strncmp(fname, sname, slen) == 0 && fname[slen] == '_') { + strcpy(proto, fname); + } else { + sprintf(proto, "%s_%s", sname, fname); + } - if (skip) - { - f = f->next; - continue; - } + if (m->func.is_async) { + fprintf(out, "Async %s(%s);\n", proto, m->func.args); + } else { + fprintf(out, "%s %s(%s);\n", m->func.ret_type, proto, m->func.args); + } - ASTNode *m = f->impl_trait.methods; - while (m) - { - if (m->func.is_async) - { - fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args); - } - else - { - fprintf(out, "%s %s(%s);\n", m->func.ret_type, m->func.name, m->func.args); - } - m = m->next; - } - // RAII: Emit glue prototype - if (strcmp(f->impl_trait.trait_name, "Drop") == 0) - { - char *tname = f->impl_trait.target_type; - fprintf(out, "void %s_Drop_glue(%s *self);\n", tname, tname); - } + free(proto); + m = m->next; + } + } else if (f->type == NODE_IMPL_TRAIT) { + char *sname = f->impl_trait.target_type; + if (!sname) { + f = f->next; + continue; + } + + char *mangled = replace_string_type(sname); + ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); + int skip = 0; + if (def) { + if (def->strct.is_template) { + skip = 1; } + } else { + char *lt = strchr(sname, '<'); + if (lt) { + 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) { + skip = 1; + } + free(buf); + } + } + if (mangled) { + free(mangled); + } + + if (skip) { f = f->next; + continue; + } + + ASTNode *m = f->impl_trait.methods; + while (m) { + if (m->func.is_async) { + fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args); + } else { + fprintf(out, "%s %s(%s);\n", m->func.ret_type, m->func.name, + m->func.args); + } + m = m->next; + } + // RAII: Emit glue prototype + if (strcmp(f->impl_trait.trait_name, "Drop") == 0) { + char *tname = f->impl_trait.target_type; + fprintf(out, "void %s_Drop_glue(%s *self);\n", tname, tname); + } } + f = f->next; + } } // Emit VTable instances for trait implementations. -void emit_impl_vtables(ParserContext *ctx, FILE *out) -{ - StructRef *ref = ctx->parsed_impls_list; - struct - { - char *trait; - char *strct; - } emitted[1024]; - int count = 0; - - while (ref) - { - ASTNode *node = ref->node; - if (node && node->type == NODE_IMPL_TRAIT) - { - char *trait = node->impl_trait.trait_name; - char *strct = node->impl_trait.target_type; - - // Filter templates - char *mangled = replace_string_type(strct); - ASTNode *def = find_struct_def_codegen(ctx, mangled); - int skip = 0; - if (def) - { - if (def->type == NODE_STRUCT && def->strct.is_template) - { - skip = 1; - } - else if (def->type == NODE_ENUM && def->enm.is_template) - { - skip = 1; - } - } - else - { - char *lt = strchr(strct, '<'); - if (lt) - { - 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) - { - skip = 1; - } - free(buf); - } - } - if (mangled) - { - free(mangled); - } - if (skip) - { - ref = ref->next; - continue; - } - - // Check duplication - int dup = 0; - for (int i = 0; i < count; i++) - { - if (strcmp(emitted[i].trait, trait) == 0 && strcmp(emitted[i].strct, strct) == 0) - { - dup = 1; - break; - } - } - if (dup) - { - ref = ref->next; - continue; - } - - emitted[count].trait = trait; - emitted[count].strct = strct; - count++; - - fprintf(out, "%s_VTable %s_%s_VTable = {", trait, strct, trait); - - ASTNode *m = node->impl_trait.methods; - while (m) - { - const char *orig = parse_original_method_name(m->func.name); - fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s_%s_%s", orig, trait, orig, - strct, trait, orig); - if (m->next) - { - fprintf(out, ", "); - } - m = m->next; - } - fprintf(out, "};\n"); +void emit_impl_vtables(ParserContext *ctx, FILE *out) { + StructRef *ref = ctx->parsed_impls_list; + struct { + char *trait; + char *strct; + } emitted[1024]; + int count = 0; + + while (ref) { + ASTNode *node = ref->node; + if (node && node->type == NODE_IMPL_TRAIT) { + char *trait = node->impl_trait.trait_name; + char *strct = node->impl_trait.target_type; + + // Filter templates + char *mangled = replace_string_type(strct); + ASTNode *def = find_struct_def_codegen(ctx, mangled); + int skip = 0; + if (def) { + if (def->type == NODE_STRUCT && def->strct.is_template) { + skip = 1; + } else if (def->type == NODE_ENUM && def->enm.is_template) { + skip = 1; + } + } else { + char *lt = strchr(strct, '<'); + if (lt) { + 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) { + skip = 1; + } + free(buf); + } + } + if (mangled) { + free(mangled); + } + if (skip) { + ref = ref->next; + continue; + } + + // Check duplication + int dup = 0; + for (int i = 0; i < count; i++) { + if (strcmp(emitted[i].trait, trait) == 0 && + strcmp(emitted[i].strct, strct) == 0) { + dup = 1; + break; } + } + if (dup) { ref = ref->next; + continue; + } + + emitted[count].trait = trait; + emitted[count].strct = strct; + count++; + + fprintf(out, "%s_VTable %s_%s_VTable = {", trait, strct, trait); + + ASTNode *m = node->impl_trait.methods; + while (m) { + const char *orig = parse_original_method_name(m->func.name); + fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s_%s_%s", orig, + trait, orig, strct, trait, orig); + if (m->next) { + fprintf(out, ", "); + } + m = m->next; + } + fprintf(out, "};\n"); } + ref = ref->next; + } } // Emit test functions and runner -void emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out) -{ - ASTNode *cur = node; - int test_count = 0; - while (cur) - { - if (cur->type == NODE_TEST) - { - fprintf(out, "static void _z_test_%d() {\n", test_count); - codegen_walker(ctx, cur->test_stmt.body, out); - fprintf(out, "}\n"); - test_count++; - } - cur = cur->next; +void emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out) { + ASTNode *cur = node; + int test_count = 0; + while (cur) { + if (cur->type == NODE_TEST) { + fprintf(out, "static void _z_test_%d() {\n", test_count); + codegen_walker(ctx, cur->test_stmt.body, out); + fprintf(out, "}\n"); + test_count++; } - if (test_count > 0) - { - fprintf(out, "\nvoid _z_run_tests() {\n"); - for (int i = 0; i < test_count; i++) - { - fprintf(out, " _z_test_%d();\n", i); - } - fprintf(out, "}\n\n"); - } - else - { - fprintf(out, "void _z_run_tests() {}\n"); + cur = cur->next; + } + if (test_count > 0) { + fprintf(out, "\nvoid _z_run_tests() {\n"); + for (int i = 0; i < test_count; i++) { + fprintf(out, " _z_test_%d();\n", i); } + fprintf(out, "}\n\n"); + } else { + fprintf(out, "void _z_run_tests() {}\n"); + } } // Emit type definitions- -void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) -{ - if (!g_config.is_freestanding) - { - fprintf(out, "typedef char* string;\n"); - } - - fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n"); - fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n"); - fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { " - "v->cap = v->cap?v->cap*2:8; " - "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } " - "v->data[v->len++] = item; }\n"); - fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n"); - fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = " - "count > 8 ? " - "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list " - "args; " - "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = " - "va_arg(args, void*); } va_end(args); return v; }\n"); - - if (g_config.is_freestanding) - { - fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = " - "(index); if(_i < 0 " - "|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n"); +void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) { + if (!g_config.is_freestanding) { + fprintf(out, "typedef char* string;\n"); + } + + fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n"); + fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n"); + fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { " + "v->cap = v->cap?v->cap*2:8; " + "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } " + "v->data[v->len++] = item; }\n"); + fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n"); + fprintf( + out, + "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = " + "count > 8 ? " + "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list " + "args; " + "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = " + "va_arg(args, void*); } va_end(args); return v; }\n"); + + if (g_config.is_freestanding) { + fprintf(out, + "#define _z_check_bounds(index, limit) ({ __auto_type _i = " + "(index); if(_i < 0 " + "|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n"); + } else { + fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = " + "(index); if(_i < 0 " + "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: " + "%%ld (limit " + "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n"); + } + + SliceType *c = ctx->used_slices; + while (c) { + fprintf(out, + "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; " + "int len; int cap; };\n", + c->name, c->name, c->name, c->name); + c = c->next; + } + + TupleType *t = ctx->used_tuples; + while (t) { + fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", + t->sig, t->sig, t->sig); + char *s = xstrdup(t->sig); + char *p = strtok(s, "_"); + int i = 0; + while (p) { + fprintf(out, "%s v%d; ", p, i++); + p = strtok(NULL, "_"); } - else - { - fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = " - "(index); if(_i < 0 " - "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: " - "%%ld (limit " - "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n"); + free(s); + fprintf(out, "};\n"); + t = t->next; + } + fprintf(out, "\n"); + + // FIRST: Emit typedefs for ALL structs and enums in the current compilation + // unit (local definitions) + ASTNode *local = nodes; + while (local) { + if (local->type == NODE_STRUCT && !local->strct.is_template) { + const char *keyword = local->strct.is_union ? "union" : "struct"; + fprintf(out, "typedef %s %s %s;\n", keyword, local->strct.name, + local->strct.name); } - - SliceType *c = ctx->used_slices; - while (c) - { - fprintf(out, - "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; " - "int len; int cap; };\n", - c->name, c->name, c->name, c->name); - c = c->next; + if (local->type == NODE_ENUM && !local->enm.is_template) { + fprintf(out, "typedef struct %s %s;\n", local->enm.name, local->enm.name); } - - TupleType *t = ctx->used_tuples; - while (t) - { - fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", t->sig, t->sig, - t->sig); - char *s = xstrdup(t->sig); - char *p = strtok(s, "_"); - int i = 0; - while (p) - { - fprintf(out, "%s v%d; ", p, i++); - p = strtok(NULL, "_"); - } - free(s); - fprintf(out, "};\n"); - t = t->next; + local = local->next; + } + + // THEN: Emit typedefs for instantiated generics + Instantiation *i = ctx->instantiations; + while (i) { + if (i->struct_node->type == NODE_RAW_STMT) { + fprintf(out, "%s\n", i->struct_node->raw_stmt.content); + } else { + fprintf(out, "typedef struct %s %s;\n", i->struct_node->strct.name, + i->struct_node->strct.name); + codegen_node(ctx, i->struct_node, out); } - fprintf(out, "\n"); - - // FIRST: Emit typedefs for ALL structs and enums in the current compilation - // unit (local definitions) - ASTNode *local = nodes; - while (local) - { - if (local->type == NODE_STRUCT && !local->strct.is_template) - { - const char *keyword = local->strct.is_union ? "union" : "struct"; - fprintf(out, "typedef %s %s %s;\n", keyword, local->strct.name, local->strct.name); - } - if (local->type == NODE_ENUM && !local->enm.is_template) - { - fprintf(out, "typedef struct %s %s;\n", local->enm.name, local->enm.name); - } - local = local->next; + i = i->next; + } + + StructRef *sr = ctx->parsed_structs_list; + while (sr) { + if (sr->node && sr->node->type == NODE_STRUCT && + !sr->node->strct.is_template) { + const char *keyword = sr->node->strct.is_union ? "union" : "struct"; + fprintf(out, "typedef %s %s %s;\n", keyword, sr->node->strct.name, + sr->node->strct.name); } - // THEN: Emit typedefs for instantiated generics - Instantiation *i = ctx->instantiations; - while (i) - { - if (i->struct_node->type == NODE_RAW_STMT) - { - fprintf(out, "%s\n", i->struct_node->raw_stmt.content); - } - else - { - fprintf(out, "typedef struct %s %s;\n", i->struct_node->strct.name, - i->struct_node->strct.name); - codegen_node(ctx, i->struct_node, out); - } - i = i->next; + if (sr->node && sr->node->type == NODE_ENUM && !sr->node->enm.is_template) { + fprintf(out, "typedef struct %s %s;\n", sr->node->enm.name, + sr->node->enm.name); } - - StructRef *sr = ctx->parsed_structs_list; - while (sr) - { - if (sr->node && sr->node->type == NODE_STRUCT && !sr->node->strct.is_template) - { - const char *keyword = sr->node->strct.is_union ? "union" : "struct"; - fprintf(out, "typedef %s %s %s;\n", keyword, sr->node->strct.name, - sr->node->strct.name); - } - - if (sr->node && sr->node->type == NODE_ENUM && !sr->node->enm.is_template) - { - fprintf(out, "typedef struct %s %s;\n", sr->node->enm.name, sr->node->enm.name); - } - sr = sr->next; + sr = sr->next; + } + + // Also check instantiated_structs list. + ASTNode *inst_s = ctx->instantiated_structs; + while (inst_s) { + if (inst_s->type == NODE_STRUCT && !inst_s->strct.is_template) { + const char *keyword = inst_s->strct.is_union ? "union" : "struct"; + fprintf(out, "typedef %s %s %s;\n", keyword, inst_s->strct.name, + inst_s->strct.name); } - // Also check instantiated_structs list. - ASTNode *inst_s = ctx->instantiated_structs; - while (inst_s) - { - if (inst_s->type == NODE_STRUCT && !inst_s->strct.is_template) - { - const char *keyword = inst_s->strct.is_union ? "union" : "struct"; - fprintf(out, "typedef %s %s %s;\n", keyword, inst_s->strct.name, inst_s->strct.name); - } - - if (inst_s->type == NODE_ENUM && !inst_s->enm.is_template) - { - fprintf(out, "typedef struct %s %s;\n", inst_s->enm.name, inst_s->enm.name); - } - inst_s = inst_s->next; + if (inst_s->type == NODE_ENUM && !inst_s->enm.is_template) { + fprintf(out, "typedef struct %s %s;\n", inst_s->enm.name, + inst_s->enm.name); } + inst_s = inst_s->next; + } } diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index 3634af3..3b0e4dc 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -7,563 +7,457 @@ #include <string.h> // Helper: Check if a struct depends on another struct/enum by-value. -static int struct_depends_on(ASTNode *s1, const char *target_name) -{ - if (!s1) - { - return 0; - } - - // Only structs have dependencies that matter for ordering. - if (s1->type != NODE_STRUCT) - { - return 0; - } - - ASTNode *field = s1->strct.fields; - while (field) - { - if (field->type == NODE_FIELD && field->field.type) - { - char *type_str = field->field.type; - // Skip pointers - they don't create ordering dependency. - if (strchr(type_str, '*')) - { - field = field->next; - continue; - } +static int struct_depends_on(ASTNode *s1, const char *target_name) { + if (!s1) { + return 0; + } - // Check if this field's type matches target (struct or enum). - if (strcmp(type_str, target_name) == 0) - { - return 1; - } - } + // Only structs have dependencies that matter for ordering. + if (s1->type != NODE_STRUCT) { + return 0; + } + + ASTNode *field = s1->strct.fields; + while (field) { + if (field->type == NODE_FIELD && field->field.type) { + char *type_str = field->field.type; + // Skip pointers - they don't create ordering dependency. + if (strchr(type_str, '*')) { field = field->next; + continue; + } + + // Check if this field's type matches target (struct or enum). + if (strcmp(type_str, target_name) == 0) { + return 1; + } } - return 0; + field = field->next; + } + return 0; } // Topologically sort a list of struct/enum nodes. -static ASTNode *topo_sort_structs(ASTNode *head) -{ - if (!head) - { - return NULL; +static ASTNode *topo_sort_structs(ASTNode *head) { + if (!head) { + return NULL; + } + + // Count all nodes (structs + enums). + int count = 0; + ASTNode *n = head; + while (n) { + if (n->type == NODE_STRUCT || n->type == NODE_ENUM) { + count++; + } + n = n->next; + } + if (count == 0) { + return head; + } + + // Build array of all nodes. + ASTNode **nodes = malloc(count * sizeof(ASTNode *)); + int *emitted = calloc(count, sizeof(int)); + n = head; + int idx = 0; + while (n) { + if (n->type == NODE_STRUCT || n->type == NODE_ENUM) { + nodes[idx++] = n; } + n = n->next; + } + + // Build order array (indices in emission order). + int *order = malloc(count * sizeof(int)); + int order_idx = 0; + + int changed = 1; + int max_iterations = count * count; + int iterations = 0; + + while (changed && iterations < max_iterations) { + changed = 0; + iterations++; + + for (int i = 0; i < count; i++) { + if (emitted[i]) { + continue; + } + + // Enums have no dependencies, emit first. + if (nodes[i]->type == NODE_ENUM) { + order[order_idx++] = i; + emitted[i] = 1; + changed = 1; + continue; + } + + // For structs, check if all dependencies are emitted. + int can_emit = 1; + for (int j = 0; j < count; j++) { + if (i == j || emitted[j]) { + continue; + } - // Count all nodes (structs + enums). - int count = 0; - ASTNode *n = head; - while (n) - { - if (n->type == NODE_STRUCT || n->type == NODE_ENUM) - { - count++; + // Get the name of the potential dependency. + const char *dep_name = NULL; + if (nodes[j]->type == NODE_STRUCT) { + dep_name = nodes[j]->strct.name; + } else if (nodes[j]->type == NODE_ENUM) { + dep_name = nodes[j]->enm.name; } - n = n->next; - } - if (count == 0) - { - return head; - } - // Build array of all nodes. - ASTNode **nodes = malloc(count * sizeof(ASTNode *)); - int *emitted = calloc(count, sizeof(int)); - n = head; - int idx = 0; - while (n) - { - if (n->type == NODE_STRUCT || n->type == NODE_ENUM) - { - nodes[idx++] = n; + if (dep_name && struct_depends_on(nodes[i], dep_name)) { + can_emit = 0; + break; } - n = n->next; - } + } - // Build order array (indices in emission order). - int *order = malloc(count * sizeof(int)); - int order_idx = 0; + if (can_emit) { + order[order_idx++] = i; + emitted[i] = 1; + changed = 1; + } + } + } - int changed = 1; - int max_iterations = count * count; - int iterations = 0; + // Add any remaining nodes (cycles). + for (int i = 0; i < count; i++) { + if (!emitted[i]) { + order[order_idx++] = i; + } + } + + // Now build the linked list in the correct order. + ASTNode *result = NULL; + ASTNode *result_tail = NULL; + + for (int i = 0; i < order_idx; i++) { + ASTNode *node = nodes[order[i]]; + if (!result) { + result = node; + result_tail = node; + } else { + result_tail->next = node; + result_tail = node; + } + } + if (result_tail) { + result_tail->next = NULL; + } + + free(nodes); + free(emitted); + free(order); + return result; +} - while (changed && iterations < max_iterations) - { - changed = 0; - iterations++; +// Main entry point for code generation. +void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) { + if (node->type == NODE_ROOT) { + ASTNode *kids = node->root.children; + // Recursive Unwrap of Nested Roots (if accidentally wrapped multiple + // times). + while (kids && kids->type == NODE_ROOT) { + kids = kids->root.children; + } - for (int i = 0; i < count; i++) - { - if (emitted[i]) - { - continue; - } + global_user_structs = kids; - // Enums have no dependencies, emit first. - if (nodes[i]->type == NODE_ENUM) - { - order[order_idx++] = i; - emitted[i] = 1; - changed = 1; - continue; - } + if (!ctx->skip_preamble) { + emit_preamble(ctx, out); + } + emit_includes_and_aliases(kids, out); + + // Emit Hoisted Code (from plugins) + if (ctx->hoist_out) { + long pos = ftell(ctx->hoist_out); + rewind(ctx->hoist_out); + char buf[4096]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), ctx->hoist_out)) > 0) { + fwrite(buf, 1, n, out); + } + fseek(ctx->hoist_out, pos, SEEK_SET); + } - // For structs, check if all dependencies are emitted. - int can_emit = 1; - for (int j = 0; j < count; j++) - { - if (i == j || emitted[j]) - { - continue; - } - - // Get the name of the potential dependency. - const char *dep_name = NULL; - if (nodes[j]->type == NODE_STRUCT) - { - dep_name = nodes[j]->strct.name; - } - else if (nodes[j]->type == NODE_ENUM) - { - dep_name = nodes[j]->enm.name; - } - - if (dep_name && struct_depends_on(nodes[i], dep_name)) - { - can_emit = 0; - break; - } - } + ASTNode *merged = NULL; + ASTNode *merged_tail = NULL; + + ASTNode *s = ctx->instantiated_structs; + while (s) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *s; + copy->next = NULL; + if (!merged) { + merged = copy; + merged_tail = copy; + } else { + merged_tail->next = copy; + merged_tail = copy; + } + s = s->next; + } - if (can_emit) - { - order[order_idx++] = i; - emitted[i] = 1; - changed = 1; - } + StructRef *sr = ctx->parsed_structs_list; + while (sr) { + if (sr->node) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *sr->node; + copy->next = NULL; + if (!merged) { + merged = copy; + merged_tail = copy; + } else { + merged_tail->next = copy; + merged_tail = copy; } + } + sr = sr->next; } - // Add any remaining nodes (cycles). - for (int i = 0; i < count; i++) - { - if (!emitted[i]) - { - order[order_idx++] = i; + StructRef *er = ctx->parsed_enums_list; + while (er) { + if (er->node) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *er->node; + copy->next = NULL; + if (!merged) { + merged = copy; + merged_tail = copy; + } else { + merged_tail->next = copy; + merged_tail = copy; } + } + er = er->next; } - // Now build the linked list in the correct order. - ASTNode *result = NULL; - ASTNode *result_tail = NULL; - - for (int i = 0; i < order_idx; i++) - { - ASTNode *node = nodes[order[i]]; - if (!result) - { - result = node; - result_tail = node; + ASTNode *k = kids; + while (k) { + if (k->type == NODE_STRUCT || k->type == NODE_ENUM) { + int found = 0; + ASTNode *chk = merged; + while (chk) { + if (chk->type == k->type) { + const char *n1 = + (k->type == NODE_STRUCT) ? k->strct.name : k->enm.name; + const char *n2 = + (chk->type == NODE_STRUCT) ? chk->strct.name : chk->enm.name; + if (n1 && n2 && strcmp(n1, n2) == 0) { + found = 1; + break; + } + } + chk = chk->next; } - else - { - result_tail->next = node; - result_tail = node; + + if (!found) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *k; + copy->next = NULL; + if (!merged) { + merged = copy; + merged_tail = copy; + } else { + merged_tail->next = copy; + merged_tail = copy; + } } - } - if (result_tail) - { - result_tail->next = NULL; + } + k = k->next; } - free(nodes); - free(emitted); - free(order); - return result; -} + // Topologically sort. + ASTNode *sorted = topo_sort_structs(merged); -// Main entry point for code generation. -void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) -{ - if (node->type == NODE_ROOT) - { - ASTNode *kids = node->root.children; - // Recursive Unwrap of Nested Roots (if accidentally wrapped multiple - // times). - while (kids && kids->type == NODE_ROOT) - { - kids = kids->root.children; - } + print_type_defs(ctx, out, sorted); + emit_enum_protos(sorted, out); - global_user_structs = kids; + if (sorted) { + emit_struct_defs(ctx, sorted, out); + } + emit_trait_defs(kids, out); + + ASTNode *raw_iter = kids; + while (raw_iter) { + if (raw_iter->type == NODE_RAW_STMT) { + fprintf(out, "%s\n", raw_iter->raw_stmt.content); + } + raw_iter = raw_iter->next; + } - if (!ctx->skip_preamble) - { - emit_preamble(ctx, out); - } - emit_includes_and_aliases(kids, out); - - // Emit Hoisted Code (from plugins) - if (ctx->hoist_out) - { - long pos = ftell(ctx->hoist_out); - rewind(ctx->hoist_out); - char buf[4096]; - size_t n; - while ((n = fread(buf, 1, sizeof(buf), ctx->hoist_out)) > 0) - { - fwrite(buf, 1, n, out); - } - fseek(ctx->hoist_out, pos, SEEK_SET); - } + ASTNode *merged_globals = NULL; // Head - ASTNode *merged = NULL; - ASTNode *merged_tail = NULL; - - ASTNode *s = ctx->instantiated_structs; - while (s) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *s; - copy->next = NULL; - if (!merged) - { - merged = copy; - merged_tail = copy; - } - else - { - merged_tail->next = copy; - merged_tail = copy; - } - s = s->next; - } + if (ctx->parsed_globals_list) { + StructRef *s = ctx->parsed_globals_list; + while (s) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *s->node; + copy->next = merged_globals; + merged_globals = copy; - StructRef *sr = ctx->parsed_structs_list; - while (sr) - { - if (sr->node) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *sr->node; - copy->next = NULL; - if (!merged) - { - merged = copy; - merged_tail = copy; - } - else - { - merged_tail->next = copy; - merged_tail = copy; - } - } - sr = sr->next; - } + s = s->next; + } + } - StructRef *er = ctx->parsed_enums_list; - while (er) - { - if (er->node) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *er->node; - copy->next = NULL; - if (!merged) - { - merged = copy; - merged_tail = copy; - } - else - { - merged_tail->next = copy; - merged_tail = copy; - } - } - er = er->next; + emit_globals(ctx, merged_globals, out); + + ASTNode *merged_funcs = NULL; + ASTNode *merged_funcs_tail = NULL; + + if (ctx->instantiated_funcs) { + ASTNode *s = ctx->instantiated_funcs; + while (s) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *s; + copy->next = NULL; + if (!merged_funcs) { + merged_funcs = copy; + merged_funcs_tail = copy; + } else { + merged_funcs_tail->next = copy; + merged_funcs_tail = copy; } + s = s->next; + } + } - ASTNode *k = kids; - while (k) - { - if (k->type == NODE_STRUCT || k->type == NODE_ENUM) - { - int found = 0; - ASTNode *chk = merged; - while (chk) - { - if (chk->type == k->type) - { - const char *n1 = (k->type == NODE_STRUCT) ? k->strct.name : k->enm.name; - const char *n2 = - (chk->type == NODE_STRUCT) ? chk->strct.name : chk->enm.name; - if (n1 && n2 && strcmp(n1, n2) == 0) - { - found = 1; - break; - } - } - chk = chk->next; - } - - if (!found) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *k; - copy->next = NULL; - if (!merged) - { - merged = copy; - merged_tail = copy; - } - else - { - merged_tail->next = copy; - merged_tail = copy; - } - } - } - k = k->next; + if (ctx->parsed_funcs_list) { + StructRef *s = ctx->parsed_funcs_list; + while (s) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *s->node; + copy->next = NULL; + if (!merged_funcs) { + merged_funcs = copy; + merged_funcs_tail = copy; + } else { + merged_funcs_tail->next = copy; + merged_funcs_tail = copy; } + s = s->next; + } + } - // Topologically sort. - ASTNode *sorted = topo_sort_structs(merged); + if (ctx->parsed_impls_list) { + StructRef *s = ctx->parsed_impls_list; + while (s) { + ASTNode *copy = xmalloc(sizeof(ASTNode)); + *copy = *s->node; + copy->next = NULL; + if (!merged_funcs) { + merged_funcs = copy; + merged_funcs_tail = copy; + } else { + merged_funcs_tail->next = copy; + merged_funcs_tail = copy; + } + s = s->next; + } + } - print_type_defs(ctx, out, sorted); - emit_enum_protos(sorted, out); + emit_protos(merged_funcs, out); - if (sorted) - { - emit_struct_defs(ctx, sorted, out); - } - emit_trait_defs(kids, out); - - ASTNode *raw_iter = kids; - while (raw_iter) - { - if (raw_iter->type == NODE_RAW_STMT) - { - fprintf(out, "%s\n", raw_iter->raw_stmt.content); - } - raw_iter = raw_iter->next; - } + emit_impl_vtables(ctx, out); - ASTNode *merged_globals = NULL; // Head + emit_lambda_defs(ctx, out); - if (ctx->parsed_globals_list) - { - StructRef *s = ctx->parsed_globals_list; - while (s) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *s->node; - copy->next = merged_globals; - merged_globals = copy; + emit_tests_and_runner(ctx, kids, out); - s = s->next; - } + ASTNode *iter = merged_funcs; + while (iter) { + if (iter->type == NODE_IMPL) { + char *sname = iter->impl.struct_name; + if (!sname) { + iter = iter->next; + continue; } - emit_globals(ctx, merged_globals, out); - - ASTNode *merged_funcs = NULL; - ASTNode *merged_funcs_tail = NULL; - - if (ctx->instantiated_funcs) - { - ASTNode *s = ctx->instantiated_funcs; - while (s) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *s; - copy->next = NULL; - if (!merged_funcs) - { - merged_funcs = copy; - merged_funcs_tail = copy; - } - else - { - merged_funcs_tail->next = copy; - merged_funcs_tail = copy; - } - s = s->next; + char *mangled = replace_string_type(sname); + ASTNode *def = find_struct_def_codegen(ctx, mangled); + int skip = 0; + if (def) { + if (def->type == NODE_STRUCT && def->strct.is_template) { + skip = 1; + } else if (def->type == NODE_ENUM && def->enm.is_template) { + skip = 1; + } + } else { + char *lt = strchr(sname, '<'); + if (lt) { + 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) { + skip = 1; } + free(buf); + } } - - if (ctx->parsed_funcs_list) - { - StructRef *s = ctx->parsed_funcs_list; - while (s) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *s->node; - copy->next = NULL; - if (!merged_funcs) - { - merged_funcs = copy; - merged_funcs_tail = copy; - } - else - { - merged_funcs_tail->next = copy; - merged_funcs_tail = copy; - } - s = s->next; - } + if (mangled) { + free(mangled); } - - if (ctx->parsed_impls_list) - { - StructRef *s = ctx->parsed_impls_list; - while (s) - { - ASTNode *copy = xmalloc(sizeof(ASTNode)); - *copy = *s->node; - copy->next = NULL; - if (!merged_funcs) - { - merged_funcs = copy; - merged_funcs_tail = copy; - } - else - { - merged_funcs_tail->next = copy; - merged_funcs_tail = copy; - } - s = s->next; - } + if (skip) { + iter = iter->next; + continue; } - - emit_protos(merged_funcs, out); - - emit_impl_vtables(ctx, out); - - emit_lambda_defs(ctx, out); - - emit_tests_and_runner(ctx, kids, out); - - ASTNode *iter = merged_funcs; - while (iter) - { - if (iter->type == NODE_IMPL) - { - char *sname = iter->impl.struct_name; - if (!sname) - { - iter = iter->next; - continue; - } - - char *mangled = replace_string_type(sname); - ASTNode *def = find_struct_def_codegen(ctx, mangled); - int skip = 0; - if (def) - { - if (def->type == NODE_STRUCT && def->strct.is_template) - { - skip = 1; - } - else if (def->type == NODE_ENUM && def->enm.is_template) - { - skip = 1; - } - } - else - { - char *lt = strchr(sname, '<'); - if (lt) - { - 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) - { - skip = 1; - } - free(buf); - } - } - if (mangled) - { - free(mangled); - } - if (skip) - { - iter = iter->next; - continue; - } - } - if (iter->type == NODE_IMPL_TRAIT) - { - char *sname = iter->impl_trait.target_type; - if (!sname) - { - iter = iter->next; - continue; - } - - char *mangled = replace_string_type(sname); - ASTNode *def = find_struct_def_codegen(ctx, mangled); - int skip = 0; - if (def) - { - if (def->strct.is_template) - { - skip = 1; - } - } - else - { - char *lt = strchr(sname, '<'); - if (lt) - { - 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) - { - skip = 1; - } - free(buf); - } - } - if (mangled) - { - free(mangled); - } - if (skip) - { - iter = iter->next; - continue; - } - } - codegen_node_single(ctx, iter, out); - iter = iter->next; + } + if (iter->type == NODE_IMPL_TRAIT) { + char *sname = iter->impl_trait.target_type; + if (!sname) { + iter = iter->next; + continue; } - int has_user_main = 0; - ASTNode *chk = merged_funcs; - while (chk) - { - if (chk->type == NODE_FUNCTION && strcmp(chk->func.name, "main") == 0) - { - has_user_main = 1; - break; + char *mangled = replace_string_type(sname); + ASTNode *def = find_struct_def_codegen(ctx, mangled); + int skip = 0; + if (def) { + if (def->strct.is_template) { + skip = 1; + } + } else { + char *lt = strchr(sname, '<'); + if (lt) { + 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) { + skip = 1; } - chk = chk->next; + free(buf); + } } - - if (!has_user_main) - { - fprintf(out, "\nint main() { _z_run_tests(); return 0; }\n"); + if (mangled) { + free(mangled); + } + if (skip) { + iter = iter->next; + continue; } + } + codegen_node_single(ctx, iter, out); + iter = iter->next; + } + + int has_user_main = 0; + ASTNode *chk = merged_funcs; + while (chk) { + if (chk->type == NODE_FUNCTION && strcmp(chk->func.name, "main") == 0) { + has_user_main = 1; + break; + } + chk = chk->next; + } + + if (!has_user_main) { + fprintf(out, "\nint main() { _z_run_tests(); return 0; }\n"); } + } } diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 2feb757..c10685b 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -17,505 +17,417 @@ int defer_count = 0; ASTNode *g_current_lambda = NULL; // Helper to emit variable declarations with array types. -void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name) -{ - (void)ctx; - - char *bracket = strchr(type_str, '['); - - if (bracket) - { - int base_len = bracket - type_str; - fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket); - } - else - { - fprintf(out, "%s %s", type_str, var_name); - } +void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, + const char *var_name) { + (void)ctx; + + char *bracket = strchr(type_str, '['); + + if (bracket) { + int base_len = bracket - type_str; + fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket); + } else { + fprintf(out, "%s %s", type_str, var_name); + } } // Find struct definition -ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) -{ - if (!name) - { - return NULL; - } - ASTNode *s = global_user_structs; - while (s) - { - if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete) - { - return s; - } - s = s->next; +ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) { + if (!name) { + return NULL; + } + ASTNode *s = global_user_structs; + while (s) { + if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && + !s->strct.is_incomplete) { + return s; } - - // Check parsed structs list (imports)- - StructRef *sr = ctx->parsed_structs_list; - while (sr) - { - if (sr->node && sr->node->type == NODE_STRUCT && strcmp(sr->node->strct.name, name) == 0 && - !sr->node->strct.is_incomplete) - { - return sr->node; - } - sr = sr->next; + s = s->next; + } + + // Check parsed structs list (imports)- + StructRef *sr = ctx->parsed_structs_list; + while (sr) { + if (sr->node && sr->node->type == NODE_STRUCT && + strcmp(sr->node->strct.name, name) == 0 && + !sr->node->strct.is_incomplete) { + return sr->node; } - s = ctx->instantiated_structs; - while (s) - { - if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete) - { - return s; - } - s = s->next; + sr = sr->next; + } + s = ctx->instantiated_structs; + while (s) { + if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && + !s->strct.is_incomplete) { + return s; } - return NULL; + s = s->next; + } + return NULL; } // Get field type from struct. -char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name) -{ - char clean_name[256]; - strncpy(clean_name, struct_name, sizeof(clean_name) - 1); - clean_name[sizeof(clean_name) - 1] = 0; - - char *ptr = strchr(clean_name, '<'); - if (ptr) - { - *ptr = 0; - } - - ASTNode *def = find_struct_def_codegen(ctx, clean_name); - if (!def) - { - return NULL; - } +char *get_field_type_str(ParserContext *ctx, const char *struct_name, + const char *field_name) { + char clean_name[256]; + strncpy(clean_name, struct_name, sizeof(clean_name) - 1); + clean_name[sizeof(clean_name) - 1] = 0; + + char *ptr = strchr(clean_name, '<'); + if (ptr) { + *ptr = 0; + } + + ASTNode *def = find_struct_def_codegen(ctx, clean_name); + if (!def) { + return NULL; + } - ASTNode *f = def->strct.fields; - while (f) - { - if (strcmp(f->field.name, field_name) == 0) - { - return f->field.type; - } - f = f->next; + ASTNode *f = def->strct.fields; + while (f) { + if (strcmp(f->field.name, field_name) == 0) { + return f->field.type; } - return NULL; + f = f->next; + } + return NULL; } // Type inference. -char *infer_type(ParserContext *ctx, ASTNode *node) -{ - if (!node) - { - return NULL; +char *infer_type(ParserContext *ctx, ASTNode *node) { + if (!node) { + return NULL; + } + if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0) { + return node->resolved_type; + } + + if (node->type == NODE_EXPR_LITERAL) { + if (node->type_info) { + return type_to_string(node->type_info); } - if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0) - { - return node->resolved_type; + return NULL; + } + + if (node->type == NODE_EXPR_VAR) { + Symbol *sym = find_symbol_entry(ctx, node->var_ref.name); + if (sym) { + if (sym->type_name) { + return sym->type_name; + } + if (sym->type_info) { + return type_to_string(sym->type_info); + } } + } - if (node->type == NODE_EXPR_LITERAL) - { - if (node->type_info) - { - return type_to_string(node->type_info); + if (node->type == NODE_EXPR_CALL) { + if (node->call.callee->type == NODE_EXPR_VAR) { + FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name); + if (sig) { + if (sig->is_async) { + return "Async"; } - return NULL; - } - - if (node->type == NODE_EXPR_VAR) - { - Symbol *sym = find_symbol_entry(ctx, node->var_ref.name); - if (sym) - { - if (sym->type_name) - { - return sym->type_name; - } - if (sym->type_info) - { - return type_to_string(sym->type_info); - } + if (sig->ret_type) { + return type_to_string(sig->ret_type); } - } + } - if (node->type == NODE_EXPR_CALL) - { - if (node->call.callee->type == NODE_EXPR_VAR) - { - FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name); - if (sig) - { - if (sig->is_async) - { - return "Async"; - } - if (sig->ret_type) - { - return type_to_string(sig->ret_type); - } - } - - // Fallback for known stdlib memory functions. - if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 || - strcmp(node->call.callee->var_ref.name, "calloc") == 0 || - strcmp(node->call.callee->var_ref.name, "realloc") == 0) - { - return "void*"; - } - ASTNode *sdef = find_struct_def_codegen(ctx, node->call.callee->var_ref.name); - if (sdef) - { - return node->call.callee->var_ref.name; - } - } - // Method call: target.method() - look up Type_method signature. - if (node->call.callee->type == NODE_EXPR_MEMBER) - { - char *target_type = infer_type(ctx, node->call.callee->member.target); - if (target_type) - { - char clean_type[256]; - strcpy(clean_type, target_type); - char *ptr = strchr(clean_type, '*'); - if (ptr) - { - *ptr = 0; - } - - char *base = clean_type; - if (strncmp(base, "struct ", 7) == 0) - { - base += 7; - } - - char func_name[512]; - sprintf(func_name, "%s_%s", base, node->call.callee->member.field); - - FuncSig *sig = find_func(ctx, func_name); - if (sig && sig->ret_type) - { - return type_to_string(sig->ret_type); - } - } + // Fallback for known stdlib memory functions. + if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 || + strcmp(node->call.callee->var_ref.name, "calloc") == 0 || + strcmp(node->call.callee->var_ref.name, "realloc") == 0) { + return "void*"; + } + ASTNode *sdef = + find_struct_def_codegen(ctx, node->call.callee->var_ref.name); + if (sdef) { + return node->call.callee->var_ref.name; + } + } + // Method call: target.method() - look up Type_method signature. + if (node->call.callee->type == NODE_EXPR_MEMBER) { + char *target_type = infer_type(ctx, node->call.callee->member.target); + if (target_type) { + char clean_type[256]; + strcpy(clean_type, target_type); + char *ptr = strchr(clean_type, '*'); + if (ptr) { + *ptr = 0; } - if (node->call.callee->type == NODE_EXPR_VAR) - { - Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name); - if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION && - sym->type_info->inner) - { - return type_to_string(sym->type_info->inner); - } + char *base = clean_type; + if (strncmp(base, "struct ", 7) == 0) { + base += 7; } - } - if (node->type == NODE_TRY) - { - char *inner_type = infer_type(ctx, node->try_stmt.expr); - if (inner_type) - { - // Extract T from Result<T> or Option<T> - char *start = strchr(inner_type, '<'); - if (start) - { - start++; // Skip < - char *end = strrchr(inner_type, '>'); - if (end && end > start) - { - int len = end - start; - char *extracted = xmalloc(len + 1); - strncpy(extracted, start, len); - extracted[len] = 0; - return extracted; - } - } - } - } + char func_name[512]; + sprintf(func_name, "%s_%s", base, node->call.callee->member.field); - if (node->type == NODE_EXPR_MEMBER) - { - char *parent_type = infer_type(ctx, node->member.target); - if (!parent_type) - { - return NULL; + FuncSig *sig = find_func(ctx, func_name); + if (sig && sig->ret_type) { + return type_to_string(sig->ret_type); } + } + } - char clean_name[256]; - strcpy(clean_name, parent_type); - char *ptr = strchr(clean_name, '*'); - if (ptr) - { - *ptr = 0; - } + if (node->call.callee->type == NODE_EXPR_VAR) { + Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name); + if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION && + sym->type_info->inner) { + return type_to_string(sym->type_info->inner); + } + } + } + + if (node->type == NODE_TRY) { + char *inner_type = infer_type(ctx, node->try_stmt.expr); + if (inner_type) { + // Extract T from Result<T> or Option<T> + char *start = strchr(inner_type, '<'); + if (start) { + start++; // Skip < + char *end = strrchr(inner_type, '>'); + if (end && end > start) { + int len = end - start; + char *extracted = xmalloc(len + 1); + strncpy(extracted, start, len); + extracted[len] = 0; + return extracted; + } + } + } + } - return get_field_type_str(ctx, clean_name, node->member.field); + if (node->type == NODE_EXPR_MEMBER) { + char *parent_type = infer_type(ctx, node->member.target); + if (!parent_type) { + return NULL; } - if (node->type == NODE_EXPR_BINARY) - { - if (strcmp(node->binary.op, "??") == 0) - { - return infer_type(ctx, node->binary.left); - } + char clean_name[256]; + strcpy(clean_name, parent_type); + char *ptr = strchr(clean_name, '*'); + if (ptr) { + *ptr = 0; + } - const char *op = node->binary.op; - char *left_type = infer_type(ctx, node->binary.left); - char *right_type = infer_type(ctx, node->binary.right); + return get_field_type_str(ctx, clean_name, node->member.field); + } - int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || strcmp(op, "==") == 0 || - strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 || strcmp(op, ">") == 0 || - strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0); + if (node->type == NODE_EXPR_BINARY) { + if (strcmp(node->binary.op, "??") == 0) { + return infer_type(ctx, node->binary.left); + } - if (is_logical) - { - return xstrdup("int"); - } + const char *op = node->binary.op; + char *left_type = infer_type(ctx, node->binary.left); + char *right_type = infer_type(ctx, node->binary.right); - if (left_type && strcmp(left_type, "usize") == 0) - { - return "usize"; - } - if (right_type && strcmp(right_type, "usize") == 0) - { - return "usize"; - } - if (left_type && strcmp(left_type, "double") == 0) - { - return "double"; - } + int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || + strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 || + strcmp(op, "<") == 0 || strcmp(op, ">") == 0 || + strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0); - return left_type ? left_type : right_type; + if (is_logical) { + return xstrdup("int"); } - if (node->type == NODE_MATCH) - { - ASTNode *case_node = node->match_stmt.cases; - while (case_node) - { - char *type = infer_type(ctx, case_node->match_case.body); - if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0) - { - return type; - } - case_node = case_node->next; - } - return NULL; + if (left_type && strcmp(left_type, "usize") == 0) { + return "usize"; } - - if (node->type == NODE_EXPR_INDEX) - { - char *array_type = infer_type(ctx, node->index.array); - if (array_type) - { - // If T*, returns T. If T[], returns T. - char *ptr = strrchr(array_type, '*'); - if (ptr) - { - int len = ptr - array_type; - char *buf = xmalloc(len + 1); - strncpy(buf, array_type, len); - buf[len] = 0; - return buf; - } - } - return "int"; + if (right_type && strcmp(right_type, "usize") == 0) { + return "usize"; + } + if (left_type && strcmp(left_type, "double") == 0) { + return "double"; } - if (node->type == NODE_EXPR_UNARY) - { - if (strcmp(node->unary.op, "&") == 0) - { - char *inner = infer_type(ctx, node->unary.operand); - if (inner) - { - char *buf = xmalloc(strlen(inner) + 2); - sprintf(buf, "%s*", inner); - return buf; - } - } - if (strcmp(node->unary.op, "*") == 0) - { - char *inner = infer_type(ctx, node->unary.operand); - if (inner) - { - char *ptr = strchr(inner, '*'); - if (ptr) - { - // Return base type (naive) - int len = ptr - inner; - char *dup = xmalloc(len + 1); - strncpy(dup, inner, len); - dup[len] = 0; - return dup; - } - } - } - return infer_type(ctx, node->unary.operand); + return left_type ? left_type : right_type; + } + + if (node->type == NODE_MATCH) { + ASTNode *case_node = node->match_stmt.cases; + while (case_node) { + char *type = infer_type(ctx, case_node->match_case.body); + if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0) { + return type; + } + case_node = case_node->next; + } + return NULL; + } + + if (node->type == NODE_EXPR_INDEX) { + char *array_type = infer_type(ctx, node->index.array); + if (array_type) { + // If T*, returns T. If T[], returns T. + char *ptr = strrchr(array_type, '*'); + if (ptr) { + int len = ptr - array_type; + char *buf = xmalloc(len + 1); + strncpy(buf, array_type, len); + buf[len] = 0; + return buf; + } + } + return "int"; + } + + if (node->type == NODE_EXPR_UNARY) { + if (strcmp(node->unary.op, "&") == 0) { + char *inner = infer_type(ctx, node->unary.operand); + if (inner) { + char *buf = xmalloc(strlen(inner) + 2); + sprintf(buf, "%s*", inner); + return buf; + } + } + if (strcmp(node->unary.op, "*") == 0) { + char *inner = infer_type(ctx, node->unary.operand); + if (inner) { + char *ptr = strchr(inner, '*'); + if (ptr) { + // Return base type (naive) + int len = ptr - inner; + char *dup = xmalloc(len + 1); + strncpy(dup, inner, len); + dup[len] = 0; + return dup; + } + } + } + return infer_type(ctx, node->unary.operand); + } + + if (node->type == NODE_AWAIT) { + // Infer underlying type T from await Async<T> + // If it's a direct call await foo(), we know T from foo's signature. + if (node->unary.operand->type == NODE_EXPR_CALL && + node->unary.operand->call.callee->type == NODE_EXPR_VAR) { + FuncSig *sig = + find_func(ctx, node->unary.operand->call.callee->var_ref.name); + if (sig && sig->ret_type) { + return type_to_string(sig->ret_type); + } } - if (node->type == NODE_AWAIT) - { - // Infer underlying type T from await Async<T> - // If it's a direct call await foo(), we know T from foo's signature. - if (node->unary.operand->type == NODE_EXPR_CALL && - node->unary.operand->call.callee->type == NODE_EXPR_VAR) - { - FuncSig *sig = find_func(ctx, node->unary.operand->call.callee->var_ref.name); - if (sig && sig->ret_type) - { - return type_to_string(sig->ret_type); - } - } + return "void*"; + } - return "void*"; - } + if (node->type == NODE_EXPR_CAST) { + return node->cast.target_type; + } - if (node->type == NODE_EXPR_CAST) - { - return node->cast.target_type; - } + if (node->type == NODE_EXPR_STRUCT_INIT) { + return node->struct_init.struct_name; + } - if (node->type == NODE_EXPR_STRUCT_INIT) - { - return node->struct_init.struct_name; + if (node->type == NODE_EXPR_LITERAL) { + if (node->literal.type_kind == TOK_STRING) { + return "string"; } - - if (node->type == NODE_EXPR_LITERAL) - { - if (node->literal.type_kind == TOK_STRING) - { - return "string"; - } - if (node->literal.type_kind == TOK_CHAR) - { - return "char"; - } - if (node->literal.type_kind == 1) - { - return "double"; - } - return "int"; + if (node->literal.type_kind == TOK_CHAR) { + return "char"; + } + if (node->literal.type_kind == 1) { + return "double"; } + return "int"; + } - return NULL; + return NULL; } // Extract variable names from argument string. -char *extract_call_args(const char *args) -{ - if (!args || strlen(args) == 0) - { - return xstrdup(""); +char *extract_call_args(const char *args) { + if (!args || strlen(args) == 0) { + return xstrdup(""); + } + char *out = xmalloc(strlen(args) + 1); + out[0] = 0; + + char *dup = xstrdup(args); + char *p = strtok(dup, ","); + while (p) { + while (*p == ' ') { + p++; } - char *out = xmalloc(strlen(args) + 1); - out[0] = 0; - - char *dup = xstrdup(args); - char *p = strtok(dup, ","); - while (p) - { - while (*p == ' ') - { - p++; - } - char *last_space = strrchr(p, ' '); - char *ptr_star = strrchr(p, '*'); - - char *name = p; - if (last_space) - { - name = last_space + 1; - } - if (ptr_star && ptr_star > last_space) - { - name = ptr_star + 1; - } + char *last_space = strrchr(p, ' '); + char *ptr_star = strrchr(p, '*'); - if (strlen(out) > 0) - { - strcat(out, ", "); - } - strcat(out, name); + char *name = p; + if (last_space) { + name = last_space + 1; + } + if (ptr_star && ptr_star > last_space) { + name = ptr_star + 1; + } - p = strtok(NULL, ","); + if (strlen(out) > 0) { + strcat(out, ", "); } - free(dup); - return out; + strcat(out, name); + + p = strtok(NULL, ","); + } + free(dup); + return out; } // Parse original method name from mangled name. -const char *parse_original_method_name(const char *mangled) -{ - const char *last = strrchr(mangled, '_'); - return last ? last + 1 : mangled; +const char *parse_original_method_name(const char *mangled) { + const char *last = strrchr(mangled, '_'); + return last ? last + 1 : mangled; } // Replace string type in arguments. -char *replace_string_type(const char *args) -{ - if (!args) - { - return NULL; - } - char *res = xmalloc(strlen(args) * 2 + 1); - res[0] = 0; - const char *p = args; - while (*p) - { - const char *match = strstr(p, "string "); - if (match) - { - if (match > args && (isalnum(*(match - 1)) || *(match - 1) == '_')) - { - strncat(res, p, match - p + 6); - p = match + 6; - } - else - { - strncat(res, p, match - p); - strcat(res, "const char* "); - p = match + 7; - } - } - else - { - strcat(res, p); - break; - } +char *replace_string_type(const char *args) { + if (!args) { + return NULL; + } + char *res = xmalloc(strlen(args) * 2 + 1); + res[0] = 0; + const char *p = args; + while (*p) { + const char *match = strstr(p, "string "); + if (match) { + if (match > args && (isalnum(*(match - 1)) || *(match - 1) == '_')) { + strncat(res, p, match - p + 6); + p = match + 6; + } else { + strncat(res, p, match - p); + strcat(res, "const char* "); + p = match + 7; + } + } else { + strcat(res, p); + break; } - return res; + } + return res; } // Helper to emit auto type or fallback. -void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out) -{ - char *inferred = NULL; - if (init_expr) - { - inferred = infer_type(ctx, init_expr); - } - - if (inferred && strcmp(inferred, "__auto_type") != 0 && strcmp(inferred, "unknown") != 0) - { - fprintf(out, "%s", inferred); - } - else - { - if (strstr(g_config.cc, "tcc")) - { - zpanic_with_suggestion(t, - "Type inference failed for variable initialization and TCC does " - "not support __auto_type", - "Please specify the type explicitly: 'var x: Type = ...'"); - } - else - { - fprintf(out, "__auto_type"); - } +void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, + FILE *out) { + char *inferred = NULL; + if (init_expr) { + inferred = infer_type(ctx, init_expr); + } + + if (inferred && strcmp(inferred, "__auto_type") != 0 && + strcmp(inferred, "unknown") != 0) { + fprintf(out, "%s", inferred); + } else { + if (strstr(g_config.cc, "tcc")) { + zpanic_with_suggestion( + t, + "Type inference failed for variable initialization and TCC does " + "not support __auto_type", + "Please specify the type explicitly: 'var x: Type = ...'"); + } else { + fprintf(out, "__auto_type"); } + } } diff --git a/src/lexer/token.c b/src/lexer/token.c index cd662d9..01e414f 100644 --- a/src/lexer/token.c +++ b/src/lexer/token.c @@ -1,453 +1,353 @@ #include "zprep.h" -void lexer_init(Lexer *l, const char *src) -{ - l->src = src; - l->pos = 0; - l->line = 1; - l->col = 1; +void lexer_init(Lexer *l, const char *src) { + l->src = src; + l->pos = 0; + l->line = 1; + l->col = 1; } -static int is_ident_start(char c) -{ - return isalpha(c) || c == '_'; -} +static int is_ident_start(char c) { return isalpha(c) || c == '_'; } -static int is_ident_char(char c) -{ - return isalnum(c) || c == '_'; -} +static int is_ident_char(char c) { return isalnum(c) || c == '_'; } -Token lexer_next(Lexer *l) -{ - const char *s = l->src + l->pos; - int start_line = l->line; - int start_col = l->col; - - while (isspace(*s)) - { - if (*s == '\n') - { - l->line++; - l->col = 1; - } - else - { - l->col++; - } - l->pos++; - s++; - start_line = l->line; - start_col = l->col; - } - - // Check for EOF. - if (!*s) - { - return (Token){TOK_EOF, s, 0, start_line, start_col}; - } - - // C preprocessor directives. - if (*s == '#') - { - int len = 0; - while (s[len] && s[len] != '\n') - { - if (s[len] == '\\' && s[len + 1] == '\n') - { - len += 2; - l->line++; - } - else - { - len++; - } - } - l->pos += len; +Token lexer_next(Lexer *l) { + const char *s = l->src + l->pos; + int start_line = l->line; + int start_col = l->col; - return (Token){TOK_PREPROC, s, len, start_line, start_col}; + while (isspace(*s)) { + if (*s == '\n') { + l->line++; + l->col = 1; + } else { + l->col++; } - - // Comments. - if (s[0] == '/' && s[1] == '/') - { - int len = 2; - while (s[len] && s[len] != '\n') - { - len++; - } - l->pos += len; - l->col += len; - return lexer_next(l); + l->pos++; + s++; + start_line = l->line; + start_col = l->col; + } + + // Check for EOF. + if (!*s) { + return (Token){TOK_EOF, s, 0, start_line, start_col}; + } + + // C preprocessor directives. + if (*s == '#') { + int len = 0; + while (s[len] && s[len] != '\n') { + if (s[len] == '\\' && s[len + 1] == '\n') { + len += 2; + l->line++; + } else { + len++; + } } + l->pos += len; - // Block Comments. - if (s[0] == '/' && s[1] == '*') - { - // skip two start chars + return (Token){TOK_PREPROC, s, len, start_line, start_col}; + } + + // Comments. + if (s[0] == '/' && s[1] == '/') { + int len = 2; + while (s[len] && s[len] != '\n') { + len++; + } + l->pos += len; + l->col += len; + return lexer_next(l); + } + + // Block Comments. + if (s[0] == '/' && s[1] == '*') { + // skip two start chars + l->pos += 2; + s += 2; + + while (s[0]) { + // s[len+1] can be at most the null terminator + if (s[0] == '*' && s[1] == '/') { + // go over */ l->pos += 2; s += 2; - - while (s[0]) - { - // s[len+1] can be at most the null terminator - if (s[0] == '*' && s[1] == '/') - { - // go over */ - l->pos += 2; - s += 2; - break; - } - - if (s[0] == '\n') - { - l->line++; - l->col = 1; - } - else - { - l->col++; - } - - l->pos++; - s++; - } - - return lexer_next(l); + break; + } + + if (s[0] == '\n') { + l->line++; + l->col = 1; + } else { + l->col++; + } + + l->pos++; + s++; } - // Identifiers. - if (is_ident_start(*s)) - { - int len = 0; - while (is_ident_char(s[len])) - { - len++; - } + return lexer_next(l); + } - l->pos += len; - l->col += len; + // Identifiers. + if (is_ident_start(*s)) { + int len = 0; + while (is_ident_char(s[len])) { + len++; + } - if (len == 4 && strncmp(s, "test", 4) == 0) - { - return (Token){TOK_TEST, s, 4, start_line, start_col}; - } - if (len == 6 && strncmp(s, "assert", 6) == 0) - { - return (Token){TOK_ASSERT, s, 6, start_line, start_col}; - } - if (len == 6 && strncmp(s, "sizeof", 6) == 0) - { - return (Token){TOK_SIZEOF, s, 6, start_line, start_col}; - } - if (len == 5 && strncmp(s, "defer", 5) == 0) - { - return (Token){TOK_DEFER, s, 5, start_line, start_col}; - } - if (len == 8 && strncmp(s, "autofree", 8) == 0) - { - return (Token){TOK_AUTOFREE, s, 8, start_line, start_col}; - } - if (len == 3 && strncmp(s, "use", 3) == 0) - { - return (Token){TOK_USE, s, 3, start_line, start_col}; - } - if (len == 8 && strncmp(s, "comptime", 8) == 0) - { - return (Token){TOK_COMPTIME, s, 8, start_line, start_col}; - } - if (len == 5 && strncmp(s, "union", 5) == 0) - { - return (Token){TOK_UNION, s, 5, start_line, start_col}; - } - if (len == 3 && strncmp(s, "asm", 3) == 0) - { - return (Token){TOK_ASM, s, 3, start_line, start_col}; - } - if (len == 8 && strncmp(s, "volatile", 8) == 0) - { - return (Token){TOK_VOLATILE, s, 8, start_line, start_col}; - } - if (len == 3 && strncmp(s, "mut", 3) == 0) - { - return (Token){TOK_MUT, s, 3, start_line, start_col}; - } - if (len == 5 && strncmp(s, "async", 5) == 0) - { - return (Token){TOK_ASYNC, s, 5, start_line, start_col}; - } - if (len == 5 && strncmp(s, "await", 5) == 0) - { - return (Token){TOK_AWAIT, s, 5, start_line, start_col}; - } - if (len == 3 && strncmp(s, "and", 3) == 0) - { - return (Token){TOK_AND, s, 3, start_line, start_col}; - } - if (len == 2 && strncmp(s, "or", 2) == 0) - { - return (Token){TOK_OR, s, 2, start_line, start_col}; - } + l->pos += len; + l->col += len; - // F-Strings - if (len == 1 && s[0] == 'f' && s[1] == '"') - { - // Reset pos/col because we want to parse string - l->pos -= len; - l->col -= len; - } - else - { - return (Token){TOK_IDENT, s, len, start_line, start_col}; - } + if (len == 4 && strncmp(s, "test", 4) == 0) { + return (Token){TOK_TEST, s, 4, start_line, start_col}; } - - if (s[0] == 'f' && s[1] == '"') - { - int len = 2; - while (s[len] && s[len] != '"') - { - if (s[len] == '\\') - { - len++; - } - len++; - } - if (s[len] == '"') - { - len++; - } - l->pos += len; - l->col += len; - return (Token){TOK_FSTRING, s, len, start_line, start_col}; - } - - // Numbers - if (isdigit(*s)) - { - int len = 0; - if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) - { - len = 2; - while (isxdigit(s[len])) - { - len++; - } - } - else if (s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) - { - len = 2; - while (s[len] == '0' || s[len] == '1') - { - len++; - } - } - else - { - while (isdigit(s[len])) - { - len++; - } - if (s[len] == '.') - { - if (s[len + 1] != '.') - { - len++; - while (isdigit(s[len])) - { - len++; - } - l->pos += len; - l->col += len; - return (Token){TOK_FLOAT, s, len, start_line, start_col}; - } - } - } - l->pos += len; - l->col += len; - return (Token){TOK_INT, s, len, start_line, start_col}; - } - - // Strings - if (*s == '"') - { - int len = 1; - while (s[len] && s[len] != '"') - { - if (s[len] == '\\') - { - len++; - } - len++; - } - if (s[len] == '"') - { - len++; - } - l->pos += len; - l->col += len; - return (Token){TOK_STRING, s, len, start_line, start_col}; + if (len == 6 && strncmp(s, "assert", 6) == 0) { + return (Token){TOK_ASSERT, s, 6, start_line, start_col}; + } + if (len == 6 && strncmp(s, "sizeof", 6) == 0) { + return (Token){TOK_SIZEOF, s, 6, start_line, start_col}; + } + if (len == 5 && strncmp(s, "defer", 5) == 0) { + return (Token){TOK_DEFER, s, 5, start_line, start_col}; + } + if (len == 8 && strncmp(s, "autofree", 8) == 0) { + return (Token){TOK_AUTOFREE, s, 8, start_line, start_col}; + } + if (len == 3 && strncmp(s, "use", 3) == 0) { + return (Token){TOK_USE, s, 3, start_line, start_col}; + } + if (len == 8 && strncmp(s, "comptime", 8) == 0) { + return (Token){TOK_COMPTIME, s, 8, start_line, start_col}; + } + if (len == 5 && strncmp(s, "union", 5) == 0) { + return (Token){TOK_UNION, s, 5, start_line, start_col}; + } + if (len == 3 && strncmp(s, "asm", 3) == 0) { + return (Token){TOK_ASM, s, 3, start_line, start_col}; + } + if (len == 8 && strncmp(s, "volatile", 8) == 0) { + return (Token){TOK_VOLATILE, s, 8, start_line, start_col}; + } + if (len == 3 && strncmp(s, "mut", 3) == 0) { + return (Token){TOK_MUT, s, 3, start_line, start_col}; + } + if (len == 5 && strncmp(s, "async", 5) == 0) { + return (Token){TOK_ASYNC, s, 5, start_line, start_col}; + } + if (len == 5 && strncmp(s, "await", 5) == 0) { + return (Token){TOK_AWAIT, s, 5, start_line, start_col}; + } + if (len == 3 && strncmp(s, "and", 3) == 0) { + return (Token){TOK_AND, s, 3, start_line, start_col}; + } + if (len == 2 && strncmp(s, "or", 2) == 0) { + return (Token){TOK_OR, s, 2, start_line, start_col}; } - if (*s == '\'') - { - int len = 1; - // Handle escapes like '\n' or regular 'a' - if (s[len] == '\\') - { - len++; - len++; - } - else - { - len++; - } - if (s[len] == '\'') - { + // F-Strings + if (len == 1 && s[0] == 'f' && s[1] == '"') { + // Reset pos/col because we want to parse string + l->pos -= len; + l->col -= len; + } else { + return (Token){TOK_IDENT, s, len, start_line, start_col}; + } + } + + if (s[0] == 'f' && s[1] == '"') { + int len = 2; + while (s[len] && s[len] != '"') { + if (s[len] == '\\') { + len++; + } + len++; + } + if (s[len] == '"') { + len++; + } + l->pos += len; + l->col += len; + return (Token){TOK_FSTRING, s, len, start_line, start_col}; + } + + // Numbers + if (isdigit(*s)) { + int len = 0; + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + len = 2; + while (isxdigit(s[len])) { + len++; + } + } else if (s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) { + len = 2; + while (s[len] == '0' || s[len] == '1') { + len++; + } + } else { + while (isdigit(s[len])) { + len++; + } + if (s[len] == '.') { + if (s[len + 1] != '.') { + len++; + while (isdigit(s[len])) { len++; + } + l->pos += len; + l->col += len; + return (Token){TOK_FLOAT, s, len, start_line, start_col}; } - - l->pos += len; - l->col += len; - return (Token){TOK_CHAR, s, len, start_line, start_col}; + } } + l->pos += len; + l->col += len; + return (Token){TOK_INT, s, len, start_line, start_col}; + } - // Operators. + // Strings + if (*s == '"') { int len = 1; - TokenType type = TOK_OP; - - if (s[0] == '?' && s[1] == '.') - { - len = 2; - type = TOK_Q_DOT; - } - else if (s[0] == '?' && s[1] == '?') - { - if (s[2] == '=') - { - len = 3; - type = TOK_QQ_EQ; - } - else - { - len = 2; - type = TOK_QQ; - } - } - else if (*s == '?') - { - type = TOK_QUESTION; - } - else if (s[0] == '|' && s[1] == '>') - { - len = 2; - type = TOK_PIPE; - } - else if (s[0] == ':' && s[1] == ':') - { - len = 2; - type = TOK_DCOLON; - } - else if (s[0] == '.' && s[1] == '.' && s[2] == '.') - { - len = 3; - type = TOK_ELLIPSIS; - } - else if (s[0] == '.' && s[1] == '.') - { - len = 2; - type = TOK_DOTDOT; - } - else if ((s[0] == '-' && s[1] == '>') || (s[0] == '=' && s[1] == '>')) - { - len = 2; - type = TOK_ARROW; - } - - else if ((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>')) - { - len = 2; - if (s[2] == '=') - { - len = 3; // Handle <<= and >>= - } + while (s[len] && s[len] != '"') { + if (s[len] == '\\') { + len++; + } + len++; } - else if ((s[0] == '&' && s[1] == '&') || (s[0] == '|' && s[1] == '|') || - (s[0] == '+' && s[1] == '+') || (s[0] == '-' && s[1] == '-')) - { - len = 2; - } - else if (s[1] == '=') - { - // This catches: == != <= >= += -= *= /= %= |= &= ^= - if (strchr("=!<>+-*/%|&^", s[0])) - { - len = 2; - } + if (s[len] == '"') { + len++; } + l->pos += len; + l->col += len; + return (Token){TOK_STRING, s, len, start_line, start_col}; + } - else - { - switch (*s) - { - - case '(': - type = TOK_LPAREN; - break; - case ')': - type = TOK_RPAREN; - break; - case '{': - type = TOK_LBRACE; - break; - case '}': - type = TOK_RBRACE; - break; - case '[': - type = TOK_LBRACKET; - break; - case ']': - type = TOK_RBRACKET; - break; - case '<': - type = TOK_LANGLE; - break; - case '>': - type = TOK_RANGLE; - break; - case ',': - type = TOK_COMMA; - break; - case ':': - type = TOK_COLON; - break; - case ';': - type = TOK_SEMICOLON; - break; - case '@': - type = TOK_AT; - break; - default: - type = TOK_OP; - break; - } + if (*s == '\'') { + int len = 1; + // Handle escapes like '\n' or regular 'a' + if (s[len] == '\\') { + len++; + len++; + } else { + len++; + } + if (s[len] == '\'') { + len++; } l->pos += len; l->col += len; - return (Token){type, s, len, start_line, start_col}; + return (Token){TOK_CHAR, s, len, start_line, start_col}; + } + + // Operators. + int len = 1; + TokenType type = TOK_OP; + + if (s[0] == '?' && s[1] == '.') { + len = 2; + type = TOK_Q_DOT; + } else if (s[0] == '?' && s[1] == '?') { + if (s[2] == '=') { + len = 3; + type = TOK_QQ_EQ; + } else { + len = 2; + type = TOK_QQ; + } + } else if (*s == '?') { + type = TOK_QUESTION; + } else if (s[0] == '|' && s[1] == '>') { + len = 2; + type = TOK_PIPE; + } else if (s[0] == ':' && s[1] == ':') { + len = 2; + type = TOK_DCOLON; + } else if (s[0] == '.' && s[1] == '.' && s[2] == '.') { + len = 3; + type = TOK_ELLIPSIS; + } else if (s[0] == '.' && s[1] == '.') { + len = 2; + type = TOK_DOTDOT; + } else if ((s[0] == '-' && s[1] == '>') || (s[0] == '=' && s[1] == '>')) { + len = 2; + type = TOK_ARROW; + } + + else if ((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>')) { + len = 2; + if (s[2] == '=') { + len = 3; // Handle <<= and >>= + } + } else if ((s[0] == '&' && s[1] == '&') || (s[0] == '|' && s[1] == '|') || + (s[0] == '+' && s[1] == '+') || (s[0] == '-' && s[1] == '-')) { + len = 2; + } else if (s[1] == '=') { + // This catches: == != <= >= += -= *= /= %= |= &= ^= + if (strchr("=!<>+-*/%|&^", s[0])) { + len = 2; + } + } + + else { + switch (*s) { + + case '(': + type = TOK_LPAREN; + break; + case ')': + type = TOK_RPAREN; + break; + case '{': + type = TOK_LBRACE; + break; + case '}': + type = TOK_RBRACE; + break; + case '[': + type = TOK_LBRACKET; + break; + case ']': + type = TOK_RBRACKET; + break; + case '<': + type = TOK_LANGLE; + break; + case '>': + type = TOK_RANGLE; + break; + case ',': + type = TOK_COMMA; + break; + case ':': + type = TOK_COLON; + break; + case ';': + type = TOK_SEMICOLON; + break; + case '@': + type = TOK_AT; + break; + default: + type = TOK_OP; + break; + } + } + + l->pos += len; + l->col += len; + return (Token){type, s, len, start_line, start_col}; } -Token lexer_peek(Lexer *l) -{ - Lexer saved = *l; - return lexer_next(&saved); +Token lexer_peek(Lexer *l) { + Lexer saved = *l; + return lexer_next(&saved); } -Token lexer_peek2(Lexer *l) -{ - Lexer saved = *l; - lexer_next(&saved); - return lexer_next(&saved); +Token lexer_peek2(Lexer *l) { + Lexer saved = *l; + lexer_next(&saved); + return lexer_next(&saved); } diff --git a/src/lsp/json_rpc.c b/src/lsp/json_rpc.c index 903da71..c75af95 100644 --- a/src/lsp/json_rpc.c +++ b/src/lsp/json_rpc.c @@ -5,107 +5,81 @@ #include <string.h> // Basic JSON parsing helpers -char *get_json_string(const char *json, const char *key) -{ - char search[256]; - sprintf(search, "\"%s\":\"", key); - char *p = strstr(json, search); - if (!p) - { - return NULL; - } - p += strlen(search); - char *end = strchr(p, '"'); - if (!end) - { - return NULL; - } - int len = end - p; - char *res = malloc(len + 1); - strncpy(res, p, len); - res[len] = 0; - return res; +char *get_json_string(const char *json, const char *key) { + char search[256]; + sprintf(search, "\"%s\":\"", key); + char *p = strstr(json, search); + if (!p) { + return NULL; + } + p += strlen(search); + char *end = strchr(p, '"'); + if (!end) { + return NULL; + } + int len = end - p; + char *res = malloc(len + 1); + strncpy(res, p, len); + res[len] = 0; + return res; } // Extract nested "text" from params/contentChanges/0/text or // params/textDocument/text This is very hacky for MVP. proper JSON library // needed. -char *get_text_content(const char *json) -{ - char *p = strstr(json, "\"text\":\""); - if (!p) - { - return NULL; - } - p += 8; - - size_t cap = strlen(p); - char *res = malloc(cap + 1); - char *dst = res; - - while (*p) - { - if (*p == '\\') - { - p++; - if (*p == 'n') - { - *dst++ = '\n'; - } - else if (*p == 'r') - { - *dst++ = '\r'; - } - else if (*p == 't') - { - *dst++ = '\t'; - } - else if (*p == '"') - { - *dst++ = '"'; - } - else if (*p == '\\') - { - *dst++ = '\\'; - } - else - { - *dst++ = *p; // preserve others - } - p++; - } - else if (*p == '"') - { - break; // End of string. - } - else - { - *dst++ = *p++; - } +char *get_text_content(const char *json) { + char *p = strstr(json, "\"text\":\""); + if (!p) { + return NULL; + } + p += 8; + + size_t cap = strlen(p); + char *res = malloc(cap + 1); + char *dst = res; + + while (*p) { + if (*p == '\\') { + p++; + if (*p == 'n') { + *dst++ = '\n'; + } else if (*p == 'r') { + *dst++ = '\r'; + } else if (*p == 't') { + *dst++ = '\t'; + } else if (*p == '"') { + *dst++ = '"'; + } else if (*p == '\\') { + *dst++ = '\\'; + } else { + *dst++ = *p; // preserve others + } + p++; + } else if (*p == '"') { + break; // End of string. + } else { + *dst++ = *p++; } - *dst = 0; - return res; + } + *dst = 0; + return res; } // Helper to get line/char -void get_json_position(const char *json, int *line, int *col) -{ - char *pos = strstr(json, "\"position\":"); - if (!pos) - { - return; - } - char *l = strstr(pos, "\"line\":"); - if (l) - { - *line = atoi(l + 7); - } - char *c = strstr(pos, "\"character\":"); - if (c) - { - *col = atoi(c + 12); - } +void get_json_position(const char *json, int *line, int *col) { + char *pos = strstr(json, "\"position\":"); + if (!pos) { + return; + } + char *l = strstr(pos, "\"line\":"); + if (l) { + *line = atoi(l + 7); + } + char *c = strstr(pos, "\"character\":"); + if (c) { + *col = atoi(c + 12); + } } void lsp_check_file(const char *uri, const char *src); @@ -113,82 +87,71 @@ void lsp_goto_definition(const char *uri, int line, int col); void lsp_hover(const char *uri, int line, int col); void lsp_completion(const char *uri, int line, int col); -void handle_request(const char *json_str) -{ - if (strstr(json_str, "\"method\":\"initialize\"")) - { - const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{" - "\"capabilities\":{\"textDocumentSync\":1," - "\"definitionProvider\":true,\"hoverProvider\":true," - "\"completionProvider\":{" - "\"triggerCharacters\":[\".\"]}}}}"; - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response), response); - fflush(stdout); - return; +void handle_request(const char *json_str) { + if (strstr(json_str, "\"method\":\"initialize\"")) { + const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{" + "\"capabilities\":{\"textDocumentSync\":1," + "\"definitionProvider\":true,\"hoverProvider\":true," + "\"completionProvider\":{" + "\"triggerCharacters\":[\".\"]}}}}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response), + response); + fflush(stdout); + return; + } + + if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") || + strstr(json_str, "\"method\":\"textDocument/didChange\"")) { + + char *uri = get_json_string(json_str, "uri"); + char *text = get_text_content(json_str); + + if (uri && text) { + fprintf(stderr, "zls: Checking %s\n", uri); + lsp_check_file(uri, text); } - if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") || - strstr(json_str, "\"method\":\"textDocument/didChange\"")) - { - - char *uri = get_json_string(json_str, "uri"); - char *text = get_text_content(json_str); - - if (uri && text) - { - fprintf(stderr, "zls: Checking %s\n", uri); - lsp_check_file(uri, text); - } - - if (uri) - { - free(uri); - } - if (text) - { - free(text); - } + if (uri) { + free(uri); + } + if (text) { + free(text); } + } + + if (strstr(json_str, "\"method\":\"textDocument/definition\"")) { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); - if (strstr(json_str, "\"method\":\"textDocument/definition\"")) - { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - - if (uri) - { - fprintf(stderr, "zls: Definition request at %d:%d\n", line, col); - lsp_goto_definition(uri, line, col); - free(uri); - } + if (uri) { + fprintf(stderr, "zls: Definition request at %d:%d\n", line, col); + lsp_goto_definition(uri, line, col); + free(uri); } + } - if (strstr(json_str, "\"method\":\"textDocument/hover\"")) - { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - - if (uri) - { - fprintf(stderr, "zls: Hover request at %d:%d\n", line, col); - lsp_hover(uri, line, col); - free(uri); - } + if (strstr(json_str, "\"method\":\"textDocument/hover\"")) { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); + + if (uri) { + fprintf(stderr, "zls: Hover request at %d:%d\n", line, col); + lsp_hover(uri, line, col); + free(uri); } + } + + if (strstr(json_str, "\"method\":\"textDocument/completion\"")) { + char *uri = get_json_string(json_str, "uri"); + int line = 0, col = 0; + get_json_position(json_str, &line, &col); - if (strstr(json_str, "\"method\":\"textDocument/completion\"")) - { - char *uri = get_json_string(json_str, "uri"); - int line = 0, col = 0; - get_json_position(json_str, &line, &col); - - if (uri) - { - fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); - lsp_completion(uri, line, col); - free(uri); - } + if (uri) { + fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); + lsp_completion(uri, line, col); + free(uri); } + } } diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c index d455894..73ba2c5 100644 --- a/src/lsp/lsp_analysis.c +++ b/src/lsp/lsp_analysis.c @@ -9,387 +9,335 @@ static LSPIndex *g_index = NULL; -typedef struct Diagnostic -{ - int line; - int col; - char *message; - struct Diagnostic *next; +typedef struct Diagnostic { + int line; + int col; + char *message; + struct Diagnostic *next; } Diagnostic; -typedef struct -{ - Diagnostic *head; - Diagnostic *tail; +typedef struct { + Diagnostic *head; + Diagnostic *tail; } DiagnosticList; static ParserContext *g_ctx = NULL; static char *g_last_src = NULL; // Callback for parser errors. -void lsp_on_error(void *data, Token t, const char *msg) -{ - DiagnosticList *list = (DiagnosticList *)data; - Diagnostic *d = xmalloc(sizeof(Diagnostic)); - d->line = t.line > 0 ? t.line - 1 : 0; - d->col = t.col > 0 ? t.col - 1 : 0; - d->message = xstrdup(msg); - d->next = NULL; - - if (!list->head) - { - list->head = d; - list->tail = d; - } - else - { - list->tail->next = d; - list->tail = d; - } +void lsp_on_error(void *data, Token t, const char *msg) { + DiagnosticList *list = (DiagnosticList *)data; + Diagnostic *d = xmalloc(sizeof(Diagnostic)); + d->line = t.line > 0 ? t.line - 1 : 0; + d->col = t.col > 0 ? t.col - 1 : 0; + d->message = xstrdup(msg); + d->next = NULL; + + if (!list->head) { + list->head = d; + list->tail = d; + } else { + list->tail->next = d; + list->tail = d; + } } -void lsp_check_file(const char *uri, const char *json_src) -{ - // Prepare ParserContext (persistent). - if (g_ctx) - { +void lsp_check_file(const char *uri, const char *json_src) { + // Prepare ParserContext (persistent). + if (g_ctx) { - free(g_ctx); - } - g_ctx = calloc(1, sizeof(ParserContext)); - g_ctx->is_fault_tolerant = 1; - - DiagnosticList diagnostics = {0}; - g_ctx->error_callback_data = &diagnostics; - g_ctx->on_error = lsp_on_error; - - // Prepare Lexer. - // Cache source. - if (g_last_src) - { - free(g_last_src); - } - g_last_src = strdup(json_src); + free(g_ctx); + } + g_ctx = calloc(1, sizeof(ParserContext)); + g_ctx->is_fault_tolerant = 1; - Lexer l; - lexer_init(&l, json_src); + DiagnosticList diagnostics = {0}; + g_ctx->error_callback_data = &diagnostics; + g_ctx->on_error = lsp_on_error; - ASTNode *root = parse_program(g_ctx, &l); + // Prepare Lexer. + // Cache source. + if (g_last_src) { + free(g_last_src); + } + g_last_src = strdup(json_src); - if (g_index) - { - lsp_index_free(g_index); - } - g_index = lsp_index_new(); - if (root) - { - lsp_build_index(g_index, root); - } + Lexer l; + lexer_init(&l, json_src); + + ASTNode *root = parse_program(g_ctx, &l); - // Construct JSON Response (notification) + if (g_index) { + lsp_index_free(g_index); + } + g_index = lsp_index_new(); + if (root) { + lsp_build_index(g_index, root); + } + + // Construct JSON Response (notification) + + char *notification = malloc(128 * 1024); + char *p = notification; + p += sprintf( + p, + "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" + "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[", + uri); + + Diagnostic *d = diagnostics.head; + while (d) { - char *notification = malloc(128 * 1024); - char *p = notification; p += sprintf(p, - "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" - "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[", - uri); - - Diagnostic *d = diagnostics.head; - while (d) - { - - p += sprintf(p, - "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":" - "{\"line\":%d," - "\"character\":%d}},\"severity\":1,\"message\":\"%s\"}", - d->line, d->col, d->line, d->col + 1, d->message); - - if (d->next) - { - p += sprintf(p, ","); - } + "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":" + "{\"line\":%d," + "\"character\":%d}},\"severity\":1,\"message\":\"%s\"}", + d->line, d->col, d->line, d->col + 1, d->message); - d = d->next; + if (d->next) { + p += sprintf(p, ","); } - p += sprintf(p, "]}}"); + d = d->next; + } - // Send notification. - long len = strlen(notification); - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); - fflush(stdout); + p += sprintf(p, "]}}"); - free(notification); + // Send notification. + long len = strlen(notification); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); + fflush(stdout); - // Cleanup. - Diagnostic *cur = diagnostics.head; - while (cur) - { - Diagnostic *next = cur->next; - free(cur->message); - free(cur); - cur = next; - } -} + free(notification); -void lsp_goto_definition(const char *uri, int line, int col) -{ - if (!g_index) - { - return; - } + // Cleanup. + Diagnostic *cur = diagnostics.head; + while (cur) { + Diagnostic *next = cur->next; + free(cur->message); + free(cur); + cur = next; + } +} - LSPRange *r = lsp_find_at(g_index, line, col); - if (r && r->type == RANGE_REFERENCE) - { - // Found reference, return definition - char resp[1024]; - sprintf(resp, - "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\"," - "\"range\":{\"start\":{" - "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%" - "d}}}}", - uri, r->def_line, r->def_col, r->def_line, r->def_col); - - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); - fflush(stdout); - } - else if (r && r->type == RANGE_DEFINITION) - { - // Already at definition? Return itself. - char resp[1024]; - sprintf(resp, - "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\"," - "\"range\":{\"start\":{" - "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%" - "d}}}}", - uri, r->start_line, r->start_col, r->end_line, r->end_col); - - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); - fflush(stdout); - } - else - { - // Null result - const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp); - fflush(stdout); - } +void lsp_goto_definition(const char *uri, int line, int col) { + if (!g_index) { + return; + } + + LSPRange *r = lsp_find_at(g_index, line, col); + if (r && r->type == RANGE_REFERENCE) { + // Found reference, return definition + char resp[1024]; + sprintf(resp, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\"," + "\"range\":{\"start\":{" + "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%" + "d}}}}", + uri, r->def_line, r->def_col, r->def_line, r->def_col); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); + fflush(stdout); + } else if (r && r->type == RANGE_DEFINITION) { + // Already at definition? Return itself. + char resp[1024]; + sprintf(resp, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\"," + "\"range\":{\"start\":{" + "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%" + "d}}}}", + uri, r->start_line, r->start_col, r->end_line, r->end_col); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp); + fflush(stdout); + } else { + // Null result + const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), + null_resp); + fflush(stdout); + } } -void lsp_hover(const char *uri, int line, int col) -{ - (void)uri; - if (!g_index) - { - return; +void lsp_hover(const char *uri, int line, int col) { + (void)uri; + if (!g_index) { + return; + } + + LSPRange *r = lsp_find_at(g_index, line, col); + char *text = NULL; + + if (r) { + if (r->type == RANGE_DEFINITION) { + text = r->hover_text; + } else if (r->type == RANGE_REFERENCE) { + LSPRange *def = lsp_find_at(g_index, r->def_line, r->def_col); + if (def && def->type == RANGE_DEFINITION) { + text = def->hover_text; + } } + } - LSPRange *r = lsp_find_at(g_index, line, col); - char *text = NULL; + if (text) { + char *json = malloc(16384); + // content: { kind: markdown, value: text } + sprintf(json, + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":" + "\"markdown\"," + "\"value\":\"```c\\n%s\\n```\"}}}", + text); - if (r) - { - if (r->type == RANGE_DEFINITION) - { - text = r->hover_text; - } - else if (r->type == RANGE_REFERENCE) - { - LSPRange *def = lsp_find_at(g_index, r->def_line, r->def_col); - if (def && def->type == RANGE_DEFINITION) - { - text = def->hover_text; - } - } - } - - if (text) - { - char *json = malloc(16384); - // content: { kind: markdown, value: text } - sprintf(json, - "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":" - "\"markdown\"," - "\"value\":\"```c\\n%s\\n```\"}}}", - text); - - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); - fflush(stdout); - free(json); - } - else - { - const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp); - fflush(stdout); - } + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); + fflush(stdout); + free(json); + } else { + const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}"; + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), + null_resp); + fflush(stdout); + } } -void lsp_completion(const char *uri, int line, int col) -{ - (void)uri; - if (!g_ctx) - { - return; +void lsp_completion(const char *uri, int line, int col) { + (void)uri; + if (!g_ctx) { + return; + } + + // Context-aware completion (Dot access) + if (g_last_src) { + // Simple line access + int cur_line = 0; + + char *ptr = g_last_src; + // Fast forward to line + while (*ptr && cur_line < line) { + if (*ptr == '\n') { + cur_line++; + } + ptr++; } - // Context-aware completion (Dot access) - if (g_last_src) - { - // Simple line access - int cur_line = 0; - - char *ptr = g_last_src; - // Fast forward to line - while (*ptr && cur_line < line) - { - if (*ptr == '\n') - { - cur_line++; - } - ptr++; + if (col > 0 && ptr[col - 1] == '.') { + // Found dot! + // Scan backwards for identifier: [whitespace] [ident] . + int i = col - 2; + while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t')) { + i--; + } + + if (i >= 0) { + int end_ident = i; + while (i >= 0 && (isalnum(ptr[i]) || ptr[i] == '_')) { + i--; } - - if (col > 0 && ptr[col - 1] == '.') - { - // Found dot! - // Scan backwards for identifier: [whitespace] [ident] . - int i = col - 2; - while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t')) - { - i--; + int start_ident = i + 1; + + if (start_ident <= end_ident) { + int len = end_ident - start_ident + 1; + char var_name[256]; + strncpy(var_name, ptr + start_ident, len); + var_name[len] = 0; + + char *type_name = NULL; + Symbol *sym = find_symbol_in_all(g_ctx, var_name); + + if (sym) { + if (sym->type_info) { + type_name = type_to_string(sym->type_info); + } else if (sym->type_name) { + type_name = sym->type_name; } + } - if (i >= 0) - { - int end_ident = i; - while (i >= 0 && (isalnum(ptr[i]) || ptr[i] == '_')) - { - i--; - } - int start_ident = i + 1; - - if (start_ident <= end_ident) - { - int len = end_ident - start_ident + 1; - char var_name[256]; - strncpy(var_name, ptr + start_ident, len); - var_name[len] = 0; - - char *type_name = NULL; - Symbol *sym = find_symbol_in_all(g_ctx, var_name); - - if (sym) - { - if (sym->type_info) - { - type_name = type_to_string(sym->type_info); - } - else if (sym->type_name) - { - type_name = sym->type_name; - } - } - - if (type_name) - { - char clean_name[256]; - char *src = type_name; - if (0 == strncmp(src, "struct ", 7)) - { - src += 7; - } - char *dst = clean_name; - while (*src && *src != '*') - { - *dst++ = *src++; - } - *dst = 0; - - // Lookup struct. - StructDef *sd = g_ctx->struct_defs; - while (sd) - { - if (0 == strcmp(sd->name, clean_name)) - { - char *json_fields = malloc(1024 * 1024); - char *pj = json_fields; - pj += sprintf(pj, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); - - int ffirst = 1; - if (sd->node && sd->node->strct.fields) - { - ASTNode *field = sd->node->strct.fields; - while (field) - { - if (!ffirst) - { - pj += sprintf(pj, ","); - } - pj += sprintf( - pj, - "{\"label\":\"%s\",\"kind\":5,\"detail\":\"field %s\"}", - field->field.name, field->field.type); // Kind 5 = Field - ffirst = 0; - field = field->next; - } - } - - pj += sprintf(pj, "]}"); - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", - strlen(json_fields), json_fields); - fflush(stdout); - free(json_fields); - free(json); - return; // Done, yippee. - } - sd = sd->next; - } + if (type_name) { + char clean_name[256]; + char *src = type_name; + if (0 == strncmp(src, "struct ", 7)) { + src += 7; + } + char *dst = clean_name; + while (*src && *src != '*') { + *dst++ = *src++; + } + *dst = 0; + + // Lookup struct. + StructDef *sd = g_ctx->struct_defs; + while (sd) { + if (0 == strcmp(sd->name, clean_name)) { + char *json_fields = malloc(1024 * 1024); + char *pj = json_fields; + pj += sprintf(pj, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); + + int ffirst = 1; + if (sd->node && sd->node->strct.fields) { + ASTNode *field = sd->node->strct.fields; + while (field) { + if (!ffirst) { + pj += sprintf(pj, ","); } + pj += sprintf( + pj, + "{\"label\":\"%s\",\"kind\":5,\"detail\":\"field %s\"}", + field->field.name, field->field.type); // Kind 5 = Field + ffirst = 0; + field = field->next; + } } + + pj += sprintf(pj, "]}"); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", + strlen(json_fields), json_fields); + fflush(stdout); + free(json_fields); + free(json); + return; // Done, yippee. + } + sd = sd->next; } + } } + } } + } - char *json = xmalloc(1024 * 1024); // 1MB buffer. - char *p = json; - p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); + char *json = xmalloc(1024 * 1024); // 1MB buffer. + char *p = json; + p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":["); - int first = 1; + int first = 1; - // Functions - FuncSig *f = g_ctx->func_registry; - while (f) - { - if (!first) - { - p += sprintf(p, ","); - } - p += sprintf(p, "{\"label\":\"%s\",\"kind\":3,\"detail\":\"fn %s(...)\"}", f->name, - f->name); // Kind 3 = Function - first = 0; - f = f->next; + // Functions + FuncSig *f = g_ctx->func_registry; + while (f) { + if (!first) { + p += sprintf(p, ","); } - - // Structs - StructDef *s = g_ctx->struct_defs; - while (s) - { - if (!first) - { - p += sprintf(p, ","); - } - p += sprintf(p, "{\"label\":\"%s\",\"kind\":22,\"detail\":\"struct %s\"}", s->name, - s->name); // Kind 22 = Struct - first = 0; - s = s->next; + p += sprintf(p, "{\"label\":\"%s\",\"kind\":3,\"detail\":\"fn %s(...)\"}", + f->name, + f->name); // Kind 3 = Function + first = 0; + f = f->next; + } + + // Structs + StructDef *s = g_ctx->struct_defs; + while (s) { + if (!first) { + p += sprintf(p, ","); } - - p += sprintf(p, "]}"); - - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); - fflush(stdout); - free(json); + p += sprintf(p, "{\"label\":\"%s\",\"kind\":22,\"detail\":\"struct %s\"}", + s->name, + s->name); // Kind 22 = Struct + first = 0; + s = s->next; + } + + p += sprintf(p, "]}"); + + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json); + fflush(stdout); + free(json); } diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c index d952b77..fdd0cf8 100644 --- a/src/lsp/lsp_index.c +++ b/src/lsp/lsp_index.c @@ -4,201 +4,168 @@ #include <stdlib.h> #include <string.h> -LSPIndex *lsp_index_new() -{ - return calloc(1, sizeof(LSPIndex)); -} - -void lsp_index_free(LSPIndex *idx) -{ - if (!idx) - { - return; - } - LSPRange *c = idx->head; - while (c) - { - LSPRange *n = c->next; - if (c->hover_text) - { - free(c->hover_text); - } - free(c); - c = n; - } - free(idx); -} - -void lsp_index_add(LSPIndex *idx, LSPRange *r) -{ - if (!idx->head) - { - idx->head = r; - idx->tail = r; - } - else - { - idx->tail->next = r; - idx->tail = r; +LSPIndex *lsp_index_new() { return calloc(1, sizeof(LSPIndex)); } + +void lsp_index_free(LSPIndex *idx) { + if (!idx) { + return; + } + LSPRange *c = idx->head; + while (c) { + LSPRange *n = c->next; + if (c->hover_text) { + free(c->hover_text); } + free(c); + c = n; + } + free(idx); } -void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node) -{ - if (t.line <= 0) - { - return; - } - LSPRange *r = calloc(1, sizeof(LSPRange)); - r->type = RANGE_DEFINITION; - r->start_line = t.line - 1; - r->start_col = t.col - 1; - r->end_line = t.line - 1; - r->end_col = t.col - 1 + t.len; - if (hover) - { - r->hover_text = strdup(hover); - } - r->node = node; - - lsp_index_add(idx, r); +void lsp_index_add(LSPIndex *idx, LSPRange *r) { + if (!idx->head) { + idx->head = r; + idx->tail = r; + } else { + idx->tail->next = r; + idx->tail = r; + } } -void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node) -{ - if (t.line <= 0 || def_t.line <= 0) - { - return; - } - LSPRange *r = calloc(1, sizeof(LSPRange)); - r->type = RANGE_REFERENCE; - r->start_line = t.line - 1; - r->start_col = t.col - 1; - r->end_line = t.line - 1; - r->end_col = t.col - 1 + t.len; - - r->def_line = def_t.line - 1; - r->def_col = def_t.col - 1; - r->node = node; - - lsp_index_add(idx, r); +void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, + ASTNode *node) { + if (t.line <= 0) { + return; + } + LSPRange *r = calloc(1, sizeof(LSPRange)); + r->type = RANGE_DEFINITION; + r->start_line = t.line - 1; + r->start_col = t.col - 1; + r->end_line = t.line - 1; + r->end_col = t.col - 1 + t.len; + if (hover) { + r->hover_text = strdup(hover); + } + r->node = node; + + lsp_index_add(idx, r); } -LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) -{ - LSPRange *curr = idx->head; - LSPRange *best = NULL; - - while (curr) - { - if (line >= curr->start_line && line <= curr->end_line) - { - if (line == curr->start_line && col < curr->start_col) - { - curr = curr->next; - continue; - } - - if (line == curr->end_line && col > curr->end_col) - { - curr = curr->next; - continue; - } - - best = curr; - } - curr = curr->next; - } - return best; +void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node) { + if (t.line <= 0 || def_t.line <= 0) { + return; + } + LSPRange *r = calloc(1, sizeof(LSPRange)); + r->type = RANGE_REFERENCE; + r->start_line = t.line - 1; + r->start_col = t.col - 1; + r->end_line = t.line - 1; + r->end_col = t.col - 1 + t.len; + + r->def_line = def_t.line - 1; + r->def_col = def_t.col - 1; + r->node = node; + + lsp_index_add(idx, r); } -// Walker. - -void lsp_walk_node(LSPIndex *idx, ASTNode *node) -{ - if (!node) - { - return; - } - - // Definition logic. - if (node->type == NODE_FUNCTION) - { - char hover[256]; - sprintf(hover, "fn %s(...) -> %s", node->func.name, - node->func.ret_type ? node->func.ret_type : "void"); - lsp_index_add_def(idx, node->token, hover, node); +LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) { + LSPRange *curr = idx->head; + LSPRange *best = NULL; - // Recurse body. - lsp_walk_node(idx, node->func.body); - } - else if (node->type == NODE_VAR_DECL) - { - char hover[256]; - sprintf(hover, "var %s", node->var_decl.name); - lsp_index_add_def(idx, node->token, hover, node); - - lsp_walk_node(idx, node->var_decl.init_expr); - } - else if (node->type == NODE_CONST) - { - char hover[256]; - sprintf(hover, "const %s", node->var_decl.name); - lsp_index_add_def(idx, node->token, hover, node); + while (curr) { + if (line >= curr->start_line && line <= curr->end_line) { + if (line == curr->start_line && col < curr->start_col) { + curr = curr->next; + continue; + } - lsp_walk_node(idx, node->var_decl.init_expr); - } + if (line == curr->end_line && col > curr->end_col) { + curr = curr->next; + continue; + } - // Reference logic. - if (node->definition_token.line > 0 && node->definition_token.line != node->token.line) - { - // It has a definition! - lsp_index_add_ref(idx, node->token, node->definition_token, node); - } - else if (node->definition_token.line > 0) - { - lsp_index_add_ref(idx, node->token, node->definition_token, node); + best = curr; } + curr = curr->next; + } + return best; +} - // General recursion. - - switch (node->type) - { - case NODE_ROOT: - lsp_walk_node(idx, node->root.children); - break; - case NODE_BLOCK: - lsp_walk_node(idx, node->block.statements); - break; - case NODE_IF: - lsp_walk_node(idx, node->if_stmt.condition); - lsp_walk_node(idx, node->if_stmt.then_body); - lsp_walk_node(idx, node->if_stmt.else_body); - break; - case NODE_WHILE: - lsp_walk_node(idx, node->while_stmt.condition); - lsp_walk_node(idx, node->while_stmt.body); - break; - case NODE_RETURN: - lsp_walk_node(idx, node->ret.value); - break; - case NODE_EXPR_BINARY: - lsp_walk_node(idx, node->binary.left); - lsp_walk_node(idx, node->binary.right); - break; - case NODE_EXPR_CALL: - lsp_walk_node(idx, node->call.callee); - lsp_walk_node(idx, node->call.args); - break; - default: - break; - } +// Walker. - // Walk next sibling. - lsp_walk_node(idx, node->next); +void lsp_walk_node(LSPIndex *idx, ASTNode *node) { + if (!node) { + return; + } + + // Definition logic. + if (node->type == NODE_FUNCTION) { + char hover[256]; + sprintf(hover, "fn %s(...) -> %s", node->func.name, + node->func.ret_type ? node->func.ret_type : "void"); + lsp_index_add_def(idx, node->token, hover, node); + + // Recurse body. + lsp_walk_node(idx, node->func.body); + } else if (node->type == NODE_VAR_DECL) { + char hover[256]; + sprintf(hover, "var %s", node->var_decl.name); + lsp_index_add_def(idx, node->token, hover, node); + + lsp_walk_node(idx, node->var_decl.init_expr); + } else if (node->type == NODE_CONST) { + char hover[256]; + sprintf(hover, "const %s", node->var_decl.name); + lsp_index_add_def(idx, node->token, hover, node); + + lsp_walk_node(idx, node->var_decl.init_expr); + } + + // Reference logic. + if (node->definition_token.line > 0 && + node->definition_token.line != node->token.line) { + // It has a definition! + lsp_index_add_ref(idx, node->token, node->definition_token, node); + } else if (node->definition_token.line > 0) { + lsp_index_add_ref(idx, node->token, node->definition_token, node); + } + + // General recursion. + + switch (node->type) { + case NODE_ROOT: + lsp_walk_node(idx, node->root.children); + break; + case NODE_BLOCK: + lsp_walk_node(idx, node->block.statements); + break; + case NODE_IF: + lsp_walk_node(idx, node->if_stmt.condition); + lsp_walk_node(idx, node->if_stmt.then_body); + lsp_walk_node(idx, node->if_stmt.else_body); + break; + case NODE_WHILE: + lsp_walk_node(idx, node->while_stmt.condition); + lsp_walk_node(idx, node->while_stmt.body); + break; + case NODE_RETURN: + lsp_walk_node(idx, node->ret.value); + break; + case NODE_EXPR_BINARY: + lsp_walk_node(idx, node->binary.left); + lsp_walk_node(idx, node->binary.right); + break; + case NODE_EXPR_CALL: + lsp_walk_node(idx, node->call.callee); + lsp_walk_node(idx, node->call.args); + break; + default: + break; + } + + // Walk next sibling. + lsp_walk_node(idx, node->next); } -void lsp_build_index(LSPIndex *idx, ASTNode *root) -{ - lsp_walk_node(idx, root); -} +void lsp_build_index(LSPIndex *idx, ASTNode *root) { lsp_walk_node(idx, root); } diff --git a/src/lsp/lsp_index.h b/src/lsp/lsp_index.h index f4aaf96..8b8207b 100644 --- a/src/lsp/lsp_index.h +++ b/src/lsp/lsp_index.h @@ -4,36 +4,31 @@ #include "parser.h" -typedef enum -{ - RANGE_DEFINITION, - RANGE_REFERENCE -} RangeType; - -typedef struct LSPRange -{ - int start_line; - int start_col; - int end_line; - int end_col; // Approximation. - RangeType type; - int def_line; - int def_col; - char *hover_text; - ASTNode *node; - struct LSPRange *next; +typedef enum { RANGE_DEFINITION, RANGE_REFERENCE } RangeType; + +typedef struct LSPRange { + int start_line; + int start_col; + int end_line; + int end_col; // Approximation. + RangeType type; + int def_line; + int def_col; + char *hover_text; + ASTNode *node; + struct LSPRange *next; } LSPRange; -typedef struct LSPIndex -{ - LSPRange *head; - LSPRange *tail; +typedef struct LSPIndex { + LSPRange *head; + LSPRange *tail; } LSPIndex; // API. LSPIndex *lsp_index_new(); void lsp_index_free(LSPIndex *idx); -void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node); +void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, + ASTNode *node); void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node); LSPRange *lsp_find_at(LSPIndex *idx, int line, int col); diff --git a/src/lsp/lsp_main.c b/src/lsp/lsp_main.c index fbe5312..a9a9f06 100644 --- a/src/lsp/lsp_main.c +++ b/src/lsp/lsp_main.c @@ -6,55 +6,47 @@ #include <unistd.h> // Simple Main Loop for LSP. -int lsp_main(int argc, char **argv) -{ - (void)argc; - (void)argv; - fprintf(stderr, "zls: Zen Language Server starting...\n"); - - while (1) - { - // Read headers - int content_len = 0; - char line[512]; - while (fgets(line, sizeof(line), stdin)) - { - if (0 == strcmp(line, "\r\n")) - { - break; // End of headers - } - if (0 == strncmp(line, "Content-Length: ", 16)) - { - content_len = atoi(line + 16); - } - } - - if (content_len <= 0) - { - // Maybe EOF or error? - if (feof(stdin)) - { - break; - } - continue; // Wait for more (yeah we gotta work on this). - } - - // Read body. - char *body = malloc(content_len + 1); - if (fread(body, 1, content_len, stdin) != (size_t)content_len) - { - fprintf(stderr, "zls: Error reading body\n"); - free(body); - break; - } - body[content_len] = 0; - - // Process JSON-RPC. - fprintf(stderr, "zls: Received: %s\n", body); - handle_request(body); - - free(body); +int lsp_main(int argc, char **argv) { + (void)argc; + (void)argv; + fprintf(stderr, "zls: Zen Language Server starting...\n"); + + while (1) { + // Read headers + int content_len = 0; + char line[512]; + while (fgets(line, sizeof(line), stdin)) { + if (0 == strcmp(line, "\r\n")) { + break; // End of headers + } + if (0 == strncmp(line, "Content-Length: ", 16)) { + content_len = atoi(line + 16); + } } - return 0; + if (content_len <= 0) { + // Maybe EOF or error? + if (feof(stdin)) { + break; + } + continue; // Wait for more (yeah we gotta work on this). + } + + // Read body. + char *body = malloc(content_len + 1); + if (fread(body, 1, content_len, stdin) != (size_t)content_len) { + fprintf(stderr, "zls: Error reading body\n"); + free(body); + break; + } + body[content_len] = 0; + + // Process JSON-RPC. + fprintf(stderr, "zls: Received: %s\n", body); + handle_request(body); + + free(body); + } + + return 0; } @@ -12,334 +12,258 @@ // Forward decl for LSP int lsp_main(int argc, char **argv); -void print_search_paths() -{ - printf("Search paths:\n"); - printf(" ./\n"); - printf(" ./std/\n"); - printf(" /usr/local/share/zenc\n"); - printf(" /usr/share/zenc\n"); +void print_search_paths() { + printf("Search paths:\n"); + printf(" ./\n"); + printf(" ./std/\n"); + printf(" /usr/local/share/zenc\n"); + printf(" /usr/share/zenc\n"); } -void print_version() -{ - printf("Zen C version %s\n", ZEN_VERSION); +void print_version() { printf("Zen C version %s\n", ZEN_VERSION); } + +void print_usage() { + printf("Usage: zc [command] [options] <file.zc>\n"); + printf("Commands:\n"); + printf(" run Compile and run the program\n"); + printf(" build Compile to executable\n"); + printf(" check Check for errors only\n"); + printf(" repl Start Interactive REPL\n"); + printf(" transpile Transpile to C code only (no compilation)\n"); + printf(" lsp Start Language Server\n"); + printf("Options:\n"); + printf(" --version Print version information"); + printf(" -o <file> Output executable name\n"); + printf(" --emit-c Keep generated C file (out.c)\n"); + printf(" --freestanding Freestanding mode (no stdlib)\n"); + printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n"); + printf(" -O<level> Optimization level\n"); + printf(" -g Debug info\n"); + printf(" -v, --verbose Verbose output\n"); + printf(" -q, --quiet Quiet output\n"); + printf(" -c Compile only (produce .o)\n"); } -void print_usage() -{ - printf("Usage: zc [command] [options] <file.zc>\n"); - printf("Commands:\n"); - printf(" run Compile and run the program\n"); - printf(" build Compile to executable\n"); - printf(" check Check for errors only\n"); - printf(" repl Start Interactive REPL\n"); - printf(" transpile Transpile to C code only (no compilation)\n"); - printf(" lsp Start Language Server\n"); - printf("Options:\n"); - printf(" --version Print version information"); - printf(" -o <file> Output executable name\n"); - printf(" --emit-c Keep generated C file (out.c)\n"); - printf(" --freestanding Freestanding mode (no stdlib)\n"); - printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n"); - printf(" -O<level> Optimization level\n"); - printf(" -g Debug info\n"); - printf(" -v, --verbose Verbose output\n"); - printf(" -q, --quiet Quiet output\n"); - printf(" -c Compile only (produce .o)\n"); -} - -int main(int argc, char **argv) -{ - // Defaults - memset(&g_config, 0, sizeof(g_config)); - strcpy(g_config.cc, "gcc"); +int main(int argc, char **argv) { + // Defaults + memset(&g_config, 0, sizeof(g_config)); + strcpy(g_config.cc, "gcc"); - if (argc < 2) - { - print_usage(); - return 1; - } + if (argc < 2) { + print_usage(); + return 1; + } - // Parse command - char *command = argv[1]; - int arg_start = 2; + // Parse command + char *command = argv[1]; + int arg_start = 2; - if (strcmp(command, "lsp") == 0) - { - return lsp_main(argc, argv); - } - else if (strcmp(command, "repl") == 0) - { - run_repl(argv[0]); // Pass self path for recursive calls - return 0; - } - else if (strcmp(command, "transpile") == 0) - { - g_config.mode_transpile = 1; - g_config.emit_c = 1; // Transpile implies emitting C - } - else if (strcmp(command, "run") == 0) - { - g_config.mode_run = 1; - } - else if (strcmp(command, "check") == 0) - { - g_config.mode_check = 1; - } - else if (strcmp(command, "build") == 0) - { - // default mode - } - else if (command[0] == '-') - { - // implicit build or run? assume build if starts with flag, but usually - // command first If file provided directly: "zc file.zc" -> build - if (strchr(command, '.')) - { - // treat as filename - g_config.input_file = command; - arg_start = 2; // already consumed - } - else - { - // Flags - arg_start = 1; - } + if (strcmp(command, "lsp") == 0) { + return lsp_main(argc, argv); + } else if (strcmp(command, "repl") == 0) { + run_repl(argv[0]); // Pass self path for recursive calls + return 0; + } else if (strcmp(command, "transpile") == 0) { + g_config.mode_transpile = 1; + g_config.emit_c = 1; // Transpile implies emitting C + } else if (strcmp(command, "run") == 0) { + g_config.mode_run = 1; + } else if (strcmp(command, "check") == 0) { + g_config.mode_check = 1; + } else if (strcmp(command, "build") == 0) { + // default mode + } else if (command[0] == '-') { + // implicit build or run? assume build if starts with flag, but usually + // command first If file provided directly: "zc file.zc" -> build + if (strchr(command, '.')) { + // treat as filename + g_config.input_file = command; + arg_start = 2; // already consumed + } else { + // Flags + arg_start = 1; } - else - { - // Check if file - if (strchr(command, '.')) - { - g_config.input_file = command; - arg_start = 2; - } + } else { + // Check if file + if (strchr(command, '.')) { + g_config.input_file = command; + arg_start = 2; } - - // Parse args - for (int i = arg_start; i < argc; i++) - { - char *arg = argv[i]; - if (strcmp(arg, "--emit-c") == 0) - { - g_config.emit_c = 1; - } - else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) - { - print_version(); - return 0; + } + + // Parse args + for (int i = arg_start; i < argc; i++) { + char *arg = argv[i]; + if (strcmp(arg, "--emit-c") == 0) { + g_config.emit_c = 1; + } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) { + print_version(); + return 0; + } else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) { + g_config.verbose = 1; + } else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) { + g_config.quiet = 1; + } else if (strcmp(arg, "--freestanding") == 0) { + g_config.is_freestanding = 1; + } else if (strcmp(arg, "--check") == 0) { + g_config.mode_check = 1; + } else if (strcmp(arg, "--cc") == 0) { + if (i + 1 < argc) { + char *cc_arg = argv[++i]; + // Handle "zig" shorthand for "zig cc" + if (strcmp(cc_arg, "zig") == 0) { + strcpy(g_config.cc, "zig cc"); + } else { + strcpy(g_config.cc, cc_arg); } - else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) - { - g_config.verbose = 1; - } - else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) - { - g_config.quiet = 1; - } - else if (strcmp(arg, "--freestanding") == 0) - { - g_config.is_freestanding = 1; - } - else if (strcmp(arg, "--check") == 0) - { - g_config.mode_check = 1; - } - else if (strcmp(arg, "--cc") == 0) - { - if (i + 1 < argc) - { - char *cc_arg = argv[++i]; - // Handle "zig" shorthand for "zig cc" - if (strcmp(cc_arg, "zig") == 0) - { - strcpy(g_config.cc, "zig cc"); - } - else - { - strcpy(g_config.cc, cc_arg); - } - } - } - else if (strcmp(arg, "-o") == 0) - { - if (i + 1 < argc) - { - g_config.output_file = argv[++i]; - } - } - else if (strncmp(arg, "-O", 2) == 0) - { - // Add to CFLAGS - strcat(g_config.gcc_flags, " "); - strcat(g_config.gcc_flags, arg); - } - else if (strcmp(arg, "-g") == 0) - { - strcat(g_config.gcc_flags, " -g"); - } - else if (arg[0] == '-') - { - // Unknown flag or C flag - strcat(g_config.gcc_flags, " "); - strcat(g_config.gcc_flags, arg); - } - else - { - if (!g_config.input_file) - { - g_config.input_file = arg; - } - else - { - printf("Multiple input files not supported yet.\n"); - return 1; - } - } - } - - if (!g_config.input_file) - { - printf("Error: No input file specified.\n"); - return 1; - } - - g_current_filename = g_config.input_file; - - // Load file - char *src = load_file(g_config.input_file); - if (!src) - { - printf("Error: Could not read file %s\n", g_config.input_file); + } + } else if (strcmp(arg, "-o") == 0) { + if (i + 1 < argc) { + g_config.output_file = argv[++i]; + } + } else if (strncmp(arg, "-O", 2) == 0) { + // Add to CFLAGS + strcat(g_config.gcc_flags, " "); + strcat(g_config.gcc_flags, arg); + } else if (strcmp(arg, "-g") == 0) { + strcat(g_config.gcc_flags, " -g"); + } else if (arg[0] == '-') { + // Unknown flag or C flag + strcat(g_config.gcc_flags, " "); + strcat(g_config.gcc_flags, arg); + } else { + if (!g_config.input_file) { + g_config.input_file = arg; + } else { + printf("Multiple input files not supported yet.\n"); return 1; + } } - - init_builtins(); - zen_init(); - - // Initialize Plugin Manager - zptr_plugin_mgr_init(); - - // Parse context init - ParserContext ctx; - memset(&ctx, 0, sizeof(ctx)); - - // Scan for build directives (e.g. //> link: -lm) - scan_build_directives(&ctx, src); - - Lexer l; - lexer_init(&l, src); - - ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting - if (!ctx.hoist_out) - { - perror("tmpfile for hoisting"); - return 1; - } - g_parser_ctx = &ctx; - - if (!g_config.quiet) - { - printf("[zc] Compiling %s...\n", g_config.input_file); - } - - ASTNode *root = parse_program(&ctx, &l); - if (!root) - { - // Parse failed - return 1; - } - - if (g_config.mode_check) - { - // Just verify - printf("Check passed.\n"); - return 0; - } - - // Codegen to C - FILE *out = fopen("out.c", "w"); - if (!out) - { - perror("fopen out.c"); + } + + if (!g_config.input_file) { + printf("Error: No input file specified.\n"); + return 1; + } + + g_current_filename = g_config.input_file; + + // Load file + char *src = load_file(g_config.input_file); + if (!src) { + printf("Error: Could not read file %s\n", g_config.input_file); + return 1; + } + + init_builtins(); + zen_init(); + + // Initialize Plugin Manager + zptr_plugin_mgr_init(); + + // Parse context init + ParserContext ctx; + memset(&ctx, 0, sizeof(ctx)); + + // Scan for build directives (e.g. //> link: -lm) + scan_build_directives(&ctx, src); + + Lexer l; + lexer_init(&l, src); + + ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting + if (!ctx.hoist_out) { + perror("tmpfile for hoisting"); + return 1; + } + g_parser_ctx = &ctx; + + if (!g_config.quiet) { + printf("[zc] Compiling %s...\n", g_config.input_file); + } + + ASTNode *root = parse_program(&ctx, &l); + if (!root) { + // Parse failed + return 1; + } + + if (g_config.mode_check) { + // Just verify + printf("Check passed.\n"); + return 0; + } + + // Codegen to C + FILE *out = fopen("out.c", "w"); + if (!out) { + perror("fopen out.c"); + return 1; + } + + codegen_node(&ctx, root, out); + fclose(out); + + if (g_config.mode_transpile) { + if (g_config.output_file) { + // If user specified -o, rename out.c to that + if (rename("out.c", g_config.output_file) != 0) { + perror("rename out.c"); return 1; + } + if (!g_config.quiet) { + printf("[zc] Transpiled to %s\n", g_config.output_file); + } + } else { + if (!g_config.quiet) { + printf("[zc] Transpiled to out.c\n"); + } } + // Done, no C compilation + return 0; + } - codegen_node(&ctx, root, out); - fclose(out); - - if (g_config.mode_transpile) - { - if (g_config.output_file) - { - // If user specified -o, rename out.c to that - if (rename("out.c", g_config.output_file) != 0) - { - perror("rename out.c"); - return 1; - } - if (!g_config.quiet) - { - printf("[zc] Transpiled to %s\n", g_config.output_file); - } - } - else - { - if (!g_config.quiet) - { - printf("[zc] Transpiled to out.c\n"); - } - } - // Done, no C compilation - return 0; - } - - // Compile C - char cmd[8192]; - char *outfile = g_config.output_file ? g_config.output_file : "a.out"; - - // TCC-specific adjustments? - // Already handled by user passing --cc tcc - - snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", g_config.cc, - g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "", - outfile, g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); + // Compile C + char cmd[8192]; + char *outfile = g_config.output_file ? g_config.output_file : "a.out"; - if (g_config.verbose) - { - printf("[CMD] %s\n", cmd); - } + // TCC-specific adjustments? + // Already handled by user passing --cc tcc - int ret = system(cmd); - if (ret != 0) - { - printf("C compilation failed.\n"); - if (!g_config.emit_c) - { - remove("out.c"); - } - return 1; - } + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", + g_config.cc, g_config.gcc_flags, g_cflags, + g_config.is_freestanding ? "-ffreestanding" : "", "", outfile, + g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); - if (!g_config.emit_c) - { - // remove("out.c"); // Keep it for debugging for now or follow flag - remove("out.c"); - } + if (g_config.verbose) { + printf("[CMD] %s\n", cmd); + } - if (g_config.mode_run) - { - char run_cmd[2048]; - sprintf(run_cmd, "./%s", outfile); - ret = system(run_cmd); - remove(outfile); - zptr_plugin_mgr_cleanup(); - zen_trigger_global(); - return ret; + int ret = system(cmd); + if (ret != 0) { + printf("C compilation failed.\n"); + if (!g_config.emit_c) { + remove("out.c"); } - + return 1; + } + + if (!g_config.emit_c) { + // remove("out.c"); // Keep it for debugging for now or follow flag + remove("out.c"); + } + + if (g_config.mode_run) { + char run_cmd[2048]; + sprintf(run_cmd, "./%s", outfile); + ret = system(run_cmd); + remove(outfile); zptr_plugin_mgr_cleanup(); zen_trigger_global(); - return 0; + return ret; + } + + zptr_plugin_mgr_cleanup(); + zen_trigger_global(); + return 0; } diff --git a/src/parser/parser.h b/src/parser/parser.h index 4d105cf..815164c 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -6,20 +6,19 @@ #include "zprep.h" // Operator precedence for expression parsing -typedef enum -{ - PREC_NONE, - PREC_ASSIGNMENT, - PREC_TERNARY, - PREC_OR, - PREC_AND, - PREC_EQUALITY, - PREC_COMPARISON, - PREC_TERM, - PREC_FACTOR, - PREC_UNARY, - PREC_CALL, - PREC_PRIMARY +typedef enum { + PREC_NONE, + PREC_ASSIGNMENT, + PREC_TERNARY, + PREC_OR, + PREC_AND, + PREC_EQUALITY, + PREC_COMPARISON, + PREC_TERM, + PREC_FACTOR, + PREC_UNARY, + PREC_CALL, + PREC_PRIMARY } Precedence; // Main entry points @@ -32,240 +31,219 @@ ASTNode *parse_program(ParserContext *ctx, Lexer *l); extern ParserContext *g_parser_ctx; // Symbol table -typedef struct Symbol -{ - char *name; - char *type_name; - Type *type_info; - int is_mutable; - int is_used; - int is_autofree; - Token decl_token; - int is_const_value; - int const_int_val; - struct Symbol *next; +typedef struct Symbol { + char *name; + char *type_name; + Type *type_info; + int is_mutable; + int is_used; + int is_autofree; + Token decl_token; + int is_const_value; + int const_int_val; + struct Symbol *next; } Symbol; -typedef struct Scope -{ - Symbol *symbols; - struct Scope *parent; +typedef struct Scope { + Symbol *symbols; + struct Scope *parent; } Scope; // Function registry -typedef struct FuncSig -{ - char *name; - Token decl_token; // For LSP - int total_args; - char **defaults; - Type **arg_types; - Type *ret_type; - int is_varargs; - int is_async; // Async function flag - int must_use; // Attribute: warn if return value discarded - struct FuncSig *next; +typedef struct FuncSig { + char *name; + Token decl_token; // For LSP + int total_args; + char **defaults; + Type **arg_types; + Type *ret_type; + int is_varargs; + int is_async; // Async function flag + int must_use; // Attribute: warn if return value discarded + struct FuncSig *next; } FuncSig; // Lambda tracking -typedef struct LambdaRef -{ - ASTNode *node; - struct LambdaRef *next; +typedef struct LambdaRef { + ASTNode *node; + struct LambdaRef *next; } LambdaRef; -typedef struct GenericTemplate -{ - char *name; - ASTNode *struct_node; - struct GenericTemplate *next; +typedef struct GenericTemplate { + char *name; + ASTNode *struct_node; + struct GenericTemplate *next; } GenericTemplate; -typedef struct GenericFuncTemplate -{ - char *name; - char *generic_param; - ASTNode *func_node; - struct GenericFuncTemplate *next; +typedef struct GenericFuncTemplate { + char *name; + char *generic_param; + ASTNode *func_node; + struct GenericFuncTemplate *next; } GenericFuncTemplate; -typedef struct GenericImplTemplate -{ - char *struct_name; - char *generic_param; - ASTNode *impl_node; - struct GenericImplTemplate *next; +typedef struct GenericImplTemplate { + char *struct_name; + char *generic_param; + ASTNode *impl_node; + struct GenericImplTemplate *next; } GenericImplTemplate; -typedef struct ImportedFile -{ - char *path; - struct ImportedFile *next; +typedef struct ImportedFile { + char *path; + struct ImportedFile *next; } ImportedFile; -typedef struct VarMutability -{ - char *name; - int is_mutable; - struct VarMutability *next; +typedef struct VarMutability { + char *name; + int is_mutable; + struct VarMutability *next; } VarMutability; // Instantiation tracking -typedef struct Instantiation -{ - char *name; - char *template_name; - char *concrete_arg; - ASTNode *struct_node; - struct Instantiation *next; +typedef struct Instantiation { + char *name; + char *template_name; + char *concrete_arg; + ASTNode *struct_node; + struct Instantiation *next; } Instantiation; -typedef struct StructRef -{ - ASTNode *node; - struct StructRef *next; +typedef struct StructRef { + ASTNode *node; + struct StructRef *next; } StructRef; -typedef struct StructDef -{ - char *name; - ASTNode *node; - struct StructDef *next; +typedef struct StructDef { + char *name; + ASTNode *node; + struct StructDef *next; } StructDef; // Type tracking -typedef struct SliceType -{ - char *name; - struct SliceType *next; +typedef struct SliceType { + char *name; + struct SliceType *next; } SliceType; -typedef struct TupleType -{ - char *sig; - struct TupleType *next; +typedef struct TupleType { + char *sig; + struct TupleType *next; } TupleType; // Enum tracking -typedef struct EnumVariantReg -{ - char *enum_name; - char *variant_name; - int tag_id; - struct EnumVariantReg *next; +typedef struct EnumVariantReg { + char *enum_name; + char *variant_name; + int tag_id; + struct EnumVariantReg *next; } EnumVariantReg; // Deprecated function tracking -typedef struct DeprecatedFunc -{ - char *name; - char *reason; // Optional reason message - struct DeprecatedFunc *next; +typedef struct DeprecatedFunc { + char *name; + char *reason; // Optional reason message + struct DeprecatedFunc *next; } DeprecatedFunc; // Module system -typedef struct Module -{ - char *alias; - char *path; - char *base_name; - int is_c_header; - struct Module *next; +typedef struct Module { + char *alias; + char *path; + char *base_name; + int is_c_header; + struct Module *next; } Module; -typedef struct SelectiveImport -{ - char *symbol; - char *alias; - char *source_module; - struct SelectiveImport *next; +typedef struct SelectiveImport { + char *symbol; + char *alias; + char *source_module; + struct SelectiveImport *next; } SelectiveImport; // Impl cache -typedef struct ImplReg -{ - char *trait; - char *strct; - struct ImplReg *next; +typedef struct ImplReg { + char *trait; + char *strct; + struct ImplReg *next; } ImplReg; // Plugin tracking -typedef struct ImportedPlugin -{ - char *name; // Original plugin name (for example, "brainfuck") - char *alias; // Optional alias (for example, "bf"), NULL if no alias - struct ImportedPlugin *next; +typedef struct ImportedPlugin { + char *name; // Original plugin name (for example, "brainfuck") + char *alias; // Optional alias (for example, "bf"), NULL if no alias + struct ImportedPlugin *next; } ImportedPlugin; -struct ParserContext -{ - Scope *current_scope; - FuncSig *func_registry; +struct ParserContext { + Scope *current_scope; + FuncSig *func_registry; - // Lambdas - LambdaRef *global_lambdas; - int lambda_counter; + // Lambdas + LambdaRef *global_lambdas; + int lambda_counter; // Generics #define MAX_KNOWN_GENERICS 1024 - char *known_generics[MAX_KNOWN_GENERICS]; - int known_generics_count; - GenericTemplate *templates; - GenericFuncTemplate *func_templates; - GenericImplTemplate *impl_templates; - - // Instantiations - Instantiation *instantiations; - ASTNode *instantiated_structs; - ASTNode *instantiated_funcs; - - // Structs/Enums - StructRef *parsed_structs_list; - StructRef *parsed_enums_list; - StructRef *parsed_funcs_list; - StructRef *parsed_impls_list; - StructRef *parsed_globals_list; - StructDef *struct_defs; - EnumVariantReg *enum_variants; - ImplReg *registered_impls; - - // Types - SliceType *used_slices; - TupleType *used_tuples; - - // Modules/Imports - Module *modules; - SelectiveImport *selective_imports; - char *current_module_prefix; - ImportedFile *imported_files; - ImportedPlugin *imported_plugins; // Plugin imports - - // Config/State - int immutable_by_default; - char *current_impl_struct; - - // Internal tracking - VarMutability *var_mutability_table; - DeprecatedFunc *deprecated_funcs; - - // LSP / Fault Tolerance - int is_fault_tolerant; - void *error_callback_data; - void (*on_error)(void *data, Token t, const char *msg); - - // LSP: Flat symbol list (persists after parsing for LSP queries) - Symbol *all_symbols; - - // External C interop: suppress undefined warnings for external symbols - int has_external_includes; // Set when include <...> is used - char **extern_symbols; // Explicitly declared extern symbols - int extern_symbol_count; - - // Codegen state: - FILE *hoist_out; // For plugins to hoist code to file scope - int skip_preamble; // If 1, codegen_node(NODE_ROOT) won't emit preamble - int is_repl; // REPL mode flag - int has_async; // Track if async features are used + char *known_generics[MAX_KNOWN_GENERICS]; + int known_generics_count; + GenericTemplate *templates; + GenericFuncTemplate *func_templates; + GenericImplTemplate *impl_templates; + + // Instantiations + Instantiation *instantiations; + ASTNode *instantiated_structs; + ASTNode *instantiated_funcs; + + // Structs/Enums + StructRef *parsed_structs_list; + StructRef *parsed_enums_list; + StructRef *parsed_funcs_list; + StructRef *parsed_impls_list; + StructRef *parsed_globals_list; + StructDef *struct_defs; + EnumVariantReg *enum_variants; + ImplReg *registered_impls; + + // Types + SliceType *used_slices; + TupleType *used_tuples; + + // Modules/Imports + Module *modules; + SelectiveImport *selective_imports; + char *current_module_prefix; + ImportedFile *imported_files; + ImportedPlugin *imported_plugins; // Plugin imports + + // Config/State + int immutable_by_default; + char *current_impl_struct; + + // Internal tracking + VarMutability *var_mutability_table; + DeprecatedFunc *deprecated_funcs; + + // LSP / Fault Tolerance + int is_fault_tolerant; + void *error_callback_data; + void (*on_error)(void *data, Token t, const char *msg); + + // LSP: Flat symbol list (persists after parsing for LSP queries) + Symbol *all_symbols; + + // External C interop: suppress undefined warnings for external symbols + int has_external_includes; // Set when include <...> is used + char **extern_symbols; // Explicitly declared extern symbols + int extern_symbol_count; + + // Codegen state: + FILE *hoist_out; // For plugins to hoist code to file scope + int skip_preamble; // If 1, codegen_node(NODE_ROOT) won't emit preamble + int is_repl; // REPL mode flag + int has_async; // Track if async features are used }; // Token helpers @@ -283,9 +261,10 @@ void warn_c_reserved_word(Token t, const char *name); // Symbol table void enter_scope(ParserContext *ctx); void exit_scope(ParserContext *ctx); -void add_symbol(ParserContext *ctx, const char *n, const char *t, Type *type_info); -void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, Type *type_info, - Token tok); +void add_symbol(ParserContext *ctx, const char *n, const char *t, + Type *type_info); +void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, + Type *type_info, Token tok); Type *find_symbol_type_info(ParserContext *ctx, const char *n); char *find_symbol_type(ParserContext *ctx, const char *n); Symbol *find_symbol_entry(ParserContext *ctx, const char *n); @@ -294,17 +273,18 @@ Symbol *find_symbol_in_all(ParserContext *ctx, char *find_similar_symbol(ParserContext *ctx, const char *name); // Function registry -void register_func(ParserContext *ctx, const char *name, int count, char **defaults, - Type **arg_types, Type *ret_type, int is_varargs, int is_async, - Token decl_token); -void register_func_template(ParserContext *ctx, const char *name, const char *param, ASTNode *node); +void register_func(ParserContext *ctx, const char *name, int count, + char **defaults, Type **arg_types, Type *ret_type, + int is_varargs, int is_async, Token decl_token); +void register_func_template(ParserContext *ctx, const char *name, + const char *param, ASTNode *node); GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name); // Generic/template helpers void register_generic(ParserContext *ctx, char *name); int is_known_generic(ParserContext *ctx, char *name); -void register_impl_template(ParserContext *ctx, const char *sname, const char *param, - ASTNode *node); +void register_impl_template(ParserContext *ctx, const char *sname, + const char *param, ASTNode *node); void add_to_struct_list(ParserContext *ctx, ASTNode *node); void add_to_enum_list(ParserContext *ctx, ASTNode *node); void add_to_func_list(ParserContext *ctx, ASTNode *node); @@ -312,19 +292,24 @@ void add_to_impl_list(ParserContext *ctx, ASTNode *node); void add_to_global_list(ParserContext *ctx, ASTNode *node); void register_builtins(ParserContext *ctx); void add_instantiated_func(ParserContext *ctx, ASTNode *fn); -void instantiate_generic(ParserContext *ctx, const char *name, const char *concrete_type, Token t); +void instantiate_generic(ParserContext *ctx, const char *name, + const char *concrete_type, Token t); char *sanitize_mangled_name(const char *s); void register_impl(ParserContext *ctx, const char *trait, const char *strct); int check_impl(ParserContext *ctx, const char *trait, const char *strct); void register_template(ParserContext *ctx, const char *name, ASTNode *node); -void register_deprecated_func(ParserContext *ctx, const char *name, const char *reason); +void register_deprecated_func(ParserContext *ctx, const char *name, + const char *reason); DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name); -ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_name); -ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_names, int num_params); +ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, + char *param_name); +ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, + char **param_names, int num_params); // Utils -char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out); +char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, + int *count_out, Type ***types_out, + char ***names_out, int *is_varargs_out); int is_file_imported(ParserContext *ctx, const char *path); void mark_file_imported(ParserContext *ctx, const char *path); void register_plugin(ParserContext *ctx, const char *name, const char *alias); @@ -335,13 +320,15 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes); char *replace_in_string(const char *src, const char *old_w, const char *new_w); char *replace_type_str(const char *src, const char *param, const char *concrete, const char *old_struct, const char *new_struct); -Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, const char *ns); -ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os, - const char *ns); +Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, + const char *ns); +ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, + const char *os, const char *ns); char *extract_module_name(const char *path); // Enum helpers -void register_enum_variant(ParserContext *ctx, const char *ename, const char *vname, int tag); +void register_enum_variant(ParserContext *ctx, const char *ename, + const char *vname, int tag); EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname); // Lambda helpers @@ -359,12 +346,13 @@ void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node); // Module system Module *find_module(ParserContext *ctx, const char *alias); void register_module(ParserContext *ctx, const char *alias, const char *path); -void register_selective_import(ParserContext *ctx, const char *symbol, const char *alias, - const char *source_module); +void register_selective_import(ParserContext *ctx, const char *symbol, + const char *alias, const char *source_module); SelectiveImport *find_selective_import(ParserContext *ctx, const char *name); // Mutability tracking -void register_var_mutability(ParserContext *ctx, const char *name, int is_mutable); +void register_var_mutability(ParserContext *ctx, const char *name, + int is_mutable); int is_var_mutable(ParserContext *ctx, const char *name); // External symbol tracking (C interop) @@ -377,7 +365,8 @@ void init_builtins(); // Expression rewriting char *rewrite_expr_methods(ParserContext *ctx, char *raw); -char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count); +char *process_fstring(ParserContext *ctx, const char *content, + char ***used_syms, int *count); char *instantiate_function_template(ParserContext *ctx, const char *name, const char *concrete_type); FuncSig *find_func(ParserContext *ctx, const char *name); @@ -409,8 +398,8 @@ ASTNode *parse_guard(ParserContext *ctx, Lexer *l); ASTNode *parse_match(ParserContext *ctx, Lexer *l); ASTNode *parse_return(ParserContext *ctx, Lexer *l); -char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target, - char ***used_syms, int *count); +char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, + const char *target, char ***used_syms, int *count); ASTNode *parse_assert(ParserContext *ctx, Lexer *l); ASTNode *parse_defer(ParserContext *ctx, Lexer *l); ASTNode *parse_asm(ParserContext *ctx, Lexer *l); diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c index 7d5bc32..777fc46 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -5,614 +5,453 @@ #include <stdlib.h> #include <string.h> -static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char **traits, int count); +static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, + char **traits, int count); // Main parsing entry point -ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l) -{ - ASTNode *h = 0, *tl = 0; - while (1) - { - skip_comments(l); - Token t = lexer_peek(l); - - if (t.type == TOK_EOF) - { - break; - } +ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l) { + ASTNode *h = 0, *tl = 0; + while (1) { + skip_comments(l); + Token t = lexer_peek(l); + + if (t.type == TOK_EOF) { + break; + } - if (t.type == TOK_COMPTIME) - { - ASTNode *gen = parse_comptime(ctx, l); - if (gen) - { - if (!h) - { - h = gen; - } - else - { - tl->next = gen; - } - if (!tl) - { - tl = gen; - } - while (tl->next) - { - tl = tl->next; - } - } - continue; + if (t.type == TOK_COMPTIME) { + ASTNode *gen = parse_comptime(ctx, l); + if (gen) { + if (!h) { + h = gen; + } else { + tl->next = gen; + } + if (!tl) { + tl = gen; + } + while (tl->next) { + tl = tl->next; } + } + continue; + } - ASTNode *s = 0; - - int attr_must_use = 0; - int attr_deprecated = 0; - int attr_inline = 0; - int attr_pure = 0; - int attr_noreturn = 0; - int attr_cold = 0; - int attr_hot = 0; - int attr_packed = 0; - int attr_align = 0; - int attr_noinline = 0; - int attr_constructor = 0; - int attr_destructor = 0; - int attr_unused = 0; - int attr_weak = 0; - int attr_export = 0; - int attr_comptime = 0; - char *deprecated_msg = NULL; - char *attr_section = NULL; - - char *derived_traits[32]; - int derived_count = 0; - - while (t.type == TOK_AT) - { - lexer_next(l); - Token attr = lexer_next(l); - if (attr.type != TOK_IDENT && attr.type != TOK_COMPTIME) - { - zpanic_at(attr, "Expected attribute name after @"); - } + ASTNode *s = 0; + + int attr_must_use = 0; + int attr_deprecated = 0; + int attr_inline = 0; + int attr_pure = 0; + int attr_noreturn = 0; + int attr_cold = 0; + int attr_hot = 0; + int attr_packed = 0; + int attr_align = 0; + int attr_noinline = 0; + int attr_constructor = 0; + int attr_destructor = 0; + int attr_unused = 0; + int attr_weak = 0; + int attr_export = 0; + int attr_comptime = 0; + char *deprecated_msg = NULL; + char *attr_section = NULL; + + char *derived_traits[32]; + int derived_count = 0; + + while (t.type == TOK_AT) { + lexer_next(l); + Token attr = lexer_next(l); + if (attr.type != TOK_IDENT && attr.type != TOK_COMPTIME) { + zpanic_at(attr, "Expected attribute name after @"); + } + + if (0 == strncmp(attr.start, "must_use", 8) && 8 == attr.len) { + attr_must_use = 1; + } else if (0 == strncmp(attr.start, "deprecated", 10) && 10 == attr.len) { + attr_deprecated = 1; + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); + Token msg = lexer_next(l); + if (msg.type == TOK_STRING) { + deprecated_msg = xmalloc(msg.len - 1); + strncpy(deprecated_msg, msg.start + 1, msg.len - 2); + deprecated_msg[msg.len - 2] = 0; + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after deprecated message"); + } + } + } else if (0 == strncmp(attr.start, "inline", 6) && 6 == attr.len) { + attr_inline = 1; + } else if (0 == strncmp(attr.start, "noinline", 8) && 8 == attr.len) { + attr_noinline = 1; + } else if (0 == strncmp(attr.start, "pure", 4) && 4 == attr.len) { + attr_pure = 1; + } else if (0 == strncmp(attr.start, "noreturn", 8) && 8 == attr.len) { + attr_noreturn = 1; + } else if (0 == strncmp(attr.start, "cold", 4) && 4 == attr.len) { + attr_cold = 1; + } else if (0 == strncmp(attr.start, "hot", 3) && 3 == attr.len) { + attr_hot = 1; + } else if (0 == strncmp(attr.start, "constructor", 11) && + 11 == attr.len) { + attr_constructor = 1; + } else if (0 == strncmp(attr.start, "destructor", 10) && 10 == attr.len) { + attr_destructor = 1; + } else if (0 == strncmp(attr.start, "unused", 6) && 6 == attr.len) { + attr_unused = 1; + } else if (0 == strncmp(attr.start, "weak", 4) && 4 == attr.len) { + attr_weak = 1; + } else if (0 == strncmp(attr.start, "export", 6) && 6 == attr.len) { + attr_export = 1; + } else if (0 == strncmp(attr.start, "comptime", 8) && 8 == attr.len) { + attr_comptime = 1; + } else if (0 == strncmp(attr.start, "section", 7) && 7 == attr.len) { + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); + Token sec = lexer_next(l); + if (sec.type == TOK_STRING) { + attr_section = xmalloc(sec.len - 1); + strncpy(attr_section, sec.start + 1, sec.len - 2); + attr_section[sec.len - 2] = 0; + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after section name"); + } + } else { + zpanic_at(lexer_peek(l), + "@section requires a name: @section(\"name\")"); + } + } else if (0 == strncmp(attr.start, "packed", 6) && 6 == attr.len) { + attr_packed = 1; + } else if (0 == strncmp(attr.start, "align", 5) && 5 == attr.len) { + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); + Token num = lexer_next(l); + if (num.type == TOK_INT) { + attr_align = atoi(num.start); + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after align value"); + } + } else { + zpanic_at(lexer_peek(l), "@align requires a value: @align(N)"); + } + } else if (0 == strncmp(attr.start, "derive", 6) && 6 == attr.len) { + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); + while (1) { + Token t = lexer_next(l); + if (t.type != TOK_IDENT) { + zpanic_at(t, "Expected trait name in @derive"); + } + if (derived_count < 32) { + derived_traits[derived_count++] = token_strdup(t); + } + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after derive traits"); + } + } else { + zpanic_at(lexer_peek(l), + "@derive requires traits: @derive(Debug, Clone)"); + } + } else { + zwarn_at(attr, "Unknown attribute: %.*s", attr.len, attr.start); + } - if (0 == strncmp(attr.start, "must_use", 8) && 8 == attr.len) - { - attr_must_use = 1; - } - else if (0 == strncmp(attr.start, "deprecated", 10) && 10 == attr.len) - { - attr_deprecated = 1; - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - Token msg = lexer_next(l); - if (msg.type == TOK_STRING) - { - deprecated_msg = xmalloc(msg.len - 1); - strncpy(deprecated_msg, msg.start + 1, msg.len - 2); - deprecated_msg[msg.len - 2] = 0; - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after deprecated message"); - } - } - } - else if (0 == strncmp(attr.start, "inline", 6) && 6 == attr.len) - { - attr_inline = 1; - } - else if (0 == strncmp(attr.start, "noinline", 8) && 8 == attr.len) - { - attr_noinline = 1; - } - else if (0 == strncmp(attr.start, "pure", 4) && 4 == attr.len) - { - attr_pure = 1; - } - else if (0 == strncmp(attr.start, "noreturn", 8) && 8 == attr.len) - { - attr_noreturn = 1; - } - else if (0 == strncmp(attr.start, "cold", 4) && 4 == attr.len) - { - attr_cold = 1; - } - else if (0 == strncmp(attr.start, "hot", 3) && 3 == attr.len) - { - attr_hot = 1; - } - else if (0 == strncmp(attr.start, "constructor", 11) && 11 == attr.len) - { - attr_constructor = 1; - } - else if (0 == strncmp(attr.start, "destructor", 10) && 10 == attr.len) - { - attr_destructor = 1; - } - else if (0 == strncmp(attr.start, "unused", 6) && 6 == attr.len) - { - attr_unused = 1; - } - else if (0 == strncmp(attr.start, "weak", 4) && 4 == attr.len) - { - attr_weak = 1; - } - else if (0 == strncmp(attr.start, "export", 6) && 6 == attr.len) - { - attr_export = 1; - } - else if (0 == strncmp(attr.start, "comptime", 8) && 8 == attr.len) - { - attr_comptime = 1; - } - else if (0 == strncmp(attr.start, "section", 7) && 7 == attr.len) - { - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - Token sec = lexer_next(l); - if (sec.type == TOK_STRING) - { - attr_section = xmalloc(sec.len - 1); - strncpy(attr_section, sec.start + 1, sec.len - 2); - attr_section[sec.len - 2] = 0; - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after section name"); - } - } - else - { - zpanic_at(lexer_peek(l), "@section requires a name: @section(\"name\")"); - } - } - else if (0 == strncmp(attr.start, "packed", 6) && 6 == attr.len) - { - attr_packed = 1; - } - else if (0 == strncmp(attr.start, "align", 5) && 5 == attr.len) - { - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - Token num = lexer_next(l); - if (num.type == TOK_INT) - { - attr_align = atoi(num.start); - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after align value"); - } - } - else - { - zpanic_at(lexer_peek(l), "@align requires a value: @align(N)"); - } - } - else if (0 == strncmp(attr.start, "derive", 6) && 6 == attr.len) - { - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - while (1) - { - Token t = lexer_next(l); - if (t.type != TOK_IDENT) - { - zpanic_at(t, "Expected trait name in @derive"); - } - if (derived_count < 32) - { - derived_traits[derived_count++] = token_strdup(t); - } - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after derive traits"); - } - } - else - { - zpanic_at(lexer_peek(l), "@derive requires traits: @derive(Debug, Clone)"); - } - } - else - { - zwarn_at(attr, "Unknown attribute: %.*s", attr.len, attr.start); - } + t = lexer_peek(l); + } - t = lexer_peek(l); + if (t.type == TOK_PREPROC) { + lexer_next(l); + char *content = xmalloc(t.len + 2); + strncpy(content, t.start, t.len); + content[t.len] = '\n'; + content[t.len + 1] = 0; + s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + } else if (t.type == TOK_IDENT) { + // Inline function: inline fn name(...) { } + if (0 == strncmp(t.start, "inline", 6) && 6 == t.len) { + lexer_next(l); + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && 2 == next.len && + 0 == strncmp(next.start, "fn", 2)) { + s = parse_function(ctx, l, 0); + attr_inline = 1; + } else { + zpanic_at(next, "Expected 'fn' after 'inline'"); } - - if (t.type == TOK_PREPROC) - { - lexer_next(l); - char *content = xmalloc(t.len + 2); - strncpy(content, t.start, t.len); - content[t.len] = '\n'; - content[t.len + 1] = 0; - s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = content; + } else if (0 == strncmp(t.start, "fn", 2) && 2 == t.len) { + s = parse_function(ctx, l, 0); + } else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len) { + s = parse_struct(ctx, l, 0); + if (s && s->type == NODE_STRUCT) { + s->strct.is_packed = attr_packed; + s->strct.align = attr_align; + + if (derived_count > 0) { + ASTNode *impls = + generate_derive_impls(ctx, s, derived_traits, derived_count); + s->next = impls; + } } - else if (t.type == TOK_IDENT) - { - // Inline function: inline fn name(...) { } - if (0 == strncmp(t.start, "inline", 6) && 6 == t.len) - { - lexer_next(l); - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && 2 == next.len && 0 == strncmp(next.start, "fn", 2)) - { - s = parse_function(ctx, l, 0); - attr_inline = 1; - } - else - { - zpanic_at(next, "Expected 'fn' after 'inline'"); - } - } - else if (0 == strncmp(t.start, "fn", 2) && 2 == t.len) - { - s = parse_function(ctx, l, 0); - } - else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len) - { - s = parse_struct(ctx, l, 0); - if (s && s->type == NODE_STRUCT) - { - s->strct.is_packed = attr_packed; - s->strct.align = attr_align; - - if (derived_count > 0) - { - ASTNode *impls = - generate_derive_impls(ctx, s, derived_traits, derived_count); - s->next = impls; - } - } - } - else if (0 == strncmp(t.start, "enum", 4) && 4 == t.len) - { - s = parse_enum(ctx, l); - if (s && s->type == NODE_ENUM) - { - if (derived_count > 0) - { - ASTNode *impls = - generate_derive_impls(ctx, s, derived_traits, derived_count); - s->next = impls; - } - } - } - else if (t.len == 4 && strncmp(t.start, "impl", 4) == 0) - { - s = parse_impl(ctx, l); - } - else if (t.len == 5 && strncmp(t.start, "trait", 5) == 0) - { - s = parse_trait(ctx, l); - } - else if (t.len == 7 && strncmp(t.start, "include", 7) == 0) - { - s = parse_include(ctx, l); - } - else if (t.len == 6 && strncmp(t.start, "import", 6) == 0) - { - s = parse_import(ctx, l); - } - else if (t.len == 3 && strncmp(t.start, "var", 3) == 0) - { - s = parse_var_decl(ctx, l); - } - else if (t.len == 5 && strncmp(t.start, "const", 5) == 0) - { - s = parse_const(ctx, l); - } - else if (t.len == 6 && strncmp(t.start, "extern", 6) == 0) - { - lexer_next(l); - - Token peek = lexer_peek(l); - if (peek.type == TOK_IDENT && peek.len == 2 && strncmp(peek.start, "fn", 2) == 0) - { - s = parse_function(ctx, l, 0); - } - else - { - while (1) - { - Token sym = lexer_next(l); - if (sym.type != TOK_IDENT) - { - break; - } - - char *name = token_strdup(sym); - register_extern_symbol(ctx, name); - - Token next = lexer_peek(l); - if (next.type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - continue; - } - } - else if (0 == strncmp(t.start, "type", 4) && 4 == t.len) - { - s = parse_type_alias(ctx, l); - } - else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len) - { - lexer_next(l); - if (lexer_peek(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected { after raw"); - } - lexer_next(l); - - 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; - - s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = content; - } - else - { - lexer_next(l); - } + } else if (0 == strncmp(t.start, "enum", 4) && 4 == t.len) { + s = parse_enum(ctx, l); + if (s && s->type == NODE_ENUM) { + if (derived_count > 0) { + ASTNode *impls = + generate_derive_impls(ctx, s, derived_traits, derived_count); + s->next = impls; + } } - else if (t.type == TOK_ASYNC) - { - lexer_next(l); + } else if (t.len == 4 && strncmp(t.start, "impl", 4) == 0) { + s = parse_impl(ctx, l); + } else if (t.len == 5 && strncmp(t.start, "trait", 5) == 0) { + s = parse_trait(ctx, l); + } else if (t.len == 7 && strncmp(t.start, "include", 7) == 0) { + s = parse_include(ctx, l); + } else if (t.len == 6 && strncmp(t.start, "import", 6) == 0) { + s = parse_import(ctx, l); + } else if (t.len == 3 && strncmp(t.start, "var", 3) == 0) { + s = parse_var_decl(ctx, l); + } else if (t.len == 5 && strncmp(t.start, "const", 5) == 0) { + s = parse_const(ctx, l); + } else if (t.len == 6 && strncmp(t.start, "extern", 6) == 0) { + lexer_next(l); + + Token peek = lexer_peek(l); + if (peek.type == TOK_IDENT && peek.len == 2 && + strncmp(peek.start, "fn", 2) == 0) { + s = parse_function(ctx, l, 0); + } else { + while (1) { + Token sym = lexer_next(l); + if (sym.type != TOK_IDENT) { + break; + } + + char *name = token_strdup(sym); + register_extern_symbol(ctx, name); + Token next = lexer_peek(l); - if (0 == strncmp(next.start, "fn", 2) && 2 == next.len) - { - s = parse_function(ctx, l, 1); - if (s) - { - s->func.is_async = 1; - } + if (next.type == TOK_COMMA) { + lexer_next(l); + } else { + break; } - else - { - zpanic_at(next, "Expected 'fn' after 'async'"); - } - } + } - else if (t.type == TOK_UNION) - { - s = parse_struct(ctx, l, 1); - } - else if (t.type == TOK_TRAIT) - { - s = parse_trait(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } + continue; } - else if (t.type == TOK_IMPL) - { - s = parse_impl(ctx, l); + } else if (0 == strncmp(t.start, "type", 4) && 4 == t.len) { + s = parse_type_alias(ctx, l); + } else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len) { + lexer_next(l); + if (lexer_peek(l).type != TOK_LBRACE) { + zpanic_at(lexer_peek(l), "Expected { after raw"); } - else if (t.type == TOK_TEST) - { - s = parse_test(ctx, l); + lexer_next(l); + + 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--; + } } - else - { - lexer_next(l); + + 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; + + s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + } else { + lexer_next(l); + } + } else if (t.type == TOK_ASYNC) { + lexer_next(l); + Token next = lexer_peek(l); + if (0 == strncmp(next.start, "fn", 2) && 2 == next.len) { + s = parse_function(ctx, l, 1); + if (s) { + s->func.is_async = 1; } + } else { + zpanic_at(next, "Expected 'fn' after 'async'"); + } + } - if (s && s->type == NODE_FUNCTION) - { - s->func.must_use = attr_must_use; - s->func.is_inline = attr_inline || s->func.is_inline; - s->func.noinline = attr_noinline; - s->func.constructor = attr_constructor; - s->func.destructor = attr_destructor; - s->func.unused = attr_unused; - s->func.weak = attr_weak; - s->func.is_export = attr_export; - s->func.cold = attr_cold; - s->func.hot = attr_hot; - s->func.noreturn = attr_noreturn; - s->func.pure = attr_pure; - s->func.section = attr_section; - s->func.is_comptime = attr_comptime; - - if (attr_deprecated && s->func.name) - { - register_deprecated_func(ctx, s->func.name, deprecated_msg); - } + else if (t.type == TOK_UNION) { + s = parse_struct(ctx, l, 1); + } else if (t.type == TOK_TRAIT) { + s = parse_trait(ctx, l); + } else if (t.type == TOK_IMPL) { + s = parse_impl(ctx, l); + } else if (t.type == TOK_TEST) { + s = parse_test(ctx, l); + } else { + lexer_next(l); + } - if (attr_must_use && s->func.name) - { - FuncSig *sig = find_func(ctx, s->func.name); - if (sig) - { - sig->must_use = 1; - } - } + if (s && s->type == NODE_FUNCTION) { + s->func.must_use = attr_must_use; + s->func.is_inline = attr_inline || s->func.is_inline; + s->func.noinline = attr_noinline; + s->func.constructor = attr_constructor; + s->func.destructor = attr_destructor; + s->func.unused = attr_unused; + s->func.weak = attr_weak; + s->func.is_export = attr_export; + s->func.cold = attr_cold; + s->func.hot = attr_hot; + s->func.noreturn = attr_noreturn; + s->func.pure = attr_pure; + s->func.section = attr_section; + s->func.is_comptime = attr_comptime; + + if (attr_deprecated && s->func.name) { + register_deprecated_func(ctx, s->func.name, deprecated_msg); + } + + if (attr_must_use && s->func.name) { + FuncSig *sig = find_func(ctx, s->func.name); + if (sig) { + sig->must_use = 1; } + } + } - if (s) - { - if (!h) - { - h = s; - } - else - { - tl->next = s; - } - tl = s; - while (tl->next) - { - tl = tl->next; - } - } + if (s) { + if (!h) { + h = s; + } else { + tl->next = s; + } + tl = s; + while (tl->next) { + tl = tl->next; + } } - return h; + } + return h; } -ASTNode *parse_program(ParserContext *ctx, Lexer *l) -{ - g_parser_ctx = ctx; - enter_scope(ctx); - register_builtins(ctx); +ASTNode *parse_program(ParserContext *ctx, Lexer *l) { + g_parser_ctx = ctx; + enter_scope(ctx); + register_builtins(ctx); - ASTNode *r = ast_create(NODE_ROOT); - r->root.children = parse_program_nodes(ctx, l); - return r; + ASTNode *r = ast_create(NODE_ROOT); + r->root.children = parse_program_nodes(ctx, l); + return r; } -static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char **traits, int count) -{ - ASTNode *head = NULL, *tail = NULL; - char *name = strct->strct.name; - - for (int i = 0; i < count; i++) - { - char *trait = traits[i]; - char *code = NULL; - - if (0 == strcmp(trait, "Clone")) - { - code = xmalloc(1024); - sprintf(code, "impl %s { fn clone(self) -> %s { return *self; } }", name, name); +static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, + char **traits, int count) { + ASTNode *head = NULL, *tail = NULL; + char *name = strct->strct.name; + + for (int i = 0; i < count; i++) { + char *trait = traits[i]; + char *code = NULL; + + if (0 == strcmp(trait, "Clone")) { + code = xmalloc(1024); + sprintf(code, "impl %s { fn clone(self) -> %s { return *self; } }", name, + name); + } else if (0 == strcmp(trait, "Eq")) { + char body[4096]; + body[0] = 0; + + if (strct->type == NODE_ENUM) { + // Simple Enum equality (tag comparison) + // Generate Eq impl for Enum + + sprintf(body, "return self.tag == other.tag;"); + } else { + ASTNode *f = strct->strct.fields; + int first = 1; + strcat(body, "return "); + while (f) { + if (f->type == NODE_FIELD) { + char *fn = f->field.name; + char *ft = f->field.type; + if (!first) { + strcat(body, " && "); + } + char cmp[256]; + + ASTNode *fdef = find_struct_def(ctx, ft); + if (fdef && fdef->type == NODE_ENUM) { + // Enum field: compare tags + sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn); + } else if (fdef && fdef->type == NODE_STRUCT) { + // Struct field: use _eq function + sprintf(cmp, "%s_eq(&self.%s, other.%s)", ft, fn, fn); + } else { + // Primitive or unknown: use == + sprintf(cmp, "self.%s == other.%s", fn, fn); + } + strcat(body, cmp); + first = 0; + } + f = f->next; } - else if (0 == strcmp(trait, "Eq")) - { - char body[4096]; - body[0] = 0; - - if (strct->type == NODE_ENUM) - { - // Simple Enum equality (tag comparison) - // Generate Eq impl for Enum - - sprintf(body, "return self.tag == other.tag;"); - } - else - { - ASTNode *f = strct->strct.fields; - int first = 1; - strcat(body, "return "); - while (f) - { - if (f->type == NODE_FIELD) - { - char *fn = f->field.name; - char *ft = f->field.type; - if (!first) - { - strcat(body, " && "); - } - char cmp[256]; - - ASTNode *fdef = find_struct_def(ctx, ft); - if (fdef && fdef->type == NODE_ENUM) - { - // Enum field: compare tags - sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn); - } - else if (fdef && fdef->type == NODE_STRUCT) - { - // Struct field: use _eq function - sprintf(cmp, "%s_eq(&self.%s, other.%s)", ft, fn, fn); - } - else - { - // Primitive or unknown: use == - sprintf(cmp, "self.%s == other.%s", fn, fn); - } - strcat(body, cmp); - first = 0; - } - f = f->next; - } - if (first) - { - strcat(body, "true"); - } - strcat(body, ";"); - } - code = xmalloc(4096 + 1024); - sprintf(code, "impl %s { fn eq(self, other: %s) -> bool { %s } }", name, name, body); - } - else if (0 == strcmp(trait, "Debug")) - { - // Simplistic Debug for now, I know. - code = xmalloc(1024); - sprintf(code, "impl %s { fn to_string(self) -> char* { return \"%s { ... }\"; } }", - name, name); + if (first) { + strcat(body, "true"); } + strcat(body, ";"); + } + code = xmalloc(4096 + 1024); + sprintf(code, "impl %s { fn eq(self, other: %s) -> bool { %s } }", name, + name, body); + } else if (0 == strcmp(trait, "Debug")) { + // Simplistic Debug for now, I know. + code = xmalloc(1024); + sprintf( + code, + "impl %s { fn to_string(self) -> char* { return \"%s { ... }\"; } }", + name, name); + } - if (code) - { - Lexer tmp; - lexer_init(&tmp, code); - ASTNode *impl = parse_impl(ctx, &tmp); - if (impl) - { - if (!head) - { - head = impl; - } - else - { - tail->next = impl; - } - tail = impl; - } + if (code) { + Lexer tmp; + lexer_init(&tmp, code); + ASTNode *impl = parse_impl(ctx, &tmp); + if (impl) { + if (!head) { + head = impl; + } else { + tail->next = impl; } + tail = impl; + } } - return head; + } + return head; } diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 40d6ae0..4c360c0 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -6,3675 +6,3080 @@ #include <stdlib.h> #include <string.h> -Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name); +Type *get_field_type(ParserContext *ctx, Type *struct_type, + const char *field_name); -static int type_is_unsigned(Type *t) -{ - if (!t) - { - return 0; - } - - return (t->kind == TYPE_U8 || t->kind == TYPE_U16 || t->kind == TYPE_U32 || - t->kind == TYPE_U64 || t->kind == TYPE_USIZE || t->kind == TYPE_BYTE || - t->kind == TYPE_U128 || t->kind == TYPE_UINT || - (t->kind == TYPE_STRUCT && t->name && - (0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "uint16_t") || - 0 == strcmp(t->name, "uint32_t") || 0 == strcmp(t->name, "uint64_t") || - 0 == strcmp(t->name, "size_t")))); +static int type_is_unsigned(Type *t) { + if (!t) { + return 0; + } + + return ( + t->kind == TYPE_U8 || t->kind == TYPE_U16 || t->kind == TYPE_U32 || + t->kind == TYPE_U64 || t->kind == TYPE_USIZE || t->kind == TYPE_BYTE || + t->kind == TYPE_U128 || t->kind == TYPE_UINT || + (t->kind == TYPE_STRUCT && t->name && + (0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "uint16_t") || + 0 == strcmp(t->name, "uint32_t") || 0 == strcmp(t->name, "uint64_t") || + 0 == strcmp(t->name, "size_t")))); } -static void check_format_string(ASTNode *call, Token t) -{ - if (call->type != NODE_EXPR_CALL) - { - return; - } - ASTNode *callee = call->call.callee; - if (callee->type != NODE_EXPR_VAR) - { - return; - } +static void check_format_string(ASTNode *call, Token t) { + if (call->type != NODE_EXPR_CALL) { + return; + } + ASTNode *callee = call->call.callee; + if (callee->type != NODE_EXPR_VAR) { + return; + } + + char *fname = callee->var_ref.name; + if (!fname) { + return; + } + + if (strcmp(fname, "printf") != 0 && strcmp(fname, "sprintf") != 0 && + strcmp(fname, "fprintf") != 0 && strcmp(fname, "dprintf") != 0) { + return; + } + + int fmt_idx = 0; + if (strcmp(fname, "fprintf") == 0 || strcmp(fname, "sprintf") == 0 || + strcmp(fname, "dprintf") == 0) { + fmt_idx = 1; + } + + ASTNode *args = call->call.args; + ASTNode *fmt_arg = args; + for (int i = 0; i < fmt_idx; i++) { + if (!fmt_arg) { + return; + } + fmt_arg = fmt_arg->next; + } + if (!fmt_arg) { + return; + } + + if (fmt_arg->type != NODE_EXPR_LITERAL || + fmt_arg->literal.type_kind != TOK_STRING) { + return; + } + + const char *fmt = fmt_arg->literal.string_val; + + ASTNode *curr_arg = fmt_arg->next; + int arg_num = fmt_idx + 2; + + for (int i = 0; fmt[i]; i++) { + if (fmt[i] == '%') { + i++; + if (fmt[i] == 0) { + break; + } + if (fmt[i] == '%') { + continue; + } + + // Flags. + while (fmt[i] == '-' || fmt[i] == '+' || fmt[i] == ' ' || fmt[i] == '#' || + fmt[i] == '0') { + i++; + } + + // Width. + while (isdigit(fmt[i])) { + i++; + } + + if (fmt[i] == '*') { + i++; + if (!curr_arg) { + warn_format_string(t, arg_num, "width(int)", "missing"); + } else { + /* check int */ + curr_arg = curr_arg->next; + arg_num++; + } + } + + // Precision. + if (fmt[i] == '.') { + i++; + while (isdigit(fmt[i])) { + i++; + } + + if (fmt[i] == '*') { + i++; + if (!curr_arg) { + warn_format_string(t, arg_num, "precision(int)", "missing"); + } else { + /* check int */ + curr_arg = curr_arg->next; + arg_num++; + } + } + } - char *fname = callee->var_ref.name; - if (!fname) - { - return; - } + // Length. + if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L' || fmt[i] == 'z' || + fmt[i] == 'j' || fmt[i] == 't') { + if (fmt[i] == 'h' && fmt[i + 1] == 'h') { + i++; + } else if (fmt[i] == 'l' && fmt[i + 1] == 'l') { + i++; + } + i++; + } - if (strcmp(fname, "printf") != 0 && strcmp(fname, "sprintf") != 0 && - strcmp(fname, "fprintf") != 0 && strcmp(fname, "dprintf") != 0) - { - return; - } + char spec = fmt[i]; - int fmt_idx = 0; - if (strcmp(fname, "fprintf") == 0 || strcmp(fname, "sprintf") == 0 || - strcmp(fname, "dprintf") == 0) - { - fmt_idx = 1; - } + if (!curr_arg) { + warn_format_string(t, arg_num, "argument", "missing"); + continue; + } - ASTNode *args = call->call.args; - ASTNode *fmt_arg = args; - for (int i = 0; i < fmt_idx; i++) - { - if (!fmt_arg) - { - return; + Type *vt = curr_arg->type_info; + char *got_type = vt ? type_to_string(vt) : "?"; + + if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || + spec == 'X' || spec == 'o') { + if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && + !type_is_unsigned(vt) && vt->kind != TYPE_CHAR) { + warn_format_string(t, arg_num, "integer", got_type); } - fmt_arg = fmt_arg->next; - } - if (!fmt_arg) - { - return; - } + } else if (spec == 's') { + if (vt && vt->kind != TYPE_STRING && vt->kind != TYPE_POINTER && + vt->kind != TYPE_ARRAY) { + warn_format_string(t, arg_num, "string", got_type); + } + } else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || + spec == 'g' || spec == 'G') { + if (vt && vt->kind != TYPE_FLOAT && vt->kind != TYPE_F64) { + warn_format_string(t, arg_num, "float", got_type); + } + } else if (spec == 'p') { + if (vt && vt->kind != TYPE_POINTER && vt->kind != TYPE_ARRAY) { + warn_format_string(t, arg_num, "pointer", got_type); + } + } - if (fmt_arg->type != NODE_EXPR_LITERAL || fmt_arg->literal.type_kind != TOK_STRING) - { - return; + curr_arg = curr_arg->next; + arg_num++; } + } +} - const char *fmt = fmt_arg->literal.string_val; +ASTNode *parse_expression(ParserContext *ctx, Lexer *l) { + return parse_expr_prec(ctx, l, PREC_NONE); +} - ASTNode *curr_arg = fmt_arg->next; - int arg_num = fmt_idx + 2; +Precedence get_token_precedence(Token t) { + if (t.type == TOK_INT || t.type == TOK_FLOAT || t.type == TOK_STRING || + t.type == TOK_IDENT || t.type == TOK_FSTRING) { + return PREC_NONE; + } - for (int i = 0; fmt[i]; i++) - { - if (fmt[i] == '%') - { - i++; - if (fmt[i] == 0) - { - break; - } - if (fmt[i] == '%') - { - continue; - } + if (t.type == TOK_QUESTION) { + return PREC_CALL; + } - // Flags. - while (fmt[i] == '-' || fmt[i] == '+' || fmt[i] == ' ' || fmt[i] == '#' || - fmt[i] == '0') - { - i++; - } + if (t.type == TOK_ARROW && t.start[0] == '-') { + return PREC_CALL; + } - // Width. - while (isdigit(fmt[i])) - { - i++; - } + if (t.type == TOK_Q_DOT) { + return PREC_CALL; + } - if (fmt[i] == '*') - { - i++; - if (!curr_arg) - { - warn_format_string(t, arg_num, "width(int)", "missing"); - } - else - { - /* check int */ - curr_arg = curr_arg->next; - arg_num++; - } - } + if (t.type == TOK_QQ) { + return PREC_OR; + } - // Precision. - if (fmt[i] == '.') - { - i++; - while (isdigit(fmt[i])) - { - i++; - } - - if (fmt[i] == '*') - { - i++; - if (!curr_arg) - { - warn_format_string(t, arg_num, "precision(int)", "missing"); - } - else - { - /* check int */ - curr_arg = curr_arg->next; - arg_num++; - } - } - } + if (t.type == TOK_OR) { + return PREC_OR; + } - // Length. - if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L' || fmt[i] == 'z' || fmt[i] == 'j' || - fmt[i] == 't') - { - if (fmt[i] == 'h' && fmt[i + 1] == 'h') - { - i++; - } - else if (fmt[i] == 'l' && fmt[i + 1] == 'l') - { - i++; - } - i++; - } + if (t.type == TOK_AND) { + return PREC_AND; + } - char spec = fmt[i]; + if (t.type == TOK_QQ_EQ) { + return PREC_ASSIGNMENT; + } - if (!curr_arg) - { - warn_format_string(t, arg_num, "argument", "missing"); - continue; - } + if (t.type == TOK_PIPE) { + return PREC_TERM; + } - Type *vt = curr_arg->type_info; - char *got_type = vt ? type_to_string(vt) : "?"; - - if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || - spec == 'o') - { - if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) && - vt->kind != TYPE_CHAR) - { - warn_format_string(t, arg_num, "integer", got_type); - } - } - else if (spec == 's') - { - if (vt && vt->kind != TYPE_STRING && vt->kind != TYPE_POINTER && - vt->kind != TYPE_ARRAY) - { - warn_format_string(t, arg_num, "string", got_type); - } - } - else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' || - spec == 'G') - { - if (vt && vt->kind != TYPE_FLOAT && vt->kind != TYPE_F64) - { - warn_format_string(t, arg_num, "float", got_type); - } - } - else if (spec == 'p') - { - if (vt && vt->kind != TYPE_POINTER && vt->kind != TYPE_ARRAY) - { - warn_format_string(t, arg_num, "pointer", got_type); - } - } + if (t.type == TOK_LANGLE || t.type == TOK_RANGLE) { + return PREC_COMPARISON; + } - curr_arg = curr_arg->next; - arg_num++; - } + if (t.type == TOK_OP) { + if (is_token(t, "=") || is_token(t, "+=") || is_token(t, "-=") || + is_token(t, "*=") || is_token(t, "/=") || is_token(t, "%=") || + is_token(t, "|=") || is_token(t, "&=") || is_token(t, "^=") || + is_token(t, "<<=") || is_token(t, ">>=")) { + return PREC_ASSIGNMENT; } -} -ASTNode *parse_expression(ParserContext *ctx, Lexer *l) -{ - return parse_expr_prec(ctx, l, PREC_NONE); -} - -Precedence get_token_precedence(Token t) -{ - if (t.type == TOK_INT || t.type == TOK_FLOAT || t.type == TOK_STRING || t.type == TOK_IDENT || - t.type == TOK_FSTRING) - { - return PREC_NONE; + if (is_token(t, "||") || is_token(t, "or")) { + return PREC_OR; } - if (t.type == TOK_QUESTION) - { - return PREC_CALL; + if (is_token(t, "&&") || is_token(t, "and")) { + return PREC_AND; } - if (t.type == TOK_ARROW && t.start[0] == '-') - { - return PREC_CALL; + if (is_token(t, "|")) { + return PREC_TERM; } - if (t.type == TOK_Q_DOT) - { - return PREC_CALL; + if (is_token(t, "^")) { + return PREC_TERM; } - if (t.type == TOK_QQ) - { - return PREC_OR; + if (is_token(t, "&")) { + return PREC_TERM; } - if (t.type == TOK_OR) - { - return PREC_OR; + if (is_token(t, "<<") || is_token(t, ">>")) { + return PREC_TERM; } - if (t.type == TOK_AND) - { - return PREC_AND; + if (is_token(t, "==") || is_token(t, "!=")) { + return PREC_EQUALITY; } - if (t.type == TOK_QQ_EQ) - { - return PREC_ASSIGNMENT; + if (is_token(t, "<") || is_token(t, ">") || is_token(t, "<=") || + is_token(t, ">=")) { + return PREC_COMPARISON; } - if (t.type == TOK_PIPE) - { - return PREC_TERM; + if (is_token(t, "+") || is_token(t, "-")) { + return PREC_TERM; } - if (t.type == TOK_LANGLE || t.type == TOK_RANGLE) - { - return PREC_COMPARISON; + if (is_token(t, "*") || is_token(t, "/") || is_token(t, "%")) { + return PREC_FACTOR; } - if (t.type == TOK_OP) - { - if (is_token(t, "=") || is_token(t, "+=") || is_token(t, "-=") || is_token(t, "*=") || - is_token(t, "/=") || is_token(t, "%=") || is_token(t, "|=") || is_token(t, "&=") || - is_token(t, "^=") || is_token(t, "<<=") || is_token(t, ">>=")) - { - return PREC_ASSIGNMENT; - } - - if (is_token(t, "||") || is_token(t, "or")) - { - return PREC_OR; - } + if (is_token(t, ".")) { + return PREC_CALL; + } - if (is_token(t, "&&") || is_token(t, "and")) - { - return PREC_AND; - } + if (is_token(t, "|>")) { + return PREC_TERM; + } + } - if (is_token(t, "|")) - { - return PREC_TERM; - } + if (t.type == TOK_LBRACKET || t.type == TOK_LPAREN) { + return PREC_CALL; + } - if (is_token(t, "^")) - { - return PREC_TERM; - } + if (is_token(t, "??")) { + return PREC_OR; + } - if (is_token(t, "&")) - { - return PREC_TERM; - } + if (is_token(t, "\?\?=")) { + return PREC_ASSIGNMENT; + } - if (is_token(t, "<<") || is_token(t, ">>")) - { - return PREC_TERM; - } + return PREC_NONE; +} - if (is_token(t, "==") || is_token(t, "!=")) - { - return PREC_EQUALITY; - } +// Helper to check if a variable name is in a list. +static int is_in_list(const char *name, char **list, int count) { + for (int i = 0; i < count; i++) { + if (0 == strcmp(name, list[i])) { + return 1; + } + } + return 0; +} - if (is_token(t, "<") || is_token(t, ">") || is_token(t, "<=") || is_token(t, ">=")) - { - return PREC_COMPARISON; - } +// Recursively find all variable references in an expression/statement. +static void find_var_refs(ASTNode *node, char ***refs, int *ref_count) { + if (!node) { + return; + } + + if (node->type == NODE_EXPR_VAR) { + *refs = xrealloc(*refs, sizeof(char *) * (*ref_count + 1)); + (*refs)[*ref_count] = xstrdup(node->var_ref.name); + (*ref_count)++; + } + + switch (node->type) { + case NODE_EXPR_BINARY: + find_var_refs(node->binary.left, refs, ref_count); + find_var_refs(node->binary.right, refs, ref_count); + break; + case NODE_EXPR_UNARY: + find_var_refs(node->unary.operand, refs, ref_count); + break; + case NODE_EXPR_CALL: + find_var_refs(node->call.callee, refs, ref_count); + for (ASTNode *arg = node->call.args; arg; arg = arg->next) { + find_var_refs(arg, refs, ref_count); + } + break; + case NODE_EXPR_MEMBER: + find_var_refs(node->member.target, refs, ref_count); + break; + case NODE_EXPR_INDEX: + find_var_refs(node->index.array, refs, ref_count); + find_var_refs(node->index.index, refs, ref_count); + break; + case NODE_EXPR_SLICE: + find_var_refs(node->slice.array, refs, ref_count); + find_var_refs(node->slice.start, refs, ref_count); + find_var_refs(node->slice.end, refs, ref_count); + break; + case NODE_BLOCK: + for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) { + find_var_refs(stmt, refs, ref_count); + } + break; + case NODE_RETURN: + find_var_refs(node->ret.value, refs, ref_count); + break; + case NODE_VAR_DECL: + case NODE_CONST: + find_var_refs(node->var_decl.init_expr, refs, ref_count); + break; + case NODE_IF: + find_var_refs(node->if_stmt.condition, refs, ref_count); + find_var_refs(node->if_stmt.then_body, refs, ref_count); + if (node->if_stmt.else_body) { + find_var_refs(node->if_stmt.else_body, refs, ref_count); + } + break; + case NODE_WHILE: + find_var_refs(node->while_stmt.condition, refs, ref_count); + find_var_refs(node->while_stmt.body, refs, ref_count); + break; + case NODE_FOR: + find_var_refs(node->for_stmt.init, refs, ref_count); + find_var_refs(node->for_stmt.condition, refs, ref_count); + find_var_refs(node->for_stmt.step, refs, ref_count); + find_var_refs(node->for_stmt.body, refs, ref_count); + break; + case NODE_MATCH: + find_var_refs(node->match_stmt.expr, refs, ref_count); + for (ASTNode *c = node->match_stmt.cases; c; c = c->next) { + find_var_refs(c->match_case.body, refs, ref_count); + } + break; + default: + break; + } +} - if (is_token(t, "+") || is_token(t, "-")) - { - return PREC_TERM; - } +// Helper to find variable declarations in a subtree +static void find_declared_vars(ASTNode *node, char ***decls, int *count) { + if (!node) { + return; + } + + if (node->type == NODE_VAR_DECL) { + *decls = xrealloc(*decls, sizeof(char *) * (*count + 1)); + (*decls)[*count] = xstrdup(node->var_decl.name); + (*count)++; + } + + if (node->type == NODE_MATCH_CASE && node->match_case.binding_name) { + *decls = xrealloc(*decls, sizeof(char *) * (*count + 1)); + (*decls)[*count] = xstrdup(node->match_case.binding_name); + (*count)++; + } + + switch (node->type) { + case NODE_BLOCK: + for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) { + find_declared_vars(stmt, decls, count); + } + break; + case NODE_IF: + find_declared_vars(node->if_stmt.then_body, decls, count); + find_declared_vars(node->if_stmt.else_body, decls, count); + break; + case NODE_WHILE: + find_declared_vars(node->while_stmt.body, decls, count); + break; + case NODE_FOR: + find_declared_vars(node->for_stmt.init, decls, count); + find_declared_vars(node->for_stmt.body, decls, count); + break; + case NODE_MATCH: + for (ASTNode *c = node->match_stmt.cases; c; c = c->next) { + find_declared_vars(c, decls, count); + find_declared_vars(c->match_case.body, decls, count); + } + break; + default: + break; + } +} - if (is_token(t, "*") || is_token(t, "/") || is_token(t, "%")) - { - return PREC_FACTOR; - } +// Analyze lambda body to find captured variables. +void analyze_lambda_captures(ParserContext *ctx, ASTNode *lambda) { + if (!lambda || lambda->type != NODE_LAMBDA) { + return; + } - if (is_token(t, ".")) - { - return PREC_CALL; - } + char **all_refs = NULL; + int num_refs = 0; + find_var_refs(lambda->lambda.body, &all_refs, &num_refs); - if (is_token(t, "|>")) - { - return PREC_TERM; - } - } + char **local_decls = NULL; + int num_local_decls = 0; + find_declared_vars(lambda->lambda.body, &local_decls, &num_local_decls); - if (t.type == TOK_LBRACKET || t.type == TOK_LPAREN) - { - return PREC_CALL; - } + char **captures = xmalloc(sizeof(char *) * 16); + char **capture_types = xmalloc(sizeof(char *) * 16); + int num_captures = 0; - if (is_token(t, "??")) - { - return PREC_OR; - } + for (int i = 0; i < num_refs; i++) { + const char *var_name = all_refs[i]; - if (is_token(t, "\?\?=")) - { - return PREC_ASSIGNMENT; + if (is_in_list(var_name, lambda->lambda.param_names, + lambda->lambda.num_params)) { + continue; } - return PREC_NONE; -} - -// Helper to check if a variable name is in a list. -static int is_in_list(const char *name, char **list, int count) -{ - for (int i = 0; i < count; i++) - { - if (0 == strcmp(name, list[i])) - { - return 1; - } + if (is_in_list(var_name, local_decls, num_local_decls)) { + continue; } - return 0; -} -// Recursively find all variable references in an expression/statement. -static void find_var_refs(ASTNode *node, char ***refs, int *ref_count) -{ - if (!node) - { - return; + if (is_in_list(var_name, captures, num_captures)) { + continue; } - if (node->type == NODE_EXPR_VAR) - { - *refs = xrealloc(*refs, sizeof(char *) * (*ref_count + 1)); - (*refs)[*ref_count] = xstrdup(node->var_ref.name); - (*ref_count)++; + if (strcmp(var_name, "printf") == 0 || strcmp(var_name, "malloc") == 0 || + strcmp(var_name, "strcmp") == 0 || strcmp(var_name, "free") == 0 || + strcmp(var_name, "Vec_new") == 0 || strcmp(var_name, "Vec_push") == 0) { + continue; } - switch (node->type) - { - case NODE_EXPR_BINARY: - find_var_refs(node->binary.left, refs, ref_count); - find_var_refs(node->binary.right, refs, ref_count); - break; - case NODE_EXPR_UNARY: - find_var_refs(node->unary.operand, refs, ref_count); - break; - case NODE_EXPR_CALL: - find_var_refs(node->call.callee, refs, ref_count); - for (ASTNode *arg = node->call.args; arg; arg = arg->next) - { - find_var_refs(arg, refs, ref_count); - } - break; - case NODE_EXPR_MEMBER: - find_var_refs(node->member.target, refs, ref_count); - break; - case NODE_EXPR_INDEX: - find_var_refs(node->index.array, refs, ref_count); - find_var_refs(node->index.index, refs, ref_count); - break; - case NODE_EXPR_SLICE: - find_var_refs(node->slice.array, refs, ref_count); - find_var_refs(node->slice.start, refs, ref_count); - find_var_refs(node->slice.end, refs, ref_count); - break; - case NODE_BLOCK: - for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) - { - find_var_refs(stmt, refs, ref_count); - } - break; - case NODE_RETURN: - find_var_refs(node->ret.value, refs, ref_count); - break; - case NODE_VAR_DECL: - case NODE_CONST: - find_var_refs(node->var_decl.init_expr, refs, ref_count); - break; - case NODE_IF: - find_var_refs(node->if_stmt.condition, refs, ref_count); - find_var_refs(node->if_stmt.then_body, refs, ref_count); - if (node->if_stmt.else_body) - { - find_var_refs(node->if_stmt.else_body, refs, ref_count); - } + FuncSig *fs = ctx->func_registry; + int is_func = 0; + while (fs) { + if (0 == strcmp(fs->name, var_name)) { + is_func = 1; break; - case NODE_WHILE: - find_var_refs(node->while_stmt.condition, refs, ref_count); - find_var_refs(node->while_stmt.body, refs, ref_count); - break; - case NODE_FOR: - find_var_refs(node->for_stmt.init, refs, ref_count); - find_var_refs(node->for_stmt.condition, refs, ref_count); - find_var_refs(node->for_stmt.step, refs, ref_count); - find_var_refs(node->for_stmt.body, refs, ref_count); - break; - case NODE_MATCH: - find_var_refs(node->match_stmt.expr, refs, ref_count); - for (ASTNode *c = node->match_stmt.cases; c; c = c->next) - { - find_var_refs(c->match_case.body, refs, ref_count); - } - break; - default: + } + fs = fs->next; + } + if (is_func) { + continue; + } + + Scope *s = ctx->current_scope; + int is_local = 0; + int is_found = 0; + while (s) { + Symbol *cur = s->symbols; + while (cur) { + if (0 == strcmp(cur->name, var_name)) { + is_found = 1; + if (s->parent != NULL) { + is_local = 1; + } + break; + } + cur = cur->next; + } + if (is_found) { break; + } + s = s->parent; } -} -// Helper to find variable declarations in a subtree -static void find_declared_vars(ASTNode *node, char ***decls, int *count) -{ - if (!node) - { - return; + if (is_found && !is_local) { + continue; } - if (node->type == NODE_VAR_DECL) - { - *decls = xrealloc(*decls, sizeof(char *) * (*count + 1)); - (*decls)[*count] = xstrdup(node->var_decl.name); - (*count)++; - } + captures[num_captures] = xstrdup(var_name); - if (node->type == NODE_MATCH_CASE && node->match_case.binding_name) - { - *decls = xrealloc(*decls, sizeof(char *) * (*count + 1)); - (*decls)[*count] = xstrdup(node->match_case.binding_name); - (*count)++; + Type *t = find_symbol_type_info(ctx, var_name); + if (t) { + capture_types[num_captures] = type_to_string(t); + } else { + // Fallback for global/unknown + // If looks like a function, use "void*" (for closure ctx) + // else default to "int" or "void*" + capture_types[num_captures] = xstrdup("int"); } + num_captures++; + } - switch (node->type) - { - case NODE_BLOCK: - for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) - { - find_declared_vars(stmt, decls, count); - } - break; - case NODE_IF: - find_declared_vars(node->if_stmt.then_body, decls, count); - find_declared_vars(node->if_stmt.else_body, decls, count); - break; - case NODE_WHILE: - find_declared_vars(node->while_stmt.body, decls, count); - break; - case NODE_FOR: - find_declared_vars(node->for_stmt.init, decls, count); - find_declared_vars(node->for_stmt.body, decls, count); - break; - case NODE_MATCH: - for (ASTNode *c = node->match_stmt.cases; c; c = c->next) - { - find_declared_vars(c, decls, count); - find_declared_vars(c->match_case.body, decls, count); - } - break; - default: - break; - } -} + lambda->lambda.captured_vars = captures; + lambda->lambda.captured_types = capture_types; + lambda->lambda.num_captures = num_captures; -// Analyze lambda body to find captured variables. -void analyze_lambda_captures(ParserContext *ctx, ASTNode *lambda) -{ - if (!lambda || lambda->type != NODE_LAMBDA) - { - return; + if (local_decls) { + for (int i = 0; i < num_local_decls; i++) { + free(local_decls[i]); } + free(local_decls); + } + for (int i = 0; i < num_refs; i++) { + free(all_refs[i]); + } + if (all_refs) { + free(all_refs); + } +} - char **all_refs = NULL; - int num_refs = 0; - find_var_refs(lambda->lambda.body, &all_refs, &num_refs); - - char **local_decls = NULL; - int num_local_decls = 0; - find_declared_vars(lambda->lambda.body, &local_decls, &num_local_decls); - - char **captures = xmalloc(sizeof(char *) * 16); - char **capture_types = xmalloc(sizeof(char *) * 16); - int num_captures = 0; - - for (int i = 0; i < num_refs; i++) - { - const char *var_name = all_refs[i]; - - if (is_in_list(var_name, lambda->lambda.param_names, lambda->lambda.num_params)) - { - continue; - } - - if (is_in_list(var_name, local_decls, num_local_decls)) - { - continue; - } +ASTNode *parse_lambda(ParserContext *ctx, Lexer *l) { + lexer_next(l); - if (is_in_list(var_name, captures, num_captures)) - { - continue; - } + if (lexer_peek(l).type != TOK_LPAREN) { + zpanic_at(lexer_peek(l), "Expected '(' after 'fn' in lambda"); + } - if (strcmp(var_name, "printf") == 0 || strcmp(var_name, "malloc") == 0 || - strcmp(var_name, "strcmp") == 0 || strcmp(var_name, "free") == 0 || - strcmp(var_name, "Vec_new") == 0 || strcmp(var_name, "Vec_push") == 0) - { - continue; - } + lexer_next(l); - FuncSig *fs = ctx->func_registry; - int is_func = 0; - while (fs) - { - if (0 == strcmp(fs->name, var_name)) - { - is_func = 1; - break; - } - fs = fs->next; - } - if (is_func) - { - continue; - } + char **param_names = xmalloc(sizeof(char *) * 16); + char **param_types = xmalloc(sizeof(char *) * 16); + int num_params = 0; - Scope *s = ctx->current_scope; - int is_local = 0; - int is_found = 0; - while (s) - { - Symbol *cur = s->symbols; - while (cur) - { - if (0 == strcmp(cur->name, var_name)) - { - is_found = 1; - if (s->parent != NULL) - { - is_local = 1; - } - break; - } - cur = cur->next; - } - if (is_found) - { - break; - } - s = s->parent; - } + while (lexer_peek(l).type != TOK_RPAREN) { + if (num_params > 0) { + if (lexer_peek(l).type != TOK_COMMA) { + zpanic_at(lexer_peek(l), "Expected ',' between parameters"); + } - if (is_found && !is_local) - { - continue; - } - - captures[num_captures] = xstrdup(var_name); - - Type *t = find_symbol_type_info(ctx, var_name); - if (t) - { - capture_types[num_captures] = type_to_string(t); - } - else - { - // Fallback for global/unknown - // If looks like a function, use "void*" (for closure ctx) - // else default to "int" or "void*" - capture_types[num_captures] = xstrdup("int"); - } - num_captures++; + lexer_next(l); } - lambda->lambda.captured_vars = captures; - lambda->lambda.captured_types = capture_types; - lambda->lambda.num_captures = num_captures; - - if (local_decls) - { - for (int i = 0; i < num_local_decls; i++) - { - free(local_decls[i]); - } - free(local_decls); - } - for (int i = 0; i < num_refs; i++) - { - free(all_refs[i]); - } - if (all_refs) - { - free(all_refs); + Token name_tok = lexer_next(l); + if (name_tok.type != TOK_IDENT) { + zpanic_at(name_tok, "Expected parameter name"); } -} -ASTNode *parse_lambda(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); + param_names[num_params] = token_strdup(name_tok); - if (lexer_peek(l).type != TOK_LPAREN) - { - zpanic_at(lexer_peek(l), "Expected '(' after 'fn' in lambda"); + if (lexer_peek(l).type != TOK_COLON) { + zpanic_at(lexer_peek(l), "Expected ':' after parameter name"); } lexer_next(l); - char **param_names = xmalloc(sizeof(char *) * 16); - char **param_types = xmalloc(sizeof(char *) * 16); - int num_params = 0; - - while (lexer_peek(l).type != TOK_RPAREN) - { - if (num_params > 0) - { - if (lexer_peek(l).type != TOK_COMMA) - { - zpanic_at(lexer_peek(l), "Expected ',' between parameters"); - } - - lexer_next(l); - } - - Token name_tok = lexer_next(l); - if (name_tok.type != TOK_IDENT) - { - zpanic_at(name_tok, "Expected parameter name"); - } - - param_names[num_params] = token_strdup(name_tok); + char *param_type_str = parse_type(ctx, l); + param_types[num_params] = param_type_str; + num_params++; + } + lexer_next(l); - if (lexer_peek(l).type != TOK_COLON) - { - zpanic_at(lexer_peek(l), "Expected ':' after parameter name"); - } + char *return_type = xstrdup("void"); + if (lexer_peek(l).type == TOK_ARROW) { + lexer_next(l); + return_type = parse_type(ctx, l); + } + + ASTNode *body = NULL; + if (lexer_peek(l).type == TOK_LBRACE) { + body = parse_block(ctx, l); + } else { + zpanic_at(lexer_peek(l), "Expected '{' for lambda body"); + } + + ASTNode *lambda = ast_create(NODE_LAMBDA); + lambda->lambda.param_names = param_names; + lambda->lambda.param_types = param_types; + lambda->lambda.return_type = return_type; + lambda->lambda.body = body; + lambda->lambda.num_params = num_params; + lambda->lambda.lambda_id = ctx->lambda_counter++; + lambda->lambda.is_expression = 0; + register_lambda(ctx, lambda); + analyze_lambda_captures(ctx, lambda); + + return lambda; +} - lexer_next(l); +// Helper to create AST for f-string content. +static ASTNode *create_fstring_block(ParserContext *ctx, const char *content) { + ASTNode *block = ast_create(NODE_BLOCK); + block->type_info = type_new(TYPE_STRING); + block->resolved_type = xstrdup("string"); + + ASTNode *head = NULL, *tail = NULL; + + ASTNode *decl_b = ast_create(NODE_RAW_STMT); + decl_b->raw_stmt.content = xstrdup("static char _b[4096]; _b[0]=0;"); + if (!head) { + head = decl_b; + } else { + tail->next = decl_b; + } + tail = decl_b; + + ASTNode *decl_t = ast_create(NODE_RAW_STMT); + decl_t->raw_stmt.content = xstrdup("char _t[128];"); + tail->next = decl_t; + tail = decl_t; + + const char *cur = content; + while (*cur) { + char *brace = strchr(cur, '{'); + if (!brace) { + if (strlen(cur) > 0) { + ASTNode *cat = ast_create(NODE_RAW_STMT); + cat->raw_stmt.content = xmalloc(strlen(cur) + 20); + sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", cur); + tail->next = cat; + tail = cat; + } + break; + } + + if (brace > cur) { + int len = brace - cur; + char *txt = xmalloc(len + 1); + strncpy(txt, cur, len); + txt[len] = 0; + ASTNode *cat = ast_create(NODE_RAW_STMT); + cat->raw_stmt.content = xmalloc(len + 20); + sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", txt); + tail->next = cat; + tail = cat; + free(txt); + } + + char *end_brace = strchr(brace, '}'); + if (!end_brace) { + break; + } + + char *colon = NULL; + char *p = brace + 1; + int depth = 1; + while (p < end_brace) { + if (*p == '{') { + depth++; + } + if (*p == '}') { + depth--; + } + if (depth == 1 && *p == ':' && !colon) { + colon = p; + } + p++; + } + + char *expr_str; + char *fmt = NULL; + + if (colon && colon < end_brace) { + int expr_len = colon - (brace + 1); + expr_str = xmalloc(expr_len + 1); + strncpy(expr_str, brace + 1, expr_len); + expr_str[expr_len] = 0; + + int fmt_len = end_brace - (colon + 1); + fmt = xmalloc(fmt_len + 1); + strncpy(fmt, colon + 1, fmt_len); + fmt[fmt_len] = 0; + } else { + int expr_len = end_brace - (brace + 1); + expr_str = xmalloc(expr_len + 1); + strncpy(expr_str, brace + 1, expr_len); + expr_str[expr_len] = 0; + } + + Lexer sub_l; + lexer_init(&sub_l, expr_str); + ASTNode *expr_node = parse_expression(ctx, &sub_l); + + if (expr_node && expr_node->type == NODE_EXPR_VAR) { + Symbol *sym = find_symbol_entry(ctx, expr_node->var_ref.name); + if (sym) { + sym->is_used = 1; + } + } + + ASTNode *call_sprintf = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup("sprintf"); + call_sprintf->call.callee = callee; + + ASTNode *arg_t = ast_create(NODE_EXPR_VAR); + arg_t->var_ref.name = xstrdup("_t"); + + ASTNode *arg_fmt = NULL; + if (fmt) { + char *fmt_str = xmalloc(strlen(fmt) + 3); + sprintf(fmt_str, "%%%s", fmt); + arg_fmt = ast_create(NODE_EXPR_LITERAL); + arg_fmt->literal.type_kind = 2; + arg_fmt->literal.string_val = fmt_str; + arg_fmt->type_info = type_new(TYPE_STRING); + } else { + // _z_str(expr) + ASTNode *call_macro = ast_create(NODE_EXPR_CALL); + ASTNode *macro_callee = ast_create(NODE_EXPR_VAR); + macro_callee->var_ref.name = xstrdup("_z_str"); + call_macro->call.callee = macro_callee; + Lexer l2; + lexer_init(&l2, expr_str); + ASTNode *expr_copy = parse_expression(ctx, &l2); + + call_macro->call.args = expr_copy; + arg_fmt = call_macro; + } + + call_sprintf->call.args = arg_t; + arg_t->next = arg_fmt; + arg_fmt->next = expr_node; + + tail->next = call_sprintf; + tail = call_sprintf; + + // strcat(_b, _t) + ASTNode *cat_t = ast_create(NODE_RAW_STMT); + cat_t->raw_stmt.content = xstrdup("strcat(_b, _t);"); + tail->next = cat_t; + tail = cat_t; + + cur = end_brace + 1; + free(expr_str); + if (fmt) { + free(fmt); + } + } + + // Return _b + ASTNode *ret_b = ast_create(NODE_RAW_STMT); + ret_b->raw_stmt.content = xstrdup("_b;"); + tail->next = ret_b; + tail = ret_b; + + block->block.statements = head; + return block; +} - char *param_type_str = parse_type(ctx, l); - param_types[num_params] = param_type_str; - num_params++; +ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { + ASTNode *node = NULL; + Token t = lexer_next(l); + + // ** Prefixes ** + + // Literals + if (t.type == TOK_INT) { + node = ast_create(NODE_EXPR_LITERAL); + node->literal.type_kind = 0; + node->type_info = type_new(TYPE_INT); + char *s = token_strdup(t); + unsigned long long val; + if (t.len > 2 && s[0] == '0' && s[1] == 'b') { + val = strtoull(s + 2, NULL, 2); + } else { + val = strtoull(s, NULL, 0); + } + node->literal.int_val = (unsigned long long)val; + free(s); + } else if (t.type == TOK_FLOAT) { + node = ast_create(NODE_EXPR_LITERAL); + node->literal.type_kind = 1; + node->literal.float_val = atof(t.start); + node->type_info = type_new(TYPE_F64); + } else if (t.type == TOK_STRING) { + node = ast_create(NODE_EXPR_LITERAL); + node->literal.type_kind = TOK_STRING; + node->literal.string_val = xmalloc(t.len); + strncpy(node->literal.string_val, t.start + 1, t.len - 2); + node->literal.string_val[t.len - 2] = 0; + node->type_info = type_new(TYPE_STRING); + } else if (t.type == TOK_FSTRING) { + char *inner = xmalloc(t.len); + strncpy(inner, t.start + 2, t.len - 3); + inner[t.len - 3] = 0; + node = create_fstring_block(ctx, inner); + free(inner); + } else if (t.type == TOK_CHAR) { + node = ast_create(NODE_EXPR_LITERAL); + node->literal.type_kind = TOK_CHAR; + node->literal.string_val = token_strdup(t); + node->type_info = type_new(TYPE_I8); + } + + else if (t.type == TOK_SIZEOF) { + if (lexer_peek(l).type != TOK_LPAREN) { + zpanic_at(lexer_peek(l), "Expected ( after sizeof"); } lexer_next(l); - char *return_type = xstrdup("void"); - if (lexer_peek(l).type == TOK_ARROW) - { - lexer_next(l); - return_type = parse_type(ctx, l); - } - - ASTNode *body = NULL; - if (lexer_peek(l).type == TOK_LBRACE) - { - body = parse_block(ctx, l); + int pos = l->pos; + int col = l->col; + int line = l->line; + Type *ty = parse_type_formal(ctx, l); + + if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) { + lexer_next(l); + char *ts = type_to_string(ty); + node = ast_create(NODE_EXPR_SIZEOF); + node->size_of.target_type = ts; + node->size_of.expr = NULL; + node->type_info = type_new(TYPE_USIZE); + } else { + l->pos = pos; + l->col = col; + l->line = line; + ASTNode *ex = parse_expression(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after sizeof identifier"); + } + node = ast_create(NODE_EXPR_SIZEOF); + node->size_of.target_type = NULL; + node->size_of.expr = ex; + node->type_info = type_new(TYPE_USIZE); + } + } + + else if (t.type == TOK_IDENT && strncmp(t.start, "typeof", 6) == 0 && + t.len == 6) { + if (lexer_peek(l).type != TOK_LPAREN) { + zpanic_at(lexer_peek(l), "Expected ( after typeof"); } - else - { - zpanic_at(lexer_peek(l), "Expected '{' for lambda body"); - } - - ASTNode *lambda = ast_create(NODE_LAMBDA); - lambda->lambda.param_names = param_names; - lambda->lambda.param_types = param_types; - lambda->lambda.return_type = return_type; - lambda->lambda.body = body; - lambda->lambda.num_params = num_params; - lambda->lambda.lambda_id = ctx->lambda_counter++; - lambda->lambda.is_expression = 0; - register_lambda(ctx, lambda); - analyze_lambda_captures(ctx, lambda); + lexer_next(l); - return lambda; -} + int pos = l->pos; + int col = l->col; + int line = l->line; + Type *ty = parse_type_formal(ctx, l); -// Helper to create AST for f-string content. -static ASTNode *create_fstring_block(ParserContext *ctx, const char *content) -{ - ASTNode *block = ast_create(NODE_BLOCK); - block->type_info = type_new(TYPE_STRING); - block->resolved_type = xstrdup("string"); + if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) { + lexer_next(l); + char *ts = type_to_string(ty); + node = ast_create(NODE_TYPEOF); + node->size_of.target_type = ts; + node->size_of.expr = NULL; + } else { + l->pos = pos; + l->col = col; + l->line = line; + ASTNode *ex = parse_expression(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after typeof expression"); + } + node = ast_create(NODE_TYPEOF); + node->size_of.target_type = NULL; + node->size_of.expr = ex; + } + } - ASTNode *head = NULL, *tail = NULL; + else if (t.type == TOK_AT) { + Token ident = lexer_next(l); + if (ident.type != TOK_IDENT) { + zpanic_at(ident, "Expected intrinsic name after @"); + } - ASTNode *decl_b = ast_create(NODE_RAW_STMT); - decl_b->raw_stmt.content = xstrdup("static char _b[4096]; _b[0]=0;"); - if (!head) - { - head = decl_b; + int kind = -1; + if (strncmp(ident.start, "type_name", 9) == 0 && ident.len == 9) { + kind = 0; + } else if (strncmp(ident.start, "fields", 6) == 0 && ident.len == 6) { + kind = 1; + } else { + zpanic_at(ident, "Unknown intrinsic @%.*s", ident.len, ident.start); } - else + { - tail->next = decl_b; + Token t = lexer_next(l); + if (t.type != TOK_LPAREN) { + zpanic_at(t, "Expected ( after intrinsic"); + } } - tail = decl_b; - ASTNode *decl_t = ast_create(NODE_RAW_STMT); - decl_t->raw_stmt.content = xstrdup("char _t[128];"); - tail->next = decl_t; - tail = decl_t; + Type *target = parse_type_formal(ctx, l); - const char *cur = content; - while (*cur) { - char *brace = strchr(cur, '{'); - if (!brace) - { - if (strlen(cur) > 0) - { - ASTNode *cat = ast_create(NODE_RAW_STMT); - cat->raw_stmt.content = xmalloc(strlen(cur) + 20); - sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", cur); - tail->next = cat; - tail = cat; - } - break; - } - - if (brace > cur) - { - int len = brace - cur; - char *txt = xmalloc(len + 1); - strncpy(txt, cur, len); - txt[len] = 0; - ASTNode *cat = ast_create(NODE_RAW_STMT); - cat->raw_stmt.content = xmalloc(len + 20); - sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", txt); - tail->next = cat; - tail = cat; - free(txt); - } - - char *end_brace = strchr(brace, '}'); - if (!end_brace) - { - break; - } - - char *colon = NULL; - char *p = brace + 1; - int depth = 1; - while (p < end_brace) - { - if (*p == '{') - { - depth++; - } - if (*p == '}') - { - depth--; - } - if (depth == 1 && *p == ':' && !colon) - { - colon = p; - } - p++; - } - - char *expr_str; - char *fmt = NULL; - - if (colon && colon < end_brace) - { - int expr_len = colon - (brace + 1); - expr_str = xmalloc(expr_len + 1); - strncpy(expr_str, brace + 1, expr_len); - expr_str[expr_len] = 0; - - int fmt_len = end_brace - (colon + 1); - fmt = xmalloc(fmt_len + 1); - strncpy(fmt, colon + 1, fmt_len); - fmt[fmt_len] = 0; - } - else - { - int expr_len = end_brace - (brace + 1); - expr_str = xmalloc(expr_len + 1); - strncpy(expr_str, brace + 1, expr_len); - expr_str[expr_len] = 0; - } - - Lexer sub_l; - lexer_init(&sub_l, expr_str); - ASTNode *expr_node = parse_expression(ctx, &sub_l); - - if (expr_node && expr_node->type == NODE_EXPR_VAR) - { - Symbol *sym = find_symbol_entry(ctx, expr_node->var_ref.name); - if (sym) - { - sym->is_used = 1; - } - } - - ASTNode *call_sprintf = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup("sprintf"); - call_sprintf->call.callee = callee; - - ASTNode *arg_t = ast_create(NODE_EXPR_VAR); - arg_t->var_ref.name = xstrdup("_t"); - - ASTNode *arg_fmt = NULL; - if (fmt) - { - char *fmt_str = xmalloc(strlen(fmt) + 3); - sprintf(fmt_str, "%%%s", fmt); - arg_fmt = ast_create(NODE_EXPR_LITERAL); - arg_fmt->literal.type_kind = 2; - arg_fmt->literal.string_val = fmt_str; - arg_fmt->type_info = type_new(TYPE_STRING); - } - else - { - // _z_str(expr) - ASTNode *call_macro = ast_create(NODE_EXPR_CALL); - ASTNode *macro_callee = ast_create(NODE_EXPR_VAR); - macro_callee->var_ref.name = xstrdup("_z_str"); - call_macro->call.callee = macro_callee; - Lexer l2; - lexer_init(&l2, expr_str); - ASTNode *expr_copy = parse_expression(ctx, &l2); - - call_macro->call.args = expr_copy; - arg_fmt = call_macro; - } - - call_sprintf->call.args = arg_t; - arg_t->next = arg_fmt; - arg_fmt->next = expr_node; - - tail->next = call_sprintf; - tail = call_sprintf; - - // strcat(_b, _t) - ASTNode *cat_t = ast_create(NODE_RAW_STMT); - cat_t->raw_stmt.content = xstrdup("strcat(_b, _t);"); - tail->next = cat_t; - tail = cat_t; - - cur = end_brace + 1; - free(expr_str); - if (fmt) - { - free(fmt); - } - } - - // Return _b - ASTNode *ret_b = ast_create(NODE_RAW_STMT); - ret_b->raw_stmt.content = xstrdup("_b;"); - tail->next = ret_b; - tail = ret_b; - - block->block.statements = head; - return block; -} - -ASTNode *parse_primary(ParserContext *ctx, Lexer *l) -{ - ASTNode *node = NULL; - Token t = lexer_next(l); + Token t = lexer_next(l); + if (t.type != TOK_RPAREN) { + zpanic_at(t, "Expected ) after intrinsic type"); + } + } - // ** Prefixes ** + node = ast_create(NODE_REFLECTION); + node->reflection.kind = kind; + node->reflection.target_type = target; + node->type_info = + (kind == 0) ? type_new(TYPE_STRING) : type_new_ptr(type_new(TYPE_VOID)); + } - // Literals - if (t.type == TOK_INT) - { - node = ast_create(NODE_EXPR_LITERAL); - node->literal.type_kind = 0; - node->type_info = type_new(TYPE_INT); - char *s = token_strdup(t); - unsigned long long val; - if (t.len > 2 && s[0] == '0' && s[1] == 'b') - { - val = strtoull(s + 2, NULL, 2); - } - else - { - val = strtoull(s, NULL, 0); - } - node->literal.int_val = (unsigned long long)val; - free(s); - } - else if (t.type == TOK_FLOAT) - { - node = ast_create(NODE_EXPR_LITERAL); - node->literal.type_kind = 1; - node->literal.float_val = atof(t.start); - node->type_info = type_new(TYPE_F64); - } - else if (t.type == TOK_STRING) + else if (t.type == TOK_IDENT && strncmp(t.start, "match", 5) == 0 && + t.len == 5) { + ASTNode *expr = parse_expression(ctx, l); + skip_comments(l); { - node = ast_create(NODE_EXPR_LITERAL); - node->literal.type_kind = TOK_STRING; - node->literal.string_val = xmalloc(t.len); - strncpy(node->literal.string_val, t.start + 1, t.len - 2); - node->literal.string_val[t.len - 2] = 0; - node->type_info = type_new(TYPE_STRING); - } - else if (t.type == TOK_FSTRING) - { - char *inner = xmalloc(t.len); - strncpy(inner, t.start + 2, t.len - 3); - inner[t.len - 3] = 0; - node = create_fstring_block(ctx, inner); - free(inner); - } - else if (t.type == TOK_CHAR) - { - node = ast_create(NODE_EXPR_LITERAL); - node->literal.type_kind = TOK_CHAR; - node->literal.string_val = token_strdup(t); - node->type_info = type_new(TYPE_I8); + Token t = lexer_next(l); + if (t.type != TOK_LBRACE) { + zpanic_at(t, "Expected { after match expression"); + } } - else if (t.type == TOK_SIZEOF) - { - if (lexer_peek(l).type != TOK_LPAREN) - { - zpanic_at(lexer_peek(l), "Expected ( after sizeof"); - } + ASTNode *h = 0, *tl = 0; + while (1) { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) { + break; + } + if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); + } - int pos = l->pos; - int col = l->col; - int line = l->line; - Type *ty = parse_type_formal(ctx, l); + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) { + break; + } - if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); - char *ts = type_to_string(ty); - node = ast_create(NODE_EXPR_SIZEOF); - node->size_of.target_type = ts; - node->size_of.expr = NULL; - node->type_info = type_new(TYPE_USIZE); - } - else - { - l->pos = pos; - l->col = col; - l->line = line; - ASTNode *ex = parse_expression(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after sizeof identifier"); - } - node = ast_create(NODE_EXPR_SIZEOF); - node->size_of.target_type = NULL; - node->size_of.expr = ex; - node->type_info = type_new(TYPE_USIZE); - } - } + Token p = lexer_next(l); + char *pattern = token_strdup(p); + int is_default = (strcmp(pattern, "_") == 0); - else if (t.type == TOK_IDENT && strncmp(t.start, "typeof", 6) == 0 && t.len == 6) - { - if (lexer_peek(l).type != TOK_LPAREN) - { - zpanic_at(lexer_peek(l), "Expected ( after typeof"); - } + char *binding = NULL; + int is_destructure = 0; + skip_comments(l); + if (!is_default && lexer_peek(l).type == TOK_LPAREN) { lexer_next(l); - - int pos = l->pos; - int col = l->col; - int line = l->line; - Type *ty = parse_type_formal(ctx, l); - - if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); - char *ts = type_to_string(ty); - node = ast_create(NODE_TYPEOF); - node->size_of.target_type = ts; - node->size_of.expr = NULL; - } - else - { - l->pos = pos; - l->col = col; - l->line = line; - ASTNode *ex = parse_expression(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after typeof expression"); - } - node = ast_create(NODE_TYPEOF); - node->size_of.target_type = NULL; - node->size_of.expr = ex; + Token b = lexer_next(l); + if (b.type != TOK_IDENT) { + zpanic_at(b, "Expected binding name"); } - } - - else if (t.type == TOK_AT) - { - Token ident = lexer_next(l); - if (ident.type != TOK_IDENT) - { - zpanic_at(ident, "Expected intrinsic name after @"); + binding = token_strdup(b); + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); } + is_destructure = 1; + } - int kind = -1; - if (strncmp(ident.start, "type_name", 9) == 0 && ident.len == 9) - { - kind = 0; - } - else if (strncmp(ident.start, "fields", 6) == 0 && ident.len == 6) - { - kind = 1; - } - else - { - zpanic_at(ident, "Unknown intrinsic @%.*s", ident.len, ident.start); - } + ASTNode *guard = NULL; + skip_comments(l); + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "if", 2) == 0) { + lexer_next(l); + guard = parse_expression(ctx, l); + } + + skip_comments(l); + if (lexer_next(l).type != TOK_ARROW) { + zpanic_at(lexer_peek(l), "Expected '=>'"); + } + + ASTNode *body; + skip_comments(l); + Token pk = lexer_peek(l); + if (pk.type == TOK_LBRACE) { + body = parse_block(ctx, l); + } else if (pk.type == TOK_ASSERT || + (pk.type == TOK_IDENT && + strncmp(pk.start, "assert", 6) == 0)) { + body = parse_assert(ctx, l); + } else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) { + body = parse_return(ctx, l); + } else { + body = parse_expression(ctx, l); + } + + ASTNode *c = ast_create(NODE_MATCH_CASE); + c->match_case.pattern = pattern; + c->match_case.binding_name = binding; + c->match_case.is_destructuring = is_destructure; + c->match_case.guard = guard; + c->match_case.body = body; + c->match_case.is_default = is_default; + if (!h) { + h = c; + } else { + tl->next = c; + } + tl = c; + } + lexer_next(l); + node = ast_create(NODE_MATCH); + node->match_stmt.expr = expr; + node->match_stmt.cases = h; + } - { - Token t = lexer_next(l); - if (t.type != TOK_LPAREN) - { - zpanic_at(t, "Expected ( after intrinsic"); - } - } + else if (t.type == TOK_IDENT) { + if (t.len == 2 && strncmp(t.start, "fn", 2) == 0 && + lexer_peek(l).type == TOK_LPAREN) { + l->pos -= t.len; + l->col -= t.len; + return parse_lambda(ctx, l); + } - Type *target = parse_type_formal(ctx, l); + char *ident = token_strdup(t); - { - Token t = lexer_next(l); - if (t.type != TOK_RPAREN) - { - zpanic_at(t, "Expected ) after intrinsic type"); - } - } + if (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '!' && + lexer_peek(l).len == 1) { + node = parse_macro_call(ctx, l, ident); + if (node) { + free(ident); + return node; + } + } - node = ast_create(NODE_REFLECTION); - node->reflection.kind = kind; - node->reflection.target_type = target; - node->type_info = (kind == 0) ? type_new(TYPE_STRING) : type_new_ptr(type_new(TYPE_VOID)); + if (lexer_peek(l).type == TOK_ARROW) { + lexer_next(l); + return parse_arrow_lambda_single(ctx, l, ident); } - else if (t.type == TOK_IDENT && strncmp(t.start, "match", 5) == 0 && t.len == 5) - { - ASTNode *expr = parse_expression(ctx, l); - skip_comments(l); - { - Token t = lexer_next(l); - if (t.type != TOK_LBRACE) - { - zpanic_at(t, "Expected { after match expression"); - } + char *acc = ident; + while (1) { + int changed = 0; + if (lexer_peek(l).type == TOK_DCOLON) { + lexer_next(l); + Token suffix = lexer_next(l); + if (suffix.type != TOK_IDENT) { + zpanic_at(suffix, "Expected identifier after ::"); + } + + SelectiveImport *si = (!ctx->current_module_prefix) + ? find_selective_import(ctx, acc) + : NULL; + if (si) { + char *tmp = xmalloc(strlen(si->source_module) + strlen(si->symbol) + + suffix.len + 3); + sprintf(tmp, "%s_%s_", si->source_module, si->symbol); + strncat(tmp, suffix.start, suffix.len); + free(acc); + acc = tmp; + } else { + Module *mod = find_module(ctx, acc); + if (mod) { + if (mod->is_c_header) { + char *tmp = xmalloc(suffix.len + 1); + strncpy(tmp, suffix.start, suffix.len); + tmp[suffix.len] = 0; + free(acc); + acc = tmp; + } + + else { + char *tmp = xmalloc(strlen(mod->base_name) + suffix.len + 2); + sprintf(tmp, "%s_", mod->base_name); + strncat(tmp, suffix.start, suffix.len); + free(acc); + acc = tmp; + } + } else { + char *tmp = xmalloc(strlen(acc) + suffix.len + 2); + sprintf(tmp, "%s_", acc); + strncat(tmp, suffix.start, suffix.len); + free(acc); + acc = tmp; + } } + changed = 1; + } - ASTNode *h = 0, *tl = 0; - while (1) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - break; - } - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - break; - } - - Token p = lexer_next(l); - char *pattern = token_strdup(p); - int is_default = (strcmp(pattern, "_") == 0); - - char *binding = NULL; - int is_destructure = 0; - skip_comments(l); - if (!is_default && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - Token b = lexer_next(l); - if (b.type != TOK_IDENT) - { - zpanic_at(b, "Expected binding name"); - } - binding = token_strdup(b); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - is_destructure = 1; - } - - ASTNode *guard = NULL; - skip_comments(l); - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0) - { - lexer_next(l); - guard = parse_expression(ctx, l); - } - - skip_comments(l); - if (lexer_next(l).type != TOK_ARROW) - { - zpanic_at(lexer_peek(l), "Expected '=>'"); - } - - ASTNode *body; - skip_comments(l); - Token pk = lexer_peek(l); - if (pk.type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else if (pk.type == TOK_ASSERT || - (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0)) - { - body = parse_assert(ctx, l); - } - else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) - { - body = parse_return(ctx, l); - } - else - { - body = parse_expression(ctx, l); - } + if (lexer_peek(l).type == TOK_LANGLE) { + Lexer lookahead = *l; + lexer_next(&lookahead); + parse_type(ctx, &lookahead); + if (lexer_peek(&lookahead).type == TOK_RANGLE) { + lexer_next(l); + char *concrete_type = parse_type(ctx, l); + lexer_next(l); + + int is_struct = 0; + GenericTemplate *st = ctx->templates; + while (st) { + if (strcmp(st->name, acc) == 0) { + is_struct = 1; + break; + } + st = st->next; + } + if (!is_struct && + (strcmp(acc, "Result") == 0 || strcmp(acc, "Option") == 0)) { + is_struct = 1; + } + + if (is_struct) { + instantiate_generic(ctx, acc, concrete_type, t); + + char *clean_type = sanitize_mangled_name(concrete_type); + + char *m = xmalloc(strlen(acc) + strlen(clean_type) + 2); + sprintf(m, "%s_%s", acc, clean_type); + free(clean_type); - ASTNode *c = ast_create(NODE_MATCH_CASE); - c->match_case.pattern = pattern; - c->match_case.binding_name = binding; - c->match_case.is_destructuring = is_destructure; - c->match_case.guard = guard; - c->match_case.body = body; - c->match_case.is_default = is_default; - if (!h) - { - h = c; - } - else - { - tl->next = c; - } - tl = c; + free(acc); + acc = m; + } else { + char *m = instantiate_function_template(ctx, acc, concrete_type); + if (m) { + free(acc); + acc = m; + } else { + zpanic_at(t, "Unknown generic %s", acc); + } + } + changed = 1; + } + } + if (!changed) { + break; + } + } + + if (lexer_peek(l).type == TOK_LBRACE) { + int is_struct_init = 0; + Lexer pl = *l; + lexer_next(&pl); + Token fi = lexer_peek(&pl); + + if (fi.type == TOK_RBRACE) { + is_struct_init = 1; + } else if (fi.type == TOK_IDENT) { + lexer_next(&pl); + if (lexer_peek(&pl).type == TOK_COLON) { + is_struct_init = 1; + } + } + if (is_struct_init) { + char *struct_name = acc; + if (!ctx->current_module_prefix) { + SelectiveImport *si = find_selective_import(ctx, acc); + if (si) { + struct_name = + xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2); + sprintf(struct_name, "%s_%s", si->source_module, si->symbol); + } + } + if (struct_name == acc && ctx->current_module_prefix && + !is_known_generic(ctx, acc)) { + char *prefixed = + xmalloc(strlen(ctx->current_module_prefix) + strlen(acc) + 2); + sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc); + struct_name = prefixed; } lexer_next(l); - node = ast_create(NODE_MATCH); - node->match_stmt.expr = expr; - node->match_stmt.cases = h; - } - - else if (t.type == TOK_IDENT) - { - if (t.len == 2 && strncmp(t.start, "fn", 2) == 0 && lexer_peek(l).type == TOK_LPAREN) - { - l->pos -= t.len; - l->col -= t.len; - return parse_lambda(ctx, l); - } - - char *ident = token_strdup(t); - - if (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '!' && lexer_peek(l).len == 1) - { - node = parse_macro_call(ctx, l, ident); - if (node) - { - free(ident); - return node; - } - } + node = ast_create(NODE_EXPR_STRUCT_INIT); + node->struct_init.struct_name = struct_name; + Type *init_type = type_new(TYPE_STRUCT); + init_type->name = xstrdup(struct_name); + node->type_info = init_type; - if (lexer_peek(l).type == TOK_ARROW) - { + ASTNode *head = NULL, *tail = NULL; + int first = 1; + while (lexer_peek(l).type != TOK_RBRACE) { + if (!first && lexer_peek(l).type == TOK_COMMA) { lexer_next(l); - return parse_arrow_lambda_single(ctx, l, ident); - } - - char *acc = ident; - while (1) - { - int changed = 0; - if (lexer_peek(l).type == TOK_DCOLON) - { - lexer_next(l); - Token suffix = lexer_next(l); - if (suffix.type != TOK_IDENT) - { - zpanic_at(suffix, "Expected identifier after ::"); - } - - SelectiveImport *si = - (!ctx->current_module_prefix) ? find_selective_import(ctx, acc) : NULL; - if (si) - { - char *tmp = - xmalloc(strlen(si->source_module) + strlen(si->symbol) + suffix.len + 3); - sprintf(tmp, "%s_%s_", si->source_module, si->symbol); - strncat(tmp, suffix.start, suffix.len); - free(acc); - acc = tmp; - } - else - { - Module *mod = find_module(ctx, acc); - if (mod) - { - if (mod->is_c_header) - { - char *tmp = xmalloc(suffix.len + 1); - strncpy(tmp, suffix.start, suffix.len); - tmp[suffix.len] = 0; - free(acc); - acc = tmp; - } - - else - { - char *tmp = xmalloc(strlen(mod->base_name) + suffix.len + 2); - sprintf(tmp, "%s_", mod->base_name); - strncat(tmp, suffix.start, suffix.len); - free(acc); - acc = tmp; - } - } - else - { - char *tmp = xmalloc(strlen(acc) + suffix.len + 2); - sprintf(tmp, "%s_", acc); - strncat(tmp, suffix.start, suffix.len); - free(acc); - acc = tmp; - } - } - changed = 1; - } - - if (lexer_peek(l).type == TOK_LANGLE) - { - Lexer lookahead = *l; - lexer_next(&lookahead); - parse_type(ctx, &lookahead); - if (lexer_peek(&lookahead).type == TOK_RANGLE) - { - lexer_next(l); - char *concrete_type = parse_type(ctx, l); - lexer_next(l); - - int is_struct = 0; - GenericTemplate *st = ctx->templates; - while (st) - { - if (strcmp(st->name, acc) == 0) - { - is_struct = 1; - break; - } - st = st->next; - } - if (!is_struct && (strcmp(acc, "Result") == 0 || strcmp(acc, "Option") == 0)) - { - is_struct = 1; - } - - if (is_struct) - { - instantiate_generic(ctx, acc, concrete_type, t); - - char *clean_type = sanitize_mangled_name(concrete_type); - - char *m = xmalloc(strlen(acc) + strlen(clean_type) + 2); - sprintf(m, "%s_%s", acc, clean_type); - free(clean_type); - - free(acc); - acc = m; - } - else - { - char *m = instantiate_function_template(ctx, acc, concrete_type); - if (m) - { - free(acc); - acc = m; - } - else - { - zpanic_at(t, "Unknown generic %s", acc); - } - } - changed = 1; - } - } - if (!changed) - { - break; - } - } - - if (lexer_peek(l).type == TOK_LBRACE) - { - int is_struct_init = 0; - Lexer pl = *l; - lexer_next(&pl); - Token fi = lexer_peek(&pl); - - if (fi.type == TOK_RBRACE) - { - is_struct_init = 1; - } - else if (fi.type == TOK_IDENT) - { - lexer_next(&pl); - if (lexer_peek(&pl).type == TOK_COLON) - { - is_struct_init = 1; - } - } - if (is_struct_init) - { - char *struct_name = acc; - if (!ctx->current_module_prefix) - { - SelectiveImport *si = find_selective_import(ctx, acc); - if (si) - { - struct_name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2); - sprintf(struct_name, "%s_%s", si->source_module, si->symbol); - } - } - if (struct_name == acc && ctx->current_module_prefix && !is_known_generic(ctx, acc)) - { - char *prefixed = xmalloc(strlen(ctx->current_module_prefix) + strlen(acc) + 2); - sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc); - struct_name = prefixed; - } - lexer_next(l); - node = ast_create(NODE_EXPR_STRUCT_INIT); - node->struct_init.struct_name = struct_name; - Type *init_type = type_new(TYPE_STRUCT); - init_type->name = xstrdup(struct_name); - node->type_info = init_type; - - ASTNode *head = NULL, *tail = NULL; - int first = 1; - while (lexer_peek(l).type != TOK_RBRACE) - { - if (!first && lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - if (lexer_peek(l).type == TOK_RBRACE) - { - break; - } - Token fn = lexer_next(l); - if (lexer_next(l).type != TOK_COLON) - { - zpanic_at(lexer_peek(l), "Expected :"); - } - ASTNode *val = parse_expression(ctx, l); - ASTNode *assign = ast_create(NODE_VAR_DECL); - assign->var_decl.name = token_strdup(fn); - assign->var_decl.init_expr = val; - if (!head) - { - head = assign; - } - else - { - tail->next = assign; - } - tail = assign; - first = 0; - } - lexer_next(l); - node->struct_init.fields = head; - Type *st = type_new(TYPE_STRUCT); - st->name = xstrdup(struct_name); - node->type_info = st; - return node; // Struct init cannot be called/indexed usually, return - // early - } - } - - // E. readln(args...) Magic - FuncSig *sig = find_func(ctx, acc); - if (strcmp(acc, "readln") == 0 && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // eat ( - - // Parse args - ASTNode *args[16]; - int ac = 0; - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - args[ac++] = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - - if (ac == 0) - { - // readln() -> _z_readln_raw() - node = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup("_z_readln_raw"); - node->call.callee = callee; - node->type_info = type_new(TYPE_STRING); - } - else - { - // readln(vars...) -> _z_scan_helper("fmt", &vars...) - char fmt[256]; - fmt[0] = 0; - for (int i = 0; i < ac; i++) - { - Type *t = args[i]->type_info; - if (!t && args[i]->type == NODE_EXPR_VAR) - { - t = find_symbol_type_info(ctx, args[i]->var_ref.name); - } - - if (!t) - { - strcat(fmt, "%d"); // Fallback - } - else - { - if (t->kind == TYPE_INT || t->kind == TYPE_I32 || t->kind == TYPE_BOOL) - { - strcat(fmt, "%d"); - } - else if (t->kind == TYPE_F64) - { - strcat(fmt, "%lf"); - } - else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) - { - strcat(fmt, "%f"); - } - else if (t->kind == TYPE_STRING) - { - strcat(fmt, "%s"); - } - else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 || - t->kind == TYPE_BYTE) - { - strcat(fmt, " %c"); // Space skip whitespace - } - else - { - strcat(fmt, "%d"); - } - } - if (i < ac - 1) - { - strcat(fmt, " "); - } - } - - node = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup("_z_scan_helper"); - node->call.callee = callee; - node->type_info = type_new(TYPE_INT); // Returns count - - ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL); - fmt_node->literal.type_kind = 2; // string - fmt_node->literal.string_val = xstrdup(fmt); - - ASTNode *head = fmt_node, *tail = fmt_node; - - for (int i = 0; i < ac; i++) - { - // Create Unary & (AddressOf) node wrapping the arg - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = xstrdup("&"); - addr->unary.operand = args[i]; - // Link - tail->next = addr; - tail = addr; - } - node->call.args = head; - } - free(acc); + } + if (lexer_peek(l).type == TOK_RBRACE) { + break; + } + Token fn = lexer_next(l); + if (lexer_next(l).type != TOK_COLON) { + zpanic_at(lexer_peek(l), "Expected :"); + } + ASTNode *val = parse_expression(ctx, l); + ASTNode *assign = ast_create(NODE_VAR_DECL); + assign->var_decl.name = token_strdup(fn); + assign->var_decl.init_expr = val; + if (!head) { + head = assign; + } else { + tail->next = assign; + } + tail = assign; + first = 0; } - else if (sig && lexer_peek(l).type == TOK_LPAREN) - { + lexer_next(l); + node->struct_init.fields = head; + Type *st = type_new(TYPE_STRUCT); + st->name = xstrdup(struct_name); + node->type_info = st; + return node; // Struct init cannot be called/indexed usually, return + // early + } + } + + // E. readln(args...) Magic + FuncSig *sig = find_func(ctx, acc); + if (strcmp(acc, "readln") == 0 && lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // eat ( + + // Parse args + ASTNode *args[16]; + int ac = 0; + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + args[ac++] = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); - ASTNode *head = NULL, *tail = NULL; - int args_provided = 0; - char **arg_names = NULL; - int has_named = 0; - - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - char *arg_name = NULL; - - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) - { - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); - lexer_next(l); - } - } - - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - args_provided++; - - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; - - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - for (int i = args_provided; i < sig->total_args; i++) - { - if (sig->defaults[i]) - { - ASTNode *def = ast_create(NODE_RAW_STMT); - def->raw_stmt.content = xstrdup(sig->defaults[i]); - if (!head) - { - head = def; - } - else - { - tail->next = def; - } - tail = def; - } - } - node = ast_create(NODE_EXPR_CALL); - node->token = t; // Set source token - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = acc; - node->call.callee = callee; - node->call.args = head; - node->call.arg_names = has_named ? arg_names : NULL; - node->call.arg_count = args_provided; - if (sig) - { - node->definition_token = sig->decl_token; - } - if (sig->is_async) - { - Type *async_type = type_new(TYPE_STRUCT); - async_type->name = xstrdup("Async"); - node->type_info = async_type; - node->resolved_type = xstrdup("Async"); - } - else if (sig->ret_type) - { - node->type_info = sig->ret_type; - node->resolved_type = type_to_string(sig->ret_type); - } - else - { - node->resolved_type = xstrdup("void"); - } - } - else if (!sig && !find_symbol_entry(ctx, acc) && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // eat ( - ASTNode *head = NULL, *tail = NULL; - char **arg_names = NULL; - int args_provided = 0; - int has_named = 0; - - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - char *arg_name = NULL; - - // Check for named argument: name: value - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) - { - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); - lexer_next(l); - } - } - - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - args_provided++; - - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - - node = ast_create(NODE_EXPR_CALL); - node->token = t; - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = acc; - node->call.callee = callee; - node->call.args = head; - node->call.arg_names = has_named ? arg_names : NULL; - node->call.arg_count = args_provided; - // Unknown return type - let codegen infer it - node->resolved_type = xstrdup("unknown"); - // Fall through to Postfix - } - else - { - node = ast_create(NODE_EXPR_VAR); - node->token = t; // Set source token - node->var_ref.name = acc; - node->type_info = find_symbol_type_info(ctx, acc); - - Symbol *sym = find_symbol_entry(ctx, acc); - if (sym) - { - sym->is_used = 1; - node->definition_token = sym->decl_token; - } - - char *type_str = find_symbol_type(ctx, acc); - - if (type_str) - { - node->resolved_type = type_str; - node->var_ref.suggestion = NULL; - } - else - { - node->resolved_type = xstrdup("unknown"); - if (should_suppress_undef_warning(ctx, acc)) - { - node->var_ref.suggestion = NULL; - } - else - { - node->var_ref.suggestion = find_similar_symbol(ctx, acc); - } - } + } else { + break; + } } - } - - else if (t.type == TOK_LPAREN) - { + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } - Lexer lookahead = *l; - int is_lambda = 0; - char **params = xmalloc(sizeof(char *) * 16); - int nparams = 0; - - while (1) - { - if (lexer_peek(&lookahead).type != TOK_IDENT) - { - break; - } - params[nparams++] = token_strdup(lexer_next(&lookahead)); - Token sep = lexer_peek(&lookahead); - if (sep.type == TOK_COMMA) - { - lexer_next(&lookahead); - continue; - } - else if (sep.type == TOK_RPAREN) - { - lexer_next(&lookahead); - if (lexer_peek(&lookahead).type == TOK_ARROW) - { - lexer_next(&lookahead); - is_lambda = 1; - } - break; - } - else - { - break; - } + if (ac == 0) { + // readln() -> _z_readln_raw() + node = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup("_z_readln_raw"); + node->call.callee = callee; + node->type_info = type_new(TYPE_STRING); + } else { + // readln(vars...) -> _z_scan_helper("fmt", &vars...) + char fmt[256]; + fmt[0] = 0; + for (int i = 0; i < ac; i++) { + Type *t = args[i]->type_info; + if (!t && args[i]->type == NODE_EXPR_VAR) { + t = find_symbol_type_info(ctx, args[i]->var_ref.name); + } + + if (!t) { + strcat(fmt, "%d"); // Fallback + } else { + if (t->kind == TYPE_INT || t->kind == TYPE_I32 || + t->kind == TYPE_BOOL) { + strcat(fmt, "%d"); + } else if (t->kind == TYPE_F64) { + strcat(fmt, "%lf"); + } else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) { + strcat(fmt, "%f"); + } else if (t->kind == TYPE_STRING) { + strcat(fmt, "%s"); + } else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || + t->kind == TYPE_U8 || t->kind == TYPE_BYTE) { + strcat(fmt, " %c"); // Space skip whitespace + } else { + strcat(fmt, "%d"); + } + } + if (i < ac - 1) { + strcat(fmt, " "); + } + } + + node = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup("_z_scan_helper"); + node->call.callee = callee; + node->type_info = type_new(TYPE_INT); // Returns count + + ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL); + fmt_node->literal.type_kind = 2; // string + fmt_node->literal.string_val = xstrdup(fmt); + + ASTNode *head = fmt_node, *tail = fmt_node; + + for (int i = 0; i < ac; i++) { + // Create Unary & (AddressOf) node wrapping the arg + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = xstrdup("&"); + addr->unary.operand = args[i]; + // Link + tail->next = addr; + tail = addr; + } + node->call.args = head; + } + free(acc); + } else if (sig && lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); + ASTNode *head = NULL, *tail = NULL; + int args_provided = 0; + char **arg_names = NULL; + int has_named = 0; + + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + char *arg_name = NULL; + + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) { + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); + lexer_next(l); + } + } + + ASTNode *arg = parse_expression(ctx, l); + if (!head) { + head = arg; + } else { + tail->next = arg; + } + tail = arg; + args_provided++; + + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; + + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; + + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } + for (int i = args_provided; i < sig->total_args; i++) { + if (sig->defaults[i]) { + ASTNode *def = ast_create(NODE_RAW_STMT); + def->raw_stmt.content = xstrdup(sig->defaults[i]); + if (!head) { + head = def; + } else { + tail->next = def; + } + tail = def; + } + } + node = ast_create(NODE_EXPR_CALL); + node->token = t; // Set source token + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = acc; + node->call.callee = callee; + node->call.args = head; + node->call.arg_names = has_named ? arg_names : NULL; + node->call.arg_count = args_provided; + if (sig) { + node->definition_token = sig->decl_token; + } + if (sig->is_async) { + Type *async_type = type_new(TYPE_STRUCT); + async_type->name = xstrdup("Async"); + node->type_info = async_type; + node->resolved_type = xstrdup("Async"); + } else if (sig->ret_type) { + node->type_info = sig->ret_type; + node->resolved_type = type_to_string(sig->ret_type); + } else { + node->resolved_type = xstrdup("void"); + } + } else if (!sig && !find_symbol_entry(ctx, acc) && + lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // eat ( + ASTNode *head = NULL, *tail = NULL; + char **arg_names = NULL; + int args_provided = 0; + int has_named = 0; + + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + char *arg_name = NULL; + + // Check for named argument: name: value + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) { + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); + lexer_next(l); + } + } + + ASTNode *arg = parse_expression(ctx, l); + if (!head) { + head = arg; + } else { + tail->next = arg; + } + tail = arg; + args_provided++; + + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; + + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } + + node = ast_create(NODE_EXPR_CALL); + node->token = t; + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = acc; + node->call.callee = callee; + node->call.args = head; + node->call.arg_names = has_named ? arg_names : NULL; + node->call.arg_count = args_provided; + // Unknown return type - let codegen infer it + node->resolved_type = xstrdup("unknown"); + // Fall through to Postfix + } else { + node = ast_create(NODE_EXPR_VAR); + node->token = t; // Set source token + node->var_ref.name = acc; + node->type_info = find_symbol_type_info(ctx, acc); + + Symbol *sym = find_symbol_entry(ctx, acc); + if (sym) { + sym->is_used = 1; + node->definition_token = sym->decl_token; + } + + char *type_str = find_symbol_type(ctx, acc); + + if (type_str) { + node->resolved_type = type_str; + node->var_ref.suggestion = NULL; + } else { + node->resolved_type = xstrdup("unknown"); + if (should_suppress_undef_warning(ctx, acc)) { + node->var_ref.suggestion = NULL; + } else { + node->var_ref.suggestion = find_similar_symbol(ctx, acc); + } + } + } + } + + else if (t.type == TOK_LPAREN) { + + Lexer lookahead = *l; + int is_lambda = 0; + char **params = xmalloc(sizeof(char *) * 16); + int nparams = 0; + + while (1) { + if (lexer_peek(&lookahead).type != TOK_IDENT) { + break; + } + params[nparams++] = token_strdup(lexer_next(&lookahead)); + Token sep = lexer_peek(&lookahead); + if (sep.type == TOK_COMMA) { + lexer_next(&lookahead); + continue; + } else if (sep.type == TOK_RPAREN) { + lexer_next(&lookahead); + if (lexer_peek(&lookahead).type == TOK_ARROW) { + lexer_next(&lookahead); + is_lambda = 1; } - - if (is_lambda && nparams > 0) - { - *l = lookahead; // Commit - return parse_arrow_lambda_multi(ctx, l, params, nparams); - } - - int saved = l->pos; - if (lexer_peek(l).type == TOK_IDENT) - { - Lexer cast_look = *l; - lexer_next(&cast_look); // eat ident - while (lexer_peek(&cast_look).type == TOK_DCOLON) - { // handle A::B - lexer_next(&cast_look); - if (lexer_peek(&cast_look).type == TOK_IDENT) - { - lexer_next(&cast_look); - } - else - { - break; - } - } - while (lexer_peek(&cast_look).type == TOK_OP && is_token(lexer_peek(&cast_look), "*")) - { - lexer_next(&cast_look); - } - - if (lexer_peek(&cast_look).type == TOK_RPAREN) - { - lexer_next(&cast_look); // eat ) - Token next = lexer_peek(&cast_look); - // Heuristic: It's a cast if followed by literal, ident, paren, or &/* - if (next.type == TOK_STRING || next.type == TOK_INT || next.type == TOK_FLOAT || - (next.type == TOK_OP && - (is_token(next, "&") || is_token(next, "*") || is_token(next, "!"))) || - next.type == TOK_IDENT || next.type == TOK_LPAREN) - { - - Type *cast_type_obj = parse_type_formal(ctx, l); - char *cast_type = type_to_string(cast_type_obj); - { - Token t = lexer_next(l); - if (t.type != TOK_RPAREN) - { - zpanic_at(t, "Expected ) after cast"); - } - } - ASTNode *target = parse_expr_prec(ctx, l, PREC_UNARY); - - node = ast_create(NODE_EXPR_CAST); - node->cast.target_type = cast_type; - node->cast.expr = target; - node->type_info = cast_type_obj; - return node; // Casts are usually unary, handled here. - } + break; + } else { + break; + } + } + + if (is_lambda && nparams > 0) { + *l = lookahead; // Commit + return parse_arrow_lambda_multi(ctx, l, params, nparams); + } + + int saved = l->pos; + if (lexer_peek(l).type == TOK_IDENT) { + Lexer cast_look = *l; + lexer_next(&cast_look); // eat ident + while (lexer_peek(&cast_look).type == TOK_DCOLON) { // handle A::B + lexer_next(&cast_look); + if (lexer_peek(&cast_look).type == TOK_IDENT) { + lexer_next(&cast_look); + } else { + break; + } + } + while (lexer_peek(&cast_look).type == TOK_OP && + is_token(lexer_peek(&cast_look), "*")) { + lexer_next(&cast_look); + } + + if (lexer_peek(&cast_look).type == TOK_RPAREN) { + lexer_next(&cast_look); // eat ) + Token next = lexer_peek(&cast_look); + // Heuristic: It's a cast if followed by literal, ident, paren, or &/* + if (next.type == TOK_STRING || next.type == TOK_INT || + next.type == TOK_FLOAT || + (next.type == TOK_OP && + (is_token(next, "&") || is_token(next, "*") || + is_token(next, "!"))) || + next.type == TOK_IDENT || next.type == TOK_LPAREN) { + + Type *cast_type_obj = parse_type_formal(ctx, l); + char *cast_type = type_to_string(cast_type_obj); + { + Token t = lexer_next(l); + if (t.type != TOK_RPAREN) { + zpanic_at(t, "Expected ) after cast"); } - } - l->pos = saved; // Reset if not a cast + } + ASTNode *target = parse_expr_prec(ctx, l, PREC_UNARY); - ASTNode *expr = parse_expression(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); + node = ast_create(NODE_EXPR_CAST); + node->cast.target_type = cast_type; + node->cast.expr = target; + node->type_info = cast_type_obj; + return node; // Casts are usually unary, handled here. } - node = expr; + } } + l->pos = saved; // Reset if not a cast - else if (t.type == TOK_LBRACKET) - { - ASTNode *head = NULL, *tail = NULL; - int count = 0; - while (lexer_peek(l).type != TOK_RBRACKET) - { - ASTNode *elem = parse_expression(ctx, l); - count++; - if (!head) - { - head = elem; - tail = elem; - } - else - { - tail->next = elem; - tail = elem; - } - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - if (lexer_next(l).type != TOK_RBRACKET) - { - zpanic_at(lexer_peek(l), "Expected ] after array literal"); - } - node = ast_create(NODE_EXPR_ARRAY_LITERAL); - node->array_literal.elements = head; - node->array_literal.count = count; - if (head && head->type_info) - { - Type *elem_type = head->type_info; - Type *arr_type = type_new(TYPE_ARRAY); - arr_type->inner = elem_type; - arr_type->array_size = count; - node->type_info = arr_type; - } + ASTNode *expr = parse_expression(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); } - else - { - zpanic_at(t, "Unexpected token in parse_primary: %.*s", t.len, t.start); - } - - while (1) - { - if (lexer_peek(l).type == TOK_LPAREN) - { - Token op = lexer_next(l); // consume '(' - ASTNode *head = NULL, *tail = NULL; - char **arg_names = NULL; - int arg_count = 0; - int has_named = 0; - - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - char *arg_name = NULL; - - // Check for named argument: IDENT : expr - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) - { - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); // eat IDENT - lexer_next(l); // eat : - } - } - - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - - arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *)); - arg_names[arg_count] = arg_name; - arg_count++; - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - { - Token t = lexer_next(l); - if (t.type != TOK_RPAREN) - { - zpanic_at(t, "Expected ) after call arguments"); - } - } - - ASTNode *call = ast_create(NODE_EXPR_CALL); - call->call.callee = node; - call->call.args = head; - call->call.arg_names = has_named ? arg_names : NULL; - call->call.arg_count = arg_count; - check_format_string(call, op); - - // Try to infer type if callee has function type info - call->resolved_type = xstrdup("unknown"); // Default (was int) - if (node->type_info && node->type_info->kind == TYPE_FUNCTION && node->type_info->inner) - { - call->type_info = node->type_info->inner; - - // Update resolved_type based on real return - // (Optional: type_to_string(call->type_info)) - } - node = call; - } - - else if (lexer_peek(l).type == TOK_LBRACKET) - { - Token bracket = lexer_next(l); // consume '[' - ASTNode *index = parse_expression(ctx, l); - { - Token t = lexer_next(l); - if (t.type != TOK_RBRACKET) - { - zpanic_at(t, "Expected ] after index"); - } - } - - // Static Array Bounds Check - if (node->type_info && node->type_info->kind == TYPE_ARRAY && - node->type_info->array_size > 0) - { - if (index->type == NODE_EXPR_LITERAL && index->literal.type_kind == 0) - { - int idx = index->literal.int_val; - if (idx < 0 || idx >= node->type_info->array_size) - { - warn_array_bounds(bracket, idx, node->type_info->array_size); - } - } - } - - int overloaded_get = 0; - if (node->type_info && node->type_info->kind != TYPE_ARRAY && - (node->type_info->kind == TYPE_STRUCT || - (node->type_info->kind == TYPE_POINTER && node->type_info->inner && - node->type_info->inner->kind == TYPE_STRUCT))) - { - Type *st = node->type_info; - char *struct_name = (st->kind == TYPE_STRUCT) ? st->name : st->inner->name; - int is_ptr = (st->kind == TYPE_POINTER); - - char mangled[256]; - sprintf(mangled, "%s_get", struct_name); - FuncSig *sig = find_func(ctx, mangled); - if (sig) - { - // Rewrite to Call: node.get(index) - ASTNode *call = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup(mangled); - call->call.callee = callee; - - // Arg 1: Self - ASTNode *arg1 = node; - if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && !is_ptr) - { - // Needs ptr, have value -> &node - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = xstrdup("&"); - addr->unary.operand = node; - addr->type_info = type_new_ptr(st); - arg1 = addr; - } - else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) - { - // Needs value, have ptr -> *node - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = node; - arg1 = deref; - } - - // Arg 2: Index - arg1->next = index; - index->next = NULL; - call->call.args = arg1; - - call->type_info = sig->ret_type; - call->resolved_type = type_to_string(sig->ret_type); - - node = call; - overloaded_get = 1; - } - } + node = expr; + } - if (!overloaded_get) - { - ASTNode *idx_node = ast_create(NODE_EXPR_INDEX); - idx_node->index.array = node; - idx_node->index.index = index; - idx_node->type_info = (node->type_info && node->type_info->inner) - ? node->type_info->inner - : type_new(TYPE_INT); - node = idx_node; - } - } - - else - { + else if (t.type == TOK_LBRACKET) { + ASTNode *head = NULL, *tail = NULL; + int count = 0; + while (lexer_peek(l).type != TOK_RBRACKET) { + ASTNode *elem = parse_expression(ctx, l); + count++; + if (!head) { + head = elem; + tail = elem; + } else { + tail->next = elem; + tail = elem; + } + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + if (lexer_next(l).type != TOK_RBRACKET) { + zpanic_at(lexer_peek(l), "Expected ] after array literal"); + } + node = ast_create(NODE_EXPR_ARRAY_LITERAL); + node->array_literal.elements = head; + node->array_literal.count = count; + if (head && head->type_info) { + Type *elem_type = head->type_info; + Type *arr_type = type_new(TYPE_ARRAY); + arr_type->inner = elem_type; + arr_type->array_size = count; + node->type_info = arr_type; + } + } else { + zpanic_at(t, "Unexpected token in parse_primary: %.*s", t.len, t.start); + } + + while (1) { + if (lexer_peek(l).type == TOK_LPAREN) { + Token op = lexer_next(l); // consume '(' + ASTNode *head = NULL, *tail = NULL; + char **arg_names = NULL; + int arg_count = 0; + int has_named = 0; + + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + char *arg_name = NULL; + + // Check for named argument: IDENT : expr + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) { + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); // eat IDENT + lexer_next(l); // eat : + } + } + + ASTNode *arg = parse_expression(ctx, l); + if (!head) { + head = arg; + } else { + tail->next = arg; + } + tail = arg; + + arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *)); + arg_names[arg_count] = arg_name; + arg_count++; + + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { break; - } - } - - return node; + } + } + } + { + Token t = lexer_next(l); + if (t.type != TOK_RPAREN) { + zpanic_at(t, "Expected ) after call arguments"); + } + } + + ASTNode *call = ast_create(NODE_EXPR_CALL); + call->call.callee = node; + call->call.args = head; + call->call.arg_names = has_named ? arg_names : NULL; + call->call.arg_count = arg_count; + check_format_string(call, op); + + // Try to infer type if callee has function type info + call->resolved_type = xstrdup("unknown"); // Default (was int) + if (node->type_info && node->type_info->kind == TYPE_FUNCTION && + node->type_info->inner) { + call->type_info = node->type_info->inner; + + // Update resolved_type based on real return + // (Optional: type_to_string(call->type_info)) + } + node = call; + } + + else if (lexer_peek(l).type == TOK_LBRACKET) { + Token bracket = lexer_next(l); // consume '[' + ASTNode *index = parse_expression(ctx, l); + { + Token t = lexer_next(l); + if (t.type != TOK_RBRACKET) { + zpanic_at(t, "Expected ] after index"); + } + } + + // Static Array Bounds Check + if (node->type_info && node->type_info->kind == TYPE_ARRAY && + node->type_info->array_size > 0) { + if (index->type == NODE_EXPR_LITERAL && index->literal.type_kind == 0) { + int idx = index->literal.int_val; + if (idx < 0 || idx >= node->type_info->array_size) { + warn_array_bounds(bracket, idx, node->type_info->array_size); + } + } + } + + int overloaded_get = 0; + if (node->type_info && node->type_info->kind != TYPE_ARRAY && + (node->type_info->kind == TYPE_STRUCT || + (node->type_info->kind == TYPE_POINTER && node->type_info->inner && + node->type_info->inner->kind == TYPE_STRUCT))) { + Type *st = node->type_info; + char *struct_name = + (st->kind == TYPE_STRUCT) ? st->name : st->inner->name; + int is_ptr = (st->kind == TYPE_POINTER); + + char mangled[256]; + sprintf(mangled, "%s_get", struct_name); + FuncSig *sig = find_func(ctx, mangled); + if (sig) { + // Rewrite to Call: node.get(index) + ASTNode *call = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup(mangled); + call->call.callee = callee; + + // Arg 1: Self + ASTNode *arg1 = node; + if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && + !is_ptr) { + // Needs ptr, have value -> &node + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = xstrdup("&"); + addr->unary.operand = node; + addr->type_info = type_new_ptr(st); + arg1 = addr; + } else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) { + // Needs value, have ptr -> *node + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = node; + arg1 = deref; + } + + // Arg 2: Index + arg1->next = index; + index->next = NULL; + call->call.args = arg1; + + call->type_info = sig->ret_type; + call->resolved_type = type_to_string(sig->ret_type); + + node = call; + overloaded_get = 1; + } + } + + if (!overloaded_get) { + ASTNode *idx_node = ast_create(NODE_EXPR_INDEX); + idx_node->index.array = node; + idx_node->index.index = index; + idx_node->type_info = (node->type_info && node->type_info->inner) + ? node->type_info->inner + : type_new(TYPE_INT); + node = idx_node; + } + } + + else { + break; + } + } + + return node; } -int is_comparison_op(const char *op) -{ - return (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 || - strcmp(op, ">") == 0 || strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0); +int is_comparison_op(const char *op) { + return (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 || + strcmp(op, "<") == 0 || strcmp(op, ">") == 0 || + strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0); } -Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name) -{ - if (!struct_type) - { - return NULL; - } - - // Built-in Fields for Arrays/Slices - if (struct_type->kind == TYPE_ARRAY) - { - if (strcmp(field_name, "len") == 0) - { - return type_new(TYPE_INT); - } - if (struct_type->array_size == 0) - { // Slice - if (strcmp(field_name, "cap") == 0) - { - return type_new(TYPE_INT); - } - if (strcmp(field_name, "data") == 0) - { - return type_new_ptr(struct_type->inner); - } - } - } - - char *sname = struct_type->name; - // Handle Pointers (User* -> User) - if (struct_type->kind == TYPE_POINTER && struct_type->inner) - { - sname = struct_type->inner->name; - } - if (!sname) - { - return NULL; - } - - ASTNode *def = find_struct_def(ctx, sname); - if (!def) - { - return NULL; - } - - ASTNode *f = def->strct.fields; - while (f) - { - if (strcmp(f->field.name, field_name) == 0) - { - return f->type_info; - } - f = f->next; - } +Type *get_field_type(ParserContext *ctx, Type *struct_type, + const char *field_name) { + if (!struct_type) { return NULL; -} - -const char *get_operator_method(const char *op) -{ - // Arithmetic - if (strcmp(op, "+") == 0) - { - return "add"; - } - if (strcmp(op, "-") == 0) - { - return "sub"; - } - if (strcmp(op, "*") == 0) - { - return "mul"; - } - if (strcmp(op, "/") == 0) - { - return "div"; - } - if (strcmp(op, "%") == 0) - { - return "rem"; - } - - // Comparison - if (strcmp(op, "==") == 0) - { - return "eq"; - } - if (strcmp(op, "!=") == 0) - { - return "neq"; - } - if (strcmp(op, "<") == 0) - { - return "lt"; - } - if (strcmp(op, ">") == 0) - { - return "gt"; - } - if (strcmp(op, "<=") == 0) - { - return "le"; - } - if (strcmp(op, ">=") == 0) - { - return "ge"; - } - - // --- NEW: Bitwise --- - if (strcmp(op, "&") == 0) - { - return "bitand"; - } - if (strcmp(op, "|") == 0) - { - return "bitor"; - } - if (strcmp(op, "^") == 0) - { - return "bitxor"; - } - if (strcmp(op, "<<") == 0) - { - return "shl"; - } - if (strcmp(op, ">>") == 0) - { - return "shr"; - } - + } + + // Built-in Fields for Arrays/Slices + if (struct_type->kind == TYPE_ARRAY) { + if (strcmp(field_name, "len") == 0) { + return type_new(TYPE_INT); + } + if (struct_type->array_size == 0) { // Slice + if (strcmp(field_name, "cap") == 0) { + return type_new(TYPE_INT); + } + if (strcmp(field_name, "data") == 0) { + return type_new_ptr(struct_type->inner); + } + } + } + + char *sname = struct_type->name; + // Handle Pointers (User* -> User) + if (struct_type->kind == TYPE_POINTER && struct_type->inner) { + sname = struct_type->inner->name; + } + if (!sname) { return NULL; -} - -ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) -{ - Token t = lexer_peek(l); - ASTNode *lhs = NULL; - - if (t.type == TOK_QUESTION) - { - Lexer lookahead = *l; - lexer_next(&lookahead); - Token next = lexer_peek(&lookahead); - - if (next.type == TOK_STRING || next.type == TOK_FSTRING) - { - lexer_next(l); // consume '?' - Token t_str = lexer_next(l); - - char *inner = xmalloc(t_str.len); - if (t_str.type == TOK_FSTRING) - { - strncpy(inner, t_str.start + 2, t_str.len - 3); - inner[t_str.len - 3] = 0; - } - else - { - strncpy(inner, t_str.start + 1, t_str.len - 2); - inner[t_str.len - 2] = 0; - } - - // Reuse printf sugar to generate the prompt print - char *print_code = process_printf_sugar(ctx, inner, 0, "stdout", NULL, NULL); - free(inner); - - // Checks for (args...) suffix for SCAN mode - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // consume ( - - // Parse args - ASTNode *args[16]; - int ac = 0; - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - args[ac++] = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - - char fmt[256]; - fmt[0] = 0; - for (int i = 0; i < ac; i++) - { - Type *t = args[i]->type_info; - if (!t && args[i]->type == NODE_EXPR_VAR) - { - t = find_symbol_type_info(ctx, args[i]->var_ref.name); - } - - if (!t) - { - strcat(fmt, "%d"); - } - else - { - if (t->kind == TYPE_INT || t->kind == TYPE_I32 || t->kind == TYPE_BOOL) - { - strcat(fmt, "%d"); - } - else if (t->kind == TYPE_F64) - { - strcat(fmt, "%lf"); - } - else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) - { - strcat(fmt, "%f"); - } - else if (t->kind == TYPE_STRING) - { - strcat(fmt, "%s"); - } - else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 || - t->kind == TYPE_BYTE) - { - strcat(fmt, " %c"); - } - else - { - strcat(fmt, "%d"); - } - } - if (i < ac - 1) - { - strcat(fmt, " "); - } - } - - ASTNode *block = ast_create(NODE_BLOCK); - - ASTNode *s1 = ast_create(NODE_RAW_STMT); - // Append semicolon to ensure it's a valid statement - char *s1_code = xmalloc(strlen(print_code) + 2); - sprintf(s1_code, "%s;", print_code); - s1->raw_stmt.content = s1_code; - free(print_code); - - ASTNode *call = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup("_z_scan_helper"); - call->call.callee = callee; - call->type_info = type_new(TYPE_INT); - - ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL); - fmt_node->literal.type_kind = TOK_STRING; - fmt_node->literal.string_val = xstrdup(fmt); - ASTNode *head = fmt_node, *tail = fmt_node; - - for (int i = 0; i < ac; i++) - { - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = xstrdup("&"); - addr->unary.operand = args[i]; - tail->next = addr; - tail = addr; - } - call->call.args = head; - - // Link Statements - s1->next = call; - block->block.statements = s1; - - return block; - } - else - { - // String Mode (Original) - size_t len = strlen(print_code); - if (len > 5) - { - print_code[len - 5] = 0; // Strip "0; })" - } - - char *final_code = xmalloc(strlen(print_code) + 64); - sprintf(final_code, "%s readln(); })", print_code); - free(print_code); - - ASTNode *n = ast_create(NODE_RAW_STMT); - n->raw_stmt.content = final_code; - return n; - } - } - } - if (t.type == TOK_OP && is_token(t, "!")) - { - Lexer lookahead = *l; - lexer_next(&lookahead); - Token next = lexer_peek(&lookahead); - - if (next.type == TOK_STRING || next.type == TOK_FSTRING) - { - lexer_next(l); // consume '!' - Token t_str = lexer_next(l); - - char *inner = xmalloc(t_str.len); - if (t_str.type == TOK_FSTRING) - { - strncpy(inner, t_str.start + 2, t_str.len - 3); - inner[t_str.len - 3] = 0; - } - else - { - strncpy(inner, t_str.start + 1, t_str.len - 2); - inner[t_str.len - 2] = 0; - } - - // Check for .. suffix (.. suppresses newline) - int newline = 1; - if (lexer_peek(l).type == TOK_DOTDOT) - { - lexer_next(l); // consume .. - newline = 0; - } - - char *code = process_printf_sugar(ctx, inner, newline, "stderr", NULL, NULL); - free(inner); - - ASTNode *n = ast_create(NODE_RAW_STMT); - n->raw_stmt.content = code; - return n; - } - } + } - if (t.type == TOK_AWAIT) - { - lexer_next(l); // consume await - ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY); - - lhs = ast_create(NODE_AWAIT); - lhs->unary.operand = operand; - // Type inference: await Async<T> yields T - // If operand is a call to an async function, look up its ret_type (not - // Async) - if (operand->type == NODE_EXPR_CALL && operand->call.callee->type == NODE_EXPR_VAR) - { - FuncSig *sig = find_func(ctx, operand->call.callee->var_ref.name); - if (sig && sig->is_async && sig->ret_type) - { - lhs->type_info = sig->ret_type; - lhs->resolved_type = type_to_string(sig->ret_type); - } - else if (sig && !sig->is_async) - { - // Not an async function - shouldn't await it - lhs->type_info = type_new(TYPE_VOID); - lhs->resolved_type = xstrdup("void"); - } - else - { - lhs->type_info = type_new_ptr(type_new(TYPE_VOID)); - lhs->resolved_type = xstrdup("void*"); - } - } - else - { - // Awaiting a variable - harder to determine underlying type - // Fallback to void* for now (could be improved with metadata) - lhs->type_info = type_new_ptr(type_new(TYPE_VOID)); - lhs->resolved_type = xstrdup("void*"); - } + ASTNode *def = find_struct_def(ctx, sname); + if (!def) { + return NULL; + } - goto after_unary; + ASTNode *f = def->strct.fields; + while (f) { + if (strcmp(f->field.name, field_name) == 0) { + return f->type_info; } + f = f->next; + } + return NULL; +} - if (t.type == TOK_OP && - (is_token(t, "-") || is_token(t, "!") || is_token(t, "*") || is_token(t, "&") || - is_token(t, "~") || is_token(t, "&&") || is_token(t, "++") || is_token(t, "--"))) - { - lexer_next(l); // consume op - ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY); - - char *method = NULL; - if (is_token(t, "-")) - { - method = "neg"; - } - if (is_token(t, "!")) - { - method = "not"; - } - if (is_token(t, "~")) - { - method = "bitnot"; - } - - if (method && operand->type_info) - { - Type *ot = operand->type_info; - char *struct_name = NULL; - int is_ptr = 0; - // Unwrap pointer if needed (Struct* -> Struct) to find the method - if (ot->kind == TYPE_STRUCT) - { - struct_name = ot->name; - is_ptr = 0; - } - else if (ot->kind == TYPE_POINTER && ot->inner->kind == TYPE_STRUCT) - { - struct_name = ot->inner->name; - is_ptr = 1; - } +const char *get_operator_method(const char *op) { + // Arithmetic + if (strcmp(op, "+") == 0) { + return "add"; + } + if (strcmp(op, "-") == 0) { + return "sub"; + } + if (strcmp(op, "*") == 0) { + return "mul"; + } + if (strcmp(op, "/") == 0) { + return "div"; + } + if (strcmp(op, "%") == 0) { + return "rem"; + } + + // Comparison + if (strcmp(op, "==") == 0) { + return "eq"; + } + if (strcmp(op, "!=") == 0) { + return "neq"; + } + if (strcmp(op, "<") == 0) { + return "lt"; + } + if (strcmp(op, ">") == 0) { + return "gt"; + } + if (strcmp(op, "<=") == 0) { + return "le"; + } + if (strcmp(op, ">=") == 0) { + return "ge"; + } + + // --- NEW: Bitwise --- + if (strcmp(op, "&") == 0) { + return "bitand"; + } + if (strcmp(op, "|") == 0) { + return "bitor"; + } + if (strcmp(op, "^") == 0) { + return "bitxor"; + } + if (strcmp(op, "<<") == 0) { + return "shl"; + } + if (strcmp(op, ">>") == 0) { + return "shr"; + } + + return NULL; +} - if (struct_name) - { - char mangled[256]; - sprintf(mangled, "%s_%s", struct_name, method); - - if (find_func(ctx, mangled)) - { - // Rewrite: ~x -> Struct_bitnot(x) - ASTNode *call = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup(mangled); - call->call.callee = callee; - - // Handle 'self' argument adjustment (Pointer vs Value) - ASTNode *arg = operand; - FuncSig *sig = find_func(ctx, mangled); - - if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && !is_ptr) - { - int is_rvalue = - (operand->type == NODE_EXPR_CALL || operand->type == NODE_EXPR_BINARY || +ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) { + Token t = lexer_peek(l); + ASTNode *lhs = NULL; + + if (t.type == TOK_QUESTION) { + Lexer lookahead = *l; + lexer_next(&lookahead); + Token next = lexer_peek(&lookahead); + + if (next.type == TOK_STRING || next.type == TOK_FSTRING) { + lexer_next(l); // consume '?' + Token t_str = lexer_next(l); + + char *inner = xmalloc(t_str.len); + if (t_str.type == TOK_FSTRING) { + strncpy(inner, t_str.start + 2, t_str.len - 3); + inner[t_str.len - 3] = 0; + } else { + strncpy(inner, t_str.start + 1, t_str.len - 2); + inner[t_str.len - 2] = 0; + } + + // Reuse printf sugar to generate the prompt print + char *print_code = + process_printf_sugar(ctx, inner, 0, "stdout", NULL, NULL); + free(inner); + + // Checks for (args...) suffix for SCAN mode + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // consume ( + + // Parse args + ASTNode *args[16]; + int ac = 0; + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + args[ac++] = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } + + char fmt[256]; + fmt[0] = 0; + for (int i = 0; i < ac; i++) { + Type *t = args[i]->type_info; + if (!t && args[i]->type == NODE_EXPR_VAR) { + t = find_symbol_type_info(ctx, args[i]->var_ref.name); + } + + if (!t) { + strcat(fmt, "%d"); + } else { + if (t->kind == TYPE_INT || t->kind == TYPE_I32 || + t->kind == TYPE_BOOL) { + strcat(fmt, "%d"); + } else if (t->kind == TYPE_F64) { + strcat(fmt, "%lf"); + } else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) { + strcat(fmt, "%f"); + } else if (t->kind == TYPE_STRING) { + strcat(fmt, "%s"); + } else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || + t->kind == TYPE_U8 || t->kind == TYPE_BYTE) { + strcat(fmt, " %c"); + } else { + strcat(fmt, "%d"); + } + } + if (i < ac - 1) { + strcat(fmt, " "); + } + } + + ASTNode *block = ast_create(NODE_BLOCK); + + ASTNode *s1 = ast_create(NODE_RAW_STMT); + // Append semicolon to ensure it's a valid statement + char *s1_code = xmalloc(strlen(print_code) + 2); + sprintf(s1_code, "%s;", print_code); + s1->raw_stmt.content = s1_code; + free(print_code); + + ASTNode *call = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup("_z_scan_helper"); + call->call.callee = callee; + call->type_info = type_new(TYPE_INT); + + ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL); + fmt_node->literal.type_kind = TOK_STRING; + fmt_node->literal.string_val = xstrdup(fmt); + ASTNode *head = fmt_node, *tail = fmt_node; + + for (int i = 0; i < ac; i++) { + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = xstrdup("&"); + addr->unary.operand = args[i]; + tail->next = addr; + tail = addr; + } + call->call.args = head; + + // Link Statements + s1->next = call; + block->block.statements = s1; + + return block; + } else { + // String Mode (Original) + size_t len = strlen(print_code); + if (len > 5) { + print_code[len - 5] = 0; // Strip "0; })" + } + + char *final_code = xmalloc(strlen(print_code) + 64); + sprintf(final_code, "%s readln(); })", print_code); + free(print_code); + + ASTNode *n = ast_create(NODE_RAW_STMT); + n->raw_stmt.content = final_code; + return n; + } + } + } + if (t.type == TOK_OP && is_token(t, "!")) { + Lexer lookahead = *l; + lexer_next(&lookahead); + Token next = lexer_peek(&lookahead); + + if (next.type == TOK_STRING || next.type == TOK_FSTRING) { + lexer_next(l); // consume '!' + Token t_str = lexer_next(l); + + char *inner = xmalloc(t_str.len); + if (t_str.type == TOK_FSTRING) { + strncpy(inner, t_str.start + 2, t_str.len - 3); + inner[t_str.len - 3] = 0; + } else { + strncpy(inner, t_str.start + 1, t_str.len - 2); + inner[t_str.len - 2] = 0; + } + + // Check for .. suffix (.. suppresses newline) + int newline = 1; + if (lexer_peek(l).type == TOK_DOTDOT) { + lexer_next(l); // consume .. + newline = 0; + } + + char *code = + process_printf_sugar(ctx, inner, newline, "stderr", NULL, NULL); + free(inner); + + ASTNode *n = ast_create(NODE_RAW_STMT); + n->raw_stmt.content = code; + return n; + } + } + + if (t.type == TOK_AWAIT) { + lexer_next(l); // consume await + ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY); + + lhs = ast_create(NODE_AWAIT); + lhs->unary.operand = operand; + // Type inference: await Async<T> yields T + // If operand is a call to an async function, look up its ret_type (not + // Async) + if (operand->type == NODE_EXPR_CALL && + operand->call.callee->type == NODE_EXPR_VAR) { + FuncSig *sig = find_func(ctx, operand->call.callee->var_ref.name); + if (sig && sig->is_async && sig->ret_type) { + lhs->type_info = sig->ret_type; + lhs->resolved_type = type_to_string(sig->ret_type); + } else if (sig && !sig->is_async) { + // Not an async function - shouldn't await it + lhs->type_info = type_new(TYPE_VOID); + lhs->resolved_type = xstrdup("void"); + } else { + lhs->type_info = type_new_ptr(type_new(TYPE_VOID)); + lhs->resolved_type = xstrdup("void*"); + } + } else { + // Awaiting a variable - harder to determine underlying type + // Fallback to void* for now (could be improved with metadata) + lhs->type_info = type_new_ptr(type_new(TYPE_VOID)); + lhs->resolved_type = xstrdup("void*"); + } + + goto after_unary; + } + + if (t.type == TOK_OP && + (is_token(t, "-") || is_token(t, "!") || is_token(t, "*") || + is_token(t, "&") || is_token(t, "~") || is_token(t, "&&") || + is_token(t, "++") || is_token(t, "--"))) { + lexer_next(l); // consume op + ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY); + + char *method = NULL; + if (is_token(t, "-")) { + method = "neg"; + } + if (is_token(t, "!")) { + method = "not"; + } + if (is_token(t, "~")) { + method = "bitnot"; + } + + if (method && operand->type_info) { + Type *ot = operand->type_info; + char *struct_name = NULL; + int is_ptr = 0; + // Unwrap pointer if needed (Struct* -> Struct) to find the method + if (ot->kind == TYPE_STRUCT) { + struct_name = ot->name; + is_ptr = 0; + } else if (ot->kind == TYPE_POINTER && ot->inner->kind == TYPE_STRUCT) { + struct_name = ot->inner->name; + is_ptr = 1; + } + + if (struct_name) { + char mangled[256]; + sprintf(mangled, "%s_%s", struct_name, method); + + if (find_func(ctx, mangled)) { + // Rewrite: ~x -> Struct_bitnot(x) + ASTNode *call = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup(mangled); + call->call.callee = callee; + + // Handle 'self' argument adjustment (Pointer vs Value) + ASTNode *arg = operand; + FuncSig *sig = find_func(ctx, mangled); + + if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && + !is_ptr) { + int is_rvalue = (operand->type == NODE_EXPR_CALL || + operand->type == NODE_EXPR_BINARY || operand->type == NODE_MATCH); - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&"); - addr->unary.operand = operand; - addr->type_info = type_new_ptr(ot); - arg = addr; - } - else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) - { - // Function wants Value, we have Pointer -> Dereference (*) - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = operand; - deref->type_info = ot->inner; - arg = deref; - } - - call->call.args = arg; - call->type_info = sig->ret_type; - call->resolved_type = type_to_string(sig->ret_type); - lhs = call; - - // Skip standard unary node creation - goto after_unary; - } - } - } - - // Standard Unary Node (for primitives or if no overload found) - lhs = ast_create(NODE_EXPR_UNARY); - lhs->unary.op = token_strdup(t); - lhs->unary.operand = operand; - - if (operand->type_info) - { - if (is_token(t, "&")) - { - lhs->type_info = type_new_ptr(operand->type_info); - } - else if (is_token(t, "*")) - { - if (operand->type_info->kind == TYPE_POINTER) - { - lhs->type_info = operand->type_info->inner; - } - } - else - { - lhs->type_info = operand->type_info; - } - } - - after_unary:; // Label to skip standard creation if overloaded - } - - else if (is_token(t, "sizeof")) - { - lexer_next(l); - if (lexer_peek(l).type == TOK_LPAREN) - { - const char *start = l->src + l->pos; - int depth = 0; - while (1) - { - Token tk = lexer_peek(l); - if (tk.type == TOK_EOF) - { - zpanic_at(tk, "Unterminated sizeof"); - } - if (tk.type == TOK_LPAREN) - { - depth++; - } - if (tk.type == TOK_RPAREN) - { - depth--; - if (depth == 0) - { - lexer_next(l); - break; - } - } - lexer_next(l); - } - int len = (l->src + l->pos) - start; - char *content = xmalloc(len + 8); - sprintf(content, "sizeof%.*s", len, start); - lhs = ast_create(NODE_RAW_STMT); - lhs->raw_stmt.content = content; - lhs->type_info = type_new(TYPE_INT); - } - else - { - zpanic_at(lexer_peek(l), "sizeof must be followed by ("); - } - } - else - { - lhs = parse_primary(ctx, l); - } - - while (1) - { - Token op = lexer_peek(l); - Precedence prec = get_token_precedence(op); - - // Handle postfix ++ and -- (highest postfix precedence) - if (op.type == TOK_OP && op.len == 2 && - ((op.start[0] == '+' && op.start[1] == '+') || - (op.start[0] == '-' && op.start[1] == '-'))) - { - lexer_next(l); // consume ++ or -- - ASTNode *node = ast_create(NODE_EXPR_UNARY); - node->unary.op = (op.start[0] == '+') ? xstrdup("_post++") : xstrdup("_post--"); - node->unary.operand = lhs; - node->type_info = lhs->type_info; - lhs = node; - continue; - } - - if (prec == PREC_NONE || prec < min_prec) - { - break; - } - - // Pointer access: -> - if (op.type == TOK_ARROW && op.start[0] == '-') - { - lexer_next(l); - Token field = lexer_next(l); - if (field.type != TOK_IDENT) - { - zpanic_at(field, "Expected field name after ->"); - break; - } - ASTNode *node = ast_create(NODE_EXPR_MEMBER); - node->member.target = lhs; - node->member.field = token_strdup(field); - node->member.is_pointer_access = 1; - - node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); - if (node->type_info) - { - node->resolved_type = type_to_string(node->type_info); - } - else - { - node->resolved_type = xstrdup("unknown"); - } - - lhs = node; - continue; - } - - // Null-safe access: ?. - if (op.type == TOK_Q_DOT) - { - lexer_next(l); - Token field = lexer_next(l); - if (field.type != TOK_IDENT) - { - zpanic_at(field, "Expected field name after ?."); - break; - } - ASTNode *node = ast_create(NODE_EXPR_MEMBER); - node->member.target = lhs; - node->member.field = token_strdup(field); - node->member.is_pointer_access = 2; - - node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); - if (node->type_info) - { - node->resolved_type = type_to_string(node->type_info); - } - - lhs = node; - continue; - } - - // Postfix ? (Result Unwrap OR Ternary) - if (op.type == TOK_QUESTION) - { - // Disambiguate - Lexer lookahead = *l; - lexer_next(&lookahead); // skip ? - Token next = lexer_peek(&lookahead); - - // Heuristic: If next token starts an expression => Ternary - // (Ident, Number, String, (, {, -, !, *, etc) - int is_ternary = 0; - if (next.type == TOK_INT || next.type == TOK_FLOAT || next.type == TOK_STRING || - next.type == TOK_IDENT || next.type == TOK_LPAREN || next.type == TOK_LBRACE || - next.type == TOK_SIZEOF || next.type == TOK_DEFER || next.type == TOK_AUTOFREE || - next.type == TOK_FSTRING || next.type == TOK_CHAR) - { - is_ternary = 1; - } - // Check unary ops - if (next.type == TOK_OP) - { - if (is_token(next, "-") || is_token(next, "!") || is_token(next, "*") || - is_token(next, "&") || is_token(next, "~")) - { - is_ternary = 1; - } - } - - if (is_ternary) - { - if (PREC_TERNARY < min_prec) - { - break; // Return to caller to handle precedence - } - - lexer_next(l); // consume ? - ASTNode *true_expr = parse_expression(ctx, l); - expect(l, TOK_COLON, "Expected : in ternary"); - ASTNode *false_expr = parse_expr_prec(ctx, l, PREC_TERNARY); // Right associative - - ASTNode *tern = ast_create(NODE_TERNARY); - zen_trigger_at(TRIGGER_TERNARY, lhs->token); - - tern->ternary.cond = lhs; - tern->ternary.true_expr = true_expr; - tern->ternary.false_expr = false_expr; - - // Type inference hint: Both branches should match? - // Logic later in codegen/semant. - lhs = tern; - continue; - } - - // Otherwise: Unwrap (High Precedence) - if (PREC_CALL < min_prec) - { - break; - } - + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&"); + addr->unary.operand = operand; + addr->type_info = type_new_ptr(ot); + arg = addr; + } else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) { + // Function wants Value, we have Pointer -> Dereference (*) + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = operand; + deref->type_info = ot->inner; + arg = deref; + } + + call->call.args = arg; + call->type_info = sig->ret_type; + call->resolved_type = type_to_string(sig->ret_type); + lhs = call; + + // Skip standard unary node creation + goto after_unary; + } + } + } + + // Standard Unary Node (for primitives or if no overload found) + lhs = ast_create(NODE_EXPR_UNARY); + lhs->unary.op = token_strdup(t); + lhs->unary.operand = operand; + + if (operand->type_info) { + if (is_token(t, "&")) { + lhs->type_info = type_new_ptr(operand->type_info); + } else if (is_token(t, "*")) { + if (operand->type_info->kind == TYPE_POINTER) { + lhs->type_info = operand->type_info->inner; + } + } else { + lhs->type_info = operand->type_info; + } + } + + after_unary:; // Label to skip standard creation if overloaded + } + + else if (is_token(t, "sizeof")) { + lexer_next(l); + if (lexer_peek(l).type == TOK_LPAREN) { + const char *start = l->src + l->pos; + int depth = 0; + while (1) { + Token tk = lexer_peek(l); + if (tk.type == TOK_EOF) { + zpanic_at(tk, "Unterminated sizeof"); + } + if (tk.type == TOK_LPAREN) { + depth++; + } + if (tk.type == TOK_RPAREN) { + depth--; + if (depth == 0) { lexer_next(l); - ASTNode *n = ast_create(NODE_TRY); - n->try_stmt.expr = lhs; - lhs = n; - continue; + break; + } } - - // Pipe: |> - if (op.type == TOK_PIPE || (op.type == TOK_OP && is_token(op, "|>"))) - { + lexer_next(l); + } + int len = (l->src + l->pos) - start; + char *content = xmalloc(len + 8); + sprintf(content, "sizeof%.*s", len, start); + lhs = ast_create(NODE_RAW_STMT); + lhs->raw_stmt.content = content; + lhs->type_info = type_new(TYPE_INT); + } else { + zpanic_at(lexer_peek(l), "sizeof must be followed by ("); + } + } else { + lhs = parse_primary(ctx, l); + } + + while (1) { + Token op = lexer_peek(l); + Precedence prec = get_token_precedence(op); + + // Handle postfix ++ and -- (highest postfix precedence) + if (op.type == TOK_OP && op.len == 2 && + ((op.start[0] == '+' && op.start[1] == '+') || + (op.start[0] == '-' && op.start[1] == '-'))) { + lexer_next(l); // consume ++ or -- + ASTNode *node = ast_create(NODE_EXPR_UNARY); + node->unary.op = + (op.start[0] == '+') ? xstrdup("_post++") : xstrdup("_post--"); + node->unary.operand = lhs; + node->type_info = lhs->type_info; + lhs = node; + continue; + } + + if (prec == PREC_NONE || prec < min_prec) { + break; + } + + // Pointer access: -> + if (op.type == TOK_ARROW && op.start[0] == '-') { + lexer_next(l); + Token field = lexer_next(l); + if (field.type != TOK_IDENT) { + zpanic_at(field, "Expected field name after ->"); + break; + } + ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->member.target = lhs; + node->member.field = token_strdup(field); + node->member.is_pointer_access = 1; + + node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); + if (node->type_info) { + node->resolved_type = type_to_string(node->type_info); + } else { + node->resolved_type = xstrdup("unknown"); + } + + lhs = node; + continue; + } + + // Null-safe access: ?. + if (op.type == TOK_Q_DOT) { + lexer_next(l); + Token field = lexer_next(l); + if (field.type != TOK_IDENT) { + zpanic_at(field, "Expected field name after ?."); + break; + } + ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->member.target = lhs; + node->member.field = token_strdup(field); + node->member.is_pointer_access = 2; + + node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); + if (node->type_info) { + node->resolved_type = type_to_string(node->type_info); + } + + lhs = node; + continue; + } + + // Postfix ? (Result Unwrap OR Ternary) + if (op.type == TOK_QUESTION) { + // Disambiguate + Lexer lookahead = *l; + lexer_next(&lookahead); // skip ? + Token next = lexer_peek(&lookahead); + + // Heuristic: If next token starts an expression => Ternary + // (Ident, Number, String, (, {, -, !, *, etc) + int is_ternary = 0; + if (next.type == TOK_INT || next.type == TOK_FLOAT || + next.type == TOK_STRING || next.type == TOK_IDENT || + next.type == TOK_LPAREN || next.type == TOK_LBRACE || + next.type == TOK_SIZEOF || next.type == TOK_DEFER || + next.type == TOK_AUTOFREE || next.type == TOK_FSTRING || + next.type == TOK_CHAR) { + is_ternary = 1; + } + // Check unary ops + if (next.type == TOK_OP) { + if (is_token(next, "-") || is_token(next, "!") || is_token(next, "*") || + is_token(next, "&") || is_token(next, "~")) { + is_ternary = 1; + } + } + + if (is_ternary) { + if (PREC_TERNARY < min_prec) { + break; // Return to caller to handle precedence + } + + lexer_next(l); // consume ? + ASTNode *true_expr = parse_expression(ctx, l); + expect(l, TOK_COLON, "Expected : in ternary"); + ASTNode *false_expr = + parse_expr_prec(ctx, l, PREC_TERNARY); // Right associative + + ASTNode *tern = ast_create(NODE_TERNARY); + zen_trigger_at(TRIGGER_TERNARY, lhs->token); + + tern->ternary.cond = lhs; + tern->ternary.true_expr = true_expr; + tern->ternary.false_expr = false_expr; + + // Type inference hint: Both branches should match? + // Logic later in codegen/semant. + lhs = tern; + continue; + } + + // Otherwise: Unwrap (High Precedence) + if (PREC_CALL < min_prec) { + break; + } + + lexer_next(l); + ASTNode *n = ast_create(NODE_TRY); + n->try_stmt.expr = lhs; + lhs = n; + continue; + } + + // Pipe: |> + if (op.type == TOK_PIPE || (op.type == TOK_OP && is_token(op, "|>"))) { + lexer_next(l); + ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1); + if (rhs->type == NODE_EXPR_CALL) { + ASTNode *old_args = rhs->call.args; + lhs->next = old_args; + rhs->call.args = lhs; + lhs = rhs; + } else { + ASTNode *call = ast_create(NODE_EXPR_CALL); + call->call.callee = rhs; + call->call.args = lhs; + lhs->next = NULL; + lhs = call; + } + continue; + } + + lexer_next(l); // Consume operator/paren/bracket + + // Call: (...) + if (op.type == TOK_LPAREN) { + ASTNode *call = ast_create(NODE_EXPR_CALL); + call->call.callee = lhs; + ASTNode *head = NULL, *tail = NULL; + char **arg_names = NULL; + int arg_count = 0; + int has_named = 0; + + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + char *arg_name = NULL; + + // Check for named argument: IDENT : expr + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) { + // Lookahead for colon + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); // eat IDENT + lexer_next(l); // eat : + } + } + + ASTNode *arg = parse_expression(ctx, l); + if (!head) { + head = arg; + } else { + tail->next = arg; + } + tail = arg; + + // Store arg name + arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *)); + arg_names[arg_count] = arg_name; + arg_count++; + + if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); - ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1); - if (rhs->type == NODE_EXPR_CALL) - { - ASTNode *old_args = rhs->call.args; - lhs->next = old_args; - rhs->call.args = lhs; - lhs = rhs; - } - else - { - ASTNode *call = ast_create(NODE_EXPR_CALL); - call->call.callee = rhs; - call->call.args = lhs; - lhs->next = NULL; - lhs = call; - } + } else { + break; + } + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } + call->call.args = head; + call->call.arg_names = has_named ? arg_names : NULL; + call->call.arg_count = arg_count; + + call->resolved_type = xstrdup("unknown"); + if (lhs->type_info && lhs->type_info->kind == TYPE_FUNCTION && + lhs->type_info->inner) { + call->type_info = lhs->type_info->inner; + } + + lhs = call; + continue; + } + + // Index: [...] or Slice: [start..end] + if (op.type == TOK_LBRACKET) { + ASTNode *start = NULL; + ASTNode *end = NULL; + int is_slice = 0; + + // Case: [..] or [..end] + if (lexer_peek(l).type == TOK_DOTDOT) { + is_slice = 1; + lexer_next(l); // consume .. + if (lexer_peek(l).type != TOK_RBRACKET) { + end = parse_expression(ctx, l); + } + } else { + // Case: [start] or [start..] or [start..end] + start = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_DOTDOT) { + is_slice = 1; + lexer_next(l); // consume .. + if (lexer_peek(l).type != TOK_RBRACKET) { + end = parse_expression(ctx, l); + } + } + } + + if (lexer_next(l).type != TOK_RBRACKET) { + zpanic_at(lexer_peek(l), "Expected ]"); + } + + if (is_slice) { + ASTNode *node = ast_create(NODE_EXPR_SLICE); + node->slice.array = lhs; + node->slice.start = start; + node->slice.end = end; + + // Type Inference & Registration + if (lhs->type_info) { + Type *inner = NULL; + if (lhs->type_info->kind == TYPE_ARRAY) { + inner = lhs->type_info->inner; + } else if (lhs->type_info->kind == TYPE_POINTER) { + inner = lhs->type_info->inner; + } + + if (inner) { + node->type_info = type_new(TYPE_ARRAY); + node->type_info->inner = inner; + node->type_info->array_size = 0; // Slice + + // Clean up string for registration (e.g. "int" from "int*") + char *inner_str = type_to_string(inner); + + // Strip * if it somehow managed to keep one, though + // parse_type_formal should handle it For now assume type_to_string + // gives base type + register_slice(ctx, inner_str); + } + } + + lhs = node; + } else { + ASTNode *node = ast_create(NODE_EXPR_INDEX); + node->index.array = lhs; + node->index.index = start; + + // Static Array Bounds Check + if (lhs->type_info && lhs->type_info->kind == TYPE_ARRAY && + lhs->type_info->array_size > 0) { + if (start->type == NODE_EXPR_LITERAL && + start->literal.type_kind == 0) { + int idx = start->literal.int_val; + if (idx < 0 || idx >= lhs->type_info->array_size) { + warn_array_bounds(op, idx, lhs->type_info->array_size); + } + } + } + + lhs = node; + } + continue; + } + + // Member: . + if (op.type == TOK_OP && is_token(op, ".")) { + Token field = lexer_next(l); + if (field.type != TOK_IDENT) { + zpanic_at(field, "Expected field name after ."); + break; + } + ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->member.target = lhs; + node->member.field = token_strdup(field); + node->member.is_pointer_access = 0; + + if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) { + node->member.is_pointer_access = 1; + + // Special case: .val() on pointer = dereference + if (strcmp(node->member.field, "val") == 0 && + lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // consume ( + if (lexer_peek(l).type == TOK_RPAREN) { + lexer_next(l); // consume ) + // Rewrite to dereference: *ptr + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = lhs; + deref->type_info = lhs->type_info->inner; + lhs = deref; continue; - } - - lexer_next(l); // Consume operator/paren/bracket - - // Call: (...) - if (op.type == TOK_LPAREN) - { + } + } + } else if (lhs->type == NODE_EXPR_VAR) { + char *type = find_symbol_type(ctx, lhs->var_ref.name); + if (type && strchr(type, '*')) { + node->member.is_pointer_access = 1; + + // Special case: .val() on pointer = dereference + if (strcmp(node->member.field, "val") == 0 && + lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // consume ( + if (lexer_peek(l).type == TOK_RPAREN) { + lexer_next(l); // consume ) + // Rewrite to dereference: *ptr + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = lhs; + // Try to get inner type + if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) { + deref->type_info = lhs->type_info->inner; + } + lhs = deref; + continue; + } + } + } + if (strcmp(lhs->var_ref.name, "self") == 0 && + !node->member.is_pointer_access) { + node->member.is_pointer_access = 1; + } + } + + node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); + + if (!node->type_info && lhs->type_info) { + char *struct_name = NULL; + Type *st = lhs->type_info; + if (st->kind == TYPE_STRUCT) { + struct_name = st->name; + } else if (st->kind == TYPE_POINTER && st->inner && + st->inner->kind == TYPE_STRUCT) { + struct_name = st->inner->name; + } + + if (struct_name) { + char mangled[256]; + sprintf(mangled, "%s_%s", struct_name, node->member.field); + + FuncSig *sig = find_func(ctx, mangled); + if (sig) { + // It is a method! Create a Function Type Info to carry the return + // type + Type *ft = type_new(TYPE_FUNCTION); + ft->name = xstrdup(mangled); + ft->inner = sig->ret_type; // Return type + node->type_info = ft; + } + } + } + + if (node->type_info) { + node->resolved_type = type_to_string(node->type_info); + } else { + node->resolved_type = xstrdup("unknown"); + } + + lhs = node; + continue; + } + + ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1); + ASTNode *bin = ast_create(NODE_EXPR_BINARY); + bin->token = op; + if (op.type == TOK_OP) { + if (is_token(op, "&") || is_token(op, "|") || is_token(op, "^")) { + zen_trigger_at(TRIGGER_BITWISE, op); + } else if (is_token(op, "<<") || is_token(op, ">>")) { + zen_trigger_at(TRIGGER_BITWISE, op); + } + } + bin->binary.left = lhs; + bin->binary.right = rhs; + + if (op.type == TOK_LANGLE) { + bin->binary.op = xstrdup("<"); + } else if (op.type == TOK_RANGLE) { + bin->binary.op = xstrdup(">"); + } else if (op.type == TOK_AND) { + bin->binary.op = xstrdup("&&"); + } else if (op.type == TOK_OR) { + bin->binary.op = xstrdup("||"); + } else { + bin->binary.op = token_strdup(op); + } + + if (strcmp(bin->binary.op, "/") == 0 || strcmp(bin->binary.op, "%") == 0) { + if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 && + rhs->literal.int_val == 0) { + warn_division_by_zero(op); + } + } + + if (is_comparison_op(bin->binary.op)) { + // Check for identical operands (x == x) + if (lhs->type == NODE_EXPR_VAR && rhs->type == NODE_EXPR_VAR) { + if (strcmp(lhs->var_ref.name, rhs->var_ref.name) == 0) { + if (strcmp(bin->binary.op, "==") == 0 || + strcmp(bin->binary.op, ">=") == 0 || + strcmp(bin->binary.op, "<=") == 0) { + warn_comparison_always_true(op, "Comparing a variable to itself"); + } else if (strcmp(bin->binary.op, "!=") == 0 || + strcmp(bin->binary.op, ">") == 0 || + strcmp(bin->binary.op, "<") == 0) { + warn_comparison_always_false(op, "Comparing a variable to itself"); + } + } + } else if (lhs->type == NODE_EXPR_LITERAL && + lhs->literal.type_kind == 0 && + rhs->type == NODE_EXPR_LITERAL && + rhs->literal.type_kind == 0) { + // Check if literals make sense (e.g. 5 > 5) + if (lhs->literal.int_val == rhs->literal.int_val) { + if (strcmp(bin->binary.op, "==") == 0 || + strcmp(bin->binary.op, ">=") == 0 || + strcmp(bin->binary.op, "<=") == 0) { + warn_comparison_always_true(op, "Comparing identical literals"); + } else { + warn_comparison_always_false(op, "Comparing identical literals"); + } + } + } + + if (lhs->type_info && type_is_unsigned(lhs->type_info)) { + if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 && + rhs->literal.int_val == 0) { + if (strcmp(bin->binary.op, ">=") == 0) { + warn_comparison_always_true(op, "Unsigned value is always >= 0"); + } else if (strcmp(bin->binary.op, "<") == 0) { + warn_comparison_always_false(op, "Unsigned value is never < 0"); + } + } + } + } + + if (strcmp(bin->binary.op, "=") == 0 || strcmp(bin->binary.op, "+=") == 0 || + strcmp(bin->binary.op, "-=") == 0 || + strcmp(bin->binary.op, "*=") == 0 || + strcmp(bin->binary.op, "/=") == 0) { + + if (lhs->type == NODE_EXPR_VAR) { + // Check if the variable is const + Type *t = find_symbol_type_info(ctx, lhs->var_ref.name); + if (t && t->is_const) { + zpanic_at(op, "Cannot assign to const variable '%s'", + lhs->var_ref.name); + } + + // Check if the variable is immutable + if (!is_var_mutable(ctx, lhs->var_ref.name)) { + zpanic_at(op, + "Cannot assign to immutable variable '%s' (use 'var mut' " + "to make it " + "mutable)", + lhs->var_ref.name); + } + } + } + + int is_compound = 0; + size_t op_len = strlen(bin->binary.op); + + // Check if operator ends with '=' but is not ==, !=, <=, >= + if (op_len > 1 && bin->binary.op[op_len - 1] == '=') { + char c = bin->binary.op[0]; + if (c != '=' && c != '!' && c != '<' && c != '>') { + is_compound = 1; + } + // Special handle for <<= and >>= + if (strcmp(bin->binary.op, "<<=") == 0 || + strcmp(bin->binary.op, ">>=") == 0) { + is_compound = 1; + } + } + + if (is_compound) { + ASTNode *op_node = ast_create(NODE_EXPR_BINARY); + op_node->binary.left = lhs; + op_node->binary.right = rhs; + + // Extract the base operator (remove last char '=') + char *inner_op = xmalloc(op_len); + strncpy(inner_op, bin->binary.op, op_len - 1); + inner_op[op_len - 1] = '\0'; + op_node->binary.op = inner_op; + + // Inherit type info temporarily + if (lhs->type_info && rhs->type_info && + type_eq(lhs->type_info, rhs->type_info)) { + op_node->type_info = lhs->type_info; + } + + const char *inner_method = get_operator_method(inner_op); + if (inner_method) { + Type *lt = lhs->type_info; + char *struct_name = NULL; + int is_lhs_ptr = 0; + + if (lt) { + if (lt->kind == TYPE_STRUCT) { + struct_name = lt->name; + is_lhs_ptr = 0; + } else if (lt->kind == TYPE_POINTER && + lt->inner->kind == TYPE_STRUCT) { + struct_name = lt->inner->name; + is_lhs_ptr = 1; + } + } + + if (struct_name) { + char mangled[256]; + sprintf(mangled, "%s_%s", struct_name, inner_method); + FuncSig *sig = find_func(ctx, mangled); + if (sig) { + // Rewrite op_node from BINARY -> CALL ASTNode *call = ast_create(NODE_EXPR_CALL); - call->call.callee = lhs; - ASTNode *head = NULL, *tail = NULL; - char **arg_names = NULL; - int arg_count = 0; - int has_named = 0; - - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - char *arg_name = NULL; - - // Check for named argument: IDENT : expr - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) - { - // Lookahead for colon - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); // eat IDENT - lexer_next(l); // eat : - } - } - - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - - // Store arg name - arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *)); - arg_names[arg_count] = arg_name; - arg_count++; - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - call->call.args = head; - call->call.arg_names = has_named ? arg_names : NULL; - call->call.arg_count = arg_count; - - call->resolved_type = xstrdup("unknown"); - if (lhs->type_info && lhs->type_info->kind == TYPE_FUNCTION && lhs->type_info->inner) - { - call->type_info = lhs->type_info->inner; - } - - lhs = call; - continue; - } - - // Index: [...] or Slice: [start..end] - if (op.type == TOK_LBRACKET) - { - ASTNode *start = NULL; - ASTNode *end = NULL; - int is_slice = 0; - - // Case: [..] or [..end] - if (lexer_peek(l).type == TOK_DOTDOT) - { - is_slice = 1; - lexer_next(l); // consume .. - if (lexer_peek(l).type != TOK_RBRACKET) - { - end = parse_expression(ctx, l); - } - } - else - { - // Case: [start] or [start..] or [start..end] - start = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_DOTDOT) - { - is_slice = 1; - lexer_next(l); // consume .. - if (lexer_peek(l).type != TOK_RBRACKET) - { - end = parse_expression(ctx, l); - } - } - } - - if (lexer_next(l).type != TOK_RBRACKET) - { - zpanic_at(lexer_peek(l), "Expected ]"); - } - - if (is_slice) - { - ASTNode *node = ast_create(NODE_EXPR_SLICE); - node->slice.array = lhs; - node->slice.start = start; - node->slice.end = end; - - // Type Inference & Registration - if (lhs->type_info) - { - Type *inner = NULL; - if (lhs->type_info->kind == TYPE_ARRAY) - { - inner = lhs->type_info->inner; - } - else if (lhs->type_info->kind == TYPE_POINTER) - { - inner = lhs->type_info->inner; - } - - if (inner) - { - node->type_info = type_new(TYPE_ARRAY); - node->type_info->inner = inner; - node->type_info->array_size = 0; // Slice - - // Clean up string for registration (e.g. "int" from "int*") - char *inner_str = type_to_string(inner); - - // Strip * if it somehow managed to keep one, though - // parse_type_formal should handle it For now assume type_to_string - // gives base type - register_slice(ctx, inner_str); - } - } - - lhs = node; - } - else - { - ASTNode *node = ast_create(NODE_EXPR_INDEX); - node->index.array = lhs; - node->index.index = start; - - // Static Array Bounds Check - if (lhs->type_info && lhs->type_info->kind == TYPE_ARRAY && - lhs->type_info->array_size > 0) - { - if (start->type == NODE_EXPR_LITERAL && start->literal.type_kind == 0) - { - int idx = start->literal.int_val; - if (idx < 0 || idx >= lhs->type_info->array_size) - { - warn_array_bounds(op, idx, lhs->type_info->array_size); - } - } - } - - lhs = node; - } - continue; - } - - // Member: . - if (op.type == TOK_OP && is_token(op, ".")) - { - Token field = lexer_next(l); - if (field.type != TOK_IDENT) - { - zpanic_at(field, "Expected field name after ."); - break; - } - ASTNode *node = ast_create(NODE_EXPR_MEMBER); - node->member.target = lhs; - node->member.field = token_strdup(field); - node->member.is_pointer_access = 0; - - if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) - { - node->member.is_pointer_access = 1; - - // Special case: .val() on pointer = dereference - if (strcmp(node->member.field, "val") == 0 && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // consume ( - if (lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); // consume ) - // Rewrite to dereference: *ptr - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = lhs; - deref->type_info = lhs->type_info->inner; - lhs = deref; - continue; - } - } - } - else if (lhs->type == NODE_EXPR_VAR) - { - char *type = find_symbol_type(ctx, lhs->var_ref.name); - if (type && strchr(type, '*')) - { - node->member.is_pointer_access = 1; - - // Special case: .val() on pointer = dereference - if (strcmp(node->member.field, "val") == 0 && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // consume ( - if (lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); // consume ) - // Rewrite to dereference: *ptr - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = lhs; - // Try to get inner type - if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) - { - deref->type_info = lhs->type_info->inner; - } - lhs = deref; - continue; - } - } - } - if (strcmp(lhs->var_ref.name, "self") == 0 && !node->member.is_pointer_access) - { - node->member.is_pointer_access = 1; - } - } - - node->type_info = get_field_type(ctx, lhs->type_info, node->member.field); - - if (!node->type_info && lhs->type_info) - { - char *struct_name = NULL; - Type *st = lhs->type_info; - if (st->kind == TYPE_STRUCT) - { - struct_name = st->name; - } - else if (st->kind == TYPE_POINTER && st->inner && st->inner->kind == TYPE_STRUCT) - { - struct_name = st->inner->name; - } - - if (struct_name) - { - char mangled[256]; - sprintf(mangled, "%s_%s", struct_name, node->member.field); - - FuncSig *sig = find_func(ctx, mangled); - if (sig) - { - // It is a method! Create a Function Type Info to carry the return - // type - Type *ft = type_new(TYPE_FUNCTION); - ft->name = xstrdup(mangled); - ft->inner = sig->ret_type; // Return type - node->type_info = ft; - } - } - } - - if (node->type_info) - { - node->resolved_type = type_to_string(node->type_info); - } - else - { - node->resolved_type = xstrdup("unknown"); - } - - lhs = node; + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup(mangled); + call->call.callee = callee; + + // Handle 'self' argument + ASTNode *arg1 = lhs; + if (sig->total_args > 0 && + sig->arg_types[0]->kind == TYPE_POINTER && !is_lhs_ptr) { + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = xstrdup("&"); + addr->unary.operand = lhs; + addr->type_info = type_new_ptr(lt); + arg1 = addr; + } else if (is_lhs_ptr && sig->arg_types[0]->kind != TYPE_POINTER) { + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = lhs; + arg1 = deref; + } + + call->call.args = arg1; + arg1->next = rhs; + rhs->next = NULL; + call->type_info = sig->ret_type; + + // Replace op_node with the call + op_node = call; + } + } + } + + free(bin->binary.op); + bin->binary.op = xstrdup("="); + bin->binary.right = op_node; + } + + // Index Set Overload: Call(get, idx) = val --> Call(set, idx, val) + if (strcmp(bin->binary.op, "=") == 0 && lhs->type == NODE_EXPR_CALL) { + if (lhs->call.callee->type == NODE_EXPR_VAR) { + char *name = lhs->call.callee->var_ref.name; + // Check if it ends in "_get" + size_t len = strlen(name); + if (len > 4 && strcmp(name + len - 4, "_get") == 0) { + char *set_name = xstrdup(name); + set_name[len - 3] = 's'; // Replace 'g' with 's' -> _set + set_name[len - 2] = 'e'; + set_name[len - 1] = 't'; + + if (find_func(ctx, set_name)) { + // Create NEW Call Node for Set + ASTNode *set_call = ast_create(NODE_EXPR_CALL); + ASTNode *set_callee = ast_create(NODE_EXPR_VAR); + set_callee->var_ref.name = set_name; + set_call->call.callee = set_callee; + + // Clone argument list (Shallow copy of arg nodes to preserve chain + // for get) + ASTNode *lhs_args = lhs->call.args; + ASTNode *new_head = NULL; + ASTNode *new_tail = NULL; + + while (lhs_args) { + ASTNode *arg_copy = xmalloc(sizeof(ASTNode)); + memcpy(arg_copy, lhs_args, sizeof(ASTNode)); + arg_copy->next = NULL; + + if (!new_head) { + new_head = arg_copy; + } else { + new_tail->next = arg_copy; + } + new_tail = arg_copy; + + lhs_args = lhs_args->next; + } + + // Append RHS to new args + ASTNode *val_expr = bin->binary.right; + if (new_tail) { + new_tail->next = val_expr; + } else { + new_head = val_expr; + } + + set_call->call.args = new_head; + set_call->type_info = type_new(TYPE_VOID); + + lhs = set_call; // Use the new Set call as the result continue; - } - - ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1); - ASTNode *bin = ast_create(NODE_EXPR_BINARY); - bin->token = op; - if (op.type == TOK_OP) - { - if (is_token(op, "&") || is_token(op, "|") || is_token(op, "^")) - { - zen_trigger_at(TRIGGER_BITWISE, op); - } - else if (is_token(op, "<<") || is_token(op, ">>")) - { - zen_trigger_at(TRIGGER_BITWISE, op); - } - } - bin->binary.left = lhs; - bin->binary.right = rhs; - - if (op.type == TOK_LANGLE) - { - bin->binary.op = xstrdup("<"); - } - else if (op.type == TOK_RANGLE) - { - bin->binary.op = xstrdup(">"); - } - else if (op.type == TOK_AND) - { - bin->binary.op = xstrdup("&&"); - } - else if (op.type == TOK_OR) - { - bin->binary.op = xstrdup("||"); - } - else - { - bin->binary.op = token_strdup(op); - } - - if (strcmp(bin->binary.op, "/") == 0 || strcmp(bin->binary.op, "%") == 0) - { - if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 && - rhs->literal.int_val == 0) - { - warn_division_by_zero(op); - } - } - - if (is_comparison_op(bin->binary.op)) - { - // Check for identical operands (x == x) - if (lhs->type == NODE_EXPR_VAR && rhs->type == NODE_EXPR_VAR) - { - if (strcmp(lhs->var_ref.name, rhs->var_ref.name) == 0) - { - if (strcmp(bin->binary.op, "==") == 0 || strcmp(bin->binary.op, ">=") == 0 || - strcmp(bin->binary.op, "<=") == 0) - { - warn_comparison_always_true(op, "Comparing a variable to itself"); - } - else if (strcmp(bin->binary.op, "!=") == 0 || - strcmp(bin->binary.op, ">") == 0 || strcmp(bin->binary.op, "<") == 0) - { - warn_comparison_always_false(op, "Comparing a variable to itself"); - } - } - } - else if (lhs->type == NODE_EXPR_LITERAL && lhs->literal.type_kind == 0 && - rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0) - { - // Check if literals make sense (e.g. 5 > 5) - if (lhs->literal.int_val == rhs->literal.int_val) - { - if (strcmp(bin->binary.op, "==") == 0 || strcmp(bin->binary.op, ">=") == 0 || - strcmp(bin->binary.op, "<=") == 0) - { - warn_comparison_always_true(op, "Comparing identical literals"); - } - else - { - warn_comparison_always_false(op, "Comparing identical literals"); - } - } - } - - if (lhs->type_info && type_is_unsigned(lhs->type_info)) - { - if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 && - rhs->literal.int_val == 0) - { - if (strcmp(bin->binary.op, ">=") == 0) - { - warn_comparison_always_true(op, "Unsigned value is always >= 0"); - } - else if (strcmp(bin->binary.op, "<") == 0) - { - warn_comparison_always_false(op, "Unsigned value is never < 0"); - } - } - } - } - - if (strcmp(bin->binary.op, "=") == 0 || strcmp(bin->binary.op, "+=") == 0 || - strcmp(bin->binary.op, "-=") == 0 || strcmp(bin->binary.op, "*=") == 0 || - strcmp(bin->binary.op, "/=") == 0) - { - - if (lhs->type == NODE_EXPR_VAR) - { - // Check if the variable is const - Type *t = find_symbol_type_info(ctx, lhs->var_ref.name); - if (t && t->is_const) - { - zpanic_at(op, "Cannot assign to const variable '%s'", lhs->var_ref.name); - } - - // Check if the variable is immutable - if (!is_var_mutable(ctx, lhs->var_ref.name)) - { - zpanic_at(op, - "Cannot assign to immutable variable '%s' (use 'var mut' " - "to make it " - "mutable)", - lhs->var_ref.name); - } - } - } - - int is_compound = 0; - size_t op_len = strlen(bin->binary.op); - - // Check if operator ends with '=' but is not ==, !=, <=, >= - if (op_len > 1 && bin->binary.op[op_len - 1] == '=') - { - char c = bin->binary.op[0]; - if (c != '=' && c != '!' && c != '<' && c != '>') - { - is_compound = 1; - } - // Special handle for <<= and >>= - if (strcmp(bin->binary.op, "<<=") == 0 || strcmp(bin->binary.op, ">>=") == 0) - { - is_compound = 1; - } - } - - if (is_compound) - { - ASTNode *op_node = ast_create(NODE_EXPR_BINARY); - op_node->binary.left = lhs; - op_node->binary.right = rhs; - - // Extract the base operator (remove last char '=') - char *inner_op = xmalloc(op_len); - strncpy(inner_op, bin->binary.op, op_len - 1); - inner_op[op_len - 1] = '\0'; - op_node->binary.op = inner_op; - - // Inherit type info temporarily - if (lhs->type_info && rhs->type_info && type_eq(lhs->type_info, rhs->type_info)) - { - op_node->type_info = lhs->type_info; - } - - const char *inner_method = get_operator_method(inner_op); - if (inner_method) - { - Type *lt = lhs->type_info; - char *struct_name = NULL; - int is_lhs_ptr = 0; - - if (lt) - { - if (lt->kind == TYPE_STRUCT) - { - struct_name = lt->name; - is_lhs_ptr = 0; - } - else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT) - { - struct_name = lt->inner->name; - is_lhs_ptr = 1; - } - } - - if (struct_name) - { - char mangled[256]; - sprintf(mangled, "%s_%s", struct_name, inner_method); - FuncSig *sig = find_func(ctx, mangled); - if (sig) - { - // Rewrite op_node from BINARY -> CALL - ASTNode *call = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup(mangled); - call->call.callee = callee; - - // Handle 'self' argument - ASTNode *arg1 = lhs; - if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && - !is_lhs_ptr) - { - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = xstrdup("&"); - addr->unary.operand = lhs; - addr->type_info = type_new_ptr(lt); - arg1 = addr; - } - else if (is_lhs_ptr && sig->arg_types[0]->kind != TYPE_POINTER) - { - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = lhs; - arg1 = deref; - } - - call->call.args = arg1; - arg1->next = rhs; - rhs->next = NULL; - call->type_info = sig->ret_type; - - // Replace op_node with the call - op_node = call; - } - } - } - - free(bin->binary.op); - bin->binary.op = xstrdup("="); - bin->binary.right = op_node; - } - - // Index Set Overload: Call(get, idx) = val --> Call(set, idx, val) - if (strcmp(bin->binary.op, "=") == 0 && lhs->type == NODE_EXPR_CALL) - { - if (lhs->call.callee->type == NODE_EXPR_VAR) - { - char *name = lhs->call.callee->var_ref.name; - // Check if it ends in "_get" - size_t len = strlen(name); - if (len > 4 && strcmp(name + len - 4, "_get") == 0) - { - char *set_name = xstrdup(name); - set_name[len - 3] = 's'; // Replace 'g' with 's' -> _set - set_name[len - 2] = 'e'; - set_name[len - 1] = 't'; - - if (find_func(ctx, set_name)) - { - // Create NEW Call Node for Set - ASTNode *set_call = ast_create(NODE_EXPR_CALL); - ASTNode *set_callee = ast_create(NODE_EXPR_VAR); - set_callee->var_ref.name = set_name; - set_call->call.callee = set_callee; - - // Clone argument list (Shallow copy of arg nodes to preserve chain - // for get) - ASTNode *lhs_args = lhs->call.args; - ASTNode *new_head = NULL; - ASTNode *new_tail = NULL; - - while (lhs_args) - { - ASTNode *arg_copy = xmalloc(sizeof(ASTNode)); - memcpy(arg_copy, lhs_args, sizeof(ASTNode)); - arg_copy->next = NULL; - - if (!new_head) - { - new_head = arg_copy; - } - else - { - new_tail->next = arg_copy; - } - new_tail = arg_copy; - - lhs_args = lhs_args->next; - } - - // Append RHS to new args - ASTNode *val_expr = bin->binary.right; - if (new_tail) - { - new_tail->next = val_expr; - } - else - { - new_head = val_expr; - } - - set_call->call.args = new_head; - set_call->type_info = type_new(TYPE_VOID); - - lhs = set_call; // Use the new Set call as the result - continue; - } - else - { - free(set_name); - } - } - } - } - - const char *method = get_operator_method(bin->binary.op); - - if (method) - { - Type *lt = lhs->type_info; - char *struct_name = NULL; - int is_lhs_ptr = 0; - - if (lt) - { - if (lt->kind == TYPE_STRUCT) - { - struct_name = lt->name; - is_lhs_ptr = 0; - } - else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT) - { - struct_name = lt->inner->name; - is_lhs_ptr = 1; - } - } - - if (struct_name) - { - char mangled[256]; - sprintf(mangled, "%s_%s", struct_name, method); - - FuncSig *sig = find_func(ctx, mangled); - - if (sig) - { - ASTNode *call = ast_create(NODE_EXPR_CALL); - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = xstrdup(mangled); - call->call.callee = callee; - - ASTNode *arg1 = lhs; - - // Check if function expects a pointer for 'self' - if (sig->total_args > 0 && sig->arg_types[0] && - sig->arg_types[0]->kind == TYPE_POINTER) - { - if (!is_lhs_ptr) - { - // Value -> Pointer. - int is_rvalue = - (lhs->type == NODE_EXPR_CALL || lhs->type == NODE_EXPR_BINARY || - lhs->type == NODE_EXPR_STRUCT_INIT || - lhs->type == NODE_EXPR_CAST || lhs->type == NODE_MATCH); - - ASTNode *addr = ast_create(NODE_EXPR_UNARY); - addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&"); - addr->unary.operand = lhs; - addr->type_info = type_new_ptr(lt); - arg1 = addr; - } - } - else - { - // Function expects value - if (is_lhs_ptr) - { - // Have pointer, need value -> *lhs - ASTNode *deref = ast_create(NODE_EXPR_UNARY); - deref->unary.op = xstrdup("*"); - deref->unary.operand = lhs; - if (lt && lt->kind == TYPE_POINTER) - { - deref->type_info = lt->inner; - } - arg1 = deref; - } - } - - call->call.args = arg1; - arg1->next = rhs; - rhs->next = NULL; - - call->type_info = sig->ret_type; - call->resolved_type = type_to_string(sig->ret_type); - - lhs = call; - continue; // Loop again with result as new lhs - } - } - } - - // Standard Type Checking (if no overload found) - if (lhs->type_info && rhs->type_info) - { - if (is_comparison_op(bin->binary.op)) - { - bin->type_info = type_new(TYPE_INT); // bool - char *t1 = type_to_string(lhs->type_info); - char *t2 = type_to_string(rhs->type_info); - // Skip type check if either operand is void* (escape hatch type) - int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); - - // Allow comparing pointers/strings with integer literal 0 (NULL) - if (!skip_check) - { - int lhs_is_ptr = - (lhs->type_info->kind == TYPE_POINTER || - lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*"))); - int rhs_is_ptr = - (rhs->type_info->kind == TYPE_POINTER || - rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*"))); - - if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL && rhs->literal.int_val == 0) - { - skip_check = 1; - } - if (rhs_is_ptr && lhs->type == NODE_EXPR_LITERAL && lhs->literal.int_val == 0) - { - skip_check = 1; - } - } - - if (!skip_check && !type_eq(lhs->type_info, rhs->type_info)) - { - char msg[256]; - sprintf(msg, "Type mismatch in comparison: cannot compare '%s' and '%s'", t1, - t2); - - char suggestion[256]; - sprintf(suggestion, "Both operands must have compatible types for comparison"); - - zpanic_with_suggestion(op, msg, suggestion); - } - } - else - { - if (type_eq(lhs->type_info, rhs->type_info)) - { - bin->type_info = lhs->type_info; - } - else - { - char *t1 = type_to_string(lhs->type_info); - char *t2 = type_to_string(rhs->type_info); - - // Allow pointer arithmetic: ptr + int, ptr - int, int + ptr - int is_ptr_arith = 0; - if (strcmp(bin->binary.op, "+") == 0 || strcmp(bin->binary.op, "-") == 0) - { - int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER || - lhs->type_info->kind == TYPE_STRING || - (t1 && strstr(t1, "*") != NULL)); - int rhs_is_ptr = (rhs->type_info->kind == TYPE_POINTER || - rhs->type_info->kind == TYPE_STRING || - (t2 && strstr(t2, "*") != NULL)); - int lhs_is_int = - (lhs->type_info->kind == TYPE_INT || lhs->type_info->kind == TYPE_I32 || - lhs->type_info->kind == TYPE_I64 || - lhs->type_info->kind == TYPE_ISIZE || - lhs->type_info->kind == TYPE_USIZE || - (t1 && (strcmp(t1, "int") == 0 || strcmp(t1, "isize") == 0 || - strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 || - strcmp(t1, "ptrdiff_t") == 0))); - int rhs_is_int = - (rhs->type_info->kind == TYPE_INT || rhs->type_info->kind == TYPE_I32 || - rhs->type_info->kind == TYPE_I64 || - rhs->type_info->kind == TYPE_ISIZE || - rhs->type_info->kind == TYPE_USIZE || - (t2 && (strcmp(t2, "int") == 0 || strcmp(t2, "isize") == 0 || - strcmp(t2, "usize") == 0 || strcmp(t2, "size_t") == 0 || - strcmp(t2, "ptrdiff_t") == 0))); - - if ((lhs_is_ptr && rhs_is_int) || (lhs_is_int && rhs_is_ptr)) - { - is_ptr_arith = 1; - bin->type_info = lhs_is_ptr ? lhs->type_info : rhs->type_info; - } - } - - if (!is_ptr_arith) - { - char msg[256]; - sprintf(msg, "Type mismatch in binary operation '%s'", bin->binary.op); - - char suggestion[512]; - sprintf(suggestion, - "Left operand has type '%s', right operand has type '%s'\n = " - "note: Consider casting one operand to match the other", - t1, t2); - - zpanic_with_suggestion(op, msg, suggestion); - } - } - } - } - - lhs = bin; - } - return lhs; + } else { + free(set_name); + } + } + } + } + + const char *method = get_operator_method(bin->binary.op); + + if (method) { + Type *lt = lhs->type_info; + char *struct_name = NULL; + int is_lhs_ptr = 0; + + if (lt) { + if (lt->kind == TYPE_STRUCT) { + struct_name = lt->name; + is_lhs_ptr = 0; + } else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT) { + struct_name = lt->inner->name; + is_lhs_ptr = 1; + } + } + + if (struct_name) { + char mangled[256]; + sprintf(mangled, "%s_%s", struct_name, method); + + FuncSig *sig = find_func(ctx, mangled); + + if (sig) { + ASTNode *call = ast_create(NODE_EXPR_CALL); + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = xstrdup(mangled); + call->call.callee = callee; + + ASTNode *arg1 = lhs; + + // Check if function expects a pointer for 'self' + if (sig->total_args > 0 && sig->arg_types[0] && + sig->arg_types[0]->kind == TYPE_POINTER) { + if (!is_lhs_ptr) { + // Value -> Pointer. + int is_rvalue = + (lhs->type == NODE_EXPR_CALL || + lhs->type == NODE_EXPR_BINARY || + lhs->type == NODE_EXPR_STRUCT_INIT || + lhs->type == NODE_EXPR_CAST || lhs->type == NODE_MATCH); + + ASTNode *addr = ast_create(NODE_EXPR_UNARY); + addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&"); + addr->unary.operand = lhs; + addr->type_info = type_new_ptr(lt); + arg1 = addr; + } + } else { + // Function expects value + if (is_lhs_ptr) { + // Have pointer, need value -> *lhs + ASTNode *deref = ast_create(NODE_EXPR_UNARY); + deref->unary.op = xstrdup("*"); + deref->unary.operand = lhs; + if (lt && lt->kind == TYPE_POINTER) { + deref->type_info = lt->inner; + } + arg1 = deref; + } + } + + call->call.args = arg1; + arg1->next = rhs; + rhs->next = NULL; + + call->type_info = sig->ret_type; + call->resolved_type = type_to_string(sig->ret_type); + + lhs = call; + continue; // Loop again with result as new lhs + } + } + } + + // Standard Type Checking (if no overload found) + if (lhs->type_info && rhs->type_info) { + if (is_comparison_op(bin->binary.op)) { + bin->type_info = type_new(TYPE_INT); // bool + char *t1 = type_to_string(lhs->type_info); + char *t2 = type_to_string(rhs->type_info); + // Skip type check if either operand is void* (escape hatch type) + int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); + + // Allow comparing pointers/strings with integer literal 0 (NULL) + if (!skip_check) { + int lhs_is_ptr = + (lhs->type_info->kind == TYPE_POINTER || + lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*"))); + int rhs_is_ptr = + (rhs->type_info->kind == TYPE_POINTER || + rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*"))); + + if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL && + rhs->literal.int_val == 0) { + skip_check = 1; + } + if (rhs_is_ptr && lhs->type == NODE_EXPR_LITERAL && + lhs->literal.int_val == 0) { + skip_check = 1; + } + } + + if (!skip_check && !type_eq(lhs->type_info, rhs->type_info)) { + char msg[256]; + sprintf(msg, + "Type mismatch in comparison: cannot compare '%s' and '%s'", + t1, t2); + + char suggestion[256]; + sprintf(suggestion, + "Both operands must have compatible types for comparison"); + + zpanic_with_suggestion(op, msg, suggestion); + } + } else { + if (type_eq(lhs->type_info, rhs->type_info)) { + bin->type_info = lhs->type_info; + } else { + char *t1 = type_to_string(lhs->type_info); + char *t2 = type_to_string(rhs->type_info); + + // Allow pointer arithmetic: ptr + int, ptr - int, int + ptr + int is_ptr_arith = 0; + if (strcmp(bin->binary.op, "+") == 0 || + strcmp(bin->binary.op, "-") == 0) { + int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER || + lhs->type_info->kind == TYPE_STRING || + (t1 && strstr(t1, "*") != NULL)); + int rhs_is_ptr = (rhs->type_info->kind == TYPE_POINTER || + rhs->type_info->kind == TYPE_STRING || + (t2 && strstr(t2, "*") != NULL)); + int lhs_is_int = + (lhs->type_info->kind == TYPE_INT || + lhs->type_info->kind == TYPE_I32 || + lhs->type_info->kind == TYPE_I64 || + lhs->type_info->kind == TYPE_ISIZE || + lhs->type_info->kind == TYPE_USIZE || + (t1 && + (strcmp(t1, "int") == 0 || strcmp(t1, "isize") == 0 || + strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 || + strcmp(t1, "ptrdiff_t") == 0))); + int rhs_is_int = + (rhs->type_info->kind == TYPE_INT || + rhs->type_info->kind == TYPE_I32 || + rhs->type_info->kind == TYPE_I64 || + rhs->type_info->kind == TYPE_ISIZE || + rhs->type_info->kind == TYPE_USIZE || + (t2 && + (strcmp(t2, "int") == 0 || strcmp(t2, "isize") == 0 || + strcmp(t2, "usize") == 0 || strcmp(t2, "size_t") == 0 || + strcmp(t2, "ptrdiff_t") == 0))); + + if ((lhs_is_ptr && rhs_is_int) || (lhs_is_int && rhs_is_ptr)) { + is_ptr_arith = 1; + bin->type_info = lhs_is_ptr ? lhs->type_info : rhs->type_info; + } + } + + if (!is_ptr_arith) { + char msg[256]; + sprintf(msg, "Type mismatch in binary operation '%s'", + bin->binary.op); + + char suggestion[512]; + sprintf( + suggestion, + "Left operand has type '%s', right operand has type '%s'\n = " + "note: Consider casting one operand to match the other", + t1, t2); + + zpanic_with_suggestion(op, msg, suggestion); + } + } + } + } + + lhs = bin; + } + return lhs; } -ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_name) -{ - ASTNode *lambda = ast_create(NODE_LAMBDA); - lambda->lambda.param_names = xmalloc(sizeof(char *)); - lambda->lambda.param_names[0] = param_name; - lambda->lambda.num_params = 1; - - // Default param type: int - lambda->lambda.param_types = xmalloc(sizeof(char *)); - lambda->lambda.param_types[0] = xstrdup("int"); - - // Create Type Info: int -> int - Type *t = type_new(TYPE_FUNCTION); - t->inner = type_new(TYPE_INT); // Return - t->args = xmalloc(sizeof(Type *)); - t->args[0] = type_new(TYPE_INT); // Arg - t->arg_count = 1; - lambda->type_info = t; - - // Body parsing... - ASTNode *body_block = NULL; - if (lexer_peek(l).type == TOK_LBRACE) - { - body_block = parse_block(ctx, l); - } - else - { - ASTNode *expr = parse_expression(ctx, l); - ASTNode *ret = ast_create(NODE_RETURN); - ret->ret.value = expr; - body_block = ast_create(NODE_BLOCK); - body_block->block.statements = ret; - } - lambda->lambda.body = body_block; - lambda->lambda.return_type = xstrdup("int"); - lambda->lambda.lambda_id = ctx->lambda_counter++; - lambda->lambda.is_expression = 1; - register_lambda(ctx, lambda); - analyze_lambda_captures(ctx, lambda); - return lambda; +ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, + char *param_name) { + ASTNode *lambda = ast_create(NODE_LAMBDA); + lambda->lambda.param_names = xmalloc(sizeof(char *)); + lambda->lambda.param_names[0] = param_name; + lambda->lambda.num_params = 1; + + // Default param type: int + lambda->lambda.param_types = xmalloc(sizeof(char *)); + lambda->lambda.param_types[0] = xstrdup("int"); + + // Create Type Info: int -> int + Type *t = type_new(TYPE_FUNCTION); + t->inner = type_new(TYPE_INT); // Return + t->args = xmalloc(sizeof(Type *)); + t->args[0] = type_new(TYPE_INT); // Arg + t->arg_count = 1; + lambda->type_info = t; + + // Body parsing... + ASTNode *body_block = NULL; + if (lexer_peek(l).type == TOK_LBRACE) { + body_block = parse_block(ctx, l); + } else { + ASTNode *expr = parse_expression(ctx, l); + ASTNode *ret = ast_create(NODE_RETURN); + ret->ret.value = expr; + body_block = ast_create(NODE_BLOCK); + body_block->block.statements = ret; + } + lambda->lambda.body = body_block; + lambda->lambda.return_type = xstrdup("int"); + lambda->lambda.lambda_id = ctx->lambda_counter++; + lambda->lambda.is_expression = 1; + register_lambda(ctx, lambda); + analyze_lambda_captures(ctx, lambda); + return lambda; } -ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_names, int num_params) -{ - ASTNode *lambda = ast_create(NODE_LAMBDA); - lambda->lambda.param_names = param_names; - lambda->lambda.num_params = num_params; - - // Type Info construction - Type *t = type_new(TYPE_FUNCTION); - t->inner = type_new(TYPE_INT); - t->args = xmalloc(sizeof(Type *) * num_params); - t->arg_count = num_params; - - lambda->lambda.param_types = xmalloc(sizeof(char *) * num_params); - for (int i = 0; i < num_params; i++) - { - lambda->lambda.param_types[i] = xstrdup("int"); - t->args[i] = type_new(TYPE_INT); - } - lambda->type_info = t; - - // Body parsing... - ASTNode *body_block = NULL; - if (lexer_peek(l).type == TOK_LBRACE) - { - body_block = parse_block(ctx, l); - } - else - { - ASTNode *expr = parse_expression(ctx, l); - ASTNode *ret = ast_create(NODE_RETURN); - ret->ret.value = expr; - body_block = ast_create(NODE_BLOCK); - body_block->block.statements = ret; - } - lambda->lambda.body = body_block; - lambda->lambda.return_type = xstrdup("int"); - lambda->lambda.lambda_id = ctx->lambda_counter++; - lambda->lambda.is_expression = 1; - register_lambda(ctx, lambda); - analyze_lambda_captures(ctx, lambda); - return lambda; +ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, + char **param_names, int num_params) { + ASTNode *lambda = ast_create(NODE_LAMBDA); + lambda->lambda.param_names = param_names; + lambda->lambda.num_params = num_params; + + // Type Info construction + Type *t = type_new(TYPE_FUNCTION); + t->inner = type_new(TYPE_INT); + t->args = xmalloc(sizeof(Type *) * num_params); + t->arg_count = num_params; + + lambda->lambda.param_types = xmalloc(sizeof(char *) * num_params); + for (int i = 0; i < num_params; i++) { + lambda->lambda.param_types[i] = xstrdup("int"); + t->args[i] = type_new(TYPE_INT); + } + lambda->type_info = t; + + // Body parsing... + ASTNode *body_block = NULL; + if (lexer_peek(l).type == TOK_LBRACE) { + body_block = parse_block(ctx, l); + } else { + ASTNode *expr = parse_expression(ctx, l); + ASTNode *ret = ast_create(NODE_RETURN); + ret->ret.value = expr; + body_block = ast_create(NODE_BLOCK); + body_block->block.statements = ret; + } + lambda->lambda.body = body_block; + lambda->lambda.return_type = xstrdup("int"); + lambda->lambda.lambda_id = ctx->lambda_counter++; + lambda->lambda.is_expression = 1; + register_lambda(ctx, lambda); + analyze_lambda_captures(ctx, lambda); + return lambda; } diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 6f93dbf..ea87947 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -7,4098 +7,3473 @@ #include <unistd.h> #include "../ast/ast.h" +#include "../codegen/codegen.h" #include "../plugins/plugin_manager.h" #include "../zen/zen_facts.h" #include "zprep_plugin.h" -#include "../codegen/codegen.h" static char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); -static void check_assignment_condition(ASTNode *cond) -{ - if (!cond) - { - return; - } - if (cond->type == NODE_EXPR_BINARY) - { - if (cond->binary.op && strcmp(cond->binary.op, "=") == 0) - { - zwarn_at(cond->token, "Assignment in condition"); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Did you mean '=='?\n"); - } - } +static void check_assignment_condition(ASTNode *cond) { + if (!cond) { + return; + } + if (cond->type == NODE_EXPR_BINARY) { + if (cond->binary.op && strcmp(cond->binary.op, "=") == 0) { + zwarn_at(cond->token, "Assignment in condition"); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "Did you mean '=='?\n"); + } + } } -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); +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; - } + 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); - } + // 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); - Token gt = lexer_next(l); - gen_param = token_strdup(gt); - if (lexer_next(l).type != TOK_RANGLE) - { - zpanic_at(lexer_peek(l), "Expected >"); - } + char *gen_param = NULL; + if (lexer_peek(l).type == TOK_LANGLE) { + lexer_next(l); + Token gt = lexer_next(l); + gen_param = token_strdup(gt); + if (lexer_next(l).type != TOK_RANGLE) { + zpanic_at(lexer_peek(l), "Expected >"); } + } - enter_scope(ctx); - char **defaults; - int count; - Type **arg_types; - char **param_names; - int is_varargs = 0; + 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 *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); + 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 (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); - } + 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); + 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; +} - extern char *curr_func_ret; - curr_func_ret = ret; +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; +} - // 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; - } +ASTNode *parse_match(ParserContext *ctx, Lexer *l) { + init_builtins(); + Token start_token = lexer_peek(l); + lexer_next(l); // eat 'match' + ASTNode *expr = parse_expression(ctx, l); - // 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) - } + Token t_brace = lexer_next(l); + if (t_brace.type != TOK_LBRACE) { + zpanic_at(t_brace, "Expected { in match"); + } - ASTNode *body = NULL; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); // consume ; + ASTNode *h = 0, *tl = 0; + while (lexer_peek(l).type != TOK_RBRACE) { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) { + break; } - else - { - body = parse_block(ctx, l); + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(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; - } + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) { + break; + } + + // --- 1. Parse Comma-Separated Patterns --- + char patterns_buf[1024]; + patterns_buf[0] = 0; + int pattern_count = 0; + + while (1) { + Token p = lexer_next(l); + char *p_str = token_strdup(p); + + while (lexer_peek(l).type == TOK_DCOLON) { + lexer_next(l); // eat :: + Token suffix = lexer_next(l); + char *tmp = xmalloc(strlen(p_str) + suffix.len + 2); + // Join with underscore: Result::Ok -> Result_Ok + sprintf(tmp, "%s_%.*s", p_str, suffix.len, suffix.start); + free(p_str); + p_str = tmp; + } + + if (pattern_count > 0) { + strcat(patterns_buf, ","); + } + strcat(patterns_buf, p_str); + free(p_str); + pattern_count++; + + Lexer lookahead = *l; + skip_comments(&lookahead); + if (lexer_peek(&lookahead).type == TOK_COMMA) { + lexer_next(l); // eat comma + skip_comments(l); + } else { + break; + } } - exit_scope(ctx); - 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 *pattern = xstrdup(patterns_buf); + int is_default = (strcmp(pattern, "_") == 0); -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); + char *binding = NULL; + int is_destructure = 0; - // 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); + // --- 2. Handle Destructuring: Ok(v) --- + // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)") + if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // eat ( + Token b = lexer_next(l); + if (b.type != TOK_IDENT) { + zpanic_at(b, "Expected variable name in pattern"); + } + binding = token_strdup(b); + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected )"); + } + is_destructure = 1; } - return new_args; -} - -ASTNode *parse_match(ParserContext *ctx, Lexer *l) -{ - init_builtins(); - Token start_token = lexer_peek(l); - lexer_next(l); // eat 'match' - ASTNode *expr = parse_expression(ctx, l); - Token t_brace = lexer_next(l); - if (t_brace.type != TOK_LBRACE) - { - zpanic_at(t_brace, "Expected { in match"); + // --- 3. Parse Guard (if condition) --- + ASTNode *guard = NULL; + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "if", 2) == 0) { + lexer_next(l); + guard = parse_expression(ctx, l); + check_assignment_condition(guard); } - ASTNode *h = 0, *tl = 0; - while (lexer_peek(l).type != TOK_RBRACE) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - break; - } - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - break; - } - - // --- 1. Parse Comma-Separated Patterns --- - char patterns_buf[1024]; - patterns_buf[0] = 0; - int pattern_count = 0; - - while (1) - { - Token p = lexer_next(l); - char *p_str = token_strdup(p); - - while (lexer_peek(l).type == TOK_DCOLON) - { - lexer_next(l); // eat :: - Token suffix = lexer_next(l); - char *tmp = xmalloc(strlen(p_str) + suffix.len + 2); - // Join with underscore: Result::Ok -> Result_Ok - sprintf(tmp, "%s_%.*s", p_str, suffix.len, suffix.start); - free(p_str); - p_str = tmp; - } - - if (pattern_count > 0) - { - strcat(patterns_buf, ","); - } - strcat(patterns_buf, p_str); - free(p_str); - pattern_count++; - - Lexer lookahead = *l; - skip_comments(&lookahead); - if (lexer_peek(&lookahead).type == TOK_COMMA) - { - lexer_next(l); // eat comma - skip_comments(l); - } - else - { - break; - } - } - - char *pattern = xstrdup(patterns_buf); - int is_default = (strcmp(pattern, "_") == 0); - - char *binding = NULL; - int is_destructure = 0; - - // --- 2. Handle Destructuring: Ok(v) --- - // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)") - if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // eat ( - Token b = lexer_next(l); - if (b.type != TOK_IDENT) - { - zpanic_at(b, "Expected variable name in pattern"); - } - binding = token_strdup(b); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - is_destructure = 1; - } - - // --- 3. Parse Guard (if condition) --- - ASTNode *guard = NULL; - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0) - { - lexer_next(l); - guard = parse_expression(ctx, l); - check_assignment_condition(guard); - } - - if (lexer_next(l).type != TOK_ARROW) - { - zpanic_at(lexer_peek(l), "Expected =>"); - } - - ASTNode *body; - Token pk = lexer_peek(l); - if (pk.type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else if (pk.type == TOK_ASSERT || - (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0)) - { - body = parse_assert(ctx, l); - } - else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) - { - body = parse_return(ctx, l); - } - else - { - body = parse_expression(ctx, l); - } - - ASTNode *c = ast_create(NODE_MATCH_CASE); - c->match_case.pattern = pattern; - c->match_case.binding_name = binding; - c->match_case.is_destructuring = is_destructure; - c->match_case.guard = guard; - c->match_case.body = body; - c->match_case.is_default = is_default; - - if (!h) - { - h = c; - } - else - { - tl->next = c; - } - tl = c; + if (lexer_next(l).type != TOK_ARROW) { + zpanic_at(lexer_peek(l), "Expected =>"); } - lexer_next(l); // eat } - ASTNode *n = ast_create(NODE_MATCH); - n->line = start_token.line; - n->token = start_token; // Capture token for rich warning - n->match_stmt.expr = expr; - n->match_stmt.cases = h; - return n; + ASTNode *body; + Token pk = lexer_peek(l); + if (pk.type == TOK_LBRACE) { + body = parse_block(ctx, l); + } else if (pk.type == TOK_ASSERT || + (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0)) { + body = parse_assert(ctx, l); + } else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) { + body = parse_return(ctx, l); + } else { + body = parse_expression(ctx, l); + } + + ASTNode *c = ast_create(NODE_MATCH_CASE); + c->match_case.pattern = pattern; + c->match_case.binding_name = binding; + c->match_case.is_destructuring = is_destructure; + c->match_case.guard = guard; + c->match_case.body = body; + c->match_case.is_default = is_default; + + if (!h) { + h = c; + } else { + tl->next = c; + } + tl = c; + } + lexer_next(l); // eat } + + ASTNode *n = ast_create(NODE_MATCH); + n->line = start_token.line; + n->token = start_token; // Capture token for rich warning + n->match_stmt.expr = expr; + n->match_stmt.cases = h; + return n; } -ASTNode *parse_loop(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - ASTNode *b = parse_block(ctx, l); - ASTNode *n = ast_create(NODE_LOOP); - n->loop_stmt.body = b; - return n; +ASTNode *parse_loop(ParserContext *ctx, Lexer *l) { + lexer_next(l); + ASTNode *b = parse_block(ctx, l); + ASTNode *n = ast_create(NODE_LOOP); + n->loop_stmt.body = b; + return n; } -ASTNode *parse_repeat(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - char *c = rewrite_expr_methods(ctx, parse_condition_raw(ctx, l)); - ASTNode *b = parse_block(ctx, l); - ASTNode *n = ast_create(NODE_REPEAT); - n->repeat_stmt.count = c; - n->repeat_stmt.body = b; - return n; +ASTNode *parse_repeat(ParserContext *ctx, Lexer *l) { + lexer_next(l); + char *c = rewrite_expr_methods(ctx, parse_condition_raw(ctx, l)); + ASTNode *b = parse_block(ctx, l); + ASTNode *n = ast_create(NODE_REPEAT); + n->repeat_stmt.count = c; + n->repeat_stmt.body = b; + return n; } -ASTNode *parse_unless(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - ASTNode *cond = parse_expression(ctx, l); - ASTNode *body = parse_block(ctx, l); - ASTNode *n = ast_create(NODE_UNLESS); - n->unless_stmt.condition = cond; - n->unless_stmt.body = body; - return n; +ASTNode *parse_unless(ParserContext *ctx, Lexer *l) { + lexer_next(l); + ASTNode *cond = parse_expression(ctx, l); + ASTNode *body = parse_block(ctx, l); + ASTNode *n = ast_create(NODE_UNLESS); + n->unless_stmt.condition = cond; + n->unless_stmt.body = body; + return n; } -ASTNode *parse_guard(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // consume 'guard' - - // Parse the condition as an AST - ASTNode *cond = parse_expression(ctx, l); - - // Check for 'else' - Token t = lexer_peek(l); - if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) - { - zpanic_at(t, "Expected 'else' after guard condition"); - } - lexer_next(l); // consume 'else' - - // Parse the body - either a block or a single statement - ASTNode *body; - if (lexer_peek(l).type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else - { - // Single statement (e.g., guard x != NULL else return;) - body = parse_statement(ctx, l); - } - - // Create the node - ASTNode *n = ast_create(NODE_GUARD); - n->guard_stmt.condition = cond; - n->guard_stmt.body = body; - return n; +ASTNode *parse_guard(ParserContext *ctx, Lexer *l) { + lexer_next(l); // consume 'guard' + + // Parse the condition as an AST + ASTNode *cond = parse_expression(ctx, l); + + // Check for 'else' + Token t = lexer_peek(l); + if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) { + zpanic_at(t, "Expected 'else' after guard condition"); + } + lexer_next(l); // consume 'else' + + // Parse the body - either a block or a single statement + ASTNode *body; + if (lexer_peek(l).type == TOK_LBRACE) { + body = parse_block(ctx, l); + } else { + // Single statement (e.g., guard x != NULL else return;) + body = parse_statement(ctx, l); + } + + // Create the node + ASTNode *n = ast_create(NODE_GUARD); + n->guard_stmt.condition = cond; + n->guard_stmt.body = body; + return n; } -ASTNode *parse_defer(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // defer - ASTNode *s; - if (lexer_peek(l).type == TOK_LBRACE) - { - s = parse_block(ctx, l); - } - else - { - s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = consume_and_rewrite(ctx, l); - } - ASTNode *n = ast_create(NODE_DEFER); - n->defer_stmt.stmt = s; - return n; +ASTNode *parse_defer(ParserContext *ctx, Lexer *l) { + lexer_next(l); // defer + ASTNode *s; + if (lexer_peek(l).type == TOK_LBRACE) { + s = parse_block(ctx, l); + } else { + s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = consume_and_rewrite(ctx, l); + } + ASTNode *n = ast_create(NODE_DEFER); + n->defer_stmt.stmt = s; + return n; } -ASTNode *parse_asm(ParserContext *ctx, Lexer *l) -{ - (void)ctx; // suppress unused parameter warning - Token t = lexer_peek(l); - zen_trigger_at(TRIGGER_ASM, t); - lexer_next(l); // eat 'asm' +ASTNode *parse_asm(ParserContext *ctx, Lexer *l) { + (void)ctx; // suppress unused parameter warning + Token t = lexer_peek(l); + zen_trigger_at(TRIGGER_ASM, t); + lexer_next(l); // eat 'asm' - // Check for 'volatile' - int is_volatile = 0; - if (lexer_peek(l).type == TOK_VOLATILE) - { - is_volatile = 1; - lexer_next(l); - } - - // Expect { - if (lexer_peek(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected { after asm"); - } + // Check for 'volatile' + int is_volatile = 0; + if (lexer_peek(l).type == TOK_VOLATILE) { + is_volatile = 1; lexer_next(l); + } - // Parse assembly template strings - char *code = xmalloc(4096); // Buffer for assembly code - code[0] = 0; + // Expect { + if (lexer_peek(l).type != TOK_LBRACE) { + zpanic_at(lexer_peek(l), "Expected { after asm"); + } + lexer_next(l); - while (1) - { - Token t = lexer_peek(l); + // Parse assembly template strings + char *code = xmalloc(4096); // Buffer for assembly code + code[0] = 0; - // Check for end of asm block or start of operands - if (t.type == TOK_RBRACE) - { - break; - } - if (t.type == TOK_COLON) - { - break; - } + while (1) { + Token t = lexer_peek(l); - // Support string literals for assembly instructions - if (t.type == TOK_STRING) - { - lexer_next(l); - // Extract string content (strip quotes) - int str_len = t.len - 2; - if (strlen(code) > 0) - { - strcat(code, "\n"); - } - strncat(code, t.start + 1, str_len); - } - // Also support bare identifiers for simple instructions like 'nop', 'pause' - else if (t.type == TOK_IDENT) - { + // Check for end of asm block or start of operands + if (t.type == TOK_RBRACE) { + break; + } + if (t.type == TOK_COLON) { + break; + } + + // Support string literals for assembly instructions + if (t.type == TOK_STRING) { + lexer_next(l); + // Extract string content (strip quotes) + int str_len = t.len - 2; + if (strlen(code) > 0) { + strcat(code, "\n"); + } + strncat(code, t.start + 1, str_len); + } + // Also support bare identifiers for simple instructions like 'nop', 'pause' + else if (t.type == TOK_IDENT) { + lexer_next(l); + if (strlen(code) > 0) { + strcat(code, "\n"); + } + strncat(code, t.start, t.len); + + // Check for instruction arguments + while (lexer_peek(l).type != TOK_RBRACE && + lexer_peek(l).type != TOK_COLON) { + Token arg = lexer_peek(l); + + if (arg.type == TOK_SEMICOLON) { + lexer_next(l); + break; + } + + // Handle substitution {var} + if (arg.type == TOK_LBRACE) { + lexer_next(l); + strcat(code, "{"); + // Consume until } + while (lexer_peek(l).type != TOK_RBRACE && + lexer_peek(l).type != TOK_EOF) { + Token sub = lexer_next(l); + strncat(code, sub.start, sub.len); + } + if (lexer_peek(l).type == TOK_RBRACE) { lexer_next(l); - if (strlen(code) > 0) - { - strcat(code, "\n"); - } - strncat(code, t.start, t.len); - - // Check for instruction arguments - while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_COLON) - { - Token arg = lexer_peek(l); - - if (arg.type == TOK_SEMICOLON) - { - lexer_next(l); - break; - } - - // Handle substitution {var} - if (arg.type == TOK_LBRACE) - { - lexer_next(l); - strcat(code, "{"); - // Consume until } - while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_EOF) - { - Token sub = lexer_next(l); - strncat(code, sub.start, sub.len); - } - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - strcat(code, "}"); - } - continue; - } - - if (arg.type == TOK_IDENT) - { - // Check prev char for % or $ - char last_char = 0; - size_t clen = strlen(code); - if (clen > 0) - { - if (code[clen - 1] == ' ' && clen > 1) - { - last_char = code[clen - 2]; - } - else - { - last_char = code[clen - 1]; - } - } - if (last_char != '%' && last_char != '$' && last_char != ',') - { - break; - } - } - - lexer_next(l); - - // No space logic - int no_space = 0; - size_t clen = strlen(code); - if (clen > 0) - { - char lc = code[clen - 1]; - if (lc == '%' || lc == '$') - { - no_space = 1; - } - } - - if (!no_space) - { - strcat(code, " "); - } - strncat(code, arg.start, arg.len); - } - } - else - { - zpanic_at(t, "Expected assembly string, instruction, or ':' in asm block"); + strcat(code, "}"); + } + continue; + } + + if (arg.type == TOK_IDENT) { + // Check prev char for % or $ + char last_char = 0; + size_t clen = strlen(code); + if (clen > 0) { + if (code[clen - 1] == ' ' && clen > 1) { + last_char = code[clen - 2]; + } else { + last_char = code[clen - 1]; + } + } + if (last_char != '%' && last_char != '$' && last_char != ',') { + break; + } } - } - - // Parse outputs (: out(x), inout(y)) - char **outputs = NULL; - char **output_modes = NULL; - int num_outputs = 0; - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); // eat : - - outputs = xmalloc(sizeof(char *) * 16); - output_modes = xmalloc(sizeof(char *) * 16); - - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_COLON || t.type == TOK_RBRACE) - { - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } + lexer_next(l); - // Parse out(var) or inout(var) - if (t.type == TOK_IDENT) - { - char *mode = token_strdup(t); - lexer_next(l); - - if (lexer_peek(l).type != TOK_LPAREN) - { - zpanic_at(lexer_peek(l), "Expected ( after output mode"); - } - lexer_next(l); - - Token var = lexer_next(l); - if (var.type != TOK_IDENT) - { - zpanic_at(var, "Expected variable name"); - } - - if (lexer_peek(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after variable"); - } - lexer_next(l); - - outputs[num_outputs] = token_strdup(var); - output_modes[num_outputs] = mode; - num_outputs++; - } - else - { - break; - } + // No space logic + int no_space = 0; + size_t clen = strlen(code); + if (clen > 0) { + char lc = code[clen - 1]; + if (lc == '%' || lc == '$') { + no_space = 1; + } } - } - - // Parse inputs (: in(a), in(b)) - char **inputs = NULL; - int num_inputs = 0; - - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); // eat : - - inputs = xmalloc(sizeof(char *) * 16); - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_COLON || t.type == TOK_RBRACE) - { - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } - - // Parse in(var) - if (t.type == TOK_IDENT && strncmp(t.start, "in", 2) == 0) - { - lexer_next(l); - - if (lexer_peek(l).type != TOK_LPAREN) - { - zpanic_at(lexer_peek(l), "Expected ( after in"); - } - lexer_next(l); - - Token var = lexer_next(l); - if (var.type != TOK_IDENT) - { - zpanic_at(var, "Expected variable name"); - } - - if (lexer_peek(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) after variable"); - } - lexer_next(l); - - inputs[num_inputs] = token_strdup(var); - num_inputs++; - } - else - { - break; - } + if (!no_space) { + strcat(code, " "); } + strncat(code, arg.start, arg.len); + } + } else { + zpanic_at(t, + "Expected assembly string, instruction, or ':' in asm block"); } + } - // Parse clobbers (: "eax", "memory") - char **clobbers = NULL; - int num_clobbers = 0; + // Parse outputs (: out(x), inout(y)) + char **outputs = NULL; + char **output_modes = NULL; + int num_outputs = 0; - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); // eat : + if (lexer_peek(l).type == TOK_COLON) { + lexer_next(l); // eat : - clobbers = xmalloc(sizeof(char *) * 16); + outputs = xmalloc(sizeof(char *) * 16); + output_modes = xmalloc(sizeof(char *) * 16); - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_RBRACE) - { - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_COLON || t.type == TOK_RBRACE) { + break; + } + if (t.type == TOK_COMMA) { + lexer_next(l); + continue; + } - if (t.type == TOK_STRING) - { - lexer_next(l); - // Extract string content - char *clob = xmalloc(t.len); - strncpy(clob, t.start + 1, t.len - 2); - clob[t.len - 2] = 0; - clobbers[num_clobbers++] = clob; - } - else - { - break; - } - } - } + // Parse out(var) or inout(var) + if (t.type == TOK_IDENT) { + char *mode = token_strdup(t); + lexer_next(l); - // Expect closing } - if (lexer_peek(l).type != TOK_RBRACE) - { - zpanic_at(lexer_peek(l), "Expected } at end of asm block"); - } - lexer_next(l); + if (lexer_peek(l).type != TOK_LPAREN) { + zpanic_at(lexer_peek(l), "Expected ( after output mode"); + } + lexer_next(l); - // Create AST node - ASTNode *n = ast_create(NODE_ASM); - n->asm_stmt.code = code; - n->asm_stmt.is_volatile = is_volatile; - n->asm_stmt.outputs = outputs; - n->asm_stmt.output_modes = output_modes; - n->asm_stmt.inputs = inputs; - n->asm_stmt.clobbers = clobbers; - n->asm_stmt.num_outputs = num_outputs; - n->asm_stmt.num_inputs = num_inputs; - n->asm_stmt.num_clobbers = num_clobbers; + Token var = lexer_next(l); + if (var.type != TOK_IDENT) { + zpanic_at(var, "Expected variable name"); + } - return n; -} + if (lexer_peek(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after variable"); + } + lexer_next(l); -ASTNode *parse_test(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'test' - Token t = lexer_next(l); - if (t.type != TOK_STRING) - { - zpanic_at(t, "Test name must be a string literal"); + outputs[num_outputs] = token_strdup(var); + output_modes[num_outputs] = mode; + num_outputs++; + } else { + break; + } } + } - // Strip quotes for AST storage - char *name = xmalloc(t.len); - strncpy(name, t.start + 1, t.len - 2); - name[t.len - 2] = 0; + // Parse inputs (: in(a), in(b)) + char **inputs = NULL; + int num_inputs = 0; - ASTNode *body = parse_block(ctx, l); + if (lexer_peek(l).type == TOK_COLON) { + lexer_next(l); // eat : - ASTNode *n = ast_create(NODE_TEST); - n->test_stmt.name = name; - n->test_stmt.body = body; - return n; -} + inputs = xmalloc(sizeof(char *) * 16); -ASTNode *parse_assert(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // assert - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // optional paren? usually yes - } - - ASTNode *cond = parse_expression(ctx, l); - - char *msg = NULL; - if (lexer_peek(l).type == TOK_COMMA) - { + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_COLON || t.type == TOK_RBRACE) { + break; + } + if (t.type == TOK_COMMA) { lexer_next(l); - Token st = lexer_next(l); - if (st.type != TOK_STRING) - { - zpanic_at(st, "Expected message string"); - } - msg = xmalloc(st.len + 1); - strncpy(msg, st.start, st.len); - msg[st.len] = 0; - } + continue; + } - if (lexer_peek(l).type == TOK_RPAREN) - { + // Parse in(var) + if (t.type == TOK_IDENT && strncmp(t.start, "in", 2) == 0) { lexer_next(l); - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *n = ast_create(NODE_ASSERT); - n->assert_stmt.condition = cond; - n->assert_stmt.message = msg; - return n; -} -// 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; + if (lexer_peek(l).type != TOK_LPAREN) { + zpanic_at(lexer_peek(l), "Expected ( after in"); } - } -} - -ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'var' - - // Check for 'mut' keyword - int is_mutable = 0; - if (lexer_peek(l).type == TOK_MUT) - { - is_mutable = 1; lexer_next(l); - } - else - { - // Default mutability depends on directive - is_mutable = !ctx->immutable_by_default; - } - // 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); - // Register mutability for each destructured variable - register_var_mutability(ctx, nm, is_mutable); - 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 ="); + Token var = lexer_next(l); + if (var.type != TOK_IDENT) { + zpanic_at(var, "Expected variable name"); } - ASTNode *init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); + + if (lexer_peek(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) after variable"); } - 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; + lexer_next(l); + + inputs[num_inputs] = token_strdup(var); + num_inputs++; + } else { + break; + } } + } - // Normal Declaration OR Named Struct Destructuring - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); + // Parse clobbers (: "eax", "memory") + char **clobbers = NULL; + int num_clobbers = 0; - // 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); - register_var_mutability(ctx, names[count], is_mutable); + if (lexer_peek(l).type == TOK_COLON) { + lexer_next(l); // eat : - count++; + clobbers = xmalloc(sizeof(char *) * 16); - Token next = lexer_next(l); - if (next.type == TOK_RBRACE) - { - break; - } - if (next.type != TOK_COMMA) - { - zpanic_at(next, "Expected comma in struct pattern"); - } - } + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_RBRACE) { + break; + } + if (t.type == TOK_COMMA) { + lexer_next(l); + continue; + } - 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); - } + if (t.type == TOK_STRING) { + lexer_next(l); + // Extract string content + char *clob = xmalloc(t.len); + strncpy(clob, t.start + 1, t.len - 2); + clob[t.len - 2] = 0; + clobbers[num_clobbers++] = clob; + } else { + break; + } + } + } + + // Expect closing } + if (lexer_peek(l).type != TOK_RBRACE) { + zpanic_at(lexer_peek(l), "Expected } at end of asm block"); + } + lexer_next(l); + + // Create AST node + ASTNode *n = ast_create(NODE_ASM); + n->asm_stmt.code = code; + n->asm_stmt.is_volatile = is_volatile; + n->asm_stmt.outputs = outputs; + n->asm_stmt.output_modes = output_modes; + n->asm_stmt.inputs = inputs; + n->asm_stmt.clobbers = clobbers; + n->asm_stmt.num_outputs = num_outputs; + n->asm_stmt.num_inputs = num_inputs; + n->asm_stmt.num_clobbers = num_clobbers; + + return n; +} - 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; - } +ASTNode *parse_test(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat 'test' + Token t = lexer_next(l); + if (t.type != TOK_STRING) { + zpanic_at(t, "Test name must be a string literal"); + } - // 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); + // Strip quotes for AST storage + char *name = xmalloc(t.len); + strncpy(name, t.start + 1, t.len - 2); + name[t.len - 2] = 0; - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); - } + ASTNode *body = parse_block(ctx, l); - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); - } + ASTNode *n = ast_create(NODE_TEST); + n->test_stmt.name = name; + n->test_stmt.body = body; + return n; +} - ASTNode *init = parse_expression(ctx, l); +ASTNode *parse_assert(ParserContext *ctx, Lexer *l) { + lexer_next(l); // assert + if (lexer_peek(l).type == TOK_LPAREN) { + lexer_next(l); // optional paren? usually yes + } - 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 *cond = parse_expression(ctx, l); - 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); - } + char *msg = NULL; + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + Token st = lexer_next(l); + if (st.type != TOK_STRING) { + zpanic_at(st, "Expected message string"); + } + msg = xmalloc(st.len + 1); + strncpy(msg, st.start, st.len); + msg[st.len] = 0; + } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } + if (lexer_peek(l).type == TOK_RPAREN) { + lexer_next(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; + ASTNode *n = ast_create(NODE_ASSERT); + n->assert_stmt.condition = cond; + n->assert_stmt.message = msg; + return n; +} - add_symbol(ctx, val_name, "unknown", NULL); - register_var_mutability(ctx, val_name, is_mutable); +// 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; + } + } +} - return n; - } +ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat 'var' - char *type = NULL; - Type *type_obj = NULL; // --- NEW: Formal Type Object --- + // Check for 'mut' keyword + int is_mutable = 0; + if (lexer_peek(l).type == TOK_MUT) { + is_mutable = 1; + lexer_next(l); + } else { + // Default mutability depends on directive + is_mutable = !ctx->immutable_by_default; + } + + // 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); + // Register mutability for each destructured variable + register_var_mutability(ctx, nm, is_mutable); + 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; + } - 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); - } + // Normal Declaration OR Named Struct Destructuring + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); - ASTNode *init = NULL; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) - { - lexer_next(l); + // 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; - // Peek for special initializers - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) - { - char *e = parse_embed(ctx, l); - init = ast_create(NODE_RAW_STMT); - init->raw_stmt.content = e; - 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); - } + while (1) { + // Parse field:name or just name + Token t = lexer_next(l); + char *ident = token_strdup(t); - if (init && type) - { - char *rhs_type = init->resolved_type; - if (!rhs_type && init->type_info) - { - rhs_type = type_to_string(init->type_info); - } + 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); + register_var_mutability(ctx, names[count], is_mutable); + + 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; + } - 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 - } - } - } + // 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); - // ** 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 (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); } - if (!type && !init) - { - zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); + if (lexer_next(l).type != TOK_OP) { + zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); } - // Register in symbol table with actual token - add_symbol_with_token(ctx, name, type, type_obj, name_tok); - register_var_mutability(ctx, name, is_mutable); + ASTNode *init = parse_expression(ctx, l); - // NEW: Capture Const Integer Values - if (!is_mutable && 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; - } + Token t = lexer_next(l); + if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) { + zpanic_at(t, "Expected 'else' in guard statement"); } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); + 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); } - 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->var_decl.is_mutable = is_mutable; - 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; - } + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); } - n->var_decl.init_expr = init; + 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; - // 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); - } + add_symbol(ctx, val_name, "unknown", NULL); + register_var_mutability(ctx, val_name, is_mutable); - // Check for 'defer' (Value-Returning Defer) - if (lexer_peek(l).type == TOK_DEFER) - { - lexer_next(l); // eat defer - // Parse the defer expression/statement - // Usually defer close(it); - // We parse expression. - ASTNode *expr = parse_expression(ctx, l); + return n; + } - // Handle "it" substitution - replace_it_with_var(expr, name); + char *type = NULL; + Type *type_obj = NULL; // --- NEW: Formal Type Object --- - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } + 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 *d = ast_create(NODE_DEFER); - d->defer_stmt.stmt = expr; + ASTNode *init = NULL; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) { + lexer_next(l); - // Chain it: var_decl -> defer - n->next = d; + // Peek for special initializers + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) { + char *e = parse_embed(ctx, l); + init = ast_create(NODE_RAW_STMT); + init->raw_stmt.content = e; + 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); } - return n; -} + if (init && type) { + char *rhs_type = init->resolved_type; + if (!rhs_type && init->type_info) { + rhs_type = type_to_string(init->type_info); + } -ASTNode *parse_const(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat const - Token n = lexer_next(l); + 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; - char *type_str = NULL; - Type *type_obj = NULL; + ASTNode *def = find_struct_def(ctx, source_struct); - 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); - } + 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 - char *ns = token_strdup(n); - if (!type_obj) - { - type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object + init = cast; // Replace init with cast + } + } } - 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); - } - } - } + // ** 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); + register_var_mutability(ctx, name, is_mutable); + + // NEW: Capture Const Integer Values + if (!is_mutable && 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->var_decl.is_mutable = is_mutable; + 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; + + // 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) + if (lexer_peek(l).type == TOK_DEFER) { + lexer_next(l); // eat defer + // Parse the defer expression/statement + // Usually defer close(it); + // We parse expression. + ASTNode *expr = parse_expression(ctx, l); - 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); - } + // Handle "it" substitution + replace_it_with_var(expr, name); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - 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; + ASTNode *d = ast_create(NODE_DEFER); + d->defer_stmt.stmt = expr; - if (!ctx->current_scope || !ctx->current_scope->parent) - { - add_to_global_list(ctx, o); - } + // Chain it: var_decl -> defer + n->next = d; + } - return o; + return n; } -ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - Token n = lexer_next(l); - lexer_next(l); - char *o = parse_type(ctx, l); - lexer_next(l); - 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; - return node; -} +ASTNode *parse_const(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat const + Token n = lexer_next(l); -ASTNode *parse_return(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'return' - ASTNode *n = ast_create(NODE_RETURN); + char *type_str = NULL; + Type *type_obj = NULL; - int handled = 0; + 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); - // 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) - { + // 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); + } - // Peek ahead to distinguish "(expr)" from "(a, b)" - int is_tuple_lit = 0; - int depth = 0; + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } - // Just scan tokens manually using a temp lexer to be safe - Lexer temp_l = *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; - while (1) - { - Token t = lexer_next(&temp_l); - if (t.type == TOK_EOF) - { - break; - } - if (t.type == TOK_SEMICOLON) - { - break; // Safety break - } + if (!ctx->current_scope || !ctx->current_scope->parent) { + add_to_global_list(ctx, o); + } - if (t.type == TOK_LPAREN) - { - depth++; - } - if (t.type == TOK_RPAREN) - { - depth--; - if (depth == 0) - { - break; // End of potential tuple - } - } + return o; +} - // 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; - } - } +ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) { + lexer_next(l); + Token n = lexer_next(l); + lexer_next(l); + char *o = parse_type(ctx, l); + lexer_next(l); + 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; + return 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; - } - } - // 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) - { - 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 *parse_return(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat 'return' + ASTNode *n = ast_create(NODE_RETURN); + + int handled = 0; + + // 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) { + + // 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(&temp_l); + if (t.type == TOK_EOF) { + break; + } + if (t.type == TOK_SEMICOLON) { + break; // Safety break + } + + if (t.type == TOK_LPAREN) { + depth++; + } + if (t.type == TOK_RPAREN) { + depth--; + if (depth == 0) { + break; // End of potential tuple + } + } + + // 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 (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; + } + } + // 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) { + 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; + } + + // 3. Standard Expression Return + if (!handled) { + if (lexer_peek(l).type == TOK_SEMICOLON) { + n->ret.value = NULL; + } else { + n->ret.value = parse_expression(ctx, l); + } + } + + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } + return n; +} - // 3. Standard Expression Return - if (!handled) - { - if (lexer_peek(l).type == TOK_SEMICOLON) - { - n->ret.value = NULL; - } - else - { - n->ret.value = parse_expression(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 (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - return n; + ASTNode *then_b = NULL; + if (lexer_peek(l).type == TOK_LBRACE) { + then_b = 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; + } + + 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; + 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 *parse_while(ParserContext *ctx, Lexer *l) { + 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; +} - ASTNode *then_b = NULL; - if (lexer_peek(l).type == TOK_LBRACE) - { - then_b = parse_block(ctx, l); - } - else - { - // Single statement: Wrap in scope + block +ASTNode *parse_for(ParserContext *ctx, Lexer *l) { + lexer_next(l); + + // 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); + if (lexer_peek(l).type == TOK_DOTDOT) { + lexer_next(l); // consume .. + 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; + + 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; + } + + // Fix: Enter scope to register loop variable enter_scope(ctx); - ASTNode *s = parse_statement(ctx, l); + // Register loop variable so body can see it + add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT)); + + // Handle body (brace or single stmt) + 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); - then_b = ast_create(NODE_BLOCK); - then_b->block.statements = s; + + return n; + } } + l->pos = saved_pos; // Restore + } - ASTNode *else_b = NULL; - skip_comments(l); - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "else", 4) == 0) - { + // 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); - 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; - return n; -} - -ASTNode *parse_while(ParserContext *ctx, Lexer *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 *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; -} + ASTNode *step = NULL; + if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) { + step = parse_expression(ctx, l); + } -ASTNode *parse_for(ParserContext *ctx, Lexer *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; +} - // 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); - if (lexer_peek(l).type == TOK_DOTDOT) - { - lexer_next(l); // consume .. - 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; - - 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; - } - - // Fix: Enter scope to register loop variable - enter_scope(ctx); - // Register loop variable so body can see it - add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT)); - - // Handle body (brace or single stmt) - 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; - } - } - l->pos = saved_pos; // Restore - } +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, "({ "); - // C-Style For Loop - enter_scope(ctx); - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - } + char *s = xstrdup(content); + char *cur = s; - 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); + while (*cur) { + // 1. Find text before the next '{' + char *brace = cur; + while (*brace && *brace != '{') { + brace++; } - 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); + 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, "\"); "); } - ASTNode *step = NULL; - if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) - { - step = parse_expression(ctx, l); + if (*brace == 0) { + break; } - if (lexer_peek(l).type == TOK_RPAREN) - { - lexer_next(l); + // 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) { + 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 + { + Lexer lex; + lexer_init(&lex, clean_expr); + 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); + } + } + } + } + + char *type = find_symbol_type(ctx, clean_expr); + char *allocated_expr = NULL; + + if (type) { + char func_name[512]; + snprintf(func_name, sizeof(func_name), "%s_to_string", type); + + // If type is a pointer (Struct*), check if base Struct has to_string + if (!find_func(ctx, func_name) && strchr(type, '*')) { + char base[256]; + strcpy(base, type); + base[strlen(base) - 1] = 0; // remove '*' + snprintf(func_name, sizeof(func_name), "%s_to_string", base); + } + + // If a _to_string method exists, rewrite 'x' -> 'x.to_string()' + if (find_func(ctx, func_name)) { + allocated_expr = xmalloc(strlen(clean_expr) + 20); + sprintf(allocated_expr, "%s.to_string()", clean_expr); + expr = allocated_expr; + } + } + + // Rewrite the expression to handle pointer access (header_ptr.magic -> + // header_ptr->magic) + char *wrapped_expr = xmalloc(strlen(expr) + 5); + sprintf(wrapped_expr, "#{%s}", expr); + char *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, "); "); + } } - ASTNode *body; - if (lexer_peek(l).type == TOK_LBRACE) - { - body = parse_block(ctx, l); - } - else - { - body = parse_statement(ctx, l); + free(rw_expr); // Don't forget to free! + if (allocated_expr) { + free(allocated_expr); // Don't forget to free the auto-generated call! } - 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) - { - // 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) - { - colon = p; - } - if (depth == 0) - { - break; - } - p++; - } + cur = p + 1; + } - *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; - } + if (newline) { + char buf[128]; + sprintf(buf, "fprintf(%s, \"\\n\"); ", target); + strcat(gen, buf); + } else { + strcat(gen, "fflush(stdout); "); + } - char *clean_expr = expr; - while (*clean_expr == ' ') - { - clean_expr++; // Skip leading spaces - } + strcat(gen, "0; })"); - // Analyze usage - { - Lexer lex; - lexer_init(&lex, clean_expr); - 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); - } - } - } - } + free(s); + return gen; +} - char *type = find_symbol_type(ctx, clean_expr); - char *allocated_expr = NULL; - - if (type) - { - char func_name[512]; - snprintf(func_name, sizeof(func_name), "%s_to_string", type); - - // If type is a pointer (Struct*), check if base Struct has to_string - if (!find_func(ctx, func_name) && strchr(type, '*')) - { - char base[256]; - strcpy(base, type); - base[strlen(base) - 1] = 0; // remove '*' - snprintf(func_name, sizeof(func_name), "%s_to_string", base); - } +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 a _to_string method exists, rewrite 'x' -> 'x.to_string()' - if (find_func(ctx, func_name)) - { - allocated_expr = xmalloc(strlen(clean_expr) + 20); - sprintf(allocated_expr, "%s.to_string()", clean_expr); - expr = allocated_expr; - } - } + if (t.type == TOK_LBRACE) { + depth++; + } + if (t.type == TOK_RBRACE) { + depth--; + } - // Rewrite the expression to handle pointer access (header_ptr.magic -> - // header_ptr->magic) - char *wrapped_expr = xmalloc(strlen(expr) + 5); - sprintf(wrapped_expr, "#{%s}", expr); - char *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, "); "); + 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++; } - 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, "); "); - } - } + strncat(body, t.start, t.len); + body_len += t.len; + } + } - free(rw_expr); // Don't forget to free! - if (allocated_expr) - { - free(allocated_expr); // Don't forget to free the auto-generated call! - } + 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; +} - cur = p + 1; - } +ASTNode *parse_statement(ParserContext *ctx, Lexer *l) { + Token tk = lexer_peek(l); + ASTNode *s = NULL; - if (newline) - { - char buf[128]; - sprintf(buf, "fprintf(%s, \"\\n\"); ", target); - strcat(gen, buf); + 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"); } - else - { - strcat(gen, "fflush(stdout); "); + 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; - 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; + 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; + } } - lexer_next(l); // consume ! - // Expect { - if (lexer_peek(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected { after macro invocation"); + if (head && !head->next) { + return head; } - 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"); + 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_LBRACE) { + depth++; } - if (t.type == TOK_RBRACE) - { - depth--; + if (t.type == TOK_RBRACE) { + depth--; } + } + const char *end = l->src + l->pos - 1; + size_t len = end - start; - 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; - } - } + char *content = xmalloc(len + 1); + memcpy(content, start, len); + content[len] = 0; - last_line = t.line; - lexer_next(l); + ASTNode *s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + return s; } - // 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); + // Check for plugin blocks + if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) { + lexer_next(l); // consume 'plugin' + return parse_plugin(ctx, l); } - // 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); + if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) { + return parse_var_decl(ctx, l); } - // Execute Plugin Immediately (Expansion) - FILE *capture = tmpfile(); - if (!capture) - { - zpanic_at(start_tok, "Failed to create capture buffer for plugin expansion"); + // 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'"); } - - 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 (strncmp(tk.start, "const", 5) == 0 && tk.len == 5) { + return parse_const(ctx, l); } - - 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 (strncmp(tk.start, "return", 6) == 0 && tk.len == 6) { + return parse_return(ctx, l); } - - 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; - } + if (strncmp(tk.start, "if", 2) == 0 && tk.len == 2) { + return parse_if(ctx, l); } - - // Block - if (tk.type == TOK_LBRACE) - { - return parse_block(ctx, l); + if (strncmp(tk.start, "while", 5) == 0 && tk.len == 5) { + return parse_while(ctx, l); } - - // Keywords / Special - if (tk.type == TOK_TRAIT) - { - return parse_trait(ctx, l); + if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3) { + return parse_for(ctx, l); } - if (tk.type == TOK_IMPL) - { - return parse_impl(ctx, l); + if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5) { + return parse_match(ctx, l); } - if (tk.type == TOK_AUTOFREE) - { + + // Break with optional label: break; or break 'outer; + if (strncmp(tk.start, "break", 5) == 0 && tk.len == 5) { + lexer_next(l); + ASTNode *n = ast_create(NODE_BREAK); + 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); - 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); + } + return n; + } + + // Continue with optional label + if (strncmp(tk.start, "continue", 8) == 0 && tk.len == 8) { + lexer_next(l); + ASTNode *n = ast_create(NODE_CONTINUE); + 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 (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 (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4) { + return parse_loop(ctx, l); } - if (tk.type == TOK_ASSERT) - { - return parse_assert(ctx, l); + if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6) { + return parse_repeat(ctx, l); } - if (tk.type == TOK_DEFER) - { - return parse_defer(ctx, l); + if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6) { + return parse_unless(ctx, l); } - if (tk.type == TOK_ASM) - { - return parse_asm(ctx, l); + if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5) { + return parse_guard(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; + // 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); - 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); - } + // 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' - 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) - { - lexer_next(l); - ASTNode *n = ast_create(NODE_BREAK); - 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) - { - lexer_next(l); - ASTNode *n = ast_create(NODE_CONTINUE); - 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); - } - 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); - } - - // 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); - - // 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' - - ASTNode *cond = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - 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 (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) - { - return parse_defer(ctx, l); - } - - // Goto statement: goto label_name; OR goto *expr; (computed goto) - if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4) - { - Token goto_tok = lexer_next(l); // eat 'goto' - Token next = lexer_peek(l); - - // 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) - { - lexer_next(l); - } - - 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) - { - // 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; - } - } - } - - 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) - { - zpanic_at(t, "Expected string literal after print/eprint"); - } + ASTNode *cond = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } - char *inner = xmalloc(t.len); - 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; - } + 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; + } - 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 (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) { + return parse_defer(ctx, l); + } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } + // Goto statement: goto label_name; OR goto *expr; (computed goto) + if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4) { + Token goto_tok = lexer_next(l); // eat 'goto' + Token next = lexer_peek(l); - 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; + // 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) { + lexer_next(l); } - } - - // Default: Expression Statement - s = parse_expression(ctx, l); - int has_semi = 0; - if (lexer_peek(l).type == TOK_SEMICOLON) - { + 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); - has_semi = 1; + } + 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; } - // 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) + // Label detection: identifier followed by : (but not ::) { - 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; - } - } + 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) { + // 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; + } + } + } + + 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) { + zpanic_at(t, "Expected string literal after print/eprint"); + } + + char *inner = xmalloc(t.len); + 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; + } + + 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); + } - 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; - } + 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; } + } - if (s) - { - s->line = tk.line; - } + // Default: Expression Statement + s = parse_expression(ctx, l); - // Check for discarded must_use result - if (s && s->type == NODE_EXPR_CALL) - { - 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 s; + 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) { + 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; + } + } + + if (s) { + s->line = tk.line; + } + + // Check for discarded must_use result + if (s && s->type == NODE_EXPR_CALL) { + 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 s; } -ASTNode *parse_block(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat '{' - enter_scope(ctx); - ASTNode *head = 0, *tail = 0; - - int unreachable = 0; - - while (1) - { - skip_comments(l); - Token tk = lexer_peek(l); - if (tk.type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (tk.type == TOK_EOF) - { - break; - } - - if (unreachable == 1) - { - warn_unreachable_code(tk); - unreachable = 2; // Warned once, don't spam - } - - 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) - { - ASTNode *s = parse_statement(ctx, &new_l); - if (!s) - { - break; // EOF or error handling dependency - } - - // Link - if (!head) - { - head = s; - } - else - { - tail->next = s; - } - tail = s; - while (tail->next) - { - tail = tail->next; - } - } - continue; - } - - ASTNode *s = parse_statement(ctx, l); - if (s) - { - if (!head) - { - head = s; - } - else - { - tail->next = s; - } - tail = s; - while (tail->next) - { - tail = tail->next; // Handle chains (e.g. var decl + defer) - } +ASTNode *parse_block(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat '{' + enter_scope(ctx); + ASTNode *head = 0, *tail = 0; - // Check for control flow interruption - if (s->type == NODE_RETURN || s->type == NODE_BREAK || s->type == NODE_CONTINUE) - { - if (unreachable == 0) - { - unreachable = 1; - } - } - } - } - - // 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; - } - - // RAII: Don't warn if type implements Drop (it is used implicitly) - int has_drop = (sym->type_info && sym->type_info->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->has_drop) - { - has_drop = 1; - } - } - - if (!has_drop) - { - warn_unused_variable(sym->decl_token, sym->name); - } - } - sym = sym->next; - } - } + int unreachable = 0; - exit_scope(ctx); - ASTNode *b = ast_create(NODE_BLOCK); - b->block.statements = head; - return b; + while (1) { + skip_comments(l); + Token tk = lexer_peek(l); + if (tk.type == TOK_RBRACE) { + lexer_next(l); + break; + } + if (tk.type == TOK_EOF) { + break; + } + + if (unreachable == 1) { + warn_unreachable_code(tk); + unreachable = 2; // Warned once, don't spam + } + + 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) { + ASTNode *s = parse_statement(ctx, &new_l); + if (!s) { + break; // EOF or error handling dependency + } + + // Link + if (!head) { + head = s; + } else { + tail->next = s; + } + tail = s; + while (tail->next) { + tail = tail->next; + } + } + continue; + } + + ASTNode *s = parse_statement(ctx, l); + if (s) { + if (!head) { + head = s; + } else { + tail->next = s; + } + tail = s; + while (tail->next) { + tail = tail->next; // Handle chains (e.g. var decl + defer) + } + + // Check for control flow interruption + if (s->type == NODE_RETURN || s->type == NODE_BREAK || + s->type == NODE_CONTINUE) { + if (unreachable == 0) { + unreachable = 1; + } + } + } + } + + // 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; + } + + // RAII: Don't warn if type implements Drop (it is used implicitly) + int has_drop = (sym->type_info && sym->type_info->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->has_drop) { + has_drop = 1; + } + } + + if (!has_drop) { + warn_unused_variable(sym->decl_token, sym->name); + } + } + sym = sym->next; + } + } + + 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) - { - zpanic_at(n, "Expected trait name"); +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; + + 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; } - char *name = xmalloc(n.len + 1); - strncpy(name, n.start, n.len); - name[n.len] = 0; - - 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; - // Re-use parse_function but stop at semicolon? - // Actually trait methods might have default impls later, but for now just - // signatures. Let's parse full function but body might be empty/null? Or - // simpler: just parse signature manually. - - 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; - } + // Parse method signature: fn name(args...) -> ret; + // Re-use parse_function but stop at semicolon? + // Actually trait methods might have default impls later, but for now just + // signatures. Let's parse full function but body might be empty/null? Or + // simpler: just parse signature manually. - 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"); - } + Token ft = lexer_next(l); + if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) { + zpanic_at(ft, "Expected fn in trait"); } - ASTNode *n_node = ast_create(NODE_TRAIT); - n_node->trait.name = name; - n_node->trait.methods = methods; - register_trait(name); - return n_node; + 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; + register_trait(name); + return n_node; } -ASTNode *parse_impl(ParserContext *ctx, Lexer *l) -{ +ASTNode *parse_impl(ParserContext *ctx, Lexer *l) { - lexer_next(l); // eat impl - Token t1 = lexer_next(l); - char *name1 = token_strdup(t1); + lexer_next(l); // eat impl + Token t1 = lexer_next(l); + char *name1 = token_strdup(t1); - char *gen_param = NULL; - // Check for <T> 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 >"); - } + char *gen_param = NULL; + // Check for <T> 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->has_drop = 1; - } - else - { - // Try finding struct definition - ASTNode *def = find_struct_def(ctx, name2); - if (def && def->type_info) - { - def->type_info->has_drop = 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; - 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) + 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; - 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); - return n; + // 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 } - else - { - // Regular impl Struct (impl Box or impl Box<T>) - - // 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; - } + Token t2 = lexer_next(l); + char *name2 = token_strdup(t2); - ctx->current_impl_struct = name1; // For patch_self_args inside parse_function + register_impl(ctx, name1, name2); - if (gen_param) - { - // GENERIC IMPL TEMPLATE: impl Box<T> - if (lexer_next(l).type != TOK_LBRACE) - { - zpanic_at(lexer_peek(l), "Expected {"); - } - 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 for template: Box_method - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2); - 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; - - 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) + 2); - 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; - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } - else - { - lexer_next(l); - } - } - // 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) + 2); - 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) + 2); - 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; + // 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->has_drop = 1; + } else { + // Try finding struct definition + ASTNode *def = find_struct_def(ctx, name2); + if (def && def->type_info) { + def->type_info->has_drop = 1; } + } } -} - -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 Param <T> - char *gp = NULL; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); - Token g = lexer_next(l); - gp = token_strdup(g); - lexer_next(l); - 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 != NULL); - n->strct.generic_param = gp; - n->strct.is_union = is_union; - n->strct.fields = NULL; - n->strct.is_incomplete = 1; - - if (!gp) - { - add_to_struct_list(ctx, n); - } - 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) { + 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; + 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) + 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; + 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); + return n; + } else { + // Regular impl Struct (impl Box or impl Box<T>) - while (1) - { + // 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<T> + if (lexer_next(l).type != TOK_LBRACE) { + zpanic_at(lexer_peek(l), "Expected {"); + } + ASTNode *h = 0, *tl = 0; + while (1) { skip_comments(l); - Token t = lexer_peek(l); - 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 - // Parse the type (e.g. Header<I32>) - 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) - { - 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; + 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) + 2); + 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; + + 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) + 2); + 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; + 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 { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } else { + lexer_next(l); + } + } + // 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) + 2); + 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) + 2); + 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; } - } - 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) - { // 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) - { - node->type_info->kind = TYPE_GENERIC; - // TODO: track generic params - } - - node->strct.fields = h; - node->strct.generic_param = gp; - node->strct.is_union = is_union; - - if (gp) - { - node->strct.is_template = 1; - register_template(ctx, name, node); - } - - // Register definition for 'use' lookups and LSP - if (!gp) - { - register_struct_def(ctx, name, node); - } - - return node; + 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; + } + } } -Type *parse_type_obj(ParserContext *ctx, Lexer *l) -{ - // 1. Parse the base type (int, U32, MyStruct, etc.) - Type *t = parse_type_base(ctx, l); +ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) { - // 2. Handle Pointers (e.g. int***) - 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; - } - - // (Optional: You can add array parsing here later if needed) - - return t; -} + lexer_next(l); // eat struct or union + Token n = lexer_next(l); + char *name = token_strdup(n); -ASTNode *parse_enum(ParserContext *ctx, Lexer *l) -{ + // Generic Param <T> + char *gp = NULL; + if (lexer_peek(l).type == TOK_LANGLE) { lexer_next(l); - Token n = lexer_next(l); - - // 1. Check for Generic <T> - 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; -} -ASTNode *parse_include(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'include' - Token t = lexer_next(l); - char *path = NULL; - int is_system = 0; + Token g = lexer_next(l); + gp = token_strdup(g); + lexer_next(l); + register_generic(ctx, name); + } - if (t.type == TOK_LANGLE) - { - // System include: include <raylib.h> - is_system = 1; - char buf[256]; - buf[0] = 0; - while (1) - { - Token i = lexer_next(l); - if (i.type == TOK_RANGLE) - { - break; - } - strncat(buf, i.start, i.len); - } - path = xstrdup(buf); + // 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 != NULL); + n->strct.generic_param = gp; + n->strct.is_union = is_union; + n->strct.fields = NULL; + n->strct.is_incomplete = 1; - // Mark that this file has external includes (suppress undefined warnings) - ctx->has_external_includes = 1; - } - else - { - // Local include: include "file.h" - is_system = 0; - int len = t.len - 2; - path = xmalloc(len + 1); - strncpy(path, t.start + 1, len); - path[len] = 0; + if (!gp) { + add_to_struct_list(ctx, n); } - - ASTNode *n = ast_create(NODE_INCLUDE); - n->include.path = path; - n->include.is_system = is_system; return n; -} -ASTNode *parse_import(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'import' - - // Check for 'plugin' keyword - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && next.len == 6 && strncmp(next.start, "plugin", 6) == 0) - { - lexer_next(l); // consume "plugin" - - // Expect string literal with plugin name - Token plugin_tok = lexer_next(l); - if (plugin_tok.type != TOK_STRING) - { - zpanic_at(plugin_tok, "Expected string literal after 'import plugin'"); - } - - // Extract plugin name (strip quotes) - int name_len = plugin_tok.len - 2; - char *plugin_name = xmalloc(name_len + 1); - strncpy(plugin_name, plugin_tok.start + 1, name_len); - plugin_name[name_len] = '\0'; - - if (plugin_name[0] == '.' && - (plugin_name[1] == '/' || (plugin_name[1] == '.' && plugin_name[2] == '/'))) - { - char *current_dir = xstrdup(g_current_filename); - char *last_slash = strrchr(current_dir, '/'); - if (last_slash) - { - *last_slash = 0; - char resolved_path[1024]; - snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, plugin_name); - free(plugin_name); - plugin_name = xstrdup(resolved_path); - } - free(current_dir); - } + } - // Check for optional "as alias" - char *alias = NULL; - Token as_tok = lexer_peek(l); - if (as_tok.type == TOK_IDENT && as_tok.len == 2 && strncmp(as_tok.start, "as", 2) == 0) - { - lexer_next(l); // consume "as" - Token alias_tok = lexer_next(l); - if (alias_tok.type != TOK_IDENT) - { - zpanic_at(alias_tok, "Expected identifier after 'as'"); - } - alias = token_strdup(alias_tok); - } + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; - // Register the plugin - register_plugin(ctx, plugin_name, alias); + while (1) { + skip_comments(l); + Token t = lexer_peek(l); + 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 + // Parse the type (e.g. Header<I32>) + 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) { + 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) { // 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) { + node->type_info->kind = TYPE_GENERIC; + // TODO: track generic params + } + + node->strct.fields = h; + node->strct.generic_param = gp; + node->strct.is_union = is_union; + + if (gp) { + node->strct.is_template = 1; + register_template(ctx, name, node); + } + + // Register definition for 'use' lookups and LSP + if (!gp) { + register_struct_def(ctx, name, node); + } + + return node; +} - // Consume optional semicolon - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } +Type *parse_type_obj(ParserContext *ctx, Lexer *l) { + // 1. Parse the base type (int, U32, MyStruct, etc.) + Type *t = parse_type_base(ctx, l); - // Return NULL - no AST node needed for imports - return NULL; - } + // 2. Handle Pointers (e.g. int***) + 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; + } - // Regular module import handling follows... - // Check if this is selective import: import { ... } from "file" - int is_selective = 0; - char *symbols[32]; // Max 32 selective imports - char *aliases[32]; - int symbol_count = 0; + // (Optional: You can add array parsing here later if needed) - if (lexer_peek(l).type == TOK_LBRACE) - { - is_selective = 1; - lexer_next(l); // eat { - - // Parse symbol list - while (lexer_peek(l).type != TOK_RBRACE) - { - if (symbol_count > 0 && lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); // eat comma - } + return t; +} - Token sym_tok = lexer_next(l); - if (sym_tok.type != TOK_IDENT) - { - zpanic_at(sym_tok, "Expected identifier in selective import"); - } +ASTNode *parse_enum(ParserContext *ctx, Lexer *l) { + lexer_next(l); + Token n = lexer_next(l); - symbols[symbol_count] = xmalloc(sym_tok.len + 1); - strncpy(symbols[symbol_count], sym_tok.start, sym_tok.len); - symbols[symbol_count][sym_tok.len] = 0; - - // Check for 'as alias' - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && next.len == 2 && strncmp(next.start, "as", 2) == 0) - { - lexer_next(l); // eat 'as' - Token alias_tok = lexer_next(l); - if (alias_tok.type != TOK_IDENT) - { - zpanic_at(alias_tok, "Expected identifier after 'as'"); - } - - aliases[symbol_count] = xmalloc(alias_tok.len + 1); - strncpy(aliases[symbol_count], alias_tok.start, alias_tok.len); - aliases[symbol_count][alias_tok.len] = 0; - } - else - { - aliases[symbol_count] = NULL; // No alias - } + // 1. Check for Generic <T> + 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"); + } - symbol_count++; - } + lexer_next(l); // eat { - lexer_next(l); // eat } + ASTNode *h = 0, *tl = 0; + int v = 0; + char *ename = token_strdup(n); // Store enum name - // Expect 'from' - Token from_tok = lexer_next(l); - if (from_tok.type != TOK_IDENT || from_tok.len != 4 || - strncmp(from_tok.start, "from", 4) != 0) - { - zpanic_at(from_tok, "Expected 'from' after selective import list, got type=%d", - from_tok.type); - } + while (1) { + skip_comments(l); + Token t = lexer_peek(l); + if (t.type == TOK_RBRACE) { + lexer_next(l); + break; } - - // Parse filename - Token t = lexer_next(l); - if (t.type != TOK_STRING) - { - zpanic_at(t, - "Expected string (filename) after 'from' in selective import, got " - "type %d", - t.type); - } - int ln = t.len - 2; // Remove quotes - char *fn = xmalloc(ln + 1); - strncpy(fn, t.start + 1, ln); - fn[ln] = 0; - - // Resolve relative paths (if starts with ./ or ../ - char resolved_path[1024]; - if (fn[0] == '.' && (fn[1] == '/' || (fn[1] == '.' && fn[2] == '/'))) - { - // Relative import - resolve relative to current file - char *current_dir = xstrdup(g_current_filename); - char *last_slash = strrchr(current_dir, '/'); - if (last_slash) - { - *last_slash = 0; // Truncate to directory - const char *leaf = fn; - if (leaf[0] == '.' && leaf[1] == '/') - { - leaf += 2; - } - snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, leaf); - } - else - { - snprintf(resolved_path, sizeof(resolved_path), "%s", fn); - } - free(current_dir); - free(fn); - fn = xstrdup(resolved_path); + if (t.type == TOK_COMMA) { + lexer_next(l); + continue; } - // Check if file exists, if not try system-wide paths - if (access(fn, R_OK) != 0) - { - // Try system-wide standard library location - static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; - - char system_path[1024]; - int found = 0; - - for (int i = 0; system_paths[i] && !found; i++) - { - snprintf(system_path, sizeof(system_path), "%s/%s", system_paths[i], fn); - if (access(system_path, R_OK) == 0) - { - free(fn); - fn = xstrdup(system_path); - found = 1; - } - } + if (t.type == TOK_IDENT) { + Token vt = lexer_next(l); + char *vname = token_strdup(vt); - if (!found) - { - // File not found anywhere - will error later when trying to open + // 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 )"); } - } + } - // Canonicalize path to avoid duplicates (for example: "./std/io.zc" vs "std/io.zc") - char *real_fn = realpath(fn, NULL); - if (real_fn) - { - free(fn); - fn = real_fn; - } + 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* - // Check if file already imported - if (is_file_imported(ctx, fn)) - { - free(fn); - return NULL; - } - mark_file_imported(ctx, fn); + // 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); - // For selective imports, register them BEFORE parsing the file - char *module_base_name = NULL; - if (is_selective) - { - module_base_name = extract_module_name(fn); - for (int i = 0; i < symbol_count; i++) - { - register_selective_import(ctx, symbols[i], aliases[i], module_base_name); - } - } - - // Check for 'as alias' syntax (for namespaced imports) + // 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; +} +ASTNode *parse_include(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat 'include' + Token t = lexer_next(l); + char *path = NULL; + int is_system = 0; + + if (t.type == TOK_LANGLE) { + // System include: include <raylib.h> + is_system = 1; + char buf[256]; + buf[0] = 0; + while (1) { + Token i = lexer_next(l); + if (i.type == TOK_RANGLE) { + break; + } + strncat(buf, i.start, i.len); + } + path = xstrdup(buf); + + // Mark that this file has external includes (suppress undefined warnings) + ctx->has_external_includes = 1; + } else { + // Local include: include "file.h" + is_system = 0; + int len = t.len - 2; + path = xmalloc(len + 1); + strncpy(path, t.start + 1, len); + path[len] = 0; + } + + ASTNode *n = ast_create(NODE_INCLUDE); + n->include.path = path; + n->include.is_system = is_system; + return n; +} +ASTNode *parse_import(ParserContext *ctx, Lexer *l) { + lexer_next(l); // eat 'import' + + // Check for 'plugin' keyword + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && next.len == 6 && + strncmp(next.start, "plugin", 6) == 0) { + lexer_next(l); // consume "plugin" + + // Expect string literal with plugin name + Token plugin_tok = lexer_next(l); + if (plugin_tok.type != TOK_STRING) { + zpanic_at(plugin_tok, "Expected string literal after 'import plugin'"); + } + + // Extract plugin name (strip quotes) + int name_len = plugin_tok.len - 2; + char *plugin_name = xmalloc(name_len + 1); + strncpy(plugin_name, plugin_tok.start + 1, name_len); + plugin_name[name_len] = '\0'; + + if (plugin_name[0] == '.' && + (plugin_name[1] == '/' || + (plugin_name[1] == '.' && plugin_name[2] == '/'))) { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) { + *last_slash = 0; + char resolved_path[1024]; + snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, + plugin_name); + free(plugin_name); + plugin_name = xstrdup(resolved_path); + } + free(current_dir); + } + + // Check for optional "as alias" char *alias = NULL; - if (!is_selective) - { - Token next_tok = lexer_peek(l); - if (next_tok.type == TOK_IDENT && next_tok.len == 2 && - strncmp(next_tok.start, "as", 2) == 0) - { - lexer_next(l); // eat 'as' - Token alias_tok = lexer_next(l); - if (alias_tok.type != TOK_IDENT) - { - zpanic_at(alias_tok, "Expected identifier after 'as'"); - } + Token as_tok = lexer_peek(l); + if (as_tok.type == TOK_IDENT && as_tok.len == 2 && + strncmp(as_tok.start, "as", 2) == 0) { + lexer_next(l); // consume "as" + Token alias_tok = lexer_next(l); + if (alias_tok.type != TOK_IDENT) { + zpanic_at(alias_tok, "Expected identifier after 'as'"); + } + alias = token_strdup(alias_tok); + } + + // Register the plugin + register_plugin(ctx, plugin_name, alias); + + // Consume optional semicolon + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } + + // Return NULL - no AST node needed for imports + return NULL; + } + + // Regular module import handling follows... + // Check if this is selective import: import { ... } from "file" + int is_selective = 0; + char *symbols[32]; // Max 32 selective imports + char *aliases[32]; + int symbol_count = 0; + + if (lexer_peek(l).type == TOK_LBRACE) { + is_selective = 1; + lexer_next(l); // eat { - alias = xmalloc(alias_tok.len + 1); - strncpy(alias, alias_tok.start, alias_tok.len); - alias[alias_tok.len] = 0; + // Parse symbol list + while (lexer_peek(l).type != TOK_RBRACE) { + if (symbol_count > 0 && lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); // eat comma + } - // Register the module + Token sym_tok = lexer_next(l); + if (sym_tok.type != TOK_IDENT) { + zpanic_at(sym_tok, "Expected identifier in selective import"); + } - // Check if C header - int is_header = 0; - if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) - { - is_header = 1; - } + symbols[symbol_count] = xmalloc(sym_tok.len + 1); + strncpy(symbols[symbol_count], sym_tok.start, sym_tok.len); + symbols[symbol_count][sym_tok.len] = 0; - // Register the module - Module *m = xmalloc(sizeof(Module)); - m->alias = xstrdup(alias); - m->path = xstrdup(fn); - m->base_name = extract_module_name(fn); - m->is_c_header = is_header; - m->next = ctx->modules; - ctx->modules = m; + // Check for 'as alias' + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && next.len == 2 && + strncmp(next.start, "as", 2) == 0) { + lexer_next(l); // eat 'as' + Token alias_tok = lexer_next(l); + if (alias_tok.type != TOK_IDENT) { + zpanic_at(alias_tok, "Expected identifier after 'as'"); } - } - // C Header: Emit include and return (don't parse) - if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) - { - ASTNode *n = ast_create(NODE_INCLUDE); - n->include.path = xstrdup(fn); // Store exact path - n->include.is_system = 0; // Double quotes - return n; - } + aliases[symbol_count] = xmalloc(alias_tok.len + 1); + strncpy(aliases[symbol_count], alias_tok.start, alias_tok.len); + aliases[symbol_count][alias_tok.len] = 0; + } else { + aliases[symbol_count] = NULL; // No alias + } - // Load and parse the file - char *src = load_file(fn); - if (!src) - { - zpanic_at(t, "Not found: %s", fn); + symbol_count++; } - Lexer i; - lexer_init(&i, src); - - // If this is a namespaced import or selective import, set the module prefix - char *prev_module_prefix = ctx->current_module_prefix; - char *temp_module_prefix = NULL; - - if (alias) - { // For 'import "file" as alias' - temp_module_prefix = extract_module_name(fn); - ctx->current_module_prefix = temp_module_prefix; - } - else if (is_selective) - { // For 'import {sym} from "file"' - temp_module_prefix = extract_module_name(fn); - ctx->current_module_prefix = temp_module_prefix; - } - - // Update global filename context for relative imports inside the new file - const char *saved_fn = g_current_filename; - g_current_filename = fn; + lexer_next(l); // eat } - ASTNode *r = parse_program_nodes(ctx, &i); + // Expect 'from' + Token from_tok = lexer_next(l); + if (from_tok.type != TOK_IDENT || from_tok.len != 4 || + strncmp(from_tok.start, "from", 4) != 0) { + zpanic_at(from_tok, + "Expected 'from' after selective import list, got type=%d", + from_tok.type); + } + } + + // Parse filename + Token t = lexer_next(l); + if (t.type != TOK_STRING) { + zpanic_at( + t, + "Expected string (filename) after 'from' in selective import, got " + "type %d", + t.type); + } + int ln = t.len - 2; // Remove quotes + char *fn = xmalloc(ln + 1); + strncpy(fn, t.start + 1, ln); + fn[ln] = 0; + + // Resolve relative paths (if starts with ./ or ../ + char resolved_path[1024]; + if (fn[0] == '.' && (fn[1] == '/' || (fn[1] == '.' && fn[2] == '/'))) { + // Relative import - resolve relative to current file + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) { + *last_slash = 0; // Truncate to directory + const char *leaf = fn; + if (leaf[0] == '.' && leaf[1] == '/') { + leaf += 2; + } + snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, + leaf); + } else { + snprintf(resolved_path, sizeof(resolved_path), "%s", fn); + } + free(current_dir); + free(fn); + fn = xstrdup(resolved_path); + } - // Restore filename context - g_current_filename = (char *)saved_fn; + // Check if file exists, if not try system-wide paths + if (access(fn, R_OK) != 0) { + // Try system-wide standard library location + static const char *system_paths[] = {"/usr/local/share/zenc", + "/usr/share/zenc", NULL}; - // Restore previous module context - if (temp_module_prefix) - { - free(temp_module_prefix); - ctx->current_module_prefix = prev_module_prefix; - } + char system_path[1024]; + int found = 0; - // Free selective import symbols and aliases - if (is_selective) - { - for (int k = 0; k < symbol_count; k++) - { - free(symbols[k]); - if (aliases[k]) - { - free(aliases[k]); - } - } + for (int i = 0; system_paths[i] && !found; i++) { + snprintf(system_path, sizeof(system_path), "%s/%s", system_paths[i], fn); + if (access(system_path, R_OK) == 0) { + free(fn); + fn = xstrdup(system_path); + found = 1; + } } - if (alias) - { - free(alias); + if (!found) { + // File not found anywhere - will error later when trying to open } + } - if (module_base_name) - { // This was only used for selective import - // registration, not for ctx->current_module_prefix - free(module_base_name); - } + // Canonicalize path to avoid duplicates (for example: "./std/io.zc" vs + // "std/io.zc") + char *real_fn = realpath(fn, NULL); + if (real_fn) { + free(fn); + fn = real_fn; + } + // Check if file already imported + if (is_file_imported(ctx, fn)) { free(fn); - return r; + return NULL; + } + mark_file_imported(ctx, fn); + + // For selective imports, register them BEFORE parsing the file + char *module_base_name = NULL; + if (is_selective) { + module_base_name = extract_module_name(fn); + for (int i = 0; i < symbol_count; i++) { + register_selective_import(ctx, symbols[i], aliases[i], module_base_name); + } + } + + // Check for 'as alias' syntax (for namespaced imports) + char *alias = NULL; + if (!is_selective) { + Token next_tok = lexer_peek(l); + if (next_tok.type == TOK_IDENT && next_tok.len == 2 && + strncmp(next_tok.start, "as", 2) == 0) { + lexer_next(l); // eat 'as' + Token alias_tok = lexer_next(l); + if (alias_tok.type != TOK_IDENT) { + zpanic_at(alias_tok, "Expected identifier after 'as'"); + } + + alias = xmalloc(alias_tok.len + 1); + strncpy(alias, alias_tok.start, alias_tok.len); + alias[alias_tok.len] = 0; + + // Register the module + + // Check if C header + int is_header = 0; + if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) { + is_header = 1; + } + + // Register the module + Module *m = xmalloc(sizeof(Module)); + m->alias = xstrdup(alias); + m->path = xstrdup(fn); + m->base_name = extract_module_name(fn); + m->is_c_header = is_header; + m->next = ctx->modules; + ctx->modules = m; + } + } + + // C Header: Emit include and return (don't parse) + if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) { + ASTNode *n = ast_create(NODE_INCLUDE); + n->include.path = xstrdup(fn); // Store exact path + n->include.is_system = 0; // Double quotes + return n; + } + + // Load and parse the file + char *src = load_file(fn); + if (!src) { + zpanic_at(t, "Not found: %s", fn); + } + + Lexer i; + lexer_init(&i, src); + + // If this is a namespaced import or selective import, set the module prefix + char *prev_module_prefix = ctx->current_module_prefix; + char *temp_module_prefix = NULL; + + if (alias) { // For 'import "file" as alias' + temp_module_prefix = extract_module_name(fn); + ctx->current_module_prefix = temp_module_prefix; + } else if (is_selective) { // For 'import {sym} from "file"' + temp_module_prefix = extract_module_name(fn); + ctx->current_module_prefix = temp_module_prefix; + } + + // Update global filename context for relative imports inside the new file + const char *saved_fn = g_current_filename; + g_current_filename = fn; + + ASTNode *r = parse_program_nodes(ctx, &i); + + // Restore filename context + g_current_filename = (char *)saved_fn; + + // Restore previous module context + if (temp_module_prefix) { + free(temp_module_prefix); + ctx->current_module_prefix = prev_module_prefix; + } + + // Free selective import symbols and aliases + if (is_selective) { + for (int k = 0; k < symbol_count; k++) { + free(symbols[k]); + if (aliases[k]) { + free(aliases[k]); + } + } + } + + if (alias) { + free(alias); + } + + if (module_base_name) { // This was only used for selective import + // registration, not for ctx->current_module_prefix + free(module_base_name); + } + + free(fn); + return r; } // Helper: Execute comptime block and return generated source -char *run_comptime_block(ParserContext *ctx, Lexer *l) -{ - (void)ctx; - expect(l, TOK_COMPTIME, "comptime"); - expect(l, TOK_LBRACE, "expected { after comptime"); - - 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 comptime block"); - } - if (t.type == TOK_LBRACE) - { - depth++; - } - if (t.type == TOK_RBRACE) - { - depth--; - } - } - // End is passed the closing brace, so pos points after it. - // The code block is between start and (current pos - 1) - int len = (l->src + l->pos - 1) - start; - char *code = xmalloc(len + 1); - strncpy(code, start, len); - code[len] = 0; - - // Wrap in block to parse mixed statements/declarations - int wrapped_len = len + 4; // "{ " + code + " }" - char *wrapped_code = xmalloc(wrapped_len + 1); - sprintf(wrapped_code, "{ %s }", code); - - Lexer cl; - lexer_init(&cl, wrapped_code); - ParserContext cctx; - memset(&cctx, 0, sizeof(cctx)); - enter_scope(&cctx); // Global scope - register_builtins(&cctx); - - ASTNode *block = parse_block(&cctx, &cl); - ASTNode *nodes = block ? block->block.statements : NULL; - - free(wrapped_code); - - char filename[64]; - sprintf(filename, "_tmp_comptime_%d.c", rand()); - FILE *f = fopen(filename, "w"); - if (!f) - { - zpanic_at(lexer_peek(l), "Could not create temp file %s", filename); - } - - emit_preamble(ctx, f); - fprintf( - f, - "size_t _z_check_bounds(size_t index, size_t size) { if (index >= size) { fprintf(stderr, " - "\"Index out of bounds: %%zu >= %%zu\\n\", index, size); exit(1); } return index; }\n"); - - ASTNode *curr = nodes; - ASTNode *stmts = NULL; - ASTNode *stmts_tail = NULL; - - while (curr) - { - ASTNode *next = curr->next; - curr->next = NULL; - - if (curr->type == NODE_INCLUDE) - { - emit_includes_and_aliases(curr, f); - } - else if (curr->type == NODE_STRUCT) - { - emit_struct_defs(&cctx, curr, f); - } - else if (curr->type == NODE_ENUM) - { - emit_enum_protos(curr, f); - } - else if (curr->type == NODE_CONST) - { - emit_globals(&cctx, curr, f); - } - else if (curr->type == NODE_FUNCTION) - { - codegen_node_single(&cctx, curr, f); - } - else if (curr->type == NODE_IMPL) - { - // Impl support pending - } - else - { - // Statement or expression -> main - if (!stmts) - { - stmts = curr; - } - else - { - stmts_tail->next = curr; - } - stmts_tail = curr; - } - curr = next; - } - - fprintf(f, "int main() {\n"); - curr = stmts; - while (curr) - { - if (curr->type >= NODE_EXPR_BINARY && curr->type <= NODE_EXPR_SLICE) - { - codegen_expression(&cctx, curr, f); - fprintf(f, ";\n"); - } - else - { - codegen_node_single(&cctx, curr, f); - } - curr = curr->next; - } - fprintf(f, "return 0;\n}\n"); - fclose(f); - - char cmd[4096]; - char bin[1024]; - sprintf(bin, "%s.bin", filename); - sprintf(cmd, "gcc %s -o %s > /dev/null 2>&1", filename, bin); - int res = system(cmd); - if (res != 0) - { - zpanic_at(lexer_peek(l), "Comptime compilation failed for:\n%s", code); - } - - char out_file[1024]; - sprintf(out_file, "%s.out", filename); - sprintf(cmd, "./%s > %s", bin, out_file); - if (system(cmd) != 0) - { - zpanic_at(lexer_peek(l), "Comptime execution failed"); - } - - char *output_src = load_file(out_file); - if (!output_src) - { - output_src = xstrdup(""); // Empty output is valid - } - - // Cleanup - remove(filename); - remove(bin); - remove(out_file); - free(code); - - return output_src; +char *run_comptime_block(ParserContext *ctx, Lexer *l) { + (void)ctx; + expect(l, TOK_COMPTIME, "comptime"); + expect(l, TOK_LBRACE, "expected { after comptime"); + + 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 comptime block"); + } + if (t.type == TOK_LBRACE) { + depth++; + } + if (t.type == TOK_RBRACE) { + depth--; + } + } + // End is passed the closing brace, so pos points after it. + // The code block is between start and (current pos - 1) + int len = (l->src + l->pos - 1) - start; + char *code = xmalloc(len + 1); + strncpy(code, start, len); + code[len] = 0; + + // Wrap in block to parse mixed statements/declarations + int wrapped_len = len + 4; // "{ " + code + " }" + char *wrapped_code = xmalloc(wrapped_len + 1); + sprintf(wrapped_code, "{ %s }", code); + + Lexer cl; + lexer_init(&cl, wrapped_code); + ParserContext cctx; + memset(&cctx, 0, sizeof(cctx)); + enter_scope(&cctx); // Global scope + register_builtins(&cctx); + + ASTNode *block = parse_block(&cctx, &cl); + ASTNode *nodes = block ? block->block.statements : NULL; + + free(wrapped_code); + + char filename[64]; + sprintf(filename, "_tmp_comptime_%d.c", rand()); + FILE *f = fopen(filename, "w"); + if (!f) { + zpanic_at(lexer_peek(l), "Could not create temp file %s", filename); + } + + emit_preamble(ctx, f); + fprintf(f, "size_t _z_check_bounds(size_t index, size_t size) { if (index >= " + "size) { fprintf(stderr, " + "\"Index out of bounds: %%zu >= %%zu\\n\", index, size); exit(1); " + "} return index; }\n"); + + ASTNode *curr = nodes; + ASTNode *stmts = NULL; + ASTNode *stmts_tail = NULL; + + while (curr) { + ASTNode *next = curr->next; + curr->next = NULL; + + if (curr->type == NODE_INCLUDE) { + emit_includes_and_aliases(curr, f); + } else if (curr->type == NODE_STRUCT) { + emit_struct_defs(&cctx, curr, f); + } else if (curr->type == NODE_ENUM) { + emit_enum_protos(curr, f); + } else if (curr->type == NODE_CONST) { + emit_globals(&cctx, curr, f); + } else if (curr->type == NODE_FUNCTION) { + codegen_node_single(&cctx, curr, f); + } else if (curr->type == NODE_IMPL) { + // Impl support pending + } else { + // Statement or expression -> main + if (!stmts) { + stmts = curr; + } else { + stmts_tail->next = curr; + } + stmts_tail = curr; + } + curr = next; + } + + fprintf(f, "int main() {\n"); + curr = stmts; + while (curr) { + if (curr->type >= NODE_EXPR_BINARY && curr->type <= NODE_EXPR_SLICE) { + codegen_expression(&cctx, curr, f); + fprintf(f, ";\n"); + } else { + codegen_node_single(&cctx, curr, f); + } + curr = curr->next; + } + fprintf(f, "return 0;\n}\n"); + fclose(f); + + char cmd[4096]; + char bin[1024]; + sprintf(bin, "%s.bin", filename); + sprintf(cmd, "gcc %s -o %s > /dev/null 2>&1", filename, bin); + int res = system(cmd); + if (res != 0) { + zpanic_at(lexer_peek(l), "Comptime compilation failed for:\n%s", code); + } + + char out_file[1024]; + sprintf(out_file, "%s.out", filename); + sprintf(cmd, "./%s > %s", bin, out_file); + if (system(cmd) != 0) { + zpanic_at(lexer_peek(l), "Comptime execution failed"); + } + + char *output_src = load_file(out_file); + if (!output_src) { + output_src = xstrdup(""); // Empty output is valid + } + + // Cleanup + remove(filename); + remove(bin); + remove(out_file); + free(code); + + return output_src; } -ASTNode *parse_comptime(ParserContext *ctx, Lexer *l) -{ - char *output_src = run_comptime_block(ctx, l); +ASTNode *parse_comptime(ParserContext *ctx, Lexer *l) { + char *output_src = run_comptime_block(ctx, l); - Lexer new_l; - lexer_init(&new_l, output_src); - return parse_program_nodes(ctx, &new_l); + Lexer new_l; + lexer_init(&new_l, output_src); + return parse_program_nodes(ctx, &new_l); } // Parse plugin block: plugin name ... end -ASTNode *parse_plugin(ParserContext *ctx, Lexer *l) -{ - (void)ctx; - - // Expect 'plugin' keyword (already consumed by caller) - // Next should be plugin name - Token tk = lexer_next(l); - if (tk.type != TOK_IDENT) - { - zpanic_at(tk, "Expected plugin name after 'plugin' keyword"); +ASTNode *parse_plugin(ParserContext *ctx, Lexer *l) { + (void)ctx; + + // Expect 'plugin' keyword (already consumed by caller) + // Next should be plugin name + Token tk = lexer_next(l); + if (tk.type != TOK_IDENT) { + zpanic_at(tk, "Expected plugin name after 'plugin' keyword"); + } + + // Extract plugin name + char *plugin_name = xmalloc(tk.len + 1); + strncpy(plugin_name, tk.start, tk.len); + plugin_name[tk.len] = '\0'; + + // Collect everything until 'end' + char *body = xmalloc(8192); + body[0] = '\0'; + int body_len = 0; + + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_EOF) { + zpanic_at(t, "Unexpected EOF in plugin block, expected 'end'"); } - // Extract plugin name - char *plugin_name = xmalloc(tk.len + 1); - strncpy(plugin_name, tk.start, tk.len); - plugin_name[tk.len] = '\0'; - - // Collect everything until 'end' - char *body = xmalloc(8192); - body[0] = '\0'; - int body_len = 0; - - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_EOF) - { - zpanic_at(t, "Unexpected EOF in plugin block, expected 'end'"); - } - - // Check for 'end' - if (t.type == TOK_IDENT && t.len == 3 && strncmp(t.start, "end", 3) == 0) - { - lexer_next(l); // consume 'end' - break; - } - - // Append token to body - if (body_len + t.len + 2 < 8192) - { - strncat(body, t.start, t.len); - body[body_len + t.len] = ' '; - body[body_len + t.len + 1] = '\0'; - body_len += t.len + 1; - } + // Check for 'end' + if (t.type == TOK_IDENT && t.len == 3 && strncmp(t.start, "end", 3) == 0) { + lexer_next(l); // consume 'end' + break; + } - lexer_next(l); + // Append token to body + if (body_len + t.len + 2 < 8192) { + strncat(body, t.start, t.len); + body[body_len + t.len] = ' '; + body[body_len + t.len + 1] = '\0'; + body_len += t.len + 1; } - // Create plugin node - ASTNode *n = ast_create(NODE_PLUGIN); - n->plugin_stmt.plugin_name = plugin_name; - n->plugin_stmt.body = body; + lexer_next(l); + } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - return n; + // Create plugin node + ASTNode *n = ast_create(NODE_PLUGIN); + n->plugin_stmt.plugin_name = plugin_name; + n->plugin_stmt.body = body; + + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); + } + return n; } diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index d39e498..e8d9c27 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -6,801 +6,648 @@ #include <stdlib.h> #include <string.h> -Type *parse_type_base(ParserContext *ctx, Lexer *l) -{ - Token t = lexer_peek(l); +Type *parse_type_base(ParserContext *ctx, Lexer *l) { + Token t = lexer_peek(l); + + if (t.type == TOK_IDENT) { + // Handle "struct Name" or "enum Name" + if ((t.len == 6 && strncmp(t.start, "struct", 6) == 0) || + (t.len == 4 && strncmp(t.start, "enum", 4) == 0)) { + lexer_next(l); // consume keyword + t = lexer_peek(l); + if (t.type != TOK_IDENT) { + zpanic_at(t, "Expected identifier after struct/enum"); + } + } - if (t.type == TOK_IDENT) - { - // Handle "struct Name" or "enum Name" - if ((t.len == 6 && strncmp(t.start, "struct", 6) == 0) || - (t.len == 4 && strncmp(t.start, "enum", 4) == 0)) - { - lexer_next(l); // consume keyword - t = lexer_peek(l); - if (t.type != TOK_IDENT) - { - zpanic_at(t, "Expected identifier after struct/enum"); - } - } + lexer_next(l); + char *name = token_strdup(t); - lexer_next(l); - char *name = token_strdup(t); + // Self type alias: Replace "Self" with current impl struct type + if (strcmp(name, "Self") == 0 && ctx->current_impl_struct) { + name = xstrdup(ctx->current_impl_struct); + } - // Self type alias: Replace "Self" with current impl struct type - if (strcmp(name, "Self") == 0 && ctx->current_impl_struct) - { - name = xstrdup(ctx->current_impl_struct); - } + // Handle Namespace :: (A::B -> A_B) + while (lexer_peek(l).type == TOK_DCOLON) { + lexer_next(l); // eat :: + Token next = lexer_next(l); + if (next.type != TOK_IDENT) { + zpanic_at(t, "Expected identifier after ::"); + } + + char *suffix = token_strdup(next); + char *resolved_suffix = suffix; + + // Map Zen Primitive suffixes to C types to match Generic Instantiation + if (strcmp(suffix, "I32") == 0) { + resolved_suffix = "int32_t"; + } else if (strcmp(suffix, "U32") == 0) { + resolved_suffix = "uint32_t"; + } else if (strcmp(suffix, "I8") == 0) { + resolved_suffix = "int8_t"; + } else if (strcmp(suffix, "U8") == 0) { + resolved_suffix = "uint8_t"; + } else if (strcmp(suffix, "I16") == 0) { + resolved_suffix = "int16_t"; + } else if (strcmp(suffix, "U16") == 0) { + resolved_suffix = "uint16_t"; + } else if (strcmp(suffix, "I64") == 0) { + resolved_suffix = "int64_t"; + } else if (strcmp(suffix, "U64") == 0) { + resolved_suffix = "uint64_t"; + } + // Lowercase aliases + else if (strcmp(suffix, "i8") == 0) { + resolved_suffix = "int8_t"; + } else if (strcmp(suffix, "u8") == 0) { + resolved_suffix = "uint8_t"; + } else if (strcmp(suffix, "i16") == 0) { + resolved_suffix = "int16_t"; + } else if (strcmp(suffix, "u16") == 0) { + resolved_suffix = "uint16_t"; + } else if (strcmp(suffix, "i32") == 0) { + resolved_suffix = "int32_t"; + } else if (strcmp(suffix, "u32") == 0) { + resolved_suffix = "uint32_t"; + } else if (strcmp(suffix, "i64") == 0) { + resolved_suffix = "int64_t"; + } else if (strcmp(suffix, "u64") == 0) { + resolved_suffix = "uint64_t"; + } else if (strcmp(suffix, "usize") == 0) { + resolved_suffix = "size_t"; + } else if (strcmp(suffix, "string") == 0) { + resolved_suffix = "char*"; + } + + // Check if 'name' is a module alias (e.g., m::Vector) + Module *mod = find_module(ctx, name); + char *merged; + if (mod) { + // Module-qualified type: Use module base name + merged = xmalloc(strlen(mod->base_name) + strlen(resolved_suffix) + 2); + sprintf(merged, "%s_%s", mod->base_name, resolved_suffix); + } else { + // Regular namespace or enum variant + merged = xmalloc(strlen(name) + strlen(resolved_suffix) + 2); + sprintf(merged, "%s_%s", name, resolved_suffix); + } + + free(name); + if (suffix != resolved_suffix) { + free(suffix); // Only free if we didn't remap + } else { + free(suffix); + } + + name = merged; + } - // Handle Namespace :: (A::B -> A_B) - while (lexer_peek(l).type == TOK_DCOLON) - { - lexer_next(l); // eat :: - Token next = lexer_next(l); - if (next.type != TOK_IDENT) - { - zpanic_at(t, "Expected identifier after ::"); - } - - char *suffix = token_strdup(next); - char *resolved_suffix = suffix; - - // Map Zen Primitive suffixes to C types to match Generic Instantiation - if (strcmp(suffix, "I32") == 0) - { - resolved_suffix = "int32_t"; - } - else if (strcmp(suffix, "U32") == 0) - { - resolved_suffix = "uint32_t"; - } - else if (strcmp(suffix, "I8") == 0) - { - resolved_suffix = "int8_t"; - } - else if (strcmp(suffix, "U8") == 0) - { - resolved_suffix = "uint8_t"; - } - else if (strcmp(suffix, "I16") == 0) - { - resolved_suffix = "int16_t"; - } - else if (strcmp(suffix, "U16") == 0) - { - resolved_suffix = "uint16_t"; - } - else if (strcmp(suffix, "I64") == 0) - { - resolved_suffix = "int64_t"; - } - else if (strcmp(suffix, "U64") == 0) - { - resolved_suffix = "uint64_t"; - } - // Lowercase aliases - else if (strcmp(suffix, "i8") == 0) - { - resolved_suffix = "int8_t"; - } - else if (strcmp(suffix, "u8") == 0) - { - resolved_suffix = "uint8_t"; - } - else if (strcmp(suffix, "i16") == 0) - { - resolved_suffix = "int16_t"; - } - else if (strcmp(suffix, "u16") == 0) - { - resolved_suffix = "uint16_t"; - } - else if (strcmp(suffix, "i32") == 0) - { - resolved_suffix = "int32_t"; - } - else if (strcmp(suffix, "u32") == 0) - { - resolved_suffix = "uint32_t"; - } - else if (strcmp(suffix, "i64") == 0) - { - resolved_suffix = "int64_t"; - } - else if (strcmp(suffix, "u64") == 0) - { - resolved_suffix = "uint64_t"; - } - else if (strcmp(suffix, "usize") == 0) - { - resolved_suffix = "size_t"; - } - else if (strcmp(suffix, "string") == 0) - { - resolved_suffix = "char*"; - } - - // Check if 'name' is a module alias (e.g., m::Vector) - Module *mod = find_module(ctx, name); - char *merged; - if (mod) - { - // Module-qualified type: Use module base name - merged = xmalloc(strlen(mod->base_name) + strlen(resolved_suffix) + 2); - sprintf(merged, "%s_%s", mod->base_name, resolved_suffix); - } - else - { - // Regular namespace or enum variant - merged = xmalloc(strlen(name) + strlen(resolved_suffix) + 2); - sprintf(merged, "%s_%s", name, resolved_suffix); - } - - free(name); - if (suffix != resolved_suffix) - { - free(suffix); // Only free if we didn't remap - } - else - { - free(suffix); - } - - name = merged; - } + // Check for Primitives (Base types) + if (strcmp(name, "U0") == 0) { + free(name); + return type_new(TYPE_VOID); + } + if (strcmp(name, "u0") == 0) { + free(name); + return type_new(TYPE_VOID); + } + if (strcmp(name, "I8") == 0) { + free(name); + return type_new(TYPE_I8); + } + if (strcmp(name, "U8") == 0) { + free(name); + return type_new(TYPE_U8); + } + if (strcmp(name, "I16") == 0) { + free(name); + return type_new(TYPE_I16); + } + if (strcmp(name, "U16") == 0) { + free(name); + return type_new(TYPE_U16); + } + if (strcmp(name, "I32") == 0) { + free(name); + return type_new(TYPE_I32); + } + if (strcmp(name, "U32") == 0) { + free(name); + return type_new(TYPE_U32); + } + if (strcmp(name, "I64") == 0) { + free(name); + return type_new(TYPE_I64); + } + if (strcmp(name, "U64") == 0) { + free(name); + return type_new(TYPE_U64); + } + if (strcmp(name, "F32") == 0) { + free(name); + return type_new(TYPE_F32); + } + if (strcmp(name, "f32") == 0) { + free(name); + return type_new(TYPE_F32); + } + if (strcmp(name, "F64") == 0) { + free(name); + return type_new(TYPE_F64); + } + if (strcmp(name, "f64") == 0) { + free(name); + return type_new(TYPE_F64); + } + if (strcmp(name, "usize") == 0) { + free(name); + return type_new(TYPE_USIZE); + } + if (strcmp(name, "isize") == 0) { + free(name); + return type_new(TYPE_ISIZE); + } + if (strcmp(name, "byte") == 0) { + free(name); + return type_new(TYPE_BYTE); + } + if (strcmp(name, "I128") == 0) { + free(name); + return type_new(TYPE_I128); + } + if (strcmp(name, "U128") == 0) { + free(name); + return type_new(TYPE_U128); + } + if (strcmp(name, "i8") == 0) { + free(name); + return type_new(TYPE_I8); + } + if (strcmp(name, "u8") == 0) { + free(name); + return type_new(TYPE_U8); + } + if (strcmp(name, "i16") == 0) { + free(name); + return type_new(TYPE_I16); + } + if (strcmp(name, "u16") == 0) { + free(name); + return type_new(TYPE_U16); + } + if (strcmp(name, "i32") == 0) { + free(name); + return type_new(TYPE_I32); + } + if (strcmp(name, "u32") == 0) { + free(name); + return type_new(TYPE_U32); + } + if (strcmp(name, "i64") == 0) { + free(name); + return type_new(TYPE_I64); + } + if (strcmp(name, "u64") == 0) { + free(name); + return type_new(TYPE_U64); + } + if (strcmp(name, "i128") == 0) { + free(name); + return type_new(TYPE_I128); + } + if (strcmp(name, "u128") == 0) { + free(name); + return type_new(TYPE_U128); + } + if (strcmp(name, "rune") == 0) { + free(name); + return type_new(TYPE_RUNE); + } + if (strcmp(name, "uint") == 0) { + free(name); + return type_new(TYPE_UINT); + } - // Check for Primitives (Base types) - if (strcmp(name, "U0") == 0) - { - free(name); - return type_new(TYPE_VOID); - } - if (strcmp(name, "u0") == 0) - { - free(name); - return type_new(TYPE_VOID); - } - if (strcmp(name, "I8") == 0) - { - free(name); - return type_new(TYPE_I8); - } - if (strcmp(name, "U8") == 0) - { - free(name); - return type_new(TYPE_U8); - } - if (strcmp(name, "I16") == 0) - { - free(name); - return type_new(TYPE_I16); - } - if (strcmp(name, "U16") == 0) - { - free(name); - return type_new(TYPE_U16); - } - if (strcmp(name, "I32") == 0) - { - free(name); - return type_new(TYPE_I32); - } - if (strcmp(name, "U32") == 0) - { - free(name); - return type_new(TYPE_U32); - } - if (strcmp(name, "I64") == 0) - { - free(name); - return type_new(TYPE_I64); - } - if (strcmp(name, "U64") == 0) - { - free(name); - return type_new(TYPE_U64); - } - if (strcmp(name, "F32") == 0) - { - free(name); - return type_new(TYPE_F32); - } - if (strcmp(name, "f32") == 0) - { - free(name); - return type_new(TYPE_F32); - } - if (strcmp(name, "F64") == 0) - { - free(name); - return type_new(TYPE_F64); - } - if (strcmp(name, "f64") == 0) - { - free(name); - return type_new(TYPE_F64); - } - if (strcmp(name, "usize") == 0) - { - free(name); - return type_new(TYPE_USIZE); - } - if (strcmp(name, "isize") == 0) - { - free(name); - return type_new(TYPE_ISIZE); - } - if (strcmp(name, "byte") == 0) - { - free(name); - return type_new(TYPE_BYTE); - } - if (strcmp(name, "I128") == 0) - { - free(name); - return type_new(TYPE_I128); - } - if (strcmp(name, "U128") == 0) - { - free(name); - return type_new(TYPE_U128); - } - if (strcmp(name, "i8") == 0) - { - free(name); - return type_new(TYPE_I8); - } - if (strcmp(name, "u8") == 0) - { - free(name); - return type_new(TYPE_U8); - } - if (strcmp(name, "i16") == 0) - { - free(name); - return type_new(TYPE_I16); - } - if (strcmp(name, "u16") == 0) - { - free(name); - return type_new(TYPE_U16); - } - if (strcmp(name, "i32") == 0) - { - free(name); - return type_new(TYPE_I32); - } - if (strcmp(name, "u32") == 0) - { - free(name); - return type_new(TYPE_U32); - } - if (strcmp(name, "i64") == 0) - { - free(name); - return type_new(TYPE_I64); - } - if (strcmp(name, "u64") == 0) - { - free(name); - return type_new(TYPE_U64); - } - if (strcmp(name, "i128") == 0) - { - free(name); - return type_new(TYPE_I128); - } - if (strcmp(name, "u128") == 0) - { - free(name); - return type_new(TYPE_U128); - } - if (strcmp(name, "rune") == 0) - { - free(name); - return type_new(TYPE_RUNE); - } - if (strcmp(name, "uint") == 0) - { - free(name); - return type_new(TYPE_UINT); - } + if (strcmp(name, "int") == 0) { + free(name); + return type_new(TYPE_INT); + } + if (strcmp(name, "float") == 0) { + free(name); + return type_new(TYPE_F32); + } + if (strcmp(name, "double") == 0) { + free(name); + return type_new(TYPE_F64); + } + if (strcmp(name, "void") == 0) { + free(name); + return type_new(TYPE_VOID); + } + if (strcmp(name, "string") == 0) { + free(name); + return type_new(TYPE_STRING); + } + if (strcmp(name, "bool") == 0) { + free(name); + return type_new(TYPE_BOOL); + } + if (strcmp(name, "char") == 0) { + free(name); + return type_new(TYPE_CHAR); + } - if (strcmp(name, "int") == 0) - { - free(name); - return type_new(TYPE_INT); - } - if (strcmp(name, "float") == 0) - { - free(name); - return type_new(TYPE_F32); - } - if (strcmp(name, "double") == 0) - { - free(name); - return type_new(TYPE_F64); - } - if (strcmp(name, "void") == 0) - { - free(name); - return type_new(TYPE_VOID); - } - if (strcmp(name, "string") == 0) - { - free(name); - return type_new(TYPE_STRING); - } - if (strcmp(name, "bool") == 0) - { - free(name); - return type_new(TYPE_BOOL); - } - if (strcmp(name, "char") == 0) - { - free(name); - return type_new(TYPE_CHAR); - } + // Selective imports ONLY apply when we're NOT in a module context + if (!ctx->current_module_prefix) { + SelectiveImport *si = find_selective_import(ctx, name); + if (si) { + // This is a selectively imported symbol + // Resolve to the actual struct name which was prefixed during module + // parsing + free(name); + name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2); + sprintf(name, "%s_%s", si->source_module, si->symbol); + } + } - // Selective imports ONLY apply when we're NOT in a module context - if (!ctx->current_module_prefix) - { - SelectiveImport *si = find_selective_import(ctx, name); - if (si) - { - // This is a selectively imported symbol - // Resolve to the actual struct name which was prefixed during module - // parsing - free(name); - name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2); - sprintf(name, "%s_%s", si->source_module, si->symbol); - } - } + // If we're IN a module and no selective import matched, apply module prefix + if (ctx->current_module_prefix && !is_known_generic(ctx, name)) { + // Auto-prefix struct name if in module context (unless it's a known + // primitive/generic) + 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; + } - // If we're IN a module and no selective import matched, apply module prefix - if (ctx->current_module_prefix && !is_known_generic(ctx, name)) - { - // Auto-prefix struct name if in module context (unless it's a known - // primitive/generic) - 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; - } + Type *ty = type_new(TYPE_STRUCT); + ty->name = name; + + // Handle Generics <T> + if (lexer_peek(l).type == TOK_LANGLE) { + lexer_next(l); // eat < + Type *arg = parse_type_formal(ctx, l); + + // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one + // op + Token next_tok = lexer_peek(l); + if (next_tok.type == TOK_RANGLE) { + lexer_next(l); // Consume > + } else if (next_tok.type == TOK_OP && next_tok.len == 2 && + strncmp(next_tok.start, ">>", 2) == 0) { + // Split >> into two > tokens + // Consume the first > by advancing lexer manually + l->pos += 1; + l->col += 1; + } else { + zpanic_at(t, "Expected > after generic"); + } + + char *arg_str = type_to_string(arg); + instantiate_generic(ctx, name, arg_str, t); + + char *clean_arg = sanitize_mangled_name(arg_str); + char mangled[256]; + sprintf(mangled, "%s_%s", name, clean_arg); + free(clean_arg); + + free(ty->name); + ty->name = xstrdup(mangled); + free(arg_str); + + ty->kind = TYPE_STRUCT; + ty->args = NULL; + ty->arg_count = 0; + } + return ty; + } + + if (t.type == TOK_LBRACKET) { + lexer_next(l); // eat [ + Type *inner = parse_type_formal(ctx, l); + + // Check for fixed-size array [T; N] + if (lexer_peek(l).type == TOK_SEMICOLON) { + lexer_next(l); // eat ; + Token size_tok = lexer_next(l); + int size = 0; + if (size_tok.type == TOK_INT) { + size = atoi(size_tok.start); + } else if (size_tok.type == TOK_IDENT) { + // Look up in symbol table for constant propagation + char *name = token_strdup(size_tok); + Symbol *sym = find_symbol_entry(ctx, name); + if (sym && sym->is_const_value) { + size = sym->const_int_val; + sym->is_used = 1; // MARK AS USED + } else { + zpanic_at( + size_tok, + "Array size must be a compile-time constant or integer literal"); + } + free(name); + } else { + zpanic_at(size_tok, "Expected integer for array size"); + } + if (lexer_next(l).type != TOK_RBRACKET) { + zpanic_at(lexer_peek(l), "Expected ] after array size"); + } + + Type *arr = type_new(TYPE_ARRAY); + arr->inner = inner; + arr->array_size = size; + return arr; + } - Type *ty = type_new(TYPE_STRUCT); - ty->name = name; - - // Handle Generics <T> - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - Type *arg = parse_type_formal(ctx, l); - - // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one - // op - Token next_tok = lexer_peek(l); - if (next_tok.type == TOK_RANGLE) - { - lexer_next(l); // Consume > - } - else if (next_tok.type == TOK_OP && next_tok.len == 2 && - strncmp(next_tok.start, ">>", 2) == 0) - { - // Split >> into two > tokens - // Consume the first > by advancing lexer manually - l->pos += 1; - l->col += 1; - } - else - { - zpanic_at(t, "Expected > after generic"); - } - - char *arg_str = type_to_string(arg); - instantiate_generic(ctx, name, arg_str, t); - - char *clean_arg = sanitize_mangled_name(arg_str); - char mangled[256]; - sprintf(mangled, "%s_%s", name, clean_arg); - free(clean_arg); - - free(ty->name); - ty->name = xstrdup(mangled); - free(arg_str); - - ty->kind = TYPE_STRUCT; - ty->args = NULL; - ty->arg_count = 0; - } - return ty; - } - - if (t.type == TOK_LBRACKET) - { - lexer_next(l); // eat [ - Type *inner = parse_type_formal(ctx, l); - - // Check for fixed-size array [T; N] - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); // eat ; - Token size_tok = lexer_next(l); - int size = 0; - if (size_tok.type == TOK_INT) - { - size = atoi(size_tok.start); - } - else if (size_tok.type == TOK_IDENT) - { - // Look up in symbol table for constant propagation - char *name = token_strdup(size_tok); - Symbol *sym = find_symbol_entry(ctx, name); - if (sym && sym->is_const_value) - { - size = sym->const_int_val; - sym->is_used = 1; // MARK AS USED - } - else - { - zpanic_at(size_tok, - "Array size must be a compile-time constant or integer literal"); - } - free(name); - } - else - { - zpanic_at(size_tok, "Expected integer for array size"); - } - if (lexer_next(l).type != TOK_RBRACKET) - { - zpanic_at(lexer_peek(l), "Expected ] after array size"); - } - - Type *arr = type_new(TYPE_ARRAY); - arr->inner = inner; - arr->array_size = size; - return arr; - } + // Otherwise it's a slice [T] + if (lexer_next(l).type != TOK_RBRACKET) { + zpanic_at(lexer_peek(l), "Expected ] in type"); + } - // Otherwise it's a slice [T] - if (lexer_next(l).type != TOK_RBRACKET) - { - zpanic_at(lexer_peek(l), "Expected ] in type"); - } + // Register Slice + char *inner_str = type_to_string(inner); + register_slice(ctx, inner_str); - // Register Slice - char *inner_str = type_to_string(inner); - register_slice(ctx, inner_str); - - Type *arr = type_new(TYPE_ARRAY); - arr->inner = inner; - arr->array_size = 0; // 0 means slice, not fixed-size - return arr; - } - - if (t.type == TOK_LPAREN) - { - lexer_next(l); // eat ( - char sig[256]; - sig[0] = 0; - - while (1) - { - Type *sub = parse_type_formal(ctx, l); - char *s = type_to_string(sub); - strcat(sig, s); - free(s); - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - strcat(sig, "_"); - } - else - { - break; - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ) in tuple"); - } + Type *arr = type_new(TYPE_ARRAY); + arr->inner = inner; + arr->array_size = 0; // 0 means slice, not fixed-size + return arr; + } - register_tuple(ctx, sig); + if (t.type == TOK_LPAREN) { + lexer_next(l); // eat ( + char sig[256]; + sig[0] = 0; - char *tuple_name = xmalloc(strlen(sig) + 7); - sprintf(tuple_name, "Tuple_%s", sig); + while (1) { + Type *sub = parse_type_formal(ctx, l); + char *s = type_to_string(sub); + strcat(sig, s); + free(s); - Type *ty = type_new(TYPE_STRUCT); - ty->name = tuple_name; - return ty; + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + strcat(sig, "_"); + } else { + break; + } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ) in tuple"); } - return type_new(TYPE_UNKNOWN); -} + register_tuple(ctx, sig); -Type *parse_type_formal(ParserContext *ctx, Lexer *l) -{ - int is_restrict = 0; - if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 8 && - strncmp(lexer_peek(l).start, "restrict", 8) == 0) - { - lexer_next(l); // eat restrict - is_restrict = 1; - } - - // Example: fn(int, int) -> int - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0 && - lexer_peek(l).len == 2) - { - - lexer_next(l); // eat 'fn' - Type *fn_type = type_new(TYPE_FUNCTION); - fn_type->is_varargs = 0; - - expect(l, TOK_LPAREN, "Expected '(' for function type"); - - // Parse Arguments - fn_type->arg_count = 0; - fn_type->args = NULL; - - while (lexer_peek(l).type != TOK_RPAREN) - { - Type *arg = parse_type_formal(ctx, l); - fn_type->arg_count++; - fn_type->args = xrealloc(fn_type->args, sizeof(Type *) * fn_type->arg_count); - fn_type->args[fn_type->arg_count - 1] = arg; - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } - } - expect(l, TOK_RPAREN, "Expected ')' after function args"); + char *tuple_name = xmalloc(strlen(sig) + 7); + sprintf(tuple_name, "Tuple_%s", sig); - // Parse Return Type (-> Type) - if (lexer_peek(l).type == TOK_ARROW) - { - lexer_next(l); // eat -> - fn_type->inner = parse_type_formal(ctx, l); // Return type stored in inner - } - else - { - fn_type->inner = type_new(TYPE_VOID); - } + Type *ty = type_new(TYPE_STRUCT); + ty->name = tuple_name; + return ty; + } - return fn_type; - } + return type_new(TYPE_UNKNOWN); +} - // Handles: int, Struct, Generic<T>, [Slice], (Tuple) - Type *t = parse_type_base(ctx, l); +Type *parse_type_formal(ParserContext *ctx, Lexer *l) { + int is_restrict = 0; + if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 8 && + strncmp(lexer_peek(l).start, "restrict", 8) == 0) { + lexer_next(l); // eat restrict + is_restrict = 1; + } - // Handles: T*, T**, etc. - while (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '*') - { - lexer_next(l); // consume '*' - t = type_new_ptr(t); - } + // Example: fn(int, int) -> int + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0 && lexer_peek(l).len == 2) { - // 4. Handle Array Suffixes (e.g. int[10]) - while (lexer_peek(l).type == TOK_LBRACKET) - { - lexer_next(l); // consume '[' + lexer_next(l); // eat 'fn' + Type *fn_type = type_new(TYPE_FUNCTION); + fn_type->is_varargs = 0; - int size = 0; - if (lexer_peek(l).type == TOK_INT) - { - Token t = lexer_peek(l); - char buffer[64]; - int len = t.len < 63 ? t.len : 63; - strncpy(buffer, t.start, len); - buffer[len] = 0; - size = atoi(buffer); - lexer_next(l); - } + expect(l, TOK_LPAREN, "Expected '(' for function type"); - expect(l, TOK_RBRACKET, "Expected ']' in array type"); + // Parse Arguments + fn_type->arg_count = 0; + fn_type->args = NULL; - Type *arr = type_new(TYPE_ARRAY); - arr->inner = t; - arr->array_size = size; - t = arr; + while (lexer_peek(l).type != TOK_RPAREN) { + Type *arg = parse_type_formal(ctx, l); + fn_type->arg_count++; + fn_type->args = + xrealloc(fn_type->args, sizeof(Type *) * fn_type->arg_count); + fn_type->args[fn_type->arg_count - 1] = arg; + + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + } else { + break; + } + } + expect(l, TOK_RPAREN, "Expected ')' after function args"); + + // Parse Return Type (-> Type) + if (lexer_peek(l).type == TOK_ARROW) { + lexer_next(l); // eat -> + fn_type->inner = parse_type_formal(ctx, l); // Return type stored in inner + } else { + fn_type->inner = type_new(TYPE_VOID); } - if (is_restrict) - { - t->is_restrict = 1; + return fn_type; + } + + // Handles: int, Struct, Generic<T>, [Slice], (Tuple) + Type *t = parse_type_base(ctx, l); + + // Handles: T*, T**, etc. + while (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '*') { + lexer_next(l); // consume '*' + t = type_new_ptr(t); + } + + // 4. Handle Array Suffixes (e.g. int[10]) + while (lexer_peek(l).type == TOK_LBRACKET) { + lexer_next(l); // consume '[' + + int size = 0; + if (lexer_peek(l).type == TOK_INT) { + Token t = lexer_peek(l); + char buffer[64]; + int len = t.len < 63 ? t.len : 63; + strncpy(buffer, t.start, len); + buffer[len] = 0; + size = atoi(buffer); + lexer_next(l); } - return t; + + expect(l, TOK_RBRACKET, "Expected ']' in array type"); + + Type *arr = type_new(TYPE_ARRAY); + arr->inner = t; + arr->array_size = size; + t = arr; + } + + if (is_restrict) { + t->is_restrict = 1; + } + return t; } -char *parse_type(ParserContext *ctx, Lexer *l) -{ - Type *t = parse_type_formal(ctx, l); +char *parse_type(ParserContext *ctx, Lexer *l) { + Type *t = parse_type_formal(ctx, l); - return type_to_string(t); + return type_to_string(t); } -char *parse_array_literal(ParserContext *ctx, Lexer *l, const char *st) -{ - (void)ctx; // suppress unused parameter warning - lexer_next(l); - size_t cap = 128; - char *c = xmalloc(cap); - c[0] = 0; - int n = 0; - - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_RBRACKET) - { - lexer_next(l); - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } +char *parse_array_literal(ParserContext *ctx, Lexer *l, const char *st) { + (void)ctx; // suppress unused parameter warning + lexer_next(l); + size_t cap = 128; + char *c = xmalloc(cap); + c[0] = 0; + int n = 0; - const char *s = l->src + l->pos; - int d = 0; - while (1) - { - Token it = lexer_peek(l); - if (it.type == TOK_EOF) - { - break; - } - if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RBRACKET)) - { - break; - } - if (it.type == TOK_LBRACKET || it.type == TOK_LPAREN) - { - d++; - } - if (it.type == TOK_RBRACKET || it.type == TOK_RPAREN) - { - d--; - } - lexer_next(l); - } + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_RBRACKET) { + lexer_next(l); + break; + } + if (t.type == TOK_COMMA) { + lexer_next(l); + continue; + } - int len = (l->src + l->pos) - s; - if (strlen(c) + len + 5 > cap) - { - cap *= 2; - c = xrealloc(c, cap); - } - if (n > 0) - { - strcat(c, ", "); - } - strncat(c, s, len); - n++; + const char *s = l->src + l->pos; + int d = 0; + while (1) { + Token it = lexer_peek(l); + if (it.type == TOK_EOF) { + break; + } + if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RBRACKET)) { + break; + } + if (it.type == TOK_LBRACKET || it.type == TOK_LPAREN) { + d++; + } + if (it.type == TOK_RBRACKET || it.type == TOK_RPAREN) { + d--; + } + lexer_next(l); } - char rt[64]; - if (strncmp(st, "Slice_", 6) == 0) - { - strcpy(rt, st + 6); + int len = (l->src + l->pos) - s; + if (strlen(c) + len + 5 > cap) { + cap *= 2; + c = xrealloc(c, cap); } - else - { - strcpy(rt, "int"); + if (n > 0) { + strcat(c, ", "); } - - char *o = xmalloc(strlen(c) + 128); - sprintf(o, "(%s){.data=(%s[]){%s},.len=%d,.cap=%d}", st, rt, c, n, n); - free(c); - return o; + strncat(c, s, len); + n++; + } + + char rt[64]; + if (strncmp(st, "Slice_", 6) == 0) { + strcpy(rt, st + 6); + } else { + strcpy(rt, "int"); + } + + char *o = xmalloc(strlen(c) + 128); + sprintf(o, "(%s){.data=(%s[]){%s},.len=%d,.cap=%d}", st, rt, c, n, n); + free(c); + return o; } -char *parse_tuple_literal(ParserContext *ctx, Lexer *l, const char *tn) -{ - (void)ctx; // suppress unused parameter warning - lexer_next(l); - size_t cap = 128; - char *c = xmalloc(cap); - c[0] = 0; - - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_RPAREN) - { - lexer_next(l); - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } +char *parse_tuple_literal(ParserContext *ctx, Lexer *l, const char *tn) { + (void)ctx; // suppress unused parameter warning + lexer_next(l); + size_t cap = 128; + char *c = xmalloc(cap); + c[0] = 0; + + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_RPAREN) { + lexer_next(l); + break; + } + if (t.type == TOK_COMMA) { + lexer_next(l); + continue; + } - const char *s = l->src + l->pos; - int d = 0; - while (1) - { - Token it = lexer_peek(l); - if (it.type == TOK_EOF) - { - break; - } - if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RPAREN)) - { - break; - } - if (it.type == TOK_LPAREN) - { - d++; - } - if (it.type == TOK_RPAREN) - { - d--; - } - lexer_next(l); - } + const char *s = l->src + l->pos; + int d = 0; + while (1) { + Token it = lexer_peek(l); + if (it.type == TOK_EOF) { + break; + } + if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RPAREN)) { + break; + } + if (it.type == TOK_LPAREN) { + d++; + } + if (it.type == TOK_RPAREN) { + d--; + } + lexer_next(l); + } - int len = (l->src + l->pos) - s; - if (strlen(c) + len + 5 > cap) - { - cap *= 2; - c = xrealloc(c, cap); - } - if (strlen(c) > 0) - { - strcat(c, ", "); - } - strncat(c, s, len); + int len = (l->src + l->pos) - s; + if (strlen(c) + len + 5 > cap) { + cap *= 2; + c = xrealloc(c, cap); } + if (strlen(c) > 0) { + strcat(c, ", "); + } + strncat(c, s, len); + } - char *o = xmalloc(strlen(c) + 128); - sprintf(o, "(%s){%s}", tn, c); - free(c); - return o; + char *o = xmalloc(strlen(c) + 128); + sprintf(o, "(%s){%s}", tn, c); + free(c); + return o; } -char *parse_embed(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - Token t = lexer_next(l); - if (t.type != TOK_STRING) - { - zpanic_at(t, "String required"); - } - char fn[256]; - strncpy(fn, t.start + 1, t.len - 2); - fn[t.len - 2] = 0; - - FILE *f = fopen(fn, "rb"); - if (!f) - { - zpanic_at(t, "404: %s", fn); - } - fseek(f, 0, SEEK_END); - long len = ftell(f); - rewind(f); - unsigned char *b = xmalloc(len); - fread(b, 1, len, f); - fclose(f); - - register_slice(ctx, "char"); - size_t oc = len * 6 + 128; - char *o = xmalloc(oc); - sprintf(o, "(Slice_char){.data=(char[]){"); - char *p = o + strlen(o); - for (int i = 0; i < len; i++) - { - p += sprintf(p, "0x%02X,", b[i]); - } - sprintf(p, "},.len=%ld,.cap=%ld}", len, len); - free(b); - return o; +char *parse_embed(ParserContext *ctx, Lexer *l) { + lexer_next(l); + Token t = lexer_next(l); + if (t.type != TOK_STRING) { + zpanic_at(t, "String required"); + } + char fn[256]; + strncpy(fn, t.start + 1, t.len - 2); + fn[t.len - 2] = 0; + + FILE *f = fopen(fn, "rb"); + if (!f) { + zpanic_at(t, "404: %s", fn); + } + fseek(f, 0, SEEK_END); + long len = ftell(f); + rewind(f); + unsigned char *b = xmalloc(len); + fread(b, 1, len, f); + fclose(f); + + register_slice(ctx, "char"); + size_t oc = len * 6 + 128; + char *o = xmalloc(oc); + sprintf(o, "(Slice_char){.data=(char[]){"); + char *p = o + strlen(o); + for (int i = 0; i < len; i++) { + p += sprintf(p, "0x%02X,", b[i]); + } + sprintf(p, "},.len=%ld,.cap=%ld}", len, len); + free(b); + return o; } diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 2e2fb5b..d616056 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -10,2631 +10,2237 @@ void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, const char *mangled_struct_name, const char *arg); -Token expect(Lexer *l, TokenType type, const char *msg) -{ - Token t = lexer_next(l); - if (t.type != type) - { - zpanic_at(t, "Expected %s, but got '%.*s'", msg, t.len, t.start); - return (Token){type, t.start, 0, t.line, t.col}; - } - return t; +Token expect(Lexer *l, TokenType type, const char *msg) { + Token t = lexer_next(l); + if (t.type != type) { + zpanic_at(t, "Expected %s, but got '%.*s'", msg, t.len, t.start); + return (Token){type, t.start, 0, t.line, t.col}; + } + return t; } -int is_token(Token t, const char *s) -{ - int len = strlen(s); - return (t.len == len && strncmp(t.start, s, len) == 0); +int is_token(Token t, const char *s) { + int len = strlen(s); + return (t.len == len && strncmp(t.start, s, len) == 0); } -char *token_strdup(Token t) -{ - char *s = xmalloc(t.len + 1); - strncpy(s, t.start, t.len); - s[t.len] = 0; - return s; +char *token_strdup(Token t) { + char *s = xmalloc(t.len + 1); + strncpy(s, t.start, t.len); + s[t.len] = 0; + return s; } -void skip_comments(Lexer *l) -{ - while (lexer_peek(l).type == TOK_COMMENT) - { - lexer_next(l); - } +void skip_comments(Lexer *l) { + while (lexer_peek(l).type == TOK_COMMENT) { + lexer_next(l); + } } // C reserved words that conflict with C when used as identifiers. // TODO: We gotta work on these. static const char *C_RESERVED_WORDS[] = { // C types that could be used as names - "double", "float", "signed", "unsigned", "short", "long", "auto", "register", + "double", "float", "signed", "unsigned", "short", "long", "auto", + "register", // C keywords - "switch", "case", "default", "do", "goto", "typedef", "static", "extern", "volatile", "inline", - "restrict", "sizeof", "const", + "switch", "case", "default", "do", "goto", "typedef", "static", "extern", + "volatile", "inline", "restrict", "sizeof", "const", // C11+ keywords - "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", - "_Static_assert", "_Thread_local", NULL}; + "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", + "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local", NULL}; -int is_c_reserved_word(const char *name) -{ - for (int i = 0; C_RESERVED_WORDS[i] != NULL; i++) - { - if (strcmp(name, C_RESERVED_WORDS[i]) == 0) - { - return 1; - } +int is_c_reserved_word(const char *name) { + for (int i = 0; C_RESERVED_WORDS[i] != NULL; i++) { + if (strcmp(name, C_RESERVED_WORDS[i]) == 0) { + return 1; } - return 0; + } + return 0; } -void warn_c_reserved_word(Token t, const char *name) -{ - zwarn_at(t, "Identifier '%s' conflicts with C reserved word", name); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET - "This will cause compilation errors in the generated C code\n"); +void warn_c_reserved_word(Token t, const char *name) { + zwarn_at(t, "Identifier '%s' conflicts with C reserved word", name); + fprintf(stderr, COLOR_CYAN + " = note: " COLOR_RESET + "This will cause compilation errors in the generated C code\n"); } -char *consume_until_semicolon(Lexer *l) -{ - const char *s = l->src + l->pos; - int d = 0; - while (1) - { - Token t = lexer_peek(l); - if (t.type == TOK_EOF) - { - break; - } - if (t.type == TOK_LBRACE || t.type == TOK_LPAREN || t.type == TOK_LBRACKET) - { - d++; - } - if (t.type == TOK_RBRACE || t.type == TOK_RPAREN || t.type == TOK_RBRACKET) - { - d--; - } - - if (d == 0 && t.type == TOK_SEMICOLON) - { - int len = t.start - s; - char *r = xmalloc(len + 1); - strncpy(r, s, len); - r[len] = 0; - lexer_next(l); - return r; - } - lexer_next(l); +char *consume_until_semicolon(Lexer *l) { + const char *s = l->src + l->pos; + int d = 0; + while (1) { + Token t = lexer_peek(l); + if (t.type == TOK_EOF) { + break; + } + if (t.type == TOK_LBRACE || t.type == TOK_LPAREN || + t.type == TOK_LBRACKET) { + d++; + } + if (t.type == TOK_RBRACE || t.type == TOK_RPAREN || + t.type == TOK_RBRACKET) { + d--; + } + + if (d == 0 && t.type == TOK_SEMICOLON) { + int len = t.start - s; + char *r = xmalloc(len + 1); + strncpy(r, s, len); + r[len] = 0; + lexer_next(l); + return r; + } + lexer_next(l); + } + return xstrdup(""); +} + +void enter_scope(ParserContext *ctx) { + Scope *s = xmalloc(sizeof(Scope)); + s->symbols = 0; + s->parent = ctx->current_scope; + ctx->current_scope = s; +} + +void exit_scope(ParserContext *ctx) { + if (!ctx->current_scope) { + return; + } + + // Check for unused variables + Symbol *sym = ctx->current_scope->symbols; + while (sym) { + if (!sym->is_used && strcmp(sym->name, "self") != 0 && + sym->name[0] != '_') { + // Could emit warning here + } + sym = sym->next; + } + + ctx->current_scope = ctx->current_scope->parent; +} + +void add_symbol(ParserContext *ctx, const char *n, const char *t, + Type *type_info) { + add_symbol_with_token(ctx, n, t, type_info, (Token){0}); +} + +void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, + Type *type_info, Token tok) { + if (!ctx->current_scope) { + enter_scope(ctx); + } + + if (n[0] != '_' && ctx->current_scope->parent && strcmp(n, "it") != 0 && + strcmp(n, "self") != 0) { + Scope *p = ctx->current_scope->parent; + while (p) { + Symbol *sh = p->symbols; + while (sh) { + if (strcmp(sh->name, n) == 0) { + warn_shadowing(tok, n); + break; + } + sh = sh->next; + } + if (sh) { + break; // found it + } + p = p->parent; + } + } + Symbol *s = xmalloc(sizeof(Symbol)); + s->name = xstrdup(n); + s->type_name = t ? xstrdup(t) : NULL; + s->type_info = type_info; + s->is_mutable = 1; + s->is_used = 0; + s->decl_token = tok; + s->is_const_value = 0; + s->next = ctx->current_scope->symbols; + ctx->current_scope->symbols = s; + + // LSP: Also add to flat list (for persistent access after scope exit) + Symbol *lsp_copy = xmalloc(sizeof(Symbol)); + *lsp_copy = *s; + lsp_copy->next = ctx->all_symbols; + ctx->all_symbols = lsp_copy; +} + +Type *find_symbol_type_info(ParserContext *ctx, const char *n) { + if (!ctx->current_scope) { + return NULL; + } + Scope *s = ctx->current_scope; + while (s) { + Symbol *sym = s->symbols; + while (sym) { + if (strcmp(sym->name, n) == 0) { + return sym->type_info; + } + sym = sym->next; + } + s = s->parent; + } + return NULL; +} + +char *find_symbol_type(ParserContext *ctx, const char *n) { + if (!ctx->current_scope) { + return NULL; + } + Scope *s = ctx->current_scope; + while (s) { + Symbol *sym = s->symbols; + while (sym) { + if (strcmp(sym->name, n) == 0) { + return sym->type_name; + } + sym = sym->next; + } + s = s->parent; + } + return NULL; +} + +Symbol *find_symbol_entry(ParserContext *ctx, const char *n) { + if (!ctx->current_scope) { + return NULL; + } + Scope *s = ctx->current_scope; + while (s) { + Symbol *sym = s->symbols; + while (sym) { + if (strcmp(sym->name, n) == 0) { + return sym; + } + sym = sym->next; } - return xstrdup(""); + s = s->parent; + } + return NULL; } -void enter_scope(ParserContext *ctx) -{ - Scope *s = xmalloc(sizeof(Scope)); - s->symbols = 0; - s->parent = ctx->current_scope; - ctx->current_scope = s; +// LSP: Search flat symbol list (works after scopes are destroyed). +Symbol *find_symbol_in_all(ParserContext *ctx, const char *n) { + Symbol *sym = ctx->all_symbols; + while (sym) { + if (strcmp(sym->name, n) == 0) { + return sym; + } + sym = sym->next; + } + return NULL; +} + +void init_builtins() { + static int init = 0; + if (init) { + return; + } + init = 1; +} + +void register_func(ParserContext *ctx, const char *name, int count, + char **defaults, Type **arg_types, Type *ret_type, + int is_varargs, int is_async, Token decl_token) { + FuncSig *f = xmalloc(sizeof(FuncSig)); + f->name = xstrdup(name); + f->decl_token = decl_token; + f->total_args = count; + f->defaults = defaults; + f->arg_types = arg_types; + f->ret_type = ret_type; + f->is_varargs = is_varargs; + f->is_async = is_async; + f->must_use = 0; // Default: can discard result + f->next = ctx->func_registry; + ctx->func_registry = f; +} + +void register_func_template(ParserContext *ctx, const char *name, + const char *param, ASTNode *node) { + GenericFuncTemplate *t = xmalloc(sizeof(GenericFuncTemplate)); + t->name = xstrdup(name); + t->generic_param = xstrdup(param); + t->func_node = node; + t->next = ctx->func_templates; + ctx->func_templates = t; +} + +void register_deprecated_func(ParserContext *ctx, const char *name, + const char *reason) { + DeprecatedFunc *d = xmalloc(sizeof(DeprecatedFunc)); + d->name = xstrdup(name); + d->reason = reason ? xstrdup(reason) : NULL; + d->next = ctx->deprecated_funcs; + ctx->deprecated_funcs = d; +} + +DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name) { + DeprecatedFunc *d = ctx->deprecated_funcs; + while (d) { + if (strcmp(d->name, name) == 0) { + return d; + } + d = d->next; + } + return NULL; +} + +GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name) { + GenericFuncTemplate *t = ctx->func_templates; + while (t) { + if (strcmp(t->name, name) == 0) { + return t; + } + t = t->next; + } + return NULL; +} + +void register_generic(ParserContext *ctx, char *name) { + for (int i = 0; i < ctx->known_generics_count; i++) { + if (strcmp(ctx->known_generics[i], name) == 0) { + return; + } + } + ctx->known_generics[ctx->known_generics_count++] = strdup(name); +} + +int is_known_generic(ParserContext *ctx, char *name) { + for (int i = 0; i < ctx->known_generics_count; i++) { + if (strcmp(ctx->known_generics[i], name) == 0) { + return 1; + } + } + return 0; +} + +void register_impl_template(ParserContext *ctx, const char *sname, + const char *param, ASTNode *node) { + GenericImplTemplate *t = xmalloc(sizeof(GenericImplTemplate)); + t->struct_name = xstrdup(sname); + t->generic_param = xstrdup(param); + t->impl_node = node; + t->next = ctx->impl_templates; + ctx->impl_templates = t; + + // Late binding: Check if any existing instantiations match this new impl + // template + Instantiation *inst = ctx->instantiations; + while (inst) { + if (inst->template_name && strcmp(inst->template_name, sname) == 0) { + instantiate_methods(ctx, t, inst->name, inst->concrete_arg); + } + inst = inst->next; + } +} + +void add_to_struct_list(ParserContext *ctx, ASTNode *node) { + StructRef *r = xmalloc(sizeof(StructRef)); + r->node = node; + r->next = ctx->parsed_structs_list; + ctx->parsed_structs_list = r; +} + +void add_to_enum_list(ParserContext *ctx, ASTNode *node) { + StructRef *r = xmalloc(sizeof(StructRef)); + r->node = node; + r->next = ctx->parsed_enums_list; + ctx->parsed_enums_list = r; +} + +void add_to_func_list(ParserContext *ctx, ASTNode *node) { + StructRef *r = xmalloc(sizeof(StructRef)); + r->node = node; + r->next = ctx->parsed_funcs_list; + ctx->parsed_funcs_list = r; +} + +void add_to_impl_list(ParserContext *ctx, ASTNode *node) { + StructRef *r = xmalloc(sizeof(StructRef)); + r->node = node; + r->next = ctx->parsed_impls_list; + ctx->parsed_impls_list = r; } -void exit_scope(ParserContext *ctx) -{ - if (!ctx->current_scope) - { - return; - } +void add_to_global_list(ParserContext *ctx, ASTNode *node) { + StructRef *r = xmalloc(sizeof(StructRef)); + r->node = node; + r->next = ctx->parsed_globals_list; + ctx->parsed_globals_list = r; +} - // Check for unused variables - Symbol *sym = ctx->current_scope->symbols; - while (sym) - { - if (!sym->is_used && strcmp(sym->name, "self") != 0 && sym->name[0] != '_') - { - // Could emit warning here - } - sym = sym->next; - } +void register_builtins(ParserContext *ctx) { + Type *t = type_new(TYPE_BOOL); + t->is_const = 1; + add_symbol(ctx, "true", "bool", t); + + t = type_new(TYPE_BOOL); + t->is_const = 1; + add_symbol(ctx, "false", "bool", t); + + // Register 'free' + Type *void_t = type_new(TYPE_VOID); + add_symbol(ctx, "free", "void", void_t); - ctx->current_scope = ctx->current_scope->parent; + // Register common libc functions to avoid warnings + add_symbol(ctx, "strdup", "string", type_new(TYPE_STRING)); + add_symbol(ctx, "malloc", "void*", type_new_ptr(void_t)); + add_symbol(ctx, "realloc", "void*", type_new_ptr(void_t)); + add_symbol(ctx, "calloc", "void*", type_new_ptr(void_t)); + add_symbol(ctx, "puts", "int", type_new(TYPE_INT)); + add_symbol(ctx, "printf", "int", type_new(TYPE_INT)); + add_symbol(ctx, "strcmp", "int", type_new(TYPE_INT)); + add_symbol(ctx, "strlen", "int", type_new(TYPE_INT)); + add_symbol(ctx, "strcpy", "string", type_new(TYPE_STRING)); + add_symbol(ctx, "strcat", "string", type_new(TYPE_STRING)); + add_symbol(ctx, "exit", "void", void_t); + + // File I/O + add_symbol(ctx, "fopen", "void*", type_new_ptr(void_t)); + add_symbol(ctx, "fclose", "int", type_new(TYPE_INT)); + add_symbol(ctx, "fread", "usize", type_new(TYPE_USIZE)); + add_symbol(ctx, "fwrite", "usize", type_new(TYPE_USIZE)); + add_symbol(ctx, "fseek", "int", type_new(TYPE_INT)); + add_symbol(ctx, "ftell", "long", type_new(TYPE_I64)); + add_symbol(ctx, "rewind", "void", void_t); + add_symbol(ctx, "fprintf", "int", type_new(TYPE_INT)); + add_symbol(ctx, "sprintf", "int", type_new(TYPE_INT)); + add_symbol(ctx, "feof", "int", type_new(TYPE_INT)); + add_symbol(ctx, "ferror", "int", type_new(TYPE_INT)); + add_symbol(ctx, "usleep", "int", type_new(TYPE_INT)); } -void add_symbol(ParserContext *ctx, const char *n, const char *t, Type *type_info) -{ - add_symbol_with_token(ctx, n, t, type_info, (Token){0}); +void add_instantiated_func(ParserContext *ctx, ASTNode *fn) { + fn->next = ctx->instantiated_funcs; + ctx->instantiated_funcs = fn; +} + +void register_enum_variant(ParserContext *ctx, const char *ename, + const char *vname, int tag) { + EnumVariantReg *r = xmalloc(sizeof(EnumVariantReg)); + r->enum_name = xstrdup(ename); + r->variant_name = xstrdup(vname); + r->tag_id = tag; + r->next = ctx->enum_variants; + ctx->enum_variants = r; } -void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, Type *type_info, - Token tok) -{ - if (!ctx->current_scope) - { - enter_scope(ctx); +EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname) { + EnumVariantReg *r = ctx->enum_variants; + while (r) { + if (strcmp(r->variant_name, vname) == 0) { + return r; } + r = r->next; + } + return NULL; +} - if (n[0] != '_' && ctx->current_scope->parent && strcmp(n, "it") != 0 && strcmp(n, "self") != 0) - { - Scope *p = ctx->current_scope->parent; - while (p) - { - Symbol *sh = p->symbols; - while (sh) - { - if (strcmp(sh->name, n) == 0) - { - warn_shadowing(tok, n); - break; - } - sh = sh->next; - } - if (sh) - { - break; // found it - } - p = p->parent; - } - } - Symbol *s = xmalloc(sizeof(Symbol)); - s->name = xstrdup(n); - s->type_name = t ? xstrdup(t) : NULL; - s->type_info = type_info; - s->is_mutable = 1; - s->is_used = 0; - s->decl_token = tok; - s->is_const_value = 0; - s->next = ctx->current_scope->symbols; - ctx->current_scope->symbols = s; - - // LSP: Also add to flat list (for persistent access after scope exit) - Symbol *lsp_copy = xmalloc(sizeof(Symbol)); - *lsp_copy = *s; - lsp_copy->next = ctx->all_symbols; - ctx->all_symbols = lsp_copy; -} - -Type *find_symbol_type_info(ParserContext *ctx, const char *n) -{ - if (!ctx->current_scope) - { - return NULL; - } - Scope *s = ctx->current_scope; - while (s) - { - Symbol *sym = s->symbols; - while (sym) - { - if (strcmp(sym->name, n) == 0) - { - return sym->type_info; - } - sym = sym->next; - } - s = s->parent; - } - return NULL; +void register_lambda(ParserContext *ctx, ASTNode *node) { + LambdaRef *ref = xmalloc(sizeof(LambdaRef)); + ref->node = node; + ref->next = ctx->global_lambdas; + ctx->global_lambdas = ref; +} + +void register_var_mutability(ParserContext *ctx, const char *name, + int is_mutable) { + VarMutability *v = xmalloc(sizeof(VarMutability)); + v->name = xstrdup(name); + v->is_mutable = is_mutable; + v->next = ctx->var_mutability_table; + ctx->var_mutability_table = v; } -char *find_symbol_type(ParserContext *ctx, const char *n) -{ - if (!ctx->current_scope) - { - return NULL; - } - Scope *s = ctx->current_scope; - while (s) - { - Symbol *sym = s->symbols; - while (sym) - { - if (strcmp(sym->name, n) == 0) - { - return sym->type_name; - } - sym = sym->next; - } - s = s->parent; - } - return NULL; +int is_var_mutable(ParserContext *ctx, const char *name) { + for (VarMutability *v = ctx->var_mutability_table; v; v = v->next) { + if (strcmp(v->name, name) == 0) { + return v->is_mutable; + } + } + return 1; } -Symbol *find_symbol_entry(ParserContext *ctx, const char *n) -{ - if (!ctx->current_scope) - { - return NULL; +void register_extern_symbol(ParserContext *ctx, const char *name) { + // Check for duplicates + for (int i = 0; i < ctx->extern_symbol_count; i++) { + if (strcmp(ctx->extern_symbols[i], name) == 0) { + return; } - Scope *s = ctx->current_scope; - while (s) - { - Symbol *sym = s->symbols; - while (sym) - { - if (strcmp(sym->name, n) == 0) - { - return sym; - } - sym = sym->next; - } - s = s->parent; - } - return NULL; + } + + // Grow array if needed + if (ctx->extern_symbol_count == 0) { + ctx->extern_symbols = xmalloc(sizeof(char *) * 64); + } else if (ctx->extern_symbol_count % 64 == 0) { + ctx->extern_symbols = xrealloc( + ctx->extern_symbols, sizeof(char *) * (ctx->extern_symbol_count + 64)); + } + + ctx->extern_symbols[ctx->extern_symbol_count++] = xstrdup(name); +} + +int is_extern_symbol(ParserContext *ctx, const char *name) { + for (int i = 0; i < ctx->extern_symbol_count; i++) { + if (strcmp(ctx->extern_symbols[i], name) == 0) { + return 1; + } + } + return 0; } -// LSP: Search flat symbol list (works after scopes are destroyed). -Symbol *find_symbol_in_all(ParserContext *ctx, const char *n) -{ - Symbol *sym = ctx->all_symbols; - while (sym) - { - if (strcmp(sym->name, n) == 0) - { - return sym; - } - sym = sym->next; +// Unified check: should we suppress "undefined variable" warning for this name? +int should_suppress_undef_warning(ParserContext *ctx, const char *name) { + if (strcmp(name, "struct") == 0 || strcmp(name, "tv") == 0) { + return 1; + } + + if (is_extern_symbol(ctx, name)) { + return 1; + } + + int is_all_caps = 1; + for (const char *p = name; *p; p++) { + if (islower((unsigned char)*p)) { + is_all_caps = 0; + break; } - return NULL; + } + if (is_all_caps && name[0] != '\0') { + return 1; + } + + if (ctx->has_external_includes) { + return 1; + } + + return 0; +} + +void register_slice(ParserContext *ctx, const char *type) { + SliceType *c = ctx->used_slices; + while (c) { + if (strcmp(c->name, type) == 0) { + return; + } + c = c->next; + } + SliceType *n = xmalloc(sizeof(SliceType)); + n->name = xstrdup(type); + n->next = ctx->used_slices; + ctx->used_slices = n; + + // Register Struct Def for Reflection + char slice_name[256]; + sprintf(slice_name, "Slice_%s", type); + + ASTNode *len_f = ast_create(NODE_FIELD); + len_f->field.name = xstrdup("len"); + len_f->field.type = xstrdup("int"); + ASTNode *cap_f = ast_create(NODE_FIELD); + cap_f->field.name = xstrdup("cap"); + cap_f->field.type = xstrdup("int"); + ASTNode *data_f = ast_create(NODE_FIELD); + data_f->field.name = xstrdup("data"); + char ptr_type[256]; + sprintf(ptr_type, "%s*", type); + data_f->field.type = xstrdup(ptr_type); + + data_f->next = len_f; + len_f->next = cap_f; + + ASTNode *def = ast_create(NODE_STRUCT); + def->strct.name = xstrdup(slice_name); + def->strct.fields = data_f; + + register_struct_def(ctx, slice_name, def); } -void init_builtins() -{ - static int init = 0; - if (init) - { - return; - } - init = 1; -} - -void register_func(ParserContext *ctx, const char *name, int count, char **defaults, - Type **arg_types, Type *ret_type, int is_varargs, int is_async, Token decl_token) -{ - FuncSig *f = xmalloc(sizeof(FuncSig)); - f->name = xstrdup(name); - f->decl_token = decl_token; - f->total_args = count; - f->defaults = defaults; - f->arg_types = arg_types; - f->ret_type = ret_type; - f->is_varargs = is_varargs; - f->is_async = is_async; - f->must_use = 0; // Default: can discard result - f->next = ctx->func_registry; - ctx->func_registry = f; -} - -void register_func_template(ParserContext *ctx, const char *name, const char *param, ASTNode *node) -{ - GenericFuncTemplate *t = xmalloc(sizeof(GenericFuncTemplate)); - t->name = xstrdup(name); - t->generic_param = xstrdup(param); - t->func_node = node; - t->next = ctx->func_templates; - ctx->func_templates = t; -} - -void register_deprecated_func(ParserContext *ctx, const char *name, const char *reason) -{ - DeprecatedFunc *d = xmalloc(sizeof(DeprecatedFunc)); - d->name = xstrdup(name); - d->reason = reason ? xstrdup(reason) : NULL; - d->next = ctx->deprecated_funcs; - ctx->deprecated_funcs = d; -} - -DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name) -{ - DeprecatedFunc *d = ctx->deprecated_funcs; - while (d) - { - if (strcmp(d->name, name) == 0) - { - return d; - } - d = d->next; +void register_tuple(ParserContext *ctx, const char *sig) { + TupleType *c = ctx->used_tuples; + while (c) { + if (strcmp(c->sig, sig) == 0) { + return; + } + c = c->next; + } + TupleType *n = xmalloc(sizeof(TupleType)); + n->sig = xstrdup(sig); + n->next = ctx->used_tuples; + ctx->used_tuples = n; + + char struct_name[1024]; + sprintf(struct_name, "Tuple_%s", sig); + + ASTNode *s_def = ast_create(NODE_STRUCT); + s_def->strct.name = xstrdup(struct_name); + + char *s_sig = xstrdup(sig); + char *tok = strtok(s_sig, "_"); + ASTNode *head = NULL, *tail = NULL; + int i = 0; + while (tok) { + ASTNode *f = ast_create(NODE_FIELD); + char fname[32]; + sprintf(fname, "v%d", i++); + f->field.name = xstrdup(fname); + f->field.type = xstrdup(tok); + + if (!head) { + head = f; + } else { + tail->next = f; + } + tail = f; + + tok = strtok(NULL, "_"); + } + free(s_sig); + s_def->strct.fields = head; + + register_struct_def(ctx, struct_name, s_def); +} + +void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node) { + StructDef *d = xmalloc(sizeof(StructDef)); + d->name = xstrdup(name); + d->node = node; + d->next = ctx->struct_defs; + ctx->struct_defs = d; +} + +ASTNode *find_struct_def(ParserContext *ctx, const char *name) { + Instantiation *i = ctx->instantiations; + while (i) { + if (strcmp(i->name, name) == 0) { + return i->struct_node; + } + i = i->next; + } + + ASTNode *s = ctx->instantiated_structs; + while (s) { + if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0) { + return s; + } + s = s->next; + } + + StructRef *r = ctx->parsed_structs_list; + while (r) { + if (strcmp(r->node->strct.name, name) == 0) { + return r->node; + } + r = r->next; + } + + // Check manually registered definitions (e.g. Slices) + StructDef *d = ctx->struct_defs; + while (d) { + if (strcmp(d->name, name) == 0) { + return d->node; } - return NULL; -} + d = d->next; + } -GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name) -{ - GenericFuncTemplate *t = ctx->func_templates; - while (t) - { - if (strcmp(t->name, name) == 0) - { - return t; - } - t = t->next; + // Check enums list (for @derive(Eq) and field type lookups) + StructRef *e = ctx->parsed_enums_list; + while (e) { + if (e->node->type == NODE_ENUM && strcmp(e->node->enm.name, name) == 0) { + return e->node; } - return NULL; + e = e->next; + } + + return NULL; } -void register_generic(ParserContext *ctx, char *name) -{ - for (int i = 0; i < ctx->known_generics_count; i++) - { - if (strcmp(ctx->known_generics[i], name) == 0) - { - return; - } +Module *find_module(ParserContext *ctx, const char *alias) { + Module *m = ctx->modules; + while (m) { + if (strcmp(m->alias, alias) == 0) { + return m; } - ctx->known_generics[ctx->known_generics_count++] = strdup(name); + m = m->next; + } + return NULL; } -int is_known_generic(ParserContext *ctx, char *name) -{ - for (int i = 0; i < ctx->known_generics_count; i++) - { - if (strcmp(ctx->known_generics[i], name) == 0) - { - return 1; - } - } - return 0; +void register_module(ParserContext *ctx, const char *alias, const char *path) { + Module *m = xmalloc(sizeof(Module)); + m->alias = xstrdup(alias); + m->path = xstrdup(path); + m->base_name = extract_module_name(path); + m->next = ctx->modules; + ctx->modules = m; } -void register_impl_template(ParserContext *ctx, const char *sname, const char *param, ASTNode *node) -{ - GenericImplTemplate *t = xmalloc(sizeof(GenericImplTemplate)); - t->struct_name = xstrdup(sname); - t->generic_param = xstrdup(param); - t->impl_node = node; - t->next = ctx->impl_templates; - ctx->impl_templates = t; +void register_selective_import(ParserContext *ctx, const char *symbol, + const char *alias, const char *source_module) { + SelectiveImport *si = xmalloc(sizeof(SelectiveImport)); + si->symbol = xstrdup(symbol); + si->alias = alias ? xstrdup(alias) : NULL; + si->source_module = xstrdup(source_module); + si->next = ctx->selective_imports; + ctx->selective_imports = si; +} - // Late binding: Check if any existing instantiations match this new impl - // template - Instantiation *inst = ctx->instantiations; - while (inst) - { - if (inst->template_name && strcmp(inst->template_name, sname) == 0) - { - instantiate_methods(ctx, t, inst->name, inst->concrete_arg); - } - inst = inst->next; - } -} - -void add_to_struct_list(ParserContext *ctx, ASTNode *node) -{ - StructRef *r = xmalloc(sizeof(StructRef)); - r->node = node; - r->next = ctx->parsed_structs_list; - ctx->parsed_structs_list = r; -} - -void add_to_enum_list(ParserContext *ctx, ASTNode *node) -{ - StructRef *r = xmalloc(sizeof(StructRef)); - r->node = node; - r->next = ctx->parsed_enums_list; - ctx->parsed_enums_list = r; -} - -void add_to_func_list(ParserContext *ctx, ASTNode *node) -{ - StructRef *r = xmalloc(sizeof(StructRef)); - r->node = node; - r->next = ctx->parsed_funcs_list; - ctx->parsed_funcs_list = r; -} - -void add_to_impl_list(ParserContext *ctx, ASTNode *node) -{ - StructRef *r = xmalloc(sizeof(StructRef)); - r->node = node; - r->next = ctx->parsed_impls_list; - ctx->parsed_impls_list = r; -} - -void add_to_global_list(ParserContext *ctx, ASTNode *node) -{ - StructRef *r = xmalloc(sizeof(StructRef)); - r->node = node; - r->next = ctx->parsed_globals_list; - ctx->parsed_globals_list = r; -} - -void register_builtins(ParserContext *ctx) -{ - Type *t = type_new(TYPE_BOOL); - t->is_const = 1; - add_symbol(ctx, "true", "bool", t); - - t = type_new(TYPE_BOOL); - t->is_const = 1; - add_symbol(ctx, "false", "bool", t); - - // Register 'free' - Type *void_t = type_new(TYPE_VOID); - add_symbol(ctx, "free", "void", void_t); - - // Register common libc functions to avoid warnings - add_symbol(ctx, "strdup", "string", type_new(TYPE_STRING)); - add_symbol(ctx, "malloc", "void*", type_new_ptr(void_t)); - add_symbol(ctx, "realloc", "void*", type_new_ptr(void_t)); - add_symbol(ctx, "calloc", "void*", type_new_ptr(void_t)); - add_symbol(ctx, "puts", "int", type_new(TYPE_INT)); - add_symbol(ctx, "printf", "int", type_new(TYPE_INT)); - add_symbol(ctx, "strcmp", "int", type_new(TYPE_INT)); - add_symbol(ctx, "strlen", "int", type_new(TYPE_INT)); - add_symbol(ctx, "strcpy", "string", type_new(TYPE_STRING)); - add_symbol(ctx, "strcat", "string", type_new(TYPE_STRING)); - add_symbol(ctx, "exit", "void", void_t); - - // File I/O - add_symbol(ctx, "fopen", "void*", type_new_ptr(void_t)); - add_symbol(ctx, "fclose", "int", type_new(TYPE_INT)); - add_symbol(ctx, "fread", "usize", type_new(TYPE_USIZE)); - add_symbol(ctx, "fwrite", "usize", type_new(TYPE_USIZE)); - add_symbol(ctx, "fseek", "int", type_new(TYPE_INT)); - add_symbol(ctx, "ftell", "long", type_new(TYPE_I64)); - add_symbol(ctx, "rewind", "void", void_t); - add_symbol(ctx, "fprintf", "int", type_new(TYPE_INT)); - add_symbol(ctx, "sprintf", "int", type_new(TYPE_INT)); - add_symbol(ctx, "feof", "int", type_new(TYPE_INT)); - add_symbol(ctx, "ferror", "int", type_new(TYPE_INT)); - add_symbol(ctx, "usleep", "int", type_new(TYPE_INT)); -} - -void add_instantiated_func(ParserContext *ctx, ASTNode *fn) -{ - fn->next = ctx->instantiated_funcs; - ctx->instantiated_funcs = fn; -} - -void register_enum_variant(ParserContext *ctx, const char *ename, const char *vname, int tag) -{ - EnumVariantReg *r = xmalloc(sizeof(EnumVariantReg)); - r->enum_name = xstrdup(ename); - r->variant_name = xstrdup(vname); - r->tag_id = tag; - r->next = ctx->enum_variants; - ctx->enum_variants = r; -} - -EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname) -{ - EnumVariantReg *r = ctx->enum_variants; - while (r) - { - if (strcmp(r->variant_name, vname) == 0) - { - return r; - } - r = r->next; +SelectiveImport *find_selective_import(ParserContext *ctx, const char *name) { + SelectiveImport *si = ctx->selective_imports; + while (si) { + if (si->alias && strcmp(si->alias, name) == 0) { + return si; } - return NULL; + if (!si->alias && strcmp(si->symbol, name) == 0) { + return si; + } + si = si->next; + } + return NULL; } -void register_lambda(ParserContext *ctx, ASTNode *node) -{ - LambdaRef *ref = xmalloc(sizeof(LambdaRef)); - ref->node = node; - ref->next = ctx->global_lambdas; - ctx->global_lambdas = ref; +char *extract_module_name(const char *path) { + const char *slash = strrchr(path, '/'); + const char *base = slash ? slash + 1 : path; + const char *dot = strrchr(base, '.'); + int len = dot ? (int)(dot - base) : (int)strlen(base); + char *name = xmalloc(len + 1); + strncpy(name, base, len); + name[len] = 0; + return name; } -void register_var_mutability(ParserContext *ctx, const char *name, int is_mutable) -{ - VarMutability *v = xmalloc(sizeof(VarMutability)); - v->name = xstrdup(name); - v->is_mutable = is_mutable; - v->next = ctx->var_mutability_table; - ctx->var_mutability_table = v; -} +int is_ident_char(char c) { return isalnum(c) || c == '_'; } -int is_var_mutable(ParserContext *ctx, const char *name) -{ - for (VarMutability *v = ctx->var_mutability_table; v; v = v->next) - { - if (strcmp(v->name, name) == 0) - { - return v->is_mutable; - } - } - return 1; +ASTNode *copy_fields(ASTNode *fields) { + if (!fields) { + return NULL; + } + ASTNode *n = ast_create(NODE_FIELD); + n->field.name = xstrdup(fields->field.name); + n->field.type = xstrdup(fields->field.type); + n->next = copy_fields(fields->next); + return n; +} +char *replace_in_string(const char *src, const char *old_w, const char *new_w) { + if (!src || !old_w || !new_w) { + return src ? xstrdup(src) : NULL; + } + + char *result; + int i, cnt = 0; + int newWlen = strlen(new_w); + int oldWlen = strlen(old_w); + + for (i = 0; src[i] != '\0'; i++) { + if (strstr(&src[i], old_w) == &src[i]) { + // Check boundaries to ensure we match whole words only + int valid = 1; + + // Check preceding character + if (i > 0 && is_ident_char(src[i - 1])) { + valid = 0; + } + + // Check following character + if (valid && is_ident_char(src[i + oldWlen])) { + valid = 0; + } + + if (valid) { + cnt++; + i += oldWlen - 1; + } + } + } + + // Allocate result buffer + result = (char *)xmalloc(i + cnt * (newWlen - oldWlen) + 1); + + i = 0; + while (*src) { + if (strstr(src, old_w) == src) { + int valid = 1; + + // Check boundary relative to the *new* result buffer built so far + if (i > 0 && is_ident_char(result[i - 1])) { + valid = 0; + } + + // Check boundary relative to the *original* source string + if (valid && is_ident_char(src[oldWlen])) { + valid = 0; + } + + if (valid) { + strcpy(&result[i], new_w); + i += newWlen; + src += oldWlen; + } else { + result[i++] = *src++; + } + } else { + result[i++] = *src++; + } + } + result[i] = '\0'; + return result; } -void register_extern_symbol(ParserContext *ctx, const char *name) -{ - // Check for duplicates - for (int i = 0; i < ctx->extern_symbol_count; i++) - { - if (strcmp(ctx->extern_symbols[i], name) == 0) - { - return; - } - } +char *replace_type_str(const char *src, const char *param, const char *concrete, + const char *old_struct, const char *new_struct) { + if (!src) { + return NULL; + } + + if (strcmp(src, param) == 0) { + return xstrdup(concrete); + } + + if (old_struct && new_struct && strcmp(src, old_struct) == 0) { + return xstrdup(new_struct); + } + + if (old_struct && new_struct && param) { + char *mangled = xmalloc(strlen(old_struct) + strlen(param) + 2); + sprintf(mangled, "%s_%s", old_struct, param); + if (strcmp(src, mangled) == 0) { + free(mangled); + return xstrdup(new_struct); + } + free(mangled); + } + + if (param && concrete && src) { + char suffix[256]; + sprintf(suffix, "_%s", param); + size_t slen = strlen(src); + size_t plen = strlen(suffix); + if (slen > plen && strcmp(src + slen - plen, suffix) == 0) { + // Ends with _T -> Replace suffix with _int (sanitize for pointers like + // JsonValue*) + char *clean_concrete = sanitize_mangled_name(concrete); + char *ret = xmalloc(slen - plen + strlen(clean_concrete) + 2); + strncpy(ret, src, slen - plen); + ret[slen - plen] = 0; + strcat(ret, "_"); + strcat(ret, clean_concrete); + free(clean_concrete); + return ret; + } + } + + size_t len = strlen(src); + if (len > 1 && src[len - 1] == '*') { + char *base = xmalloc(len); + strncpy(base, src, len - 1); + base[len - 1] = 0; + + char *new_base = + replace_type_str(base, param, concrete, old_struct, new_struct); + free(base); + + if (strcmp(new_base, base) != 0) { + char *ret = xmalloc(strlen(new_base) + 2); + sprintf(ret, "%s*", new_base); + free(new_base); + return ret; + } + free(new_base); + } + + if (strncmp(src, "Slice_", 6) == 0) { + char *base = xstrdup(src + 6); + char *new_base = + replace_type_str(base, param, concrete, old_struct, new_struct); + free(base); + + if (strcmp(new_base, base) != 0) { + char *ret = xmalloc(strlen(new_base) + 7); + sprintf(ret, "Slice_%s", new_base); + free(new_base); + return ret; + } + free(new_base); + } + + return xstrdup(src); +} + +ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, + const char *os, const char *ns); + +Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, + const char *ns) { + if (!t) { + return NULL; + } - // Grow array if needed - if (ctx->extern_symbol_count == 0) - { - ctx->extern_symbols = xmalloc(sizeof(char *) * 64); + if ((t->kind == TYPE_STRUCT || t->kind == TYPE_GENERIC) && t->name && + strcmp(t->name, p) == 0) { + if (strcmp(c, "int") == 0) { + return type_new(TYPE_INT); } - else if (ctx->extern_symbol_count % 64 == 0) - { - ctx->extern_symbols = - xrealloc(ctx->extern_symbols, sizeof(char *) * (ctx->extern_symbol_count + 64)); + if (strcmp(c, "float") == 0) { + return type_new(TYPE_FLOAT); } - - ctx->extern_symbols[ctx->extern_symbol_count++] = xstrdup(name); -} - -int is_extern_symbol(ParserContext *ctx, const char *name) -{ - for (int i = 0; i < ctx->extern_symbol_count; i++) - { - if (strcmp(ctx->extern_symbols[i], name) == 0) - { - return 1; - } + if (strcmp(c, "void") == 0) { + return type_new(TYPE_VOID); } - return 0; -} - -// Unified check: should we suppress "undefined variable" warning for this name? -int should_suppress_undef_warning(ParserContext *ctx, const char *name) -{ - if (strcmp(name, "struct") == 0 || strcmp(name, "tv") == 0) - { - return 1; + if (strcmp(c, "string") == 0) { + return type_new(TYPE_STRING); } - - if (is_extern_symbol(ctx, name)) - { - return 1; + if (strcmp(c, "bool") == 0) { + return type_new(TYPE_BOOL); + } + if (strcmp(c, "char") == 0) { + return type_new(TYPE_CHAR); } - int is_all_caps = 1; - for (const char *p = name; *p; p++) - { - if (islower((unsigned char)*p)) - { - is_all_caps = 0; - break; - } + if (strcmp(c, "I8") == 0) { + return type_new(TYPE_I8); } - if (is_all_caps && name[0] != '\0') - { - return 1; + if (strcmp(c, "U8") == 0) { + return type_new(TYPE_U8); } - - if (ctx->has_external_includes) - { - return 1; + if (strcmp(c, "I16") == 0) { + return type_new(TYPE_I16); } - - return 0; -} - -void register_slice(ParserContext *ctx, const char *type) -{ - SliceType *c = ctx->used_slices; - while (c) - { - if (strcmp(c->name, type) == 0) - { - return; - } - c = c->next; - } - SliceType *n = xmalloc(sizeof(SliceType)); - n->name = xstrdup(type); - n->next = ctx->used_slices; - ctx->used_slices = n; - - // Register Struct Def for Reflection - char slice_name[256]; - sprintf(slice_name, "Slice_%s", type); - - ASTNode *len_f = ast_create(NODE_FIELD); - len_f->field.name = xstrdup("len"); - len_f->field.type = xstrdup("int"); - ASTNode *cap_f = ast_create(NODE_FIELD); - cap_f->field.name = xstrdup("cap"); - cap_f->field.type = xstrdup("int"); - ASTNode *data_f = ast_create(NODE_FIELD); - data_f->field.name = xstrdup("data"); - char ptr_type[256]; - sprintf(ptr_type, "%s*", type); - data_f->field.type = xstrdup(ptr_type); - - data_f->next = len_f; - len_f->next = cap_f; - - ASTNode *def = ast_create(NODE_STRUCT); - def->strct.name = xstrdup(slice_name); - def->strct.fields = data_f; - - register_struct_def(ctx, slice_name, def); -} - -void register_tuple(ParserContext *ctx, const char *sig) -{ - TupleType *c = ctx->used_tuples; - while (c) - { - if (strcmp(c->sig, sig) == 0) - { - return; - } - c = c->next; + if (strcmp(c, "U16") == 0) { + return type_new(TYPE_U16); } - TupleType *n = xmalloc(sizeof(TupleType)); - n->sig = xstrdup(sig); - n->next = ctx->used_tuples; - ctx->used_tuples = n; - - char struct_name[1024]; - sprintf(struct_name, "Tuple_%s", sig); - - ASTNode *s_def = ast_create(NODE_STRUCT); - s_def->strct.name = xstrdup(struct_name); - - char *s_sig = xstrdup(sig); - char *tok = strtok(s_sig, "_"); - ASTNode *head = NULL, *tail = NULL; - int i = 0; - while (tok) - { - ASTNode *f = ast_create(NODE_FIELD); - char fname[32]; - sprintf(fname, "v%d", i++); - f->field.name = xstrdup(fname); - f->field.type = xstrdup(tok); - - if (!head) - { - head = f; - } - else - { - tail->next = f; - } - tail = f; - - tok = strtok(NULL, "_"); + if (strcmp(c, "I32") == 0) { + return type_new(TYPE_I32); } - free(s_sig); - s_def->strct.fields = head; - - register_struct_def(ctx, struct_name, s_def); -} - -void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node) -{ - StructDef *d = xmalloc(sizeof(StructDef)); - d->name = xstrdup(name); - d->node = node; - d->next = ctx->struct_defs; - ctx->struct_defs = d; -} - -ASTNode *find_struct_def(ParserContext *ctx, const char *name) -{ - Instantiation *i = ctx->instantiations; - while (i) - { - if (strcmp(i->name, name) == 0) - { - return i->struct_node; - } - i = i->next; + if (strcmp(c, "U32") == 0) { + return type_new(TYPE_U32); } - - ASTNode *s = ctx->instantiated_structs; - while (s) - { - if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0) - { - return s; - } - s = s->next; + if (strcmp(c, "I64") == 0) { + return type_new(TYPE_I64); } - - StructRef *r = ctx->parsed_structs_list; - while (r) - { - if (strcmp(r->node->strct.name, name) == 0) - { - return r->node; - } - r = r->next; + if (strcmp(c, "U64") == 0) { + return type_new(TYPE_U64); } - - // Check manually registered definitions (e.g. Slices) - StructDef *d = ctx->struct_defs; - while (d) - { - if (strcmp(d->name, name) == 0) - { - return d->node; - } - d = d->next; + if (strcmp(c, "F32") == 0) { + return type_new(TYPE_F32); } - - // Check enums list (for @derive(Eq) and field type lookups) - StructRef *e = ctx->parsed_enums_list; - while (e) - { - if (e->node->type == NODE_ENUM && strcmp(e->node->enm.name, name) == 0) - { - return e->node; - } - e = e->next; + if (strcmp(c, "f32") == 0) { + return type_new(TYPE_F32); } - - return NULL; -} - -Module *find_module(ParserContext *ctx, const char *alias) -{ - Module *m = ctx->modules; - while (m) - { - if (strcmp(m->alias, alias) == 0) - { - return m; - } - m = m->next; + if (strcmp(c, "F64") == 0) { + return type_new(TYPE_F64); } - return NULL; -} - -void register_module(ParserContext *ctx, const char *alias, const char *path) -{ - Module *m = xmalloc(sizeof(Module)); - m->alias = xstrdup(alias); - m->path = xstrdup(path); - m->base_name = extract_module_name(path); - m->next = ctx->modules; - ctx->modules = m; -} - -void register_selective_import(ParserContext *ctx, const char *symbol, const char *alias, - const char *source_module) -{ - SelectiveImport *si = xmalloc(sizeof(SelectiveImport)); - si->symbol = xstrdup(symbol); - si->alias = alias ? xstrdup(alias) : NULL; - si->source_module = xstrdup(source_module); - si->next = ctx->selective_imports; - ctx->selective_imports = si; -} - -SelectiveImport *find_selective_import(ParserContext *ctx, const char *name) -{ - SelectiveImport *si = ctx->selective_imports; - while (si) - { - if (si->alias && strcmp(si->alias, name) == 0) - { - return si; - } - if (!si->alias && strcmp(si->symbol, name) == 0) - { - return si; - } - si = si->next; + if (strcmp(c, "f64") == 0) { + return type_new(TYPE_F64); } - return NULL; -} - -char *extract_module_name(const char *path) -{ - const char *slash = strrchr(path, '/'); - const char *base = slash ? slash + 1 : path; - const char *dot = strrchr(base, '.'); - int len = dot ? (int)(dot - base) : (int)strlen(base); - char *name = xmalloc(len + 1); - strncpy(name, base, len); - name[len] = 0; - return name; -} -int is_ident_char(char c) -{ - return isalnum(c) || c == '_'; -} - -ASTNode *copy_fields(ASTNode *fields) -{ - if (!fields) - { - return NULL; + if (strcmp(c, "usize") == 0) { + return type_new(TYPE_USIZE); } - ASTNode *n = ast_create(NODE_FIELD); - n->field.name = xstrdup(fields->field.name); - n->field.type = xstrdup(fields->field.type); - n->next = copy_fields(fields->next); - return n; -} -char *replace_in_string(const char *src, const char *old_w, const char *new_w) -{ - if (!src || !old_w || !new_w) - { - return src ? xstrdup(src) : NULL; + if (strcmp(c, "isize") == 0) { + return type_new(TYPE_ISIZE); } - - char *result; - int i, cnt = 0; - int newWlen = strlen(new_w); - int oldWlen = strlen(old_w); - - for (i = 0; src[i] != '\0'; i++) - { - if (strstr(&src[i], old_w) == &src[i]) - { - // Check boundaries to ensure we match whole words only - int valid = 1; - - // Check preceding character - if (i > 0 && is_ident_char(src[i - 1])) - { - valid = 0; - } - - // Check following character - if (valid && is_ident_char(src[i + oldWlen])) - { - valid = 0; - } - - if (valid) - { - cnt++; - i += oldWlen - 1; - } - } + if (strcmp(c, "byte") == 0) { + return type_new(TYPE_BYTE); } - - // Allocate result buffer - result = (char *)xmalloc(i + cnt * (newWlen - oldWlen) + 1); - - i = 0; - while (*src) - { - if (strstr(src, old_w) == src) - { - int valid = 1; - - // Check boundary relative to the *new* result buffer built so far - if (i > 0 && is_ident_char(result[i - 1])) - { - valid = 0; - } - - // Check boundary relative to the *original* source string - if (valid && is_ident_char(src[oldWlen])) - { - valid = 0; - } - - if (valid) - { - strcpy(&result[i], new_w); - i += newWlen; - src += oldWlen; - } - else - { - result[i++] = *src++; - } - } - else - { - result[i++] = *src++; - } + if (strcmp(c, "I128") == 0) { + return type_new(TYPE_I128); } - result[i] = '\0'; - return result; -} - -char *replace_type_str(const char *src, const char *param, const char *concrete, - const char *old_struct, const char *new_struct) -{ - if (!src) - { - return NULL; + if (strcmp(c, "U128") == 0) { + return type_new(TYPE_U128); } - - if (strcmp(src, param) == 0) - { - return xstrdup(concrete); + if (strcmp(c, "i8") == 0) { + return type_new(TYPE_I8); } - - if (old_struct && new_struct && strcmp(src, old_struct) == 0) - { - return xstrdup(new_struct); + if (strcmp(c, "u8") == 0) { + return type_new(TYPE_U8); } - - if (old_struct && new_struct && param) - { - char *mangled = xmalloc(strlen(old_struct) + strlen(param) + 2); - sprintf(mangled, "%s_%s", old_struct, param); - if (strcmp(src, mangled) == 0) - { - free(mangled); - return xstrdup(new_struct); - } - free(mangled); + if (strcmp(c, "i16") == 0) { + return type_new(TYPE_I16); } - - if (param && concrete && src) - { - char suffix[256]; - sprintf(suffix, "_%s", param); - size_t slen = strlen(src); - size_t plen = strlen(suffix); - if (slen > plen && strcmp(src + slen - plen, suffix) == 0) - { - // Ends with _T -> Replace suffix with _int (sanitize for pointers like - // JsonValue*) - char *clean_concrete = sanitize_mangled_name(concrete); - char *ret = xmalloc(slen - plen + strlen(clean_concrete) + 2); - strncpy(ret, src, slen - plen); - ret[slen - plen] = 0; - strcat(ret, "_"); - strcat(ret, clean_concrete); - free(clean_concrete); - return ret; - } + if (strcmp(c, "u16") == 0) { + return type_new(TYPE_U16); } - - size_t len = strlen(src); - if (len > 1 && src[len - 1] == '*') - { - char *base = xmalloc(len); - strncpy(base, src, len - 1); - base[len - 1] = 0; - - char *new_base = replace_type_str(base, param, concrete, old_struct, new_struct); - free(base); - - if (strcmp(new_base, base) != 0) - { - char *ret = xmalloc(strlen(new_base) + 2); - sprintf(ret, "%s*", new_base); - free(new_base); - return ret; - } - free(new_base); + if (strcmp(c, "i32") == 0) { + return type_new(TYPE_I32); } - - if (strncmp(src, "Slice_", 6) == 0) - { - char *base = xstrdup(src + 6); - char *new_base = replace_type_str(base, param, concrete, old_struct, new_struct); - free(base); - - if (strcmp(new_base, base) != 0) - { - char *ret = xmalloc(strlen(new_base) + 7); - sprintf(ret, "Slice_%s", new_base); - free(new_base); - return ret; - } - free(new_base); + if (strcmp(c, "u32") == 0) { + return type_new(TYPE_U32); } - - return xstrdup(src); -} - -ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os, - const char *ns); - -Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, const char *ns) -{ - if (!t) - { - return NULL; + if (strcmp(c, "i64") == 0) { + return type_new(TYPE_I64); } - - if ((t->kind == TYPE_STRUCT || t->kind == TYPE_GENERIC) && t->name && strcmp(t->name, p) == 0) - { - if (strcmp(c, "int") == 0) - { - return type_new(TYPE_INT); - } - if (strcmp(c, "float") == 0) - { - return type_new(TYPE_FLOAT); - } - if (strcmp(c, "void") == 0) - { - return type_new(TYPE_VOID); - } - if (strcmp(c, "string") == 0) - { - return type_new(TYPE_STRING); - } - if (strcmp(c, "bool") == 0) - { - return type_new(TYPE_BOOL); - } - if (strcmp(c, "char") == 0) - { - return type_new(TYPE_CHAR); - } - - if (strcmp(c, "I8") == 0) - { - return type_new(TYPE_I8); - } - if (strcmp(c, "U8") == 0) - { - return type_new(TYPE_U8); - } - if (strcmp(c, "I16") == 0) - { - return type_new(TYPE_I16); - } - if (strcmp(c, "U16") == 0) - { - return type_new(TYPE_U16); - } - if (strcmp(c, "I32") == 0) - { - return type_new(TYPE_I32); - } - if (strcmp(c, "U32") == 0) - { - return type_new(TYPE_U32); - } - if (strcmp(c, "I64") == 0) - { - return type_new(TYPE_I64); - } - if (strcmp(c, "U64") == 0) - { - return type_new(TYPE_U64); - } - if (strcmp(c, "F32") == 0) - { - return type_new(TYPE_F32); - } - if (strcmp(c, "f32") == 0) - { - return type_new(TYPE_F32); - } - if (strcmp(c, "F64") == 0) - { - return type_new(TYPE_F64); - } - if (strcmp(c, "f64") == 0) - { - return type_new(TYPE_F64); - } - - if (strcmp(c, "usize") == 0) - { - return type_new(TYPE_USIZE); - } - if (strcmp(c, "isize") == 0) - { - return type_new(TYPE_ISIZE); - } - if (strcmp(c, "byte") == 0) - { - return type_new(TYPE_BYTE); - } - if (strcmp(c, "I128") == 0) - { - return type_new(TYPE_I128); - } - if (strcmp(c, "U128") == 0) - { - return type_new(TYPE_U128); - } - if (strcmp(c, "i8") == 0) - { - return type_new(TYPE_I8); - } - if (strcmp(c, "u8") == 0) - { - return type_new(TYPE_U8); - } - if (strcmp(c, "i16") == 0) - { - return type_new(TYPE_I16); - } - if (strcmp(c, "u16") == 0) - { - return type_new(TYPE_U16); - } - if (strcmp(c, "i32") == 0) - { - return type_new(TYPE_I32); - } - if (strcmp(c, "u32") == 0) - { - return type_new(TYPE_U32); - } - if (strcmp(c, "i64") == 0) - { - return type_new(TYPE_I64); - } - if (strcmp(c, "u64") == 0) - { - return type_new(TYPE_U64); - } - if (strcmp(c, "i128") == 0) - { - return type_new(TYPE_I128); - } - if (strcmp(c, "u128") == 0) - { - return type_new(TYPE_U128); - } - - if (strcmp(c, "rune") == 0) - { - return type_new(TYPE_RUNE); - } - if (strcmp(c, "uint") == 0) - { - return type_new(TYPE_UINT); - } - - Type *n = type_new(TYPE_STRUCT); - n->name = sanitize_mangled_name(c); - return n; + if (strcmp(c, "u64") == 0) { + return type_new(TYPE_U64); } - - Type *n = xmalloc(sizeof(Type)); - *n = *t; - - if (t->name) - { - if (os && ns && strcmp(t->name, os) == 0) - { - n->name = xstrdup(ns); - n->kind = TYPE_STRUCT; - n->arg_count = 0; - n->args = NULL; - } - - else if (p && c) - { - char suffix[256]; - sprintf(suffix, "_%s", p); // e.g. "_T" - size_t nlen = strlen(t->name); - size_t slen = strlen(suffix); - - if (nlen > slen && strcmp(t->name + nlen - slen, suffix) == 0) - { - // It ends in _T. Replace with _int (c), sanitizing for pointers - char *clean_c = sanitize_mangled_name(c); - char *new_name = xmalloc(nlen - slen + strlen(clean_c) + 2); - strncpy(new_name, t->name, nlen - slen); - new_name[nlen - slen] = 0; - strcat(new_name, "_"); - strcat(new_name, clean_c); - free(clean_c); - n->name = new_name; - // Ensure it's concrete to prevent double mangling later - n->kind = TYPE_STRUCT; - n->arg_count = 0; - n->args = NULL; - } - else - { - n->name = xstrdup(t->name); - } - } - else - { - n->name = xstrdup(t->name); - } + if (strcmp(c, "i128") == 0) { + return type_new(TYPE_I128); } - - if (t->kind == TYPE_POINTER || t->kind == TYPE_ARRAY) - { - n->inner = replace_type_formal(t->inner, p, c, os, ns); + if (strcmp(c, "u128") == 0) { + return type_new(TYPE_U128); } - if (n->arg_count > 0 && t->args) - { - n->args = xmalloc(sizeof(Type *) * t->arg_count); - for (int i = 0; i < t->arg_count; i++) - { - n->args[i] = replace_type_formal(t->args[i], p, c, os, ns); - } + if (strcmp(c, "rune") == 0) { + return type_new(TYPE_RUNE); + } + if (strcmp(c, "uint") == 0) { + return type_new(TYPE_UINT); } + Type *n = type_new(TYPE_STRUCT); + n->name = sanitize_mangled_name(c); return n; + } + + Type *n = xmalloc(sizeof(Type)); + *n = *t; + + if (t->name) { + if (os && ns && strcmp(t->name, os) == 0) { + n->name = xstrdup(ns); + n->kind = TYPE_STRUCT; + n->arg_count = 0; + n->args = NULL; + } + + else if (p && c) { + char suffix[256]; + sprintf(suffix, "_%s", p); // e.g. "_T" + size_t nlen = strlen(t->name); + size_t slen = strlen(suffix); + + if (nlen > slen && strcmp(t->name + nlen - slen, suffix) == 0) { + // It ends in _T. Replace with _int (c), sanitizing for pointers + char *clean_c = sanitize_mangled_name(c); + char *new_name = xmalloc(nlen - slen + strlen(clean_c) + 2); + strncpy(new_name, t->name, nlen - slen); + new_name[nlen - slen] = 0; + strcat(new_name, "_"); + strcat(new_name, clean_c); + free(clean_c); + n->name = new_name; + // Ensure it's concrete to prevent double mangling later + n->kind = TYPE_STRUCT; + n->arg_count = 0; + n->args = NULL; + } else { + n->name = xstrdup(t->name); + } + } else { + n->name = xstrdup(t->name); + } + } + + if (t->kind == TYPE_POINTER || t->kind == TYPE_ARRAY) { + n->inner = replace_type_formal(t->inner, p, c, os, ns); + } + + if (n->arg_count > 0 && t->args) { + n->args = xmalloc(sizeof(Type *) * t->arg_count); + for (int i = 0; i < t->arg_count; i++) { + n->args[i] = replace_type_formal(t->args[i], p, c, os, ns); + } + } + + return n; } // Helper to replace generic params in mangled names (e.g. Option_V_None -> // Option_int_None) -char *replace_mangled_part(const char *src, const char *param, const char *concrete) -{ - if (!src || !param || !concrete) - { - return src ? xstrdup(src) : NULL; - } +char *replace_mangled_part(const char *src, const char *param, + const char *concrete) { + if (!src || !param || !concrete) { + return src ? xstrdup(src) : NULL; + } + + char *result = xmalloc(4096); // Basic buffer for simplicity + result[0] = 0; + + const char *curr = src; + char *out = result; + int plen = strlen(param); + + while (*curr) { + // Check if param matches here + if (strncmp(curr, param, plen) == 0) { + // Check boundaries: Must be delimited by quoted boundaries, OR + // underscores, OR string ends + int valid = 1; + + // Check Prev: Start of string OR Underscore + if (curr > src) { + if (*(curr - 1) != '_' && is_ident_char(*(curr - 1))) { + valid = 0; + } + } + + // Check Next: End of string OR Underscore + if (valid && curr[plen] != 0 && curr[plen] != '_' && + is_ident_char(curr[plen])) { + valid = 0; + } + + if (valid) { + strcpy(out, concrete); + out += strlen(concrete); + curr += plen; + continue; + } + } + *out++ = *curr++; + } + *out = 0; + return xstrdup(result); +} + +ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, + const char *os, const char *ns) { + if (!n) { + return NULL; + } - char *result = xmalloc(4096); // Basic buffer for simplicity - result[0] = 0; + ASTNode *new_node = xmalloc(sizeof(ASTNode)); + *new_node = *n; - const char *curr = src; - char *out = result; - int plen = strlen(param); + if (n->resolved_type) { + new_node->resolved_type = replace_type_str(n->resolved_type, p, c, os, ns); + } + new_node->type_info = replace_type_formal(n->type_info, p, c, os, ns); - while (*curr) - { - // Check if param matches here - if (strncmp(curr, param, plen) == 0) - { - // Check boundaries: Must be delimited by quoted boundaries, OR - // underscores, OR string ends - int valid = 1; - - // Check Prev: Start of string OR Underscore - if (curr > src) - { - if (*(curr - 1) != '_' && is_ident_char(*(curr - 1))) - { - valid = 0; - } - } + new_node->next = copy_ast_replacing(n->next, p, c, os, ns); - // Check Next: End of string OR Underscore - if (valid && curr[plen] != 0 && curr[plen] != '_' && is_ident_char(curr[plen])) - { - valid = 0; - } + switch (n->type) { + case NODE_FUNCTION: + new_node->func.name = xstrdup(n->func.name); + new_node->func.ret_type = replace_type_str(n->func.ret_type, p, c, os, ns); - if (valid) - { - strcpy(out, concrete); - out += strlen(concrete); - curr += plen; - continue; - } - } - *out++ = *curr++; + char *tmp_args = replace_in_string(n->func.args, p, c); + if (os && ns) { + char *tmp2 = replace_in_string(tmp_args, os, ns); + free(tmp_args); + tmp_args = tmp2; } - *out = 0; - return xstrdup(result); -} - -ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os, - const char *ns) -{ - if (!n) - { - return NULL; + if (p && c) { + char *clean_c = sanitize_mangled_name(c); + char *tmp3 = replace_mangled_part(tmp_args, p, clean_c); + free(clean_c); + free(tmp_args); + tmp_args = tmp3; } + new_node->func.args = tmp_args; - ASTNode *new_node = xmalloc(sizeof(ASTNode)); - *new_node = *n; - - if (n->resolved_type) - { - new_node->resolved_type = replace_type_str(n->resolved_type, p, c, os, ns); + new_node->func.ret_type_info = + replace_type_formal(n->func.ret_type_info, p, c, os, ns); + if (n->func.arg_types) { + new_node->func.arg_types = xmalloc(sizeof(Type *) * n->func.arg_count); + for (int i = 0; i < n->func.arg_count; i++) { + new_node->func.arg_types[i] = + replace_type_formal(n->func.arg_types[i], p, c, os, ns); + } } - new_node->type_info = replace_type_formal(n->type_info, p, c, os, ns); - - new_node->next = copy_ast_replacing(n->next, p, c, os, ns); - - switch (n->type) - { - case NODE_FUNCTION: - new_node->func.name = xstrdup(n->func.name); - new_node->func.ret_type = replace_type_str(n->func.ret_type, p, c, os, ns); - - char *tmp_args = replace_in_string(n->func.args, p, c); - if (os && ns) - { - char *tmp2 = replace_in_string(tmp_args, os, ns); - free(tmp_args); - tmp_args = tmp2; - } - if (p && c) - { - char *clean_c = sanitize_mangled_name(c); - char *tmp3 = replace_mangled_part(tmp_args, p, clean_c); - free(clean_c); - free(tmp_args); - tmp_args = tmp3; - } - new_node->func.args = tmp_args; - - new_node->func.ret_type_info = replace_type_formal(n->func.ret_type_info, p, c, os, ns); - if (n->func.arg_types) - { - new_node->func.arg_types = xmalloc(sizeof(Type *) * n->func.arg_count); - for (int i = 0; i < n->func.arg_count; i++) - { - new_node->func.arg_types[i] = - replace_type_formal(n->func.arg_types[i], p, c, os, ns); - } - } - new_node->func.body = copy_ast_replacing(n->func.body, p, c, os, ns); - break; - case NODE_BLOCK: - new_node->block.statements = copy_ast_replacing(n->block.statements, p, c, os, ns); - break; - case NODE_RAW_STMT: - { - char *s1 = replace_in_string(n->raw_stmt.content, p, c); - if (os && ns) - { - char *s2 = replace_in_string(s1, os, ns); - free(s1); - s1 = s2; - } - - if (p && c) - { - char *clean_c = sanitize_mangled_name(c); - char *s3 = replace_mangled_part(s1, p, clean_c); - free(clean_c); - free(s1); - s1 = s3; - } - - new_node->raw_stmt.content = s1; - } + new_node->func.body = copy_ast_replacing(n->func.body, p, c, os, ns); break; - case NODE_VAR_DECL: - new_node->var_decl.name = xstrdup(n->var_decl.name); - new_node->var_decl.type_str = replace_type_str(n->var_decl.type_str, p, c, os, ns); - new_node->var_decl.init_expr = copy_ast_replacing(n->var_decl.init_expr, p, c, os, ns); - break; - case NODE_RETURN: - new_node->ret.value = copy_ast_replacing(n->ret.value, p, c, os, ns); - break; - case NODE_EXPR_BINARY: - new_node->binary.left = copy_ast_replacing(n->binary.left, p, c, os, ns); - new_node->binary.right = copy_ast_replacing(n->binary.right, p, c, os, ns); - new_node->binary.op = xstrdup(n->binary.op); - break; - case NODE_EXPR_UNARY: - new_node->unary.op = xstrdup(n->unary.op); - new_node->unary.operand = copy_ast_replacing(n->unary.operand, p, c, os, ns); - break; - case NODE_EXPR_CALL: - new_node->call.callee = copy_ast_replacing(n->call.callee, p, c, os, ns); - new_node->call.args = copy_ast_replacing(n->call.args, p, c, os, ns); - new_node->call.arg_names = n->call.arg_names; // Share pointer (shallow copy) - new_node->call.arg_count = n->call.arg_count; - break; - case NODE_EXPR_VAR: - { - char *n1 = xstrdup(n->var_ref.name); - if (p && c) - { - char *clean_c = sanitize_mangled_name(c); - char *n2 = replace_mangled_part(n1, p, clean_c); - free(clean_c); - free(n1); - n1 = n2; - } - new_node->var_ref.name = n1; + case NODE_BLOCK: + new_node->block.statements = + copy_ast_replacing(n->block.statements, p, c, os, ns); + break; + case NODE_RAW_STMT: { + char *s1 = replace_in_string(n->raw_stmt.content, p, c); + if (os && ns) { + char *s2 = replace_in_string(s1, os, ns); + free(s1); + s1 = s2; + } + + if (p && c) { + char *clean_c = sanitize_mangled_name(c); + char *s3 = replace_mangled_part(s1, p, clean_c); + free(clean_c); + free(s1); + s1 = s3; + } + + new_node->raw_stmt.content = s1; + } break; + case NODE_VAR_DECL: + new_node->var_decl.name = xstrdup(n->var_decl.name); + new_node->var_decl.type_str = + replace_type_str(n->var_decl.type_str, p, c, os, ns); + new_node->var_decl.init_expr = + copy_ast_replacing(n->var_decl.init_expr, p, c, os, ns); + break; + case NODE_RETURN: + new_node->ret.value = copy_ast_replacing(n->ret.value, p, c, os, ns); + break; + case NODE_EXPR_BINARY: + new_node->binary.left = copy_ast_replacing(n->binary.left, p, c, os, ns); + new_node->binary.right = copy_ast_replacing(n->binary.right, p, c, os, ns); + new_node->binary.op = xstrdup(n->binary.op); + break; + case NODE_EXPR_UNARY: + new_node->unary.op = xstrdup(n->unary.op); + new_node->unary.operand = + copy_ast_replacing(n->unary.operand, p, c, os, ns); + break; + case NODE_EXPR_CALL: + new_node->call.callee = copy_ast_replacing(n->call.callee, p, c, os, ns); + new_node->call.args = copy_ast_replacing(n->call.args, p, c, os, ns); + new_node->call.arg_names = + n->call.arg_names; // Share pointer (shallow copy) + new_node->call.arg_count = n->call.arg_count; + break; + case NODE_EXPR_VAR: { + char *n1 = xstrdup(n->var_ref.name); + if (p && c) { + char *clean_c = sanitize_mangled_name(c); + char *n2 = replace_mangled_part(n1, p, clean_c); + free(clean_c); + free(n1); + n1 = n2; + } + new_node->var_ref.name = n1; + } break; + case NODE_FIELD: + new_node->field.name = xstrdup(n->field.name); + new_node->field.type = replace_type_str(n->field.type, p, c, os, ns); + break; + case NODE_EXPR_LITERAL: + if (n->literal.type_kind == 2) { + new_node->literal.string_val = xstrdup(n->literal.string_val); } break; - case NODE_FIELD: - new_node->field.name = xstrdup(n->field.name); - new_node->field.type = replace_type_str(n->field.type, p, c, os, ns); - break; - case NODE_EXPR_LITERAL: - if (n->literal.type_kind == 2) - { - new_node->literal.string_val = xstrdup(n->literal.string_val); - } - break; - case NODE_EXPR_MEMBER: - new_node->member.target = copy_ast_replacing(n->member.target, p, c, os, ns); - new_node->member.field = xstrdup(n->member.field); - break; - case NODE_EXPR_INDEX: - new_node->index.array = copy_ast_replacing(n->index.array, p, c, os, ns); - new_node->index.index = copy_ast_replacing(n->index.index, p, c, os, ns); - break; - case NODE_EXPR_CAST: - new_node->cast.target_type = replace_type_str(n->cast.target_type, p, c, os, ns); - new_node->cast.expr = copy_ast_replacing(n->cast.expr, p, c, os, ns); - break; - case NODE_EXPR_STRUCT_INIT: - new_node->struct_init.struct_name = - replace_type_str(n->struct_init.struct_name, p, c, os, ns); - ASTNode *h = NULL, *t = NULL, *curr = n->struct_init.fields; - while (curr) - { - ASTNode *cp = copy_ast_replacing(curr, p, c, os, ns); - cp->next = NULL; - if (!h) - { - h = cp; - } - else - { - t->next = cp; - } - t = cp; - curr = curr->next; - } - new_node->struct_init.fields = h; - break; - case NODE_IF: - new_node->if_stmt.condition = copy_ast_replacing(n->if_stmt.condition, p, c, os, ns); - new_node->if_stmt.then_body = copy_ast_replacing(n->if_stmt.then_body, p, c, os, ns); - new_node->if_stmt.else_body = copy_ast_replacing(n->if_stmt.else_body, p, c, os, ns); - break; - case NODE_WHILE: - new_node->while_stmt.condition = copy_ast_replacing(n->while_stmt.condition, p, c, os, ns); - new_node->while_stmt.body = copy_ast_replacing(n->while_stmt.body, p, c, os, ns); - break; - case NODE_FOR: - new_node->for_stmt.init = copy_ast_replacing(n->for_stmt.init, p, c, os, ns); - new_node->for_stmt.condition = copy_ast_replacing(n->for_stmt.condition, p, c, os, ns); - new_node->for_stmt.step = copy_ast_replacing(n->for_stmt.step, p, c, os, ns); - new_node->for_stmt.body = copy_ast_replacing(n->for_stmt.body, p, c, os, ns); - break; - - case NODE_MATCH_CASE: - if (n->match_case.pattern) - { - char *s1 = replace_in_string(n->match_case.pattern, p, c); - if (os && ns) - { - char *s2 = replace_in_string(s1, os, ns); - free(s1); - s1 = s2; - char *colons = strstr(s1, "::"); - if (colons) - { - colons[0] = '_'; - memmove(colons + 1, colons + 2, strlen(colons + 2) + 1); - } - } - new_node->match_case.pattern = s1; - } - new_node->match_case.body = copy_ast_replacing(n->match_case.body, p, c, os, ns); - if (n->match_case.guard) - { - new_node->match_case.guard = copy_ast_replacing(n->match_case.guard, p, c, os, ns); - } - break; + case NODE_EXPR_MEMBER: + new_node->member.target = + copy_ast_replacing(n->member.target, p, c, os, ns); + new_node->member.field = xstrdup(n->member.field); + break; + case NODE_EXPR_INDEX: + new_node->index.array = copy_ast_replacing(n->index.array, p, c, os, ns); + new_node->index.index = copy_ast_replacing(n->index.index, p, c, os, ns); + break; + case NODE_EXPR_CAST: + new_node->cast.target_type = + replace_type_str(n->cast.target_type, p, c, os, ns); + new_node->cast.expr = copy_ast_replacing(n->cast.expr, p, c, os, ns); + break; + case NODE_EXPR_STRUCT_INIT: + new_node->struct_init.struct_name = + replace_type_str(n->struct_init.struct_name, p, c, os, ns); + ASTNode *h = NULL, *t = NULL, *curr = n->struct_init.fields; + while (curr) { + ASTNode *cp = copy_ast_replacing(curr, p, c, os, ns); + cp->next = NULL; + if (!h) { + h = cp; + } else { + t->next = cp; + } + t = cp; + curr = curr->next; + } + new_node->struct_init.fields = h; + break; + case NODE_IF: + new_node->if_stmt.condition = + copy_ast_replacing(n->if_stmt.condition, p, c, os, ns); + new_node->if_stmt.then_body = + copy_ast_replacing(n->if_stmt.then_body, p, c, os, ns); + new_node->if_stmt.else_body = + copy_ast_replacing(n->if_stmt.else_body, p, c, os, ns); + break; + case NODE_WHILE: + new_node->while_stmt.condition = + copy_ast_replacing(n->while_stmt.condition, p, c, os, ns); + new_node->while_stmt.body = + copy_ast_replacing(n->while_stmt.body, p, c, os, ns); + break; + case NODE_FOR: + new_node->for_stmt.init = + copy_ast_replacing(n->for_stmt.init, p, c, os, ns); + new_node->for_stmt.condition = + copy_ast_replacing(n->for_stmt.condition, p, c, os, ns); + new_node->for_stmt.step = + copy_ast_replacing(n->for_stmt.step, p, c, os, ns); + new_node->for_stmt.body = + copy_ast_replacing(n->for_stmt.body, p, c, os, ns); + break; - case NODE_IMPL: - new_node->impl.struct_name = replace_type_str(n->impl.struct_name, p, c, os, ns); - new_node->impl.methods = copy_ast_replacing(n->impl.methods, p, c, os, ns); - break; - default: - break; + case NODE_MATCH_CASE: + if (n->match_case.pattern) { + char *s1 = replace_in_string(n->match_case.pattern, p, c); + if (os && ns) { + char *s2 = replace_in_string(s1, os, ns); + free(s1); + s1 = s2; + char *colons = strstr(s1, "::"); + if (colons) { + colons[0] = '_'; + memmove(colons + 1, colons + 2, strlen(colons + 2) + 1); + } + } + new_node->match_case.pattern = s1; + } + new_node->match_case.body = + copy_ast_replacing(n->match_case.body, p, c, os, ns); + if (n->match_case.guard) { + new_node->match_case.guard = + copy_ast_replacing(n->match_case.guard, p, c, os, ns); } - return new_node; -} + break; -// Helper to sanitize type names for mangling (e.g. "int*" -> "intPtr") -char *sanitize_mangled_name(const char *s) -{ - char *buf = xmalloc(strlen(s) * 4 + 1); - char *p = buf; - while (*s) - { - if (*s == '*') - { - strcpy(p, "Ptr"); - p += 3; - } - else if (*s == ' ') - { - *p++ = '_'; - } - else if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || - *s == '_') - { - *p++ = *s; - } - else - { - *p++ = '_'; - } - s++; - } - *p = 0; - return buf; + case NODE_IMPL: + new_node->impl.struct_name = + replace_type_str(n->impl.struct_name, p, c, os, ns); + new_node->impl.methods = copy_ast_replacing(n->impl.methods, p, c, os, ns); + break; + default: + break; + } + return new_node; } -FuncSig *find_func(ParserContext *ctx, const char *name) -{ - FuncSig *c = ctx->func_registry; - while (c) - { - if (strcmp(c->name, name) == 0) - { - return c; - } - c = c->next; - } +// Helper to sanitize type names for mangling (e.g. "int*" -> "intPtr") +char *sanitize_mangled_name(const char *s) { + char *buf = xmalloc(strlen(s) * 4 + 1); + char *p = buf; + while (*s) { + if (*s == '*') { + strcpy(p, "Ptr"); + p += 3; + } else if (*s == ' ') { + *p++ = '_'; + } else if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || + (*s >= '0' && *s <= '9') || *s == '_') { + *p++ = *s; + } else { + *p++ = '_'; + } + s++; + } + *p = 0; + return buf; +} + +FuncSig *find_func(ParserContext *ctx, const char *name) { + FuncSig *c = ctx->func_registry; + while (c) { + if (strcmp(c->name, name) == 0) { + return c; + } + c = c->next; + } + return NULL; +} + +char *instantiate_function_template(ParserContext *ctx, const char *name, + const char *concrete_type) { + GenericFuncTemplate *tpl = find_func_template(ctx, name); + if (!tpl) { return NULL; -} - -char *instantiate_function_template(ParserContext *ctx, const char *name, const char *concrete_type) -{ - GenericFuncTemplate *tpl = find_func_template(ctx, name); - if (!tpl) - { - return NULL; - } - - char *clean_type = sanitize_mangled_name(concrete_type); - char *mangled = xmalloc(strlen(name) + strlen(clean_type) + 2); - sprintf(mangled, "%s_%s", name, clean_type); - free(clean_type); - - if (find_func(ctx, mangled)) - { - return mangled; - } + } - ASTNode *new_fn = - copy_ast_replacing(tpl->func_node, tpl->generic_param, concrete_type, NULL, NULL); - if (!new_fn || new_fn->type != NODE_FUNCTION) - { - return NULL; - } - free(new_fn->func.name); - new_fn->func.name = xstrdup(mangled); - - register_func(ctx, mangled, new_fn->func.arg_count, new_fn->func.defaults, - new_fn->func.arg_types, new_fn->func.ret_type_info, new_fn->func.is_varargs, 0, - new_fn->token); + char *clean_type = sanitize_mangled_name(concrete_type); + char *mangled = xmalloc(strlen(name) + strlen(clean_type) + 2); + sprintf(mangled, "%s_%s", name, clean_type); + free(clean_type); - add_instantiated_func(ctx, new_fn); + if (find_func(ctx, mangled)) { return mangled; -} - -char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count) -{ - (void)ctx; // suppress unused parameter warning - char *gen = xmalloc(4096); - - strcpy(gen, "({ static char _b[1024]; _b[0]=0; char _t[128]; "); - - char *s = xstrdup(content); - char *cur = s; - - while (*cur) - { - char *brace = cur; - while (*brace && *brace != '{') - { - brace++; - } - - if (brace > cur) - { - char tmp = *brace; - *brace = 0; - strcat(gen, "strcat(_b, \""); - strcat(gen, cur); - strcat(gen, "\"); "); - *brace = tmp; - } - - if (*brace == 0) - { - break; - } - - 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) - { - colon = p; - } - if (depth == 0) - { - break; - } - p++; - } - - *p = 0; - char *expr = brace + 1; - char *fmt = NULL; - if (colon) - { - *colon = 0; - fmt = colon + 1; - } - - // Analyze usage in expression - { - Lexer lex; - lexer_init(&lex, expr); - 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); - } - } - } - } + } - if (fmt) - { - strcat(gen, "sprintf(_t, \"%"); - strcat(gen, fmt); - strcat(gen, "\", "); - strcat(gen, expr); - strcat(gen, "); strcat(_b, _t); "); - } - else - { - strcat(gen, "sprintf(_t, _z_str("); - strcat(gen, expr); - strcat(gen, "), "); - strcat(gen, expr); - strcat(gen, "); strcat(_b, _t); "); - } + ASTNode *new_fn = copy_ast_replacing(tpl->func_node, tpl->generic_param, + concrete_type, NULL, NULL); + if (!new_fn || new_fn->type != NODE_FUNCTION) { + return NULL; + } + free(new_fn->func.name); + new_fn->func.name = xstrdup(mangled); - cur = p + 1; - } + register_func(ctx, mangled, new_fn->func.arg_count, new_fn->func.defaults, + new_fn->func.arg_types, new_fn->func.ret_type_info, + new_fn->func.is_varargs, 0, new_fn->token); - strcat(gen, "_b; })"); - free(s); - return gen; + add_instantiated_func(ctx, new_fn); + return mangled; } -void register_impl(ParserContext *ctx, const char *trait, const char *strct) -{ - ImplReg *r = xmalloc(sizeof(ImplReg)); - r->trait = xstrdup(trait); - r->strct = xstrdup(strct); - r->next = ctx->registered_impls; - ctx->registered_impls = r; -} +char *process_fstring(ParserContext *ctx, const char *content, + char ***used_syms, int *count) { + (void)ctx; // suppress unused parameter warning + char *gen = xmalloc(4096); -int check_impl(ParserContext *ctx, const char *trait, const char *strct) -{ - ImplReg *r = ctx->registered_impls; - while (r) - { - if (strcmp(r->trait, trait) == 0 && strcmp(r->strct, strct) == 0) - { - return 1; - } - r = r->next; - } - return 0; -} + strcpy(gen, "({ static char _b[1024]; _b[0]=0; char _t[128]; "); -void register_template(ParserContext *ctx, const char *name, ASTNode *node) -{ - GenericTemplate *t = xmalloc(sizeof(GenericTemplate)); - t->name = xstrdup(name); - t->struct_node = node; - t->next = ctx->templates; - ctx->templates = t; -} + char *s = xstrdup(content); + char *cur = s; -ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields, const char *param, - const char *concrete) -{ - if (!fields) - { - return NULL; + while (*cur) { + char *brace = cur; + while (*brace && *brace != '{') { + brace++; } - ASTNode *n = ast_create(NODE_FIELD); - n->field.name = xstrdup(fields->field.name); - - // Replace strings - n->field.type = replace_type_str(fields->field.type, param, concrete, NULL, NULL); - // Replace formal types (Deep Copy) - n->type_info = replace_type_formal(fields->type_info, param, concrete, NULL, NULL); - - if (n->field.type && strchr(n->field.type, '_')) - { - // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry", - // "int") - char *underscore = strrchr(n->field.type, '_'); - if (underscore && underscore > n->field.type) - { - // Remove trailing '*' if present - char *type_copy = xstrdup(n->field.type); - char *star = strchr(type_copy, '*'); - if (star) - { - *star = '\0'; - } - - underscore = strrchr(type_copy, '_'); - if (underscore) - { - *underscore = '\0'; - char *template_name = type_copy; - char *concrete_arg = underscore + 1; - - // Check if this is actually a known generic template - GenericTemplate *gt = ctx->templates; - int found = 0; - while (gt) - { - if (strcmp(gt->name, template_name) == 0) - { - found = 1; - break; - } - gt = gt->next; - } - - if (found) - { - instantiate_generic(ctx, template_name, concrete_arg, fields->token); - } - } - free(type_copy); - } + if (brace > cur) { + char tmp = *brace; + *brace = 0; + strcat(gen, "strcat(_b, \""); + strcat(gen, cur); + strcat(gen, "\"); "); + *brace = tmp; } - n->next = copy_fields_replacing(ctx, fields->next, param, concrete); - return n; -} - -void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, - const char *mangled_struct_name, const char *arg) -{ - if (check_impl(ctx, "Methods", mangled_struct_name)) - { - return; // Simple dedupe check + if (*brace == 0) { + break; } - ASTNode *backup_next = it->impl_node->next; - it->impl_node->next = NULL; // Break link to isolate node - ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg, it->struct_name, - mangled_struct_name); - it->impl_node->next = backup_next; // Restore - - new_impl->impl.struct_name = xstrdup(mangled_struct_name); - ASTNode *meth = new_impl->impl.methods; - while (meth) - { - char *suffix = strchr(meth->func.name, '_'); - if (suffix) - { - char *new_name = xmalloc(strlen(mangled_struct_name) + strlen(suffix) + 1); - sprintf(new_name, "%s%s", mangled_struct_name, suffix); - free(meth->func.name); - meth->func.name = new_name; - register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults, - meth->func.arg_types, meth->func.ret_type_info, meth->func.is_varargs, 0, - meth->token); - } - - // Handle generic return types in methods (e.g., Option<T> -> Option_int) - if (meth->func.ret_type && strchr(meth->func.ret_type, '_')) - { - char *ret_copy = xstrdup(meth->func.ret_type); - char *underscore = strrchr(ret_copy, '_'); - if (underscore && underscore > ret_copy) - { - *underscore = '\0'; - char *template_name = ret_copy; - - // Check if this looks like a generic (e.g., "Option_V" or "Result_V") - GenericTemplate *gt = ctx->templates; - while (gt) - { - if (strcmp(gt->name, template_name) == 0) - { - // Found matching template, instantiate it - instantiate_generic(ctx, template_name, arg, meth->token); - break; - } - gt = gt->next; - } - } - free(ret_copy); - } - - meth = meth->next; - } - add_instantiated_func(ctx, new_impl); -} + char *p = brace + 1; + char *colon = NULL; + int depth = 1; -void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, Token token) -{ - // Ignore generic placeholders - if (strlen(arg) == 1 && isupper(arg[0])) - { - return; - } - if (strcmp(arg, "T") == 0) - { - return; - } - - char *clean_arg = sanitize_mangled_name(arg); - char m[256]; - sprintf(m, "%s_%s", tpl, clean_arg); - free(clean_arg); - - Instantiation *c = ctx->instantiations; - while (c) - { - if (strcmp(c->name, m) == 0) - { - return; // Already instantiated, DO NOTHING. - } - c = c->next; + while (*p && depth > 0) { + if (*p == '{') { + depth++; + } + if (*p == '}') { + depth--; + } + if (depth == 1 && *p == ':' && !colon) { + colon = p; + } + if (depth == 0) { + break; + } + p++; } - GenericTemplate *t = ctx->templates; - while (t) - { - if (strcmp(t->name, tpl) == 0) - { + *p = 0; + char *expr = brace + 1; + char *fmt = NULL; + if (colon) { + *colon = 0; + fmt = colon + 1; + } + + // Analyze usage in expression + { + Lexer lex; + lexer_init(&lex, expr); + 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); + } + } + } + } + + if (fmt) { + strcat(gen, "sprintf(_t, \"%"); + strcat(gen, fmt); + strcat(gen, "\", "); + strcat(gen, expr); + strcat(gen, "); strcat(_b, _t); "); + } else { + strcat(gen, "sprintf(_t, _z_str("); + strcat(gen, expr); + strcat(gen, "), "); + strcat(gen, expr); + strcat(gen, "); strcat(_b, _t); "); + } + + cur = p + 1; + } + + strcat(gen, "_b; })"); + free(s); + return gen; +} + +void register_impl(ParserContext *ctx, const char *trait, const char *strct) { + ImplReg *r = xmalloc(sizeof(ImplReg)); + r->trait = xstrdup(trait); + r->strct = xstrdup(strct); + r->next = ctx->registered_impls; + ctx->registered_impls = r; +} + +int check_impl(ParserContext *ctx, const char *trait, const char *strct) { + ImplReg *r = ctx->registered_impls; + while (r) { + if (strcmp(r->trait, trait) == 0 && strcmp(r->strct, strct) == 0) { + return 1; + } + r = r->next; + } + return 0; +} + +void register_template(ParserContext *ctx, const char *name, ASTNode *node) { + GenericTemplate *t = xmalloc(sizeof(GenericTemplate)); + t->name = xstrdup(name); + t->struct_node = node; + t->next = ctx->templates; + ctx->templates = t; +} + +ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields, + const char *param, const char *concrete) { + if (!fields) { + return NULL; + } + ASTNode *n = ast_create(NODE_FIELD); + n->field.name = xstrdup(fields->field.name); + + // Replace strings + n->field.type = + replace_type_str(fields->field.type, param, concrete, NULL, NULL); + + // Replace formal types (Deep Copy) + n->type_info = + replace_type_formal(fields->type_info, param, concrete, NULL, NULL); + + if (n->field.type && strchr(n->field.type, '_')) { + // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry", + // "int") + char *underscore = strrchr(n->field.type, '_'); + if (underscore && underscore > n->field.type) { + // Remove trailing '*' if present + char *type_copy = xstrdup(n->field.type); + char *star = strchr(type_copy, '*'); + if (star) { + *star = '\0'; + } + + underscore = strrchr(type_copy, '_'); + if (underscore) { + *underscore = '\0'; + char *template_name = type_copy; + char *concrete_arg = underscore + 1; + + // Check if this is actually a known generic template + GenericTemplate *gt = ctx->templates; + int found = 0; + while (gt) { + if (strcmp(gt->name, template_name) == 0) { + found = 1; break; + } + gt = gt->next; } - t = t->next; - } - if (!t) - { - zpanic_at(token, "Unknown generic: %s", tpl); - } - - Instantiation *ni = xmalloc(sizeof(Instantiation)); - ni->name = xstrdup(m); - ni->template_name = xstrdup(tpl); - ni->concrete_arg = xstrdup(arg); - ni->struct_node = NULL; // Placeholder to break cycles - ni->next = ctx->instantiations; - ctx->instantiations = ni; - ASTNode *struct_node_copy = NULL; - - if (t->struct_node->type == NODE_STRUCT) - { - ASTNode *i = ast_create(NODE_STRUCT); - i->strct.name = xstrdup(m); - i->strct.is_template = 0; - i->strct.fields = copy_fields_replacing(ctx, t->struct_node->strct.fields, - t->struct_node->strct.generic_param, arg); - struct_node_copy = i; - register_struct_def(ctx, m, i); - } - else if (t->struct_node->type == NODE_ENUM) - { - ASTNode *i = ast_create(NODE_ENUM); - i->enm.name = xstrdup(m); - i->enm.is_template = 0; - ASTNode *h = 0, *tl = 0; - ASTNode *v = t->struct_node->enm.variants; - while (v) - { - ASTNode *nv = ast_create(NODE_ENUM_VARIANT); - nv->variant.name = xstrdup(v->variant.name); - nv->variant.tag_id = v->variant.tag_id; - nv->variant.payload = replace_type_formal( - v->variant.payload, t->struct_node->enm.generic_param, arg, NULL, NULL); - char mangled_var[512]; - sprintf(mangled_var, "%s_%s", m, nv->variant.name); - register_enum_variant(ctx, m, mangled_var, nv->variant.tag_id); - if (!h) - { - h = nv; - } - else - { - tl->next = nv; - } - tl = nv; - v = v->next; + if (found) { + instantiate_generic(ctx, template_name, concrete_arg, fields->token); } - i->enm.variants = h; - struct_node_copy = i; - } - - ni->struct_node = struct_node_copy; - - if (struct_node_copy) - { - struct_node_copy->next = ctx->instantiated_structs; - ctx->instantiated_structs = struct_node_copy; + } + free(type_copy); } + } - GenericImplTemplate *it = ctx->impl_templates; - while (it) - { - if (strcmp(it->struct_name, tpl) == 0) - { - instantiate_methods(ctx, it, m, arg); - } - it = it->next; - } + n->next = copy_fields_replacing(ctx, fields->next, param, concrete); + return n; } -int is_file_imported(ParserContext *ctx, const char *p) -{ - ImportedFile *c = ctx->imported_files; - while (c) - { - if (strcmp(c->path, p) == 0) - { - return 1; - } - c = c->next; +void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, + const char *mangled_struct_name, const char *arg) { + if (check_impl(ctx, "Methods", mangled_struct_name)) { + return; // Simple dedupe check + } + + ASTNode *backup_next = it->impl_node->next; + it->impl_node->next = NULL; // Break link to isolate node + ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg, + it->struct_name, mangled_struct_name); + it->impl_node->next = backup_next; // Restore + + new_impl->impl.struct_name = xstrdup(mangled_struct_name); + ASTNode *meth = new_impl->impl.methods; + while (meth) { + char *suffix = strchr(meth->func.name, '_'); + if (suffix) { + char *new_name = + xmalloc(strlen(mangled_struct_name) + strlen(suffix) + 1); + sprintf(new_name, "%s%s", mangled_struct_name, suffix); + free(meth->func.name); + meth->func.name = new_name; + register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults, + meth->func.arg_types, meth->func.ret_type_info, + meth->func.is_varargs, 0, meth->token); + } + + // Handle generic return types in methods (e.g., Option<T> -> Option_int) + if (meth->func.ret_type && strchr(meth->func.ret_type, '_')) { + char *ret_copy = xstrdup(meth->func.ret_type); + char *underscore = strrchr(ret_copy, '_'); + if (underscore && underscore > ret_copy) { + *underscore = '\0'; + char *template_name = ret_copy; + + // Check if this looks like a generic (e.g., "Option_V" or "Result_V") + GenericTemplate *gt = ctx->templates; + while (gt) { + if (strcmp(gt->name, template_name) == 0) { + // Found matching template, instantiate it + instantiate_generic(ctx, template_name, arg, meth->token); + break; + } + gt = gt->next; + } + } + free(ret_copy); + } + + meth = meth->next; + } + add_instantiated_func(ctx, new_impl); +} + +void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, + Token token) { + // Ignore generic placeholders + if (strlen(arg) == 1 && isupper(arg[0])) { + return; + } + if (strcmp(arg, "T") == 0) { + return; + } + + char *clean_arg = sanitize_mangled_name(arg); + char m[256]; + sprintf(m, "%s_%s", tpl, clean_arg); + free(clean_arg); + + Instantiation *c = ctx->instantiations; + while (c) { + if (strcmp(c->name, m) == 0) { + return; // Already instantiated, DO NOTHING. + } + c = c->next; + } + + GenericTemplate *t = ctx->templates; + while (t) { + if (strcmp(t->name, tpl) == 0) { + break; + } + t = t->next; + } + if (!t) { + zpanic_at(token, "Unknown generic: %s", tpl); + } + + Instantiation *ni = xmalloc(sizeof(Instantiation)); + ni->name = xstrdup(m); + ni->template_name = xstrdup(tpl); + ni->concrete_arg = xstrdup(arg); + ni->struct_node = NULL; // Placeholder to break cycles + ni->next = ctx->instantiations; + ctx->instantiations = ni; + + ASTNode *struct_node_copy = NULL; + + if (t->struct_node->type == NODE_STRUCT) { + ASTNode *i = ast_create(NODE_STRUCT); + i->strct.name = xstrdup(m); + i->strct.is_template = 0; + i->strct.fields = + copy_fields_replacing(ctx, t->struct_node->strct.fields, + t->struct_node->strct.generic_param, arg); + struct_node_copy = i; + register_struct_def(ctx, m, i); + } else if (t->struct_node->type == NODE_ENUM) { + ASTNode *i = ast_create(NODE_ENUM); + i->enm.name = xstrdup(m); + i->enm.is_template = 0; + ASTNode *h = 0, *tl = 0; + ASTNode *v = t->struct_node->enm.variants; + while (v) { + ASTNode *nv = ast_create(NODE_ENUM_VARIANT); + nv->variant.name = xstrdup(v->variant.name); + nv->variant.tag_id = v->variant.tag_id; + nv->variant.payload = replace_type_formal( + v->variant.payload, t->struct_node->enm.generic_param, arg, NULL, + NULL); + char mangled_var[512]; + sprintf(mangled_var, "%s_%s", m, nv->variant.name); + register_enum_variant(ctx, m, mangled_var, nv->variant.tag_id); + if (!h) { + h = nv; + } else { + tl->next = nv; + } + tl = nv; + v = v->next; + } + i->enm.variants = h; + struct_node_copy = i; + } + + ni->struct_node = struct_node_copy; + + if (struct_node_copy) { + struct_node_copy->next = ctx->instantiated_structs; + ctx->instantiated_structs = struct_node_copy; + } + + GenericImplTemplate *it = ctx->impl_templates; + while (it) { + if (strcmp(it->struct_name, tpl) == 0) { + instantiate_methods(ctx, it, m, arg); + } + it = it->next; + } +} + +int is_file_imported(ParserContext *ctx, const char *p) { + ImportedFile *c = ctx->imported_files; + while (c) { + if (strcmp(c->path, p) == 0) { + return 1; + } + c = c->next; + } + return 0; +} + +void mark_file_imported(ParserContext *ctx, const char *p) { + ImportedFile *f = xmalloc(sizeof(ImportedFile)); + f->path = xstrdup(p); + f->next = ctx->imported_files; + ctx->imported_files = f; +} + +char *parse_condition_raw(ParserContext *ctx, Lexer *l) { + (void)ctx; // suppress unused parameter warning + Token t = lexer_peek(l); + if (t.type == TOK_LPAREN) { + Token op = lexer_next(l); + const char *s = op.start; + int d = 1; + while (d > 0) { + t = lexer_next(l); + if (t.type == TOK_EOF) { + zpanic_at(t, "Unterminated condition"); + } + if (t.type == TOK_LPAREN) { + d++; + } + if (t.type == TOK_RPAREN) { + d--; + } + } + const char *cs = s + 1; + int len = t.start - cs; + char *c = xmalloc(len + 1); + strncpy(c, cs, len); + c[len] = 0; + return c; + } else { + const char *start = l->src + l->pos; + while (1) { + t = lexer_peek(l); + if (t.type == TOK_LBRACE || t.type == TOK_EOF) { + break; + } + lexer_next(l); } - return 0; -} - -void mark_file_imported(ParserContext *ctx, const char *p) -{ - ImportedFile *f = xmalloc(sizeof(ImportedFile)); - f->path = xstrdup(p); - f->next = ctx->imported_files; - ctx->imported_files = f; -} - -char *parse_condition_raw(ParserContext *ctx, Lexer *l) -{ - (void)ctx; // suppress unused parameter warning - Token t = lexer_peek(l); - if (t.type == TOK_LPAREN) - { - Token op = lexer_next(l); - const char *s = op.start; - int d = 1; - while (d > 0) - { - t = lexer_next(l); - if (t.type == TOK_EOF) - { - zpanic_at(t, "Unterminated condition"); - } - if (t.type == TOK_LPAREN) - { - d++; - } - if (t.type == TOK_RPAREN) - { - d--; - } - } - const char *cs = s + 1; - int len = t.start - cs; - char *c = xmalloc(len + 1); - strncpy(c, cs, len); - c[len] = 0; - return c; - } - else - { - const char *start = l->src + l->pos; - while (1) - { - t = lexer_peek(l); - if (t.type == TOK_LBRACE || t.type == TOK_EOF) - { - break; - } - lexer_next(l); - } - int len = (l->src + l->pos) - start; - if (len == 0) - { - zpanic_at(lexer_peek(l), "Empty condition or missing body"); - } - char *c = xmalloc(len + 1); - strncpy(c, start, len); - c[len] = 0; - return c; + int len = (l->src + l->pos) - start; + if (len == 0) { + zpanic_at(lexer_peek(l), "Empty condition or missing body"); } + char *c = xmalloc(len + 1); + strncpy(c, start, len); + c[len] = 0; + return c; + } } -char *rewrite_expr_methods(ParserContext *ctx, char *raw) -{ - if (!raw) - { - return NULL; - } - - int in_expr = 0; - char *result = xmalloc(strlen(raw) * 4 + 100); - char *dest = result; - char *src = raw; - - while (*src) - { - if (strncmp(src, "#{", 2) == 0) - { - in_expr = 1; - src += 2; - *dest++ = '('; - continue; - } - - if (in_expr && *src == '}') - { - in_expr = 0; - *dest++ = ')'; +char *rewrite_expr_methods(ParserContext *ctx, char *raw) { + if (!raw) { + return NULL; + } + + int in_expr = 0; + char *result = xmalloc(strlen(raw) * 4 + 100); + char *dest = result; + char *src = raw; + + while (*src) { + if (strncmp(src, "#{", 2) == 0) { + in_expr = 1; + src += 2; + *dest++ = '('; + continue; + } + + if (in_expr && *src == '}') { + in_expr = 0; + *dest++ = ')'; + src++; + continue; + } + + if (in_expr && *src == '.') { + char acc[64]; + int i = 0; + char *back = src - 1; + while (back >= raw && (isalnum(*back) || *back == '_')) { + back--; + } + back++; + while (back < src && i < 63) { + acc[i++] = *back++; + } + acc[i] = 0; + + char *vtype = find_symbol_type(ctx, acc); + if (!vtype) { + *dest++ = *src++; + continue; + } + + char method[64]; + i = 0; + src++; + while (isalnum(*src) || *src == '_') { + method[i++] = *src++; + } + method[i] = 0; + + // Check for field access + char *base_t = xstrdup(vtype); + char *pc = strchr(base_t, '*'); + int is_ptr_type = (pc != NULL); + if (pc) { + *pc = 0; + } + + ASTNode *def = find_struct_def(ctx, base_t); + int is_field = 0; + if (def && (def->type == NODE_STRUCT)) { + ASTNode *f = def->strct.fields; + while (f) { + if (strcmp(f->field.name, method) == 0) { + is_field = 1; + break; + } + f = f->next; + } + } + free(base_t); + + if (is_field) { + dest -= strlen(acc); + if (is_ptr_type) { + dest += sprintf(dest, "(%s)->%s", acc, method); + } else { + dest += sprintf(dest, "(%s).%s", acc, method); + } + continue; + } + + if (*src == '(') { + dest -= strlen(acc); + int paren_depth = 0; + src++; + paren_depth++; + + char ptr_check[64]; + strcpy(ptr_check, vtype); + int is_ptr = (strchr(ptr_check, '*') != NULL); + if (is_ptr) { + char *p = strchr(ptr_check, '*'); + if (p) { + *p = 0; + } + } + + dest += sprintf(dest, "%s_%s(%s%s", ptr_check, method, + is_ptr ? "" : "&", acc); + + int has_args = 0; + while (*src && paren_depth > 0) { + if (!isspace(*src)) { + has_args = 1; + } + if (*src == '(') { + paren_depth++; + } + if (*src == ')') { + paren_depth--; + } + if (paren_depth == 0) { + break; + } + *dest++ = *src++; + } + + if (has_args) { + *dest++ = ')'; + } else { + *dest++ = ')'; + } + + src++; + continue; + } else { + dest -= strlen(acc); + char ptr_check[64]; + strcpy(ptr_check, vtype); + int is_ptr = (strchr(ptr_check, '*') != NULL); + if (is_ptr) { + char *p = strchr(ptr_check, '*'); + if (p) { + *p = 0; + } + } + dest += sprintf(dest, "%s_%s(%s%s)", ptr_check, method, + is_ptr ? "" : "&", acc); + continue; + } + } + + if (!in_expr && strncmp(src, "::", 2) == 0) { + char acc[64]; + int i = 0; + char *back = src - 1; + while (back >= raw && (isalnum(*back) || *back == '_')) { + back--; + } + back++; + while (back < src && i < 63) { + acc[i++] = *back++; + } + acc[i] = 0; + + src += 2; + char field[64]; + i = 0; + while (isalnum(*src) || *src == '_') { + field[i++] = *src++; + } + field[i] = 0; + + dest -= strlen(acc); + + Module *mod = find_module(ctx, acc); + if (mod && mod->is_c_header) { + dest += sprintf(dest, "%s", field); + } else { + dest += sprintf(dest, "%s_%s", acc, field); + } + continue; + } + + if (in_expr && isalpha(*src)) { + char tok[128]; + int i = 0; + while ((isalnum(*src) || *src == '_') && i < 127) { + tok[i++] = *src++; + } + tok[i] = 0; + + while (*src == ' ' || *src == '\t') { + src++; + } + + if (strncmp(src, "::", 2) == 0) { + src += 2; + char func_name[128]; + snprintf(func_name, sizeof(func_name), "%s", tok); + char method[64]; + i = 0; + while (isalnum(*src) || *src == '_') { + method[i++] = *src++; + } + method[i] = 0; + + while (*src == ' ' || *src == '\t') { + src++; + } + + if (*src == '(') { + src++; + + char mangled[256]; + snprintf(mangled, sizeof(mangled), "%s_%s", func_name, method); + + if (*src == ')') { + dest += sprintf(dest, "%s()", mangled); src++; - continue; - } - - if (in_expr && *src == '.') - { - char acc[64]; - int i = 0; - char *back = src - 1; - while (back >= raw && (isalnum(*back) || *back == '_')) - { - back--; - } - back++; - while (back < src && i < 63) - { - acc[i++] = *back++; - } - acc[i] = 0; - - char *vtype = find_symbol_type(ctx, acc); - if (!vtype) - { + } else { + FuncSig *sig = find_func(ctx, func_name); + if (sig) { + dest += sprintf(dest, "%s(&(%s){0}", mangled, func_name); + while (*src && *src != ')') { *dest++ = *src++; - continue; - } - - char method[64]; - i = 0; - src++; - while (isalnum(*src) || *src == '_') - { - method[i++] = *src++; - } - method[i] = 0; - - // Check for field access - char *base_t = xstrdup(vtype); - char *pc = strchr(base_t, '*'); - int is_ptr_type = (pc != NULL); - if (pc) - { - *pc = 0; - } - - ASTNode *def = find_struct_def(ctx, base_t); - int is_field = 0; - if (def && (def->type == NODE_STRUCT)) - { - ASTNode *f = def->strct.fields; - while (f) - { - if (strcmp(f->field.name, method) == 0) - { - is_field = 1; - break; - } - f = f->next; - } - } - free(base_t); - - if (is_field) - { - dest -= strlen(acc); - if (is_ptr_type) - { - dest += sprintf(dest, "(%s)->%s", acc, method); - } - else - { - dest += sprintf(dest, "(%s).%s", acc, method); - } - continue; - } - - if (*src == '(') - { - dest -= strlen(acc); - int paren_depth = 0; + } + *dest++ = ')'; + if (*src == ')') { src++; - paren_depth++; - - char ptr_check[64]; - strcpy(ptr_check, vtype); - int is_ptr = (strchr(ptr_check, '*') != NULL); - if (is_ptr) - { - char *p = strchr(ptr_check, '*'); - if (p) - { - *p = 0; - } - } - - dest += sprintf(dest, "%s_%s(%s%s", ptr_check, method, is_ptr ? "" : "&", acc); - - int has_args = 0; - while (*src && paren_depth > 0) - { - if (!isspace(*src)) - { - has_args = 1; - } - if (*src == '(') - { - paren_depth++; - } - if (*src == ')') - { - paren_depth--; - } - if (paren_depth == 0) - { - break; - } - *dest++ = *src++; - } - - if (has_args) - { - *dest++ = ')'; - } - else - { - *dest++ = ')'; - } - - src++; - continue; - } - else - { - dest -= strlen(acc); - char ptr_check[64]; - strcpy(ptr_check, vtype); - int is_ptr = (strchr(ptr_check, '*') != NULL); - if (is_ptr) - { - char *p = strchr(ptr_check, '*'); - if (p) - { - *p = 0; - } - } - dest += sprintf(dest, "%s_%s(%s%s)", ptr_check, method, is_ptr ? "" : "&", acc); - continue; - } - } - - if (!in_expr && strncmp(src, "::", 2) == 0) - { - char acc[64]; - int i = 0; - char *back = src - 1; - while (back >= raw && (isalnum(*back) || *back == '_')) - { - back--; - } - back++; - while (back < src && i < 63) - { - acc[i++] = *back++; - } - acc[i] = 0; - - src += 2; - char field[64]; - i = 0; - while (isalnum(*src) || *src == '_') - { - field[i++] = *src++; - } - field[i] = 0; - - dest -= strlen(acc); - - Module *mod = find_module(ctx, acc); - if (mod && mod->is_c_header) - { - dest += sprintf(dest, "%s", field); - } - else - { - dest += sprintf(dest, "%s_%s", acc, field); - } - continue; - } - - if (in_expr && isalpha(*src)) - { - char tok[128]; - int i = 0; - while ((isalnum(*src) || *src == '_') && i < 127) - { - tok[i++] = *src++; - } - tok[i] = 0; - - while (*src == ' ' || *src == '\t') - { + } + } else { + dest += sprintf(dest, "%s(", mangled); + while (*src && *src != ')') { + *dest++ = *src++; + } + *dest++ = ')'; + if (*src == ')') { src++; + } } - - if (strncmp(src, "::", 2) == 0) - { - src += 2; - char func_name[128]; - snprintf(func_name, sizeof(func_name), "%s", tok); - char method[64]; - i = 0; - while (isalnum(*src) || *src == '_') - { - method[i++] = *src++; - } - method[i] = 0; - - while (*src == ' ' || *src == '\t') - { - src++; - } - - if (*src == '(') - { - src++; - - char mangled[256]; - snprintf(mangled, sizeof(mangled), "%s_%s", func_name, method); - - if (*src == ')') - { - dest += sprintf(dest, "%s()", mangled); - src++; - } - else - { - FuncSig *sig = find_func(ctx, func_name); - if (sig) - { - dest += sprintf(dest, "%s(&(%s){0}", mangled, func_name); - while (*src && *src != ')') - { - *dest++ = *src++; - } - *dest++ = ')'; - if (*src == ')') - { - src++; - } - } - else - { - dest += sprintf(dest, "%s(", mangled); - while (*src && *src != ')') - { - *dest++ = *src++; - } - *dest++ = ')'; - if (*src == ')') - { - src++; - } - } - } - continue; - } - } - - strcpy(dest, tok); - dest += strlen(tok); - continue; - } - - *dest++ = *src++; - } - - *dest = 0; - return result; -} - -char *consume_and_rewrite(ParserContext *ctx, Lexer *l) -{ - char *r = consume_until_semicolon(l); - char *rw = rewrite_expr_methods(ctx, r); - free(r); - return rw; -} - -char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out) -{ - Token t = lexer_next(l); - if (t.type != TOK_LPAREN) - { - zpanic_at(t, "Expected '(' in function args"); - } - - char *buf = xmalloc(1024); - buf[0] = 0; - int count = 0; - char **defaults = xmalloc(sizeof(char *) * 16); - Type **types = xmalloc(sizeof(Type *) * 16); - char **names = xmalloc(sizeof(char *) * 16); - - for (int i = 0; i < 16; i++) - { - defaults[i] = NULL; - types[i] = NULL; - names[i] = NULL; - } - - if (lexer_peek(l).type != TOK_RPAREN) - { - while (1) - { - Token t = lexer_next(l); - // Handle 'self' - if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && t.len == 4) - { - names[count] = xstrdup("self"); - if (ctx->current_impl_struct) - { - Type *st = NULL; - // Check for primitives to avoid creating struct int* - if (strcmp(ctx->current_impl_struct, "int") == 0) - { - st = type_new(TYPE_INT); - } - else if (strcmp(ctx->current_impl_struct, "float") == 0) - { - st = type_new(TYPE_F32); - } - else if (strcmp(ctx->current_impl_struct, "char") == 0) - { - st = type_new(TYPE_CHAR); - } - else if (strcmp(ctx->current_impl_struct, "bool") == 0) - { - st = type_new(TYPE_BOOL); - } - else if (strcmp(ctx->current_impl_struct, "string") == 0) - { - st = type_new(TYPE_STRING); - } - // Add other primitives as needed - else - { - st = type_new(TYPE_STRUCT); - st->name = xstrdup(ctx->current_impl_struct); - } - Type *pt = type_new_ptr(st); - - char buf_type[256]; - sprintf(buf_type, "%s*", ctx->current_impl_struct); - // Register 'self' with actual type in symbol table - add_symbol(ctx, "self", buf_type, pt); - - types[count] = pt; - - strcat(buf, "void* self"); - } - else - { - strcat(buf, "void* self"); - types[count] = type_new_ptr(type_new(TYPE_VOID)); - add_symbol(ctx, "self", "void*", types[count]); - } - count++; - } - else - { - if (t.type != TOK_IDENT) - { - zpanic_at(lexer_peek(l), "Expected arg name"); - } - char *name = token_strdup(t); - names[count] = name; // Store name - if (lexer_next(l).type != TOK_COLON) - { - zpanic_at(lexer_peek(l), "Expected ':'"); - } - - Type *arg_type = parse_type_formal(ctx, l); - char *type_str = type_to_string(arg_type); - - add_symbol(ctx, name, type_str, arg_type); - types[count] = arg_type; - - if (strlen(buf) > 0) - { - strcat(buf, ", "); - } - - char *fn_ptr = strstr(type_str, "(*)"); - if (arg_type->kind == TYPE_FUNCTION) - { - strcat(buf, "z_closure_T "); - strcat(buf, name); - } - else if (fn_ptr) - { - // Inject name into function pointer: int (*)(int) -> int (*name)(int) - int prefix_len = fn_ptr - type_str; - strncat(buf, type_str, prefix_len); - strcat(buf, " (*"); - strcat(buf, name); - strcat(buf, ")"); - strcat(buf, fn_ptr + 3); // Skip "(*)" - } - else - { - strcat(buf, type_str); - strcat(buf, " "); - strcat(buf, name); - } - - count++; - - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) - { - lexer_next(l); - Token val = lexer_next(l); - defaults[count - 1] = token_strdup(val); - } - } - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - // Check if next is ... - if (lexer_peek(l).type == TOK_ELLIPSIS) - { - lexer_next(l); - if (is_varargs_out) - { - *is_varargs_out = 1; - } - if (strlen(buf) > 0) - { - strcat(buf, ", "); - } - strcat(buf, "..."); - break; // Must be last - } - } - else - { - break; - } - } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ')' after args"); + } + continue; + } + } + + strcpy(dest, tok); + dest += strlen(tok); + continue; + } + + *dest++ = *src++; + } + + *dest = 0; + return result; +} + +char *consume_and_rewrite(ParserContext *ctx, Lexer *l) { + char *r = consume_until_semicolon(l); + char *rw = rewrite_expr_methods(ctx, r); + free(r); + return rw; +} + +char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, + int *count_out, Type ***types_out, + char ***names_out, int *is_varargs_out) { + Token t = lexer_next(l); + if (t.type != TOK_LPAREN) { + zpanic_at(t, "Expected '(' in function args"); + } + + char *buf = xmalloc(1024); + buf[0] = 0; + int count = 0; + char **defaults = xmalloc(sizeof(char *) * 16); + Type **types = xmalloc(sizeof(Type *) * 16); + char **names = xmalloc(sizeof(char *) * 16); + + for (int i = 0; i < 16; i++) { + defaults[i] = NULL; + types[i] = NULL; + names[i] = NULL; + } + + if (lexer_peek(l).type != TOK_RPAREN) { + while (1) { + Token t = lexer_next(l); + // Handle 'self' + if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && + t.len == 4) { + names[count] = xstrdup("self"); + if (ctx->current_impl_struct) { + Type *st = NULL; + // Check for primitives to avoid creating struct int* + if (strcmp(ctx->current_impl_struct, "int") == 0) { + st = type_new(TYPE_INT); + } else if (strcmp(ctx->current_impl_struct, "float") == 0) { + st = type_new(TYPE_F32); + } else if (strcmp(ctx->current_impl_struct, "char") == 0) { + st = type_new(TYPE_CHAR); + } else if (strcmp(ctx->current_impl_struct, "bool") == 0) { + st = type_new(TYPE_BOOL); + } else if (strcmp(ctx->current_impl_struct, "string") == 0) { + st = type_new(TYPE_STRING); + } + // Add other primitives as needed + else { + st = type_new(TYPE_STRUCT); + st->name = xstrdup(ctx->current_impl_struct); + } + Type *pt = type_new_ptr(st); + + char buf_type[256]; + sprintf(buf_type, "%s*", ctx->current_impl_struct); + // Register 'self' with actual type in symbol table + add_symbol(ctx, "self", buf_type, pt); + + types[count] = pt; + + strcat(buf, "void* self"); + } else { + strcat(buf, "void* self"); + types[count] = type_new_ptr(type_new(TYPE_VOID)); + add_symbol(ctx, "self", "void*", types[count]); + } + count++; + } else { + if (t.type != TOK_IDENT) { + zpanic_at(lexer_peek(l), "Expected arg name"); + } + char *name = token_strdup(t); + names[count] = name; // Store name + if (lexer_next(l).type != TOK_COLON) { + zpanic_at(lexer_peek(l), "Expected ':'"); + } + + Type *arg_type = parse_type_formal(ctx, l); + char *type_str = type_to_string(arg_type); + + add_symbol(ctx, name, type_str, arg_type); + types[count] = arg_type; + + if (strlen(buf) > 0) { + strcat(buf, ", "); + } + + char *fn_ptr = strstr(type_str, "(*)"); + if (arg_type->kind == TYPE_FUNCTION) { + strcat(buf, "z_closure_T "); + strcat(buf, name); + } else if (fn_ptr) { + // Inject name into function pointer: int (*)(int) -> int (*name)(int) + int prefix_len = fn_ptr - type_str; + strncat(buf, type_str, prefix_len); + strcat(buf, " (*"); + strcat(buf, name); + strcat(buf, ")"); + strcat(buf, fn_ptr + 3); // Skip "(*)" + } else { + strcat(buf, type_str); + strcat(buf, " "); + strcat(buf, name); + } + + count++; + + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) { + lexer_next(l); + Token val = lexer_next(l); + defaults[count - 1] = token_strdup(val); + } + } + if (lexer_peek(l).type == TOK_COMMA) { + lexer_next(l); + // Check if next is ... + if (lexer_peek(l).type == TOK_ELLIPSIS) { + lexer_next(l); + if (is_varargs_out) { + *is_varargs_out = 1; + } + if (strlen(buf) > 0) { + strcat(buf, ", "); + } + strcat(buf, "..."); + break; // Must be last + } + } else { + break; + } } + } + if (lexer_next(l).type != TOK_RPAREN) { + zpanic_at(lexer_peek(l), "Expected ')' after args"); + } - *defaults_out = defaults; - *count_out = count; - *types_out = types; - *names_out = names; - return buf; + *defaults_out = defaults; + *count_out = count; + *types_out = types; + *names_out = names; + return buf; } // Helper to find similar symbol name in current scope -char *find_similar_symbol(ParserContext *ctx, const char *name) -{ - if (!ctx->current_scope) - { - return NULL; - } - - const char *best_match = NULL; - int best_dist = 999; - - // Check local scopes - Scope *s = ctx->current_scope; - while (s) - { - Symbol *sym = s->symbols; - while (sym) - { - int dist = levenshtein(name, sym->name); - if (dist < best_dist && dist <= 3) - { - best_dist = dist; - best_match = sym->name; - } - sym = sym->next; - } - s = s->parent; - } - - // Check builtins/globals if any (simplified) - return best_match ? xstrdup(best_match) : NULL; -} - -void register_plugin(ParserContext *ctx, const char *name, const char *alias) -{ - // Try to find existing (built-in) or already loaded plugin - ZPlugin *plugin = zptr_find_plugin(name); - - // If not found, try to load it dynamically - if (!plugin) - { - plugin = zptr_load_plugin(name); - - if (!plugin) - { - char path[1024]; - snprintf(path, sizeof(path), "%s.so", name); - plugin = zptr_load_plugin(path); - } - - if (!plugin && !strchr(name, '/')) - { - char path[1024]; - snprintf(path, sizeof(path), "./%s.so", name); - plugin = zptr_load_plugin(path); - } - } - - if (!plugin) - { - fprintf(stderr, - COLOR_RED "Error:" COLOR_RESET " Could not load plugin '%s'\n" - " Tried built-ins and dynamic loading (.so)\n", - name); - exit(1); - } - - ImportedPlugin *p = xmalloc(sizeof(ImportedPlugin)); - p->name = xstrdup(plugin->name); // Use the plugin's internal name - p->alias = alias ? xstrdup(alias) : NULL; - p->next = ctx->imported_plugins; - ctx->imported_plugins = p; -} - -const char *resolve_plugin(ParserContext *ctx, const char *name_or_alias) -{ - for (ImportedPlugin *p = ctx->imported_plugins; p; p = p->next) - { - // Check if it matches the alias - if (p->alias && strcmp(p->alias, name_or_alias) == 0) - { - return p->name; - } - // Check if it matches the name - if (strcmp(p->name, name_or_alias) == 0) - { - return p->name; - } - } - return NULL; // Plugin not found +char *find_similar_symbol(ParserContext *ctx, const char *name) { + if (!ctx->current_scope) { + return NULL; + } + + const char *best_match = NULL; + int best_dist = 999; + + // Check local scopes + Scope *s = ctx->current_scope; + while (s) { + Symbol *sym = s->symbols; + while (sym) { + int dist = levenshtein(name, sym->name); + if (dist < best_dist && dist <= 3) { + best_dist = dist; + best_match = sym->name; + } + sym = sym->next; + } + s = s->parent; + } + + // Check builtins/globals if any (simplified) + return best_match ? xstrdup(best_match) : NULL; +} + +void register_plugin(ParserContext *ctx, const char *name, const char *alias) { + // Try to find existing (built-in) or already loaded plugin + ZPlugin *plugin = zptr_find_plugin(name); + + // If not found, try to load it dynamically + if (!plugin) { + plugin = zptr_load_plugin(name); + + if (!plugin) { + char path[1024]; + snprintf(path, sizeof(path), "%s.so", name); + plugin = zptr_load_plugin(path); + } + + if (!plugin && !strchr(name, '/')) { + char path[1024]; + snprintf(path, sizeof(path), "./%s.so", name); + plugin = zptr_load_plugin(path); + } + } + + if (!plugin) { + fprintf(stderr, + COLOR_RED "Error:" COLOR_RESET " Could not load plugin '%s'\n" + " Tried built-ins and dynamic loading (.so)\n", + name); + exit(1); + } + + ImportedPlugin *p = xmalloc(sizeof(ImportedPlugin)); + p->name = xstrdup(plugin->name); // Use the plugin's internal name + p->alias = alias ? xstrdup(alias) : NULL; + p->next = ctx->imported_plugins; + ctx->imported_plugins = p; +} + +const char *resolve_plugin(ParserContext *ctx, const char *name_or_alias) { + for (ImportedPlugin *p = ctx->imported_plugins; p; p = p->next) { + // Check if it matches the alias + if (p->alias && strcmp(p->alias, name_or_alias) == 0) { + return p->name; + } + // Check if it matches the name + if (strcmp(p->name, name_or_alias) == 0) { + return p->name; + } + } + return NULL; // Plugin not found } diff --git a/src/plugins/plugin_manager.c b/src/plugins/plugin_manager.c index afca55e..aab98c8 100644 --- a/src/plugins/plugin_manager.c +++ b/src/plugins/plugin_manager.c @@ -6,99 +6,82 @@ #include <string.h> // Linked list node for plugins. -typedef struct PluginNode -{ - ZPlugin *plugin; - void *handle; // dlopen handle (NULL for built-ins). - struct PluginNode *next; +typedef struct PluginNode { + ZPlugin *plugin; + void *handle; // dlopen handle (NULL for built-ins). + struct PluginNode *next; } PluginNode; static PluginNode *head = NULL; -void zptr_plugin_mgr_init(void) -{ - head = NULL; -} +void zptr_plugin_mgr_init(void) { head = NULL; } -void zptr_register_plugin(ZPlugin *plugin) -{ - if (!plugin) - { - return; - } +void zptr_register_plugin(ZPlugin *plugin) { + if (!plugin) { + return; + } - if (zptr_find_plugin(plugin->name)) - { - return; - } + if (zptr_find_plugin(plugin->name)) { + return; + } - PluginNode *node = malloc(sizeof(PluginNode)); - node->plugin = plugin; - node->handle = NULL; - node->next = head; - head = node; + PluginNode *node = malloc(sizeof(PluginNode)); + node->plugin = plugin; + node->handle = NULL; + node->next = head; + head = node; } -ZPlugin *zptr_load_plugin(const char *path) -{ - void *handle = dlopen(path, RTLD_LAZY); - if (!handle) - { - return NULL; - } +ZPlugin *zptr_load_plugin(const char *path) { + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) { + return NULL; + } - ZPluginInitFn init_fn = (ZPluginInitFn)dlsym(handle, "z_plugin_init"); - if (!init_fn) - { - fprintf(stderr, "Plugin '%s' missing 'z_plugin_init' symbol\n", path); - dlclose(handle); - return NULL; - } + ZPluginInitFn init_fn = (ZPluginInitFn)dlsym(handle, "z_plugin_init"); + if (!init_fn) { + fprintf(stderr, "Plugin '%s' missing 'z_plugin_init' symbol\n", path); + dlclose(handle); + return NULL; + } - ZPlugin *plugin = init_fn(); - if (!plugin) - { - fprintf(stderr, "Plugin '%s' init returned NULL\n", path); - dlclose(handle); - return NULL; - } + ZPlugin *plugin = init_fn(); + if (!plugin) { + fprintf(stderr, "Plugin '%s' init returned NULL\n", path); + dlclose(handle); + return NULL; + } - // Register - PluginNode *node = malloc(sizeof(PluginNode)); - node->plugin = plugin; - node->handle = handle; - node->next = head; - head = node; + // Register + PluginNode *node = malloc(sizeof(PluginNode)); + node->plugin = plugin; + node->handle = handle; + node->next = head; + head = node; - return plugin; + return plugin; } -ZPlugin *zptr_find_plugin(const char *name) -{ - PluginNode *curr = head; - while (curr) - { - if (strcmp(curr->plugin->name, name) == 0) - { - return curr->plugin; - } - curr = curr->next; +ZPlugin *zptr_find_plugin(const char *name) { + PluginNode *curr = head; + while (curr) { + if (strcmp(curr->plugin->name, name) == 0) { + return curr->plugin; } - return NULL; + curr = curr->next; + } + return NULL; } -void zptr_plugin_mgr_cleanup(void) -{ - PluginNode *curr = head; - while (curr) - { - PluginNode *next = curr->next; - if (curr->handle) - { - dlclose(curr->handle); - } - free(curr); - curr = next; +void zptr_plugin_mgr_cleanup(void) { + PluginNode *curr = head; + while (curr) { + PluginNode *next = curr->next; + if (curr->handle) { + dlclose(curr->handle); } - head = NULL; + free(curr); + curr = next; + } + head = NULL; } diff --git a/src/repl/repl.c b/src/repl/repl.c index 22fe95d..5c82038 100644 --- a/src/repl/repl.c +++ b/src/repl/repl.c @@ -9,1368 +9,1177 @@ ASTNode *parse_program(ParserContext *ctx, Lexer *l); -static int is_header_line(const char *line) -{ - return (strncmp(line, "import ", 7) == 0 || strncmp(line, "include ", 8) == 0 || - strncmp(line, "#include", 8) == 0); +static int is_header_line(const char *line) { + return (strncmp(line, "import ", 7) == 0 || + strncmp(line, "include ", 8) == 0 || + strncmp(line, "#include", 8) == 0); } -void run_repl(const char *self_path) -{ - printf("\033[1;36mZen C REPL (v0.1)\033[0m\n"); - printf("Type 'exit' or 'quit' to leave.\n"); - printf("Type :help for commands.\n"); - - // Dynamic history. - int history_cap = 64; - int history_len = 0; - char **history = xmalloc(history_cap * sizeof(char *)); - - char history_path[512]; - const char *home = getenv("HOME"); - if (home) - { - snprintf(history_path, sizeof(history_path), "%s/.zprep_history", home); - FILE *hf = fopen(history_path, "r"); - if (hf) - { - char buf[1024]; - while (fgets(buf, sizeof(buf), hf)) - { - size_t l = strlen(buf); - if (l > 0 && buf[l - 1] == '\n') - { - buf[--l] = 0; - } - if (l == 0) - { - continue; - } - if (history_len >= history_cap) - { - history_cap *= 2; - history = realloc(history, history_cap * sizeof(char *)); - } - history[history_len++] = strdup(buf); - } - fclose(hf); - if (history_len > 0) - { - printf("Loaded %d entries from history.\n", history_len); - } +void run_repl(const char *self_path) { + printf("\033[1;36mZen C REPL (v0.1)\033[0m\n"); + printf("Type 'exit' or 'quit' to leave.\n"); + printf("Type :help for commands.\n"); + + // Dynamic history. + int history_cap = 64; + int history_len = 0; + char **history = xmalloc(history_cap * sizeof(char *)); + + char history_path[512]; + const char *home = getenv("HOME"); + if (home) { + snprintf(history_path, sizeof(history_path), "%s/.zprep_history", home); + FILE *hf = fopen(history_path, "r"); + if (hf) { + char buf[1024]; + while (fgets(buf, sizeof(buf), hf)) { + size_t l = strlen(buf); + if (l > 0 && buf[l - 1] == '\n') { + buf[--l] = 0; } - } - else - { - history_path[0] = 0; - } - - // Watch list. - char *watches[16]; - int watches_len = 0; - for (int i = 0; i < 16; i++) - { - watches[i] = NULL; - } - - // Load startup file (~/.zprep_init.zc) if exists - if (home) - { - char init_path[512]; - snprintf(init_path, sizeof(init_path), "%s/.zprep_init.zc", home); - FILE *init_f = fopen(init_path, "r"); - if (init_f) - { - char buf[1024]; - int init_count = 0; - while (fgets(buf, sizeof(buf), init_f)) - { - size_t l = strlen(buf); - if (l > 0 && buf[l - 1] == '\n') - { - buf[--l] = 0; - } - char *p = buf; - while (*p == ' ' || *p == '\t') - { - p++; - } - if (*p == 0 || *p == '/' || *p == '#') - { - continue; - } - if (history_len >= history_cap) - { - history_cap *= 2; - history = realloc(history, history_cap * sizeof(char *)); - } - history[history_len++] = strdup(p); - init_count++; - } - fclose(init_f); - if (init_count > 0) - { - printf("Loaded %d lines from ~/.zprep_init.zc\n", init_count); - } + if (l == 0) { + continue; + } + if (history_len >= history_cap) { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); } + history[history_len++] = strdup(buf); + } + fclose(hf); + if (history_len > 0) { + printf("Loaded %d entries from history.\n", history_len); + } } - - char line_buf[1024]; - - char *input_buffer = NULL; - size_t input_len = 0; - int brace_depth = 0; - int paren_depth = 0; - - while (1) - { - if (brace_depth > 0 || paren_depth > 0) - { - printf("... "); + } else { + history_path[0] = 0; + } + + // Watch list. + char *watches[16]; + int watches_len = 0; + for (int i = 0; i < 16; i++) { + watches[i] = NULL; + } + + // Load startup file (~/.zprep_init.zc) if exists + if (home) { + char init_path[512]; + snprintf(init_path, sizeof(init_path), "%s/.zprep_init.zc", home); + FILE *init_f = fopen(init_path, "r"); + if (init_f) { + char buf[1024]; + int init_count = 0; + while (fgets(buf, sizeof(buf), init_f)) { + size_t l = strlen(buf); + if (l > 0 && buf[l - 1] == '\n') { + buf[--l] = 0; } - else - { - printf("\033[1;32m>>>\033[0m "); + char *p = buf; + while (*p == ' ' || *p == '\t') { + p++; } - - if (!fgets(line_buf, sizeof(line_buf), stdin)) - { - break; + if (*p == 0 || *p == '/' || *p == '#') { + continue; + } + if (history_len >= history_cap) { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); } + history[history_len++] = strdup(p); + init_count++; + } + fclose(init_f); + if (init_count > 0) { + printf("Loaded %d lines from ~/.zprep_init.zc\n", init_count); + } + } + } - // Handle commands (only on fresh line). - if (NULL == input_buffer) - { - size_t len = strlen(line_buf); - char cmd_buf[1024]; - strcpy(cmd_buf, line_buf); - if (len > 0 && cmd_buf[len - 1] == '\n') - { - cmd_buf[--len] = 0; - } - while (len > 0 && (cmd_buf[len - 1] == ' ' || cmd_buf[len - 1] == '\t')) - { - cmd_buf[--len] = 0; - } + char line_buf[1024]; - if (0 == strcmp(cmd_buf, "exit") || 0 == strcmp(cmd_buf, "quit")) - { - break; - } + char *input_buffer = NULL; + size_t input_len = 0; + int brace_depth = 0; + int paren_depth = 0; - // Commands - if (cmd_buf[0] == ':' || cmd_buf[0] == '!') - { - if (0 == strcmp(cmd_buf, ":help")) - { - printf("REPL Commands:\n"); - printf(" :help Show this help\n"); - printf(" :reset Clear history\n"); - printf(" :imports Show active imports\n"); - printf(" :vars Show active variables\n"); - printf(" :funcs Show user functions\n"); - printf(" :structs Show user structs\n"); - printf(" :history Show command history\n"); - printf(" :type <x> Show type of expression\n"); - printf(" :time <x> Benchmark expression (1000 iters)\n"); - printf(" :c <x> Show generated C code\n"); - printf(" :doc <x> Show documentation for symbol\n"); - printf(" :run Execute full session\n"); - printf(" :edit [n] Edit command n (default: last) in $EDITOR\n"); - printf(" :save <f> Save session to file\n"); - printf(" :load <f> Load file into session\n"); - printf(" :load <f> Load file into session\n"); - printf(" :watch <x> Watch expression output\n"); - printf(" :unwatch <n> Remove watch n\n"); - printf(" :undo Remove last command\n"); - printf(" :delete <n> Remove command at index n\n"); - printf(" :clear Clear screen\n"); - printf(" ! <cmd> Run shell command\n"); - printf(" :quit Exit REPL\n"); - continue; - } - else if (0 == strcmp(cmd_buf, ":reset")) - { - for (int i = 0; i < history_len; i++) - { - free(history[i]); - } - history_len = 0; - printf("History cleared.\n"); - continue; - } - else if (0 == strcmp(cmd_buf, ":quit")) - { - break; - } - else if (0 == strcmp(cmd_buf, ":clear")) - { - printf("\033[2J\033[H"); // ANSI clear screen - continue; - } - else if (0 == strcmp(cmd_buf, ":undo")) - { - if (history_len > 0) - { - history_len = history_len - 1; - free(history[history_len]); - printf("Removed last entry.\n"); - } - else - { - printf("History is empty.\n"); - } - continue; - } - else if (0 == strncmp(cmd_buf, ":delete ", 8)) - { - int idx = atoi(cmd_buf + 8) - 1; - if (idx >= 0 && idx < history_len) - { - free(history[idx]); - for (int i = idx; i < history_len - 1; i++) - { - history[i] = history[i + 1]; - } - history_len = history_len - 1; - printf("Deleted entry %d.\n", idx + 1); - } - else - { - printf("Invalid index. Use :history to see valid indices.\n"); - } - continue; - } - else if (0 == strncmp(cmd_buf, ":edit", 5)) - { - int idx = history_len - 1; - if (strlen(cmd_buf) > 6) - { - idx = atoi(cmd_buf + 6) - 1; - } + while (1) { + if (brace_depth > 0 || paren_depth > 0) { + printf("... "); + } else { + printf("\033[1;32m>>>\033[0m "); + } - if (history_len == 0) - { - printf("History is empty.\n"); - continue; - } + if (!fgets(line_buf, sizeof(line_buf), stdin)) { + break; + } - if (idx < 0 || idx >= history_len) - { - printf("Invalid index.\n"); - continue; - } + // Handle commands (only on fresh line). + if (NULL == input_buffer) { + size_t len = strlen(line_buf); + char cmd_buf[1024]; + strcpy(cmd_buf, line_buf); + if (len > 0 && cmd_buf[len - 1] == '\n') { + cmd_buf[--len] = 0; + } + while (len > 0 && (cmd_buf[len - 1] == ' ' || cmd_buf[len - 1] == '\t')) { + cmd_buf[--len] = 0; + } + + if (0 == strcmp(cmd_buf, "exit") || 0 == strcmp(cmd_buf, "quit")) { + break; + } + + // Commands + if (cmd_buf[0] == ':' || cmd_buf[0] == '!') { + if (0 == strcmp(cmd_buf, ":help")) { + printf("REPL Commands:\n"); + printf(" :help Show this help\n"); + printf(" :reset Clear history\n"); + printf(" :imports Show active imports\n"); + printf(" :vars Show active variables\n"); + printf(" :funcs Show user functions\n"); + printf(" :structs Show user structs\n"); + printf(" :history Show command history\n"); + printf(" :type <x> Show type of expression\n"); + printf(" :time <x> Benchmark expression (1000 iters)\n"); + printf(" :c <x> Show generated C code\n"); + printf(" :doc <x> Show documentation for symbol\n"); + printf(" :run Execute full session\n"); + printf(" :edit [n] Edit command n (default: last) in $EDITOR\n"); + printf(" :save <f> Save session to file\n"); + printf(" :load <f> Load file into session\n"); + printf(" :load <f> Load file into session\n"); + printf(" :watch <x> Watch expression output\n"); + printf(" :unwatch <n> Remove watch n\n"); + printf(" :undo Remove last command\n"); + printf(" :delete <n> Remove command at index n\n"); + printf(" :clear Clear screen\n"); + printf(" ! <cmd> Run shell command\n"); + printf(" :quit Exit REPL\n"); + continue; + } else if (0 == strcmp(cmd_buf, ":reset")) { + for (int i = 0; i < history_len; i++) { + free(history[i]); + } + history_len = 0; + printf("History cleared.\n"); + continue; + } else if (0 == strcmp(cmd_buf, ":quit")) { + break; + } else if (0 == strcmp(cmd_buf, ":clear")) { + printf("\033[2J\033[H"); // ANSI clear screen + continue; + } else if (0 == strcmp(cmd_buf, ":undo")) { + if (history_len > 0) { + history_len = history_len - 1; + free(history[history_len]); + printf("Removed last entry.\n"); + } else { + printf("History is empty.\n"); + } + continue; + } else if (0 == strncmp(cmd_buf, ":delete ", 8)) { + int idx = atoi(cmd_buf + 8) - 1; + if (idx >= 0 && idx < history_len) { + free(history[idx]); + for (int i = idx; i < history_len - 1; i++) { + history[i] = history[i + 1]; + } + history_len = history_len - 1; + printf("Deleted entry %d.\n", idx + 1); + } else { + printf("Invalid index. Use :history to see valid indices.\n"); + } + continue; + } else if (0 == strncmp(cmd_buf, ":edit", 5)) { + int idx = history_len - 1; + if (strlen(cmd_buf) > 6) { + idx = atoi(cmd_buf + 6) - 1; + } + + if (history_len == 0) { + printf("History is empty.\n"); + continue; + } - char edit_path[256]; - sprintf(edit_path, "/tmp/zprep_edit_%d.zc", rand()); - FILE *f = fopen(edit_path, "w"); - if (f) - { - fprintf(f, "%s", history[idx]); - fclose(f); - - const char *editor = getenv("EDITOR"); - if (!editor) - { - editor = "nano"; // Default fallback, - // 'cause I know some of you - // don't know how to exit Vim. - } - - char cmd[1024]; - sprintf(cmd, "%s %s", editor, edit_path); - int status = system(cmd); - - if (0 == status) - { - // Read back file. - FILE *fr = fopen(edit_path, "r"); - if (fr) - { - fseek(fr, 0, SEEK_END); - long length = ftell(fr); - fseek(fr, 0, SEEK_SET); - char *buffer = malloc(length + 1); - if (buffer) - { - fread(buffer, 1, length, fr); - buffer[length] = 0; - - while (length > 0 && buffer[length - 1] == '\n') - { - buffer[--length] = 0; - } - - if (strlen(buffer) > 0) - { - printf("Running: %s\n", buffer); - if (history_len >= history_cap) - { - history_cap *= 2; - history = - realloc(history, history_cap * sizeof(char *)); - } - history[history_len++] = strdup(buffer); - } - else - { - free(buffer); - } - } - fclose(fr); - } - } - } - continue; - } - else if (0 == strncmp(cmd_buf, ":watch ", 7)) - { - char *expr = cmd_buf + 7; - while (*expr == ' ') - { - expr++; - } - size_t l = strlen(expr); - while (l > 0 && expr[l - 1] == ' ') - { - expr[--l] = 0; - } + if (idx < 0 || idx >= history_len) { + printf("Invalid index.\n"); + continue; + } + + char edit_path[256]; + sprintf(edit_path, "/tmp/zprep_edit_%d.zc", rand()); + FILE *f = fopen(edit_path, "w"); + if (f) { + fprintf(f, "%s", history[idx]); + fclose(f); + + const char *editor = getenv("EDITOR"); + if (!editor) { + editor = "nano"; // Default fallback, + // 'cause I know some of you + // don't know how to exit Vim. + } - if (l > 0) - { - if (watches_len < 16) - { - watches[watches_len++] = strdup(expr); - printf("Watching: %s\n", expr); - } - else - { - printf("Watch list full (max 16).\n"); - } - } - else - { - if (watches_len == 0) - { - printf("No active watches.\n"); - } - else - { - for (int i = 0; i < watches_len; i++) - { - printf("%d: %s\n", i + 1, watches[i]); - } - } - } - continue; - } - else if (0 == strncmp(cmd_buf, ":unwatch ", 9)) - { - // Remove watch. - int idx = atoi(cmd_buf + 9) - 1; - if (idx >= 0 && idx < watches_len) - { - free(watches[idx]); - for (int i = idx; i < watches_len - 1; i++) - { - watches[i] = watches[i + 1]; - } - - watches_len--; - - printf("Removed watch %d.\n", idx + 1); - } - else - { - printf("Invalid index.\n"); - } - continue; - } - else if (cmd_buf[0] == '!') - { - // Shell escape. - system(cmd_buf + 1); - continue; - } - else if (0 == strncmp(cmd_buf, ":save ", 6)) - { - char *filename = cmd_buf + 6; - FILE *f = fopen(filename, "w"); - if (f) - { - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - fprintf(f, "%s\n", history[i]); - } - } - // Write main function body. - fprintf(f, "\nfn main() {\n"); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - fprintf(f, " %s\n", history[i]); - } - } - fprintf(f, "}\n"); - fclose(f); - printf("Session saved to %s\n", filename); - } - else - { - printf("Error: Cannot write to %s\n", filename); - } - continue; - } - else if (0 == strncmp(cmd_buf, ":load ", 6)) - { - char *filename = cmd_buf + 6; - FILE *f = fopen(filename, "r"); - if (f) - { - char buf[1024]; - int count = 0; - while (fgets(buf, sizeof(buf), f)) - { - size_t l = strlen(buf); - if (l > 0 && buf[l - 1] == '\n') - { - buf[--l] = 0; - } - if (l == 0) - { - continue; - } - if (history_len >= history_cap) - { - history_cap *= 2; - history = realloc(history, history_cap * sizeof(char *)); - } - history[history_len++] = strdup(buf); - count++; - } - fclose(f); - printf("Loaded %d lines from %s\n", count, filename); - } - else - { - printf("Error: Cannot read %s\n", filename); - } - continue; - } - else if (0 == strcmp(cmd_buf, ":imports")) - { - printf("Active Imports:\n"); - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - printf(" %s\n", history[i]); - } + char cmd[1024]; + sprintf(cmd, "%s %s", editor, edit_path); + int status = system(cmd); + + if (0 == status) { + // Read back file. + FILE *fr = fopen(edit_path, "r"); + if (fr) { + fseek(fr, 0, SEEK_END); + long length = ftell(fr); + fseek(fr, 0, SEEK_SET); + char *buffer = malloc(length + 1); + if (buffer) { + fread(buffer, 1, length, fr); + buffer[length] = 0; + + while (length > 0 && buffer[length - 1] == '\n') { + buffer[--length] = 0; + } + + if (strlen(buffer) > 0) { + printf("Running: %s\n", buffer); + if (history_len >= history_cap) { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); } - continue; + history[history_len++] = strdup(buffer); + } else { + free(buffer); + } } - else if (0 == strcmp(cmd_buf, ":history")) - { - printf("Session History:\n"); - for (int i = 0; i < history_len; i++) - { - printf("%4d %s\n", i + 1, history[i]); - } - continue; - } - else if (0 == strcmp(cmd_buf, ":vars") || 0 == strcmp(cmd_buf, ":funcs") || - 0 == strcmp(cmd_buf, ":structs")) - { - size_t code_size = 4096; - for (int i = 0; i < history_len; i++) - { - code_size += strlen(history[i]) + 2; - } - char *code = malloc(code_size + 128); - strcpy(code, ""); - - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, "\n"); - } - } - strcat(code, "fn main() { "); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, " "); - } - } - strcat(code, " }"); - - ParserContext ctx = {0}; - ctx.is_repl = 1; - ctx.skip_preamble = 1; - - Lexer l; - lexer_init(&l, code); - ASTNode *nodes = parse_program(&ctx, &l); - - ASTNode *search = nodes; - if (search && search->type == NODE_ROOT) - { - search = search->root.children; - } - - if (0 == strcmp(cmd_buf, ":vars")) - { - ASTNode *main_func = NULL; - for (ASTNode *n = search; n; n = n->next) - { - if (n->type == NODE_FUNCTION && 0 == strcmp(n->func.name, "main")) - { - main_func = n; - break; - } - } - printf("Variables:\n"); - if (main_func && main_func->func.body && - main_func->func.body->type == NODE_BLOCK) - { - int found = 0; - for (ASTNode *s = main_func->func.body->block.statements; s; - s = s->next) - { - if (s->type == NODE_VAR_DECL) - { - char *t = - s->var_decl.type_str ? s->var_decl.type_str : "Inferred"; - printf(" %s: %s\n", s->var_decl.name, t); - found = 1; - } - } - if (!found) - { - printf(" (none)\n"); - } - } - else - { - printf(" (none)\n"); - } - } - else if (0 == strcmp(cmd_buf, ":funcs")) - { - printf("Functions:\n"); - int found = 0; - for (ASTNode *n = search; n; n = n->next) - { - if (n->type == NODE_FUNCTION && 0 != strcmp(n->func.name, "main")) - { - printf(" fn %s()\n", n->func.name); - found = 1; - } - } - if (!found) - { - printf(" (none)\n"); - } - } - else if (0 == strcmp(cmd_buf, ":structs")) - { - printf("Structs:\n"); - int found = 0; - for (ASTNode *n = search; n; n = n->next) - { - if (n->type == NODE_STRUCT) - { - printf(" struct %s\n", n->strct.name); - found = 1; - } - } - if (!found) - { - printf(" (none)\n"); - } - } + fclose(fr); + } + } + } + continue; + } else if (0 == strncmp(cmd_buf, ":watch ", 7)) { + char *expr = cmd_buf + 7; + while (*expr == ' ') { + expr++; + } + size_t l = strlen(expr); + while (l > 0 && expr[l - 1] == ' ') { + expr[--l] = 0; + } + + if (l > 0) { + if (watches_len < 16) { + watches[watches_len++] = strdup(expr); + printf("Watching: %s\n", expr); + } else { + printf("Watch list full (max 16).\n"); + } + } else { + if (watches_len == 0) { + printf("No active watches.\n"); + } else { + for (int i = 0; i < watches_len; i++) { + printf("%d: %s\n", i + 1, watches[i]); + } + } + } + continue; + } else if (0 == strncmp(cmd_buf, ":unwatch ", 9)) { + // Remove watch. + int idx = atoi(cmd_buf + 9) - 1; + if (idx >= 0 && idx < watches_len) { + free(watches[idx]); + for (int i = idx; i < watches_len - 1; i++) { + watches[i] = watches[i + 1]; + } - free(code); - continue; + watches_len--; + + printf("Removed watch %d.\n", idx + 1); + } else { + printf("Invalid index.\n"); + } + continue; + } else if (cmd_buf[0] == '!') { + // Shell escape. + system(cmd_buf + 1); + continue; + } else if (0 == strncmp(cmd_buf, ":save ", 6)) { + char *filename = cmd_buf + 6; + FILE *f = fopen(filename, "w"); + if (f) { + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + fprintf(f, "%s\n", history[i]); + } + } + // Write main function body. + fprintf(f, "\nfn main() {\n"); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + fprintf(f, " %s\n", history[i]); + } + } + fprintf(f, "}\n"); + fclose(f); + printf("Session saved to %s\n", filename); + } else { + printf("Error: Cannot write to %s\n", filename); + } + continue; + } else if (0 == strncmp(cmd_buf, ":load ", 6)) { + char *filename = cmd_buf + 6; + FILE *f = fopen(filename, "r"); + if (f) { + char buf[1024]; + int count = 0; + while (fgets(buf, sizeof(buf), f)) { + size_t l = strlen(buf); + if (l > 0 && buf[l - 1] == '\n') { + buf[--l] = 0; + } + if (l == 0) { + continue; + } + if (history_len >= history_cap) { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); + } + history[history_len++] = strdup(buf); + count++; + } + fclose(f); + printf("Loaded %d lines from %s\n", count, filename); + } else { + printf("Error: Cannot read %s\n", filename); + } + continue; + } else if (0 == strcmp(cmd_buf, ":imports")) { + printf("Active Imports:\n"); + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + printf(" %s\n", history[i]); + } + } + continue; + } else if (0 == strcmp(cmd_buf, ":history")) { + printf("Session History:\n"); + for (int i = 0; i < history_len; i++) { + printf("%4d %s\n", i + 1, history[i]); + } + continue; + } else if (0 == strcmp(cmd_buf, ":vars") || + 0 == strcmp(cmd_buf, ":funcs") || + 0 == strcmp(cmd_buf, ":structs")) { + size_t code_size = 4096; + for (int i = 0; i < history_len; i++) { + code_size += strlen(history[i]) + 2; + } + char *code = malloc(code_size + 128); + strcpy(code, ""); + + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, "\n"); + } + } + strcat(code, "fn main() { "); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, " "); + } + } + strcat(code, " }"); + + ParserContext ctx = {0}; + ctx.is_repl = 1; + ctx.skip_preamble = 1; + + Lexer l; + lexer_init(&l, code); + ASTNode *nodes = parse_program(&ctx, &l); + + ASTNode *search = nodes; + if (search && search->type == NODE_ROOT) { + search = search->root.children; + } + + if (0 == strcmp(cmd_buf, ":vars")) { + ASTNode *main_func = NULL; + for (ASTNode *n = search; n; n = n->next) { + if (n->type == NODE_FUNCTION && + 0 == strcmp(n->func.name, "main")) { + main_func = n; + break; + } + } + printf("Variables:\n"); + if (main_func && main_func->func.body && + main_func->func.body->type == NODE_BLOCK) { + int found = 0; + for (ASTNode *s = main_func->func.body->block.statements; s; + s = s->next) { + if (s->type == NODE_VAR_DECL) { + char *t = + s->var_decl.type_str ? s->var_decl.type_str : "Inferred"; + printf(" %s: %s\n", s->var_decl.name, t); + found = 1; } - else if (0 == strncmp(cmd_buf, ":type ", 6)) - { - char *expr = cmd_buf + 6; - - size_t probe_size = 4096; - for (int i = 0; i < history_len; i++) - { - probe_size += strlen(history[i]) + 2; - } - - char *probe_code = malloc(probe_size + strlen(expr) + 256); - strcpy(probe_code, ""); - - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(probe_code, history[i]); - strcat(probe_code, "\n"); - } - } + } + if (!found) { + printf(" (none)\n"); + } + } else { + printf(" (none)\n"); + } + } else if (0 == strcmp(cmd_buf, ":funcs")) { + printf("Functions:\n"); + int found = 0; + for (ASTNode *n = search; n; n = n->next) { + if (n->type == NODE_FUNCTION && + 0 != strcmp(n->func.name, "main")) { + printf(" fn %s()\n", n->func.name); + found = 1; + } + } + if (!found) { + printf(" (none)\n"); + } + } else if (0 == strcmp(cmd_buf, ":structs")) { + printf("Structs:\n"); + int found = 0; + for (ASTNode *n = search; n; n = n->next) { + if (n->type == NODE_STRUCT) { + printf(" struct %s\n", n->strct.name); + found = 1; + } + } + if (!found) { + printf(" (none)\n"); + } + } - strcat(probe_code, "fn main() { _z_suppress_stdout(); "); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - strcat(probe_code, history[i]); - strcat(probe_code, " "); - } - } + free(code); + continue; + } else if (0 == strncmp(cmd_buf, ":type ", 6)) { + char *expr = cmd_buf + 6; - strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } "); - strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = ("); - strcat(probe_code, expr); - strcat(probe_code, "); }"); - - char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_type_%d.zc", rand()); - FILE *f = fopen(tmp_path, "w"); - if (f) - { - fprintf(f, "%s", probe_code); - fclose(f); - - char cmd[2048]; - sprintf(cmd, "%s run -q %s 2>&1", self_path, tmp_path); - - FILE *p = popen(cmd, "r"); - if (p) - { - char buf[1024]; - int found = 0; - while (fgets(buf, sizeof(buf), p)) - { - char *marker = "right operand has type '"; - char *start = strstr(buf, marker); - if (start) - { - start += strlen(marker); - char *end = strchr(start, '\''); - if (end) - { - *end = 0; - printf("\033[1;36mType: %s\033[0m\n", start); - found = 1; - break; - } - } - } - pclose(p); - if (!found) - { - printf("Type: <unknown>\n"); - } - } - } - free(probe_code); - continue; - } - else if (0 == strncmp(cmd_buf, ":time ", 6)) - { - // Benchmark an expression. - char *expr = cmd_buf + 6; - - size_t code_size = 4096; - for (int i = 0; i < history_len; i++) - { - code_size += strlen(history[i]) + 2; - } - char *code = malloc(code_size + strlen(expr) + 256); - strcpy(code, ""); - - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, "\n"); - } - } - strcat(code, "include \"time.h\"\n"); - strcat(code, "fn main() { _z_suppress_stdout();\n"); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, " "); - } - } - strcat(code, "_z_restore_stdout();\n"); - strcat(code, "raw { clock_t _start = clock(); }\n"); - strcat(code, "for _i in 0..1000 { "); - strcat(code, expr); - strcat(code, "; }\n"); - strcat(code, "raw { clock_t _end = clock(); double _elapsed = (double)(_end - " - "_start) / CLOCKS_PER_SEC; printf(\"1000 iterations: %.4fs " - "(%.6fs/iter)\\n\", _elapsed, _elapsed/1000); }\n"); - strcat(code, "}"); - - char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_time_%d.zc", rand()); - FILE *f = fopen(tmp_path, "w"); - if (f) - { - fprintf(f, "%s", code); - fclose(f); - char cmd[2048]; - sprintf(cmd, "%s run -q %s", self_path, tmp_path); - system(cmd); - } - free(code); - continue; - } - else if (0 == strncmp(cmd_buf, ":c ", 3)) - { - char *expr_buf = malloc(8192); - strcpy(expr_buf, cmd_buf + 3); - - int brace_depth = 0; - for (char *p = expr_buf; *p; p++) - { - if (*p == '{') - { - brace_depth++; - } - else if (*p == '}') - { - brace_depth--; - } - } + size_t probe_size = 4096; + for (int i = 0; i < history_len; i++) { + probe_size += strlen(history[i]) + 2; + } - while (brace_depth > 0) - { - printf("... "); - char more[1024]; - if (!fgets(more, sizeof(more), stdin)) - { - break; - } - size_t mlen = strlen(more); - if (mlen > 0 && more[mlen - 1] == '\n') - { - more[--mlen] = 0; - } - strcat(expr_buf, "\n"); - strcat(expr_buf, more); - for (char *p = more; *p; p++) - { - if (*p == '{') - { - brace_depth++; - } - else if (*p == '}') - { - brace_depth--; - } - } - } + char *probe_code = malloc(probe_size + strlen(expr) + 256); + strcpy(probe_code, ""); - size_t code_size = 4096 + strlen(expr_buf); - for (int i = 0; i < history_len; i++) - { - code_size += strlen(history[i]) + 2; - } - char *code = malloc(code_size + 128); - strcpy(code, ""); - - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, "\n"); - } - } - strcat(code, "fn main() {\n"); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, " "); - } - } - strcat(code, expr_buf); - strcat(code, "\n}"); - free(expr_buf); - - char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_c_%d.zc", rand()); - FILE *f = fopen(tmp_path, "w"); - if (f) - { - fprintf(f, "%s", code); - fclose(f); - char cmd[2048]; - sprintf(cmd, - "%s build -q --emit-c -o /tmp/zprep_repl_out %s " - "2>/dev/null; sed " - "-n '/^int main() {$/,/^}$/p' /tmp/zprep_repl_out.c " - "2>/dev/null | " - "tail -n +3 | head -n -2 | sed 's/^ //'", - self_path, tmp_path); - system(cmd); - } - free(code); - continue; - } - else if (0 == strcmp(cmd_buf, ":run")) - { - size_t code_size = 4096; - for (int i = 0; i < history_len; i++) - { - code_size += strlen(history[i]) + 2; - } - char *code = malloc(code_size); - strcpy(code, ""); - - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(code, history[i]); - strcat(code, "\n"); - } - } - strcat(code, "fn main() {\n"); - for (int i = 0; i < history_len; i++) - { - if (!is_header_line(history[i])) - { - strcat(code, " "); - strcat(code, history[i]); - strcat(code, "\n"); - } - } - strcat(code, "}\n"); - - char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand()); - FILE *f = fopen(tmp_path, "w"); - if (f) - { - fprintf(f, "%s", code); - fclose(f); - char cmd[2048]; - sprintf(cmd, "%s run %s", self_path, tmp_path); - system(cmd); - } - free(code); - continue; - } - else if (0 == strncmp(cmd_buf, ":doc ", 5)) - { - char *sym = cmd_buf + 5; - while (*sym == ' ') - { - sym++; - } - size_t symlen = strlen(sym); - while (symlen > 0 && sym[symlen - 1] == ' ') - { - sym[--symlen] = 0; - } + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(probe_code, history[i]); + strcat(probe_code, "\n"); + } + } - // Documentation database - - struct - { - const char *name; - const char *doc; - } docs[] = { - {"Vec", "Vec<T> - Dynamic array (generic)\n Fields: data: T*, " - "len: usize, cap: " - "usize\n Methods: new, push, pop, get, set, insert, " - "remove, contains, " - "clear, free, clone, reverse, first, last, length, " - "is_empty, eq"}, - {"Vec.new", "fn Vec<T>::new() -> Vec<T>\n Creates an empty vector."}, - {"Vec.push", "fn push(self, item: T)\n Appends item to the end. " - "Auto-grows capacity."}, - {"Vec.pop", "fn pop(self) -> T\n Removes and returns the last element. " - "Panics if empty."}, - {"Vec.get", "fn get(self, idx: usize) -> T\n Returns element at index. " - "Panics if out of bounds."}, - {"Vec.set", "fn set(self, idx: usize, item: T)\n Sets element at index. " - "Panics if out of bounds."}, - {"Vec.insert", "fn insert(self, idx: usize, item: T)\n Inserts item at " - "index, shifting elements right."}, - {"Vec.remove", "fn remove(self, idx: usize) -> T\n Removes and returns " - "element at index, shifting elements left."}, - {"Vec.contains", "fn contains(self, item: T) -> bool\n Returns true if " - "item is in the vector."}, - {"Vec.clear", "fn clear(self)\n Removes all elements but keeps capacity."}, - {"Vec.free", "fn free(self)\n Frees memory. Sets data to null."}, - {"Vec.clone", "fn clone(self) -> Vec<T>\n Returns a shallow copy."}, - {"Vec.reverse", "fn reverse(self)\n Reverses elements in place."}, - {"Vec.first", "fn first(self) -> T\n Returns first element. " - "Panics if empty."}, - {"Vec.last", - "fn last(self) -> T\n Returns last element. Panics if empty."}, - {"Vec.length", "fn length(self) -> usize\n Returns number of elements."}, - {"Vec.is_empty", - "fn is_empty(self) -> bool\n Returns true if length is 0."}, - {"String", "String - Mutable string (alias for char*)\n " - "Methods: len, split, trim, " - "contains, starts_with, ends_with, to_upper, " - "to_lower, substring, find"}, - {"String.len", "fn len(self) -> usize\n Returns string length."}, - {"String.contains", "fn contains(self, substr: string) -> bool\n Returns " - "true if string contains substr."}, - {"String.starts_with", "fn starts_with(self, prefix: string) -> bool\n " - "Returns true if string starts with prefix."}, - {"String.ends_with", "fn ends_with(self, suffix: string) -> bool\n " - "Returns true if string ends with suffix."}, - {"String.substring", "fn substring(self, start: usize, len: usize) -> " - "string\n Returns a substring. Caller must free."}, - {"String.find", "fn find(self, substr: string) -> int\n Returns index of " - "substr, or -1 if not found."}, - {"println", "println \"format string {expr}\"\n Prints to stdout with " - "newline. Auto-formats {expr} values."}, - {"print", "print \"format string {expr}\"\n Prints to stdout " - "without newline."}, - {"eprintln", - "eprintln \"format string\"\n Prints to stderr with newline."}, - {"eprint", "eprint \"format string\"\n Prints to stderr without newline."}, - {"guard", "guard condition else action\n Early exit pattern. " - "Executes action if " - "condition is false.\n Example: guard ptr != NULL " - "else return;"}, - {"defer", "defer statement\n Executes statement at end of scope.\n " - "Example: defer free(ptr);"}, - {"sizeof", "sizeof(type) or sizeof(expr)\n Returns size in bytes."}, - {"typeof", "typeof(expr)\n Returns the type of expression " - "(compile-time)."}, - {"malloc", "void *malloc(size_t size)\n Allocates size bytes. Returns " - "pointer or NULL. Free with free()."}, - {"free", "void free(void *ptr)\n Frees memory allocated by " - "malloc/calloc/realloc."}, - {"calloc", "void *calloc(size_t n, size_t size)\n Allocates n*size bytes, " - "zeroed. Returns pointer or NULL."}, - {"realloc", "void *realloc(void *ptr, size_t size)\n Resizes allocation " - "to size bytes. May move memory."}, - {"memcpy", "void *memcpy(void *dest, const void *src, size_t n)\n Copies " - "n bytes from src to dest. Returns dest. No overlap."}, - {"memmove", "void *memmove(void *dest, const void *src, size_t n)\n " - "Copies n bytes, handles overlapping regions."}, - {"memset", "void *memset(void *s, int c, size_t n)\n Sets n " - "bytes of s to value c."}, - {"strlen", "size_t strlen(const char *s)\n Returns length of string (not " - "including null terminator)."}, - {"strcpy", "char *strcpy(char *dest, const char *src)\n Copies src to " - "dest including null terminator. No bounds check."}, - {"strncpy", "char *strncpy(char *dest, const char *src, size_t n)\n " - "Copies up to n chars. May not null-terminate."}, - {"strcat", "char *strcat(char *dest, const char *src)\n Appends " - "src to dest."}, - {"strcmp", "int strcmp(const char *s1, const char *s2)\n Compares " - "strings. Returns 0 if equal, <0 or >0 otherwise."}, - {"strncmp", "int strncmp(const char *s1, const char *s2, size_t n)\n " - "Compares up to n characters."}, - {"strstr", "char *strstr(const char *haystack, const char *needle)\n " - "Finds first occurrence of needle. Returns pointer or NULL."}, - {"strchr", "char *strchr(const char *s, int c)\n Finds first occurrence " - "of char c. Returns pointer or NULL."}, - {"strdup", "char *strdup(const char *s)\n Duplicates string. Caller must " - "free the result."}, - {"printf", "int printf(const char *fmt, ...)\n Prints formatted output to " - "stdout. Returns chars written."}, - {"sprintf", "int sprintf(char *str, const char *fmt, ...)\n Prints " - "formatted output to string buffer."}, - {"snprintf", "int snprintf(char *str, size_t n, const char *fmt, ...)\n " - "Safe sprintf with size limit."}, - {"fprintf", "int fprintf(FILE *f, const char *fmt, ...)\n Prints " - "formatted output to file stream."}, - {"scanf", "int scanf(const char *fmt, ...)\n Reads formatted " - "input from stdin."}, - {"fopen", "FILE *fopen(const char *path, const char *mode)\n Opens file. " - "Modes: " - "\"r\", \"w\", \"a\", \"rb\", \"wb\". Returns NULL on error."}, - {"fclose", "int fclose(FILE *f)\n Closes file. Returns 0 on success."}, - {"fread", "size_t fread(void *ptr, size_t size, size_t n, FILE *f)\n " - "Reads n items of size bytes. Returns items read."}, - {"fwrite", "size_t fwrite(const void *ptr, size_t size, size_t n, FILE " - "*f)\n Writes n items of size bytes. Returns items written."}, - {"fgets", "char *fgets(char *s, int n, FILE *f)\n Reads line up to n-1 " - "chars. Includes newline. Returns s or NULL."}, - {"fputs", "int fputs(const char *s, FILE *f)\n Writes string to file. " - "Returns non-negative or EOF."}, - {"exit", "void exit(int status)\n Terminates program with " - "status code. 0 " - "= success."}, - {"atoi", "int atoi(const char *s)\n Converts string to int. " - "Returns 0 on error."}, - {"atof", "double atof(const char *s)\n Converts string to double."}, - {"abs", "int abs(int n)\n Returns absolute value."}, - {"rand", "int rand(void)\n Returns pseudo-random int in [0, RAND_MAX]."}, - {"srand", "void srand(unsigned seed)\n Seeds the random number " - "generator."}, - {"qsort", "void qsort(void *base, size_t n, size_t size, int(*cmp)(const " - "void*, const void*))\n Quicksorts array in-place."}, - {NULL, NULL}}; - - int found = 0; - for (int i = 0; docs[i].name != NULL; i++) - { - if (0 == strcmp(sym, docs[i].name)) - { - printf("\033[1;36m%s\033[0m\n%s\n", docs[i].name, docs[i].doc); - found = 1; - break; - } - } - if (!found) - { - // Fallback: try man pages, show only SYNOPSIS. - char man_cmd[256]; - sprintf(man_cmd, - "man 3 %s 2>/dev/null | sed -n '/^SYNOPSIS/,/^[A-Z]/p' | " - "head -10", - sym); - FILE *mp = popen(man_cmd, "r"); - if (mp) - { - char buf[256]; - int lines = 0; - while (fgets(buf, sizeof(buf), mp) && lines < 8) - { - printf("%s", buf); - lines++; - } - int status = pclose(mp); - if (0 == status && lines > 0) - { - found = 1; - printf("\033[90m(man 3 %s)\033[0m\n", sym); - } - } - if (!found) - { - printf("No documentation for '%s'.\n", sym); - } - } - continue; - } - else - { - printf("Unknown command: %s\n", cmd_buf); - continue; + strcat(probe_code, "fn main() { _z_suppress_stdout(); "); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + strcat(probe_code, history[i]); + strcat(probe_code, " "); + } + } + + strcat(probe_code, + " raw { typedef struct { int _u; } __REVEAL_TYPE__; } "); + strcat(probe_code, + " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = ("); + strcat(probe_code, expr); + strcat(probe_code, "); }"); + + char tmp_path[256]; + sprintf(tmp_path, "/tmp/zprep_repl_type_%d.zc", rand()); + FILE *f = fopen(tmp_path, "w"); + if (f) { + fprintf(f, "%s", probe_code); + fclose(f); + + char cmd[2048]; + sprintf(cmd, "%s run -q %s 2>&1", self_path, tmp_path); + + FILE *p = popen(cmd, "r"); + if (p) { + char buf[1024]; + int found = 0; + while (fgets(buf, sizeof(buf), p)) { + char *marker = "right operand has type '"; + char *start = strstr(buf, marker); + if (start) { + start += strlen(marker); + char *end = strchr(start, '\''); + if (end) { + *end = 0; + printf("\033[1;36mType: %s\033[0m\n", start); + found = 1; + break; + } } + } + pclose(p); + if (!found) { + printf("Type: <unknown>\n"); + } } - } - - int in_quote = 0; - int escaped = 0; - for (int i = 0; line_buf[i]; i++) - { - char c = line_buf[i]; - if (escaped) - { - escaped = 0; - continue; + } + free(probe_code); + continue; + } else if (0 == strncmp(cmd_buf, ":time ", 6)) { + // Benchmark an expression. + char *expr = cmd_buf + 6; + + size_t code_size = 4096; + for (int i = 0; i < history_len; i++) { + code_size += strlen(history[i]) + 2; + } + char *code = malloc(code_size + strlen(expr) + 256); + strcpy(code, ""); + + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, "\n"); } - if (c == '\\') - { - escaped = 1; - continue; + } + strcat(code, "include \"time.h\"\n"); + strcat(code, "fn main() { _z_suppress_stdout();\n"); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, " "); } - if (c == '"') - { - in_quote = !in_quote; - continue; + } + strcat(code, "_z_restore_stdout();\n"); + strcat(code, "raw { clock_t _start = clock(); }\n"); + strcat(code, "for _i in 0..1000 { "); + strcat(code, expr); + strcat(code, "; }\n"); + strcat( + code, + "raw { clock_t _end = clock(); double _elapsed = (double)(_end - " + "_start) / CLOCKS_PER_SEC; printf(\"1000 iterations: %.4fs " + "(%.6fs/iter)\\n\", _elapsed, _elapsed/1000); }\n"); + strcat(code, "}"); + + char tmp_path[256]; + sprintf(tmp_path, "/tmp/zprep_repl_time_%d.zc", rand()); + FILE *f = fopen(tmp_path, "w"); + if (f) { + fprintf(f, "%s", code); + fclose(f); + char cmd[2048]; + sprintf(cmd, "%s run -q %s", self_path, tmp_path); + system(cmd); + } + free(code); + continue; + } else if (0 == strncmp(cmd_buf, ":c ", 3)) { + char *expr_buf = malloc(8192); + strcpy(expr_buf, cmd_buf + 3); + + int brace_depth = 0; + for (char *p = expr_buf; *p; p++) { + if (*p == '{') { + brace_depth++; + } else if (*p == '}') { + brace_depth--; } + } - if (!in_quote) - { - if (c == '{') - { - brace_depth++; - } - if (c == '}') - { - brace_depth--; - } - if (c == '(') - { - paren_depth++; - } - if (c == ')') - { - paren_depth--; - } + while (brace_depth > 0) { + printf("... "); + char more[1024]; + if (!fgets(more, sizeof(more), stdin)) { + break; } + size_t mlen = strlen(more); + if (mlen > 0 && more[mlen - 1] == '\n') { + more[--mlen] = 0; + } + strcat(expr_buf, "\n"); + strcat(expr_buf, more); + for (char *p = more; *p; p++) { + if (*p == '{') { + brace_depth++; + } else if (*p == '}') { + brace_depth--; + } + } + } + + size_t code_size = 4096 + strlen(expr_buf); + for (int i = 0; i < history_len; i++) { + code_size += strlen(history[i]) + 2; + } + char *code = malloc(code_size + 128); + strcpy(code, ""); + + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, "\n"); + } + } + strcat(code, "fn main() {\n"); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, " "); + } + } + strcat(code, expr_buf); + strcat(code, "\n}"); + free(expr_buf); + + char tmp_path[256]; + sprintf(tmp_path, "/tmp/zprep_repl_c_%d.zc", rand()); + FILE *f = fopen(tmp_path, "w"); + if (f) { + fprintf(f, "%s", code); + fclose(f); + char cmd[2048]; + sprintf(cmd, + "%s build -q --emit-c -o /tmp/zprep_repl_out %s " + "2>/dev/null; sed " + "-n '/^int main() {$/,/^}$/p' /tmp/zprep_repl_out.c " + "2>/dev/null | " + "tail -n +3 | head -n -2 | sed 's/^ //'", + self_path, tmp_path); + system(cmd); + } + free(code); + continue; + } else if (0 == strcmp(cmd_buf, ":run")) { + size_t code_size = 4096; + for (int i = 0; i < history_len; i++) { + code_size += strlen(history[i]) + 2; + } + char *code = malloc(code_size); + strcpy(code, ""); + + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(code, history[i]); + strcat(code, "\n"); + } + } + strcat(code, "fn main() {\n"); + for (int i = 0; i < history_len; i++) { + if (!is_header_line(history[i])) { + strcat(code, " "); + strcat(code, history[i]); + strcat(code, "\n"); + } + } + strcat(code, "}\n"); + + char tmp_path[256]; + sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand()); + FILE *f = fopen(tmp_path, "w"); + if (f) { + fprintf(f, "%s", code); + fclose(f); + char cmd[2048]; + sprintf(cmd, "%s run %s", self_path, tmp_path); + system(cmd); + } + free(code); + continue; + } else if (0 == strncmp(cmd_buf, ":doc ", 5)) { + char *sym = cmd_buf + 5; + while (*sym == ' ') { + sym++; + } + size_t symlen = strlen(sym); + while (symlen > 0 && sym[symlen - 1] == ' ') { + sym[--symlen] = 0; + } + + // Documentation database + + struct { + const char *name; + const char *doc; + } docs[] = { + {"Vec", "Vec<T> - Dynamic array (generic)\n Fields: data: T*, " + "len: usize, cap: " + "usize\n Methods: new, push, pop, get, set, insert, " + "remove, contains, " + "clear, free, clone, reverse, first, last, length, " + "is_empty, eq"}, + {"Vec.new", + "fn Vec<T>::new() -> Vec<T>\n Creates an empty vector."}, + {"Vec.push", "fn push(self, item: T)\n Appends item to the end. " + "Auto-grows capacity."}, + {"Vec.pop", + "fn pop(self) -> T\n Removes and returns the last element. " + "Panics if empty."}, + {"Vec.get", + "fn get(self, idx: usize) -> T\n Returns element at index. " + "Panics if out of bounds."}, + {"Vec.set", + "fn set(self, idx: usize, item: T)\n Sets element at index. " + "Panics if out of bounds."}, + {"Vec.insert", + "fn insert(self, idx: usize, item: T)\n Inserts item at " + "index, shifting elements right."}, + {"Vec.remove", + "fn remove(self, idx: usize) -> T\n Removes and returns " + "element at index, shifting elements left."}, + {"Vec.contains", + "fn contains(self, item: T) -> bool\n Returns true if " + "item is in the vector."}, + {"Vec.clear", + "fn clear(self)\n Removes all elements but keeps capacity."}, + {"Vec.free", "fn free(self)\n Frees memory. Sets data to null."}, + {"Vec.clone", + "fn clone(self) -> Vec<T>\n Returns a shallow copy."}, + {"Vec.reverse", + "fn reverse(self)\n Reverses elements in place."}, + {"Vec.first", "fn first(self) -> T\n Returns first element. " + "Panics if empty."}, + {"Vec.last", + "fn last(self) -> T\n Returns last element. Panics if empty."}, + {"Vec.length", + "fn length(self) -> usize\n Returns number of elements."}, + {"Vec.is_empty", + "fn is_empty(self) -> bool\n Returns true if length is 0."}, + {"String", "String - Mutable string (alias for char*)\n " + "Methods: len, split, trim, " + "contains, starts_with, ends_with, to_upper, " + "to_lower, substring, find"}, + {"String.len", "fn len(self) -> usize\n Returns string length."}, + {"String.contains", + "fn contains(self, substr: string) -> bool\n Returns " + "true if string contains substr."}, + {"String.starts_with", + "fn starts_with(self, prefix: string) -> bool\n " + "Returns true if string starts with prefix."}, + {"String.ends_with", + "fn ends_with(self, suffix: string) -> bool\n " + "Returns true if string ends with suffix."}, + {"String.substring", + "fn substring(self, start: usize, len: usize) -> " + "string\n Returns a substring. Caller must free."}, + {"String.find", + "fn find(self, substr: string) -> int\n Returns index of " + "substr, or -1 if not found."}, + {"println", + "println \"format string {expr}\"\n Prints to stdout with " + "newline. Auto-formats {expr} values."}, + {"print", "print \"format string {expr}\"\n Prints to stdout " + "without newline."}, + {"eprintln", + "eprintln \"format string\"\n Prints to stderr with newline."}, + {"eprint", + "eprint \"format string\"\n Prints to stderr without newline."}, + {"guard", "guard condition else action\n Early exit pattern. " + "Executes action if " + "condition is false.\n Example: guard ptr != NULL " + "else return;"}, + {"defer", + "defer statement\n Executes statement at end of scope.\n " + "Example: defer free(ptr);"}, + {"sizeof", + "sizeof(type) or sizeof(expr)\n Returns size in bytes."}, + {"typeof", "typeof(expr)\n Returns the type of expression " + "(compile-time)."}, + {"malloc", + "void *malloc(size_t size)\n Allocates size bytes. Returns " + "pointer or NULL. Free with free()."}, + {"free", "void free(void *ptr)\n Frees memory allocated by " + "malloc/calloc/realloc."}, + {"calloc", + "void *calloc(size_t n, size_t size)\n Allocates n*size bytes, " + "zeroed. Returns pointer or NULL."}, + {"realloc", + "void *realloc(void *ptr, size_t size)\n Resizes allocation " + "to size bytes. May move memory."}, + {"memcpy", + "void *memcpy(void *dest, const void *src, size_t n)\n Copies " + "n bytes from src to dest. Returns dest. No overlap."}, + {"memmove", + "void *memmove(void *dest, const void *src, size_t n)\n " + "Copies n bytes, handles overlapping regions."}, + {"memset", "void *memset(void *s, int c, size_t n)\n Sets n " + "bytes of s to value c."}, + {"strlen", + "size_t strlen(const char *s)\n Returns length of string (not " + "including null terminator)."}, + {"strcpy", + "char *strcpy(char *dest, const char *src)\n Copies src to " + "dest including null terminator. No bounds check."}, + {"strncpy", + "char *strncpy(char *dest, const char *src, size_t n)\n " + "Copies up to n chars. May not null-terminate."}, + {"strcat", "char *strcat(char *dest, const char *src)\n Appends " + "src to dest."}, + {"strcmp", + "int strcmp(const char *s1, const char *s2)\n Compares " + "strings. Returns 0 if equal, <0 or >0 otherwise."}, + {"strncmp", + "int strncmp(const char *s1, const char *s2, size_t n)\n " + "Compares up to n characters."}, + {"strstr", + "char *strstr(const char *haystack, const char *needle)\n " + "Finds first occurrence of needle. Returns pointer or NULL."}, + {"strchr", + "char *strchr(const char *s, int c)\n Finds first occurrence " + "of char c. Returns pointer or NULL."}, + {"strdup", + "char *strdup(const char *s)\n Duplicates string. Caller must " + "free the result."}, + {"printf", + "int printf(const char *fmt, ...)\n Prints formatted output to " + "stdout. Returns chars written."}, + {"sprintf", + "int sprintf(char *str, const char *fmt, ...)\n Prints " + "formatted output to string buffer."}, + {"snprintf", + "int snprintf(char *str, size_t n, const char *fmt, ...)\n " + "Safe sprintf with size limit."}, + {"fprintf", + "int fprintf(FILE *f, const char *fmt, ...)\n Prints " + "formatted output to file stream."}, + {"scanf", "int scanf(const char *fmt, ...)\n Reads formatted " + "input from stdin."}, + {"fopen", + "FILE *fopen(const char *path, const char *mode)\n Opens file. " + "Modes: " + "\"r\", \"w\", \"a\", \"rb\", \"wb\". Returns NULL on error."}, + {"fclose", + "int fclose(FILE *f)\n Closes file. Returns 0 on success."}, + {"fread", + "size_t fread(void *ptr, size_t size, size_t n, FILE *f)\n " + "Reads n items of size bytes. Returns items read."}, + {"fwrite", + "size_t fwrite(const void *ptr, size_t size, size_t n, FILE " + "*f)\n Writes n items of size bytes. Returns items written."}, + {"fgets", + "char *fgets(char *s, int n, FILE *f)\n Reads line up to n-1 " + "chars. Includes newline. Returns s or NULL."}, + {"fputs", + "int fputs(const char *s, FILE *f)\n Writes string to file. " + "Returns non-negative or EOF."}, + {"exit", "void exit(int status)\n Terminates program with " + "status code. 0 " + "= success."}, + {"atoi", "int atoi(const char *s)\n Converts string to int. " + "Returns 0 on error."}, + {"atof", + "double atof(const char *s)\n Converts string to double."}, + {"abs", "int abs(int n)\n Returns absolute value."}, + {"rand", + "int rand(void)\n Returns pseudo-random int in [0, RAND_MAX]."}, + {"srand", "void srand(unsigned seed)\n Seeds the random number " + "generator."}, + {"qsort", + "void qsort(void *base, size_t n, size_t size, int(*cmp)(const " + "void*, const void*))\n Quicksorts array in-place."}, + {NULL, NULL}}; + + int found = 0; + for (int i = 0; docs[i].name != NULL; i++) { + if (0 == strcmp(sym, docs[i].name)) { + printf("\033[1;36m%s\033[0m\n%s\n", docs[i].name, docs[i].doc); + found = 1; + break; + } + } + if (!found) { + // Fallback: try man pages, show only SYNOPSIS. + char man_cmd[256]; + sprintf(man_cmd, + "man 3 %s 2>/dev/null | sed -n '/^SYNOPSIS/,/^[A-Z]/p' | " + "head -10", + sym); + FILE *mp = popen(man_cmd, "r"); + if (mp) { + char buf[256]; + int lines = 0; + while (fgets(buf, sizeof(buf), mp) && lines < 8) { + printf("%s", buf); + lines++; + } + int status = pclose(mp); + if (0 == status && lines > 0) { + found = 1; + printf("\033[90m(man 3 %s)\033[0m\n", sym); + } + } + if (!found) { + printf("No documentation for '%s'.\n", sym); + } + } + continue; + } else { + printf("Unknown command: %s\n", cmd_buf); + continue; } + } + } - size_t len = strlen(line_buf); - input_buffer = realloc(input_buffer, input_len + len + 1); - strcpy(input_buffer + input_len, line_buf); - input_len += len; - - if (brace_depth > 0 || paren_depth > 0) - { - continue; - } - - if (input_len > 0 && input_buffer[input_len - 1] == '\n') - { - input_buffer[--input_len] = 0; - } - - if (input_len == 0) - { - free(input_buffer); - input_buffer = NULL; - input_len = 0; - brace_depth = 0; - paren_depth = 0; - continue; + int in_quote = 0; + int escaped = 0; + for (int i = 0; line_buf[i]; i++) { + char c = line_buf[i]; + if (escaped) { + escaped = 0; + continue; + } + if (c == '\\') { + escaped = 1; + continue; + } + if (c == '"') { + in_quote = !in_quote; + continue; + } + + if (!in_quote) { + if (c == '{') { + brace_depth++; } - - // Add to history. - if (history_len >= history_cap) - { - history_cap *= 2; - history = realloc(history, history_cap * sizeof(char *)); + if (c == '}') { + brace_depth--; } - history[history_len++] = strdup(input_buffer); - - free(input_buffer); - input_buffer = NULL; - input_len = 0; - brace_depth = 0; - paren_depth = 0; - - size_t total_size = 4096; - for (int i = 0; i < history_len; i++) - { - total_size += strlen(history[i]) + 2; + if (c == '(') { + paren_depth++; } - if (watches_len > 0) - { - total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik. + if (c == ')') { + paren_depth--; } + } + } - char *full_code = malloc(total_size); - strcpy(full_code, ""); - - // Hoisting pass. - for (int i = 0; i < history_len; i++) - { - if (is_header_line(history[i])) - { - strcat(full_code, history[i]); - strcat(full_code, "\n"); - } - } + size_t len = strlen(line_buf); + input_buffer = realloc(input_buffer, input_len + len + 1); + strcpy(input_buffer + input_len, line_buf); + input_len += len; - strcat(full_code, "fn main() { _z_suppress_stdout(); "); + if (brace_depth > 0 || paren_depth > 0) { + continue; + } - for (int i = 0; i < history_len - 1; i++) - { - if (is_header_line(history[i])) - { - continue; - } - strcat(full_code, history[i]); - strcat(full_code, " "); - } + if (input_len > 0 && input_buffer[input_len - 1] == '\n') { + input_buffer[--input_len] = 0; + } - strcat(full_code, "_z_restore_stdout(); "); - - if (history_len > 0 && !is_header_line(history[history_len - 1])) - { - char *last_line = history[history_len - 1]; - - char *check_buf = malloc(strlen(last_line) + 2); - strcpy(check_buf, last_line); - strcat(check_buf, ";"); - - ParserContext ctx = {0}; - ctx.is_repl = 1; - ctx.skip_preamble = 1; - Lexer l; - lexer_init(&l, check_buf); - ASTNode *node = parse_statement(&ctx, &l); - free(check_buf); - - int is_expr = 0; - if (node) - { - ASTNode *child = node; - if (child->type == NODE_EXPR_BINARY || child->type == NODE_EXPR_UNARY || - child->type == NODE_EXPR_LITERAL || child->type == NODE_EXPR_VAR || - child->type == NODE_EXPR_CALL || child->type == NODE_EXPR_MEMBER || - child->type == NODE_EXPR_INDEX || child->type == NODE_EXPR_CAST || - child->type == NODE_EXPR_SIZEOF || child->type == NODE_EXPR_STRUCT_INIT || - child->type == NODE_EXPR_ARRAY_LITERAL || child->type == NODE_EXPR_SLICE || - child->type == NODE_TERNARY || child->type == NODE_MATCH) - { - is_expr = 1; - } - } + if (input_len == 0) { + free(input_buffer); + input_buffer = NULL; + input_len = 0; + brace_depth = 0; + paren_depth = 0; + continue; + } - if (is_expr) - { - size_t probesz = 4096; - for (int i = 0; i < history_len - 1; i++) - { - probesz += strlen(history[i]) + 2; - } - char *probe_code = malloc(probesz + strlen(last_line) + 512); - strcpy(probe_code, ""); - - for (int i = 0; i < history_len - 1; i++) - { - if (is_header_line(history[i])) - { - strcat(probe_code, history[i]); - strcat(probe_code, "\n"); - } - } + // Add to history. + if (history_len >= history_cap) { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); + } + history[history_len++] = strdup(input_buffer); - strcat(probe_code, "fn main() { _z_suppress_stdout(); "); + free(input_buffer); + input_buffer = NULL; + input_len = 0; + brace_depth = 0; + paren_depth = 0; - for (int i = 0; i < history_len - 1; i++) - { - if (!is_header_line(history[i])) - { - strcat(probe_code, history[i]); - strcat(probe_code, " "); - } - } + size_t total_size = 4096; + for (int i = 0; i < history_len; i++) { + total_size += strlen(history[i]) + 2; + } + if (watches_len > 0) { + total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik. + } - strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } "); - strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = ("); - strcat(probe_code, last_line); - strcat(probe_code, "); }"); - - char p_path[256]; - sprintf(p_path, "/tmp/zprep_repl_probe_%d.zc", rand()); - FILE *pf = fopen(p_path, "w"); - if (pf) - { - fprintf(pf, "%s", probe_code); - fclose(pf); - - char p_cmd[2048]; - sprintf(p_cmd, "%s run -q %s 2>&1", self_path, p_path); - - FILE *pp = popen(p_cmd, "r"); - int is_void = 0; - if (pp) - { - char buf[1024]; - while (fgets(buf, sizeof(buf), pp)) - { - if (strstr(buf, "void") && strstr(buf, "expression")) - { - is_void = 1; - } - } - pclose(pp); - } + char *full_code = malloc(total_size); + strcpy(full_code, ""); - if (!is_void) - { - strcat(full_code, "println \"{"); - strcat(full_code, last_line); - strcat(full_code, "}\";"); - } - else - { - strcat(full_code, last_line); - } - } - else - { - strcat(full_code, last_line); - } - free(probe_code); - } - else - { - strcat(full_code, last_line); - } - } + // Hoisting pass. + for (int i = 0; i < history_len; i++) { + if (is_header_line(history[i])) { + strcat(full_code, history[i]); + strcat(full_code, "\n"); + } + } - if (watches_len > 0) - { - strcat(full_code, "; "); // separator. - for (int i = 0; i < watches_len; i++) - { - // Use printf for label, then print "{expr}" for value. - char wbuf[1024]; - sprintf(wbuf, - "printf(\"\\033[90mwatch:%s = \\033[0m\"); print \"{%s}\"; " - "printf(\"\\n\"); ", - watches[i], watches[i]); - strcat(full_code, wbuf); - } - } + strcat(full_code, "fn main() { _z_suppress_stdout(); "); - strcat(full_code, " }"); + for (int i = 0; i < history_len - 1; i++) { + if (is_header_line(history[i])) { + continue; + } + strcat(full_code, history[i]); + strcat(full_code, " "); + } - char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_%d.zc", rand()); - FILE *f = fopen(tmp_path, "w"); - if (!f) - { - printf("Error: Cannot write temp file\n"); - free(full_code); - break; + strcat(full_code, "_z_restore_stdout(); "); + + if (history_len > 0 && !is_header_line(history[history_len - 1])) { + char *last_line = history[history_len - 1]; + + char *check_buf = malloc(strlen(last_line) + 2); + strcpy(check_buf, last_line); + strcat(check_buf, ";"); + + ParserContext ctx = {0}; + ctx.is_repl = 1; + ctx.skip_preamble = 1; + Lexer l; + lexer_init(&l, check_buf); + ASTNode *node = parse_statement(&ctx, &l); + free(check_buf); + + int is_expr = 0; + if (node) { + ASTNode *child = node; + if (child->type == NODE_EXPR_BINARY || child->type == NODE_EXPR_UNARY || + child->type == NODE_EXPR_LITERAL || child->type == NODE_EXPR_VAR || + child->type == NODE_EXPR_CALL || child->type == NODE_EXPR_MEMBER || + child->type == NODE_EXPR_INDEX || child->type == NODE_EXPR_CAST || + child->type == NODE_EXPR_SIZEOF || + child->type == NODE_EXPR_STRUCT_INIT || + child->type == NODE_EXPR_ARRAY_LITERAL || + child->type == NODE_EXPR_SLICE || child->type == NODE_TERNARY || + child->type == NODE_MATCH) { + is_expr = 1; } - fprintf(f, "%s", full_code); - fclose(f); - free(full_code); + } - char cmd[2048]; - sprintf(cmd, "%s run -q %s", self_path, tmp_path); + if (is_expr) { + size_t probesz = 4096; + for (int i = 0; i < history_len - 1; i++) { + probesz += strlen(history[i]) + 2; + } + char *probe_code = malloc(probesz + strlen(last_line) + 512); + strcpy(probe_code, ""); + + for (int i = 0; i < history_len - 1; i++) { + if (is_header_line(history[i])) { + strcat(probe_code, history[i]); + strcat(probe_code, "\n"); + } + } - int ret = system(cmd); - printf("\n"); + strcat(probe_code, "fn main() { _z_suppress_stdout(); "); - if (0 != ret) - { - free(history[--history_len]); + for (int i = 0; i < history_len - 1; i++) { + if (!is_header_line(history[i])) { + strcat(probe_code, history[i]); + strcat(probe_code, " "); + } } - } - if (history_path[0]) - { - FILE *hf = fopen(history_path, "w"); - if (hf) - { - for (int i = 0; i < history_len; i++) - { - fprintf(hf, "%s\n", history[i]); + strcat(probe_code, + " raw { typedef struct { int _u; } __REVEAL_TYPE__; } "); + strcat(probe_code, + " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = ("); + strcat(probe_code, last_line); + strcat(probe_code, "); }"); + + char p_path[256]; + sprintf(p_path, "/tmp/zprep_repl_probe_%d.zc", rand()); + FILE *pf = fopen(p_path, "w"); + if (pf) { + fprintf(pf, "%s", probe_code); + fclose(pf); + + char p_cmd[2048]; + sprintf(p_cmd, "%s run -q %s 2>&1", self_path, p_path); + + FILE *pp = popen(p_cmd, "r"); + int is_void = 0; + if (pp) { + char buf[1024]; + while (fgets(buf, sizeof(buf), pp)) { + if (strstr(buf, "void") && strstr(buf, "expression")) { + is_void = 1; + } } - fclose(hf); + pclose(pp); + } + + if (!is_void) { + strcat(full_code, "println \"{"); + strcat(full_code, last_line); + strcat(full_code, "}\";"); + } else { + strcat(full_code, last_line); + } + } else { + strcat(full_code, last_line); } + free(probe_code); + } else { + strcat(full_code, last_line); + } + } + + if (watches_len > 0) { + strcat(full_code, "; "); // separator. + for (int i = 0; i < watches_len; i++) { + // Use printf for label, then print "{expr}" for value. + char wbuf[1024]; + sprintf(wbuf, + "printf(\"\\033[90mwatch:%s = \\033[0m\"); print \"{%s}\"; " + "printf(\"\\n\"); ", + watches[i], watches[i]); + strcat(full_code, wbuf); + } + } + + strcat(full_code, " }"); + + char tmp_path[256]; + sprintf(tmp_path, "/tmp/zprep_repl_%d.zc", rand()); + FILE *f = fopen(tmp_path, "w"); + if (!f) { + printf("Error: Cannot write temp file\n"); + free(full_code); + break; } + fprintf(f, "%s", full_code); + fclose(f); + free(full_code); + + char cmd[2048]; + sprintf(cmd, "%s run -q %s", self_path, tmp_path); + + int ret = system(cmd); + printf("\n"); - for (int i = 0; i < history_len; i++) - { - free(history[i]); + if (0 != ret) { + free(history[--history_len]); } - free(history); - if (input_buffer) - { - free(input_buffer); + } + + if (history_path[0]) { + FILE *hf = fopen(history_path, "w"); + if (hf) { + for (int i = 0; i < history_len; i++) { + fprintf(hf, "%s\n", history[i]); + } + fclose(hf); } + } + + for (int i = 0; i < history_len; i++) { + free(history[i]); + } + free(history); + if (input_buffer) { + free(input_buffer); + } } diff --git a/src/utils/utils.c b/src/utils/utils.c index 66a0a22..cee71d8 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -8,523 +8,476 @@ ParserContext *g_parser_ctx = NULL; // ** Arena Implementation ** #define ARENA_BLOCK_SIZE (1024 * 1024) -typedef struct ArenaBlock -{ - struct ArenaBlock *next; - size_t used; - size_t cap; - char data[]; +typedef struct ArenaBlock { + struct ArenaBlock *next; + size_t used; + size_t cap; + char data[]; } ArenaBlock; static ArenaBlock *current_block = NULL; -static void *arena_alloc_raw(size_t size) -{ - size_t actual_size = size + sizeof(size_t); - actual_size = (actual_size + 7) & ~7; +static void *arena_alloc_raw(size_t size) { + size_t actual_size = size + sizeof(size_t); + actual_size = (actual_size + 7) & ~7; - if (!current_block || (current_block->used + actual_size > current_block->cap)) - { - size_t block_size = actual_size > ARENA_BLOCK_SIZE ? actual_size : ARENA_BLOCK_SIZE; + if (!current_block || + (current_block->used + actual_size > current_block->cap)) { + size_t block_size = + actual_size > ARENA_BLOCK_SIZE ? actual_size : ARENA_BLOCK_SIZE; #undef malloc - ArenaBlock *new_block = malloc(sizeof(ArenaBlock) + block_size); - if (!new_block) - { - fprintf(stderr, "Fatal: Out of memory\n"); - exit(1); - } - - new_block->cap = block_size; - new_block->used = 0; - new_block->next = current_block; - current_block = new_block; - } - - void *ptr = current_block->data + current_block->used; - current_block->used += actual_size; - *(size_t *)ptr = size; - return (char *)ptr + sizeof(size_t); -} - -void *xmalloc(size_t size) -{ - return arena_alloc_raw(size); -} - -void *xcalloc(size_t n, size_t size) -{ - size_t total = n * size; - void *p = arena_alloc_raw(total); - memset(p, 0, total); - return p; -} - -void *xrealloc(void *ptr, size_t new_size) -{ - if (!ptr) - { - return xmalloc(new_size); - } - size_t *header = (size_t *)((char *)ptr - sizeof(size_t)); - size_t old_size = *header; - if (new_size <= old_size) - { - return ptr; - } - void *new_ptr = xmalloc(new_size); - memcpy(new_ptr, ptr, old_size); - return new_ptr; -} - -char *xstrdup(const char *s) -{ - if (!s) - { - return NULL; - } - size_t len = strlen(s); - char *d = xmalloc(len + 1); - memcpy(d, s, len); - d[len] = 0; - return d; -} - -void zpanic(const char *fmt, ...) -{ - va_list a; - va_start(a, fmt); - fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); - vfprintf(stderr, fmt, a); - fprintf(stderr, COLOR_RESET "\n"); - va_end(a); - exit(1); + ArenaBlock *new_block = malloc(sizeof(ArenaBlock) + block_size); + if (!new_block) { + fprintf(stderr, "Fatal: Out of memory\n"); + exit(1); + } + + new_block->cap = block_size; + new_block->used = 0; + new_block->next = current_block; + current_block = new_block; + } + + void *ptr = current_block->data + current_block->used; + current_block->used += actual_size; + *(size_t *)ptr = size; + return (char *)ptr + sizeof(size_t); +} + +void *xmalloc(size_t size) { return arena_alloc_raw(size); } + +void *xcalloc(size_t n, size_t size) { + size_t total = n * size; + void *p = arena_alloc_raw(total); + memset(p, 0, total); + return p; +} + +void *xrealloc(void *ptr, size_t new_size) { + if (!ptr) { + return xmalloc(new_size); + } + size_t *header = (size_t *)((char *)ptr - sizeof(size_t)); + size_t old_size = *header; + if (new_size <= old_size) { + return ptr; + } + void *new_ptr = xmalloc(new_size); + memcpy(new_ptr, ptr, old_size); + return new_ptr; +} + +char *xstrdup(const char *s) { + if (!s) { + return NULL; + } + size_t len = strlen(s); + char *d = xmalloc(len + 1); + memcpy(d, s, len); + d[len] = 0; + return d; +} + +void zpanic(const char *fmt, ...) { + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + exit(1); } // Warning system (non-fatal). -void zwarn(const char *fmt, ...) -{ - if (g_config.quiet) - { - return; - } - g_warning_count++; - va_list a; - va_start(a, fmt); - fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); - vfprintf(stderr, fmt, a); - fprintf(stderr, COLOR_RESET "\n"); - va_end(a); -} - -void zwarn_at(Token t, const char *fmt, ...) -{ - if (g_config.quiet) - { - return; - } - // Header: 'warning: message'. - g_warning_count++; - va_list a; - va_start(a, fmt); - fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); - vfprintf(stderr, fmt, a); - fprintf(stderr, COLOR_RESET "\n"); - va_end(a); - - // Location. - fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, - t.col); - - // Context. Only if token has valid data. - if (t.start) - { - const char *line_start = t.start - (t.col - 1); - const char *line_end = t.start; - while (*line_end && *line_end != '\n') - { - line_end++; - } - int line_len = line_end - line_start; - - fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); - fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); - - // Caret. - for (int i = 0; i < t.col - 1; i++) - { - fprintf(stderr, " "); - } - fprintf(stderr, COLOR_YELLOW "^ here" COLOR_RESET "\n"); - fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - } -} - -void zpanic_at(Token t, const char *fmt, ...) -{ - // Header: 'error: message'. - va_list a; - va_start(a, fmt); - fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); - vfprintf(stderr, fmt, a); - fprintf(stderr, COLOR_RESET "\n"); - va_end(a); - - // Location: '--> file:line:col'. - fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, - t.col); - - // Context line. +void zwarn(const char *fmt, ...) { + if (g_config.quiet) { + return; + } + g_warning_count++; + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); +} + +void zwarn_at(Token t, const char *fmt, ...) { + if (g_config.quiet) { + return; + } + // Header: 'warning: message'. + g_warning_count++; + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + + // Location. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", + g_current_filename, t.line, t.col); + + // Context. Only if token has valid data. + if (t.start) { const char *line_start = t.start - (t.col - 1); const char *line_end = t.start; - while (*line_end && *line_end != '\n') - { - line_end++; + while (*line_end && *line_end != '\n') { + line_end++; } int line_len = line_end - line_start; - // Visual bar. fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, + line_start); fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); - // caret - for (int i = 0; i < t.col - 1; i++) - { - fprintf(stderr, " "); + // Caret. + for (int i = 0; i < t.col - 1; i++) { + fprintf(stderr, " "); } - fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); + fprintf(stderr, COLOR_YELLOW "^ here" COLOR_RESET "\n"); fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - - if (g_parser_ctx && g_parser_ctx->is_fault_tolerant && g_parser_ctx->on_error) - { - // Construct error message buffer - char msg[1024]; - va_list args2; - va_start(args2, fmt); - vsnprintf(msg, sizeof(msg), fmt, args2); - va_end(args2); - - g_parser_ctx->on_error(g_parser_ctx->error_callback_data, t, msg); - return; // Recover! - } - - exit(1); + } +} + +void zpanic_at(Token t, const char *fmt, ...) { + // Header: 'error: message'. + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + + // Location: '--> file:line:col'. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", + g_current_filename, t.line, t.col); + + // Context line. + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && *line_end != '\n') { + line_end++; + } + int line_len = line_end - line_start; + + // Visual bar. + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, + line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + + // caret + for (int i = 0; i < t.col - 1; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + + if (g_parser_ctx && g_parser_ctx->is_fault_tolerant && + g_parser_ctx->on_error) { + // Construct error message buffer + char msg[1024]; + va_list args2; + va_start(args2, fmt); + vsnprintf(msg, sizeof(msg), fmt, args2); + va_end(args2); + + g_parser_ctx->on_error(g_parser_ctx->error_callback_data, t, msg); + return; // Recover! + } + + exit(1); } // Enhanced error with suggestion. -void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion) -{ - // Header. - fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", msg); - - // Location. - fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, - t.col); - - // Context. - const char *line_start = t.start - (t.col - 1); - const char *line_end = t.start; - while (*line_end && *line_end != '\n') - { - line_end++; - } - int line_len = line_end - line_start; - +void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion) { + // Header. + fprintf(stderr, + COLOR_RED "error: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", + msg); + + // Location. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", + g_current_filename, t.line, t.col); + + // Context. + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && *line_end != '\n') { + line_end++; + } + int line_len = line_end - line_start; + + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, + line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + for (int i = 0; i < t.col - 1; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); + + // Suggestion. + if (suggestion) { fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); - fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); - for (int i = 0; i < t.col - 1; i++) - { - fprintf(stderr, " "); - } - fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); - - // Suggestion. - if (suggestion) - { - fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - fprintf(stderr, COLOR_CYAN " = help: " COLOR_RESET "%s\n", suggestion); - } + fprintf(stderr, COLOR_CYAN " = help: " COLOR_RESET "%s\n", suggestion); + } - exit(1); + exit(1); } // Specific error types with helpful messages. -void error_undefined_function(Token t, const char *func_name, const char *suggestion) -{ - char msg[256]; - sprintf(msg, "Undefined function '%s'", func_name); - - if (suggestion) - { - char help[512]; - sprintf(help, "Did you mean '%s'?", suggestion); - zpanic_with_suggestion(t, msg, help); - } - else - { - zpanic_with_suggestion(t, msg, "Check if the function is defined or imported"); - } -} - -void error_wrong_arg_count(Token t, const char *func_name, int expected, int got) -{ - char msg[256]; - sprintf(msg, "Wrong number of arguments to function '%s'", func_name); - - char help[256]; - sprintf(help, "Expected %d argument%s, but got %d", expected, expected == 1 ? "" : "s", got); - - zpanic_with_suggestion(t, msg, help); -} - -void error_undefined_field(Token t, const char *struct_name, const char *field_name, - const char *suggestion) -{ - char msg[256]; - sprintf(msg, "Struct '%s' has no field '%s'", struct_name, field_name); - - if (suggestion) - { - char help[256]; - sprintf(help, "Did you mean '%s'?", suggestion); - zpanic_with_suggestion(t, msg, help); - } - else - { - zpanic_with_suggestion(t, msg, "Check the struct definition"); - } -} - -void error_type_expected(Token t, const char *expected, const char *got) -{ - char msg[256]; - sprintf(msg, "Type mismatch"); +void error_undefined_function(Token t, const char *func_name, + const char *suggestion) { + char msg[256]; + sprintf(msg, "Undefined function '%s'", func_name); + if (suggestion) { char help[512]; - sprintf(help, "Expected type '%s', but found '%s'", expected, got); - + sprintf(help, "Did you mean '%s'?", suggestion); zpanic_with_suggestion(t, msg, help); + } else { + zpanic_with_suggestion(t, msg, + "Check if the function is defined or imported"); + } } -void error_cannot_index(Token t, const char *type_name) -{ - char msg[256]; - sprintf(msg, "Cannot index into type '%s'", type_name); +void error_wrong_arg_count(Token t, const char *func_name, int expected, + int got) { + char msg[256]; + sprintf(msg, "Wrong number of arguments to function '%s'", func_name); - zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed"); -} - -void warn_unused_variable(Token t, const char *var_name) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Unused variable '%s'", var_name); - zwarn_at(t, "%s", msg); - fprintf(stderr, - COLOR_CYAN " = note: " COLOR_RESET "Consider removing it or prefixing with '_'\n"); -} + char help[256]; + sprintf(help, "Expected %d argument%s, but got %d", expected, + expected == 1 ? "" : "s", got); -void warn_shadowing(Token t, const char *var_name) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Variable '%s' shadows a previous declaration", var_name); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This can lead to confusion\n"); + zpanic_with_suggestion(t, msg, help); } -void warn_unreachable_code(Token t) -{ - if (g_config.quiet) - { - return; - } - zwarn_at(t, "Unreachable code detected"); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This code will never execute\n"); -} +void error_undefined_field(Token t, const char *struct_name, + const char *field_name, const char *suggestion) { + char msg[256]; + sprintf(msg, "Struct '%s' has no field '%s'", struct_name, field_name); -void warn_implicit_conversion(Token t, const char *from_type, const char *to_type) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Implicit conversion from '%s' to '%s'", from_type, to_type); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Consider using an explicit cast\n"); -} - -void warn_missing_return(Token t, const char *func_name) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Function '%s' may not return a value in all paths", func_name); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET - "Add a return statement or make the function return 'void'\n"); -} - -void warn_comparison_always_true(Token t, const char *reason) -{ - if (g_config.quiet) - { - return; - } - zwarn_at(t, "Comparison is always true"); - if (reason) - { - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); - } -} - -void warn_comparison_always_false(Token t, const char *reason) -{ - if (g_config.quiet) - { - return; - } - zwarn_at(t, "Comparison is always false"); - if (reason) - { - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); - } -} - -void warn_unused_parameter(Token t, const char *param_name, const char *func_name) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Unused parameter '%s' in function '%s'", param_name, func_name); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET - "Consider prefixing with '_' if intentionally unused\n"); -} - -void warn_narrowing_conversion(Token t, const char *from_type, const char *to_type) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Narrowing conversion from '%s' to '%s'", from_type, to_type); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This may cause data loss\n"); -} - -void warn_division_by_zero(Token t) -{ - if (g_config.quiet) - { - return; - } - zwarn_at(t, "Division by zero"); - fprintf(stderr, - COLOR_CYAN " = note: " COLOR_RESET "This will cause undefined behavior at runtime\n"); -} - -void warn_integer_overflow(Token t, const char *type_name, long long value) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Integer literal %lld overflows type '%s'", value, type_name); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Value will be truncated\n"); -} - -void warn_array_bounds(Token t, int index, int size) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Array index %d is out of bounds for array of size %d", index, size); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Valid indices are 0 to %d\n", size - 1); -} - -void warn_format_string(Token t, int arg_num, const char *expected, const char *got) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Format argument %d: expected '%s', got '%s'", arg_num, expected, got); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET - "Mismatched format specifier may cause undefined behavior\n"); -} - -void warn_null_pointer(Token t, const char *expr) -{ - if (g_config.quiet) - { - return; - } - char msg[256]; - sprintf(msg, "Potential null pointer access in '%s'", expr); - zwarn_at(t, "%s", msg); - fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Add a null check before accessing\n"); -} - -char *load_file(const char *fn) -{ - FILE *f = fopen(fn, "rb"); - if (!f) - { - char *root = getenv("ZC_ROOT"); - if (root) - { - char path[1024]; - snprintf(path, sizeof(path), "%s/%s", root, fn); - f = fopen(path, "rb"); - } - } - if (!f) - { - char path[1024]; - snprintf(path, sizeof(path), "/usr/local/share/zenc/%s", fn); - f = fopen(path, "rb"); - } - if (!f) - { - char path[1024]; - snprintf(path, sizeof(path), "/usr/share/zenc/%s", fn); - f = fopen(path, "rb"); - } - - if (!f) - { - return 0; - } - fseek(f, 0, SEEK_END); - long l = ftell(f); - rewind(f); - char *b = xmalloc(l + 1); - fread(b, 1, l, f); - b[l] = 0; - fclose(f); - return b; + if (suggestion) { + char help[256]; + sprintf(help, "Did you mean '%s'?", suggestion); + zpanic_with_suggestion(t, msg, help); + } else { + zpanic_with_suggestion(t, msg, "Check the struct definition"); + } +} + +void error_type_expected(Token t, const char *expected, const char *got) { + char msg[256]; + sprintf(msg, "Type mismatch"); + + char help[512]; + sprintf(help, "Expected type '%s', but found '%s'", expected, got); + + zpanic_with_suggestion(t, msg, help); +} + +void error_cannot_index(Token t, const char *type_name) { + char msg[256]; + sprintf(msg, "Cannot index into type '%s'", type_name); + + zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed"); +} + +void warn_unused_variable(Token t, const char *var_name) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Unused variable '%s'", var_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Consider removing it or prefixing with '_'\n"); +} + +void warn_shadowing(Token t, const char *var_name) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Variable '%s' shadows a previous declaration", var_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "This can lead to confusion\n"); +} + +void warn_unreachable_code(Token t) { + if (g_config.quiet) { + return; + } + zwarn_at(t, "Unreachable code detected"); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "This code will never execute\n"); +} + +void warn_implicit_conversion(Token t, const char *from_type, + const char *to_type) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Implicit conversion from '%s' to '%s'", from_type, to_type); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Consider using an explicit cast\n"); +} + +void warn_missing_return(Token t, const char *func_name) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Function '%s' may not return a value in all paths", func_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN + " = note: " COLOR_RESET + "Add a return statement or make the function return 'void'\n"); +} + +void warn_comparison_always_true(Token t, const char *reason) { + if (g_config.quiet) { + return; + } + zwarn_at(t, "Comparison is always true"); + if (reason) { + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); + } +} + +void warn_comparison_always_false(Token t, const char *reason) { + if (g_config.quiet) { + return; + } + zwarn_at(t, "Comparison is always false"); + if (reason) { + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); + } +} + +void warn_unused_parameter(Token t, const char *param_name, + const char *func_name) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Unused parameter '%s' in function '%s'", param_name, func_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET + "Consider prefixing with '_' if intentionally unused\n"); +} + +void warn_narrowing_conversion(Token t, const char *from_type, + const char *to_type) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Narrowing conversion from '%s' to '%s'", from_type, to_type); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "This may cause data loss\n"); +} + +void warn_division_by_zero(Token t) { + if (g_config.quiet) { + return; + } + zwarn_at(t, "Division by zero"); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "This will cause undefined behavior at runtime\n"); +} + +void warn_integer_overflow(Token t, const char *type_name, long long value) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Integer literal %lld overflows type '%s'", value, type_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "Value will be truncated\n"); +} + +void warn_array_bounds(Token t, int index, int size) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Array index %d is out of bounds for array of size %d", index, + size); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "Valid indices are 0 to %d\n", + size - 1); +} + +void warn_format_string(Token t, int arg_num, const char *expected, + const char *got) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Format argument %d: expected '%s', got '%s'", arg_num, expected, + got); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN + " = note: " COLOR_RESET + "Mismatched format specifier may cause undefined behavior\n"); +} + +void warn_null_pointer(Token t, const char *expr) { + if (g_config.quiet) { + return; + } + char msg[256]; + sprintf(msg, "Potential null pointer access in '%s'", expr); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Add a null check before accessing\n"); +} + +char *load_file(const char *fn) { + FILE *f = fopen(fn, "rb"); + if (!f) { + char *root = getenv("ZC_ROOT"); + if (root) { + char path[1024]; + snprintf(path, sizeof(path), "%s/%s", root, fn); + f = fopen(path, "rb"); + } + } + if (!f) { + char path[1024]; + snprintf(path, sizeof(path), "/usr/local/share/zenc/%s", fn); + f = fopen(path, "rb"); + } + if (!f) { + char path[1024]; + snprintf(path, sizeof(path), "/usr/share/zenc/%s", fn); + f = fopen(path, "rb"); + } + + if (!f) { + return 0; + } + fseek(f, 0, SEEK_END); + long l = ftell(f); + rewind(f); + char *b = xmalloc(l + 1); + fread(b, 1, l, f); + b[l] = 0; + fclose(f); + return b; } // ** Build Directives ** @@ -533,259 +486,202 @@ char g_cflags[MAX_FLAGS_SIZE] = ""; int g_warning_count = 0; CompilerConfig g_config = {0}; -void scan_build_directives(ParserContext *ctx, const char *src) -{ - const char *p = src; - while (*p) - { - if (p[0] == '/' && p[1] == '/' && p[2] == '>') - { - p += 3; - while (*p == ' ') - { - p++; - } - - const char *start = p; - int len = 0; - while (p[len] && p[len] != '\n') - { - len++; - } - - char line[2048]; - if (len >= 2047) - { - len = 2047; - } - strncpy(line, start, len); - line[len] = 0; - - if (0 == strncmp(line, "link:", 5)) - { - char *val = line + 5; - while (*val == ' ') - { - val++; - } - if (strlen(g_link_flags) > 0) - { - strcat(g_link_flags, " "); - } - strcat(g_link_flags, val); - } - else if (0 == strncmp(line, "cflags:", 7)) - { - char *val = line + 7; - while (*val == ' ') - { - val++; - } - if (strlen(g_cflags) > 0) - { - strcat(g_cflags, " "); - } - strcat(g_cflags, val); - } - else if (0 == strncmp(line, "include:", 8)) - { - char *val = line + 8; - while (*val == ' ') - { - val++; - } - char flags[2048]; - sprintf(flags, "-I%s", val); - if (strlen(g_cflags) > 0) - { - strcat(g_cflags, " "); - } - strcat(g_cflags, flags); - } - else if (strncmp(line, "lib:", 4) == 0) - { - char *val = line + 4; - while (*val == ' ') - { - val++; - } - char flags[2048]; - sprintf(flags, "-L%s", val); - if (strlen(g_link_flags) > 0) - { - strcat(g_link_flags, " "); - } - strcat(g_link_flags, flags); - } - else if (strncmp(line, "define:", 7) == 0) - { - char *val = line + 7; - while (*val == ' ') - { - val++; - } - char flags[2048]; - sprintf(flags, "-D%s", val); - if (strlen(g_cflags) > 0) - { - strcat(g_cflags, " "); - } - strcat(g_cflags, flags); - } - else if (0 == strncmp(line, "shell:", 6)) - { - char *cmd = line + 6; - // printf("[zprep] Running shell: %s\n", cmd); - int res = system(cmd); - if (res != 0) - { - zpanic("Shell directive failed: %s", cmd); - } - } - else if (strncmp(line, "get:", 4) == 0) - { - char *url = line + 4; - while (*url == ' ') - { - url++; - } - - char *filename = strrchr(url, '/'); - if (!filename) - { - filename = "downloaded_file"; - } - else - { - filename++; - } - - // Check if file exists to avoid redownloading. - FILE *f = fopen(filename, "r"); - if (f) - { - fclose(f); - } - else - { - printf("[zprep] Downloading %s...\n", filename); - char cmd[8192]; - // Try wget, then curl. - sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url, - filename, url, filename); - if (system(cmd) != 0) - { - zpanic("Failed to download %s", url); - } - } - } - else if (strncmp(line, "pkg-config:", 11) == 0) - { - char *libs = line + 11; - while (*libs == ' ') - { - libs++; - } - - char cmd[4096]; - sprintf(cmd, "pkg-config --cflags %s", libs); - FILE *fp = popen(cmd, "r"); - if (fp) - { - char flags[4096]; - flags[0] = 0; - fgets(flags, sizeof(flags), fp); - pclose(fp); - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') - { - flags[len - 1] = 0; - } - if (strlen(g_cflags) > 0) - { - strcat(g_cflags, " "); - } - strcat(g_cflags, flags); - } - - sprintf(cmd, "pkg-config --libs %s", libs); - fp = popen(cmd, "r"); - if (fp) - { - char flags[4096]; - flags[0] = 0; - fgets(flags, sizeof(flags), fp); - pclose(fp); - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') - { - flags[len - 1] = 0; - } - if (strlen(g_link_flags) > 0) - { - strcat(g_link_flags, " "); - } - strcat(g_link_flags, flags); - } - } - else if (strncmp(line, "immutable-by-default", 20) == 0) - { - ctx->immutable_by_default = 1; - } - - p += len; +void scan_build_directives(ParserContext *ctx, const char *src) { + const char *p = src; + while (*p) { + if (p[0] == '/' && p[1] == '/' && p[2] == '>') { + p += 3; + while (*p == ' ') { + p++; + } + + const char *start = p; + int len = 0; + while (p[len] && p[len] != '\n') { + len++; + } + + char line[2048]; + if (len >= 2047) { + len = 2047; + } + strncpy(line, start, len); + line[len] = 0; + + if (0 == strncmp(line, "link:", 5)) { + char *val = line + 5; + while (*val == ' ') { + val++; + } + if (strlen(g_link_flags) > 0) { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, val); + } else if (0 == strncmp(line, "cflags:", 7)) { + char *val = line + 7; + while (*val == ' ') { + val++; + } + if (strlen(g_cflags) > 0) { + strcat(g_cflags, " "); + } + strcat(g_cflags, val); + } else if (0 == strncmp(line, "include:", 8)) { + char *val = line + 8; + while (*val == ' ') { + val++; + } + char flags[2048]; + sprintf(flags, "-I%s", val); + if (strlen(g_cflags) > 0) { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } else if (strncmp(line, "lib:", 4) == 0) { + char *val = line + 4; + while (*val == ' ') { + val++; } - while (*p && *p != '\n') - { - p++; + char flags[2048]; + sprintf(flags, "-L%s", val); + if (strlen(g_link_flags) > 0) { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, flags); + } else if (strncmp(line, "define:", 7) == 0) { + char *val = line + 7; + while (*val == ' ') { + val++; + } + char flags[2048]; + sprintf(flags, "-D%s", val); + if (strlen(g_cflags) > 0) { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } else if (0 == strncmp(line, "shell:", 6)) { + char *cmd = line + 6; + // printf("[zprep] Running shell: %s\n", cmd); + int res = system(cmd); + if (res != 0) { + zpanic("Shell directive failed: %s", cmd); + } + } else if (strncmp(line, "get:", 4) == 0) { + char *url = line + 4; + while (*url == ' ') { + url++; } - if (*p == '\n') - { - p++; + char *filename = strrchr(url, '/'); + if (!filename) { + filename = "downloaded_file"; + } else { + filename++; } - } -} -// Levenshtein distance for "did you mean?" suggestions. -int levenshtein(const char *s1, const char *s2) -{ - int len1 = strlen(s1); - int len2 = strlen(s2); - - // Quick optimization. - if (abs(len1 - len2) > 3) - { - return 999; - } + // Check if file exists to avoid redownloading. + FILE *f = fopen(filename, "r"); + if (f) { + fclose(f); + } else { + printf("[zprep] Downloading %s...\n", filename); + char cmd[8192]; + // Try wget, then curl. + sprintf(cmd, + "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", + url, filename, url, filename); + if (system(cmd) != 0) { + zpanic("Failed to download %s", url); + } + } + } else if (strncmp(line, "pkg-config:", 11) == 0) { + char *libs = line + 11; + while (*libs == ' ') { + libs++; + } + + char cmd[4096]; + sprintf(cmd, "pkg-config --cflags %s", libs); + FILE *fp = popen(cmd, "r"); + if (fp) { + char flags[4096]; + flags[0] = 0; + fgets(flags, sizeof(flags), fp); + pclose(fp); + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') { + flags[len - 1] = 0; + } + if (strlen(g_cflags) > 0) { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } - int matrix[len1 + 1][len2 + 1]; + sprintf(cmd, "pkg-config --libs %s", libs); + fp = popen(cmd, "r"); + if (fp) { + char flags[4096]; + flags[0] = 0; + fgets(flags, sizeof(flags), fp); + pclose(fp); + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') { + flags[len - 1] = 0; + } + if (strlen(g_link_flags) > 0) { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, flags); + } + } else if (strncmp(line, "immutable-by-default", 20) == 0) { + ctx->immutable_by_default = 1; + } - for (int i = 0; i <= len1; i++) - { - matrix[i][0] = i; + p += len; } - for (int j = 0; j <= len2; j++) - { - matrix[0][j] = j; + while (*p && *p != '\n') { + p++; } - for (int i = 1; i <= len1; i++) - { - for (int j = 1; j <= len2; j++) - { - int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; - int del = matrix[i - 1][j] + 1; - int ins = matrix[i][j - 1] + 1; - int sub = matrix[i - 1][j - 1] + cost; - - matrix[i][j] = (del < ins) ? del : ins; - if (sub < matrix[i][j]) - { - matrix[i][j] = sub; - } - } + if (*p == '\n') { + p++; } + } +} - return matrix[len1][len2]; +// Levenshtein distance for "did you mean?" suggestions. +int levenshtein(const char *s1, const char *s2) { + int len1 = strlen(s1); + int len2 = strlen(s2); + + // Quick optimization. + if (abs(len1 - len2) > 3) { + return 999; + } + + int matrix[len1 + 1][len2 + 1]; + + for (int i = 0; i <= len1; i++) { + matrix[i][0] = i; + } + for (int j = 0; j <= len2; j++) { + matrix[0][j] = j; + } + + for (int i = 1; i <= len1; i++) { + for (int j = 1; j <= len2; j++) { + int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; + int del = matrix[i - 1][j] + 1; + int ins = matrix[i][j - 1] + 1; + int sub = matrix[i - 1][j - 1] + cost; + + matrix[i][j] = (del < ins) ? del : ins; + if (sub < matrix[i][j]) { + matrix[i][j] = sub; + } + } + } + + return matrix[len1][len2]; } diff --git a/src/zen/zen_facts.c b/src/zen/zen_facts.c index a86e0cb..f012d8e 100644 --- a/src/zen/zen_facts.c +++ b/src/zen/zen_facts.c @@ -8,11 +8,10 @@ // We keep it low by default. #define ZEN_PROBABILITY 10 -typedef struct -{ - ZenTrigger trigger; - const char *message; - const char *url; +typedef struct { + ZenTrigger trigger; + const char *message; + const char *url; } ZenFact; static const ZenFact facts[] = { @@ -37,7 +36,8 @@ static const ZenFact facts[] = { "`sizeof(*ptr)` bytes.", NULL}, - {TRIGGER_BITWISE, "Use `(x & (x - 1)) == 0` to check if an integer is a power of two.", + {TRIGGER_BITWISE, + "Use `(x & (x - 1)) == 0` to check if an integer is a power of two.", "https://graphics.stanford.edu/~seander/bithacks.html"}, {TRIGGER_BITWISE, "XOR swap algorithm: `x ^= y; y ^= x; x ^= y;` swaps variables without a " @@ -45,7 +45,8 @@ static const ZenFact facts[] = { "optimized code is usually faster).", NULL}, - {TRIGGER_RECURSION, "To understand recursion, you must first understand recursion.", NULL}, + {TRIGGER_RECURSION, + "To understand recursion, you must first understand recursion.", NULL}, {TRIGGER_RECURSION, "Tail Call Optimization (TCO) allows some recursive calls to consume no " "additional stack " @@ -194,7 +195,8 @@ static const ZenFact facts[] = { "The `#` stringification operator in macros turns arguments into string " "literals.", NULL}, - {TRIGGER_GLOBAL, "The `##` token-pasting operator concatenates tokens in macro expansions.", + {TRIGGER_GLOBAL, + "The `##` token-pasting operator concatenates tokens in macro expansions.", NULL}, {TRIGGER_GLOBAL, "Flexible array members: `int data[];` at struct end allows variable-size " @@ -276,7 +278,9 @@ static const ZenFact facts[] = { "B, which was " "typeless.", NULL}, - {TRIGGER_GLOBAL, "The name 'C' simply comes from being the successor to the B language.", NULL}, + {TRIGGER_GLOBAL, + "The name 'C' simply comes from being the successor to the B language.", + NULL}, {TRIGGER_GLOBAL, "Plan 9 C allows `structure.member` notation even if `member` is inside " "an anonymous inner " @@ -359,152 +363,129 @@ static const ZenFact facts[] = { static int fact_count = sizeof(facts) / sizeof(ZenFact); static int has_triggered = 0; -void zen_init(void) -{ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - srand(ts.tv_nsec ^ getpid()); +void zen_init(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + srand(ts.tv_nsec ^ getpid()); } // Global helper to print. -void zzen_at(Token t, const char *msg, const char *url) -{ - fprintf(stderr, COLOR_GREEN "zen: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", msg); - - extern char *g_current_filename; - if (t.line > 0) - { - fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", - g_current_filename ? g_current_filename : "unknown", t.line, t.col); +void zzen_at(Token t, const char *msg, const char *url) { + fprintf(stderr, + COLOR_GREEN "zen: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", + msg); + + extern char *g_current_filename; + if (t.line > 0) { + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", + g_current_filename ? g_current_filename : "unknown", t.line, t.col); + } + + if (t.start) { + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && '\n' != *line_end) { + line_end++; } - - if (t.start) - { - const char *line_start = t.start - (t.col - 1); - const char *line_end = t.start; - while (*line_end && '\n' != *line_end) - { - line_end++; - } - int line_len = line_end - line_start; - - fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); - fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); - fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); - for (int i = 0; i < t.col - 1; i++) - { - fprintf(stderr, " "); - } - fprintf(stderr, COLOR_GREEN "^ zen tip" COLOR_RESET "\n"); + int line_len = line_end - line_start; + + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, + line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + for (int i = 0; i < t.col - 1; i++) { + fprintf(stderr, " "); } + fprintf(stderr, COLOR_GREEN "^ zen tip" COLOR_RESET "\n"); + } - if (url) - { - fprintf(stderr, COLOR_CYAN " = read more: %s" COLOR_RESET "\n", url); - } + if (url) { + fprintf(stderr, COLOR_CYAN " = read more: %s" COLOR_RESET "\n", url); + } } -int zen_trigger_at(ZenTrigger t, Token location) -{ - if (g_config.quiet) - { - return 0; +int zen_trigger_at(ZenTrigger t, Token location) { + if (g_config.quiet) { + return 0; + } + + if (has_triggered) { + return 0; + } + + extern int g_warning_count; + if (g_warning_count > 0) { + return 0; + } + + if ((rand() % 100) >= ZEN_PROBABILITY) { + return 0; + } + + int matches[10]; + int match_count = 0; + + for (int i = 0; i < fact_count; i++) { + if (facts[i].trigger == t) { + matches[match_count++] = i; + if (match_count >= 10) { + break; + } } + } - if (has_triggered) - { - return 0; - } - - extern int g_warning_count; - if (g_warning_count > 0) - { - return 0; - } + if (0 == match_count) { + return 0; + } - if ((rand() % 100) >= ZEN_PROBABILITY) - { - return 0; - } - - int matches[10]; - int match_count = 0; - - for (int i = 0; i < fact_count; i++) - { - if (facts[i].trigger == t) - { - matches[match_count++] = i; - if (match_count >= 10) - { - break; - } - } - } + int pick = matches[rand() % match_count]; + const ZenFact *f = &facts[pick]; - if (0 == match_count) - { - return 0; - } - - int pick = matches[rand() % match_count]; - const ZenFact *f = &facts[pick]; - - zzen_at(location, f->message, f->url); - has_triggered = 1; - return 1; + zzen_at(location, f->message, f->url); + has_triggered = 1; + return 1; } -void zen_trigger_global(void) -{ - if (g_config.quiet) - { - return; - } - if (!isatty(STDERR_FILENO)) - { - return; - } - if (has_triggered) - { - return; - } - - extern int g_warning_count; - if (g_warning_count > 0) - { - return; - } - - if ((rand() % 100) >= ZEN_PROBABILITY) - { - return; +void zen_trigger_global(void) { + if (g_config.quiet) { + return; + } + if (!isatty(STDERR_FILENO)) { + return; + } + if (has_triggered) { + return; + } + + extern int g_warning_count; + if (g_warning_count > 0) { + return; + } + + if ((rand() % 100) >= ZEN_PROBABILITY) { + return; + } + + int matches[10]; + int match_count = 0; + + for (int i = 0; i < fact_count; i++) { + if (TRIGGER_GLOBAL == facts[i].trigger) { + matches[match_count++] = i; + if (match_count >= 10) { + break; + } } + } - int matches[10]; - int match_count = 0; - - for (int i = 0; i < fact_count; i++) - { - if (TRIGGER_GLOBAL == facts[i].trigger) - { - matches[match_count++] = i; - if (match_count >= 10) - { - break; - } - } - } - - if (0 == match_count) - { - return; - } + if (0 == match_count) { + return; + } - int pick = matches[rand() % match_count]; - const ZenFact *f = &facts[pick]; + int pick = matches[rand() % match_count]; + const ZenFact *f = &facts[pick]; - Token empty = {0}; - zzen_at(empty, f->message, f->url); - has_triggered = 1; + Token empty = {0}; + zzen_at(empty, f->message, f->url); + has_triggered = 1; } diff --git a/src/zen/zen_facts.h b/src/zen/zen_facts.h index ce5b952..18a810d 100644 --- a/src/zen/zen_facts.h +++ b/src/zen/zen_facts.h @@ -4,21 +4,20 @@ #include "../zprep.h" -typedef enum -{ - TRIGGER_GOTO, - TRIGGER_POINTER_ARITH, - TRIGGER_BITWISE, - TRIGGER_RECURSION, - TRIGGER_TERNARY, - TRIGGER_ASM, - TRIGGER_WHILE_TRUE, - TRIGGER_MACRO, - TRIGGER_VOID_PTR, - TRIGGER_MAIN, - TRIGGER_FORMAT_STRING, - TRIGGER_STRUCT_PADDING, - TRIGGER_GLOBAL +typedef enum { + TRIGGER_GOTO, + TRIGGER_POINTER_ARITH, + TRIGGER_BITWISE, + TRIGGER_RECURSION, + TRIGGER_TERNARY, + TRIGGER_ASM, + TRIGGER_WHILE_TRUE, + TRIGGER_MACRO, + TRIGGER_VOID_PTR, + TRIGGER_MAIN, + TRIGGER_FORMAT_STRING, + TRIGGER_STRUCT_PADDING, + TRIGGER_GLOBAL } ZenTrigger; void zen_init(void); diff --git a/src/zprep.h b/src/zprep.h index 75d9cac..5808ac4 100644 --- a/src/zprep.h +++ b/src/zprep.h @@ -29,75 +29,72 @@ // ** GLOBAL STATE ** extern char *g_current_filename; -typedef enum -{ - TOK_EOF = 0, - TOK_IDENT, - TOK_INT, - TOK_FLOAT, - TOK_STRING, - TOK_FSTRING, - TOK_CHAR, - TOK_LPAREN, - TOK_RPAREN, - TOK_LBRACE, - TOK_RBRACE, - TOK_LBRACKET, - TOK_RBRACKET, - TOK_LANGLE, - TOK_RANGLE, - TOK_COMMA, - TOK_COLON, - TOK_SEMICOLON, - TOK_OP, - TOK_AT, - TOK_DOTDOT, - TOK_ARROW, - TOK_PIPE, - TOK_TEST, - TOK_ASSERT, - TOK_SIZEOF, - TOK_DEFER, - TOK_AUTOFREE, - TOK_QUESTION, - TOK_USE, - TOK_QQ, - TOK_QQ_EQ, - TOK_Q_DOT, - TOK_DCOLON, - TOK_TRAIT, - TOK_IMPL, - TOK_AND, - TOK_OR, - TOK_FOR, - TOK_COMPTIME, - TOK_ELLIPSIS, - TOK_UNION, - TOK_ASM, - TOK_VOLATILE, - TOK_MUT, - TOK_ASYNC, - TOK_AWAIT, - TOK_PREPROC, - TOK_COMMENT, - TOK_UNKNOWN +typedef enum { + TOK_EOF = 0, + TOK_IDENT, + TOK_INT, + TOK_FLOAT, + TOK_STRING, + TOK_FSTRING, + TOK_CHAR, + TOK_LPAREN, + TOK_RPAREN, + TOK_LBRACE, + TOK_RBRACE, + TOK_LBRACKET, + TOK_RBRACKET, + TOK_LANGLE, + TOK_RANGLE, + TOK_COMMA, + TOK_COLON, + TOK_SEMICOLON, + TOK_OP, + TOK_AT, + TOK_DOTDOT, + TOK_ARROW, + TOK_PIPE, + TOK_TEST, + TOK_ASSERT, + TOK_SIZEOF, + TOK_DEFER, + TOK_AUTOFREE, + TOK_QUESTION, + TOK_USE, + TOK_QQ, + TOK_QQ_EQ, + TOK_Q_DOT, + TOK_DCOLON, + TOK_TRAIT, + TOK_IMPL, + TOK_AND, + TOK_OR, + TOK_FOR, + TOK_COMPTIME, + TOK_ELLIPSIS, + TOK_UNION, + TOK_ASM, + TOK_VOLATILE, + TOK_MUT, + TOK_ASYNC, + TOK_AWAIT, + TOK_PREPROC, + TOK_COMMENT, + TOK_UNKNOWN } TokenType; -typedef struct -{ - TokenType type; - const char *start; - int len; - int line; - int col; +typedef struct { + TokenType type; + const char *start; + int len; + int line; + int col; } Token; -typedef struct -{ - const char *src; - int pos; - int line; - int col; +typedef struct { + const char *src; + int pos; + int line; + int col; } Lexer; void lexer_init(Lexer *l, const char *src); @@ -137,10 +134,12 @@ int levenshtein(const char *s1, const char *s2); void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion); // Specific error types. -void error_undefined_function(Token t, const char *func_name, const char *suggestion); -void error_wrong_arg_count(Token t, const char *func_name, int expected, int got); -void error_undefined_field(Token t, const char *struct_name, const char *field_name, - const char *suggestion); +void error_undefined_function(Token t, const char *func_name, + const char *suggestion); +void error_wrong_arg_count(Token t, const char *func_name, int expected, + int got); +void error_undefined_field(Token t, const char *struct_name, + const char *field_name, const char *suggestion); void error_type_expected(Token t, const char *expected, const char *got); void error_cannot_index(Token t, const char *type_name); @@ -150,41 +149,44 @@ void zwarn_at(Token t, const char *fmt, ...); // Specific warnings. void warn_unused_variable(Token t, const char *var_name); -void warn_unused_parameter(Token t, const char *param_name, const char *func_name); +void warn_unused_parameter(Token t, const char *param_name, + const char *func_name); void warn_shadowing(Token t, const char *var_name); void warn_unreachable_code(Token t); -void warn_implicit_conversion(Token t, const char *from_type, const char *to_type); -void warn_narrowing_conversion(Token t, const char *from_type, const char *to_type); +void warn_implicit_conversion(Token t, const char *from_type, + const char *to_type); +void warn_narrowing_conversion(Token t, const char *from_type, + const char *to_type); void warn_missing_return(Token t, const char *func_name); void warn_comparison_always_true(Token t, const char *reason); void warn_comparison_always_false(Token t, const char *reason); void warn_division_by_zero(Token t); void warn_integer_overflow(Token t, const char *type_name, long long value); void warn_array_bounds(Token t, int index, int size); -void warn_format_string(Token t, int arg_num, const char *expected, const char *got); +void warn_format_string(Token t, int arg_num, const char *expected, + const char *got); void warn_null_pointer(Token t, const char *expr); // ** Compiler Config ** -typedef struct -{ - char *input_file; - char *output_file; - - // Modes. - int mode_run; // 1 if 'run' command. - int mode_check; // 1 if 'check' command or --check. - int emit_c; // 1 if --emit-c (keep C file). - int verbose; // 1 if --verbose. - int quiet; // 1 if --quiet. - int repl_mode; // 1 if --repl (internal flag for REPL usage). - int is_freestanding; // 1 if --freestanding. - int mode_transpile; // 1 if 'transpile' command. - - // GCC Flags accumulator. - char gcc_flags[4096]; - - // C Compiler selection (default: gcc) - char cc[64]; +typedef struct { + char *input_file; + char *output_file; + + // Modes. + int mode_run; // 1 if 'run' command. + int mode_check; // 1 if 'check' command or --check. + int emit_c; // 1 if --emit-c (keep C file). + int verbose; // 1 if --verbose. + int quiet; // 1 if --quiet. + int repl_mode; // 1 if --repl (internal flag for REPL usage). + int is_freestanding; // 1 if --freestanding. + int mode_transpile; // 1 if 'transpile' command. + + // GCC Flags accumulator. + char gcc_flags[4096]; + + // C Compiler selection (default: gcc) + char cc[64]; } CompilerConfig; extern CompilerConfig g_config; |
