diff options
Diffstat (limited to 'src/parser/parser_stmt.c')
| -rw-r--r-- | src/parser/parser_stmt.c | 6960 |
1 files changed, 3822 insertions, 3138 deletions
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index ea87947..6f019b7 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -7,3473 +7,4157 @@ #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); - } - - 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; -} + 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); + } -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; -} + extern char *curr_func_ret; + curr_func_ret = ret; -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); + // 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; + } - Token t_brace = lexer_next(l); - if (t_brace.type != TOK_LBRACE) { - zpanic_at(t_brace, "Expected { in match"); - } + // 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 *h = 0, *tl = 0; - while (lexer_peek(l).type != TOK_RBRACE) { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) { - break; + ASTNode *body = NULL; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); // consume ; } - if (lexer_peek(l).type == TOK_COMMA) { - lexer_next(l); + else + { + body = parse_block(ctx, 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; - } + + // 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; + } } - char *pattern = xstrdup(patterns_buf); - int is_default = (strcmp(pattern, "_") == 0); + 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 *binding = NULL; - int is_destructure = 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); - // --- 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; + // Check if it starts with "void* self" + if (strncmp(args, "void* self", 10) == 0) + { + sprintf(new_args, "%s* self%s", struct_name, args + 10); + } + else + { + strcpy(new_args, args); } + return new_args; +} + +ASTNode *parse_match(ParserContext *ctx, Lexer *l) +{ + init_builtins(); + Token start_token = lexer_peek(l); + lexer_next(l); // eat 'match' + ASTNode *expr = parse_expression(ctx, l); - // --- 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); + Token t_brace = lexer_next(l); + if (t_brace.type != TOK_LBRACE) + { + zpanic_at(t_brace, "Expected { in match"); } - if (lexer_next(l).type != TOK_ARROW) { - zpanic_at(lexer_peek(l), "Expected =>"); + 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; } + lexer_next(l); // eat } - 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 *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; + // 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"); + } lexer_next(l); - } - // Expect { - if (lexer_peek(l).type != TOK_LBRACE) { - zpanic_at(lexer_peek(l), "Expected { after asm"); - } - lexer_next(l); + // Parse assembly template strings + char *code = xmalloc(4096); // Buffer for assembly code + code[0] = 0; - // Parse assembly template strings - char *code = xmalloc(4096); // Buffer for assembly code - code[0] = 0; + while (1) + { + Token t = lexer_peek(l); - while (1) { - Token t = lexer_peek(l); + // Check for end of asm block or start of operands + if (t.type == TOK_RBRACE) + { + break; + } + if (t.type == TOK_COLON) + { + break; + } - // 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) { + // Support string literals for assembly instructions + if (t.type == TOK_STRING) + { 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; - } + // 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); + 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"); } + } - lexer_next(l); + // Parse outputs (: out(x), inout(y)) + char **outputs = NULL; + char **output_modes = NULL; + int num_outputs = 0; - // 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 (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); // eat : + + outputs = xmalloc(sizeof(char *) * 16); + output_modes = xmalloc(sizeof(char *) * 16); - if (!no_space) { - strcat(code, " "); + 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 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; + } } - strncat(code, arg.start, arg.len); - } - } else { - zpanic_at(t, - "Expected assembly string, instruction, or ':' in asm block"); } - } - // Parse outputs (: out(x), inout(y)) - char **outputs = NULL; - char **output_modes = NULL; - int num_outputs = 0; + // Parse inputs (: in(a), in(b)) + char **inputs = NULL; + int num_inputs = 0; - if (lexer_peek(l).type == TOK_COLON) { - lexer_next(l); // eat : + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); // eat : - outputs = xmalloc(sizeof(char *) * 16); - output_modes = xmalloc(sizeof(char *) * 16); + 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 out(var) or inout(var) - if (t.type == TOK_IDENT) { - char *mode = token_strdup(t); - lexer_next(l); + 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 (lexer_peek(l).type != TOK_LPAREN) { - zpanic_at(lexer_peek(l), "Expected ( after output mode"); + // 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; + } } - lexer_next(l); + } - Token var = lexer_next(l); - if (var.type != TOK_IDENT) { - zpanic_at(var, "Expected variable name"); - } + // Parse clobbers (: "eax", "memory") + char **clobbers = NULL; + int num_clobbers = 0; - if (lexer_peek(l).type != TOK_RPAREN) { - zpanic_at(lexer_peek(l), "Expected ) after variable"); + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); // eat : + + clobbers = 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; + } + + 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; + } } - lexer_next(l); + } - outputs[num_outputs] = token_strdup(var); - output_modes[num_outputs] = mode; - num_outputs++; - } 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); - // Parse inputs (: in(a), in(b)) - char **inputs = NULL; - int num_inputs = 0; + // 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; - if (lexer_peek(l).type == TOK_COLON) { - lexer_next(l); // eat : + return n; +} - inputs = xmalloc(sizeof(char *) * 16); +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"); + } - 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; - } + // Strip quotes for AST storage + char *name = xmalloc(t.len); + strncpy(name, t.start + 1, t.len - 2); + name[t.len - 2] = 0; - // Parse in(var) - if (t.type == TOK_IDENT && strncmp(t.start, "in", 2) == 0) { - lexer_next(l); + ASTNode *body = parse_block(ctx, l); + + ASTNode *n = ast_create(NODE_TEST); + n->test_stmt.name = name; + n->test_stmt.body = body; + return n; +} - if (lexer_peek(l).type != TOK_LPAREN) { - zpanic_at(lexer_peek(l), "Expected ( after in"); +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) + { + 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_RPAREN) + { + lexer_next(l); + } + if (lexer_peek(l).type == TOK_SEMICOLON) + { lexer_next(l); + } - Token var = lexer_next(l); - if (var.type != TOK_IDENT) { - zpanic_at(var, "Expected variable name"); - } + ASTNode *n = ast_create(NODE_ASSERT); + n->assert_stmt.condition = cond; + n->assert_stmt.message = msg; + return n; +} - if (lexer_peek(l).type != TOK_RPAREN) { - zpanic_at(lexer_peek(l), "Expected ) after variable"); +// Helper for Value-Returning Defer +static void replace_it_with_var(ASTNode *node, char *var_name) +{ + if (!node) + { + return; + } + if (node->type == NODE_EXPR_VAR) + { + if (strcmp(node->var_ref.name, "it") == 0) + { + // Replace 'it' with var_name + node->var_ref.name = xstrdup(var_name); + } + } + else if (node->type == NODE_EXPR_CALL) + { + replace_it_with_var(node->call.callee, var_name); + ASTNode *arg = node->call.args; + while (arg) + { + replace_it_with_var(arg, var_name); + arg = arg->next; } + } + else if (node->type == NODE_EXPR_MEMBER) + { + replace_it_with_var(node->member.target, var_name); + } + else if (node->type == NODE_EXPR_BINARY) + { + replace_it_with_var(node->binary.left, var_name); + replace_it_with_var(node->binary.right, var_name); + } + else if (node->type == NODE_EXPR_UNARY) + { + replace_it_with_var(node->unary.operand, var_name); + } + else if (node->type == NODE_BLOCK) + { + ASTNode *s = node->block.statements; + while (s) + { + replace_it_with_var(s, var_name); + s = s->next; + } + } +} + +ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat 'var' + + // 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; + } - inputs[num_inputs] = token_strdup(var); - num_inputs++; - } else { - break; - } + // 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; } - } - // Parse clobbers (: "eax", "memory") - char **clobbers = NULL; - int num_clobbers = 0; + // Normal Declaration OR Named Struct Destructuring + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); - if (lexer_peek(l).type == TOK_COLON) { - lexer_next(l); // eat : + // 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); - clobbers = xmalloc(sizeof(char *) * 16); + count++; - while (1) { - Token t = lexer_peek(l); - if (t.type == TOK_RBRACE) { - break; - } - if (t.type == TOK_COMMA) { - lexer_next(l); - continue; - } + 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 (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; -} + 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 *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"); - } + 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; + } - // Strip quotes for AST storage - char *name = xmalloc(t.len); - strncpy(name, t.start + 1, t.len - 2); - name[t.len - 2] = 0; + // 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); - ASTNode *body = parse_block(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); + } - ASTNode *n = ast_create(NODE_TEST); - n->test_stmt.name = name; - n->test_stmt.body = body; - return n; -} + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); + } -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 *init = parse_expression(ctx, l); - ASTNode *cond = parse_expression(ctx, l); + Token t = lexer_next(l); + if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) + { + zpanic_at(t, "Expected 'else' in guard statement"); + } - char *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; - } + ASTNode *else_blk; + if (lexer_peek(l).type == TOK_LBRACE) + { + else_blk = parse_block(ctx, l); + } + else + { + else_blk = ast_create(NODE_BLOCK); + else_blk->block.statements = parse_statement(ctx, l); + } - if (lexer_peek(l).type == TOK_RPAREN) { - lexer_next(l); - } - if (lexer_peek(l).type == TOK_SEMICOLON) { - 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; -} + 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; -// 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; - } - } -} + add_symbol(ctx, val_name, "unknown", NULL); + register_var_mutability(ctx, val_name, is_mutable); -ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) { - lexer_next(l); // eat 'var' + return n; + } - // 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; - } + char *type = NULL; + Type *type_obj = NULL; // --- NEW: Formal Type Object --- - // Normal Declaration OR Named Struct Destructuring - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); + 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); + } - // 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; + ASTNode *init = NULL; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); - while (1) { - // Parse field:name or just name - Token t = lexer_next(l); - char *ident = token_strdup(t); + // 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); + } - 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 (init && type) + { + char *rhs_type = init->resolved_type; + if (!rhs_type && init->type_info) + { + rhs_type = type_to_string(init->type_info); + } - // Check for Guard Pattern: var Some(val) = opt else { ... } - if (lexer_peek(l).type == TOK_LPAREN) { - lexer_next(l); // eat ( - Token val_tok = lexer_next(l); - char *val_name = token_strdup(val_tok); + if (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 + } + } + } - if (lexer_next(l).type != TOK_RPAREN) { - zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); + // ** 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_OP) { - zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); + if (!type && !init) + { + zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); } - ASTNode *init = parse_expression(ctx, l); + // 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); - Token t = lexer_next(l); - if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) { - zpanic_at(t, "Expected 'else' in guard statement"); + // 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; + } } - ASTNode *else_blk; - if (lexer_peek(l).type == TOK_LBRACE) { - else_blk = parse_block(ctx, l); - } else { - else_blk = ast_create(NODE_BLOCK); - else_blk->block.statements = parse_statement(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_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; + + // 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); } - 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; + // Check for 'defer' (Value-Returning Defer) + // Only capture if it is NOT a block defer (defer { ... }) + // If it is a block defer, we leave it for the next parse_statement call. + if (lexer_peek(l).type == TOK_DEFER) + { + Lexer lookahead = *l; + lexer_next(&lookahead); // Eat defer + if (lexer_peek(&lookahead).type != TOK_LBRACE) + { + // Proceed to consume + lexer_next(l); // eat defer (real) + + // Parse the defer expression/statement + // Usually defer close(it); + // We parse expression. + ASTNode *expr = parse_expression(ctx, l); + + // Handle "it" substitution + replace_it_with_var(expr, name); + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *d = ast_create(NODE_DEFER); + d->defer_stmt.stmt = expr; - add_symbol(ctx, val_name, "unknown", NULL); - register_var_mutability(ctx, val_name, is_mutable); + // Chain it: var_decl -> defer + n->next = d; + } + } return n; - } - - char *type = NULL; - Type *type_obj = NULL; // --- NEW: Formal Type Object --- +} - if (lexer_peek(l).type == TOK_COLON) { - lexer_next(l); - // Hybrid Parse: Get Object AND String - type_obj = parse_type_formal(ctx, l); - type = type_to_string(type_obj); - } +ASTNode *parse_const(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat const + Token n = lexer_next(l); - ASTNode *init = NULL; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) { - lexer_next(l); + char *type_str = NULL; + Type *type_obj = NULL; - // 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) { + if (lexer_peek(l).type == TOK_COLON) + { lexer_next(l); - } - } else { - init = parse_expression(ctx, l); + // Hybrid Parse + type_obj = parse_type_formal(ctx, l); + type_str = type_to_string(type_obj); } - if (init && type) { - char *rhs_type = init->resolved_type; - if (!rhs_type && init->type_info) { - rhs_type = type_to_string(init->type_info); - } - - if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) { - // Strip stars to get struct names - char target_struct[256]; - strcpy(target_struct, type); - target_struct[strlen(target_struct) - 1] = 0; - char source_struct[256]; - strcpy(source_struct, rhs_type); - source_struct[strlen(source_struct) - 1] = 0; + 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 *def = find_struct_def(ctx, source_struct); + ASTNode *i = 0; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); - 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 + // 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); + } + } + } - init = cast; // Replace init with cast + 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); } - // ** 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); - - // 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 *d = ast_create(NODE_DEFER); - d->defer_stmt.stmt = expr; + ASTNode *o = ast_create(NODE_CONST); + o->var_decl.name = ns; + o->var_decl.type_str = type_str; + o->var_decl.init_expr = i; - // Chain it: var_decl -> defer - n->next = d; - } + if (!ctx->current_scope || !ctx->current_scope->parent) + { + add_to_global_list(ctx, o); + } - return n; + return o; } -ASTNode *parse_const(ParserContext *ctx, Lexer *l) { - lexer_next(l); // eat const - Token n = lexer_next(l); - - char *type_str = NULL; - Type *type_obj = NULL; - - if (lexer_peek(l).type == TOK_COLON) { +ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) +{ 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), "=")) { + Token n = lexer_next(l); lexer_next(l); - - // Check for constant integer literal - if (lexer_peek(l).type == TOK_INT) { - Token val_tok = lexer_peek(l); - int val = atoi(token_strdup(val_tok)); // quick check - - Symbol *s = find_symbol_entry(ctx, ns); - if (s) { - s->is_const_value = 1; - s->const_int_val = val; - - if (!s->type_name || strcmp(s->type_name, "unknown") == 0) { - if (s->type_name) { - free(s->type_name); - } - s->type_name = xstrdup("int"); - if (s->type_info) { - free(s->type_info); - } - s->type_info = type_new(TYPE_INT); - } - } - } - - if (lexer_peek(l).type == TOK_LPAREN && type_str && - strncmp(type_str, "Tuple_", 6) == 0) { - char *code = parse_tuple_literal(ctx, l, type_str); - i = ast_create(NODE_RAW_STMT); - i->raw_stmt.content = code; - } else { - i = parse_expression(ctx, l); - } - } else { + 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 (lexer_peek(l).type == TOK_SEMICOLON) { - lexer_next(l); - } +ASTNode *parse_return(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat 'return' + ASTNode *n = ast_create(NODE_RETURN); - ASTNode *o = ast_create(NODE_CONST); - o->var_decl.name = ns; - o->var_decl.type_str = type_str; - o->var_decl.init_expr = i; + int handled = 0; - if (!ctx->current_scope || !ctx->current_scope->parent) { - add_to_global_list(ctx, o); - } + // 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) + { - return o; -} + // Peek ahead to distinguish "(expr)" from "(a, b)" + int is_tuple_lit = 0; + int depth = 0; -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; -} + // Just scan tokens manually using a temp lexer to be safe + Lexer temp_l = *l; -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; -} + while (1) + { + Token t = lexer_next(&temp_l); + if (t.type == TOK_EOF) + { + break; + } + if (t.type == TOK_SEMICOLON) + { + break; // Safety break + } -ASTNode *parse_if(ParserContext *ctx, Lexer *l) { - lexer_next(l); // eat if - ASTNode *cond = parse_expression(ctx, l); - check_assignment_condition(cond); + if (t.type == TOK_LPAREN) + { + depth++; + } + if (t.type == TOK_RPAREN) + { + depth--; + if (depth == 0) + { + break; // End of potential tuple + } + } - 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; -} + // 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_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; + 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; } -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); - // Register loop variable so body can see it - add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT)); +ASTNode *parse_if(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat if + ASTNode *cond = parse_expression(ctx, l); + check_assignment_condition(cond); - // 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); - } + 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); - - return n; - } + then_b = ast_create(NODE_BLOCK); + then_b->block.statements = s; } - l->pos = saved_pos; // Restore - } - // C-Style For Loop - enter_scope(ctx); - if (lexer_peek(l).type == TOK_LPAREN) { - lexer_next(l); - } - - ASTNode *init = NULL; - if (lexer_peek(l).type != TOK_SEMICOLON) { - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "var", 3) == 0) { - init = parse_var_decl(ctx, l); - } else { - init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) { + 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; + } } - } 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) { + 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) +{ lexer_next(l); - } + ASTNode *cond = parse_expression(ctx, l); + check_assignment_condition(cond); - ASTNode *step = NULL; - if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) { - step = parse_expression(ctx, l); - } + // 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; +} - if (lexer_peek(l).type == TOK_RPAREN) { +ASTNode *parse_for(ParserContext *ctx, Lexer *l) +{ lexer_next(l); - } - - ASTNode *body; - if (lexer_peek(l).type == TOK_LBRACE) { - body = parse_block(ctx, l); - } else { - body = parse_statement(ctx, l); - } - exit_scope(ctx); - - ASTNode *n = ast_create(NODE_FOR); - n->for_stmt.init = init; - n->for_stmt.condition = cond; - n->for_stmt.step = step; - n->for_stmt.body = body; - return n; -} -char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, - const char *target, char ***used_syms, int *count) { - char *gen = xmalloc(8192); - strcpy(gen, "({ "); + // 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 *s = xstrdup(content); - char *cur = s; + // C-Style For Loop + enter_scope(ctx); + if (lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); + } - while (*cur) { - // 1. Find text before the next '{' - char *brace = cur; - while (*brace && *brace != '{') { - brace++; + 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); } - 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 *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 == 0) { - break; + ASTNode *step = NULL; + if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) + { + step = parse_expression(ctx, 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, "); "); - } + if (lexer_peek(l).type == TOK_RPAREN) + { + lexer_next(l); } - free(rw_expr); // Don't forget to free! - if (allocated_expr) { - free(allocated_expr); // Don't forget to free the auto-generated call! + ASTNode *body; + if (lexer_peek(l).type == TOK_LBRACE) + { + body = parse_block(ctx, l); } + else + { + body = parse_statement(ctx, l); + } + exit_scope(ctx); - cur = p + 1; - } + 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; +} - if (newline) { - char buf[128]; - sprintf(buf, "fprintf(%s, \"\\n\"); ", target); - strcat(gen, buf); - } else { - strcat(gen, "fflush(stdout); "); - } +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, "({ "); - strcat(gen, "0; })"); + char *s = xstrdup(content); + char *cur = s; - free(s); - return gen; -} + while (*cur) + { + // 1. Find text before the next '{' + char *brace = cur; + while (*brace && *brace != '{') + { + brace++; + } -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 (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 (t.type == TOK_LBRACE) { - depth++; - } - if (t.type == TOK_RBRACE) { - depth--; - } + if (*brace == 0) + { + break; + } - 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++; + // 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++; } - strncat(body, t.start, t.len); - body_len += t.len; - } - } + *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; + } - 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; -} + char *clean_expr = expr; + while (*clean_expr == ' ') + { + clean_expr++; // Skip leading spaces + } -ASTNode *parse_statement(ParserContext *ctx, Lexer *l) { - Token tk = lexer_peek(l); - ASTNode *s = NULL; + // Analyze usage & Type Check for to_string() + char *final_expr = xstrdup(clean_expr); + + // Use final_expr in usage analysis if needed, but mainly for symbol tracking + { + Lexer lex; + lexer_init(&lex, clean_expr); // Scan original for symbols + Token t; + while ((t = lexer_next(&lex)).type != TOK_EOF) + { + if (t.type == TOK_IDENT) + { + char *name = token_strdup(t); + Symbol *sym = find_symbol_entry(ctx, name); + if (sym) + { + sym->is_used = 1; + } + + if (used_syms && count) + { + *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1)); + (*used_syms)[*count] = name; + (*count)++; + } + else + { + free(name); + } + } + } + } - 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"); + expr = final_expr; + char *allocated_expr = NULL; + clean_expr = final_expr; + + int skip_rewrite = 0; + + // Check if struct and has to_string (Robust Logic) + { + Lexer lex; + lexer_init(&lex, clean_expr); + // Parse using temporary lexer to check type + ASTNode *expr_node = parse_expression(ctx, &lex); + + if (expr_node && expr_node->type_info) + { + Type *t = expr_node->type_info; + char *struct_name = NULL; + int is_ptr = 0; + + if (t->kind == TYPE_STRUCT) + { + struct_name = t->name; + } + else if (t->kind == TYPE_POINTER && t->inner && t->inner->kind == TYPE_STRUCT) + { + struct_name = t->inner->name; + is_ptr = 1; + } + + if (struct_name) + { + char mangled[256]; + sprintf(mangled, "%s__to_string", struct_name); + if (find_func(ctx, mangled)) + { + char *inner_wrapped = xmalloc(strlen(clean_expr) + 5); + sprintf(inner_wrapped, "#{%s}", clean_expr); + char *inner_c = rewrite_expr_methods(ctx, inner_wrapped); + free(inner_wrapped); + + // Now wrap in to_string call using C99 compound literal for safety + char *new_expr = xmalloc(strlen(inner_c) + strlen(mangled) + 64); + if (is_ptr) + { + sprintf(new_expr, "%s(%s)", mangled, inner_c); + } + else + { + sprintf(new_expr, "%s(({ %s _z_tmp = (%s); &_z_tmp; }))", mangled, + struct_name, inner_c); + } + + if (expr != s) + { + free(expr); // Free if explicitly allocated + } + expr = new_expr; + skip_rewrite = 1; // Don't rewrite again on the C99 syntax + } + } + } + } + + // Rewrite the expression to handle pointer access (header_ptr.magic -> + // header_ptr->magic) + char *rw_expr; + if (skip_rewrite) + { + rw_expr = xstrdup(expr); + } + else + { + char *wrapped_expr = xmalloc(strlen(expr) + 5); + sprintf(wrapped_expr, "#{%s}", expr); + rw_expr = rewrite_expr_methods(ctx, wrapped_expr); + free(wrapped_expr); + } + + if (fmt) + { + // Explicit format: {x:%.2f} + char buf[128]; + sprintf(buf, "fprintf(%s, \"%%", target); + strcat(gen, buf); + strcat(gen, fmt); + strcat(gen, "\", "); + strcat(gen, rw_expr); // Use rewritten expr + strcat(gen, "); "); + } + else + { + // Auto-detect format based on type if possible + const char *format_spec = NULL; + char *inferred_type = find_symbol_type(ctx, clean_expr); // Simple variable lookup + + // Basic Type Mappings + if (inferred_type) + { + if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0 || + strcmp(inferred_type, "bool") == 0) + { + format_spec = "%d"; + } + else if (strcmp(inferred_type, "long") == 0 || strcmp(inferred_type, "i64") == 0 || + strcmp(inferred_type, "isize") == 0) + { + format_spec = "%ld"; + } + else if (strcmp(inferred_type, "usize") == 0 || strcmp(inferred_type, "u64") == 0) + { + format_spec = "%lu"; + } + else if (strcmp(inferred_type, "float") == 0 || strcmp(inferred_type, "f32") == 0 || + strcmp(inferred_type, "double") == 0) + { + format_spec = "%f"; + } + else if (strcmp(inferred_type, "char") == 0 || strcmp(inferred_type, "byte") == 0) + { + format_spec = "%c"; + } + else if (strcmp(inferred_type, "string") == 0 || + strcmp(inferred_type, "str") == 0 || + (inferred_type[strlen(inferred_type) - 1] == '*' && + strstr(inferred_type, "char"))) + { + format_spec = "%s"; + } + else if (strstr(inferred_type, "*")) + { + format_spec = "%p"; // Pointer + } + } + + // Check for Literals if variable lookup failed + if (!format_spec) + { + if (isdigit(clean_expr[0]) || clean_expr[0] == '-') + { + format_spec = "%d"; // Naive integer guess (could be float) + } + else if (clean_expr[0] == '"') + { + format_spec = "%s"; + } + else if (clean_expr[0] == '\'') + { + format_spec = "%c"; + } + } + + if (format_spec) + { + char buf[128]; + sprintf(buf, "fprintf(%s, \"", target); + strcat(gen, buf); + strcat(gen, format_spec); + strcat(gen, "\", "); + strcat(gen, rw_expr); + strcat(gen, "); "); + } + else + { + // Fallback to runtime macro + char buf[128]; + sprintf(buf, "fprintf(%s, _z_str(", target); + strcat(gen, buf); + strcat(gen, rw_expr); + strcat(gen, "), "); + strcat(gen, rw_expr); + strcat(gen, "); "); + } + } + + free(rw_expr); // Don't forget to free! + if (allocated_expr) + { + free(allocated_expr); // Don't forget to free the auto-generated call! + } + + cur = p + 1; } - 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; + + if (newline) + { + char buf[128]; + sprintf(buf, "fprintf(%s, \"\\n\"); ", target); + strcat(gen, buf); } - 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; + else + { + strcat(gen, "fflush(stdout); "); + } + + strcat(gen, "0; })"); + + free(s); + return gen; +} - 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; - } +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 ! - if (head && !head->next) { - return head; + // Expect { + if (lexer_peek(l).type != TOK_LBRACE) + { + zpanic_at(lexer_peek(l), "Expected { after macro invocation"); } + lexer_next(l); // consume { - 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"); + // Collect body until } + char *body = xmalloc(8192); + body[0] = '\0'; + int body_len = 0; + int depth = 1; + int last_line = start_tok.line; + + while (depth > 0) + { + Token t = lexer_peek(l); + if (t.type == TOK_EOF) + { + zpanic_at(t, "Unexpected EOF in macro block"); } - if (t.type == TOK_LBRACE) { - depth++; + + if (t.type == TOK_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; - char *content = xmalloc(len + 1); - memcpy(content, start, len); - content[len] = 0; + 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; + } + } - ASTNode *s = ast_create(NODE_RAW_STMT); - s->raw_stmt.content = content; - return s; + last_line = t.line; + lexer_next(l); } - // Check for plugin blocks - if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) { - lexer_next(l); // consume 'plugin' - return parse_plugin(ctx, 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); } - if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) { - return parse_var_decl(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); } - // 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'"); + // Execute Plugin Immediately (Expansion) + FILE *capture = tmpfile(); + if (!capture) + { + zpanic_at(start_tok, "Failed to create capture buffer for plugin expansion"); } - if (strncmp(tk.start, "const", 5) == 0 && tk.len == 5) { - return parse_const(ctx, l); + + 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, "return", 6) == 0 && tk.len == 6) { - return parse_return(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, "if", 2) == 0 && tk.len == 2) { - return parse_if(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, "while", 5) == 0 && tk.len == 5) { - return parse_while(ctx, l); + + // Block + if (tk.type == TOK_LBRACE) + { + return parse_block(ctx, l); } - if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3) { - return parse_for(ctx, l); + + // Keywords / Special + if (tk.type == TOK_TRAIT) + { + return parse_trait(ctx, l); } - if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5) { - return parse_match(ctx, l); + if (tk.type == TOK_IMPL) + { + return parse_impl(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) { + if (tk.type == TOK_AUTOFREE) + { lexer_next(l); - } - return n; + if (lexer_peek(l).type != TOK_IDENT || strncmp(lexer_peek(l).start, "var", 3) != 0) + { + zpanic_at(lexer_peek(l), "Expected 'var' after autofree"); + } + s = parse_var_decl(ctx, l); + s->var_decl.is_autofree = 1; + // Mark symbol as autofree to suppress unused variable warning + Symbol *sym = find_symbol_entry(ctx, s->var_decl.name); + if (sym) + { + sym->is_autofree = 1; + } + return s; } + if (tk.type == TOK_TEST) + { + return parse_test(ctx, l); + } + if (tk.type == TOK_COMPTIME) + { + char *src = run_comptime_block(ctx, l); + Lexer new_l; + lexer_init(&new_l, src); + ASTNode *head = NULL, *tail = NULL; + + while (lexer_peek(&new_l).type != TOK_EOF) + { + ASTNode *s = parse_statement(ctx, &new_l); + if (!s) + { + break; + } + if (!head) + { + head = s; + } + else + { + tail->next = s; + } + tail = s; + while (tail->next) + { + tail = tail->next; + } + } - if (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4) { - return parse_loop(ctx, l); + if (head && !head->next) + { + return head; + } + + ASTNode *b = ast_create(NODE_BLOCK); + b->block.statements = head; + return b; } - if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6) { - return parse_repeat(ctx, l); + if (tk.type == TOK_ASSERT) + { + return parse_assert(ctx, l); } - if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6) { - return parse_unless(ctx, l); + if (tk.type == TOK_DEFER) + { + return parse_defer(ctx, l); } - if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5) { - return parse_guard(ctx, l); + if (tk.type == TOK_ASM) + { + return parse_asm(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); + // 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; + } - // 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' + // 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; - ASTNode *cond = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) { - lexer_next(l); - } + char *content = xmalloc(len + 1); + memcpy(content, start, len); + content[len] = 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; - } + ASTNode *s = ast_create(NODE_RAW_STMT); + s->raw_stmt.content = content; + return s; + } - if (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) { - return parse_defer(ctx, l); - } + // Check for plugin blocks + if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) + { + lexer_next(l); // consume 'plugin' + return parse_plugin(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); + if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) + { + return parse_var_decl(ctx, 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); + // 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); } - 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; + // 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"); + } + + 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); + } + + 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; + } } - // Label detection: identifier followed by : (but not ::) + // Default: Expression Statement + s = parse_expression(ctx, l); + + int has_semi = 0; + if (lexer_peek(l).type == TOK_SEMICOLON) { - 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); - } + 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; + } + } - 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 (!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; + } } - } - // Default: Expression Statement - s = parse_expression(ctx, l); + if (s) + { + s->line = tk.line; + } - 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; + // 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; +ASTNode *parse_block(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat '{' + enter_scope(ctx); + ASTNode *head = 0, *tail = 0; - int unreachable = 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) - } - - // 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; + 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"); - } - 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; +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; - // 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. + lexer_next(l); // eat { - Token ft = lexer_next(l); - if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) { - zpanic_at(ft, "Expected fn in trait"); - } + ASTNode *methods = NULL, *tail = NULL; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } - Token mn = lexer_next(l); - char *mname = xmalloc(mn.len + 1); - strncpy(mname, mn.start, mn.len); - mname[mn.len] = 0; + // 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. - 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; + Token ft = lexer_next(l); + if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) + { + zpanic_at(ft, "Expected fn in trait"); + } + + Token mn = lexer_next(l); + char *mname = xmalloc(mn.len + 1); + strncpy(mname, mn.start, mn.len); + mname[mn.len] = 0; + + char **defaults = NULL; + int arg_count = 0; + Type **arg_types = NULL; + char **param_names = NULL; + int is_varargs = 0; + char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, + &is_varargs); + + char *ret = xstrdup("void"); + if (lexer_peek(l).type == TOK_ARROW) + { + lexer_next(l); + char *rt = parse_type(ctx, l); + free(ret); + ret = rt; + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *m = ast_create(NODE_FUNCTION); + m->func.param_names = param_names; + m->func.name = mname; + m->func.args = args; + m->func.ret_type = ret; + m->func.body = NULL; + if (!methods) + { + methods = m; + } + else + { + tail->next = m; + } + tail = m; + } + else + { + // Default implementation? Not supported yet. + zpanic_at(lexer_peek(l), "Trait methods must end with ; for now"); + } + } + + ASTNode *n_node = ast_create(NODE_TRAIT); + n_node->trait.name = name; + n_node->trait.methods = methods; + 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 + // 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) + 5); + sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name2); + free(f->func.args); + f->func.args = na; + 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; } - Token t2 = lexer_next(l); - char *name2 = token_strdup(t2); + 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; + } - register_impl(ctx, name1, name2); + ctx->current_impl_struct = name1; // For patch_self_args inside parse_function - // 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; + 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) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); // eat async + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, 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) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, + f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + 1, f->token); + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + ctx->current_impl_struct = NULL; + ASTNode *n = ast_create(NODE_IMPL); + n->impl.struct_name = name1; + n->impl.methods = h; + add_to_impl_list(ctx, n); + return n; } - } } +} - ctx->current_impl_struct = - name2; // Set context to prevent duplicate emission and prefixing +ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) +{ - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - while (1) { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) { + 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); - 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 { + Token g = lexer_next(l); + gp = token_strdup(g); 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>) + register_generic(ctx, name); + } - // 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) { + // 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; + } + + 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 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; + 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); } - 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; + + 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; - } else { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } else { - lexer_next(l); - } - } - ctx->current_impl_struct = NULL; - ASTNode *n = ast_create(NODE_IMPL); - n->impl.struct_name = name1; - n->impl.methods = h; - add_to_impl_list(ctx, n); - return n; - } - } -} -ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) { + if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + } + } + else + { + lexer_next(l); + } + } - lexer_next(l); // eat struct or union - Token n = lexer_next(l); - char *name = token_strdup(n); + ASTNode *node = ast_create(NODE_STRUCT); + add_to_struct_list(ctx, node); - // 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); - } + // 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; + } - // 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; + node->strct.name = name; - if (!gp) { - add_to_struct_list(ctx, n); + // 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 } - return n; - } - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; + node->strct.fields = h; + node->strct.generic_param = gp; + node->strct.is_union = is_union; - 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; + 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; } -Type *parse_type_obj(ParserContext *ctx, Lexer *l) { - // 1. Parse the base type (int, U32, MyStruct, etc.) - Type *t = parse_type_base(ctx, 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); - // 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; - } + // 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) + // (Optional: You can add array parsing here later if needed) - return t; + return t; } -ASTNode *parse_enum(ParserContext *ctx, Lexer *l) { - lexer_next(l); - Token n = lexer_next(l); +ASTNode *parse_enum(ParserContext *ctx, Lexer *l) +{ + 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"); - } + // 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 { + lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - int v = 0; - char *ename = token_strdup(n); // Store enum name + 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; + 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); + } } - if (t.type == TOK_COMMA) { - lexer_next(l); - continue; + + // 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; } - if (t.type == TOK_IDENT) { - Token vt = lexer_next(l); - char *vname = token_strdup(vt); + ASTNode *node = ast_create(NODE_ENUM); + node->enm.name = ename; - // 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 )"); - } - } + node->enm.variants = h; + node->enm.generic_param = gp; // 3. Store generic param - 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* + if (gp) + { + node->enm.is_template = 1; + register_template(ctx, node->enm.name, node); + } - // 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); + add_to_enum_list(ctx, node); // Register globally - // 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; + 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_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; - 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 { +ASTNode *parse_import(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat 'import' - // 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 - } + // 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" - Token sym_tok = lexer_next(l); - if (sym_tok.type != TOK_IDENT) { - zpanic_at(sym_tok, "Expected identifier in selective import"); - } + // 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'"); + } - 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; + // 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 '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'"); + // 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); } - 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 - } + // Register the plugin + register_plugin(ctx, plugin_name, alias); - symbol_count++; + // Consume optional semicolon + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + // Return NULL - no AST node needed for imports + return NULL; } - lexer_next(l); // eat } + // 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; - // 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); - } + 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 + } - // 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}; + Token sym_tok = lexer_next(l); + if (sym_tok.type != TOK_IDENT) + { + zpanic_at(sym_tok, "Expected identifier in selective import"); + } + + 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 + } - char system_path[1024]; - int found = 0; + symbol_count++; + } + + lexer_next(l); // eat } - 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) { + // 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(system_path); - found = 1; - } + fn = xstrdup(resolved_path); } - if (!found) { - // File not found anywhere - will error later when trying to open + // 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 (!found) + { + // File not found anywhere - will error later when trying to open + } } - } - // 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; - } + // 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 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); + } - // Check if file already imported - if (is_file_imported(ctx, fn)) { free(fn); - 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; + 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"); - } - - // 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'"); +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"); } - // Check for 'end' - if (t.type == TOK_IDENT && t.len == 3 && strncmp(t.start, "end", 3) == 0) { - lexer_next(l); // consume 'end' - break; - } + // Extract plugin name + char *plugin_name = xmalloc(tk.len + 1); + strncpy(plugin_name, tk.start, tk.len); + plugin_name[tk.len] = '\0'; - // 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; - } + // Collect everything until 'end' + char *body = xmalloc(8192); + body[0] = '\0'; + int body_len = 0; - lexer_next(l); - } + 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; + } - // Create plugin node - ASTNode *n = ast_create(NODE_PLUGIN); - n->plugin_stmt.plugin_name = plugin_name; - n->plugin_stmt.body = body; + // 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; + } - if (lexer_peek(l).type == TOK_SEMICOLON) { - lexer_next(l); - } - return n; + lexer_next(l); + } + + // 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; } |
