diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-22 22:40:43 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-22 22:40:43 +0000 |
| commit | ed4bbfd8cf4a72fdf4a5d6cba94d537cab340356 (patch) | |
| tree | 80f520cb36f25a210537b477007b0ac5a24e4b8e /src/parser | |
| parent | 3d1840e8690bef6e58a208d9ca33857a59a2e852 (diff) | |
Removing some duplicates and dissecting codegen/parser.
Diffstat (limited to 'src/parser')
| -rw-r--r-- | src/parser/parser_decl.c | 802 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 1721 | ||||
| -rw-r--r-- | src/parser/parser_struct.c | 943 |
3 files changed, 1746 insertions, 1720 deletions
diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c new file mode 100644 index 0000000..63ec329 --- /dev/null +++ b/src/parser/parser_decl.c @@ -0,0 +1,802 @@ + +#include "parser.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../ast/ast.h" +#include "../plugins/plugin_manager.h" +#include "../zen/zen_facts.h" +#include "zprep_plugin.h" +#include "../codegen/codegen.h" + +ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) +{ + lexer_next(l); // eat 'fn' + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); + + if (is_async) + { + ctx->has_async = 1; + } + + // Check for C reserved word conflict + if (is_c_reserved_word(name)) + { + warn_c_reserved_word(name_tok, name); + } + + char *gen_param = NULL; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + + char buf[1024]; + buf[0] = 0; + + while (1) + { + Token gt = lexer_next(l); + if (gt.type != TOK_IDENT) + { + zpanic_at(gt, "Expected generic parameter name"); + } + char *s = token_strdup(gt); + + if (strlen(buf) + strlen(s) + 2 >= sizeof(buf)) + { + zpanic_at(gt, "Too many generic parameters"); + } + + if (buf[0]) + { + strcat(buf, ","); + } + strcat(buf, s); + free(s); + + if (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + continue; + } + break; + } + + if (lexer_next(l).type != TOK_RANGLE) + { + zpanic_at(lexer_peek(l), "Expected >"); + } + gen_param = xstrdup(buf); + } + + // Register generic parameters so type parsing recognizes them + int saved_generic_count = ctx->known_generics_count; + if (gen_param) + { + char *tmp = xstrdup(gen_param); + char *tok = strtok(tmp, ","); + while (tok) + { + register_generic(ctx, tok); + tok = strtok(NULL, ","); + } + free(tmp); + } + + enter_scope(ctx); + char **defaults; + int count; + Type **arg_types; + char **param_names; + int is_varargs = 0; + + char *args = + parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); + + char *ret = "void"; + Type *ret_type_obj = type_new(TYPE_VOID); + + if (strcmp(name, "main") == 0) + { + ret = "int"; + ret_type_obj = type_new(TYPE_INT); + } + + if (lexer_peek(l).type == TOK_ARROW) + { + lexer_next(l); + ret_type_obj = parse_type_formal(ctx, l); + ret = type_to_string(ret_type_obj); + } + + extern char *curr_func_ret; + curr_func_ret = ret; + + // Auto-prefix function name if in module context + // Don't prefix generic templates or functions inside impl blocks (already + // mangled) + if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) + { + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); + free(name); + name = prefixed_name; + } + + // Register if concrete (Global functions only) + if (!gen_param && !ctx->current_impl_struct) + { + register_func(ctx, name, count, defaults, arg_types, ret_type_obj, is_varargs, is_async, + name_tok); + // Note: must_use is set after return by caller (parser_core.c) + } + + ASTNode *body = NULL; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); // consume ; + } + else + { + body = parse_block(ctx, l); + } + + // Check for unused parameters + // The current scope contains arguments (since parse_block creates a new child + // scope for body) Only check if we parsed a body (not a prototype) function + if (body && ctx->current_scope) + { + Symbol *sym = ctx->current_scope->symbols; + while (sym) + { + // Check if unused and not prefixed with '_' (conventional ignore) + // also ignore 'self' as it is often mandated by traits + if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "self") != 0 && + strcmp(name, "main") != 0) + { + warn_unused_parameter(sym->decl_token, sym->name, name); + } + sym = sym->next; + } + } + + exit_scope(ctx); + + // Restore generic count to unregister function-scoped generics + ctx->known_generics_count = saved_generic_count; + + curr_func_ret = NULL; + + ASTNode *node = ast_create(NODE_FUNCTION); + node->token = name_tok; // Save definition location + node->func.name = name; + node->func.args = args; + node->func.ret_type = ret; + node->func.body = body; + + node->func.arg_types = arg_types; + node->func.param_names = param_names; + node->func.arg_count = count; + node->func.defaults = defaults; + node->func.ret_type_info = ret_type_obj; + node->func.is_varargs = is_varargs; + + if (gen_param) + { + register_func_template(ctx, name, gen_param, node); + return NULL; + } + if (!ctx->current_impl_struct) + { + add_to_func_list(ctx, node); + } + return node; +} + +char *patch_self_args(const char *args, const char *struct_name) +{ + if (!args) + { + return NULL; + } + char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10); + + // Check if it starts with "void* self" + if (strncmp(args, "void* self", 10) == 0) + { + sprintf(new_args, "%s* self%s", struct_name, args + 10); + } + else + { + strcpy(new_args, args); + } + return new_args; +} +// Helper for Value-Returning Defer +static void replace_it_with_var(ASTNode *node, char *var_name) +{ + if (!node) + { + return; + } + if (node->type == NODE_EXPR_VAR) + { + if (strcmp(node->var_ref.name, "it") == 0) + { + // Replace 'it' with var_name + node->var_ref.name = xstrdup(var_name); + } + } + else if (node->type == NODE_EXPR_CALL) + { + replace_it_with_var(node->call.callee, var_name); + ASTNode *arg = node->call.args; + while (arg) + { + replace_it_with_var(arg, var_name); + arg = arg->next; + } + } + else if (node->type == NODE_EXPR_MEMBER) + { + replace_it_with_var(node->member.target, var_name); + } + else if (node->type == NODE_EXPR_BINARY) + { + replace_it_with_var(node->binary.left, var_name); + replace_it_with_var(node->binary.right, var_name); + } + else if (node->type == NODE_EXPR_UNARY) + { + replace_it_with_var(node->unary.operand, var_name); + } + else if (node->type == NODE_BLOCK) + { + ASTNode *s = node->block.statements; + while (s) + { + replace_it_with_var(s, var_name); + s = s->next; + } + } +} + +ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat 'var' + + // Destructuring: var {x, y} = ... + if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN) + { + int is_struct = (lexer_peek(l).type == TOK_LBRACE); + lexer_next(l); + char **names = xmalloc(16 * sizeof(char *)); + int count = 0; + while (1) + { + Token t = lexer_next(l); + char *nm = token_strdup(t); + // UPDATE: Pass NULL to add_symbol + names[count++] = nm; + add_symbol(ctx, nm, "unknown", NULL); + Token next = lexer_next(l); + if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN)) + { + break; + } + if (next.type != TOK_COMMA) + { + zpanic_at(next, "Expected comma"); + } + } + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected ="); + } + ASTNode *init = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = names; + n->destruct.count = count; + n->destruct.init_expr = init; + n->destruct.is_struct_destruct = is_struct; + return n; + } + + // Normal Declaration OR Named Struct Destructuring + Token name_tok = lexer_next(l); + char *name = token_strdup(name_tok); + + // Check for Struct Destructuring: var Point { x, y } + if (lexer_peek(l).type == TOK_LBRACE) + { + lexer_next(l); // eat { + char **names = xmalloc(16 * sizeof(char *)); + char **fields = xmalloc(16 * sizeof(char *)); + int count = 0; + + while (1) + { + // Parse field:name or just name + Token t = lexer_next(l); + char *ident = token_strdup(t); + + if (lexer_peek(l).type == TOK_COLON) + { + // field: var_name + lexer_next(l); // eat : + Token v = lexer_next(l); + fields[count] = ident; + names[count] = token_strdup(v); + } + else + { + // Shorthand: field (implies var name = field) + fields[count] = ident; + names[count] = ident; // Share pointer or duplicate? duplicate safer if we free + } + // Register symbol for variable + add_symbol(ctx, names[count], "unknown", NULL); + + count++; + + Token next = lexer_next(l); + if (next.type == TOK_RBRACE) + { + break; + } + if (next.type != TOK_COMMA) + { + zpanic_at(next, "Expected comma in struct pattern"); + } + } + + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected ="); + } + ASTNode *init = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = names; + n->destruct.field_names = fields; + n->destruct.count = count; + n->destruct.init_expr = init; + n->destruct.is_struct_destruct = 1; + n->destruct.struct_name = name; // "Point" + return n; + } + + // Check for Guard Pattern: var Some(val) = opt else { ... } + if (lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); // eat ( + Token val_tok = lexer_next(l); + char *val_name = token_strdup(val_tok); + + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); + } + + if (lexer_next(l).type != TOK_OP) + { + zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); + } + + ASTNode *init = parse_expression(ctx, l); + + Token t = lexer_next(l); + if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) + { + zpanic_at(t, "Expected 'else' in guard statement"); + } + + ASTNode *else_blk; + if (lexer_peek(l).type == TOK_LBRACE) + { + else_blk = parse_block(ctx, l); + } + else + { + else_blk = ast_create(NODE_BLOCK); + else_blk->block.statements = parse_statement(ctx, l); + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_DESTRUCT_VAR); + n->destruct.names = xmalloc(sizeof(char *)); + n->destruct.names[0] = val_name; + n->destruct.count = 1; + n->destruct.init_expr = init; + n->destruct.is_guard = 1; + n->destruct.guard_variant = name; + n->destruct.else_block = else_blk; + + add_symbol(ctx, val_name, "unknown", NULL); + + return n; + } + + char *type = NULL; + Type *type_obj = NULL; // --- NEW: Formal Type Object --- + + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); + // Hybrid Parse: Get Object AND String + type_obj = parse_type_formal(ctx, l); + type = type_to_string(type_obj); + } + + ASTNode *init = NULL; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); + + // Peek for special initializers + Token next = lexer_peek(l); + if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) + { + init = parse_embed(ctx, l); + + if (!type && init->type_info) + { + type = type_to_string(init->type_info); + } + if (!type) + { + register_slice(ctx, "char"); + type = xstrdup("Slice_char"); + } + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else if (next.type == TOK_LBRACKET && type && strncmp(type, "Slice_", 6) == 0) + { + char *code = parse_array_literal(ctx, l, type); + init = ast_create(NODE_RAW_STMT); + init->raw_stmt.content = code; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else if (next.type == TOK_LPAREN && type && strncmp(type, "Tuple_", 6) == 0) + { + char *code = parse_tuple_literal(ctx, l, type); + init = ast_create(NODE_RAW_STMT); + init->raw_stmt.content = code; + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + } + else + { + init = parse_expression(ctx, l); + } + + if (init && type) + { + char *rhs_type = init->resolved_type; + if (!rhs_type && init->type_info) + { + rhs_type = type_to_string(init->type_info); + } + + if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) + { + // Strip stars to get struct names + char target_struct[256]; + strcpy(target_struct, type); + target_struct[strlen(target_struct) - 1] = 0; + char source_struct[256]; + strcpy(source_struct, rhs_type); + source_struct[strlen(source_struct) - 1] = 0; + + ASTNode *def = find_struct_def(ctx, source_struct); + + if (def && def->strct.parent && strcmp(def->strct.parent, target_struct) == 0) + { + // Create Cast Node + ASTNode *cast = ast_create(NODE_EXPR_CAST); + cast->cast.target_type = xstrdup(type); + cast->cast.expr = init; + cast->type_info = type_obj; // Inherit formal type + + init = cast; // Replace init with cast + } + } + } + + // ** Type Inference Logic ** + if (!type && init) + { + if (init->type_info) + { + type_obj = init->type_info; + type = type_to_string(type_obj); + } + else if (init->type == NODE_EXPR_SLICE) + { + zpanic_at(init->token, "Slice Node has NO Type Info!"); + } + // Fallbacks for literals + else if (init->type == NODE_EXPR_LITERAL) + { + if (init->literal.type_kind == 0) + { + type = xstrdup("int"); + type_obj = type_new(TYPE_INT); + } + else if (init->literal.type_kind == 1) + { + type = xstrdup("float"); + type_obj = type_new(TYPE_FLOAT); + } + else if (init->literal.type_kind == 2) + { + type = xstrdup("string"); + type_obj = type_new(TYPE_STRING); + } + } + else if (init->type == NODE_EXPR_STRUCT_INIT) + { + type = xstrdup(init->struct_init.struct_name); + type_obj = type_new(TYPE_STRUCT); + type_obj->name = xstrdup(type); + } + } + } + + if (!type && !init) + { + zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); + } + + // Register in symbol table with actual token + add_symbol_with_token(ctx, name, type, type_obj, name_tok); + + // NEW: Capture Const Integer Values + if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == 0) + { + Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct + if (s) + { + s->is_const_value = 1; + s->const_int_val = init->literal.int_val; + } + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *n = ast_create(NODE_VAR_DECL); + n->token = name_tok; // Save location + n->var_decl.name = name; + n->var_decl.type_str = type; + n->type_info = type_obj; + + // Auto-construct Trait Object + if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY && + strcmp(init->unary.op, "&") == 0 && init->unary.operand->type == NODE_EXPR_VAR) + { + char *var_ref_name = init->unary.operand->var_ref.name; + char *struct_type = find_symbol_type(ctx, var_ref_name); + if (struct_type) + { + char *code = xmalloc(512); + sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type, var_ref_name, struct_type, + type); + ASTNode *wrapper = ast_create(NODE_RAW_STMT); + wrapper->raw_stmt.content = code; + init = wrapper; + } + } + + n->var_decl.init_expr = init; + + // Move Semantics Logic for Initialization + check_move_usage(ctx, init, init ? init->token : name_tok); + if (init && init->type == NODE_EXPR_VAR) + { + Type *t = find_symbol_type_info(ctx, init->var_ref.name); + if (!t) + { + Symbol *s = find_symbol_entry(ctx, init->var_ref.name); + if (s) + { + t = s->type_info; + } + } + if (!is_type_copy(ctx, t)) + { + Symbol *s = find_symbol_entry(ctx, init->var_ref.name); + if (s) + { + s->is_moved = 1; + } + } + } + + // Global detection: Either no scope (yet) OR root scope (no parent) + if (!ctx->current_scope || !ctx->current_scope->parent) + { + add_to_global_list(ctx, n); + } + + // Check for 'defer' (Value-Returning Defer) + // Only capture if it is NOT a block defer (defer { ... }) + // If it is a block defer, we leave it for the next parse_statement call. + if (lexer_peek(l).type == TOK_DEFER) + { + Lexer lookahead = *l; + lexer_next(&lookahead); // Eat defer + if (lexer_peek(&lookahead).type != TOK_LBRACE) + { + // Proceed to consume + lexer_next(l); // eat defer (real) + + // Parse the defer expression/statement + // Usually defer close(it); + // We parse expression. + ASTNode *expr = parse_expression(ctx, l); + + // Handle "it" substitution + replace_it_with_var(expr, name); + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *d = ast_create(NODE_DEFER); + d->defer_stmt.stmt = expr; + + // Chain it: var_decl -> defer + n->next = d; + } + } + + return n; +} + +ASTNode *parse_const(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat const + Token n = lexer_next(l); + + char *type_str = NULL; + Type *type_obj = NULL; + + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); + // Hybrid Parse + type_obj = parse_type_formal(ctx, l); + type_str = type_to_string(type_obj); + } + + char *ns = token_strdup(n); + if (!type_obj) + { + type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object + } + type_obj->is_const = 1; + add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj); + + ASTNode *i = 0; + if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) + { + lexer_next(l); + + // Check for constant integer literal + if (lexer_peek(l).type == TOK_INT) + { + Token val_tok = lexer_peek(l); + int val = atoi(token_strdup(val_tok)); // quick check + + Symbol *s = find_symbol_entry(ctx, ns); + if (s) + { + s->is_const_value = 1; + s->const_int_val = val; + + if (!s->type_name || strcmp(s->type_name, "unknown") == 0) + { + if (s->type_name) + { + free(s->type_name); + } + s->type_name = xstrdup("int"); + if (s->type_info) + { + free(s->type_info); + } + s->type_info = type_new(TYPE_INT); + } + } + } + + if (lexer_peek(l).type == TOK_LPAREN && type_str && strncmp(type_str, "Tuple_", 6) == 0) + { + char *code = parse_tuple_literal(ctx, l, type_str); + i = ast_create(NODE_RAW_STMT); + i->raw_stmt.content = code; + } + else + { + i = parse_expression(ctx, l); + } + } + else + { + lexer_next(l); + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + } + + ASTNode *o = ast_create(NODE_CONST); + o->var_decl.name = ns; + o->var_decl.type_str = type_str; + o->var_decl.init_expr = i; + + if (!ctx->current_scope || !ctx->current_scope->parent) + { + add_to_global_list(ctx, o); + } + + return o; +} + +ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // consume 'type' or 'alias' + Token n = lexer_next(l); + if (n.type != TOK_IDENT) + { + zpanic_at(n, "Expected identifier for type alias"); + } + + lexer_next(l); // consume '=' + + char *o = parse_type(ctx, l); + + lexer_next(l); // consume ';' (parse_type doesn't consume it? parse_type calls parse_type_formal + // which doesn't consume ;?) + // Note: parse_type_stmt usually expects ; but parse_type just parses type expression. + // Check previous implementation: it had lexer_next(l) at end. This assumes ;. + + ASTNode *node = ast_create(NODE_TYPE_ALIAS); + node->type_alias.alias = xmalloc(n.len + 1); + strncpy(node->type_alias.alias, n.start, n.len); + node->type_alias.alias[n.len] = 0; + node->type_alias.original_type = o; + + register_type_alias(ctx, node->type_alias.alias, o); + + return node; +} diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 67045ea..5eefb81 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -12,7 +12,7 @@ #include "zprep_plugin.h" #include "../codegen/codegen.h" -static char *curr_func_ret = NULL; +char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); static void check_assignment_condition(ASTNode *cond) @@ -31,211 +31,6 @@ static void check_assignment_condition(ASTNode *cond) } } -ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) -{ - lexer_next(l); // eat 'fn' - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); - - if (is_async) - { - ctx->has_async = 1; - } - - // Check for C reserved word conflict - if (is_c_reserved_word(name)) - { - warn_c_reserved_word(name_tok, name); - } - - char *gen_param = NULL; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - - char buf[1024]; - buf[0] = 0; - - while (1) - { - Token gt = lexer_next(l); - if (gt.type != TOK_IDENT) - { - zpanic_at(gt, "Expected generic parameter name"); - } - char *s = token_strdup(gt); - - if (strlen(buf) + strlen(s) + 2 >= sizeof(buf)) - { - zpanic_at(gt, "Too many generic parameters"); - } - - if (buf[0]) - { - strcat(buf, ","); - } - strcat(buf, s); - free(s); - - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - continue; - } - break; - } - - if (lexer_next(l).type != TOK_RANGLE) - { - zpanic_at(lexer_peek(l), "Expected >"); - } - gen_param = xstrdup(buf); - } - - // Register generic parameters so type parsing recognizes them - int saved_generic_count = ctx->known_generics_count; - if (gen_param) - { - char *tmp = xstrdup(gen_param); - char *tok = strtok(tmp, ","); - while (tok) - { - register_generic(ctx, tok); - tok = strtok(NULL, ","); - } - free(tmp); - } - - enter_scope(ctx); - char **defaults; - int count; - Type **arg_types; - char **param_names; - int is_varargs = 0; - - char *args = - parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); - - char *ret = "void"; - Type *ret_type_obj = type_new(TYPE_VOID); - - if (strcmp(name, "main") == 0) - { - ret = "int"; - ret_type_obj = type_new(TYPE_INT); - } - - if (lexer_peek(l).type == TOK_ARROW) - { - lexer_next(l); - ret_type_obj = parse_type_formal(ctx, l); - ret = type_to_string(ret_type_obj); - } - - extern char *curr_func_ret; - curr_func_ret = ret; - - // Auto-prefix function name if in module context - // Don't prefix generic templates or functions inside impl blocks (already - // mangled) - if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) - { - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); - free(name); - name = prefixed_name; - } - - // Register if concrete (Global functions only) - if (!gen_param && !ctx->current_impl_struct) - { - register_func(ctx, name, count, defaults, arg_types, ret_type_obj, is_varargs, is_async, - name_tok); - // Note: must_use is set after return by caller (parser_core.c) - } - - ASTNode *body = NULL; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); // consume ; - } - else - { - body = parse_block(ctx, l); - } - - // Check for unused parameters - // The current scope contains arguments (since parse_block creates a new child - // scope for body) Only check if we parsed a body (not a prototype) function - if (body && ctx->current_scope) - { - Symbol *sym = ctx->current_scope->symbols; - while (sym) - { - // Check if unused and not prefixed with '_' (conventional ignore) - // also ignore 'self' as it is often mandated by traits - if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "self") != 0 && - strcmp(name, "main") != 0) - { - warn_unused_parameter(sym->decl_token, sym->name, name); - } - sym = sym->next; - } - } - - exit_scope(ctx); - - // Restore generic count to unregister function-scoped generics - ctx->known_generics_count = saved_generic_count; - - curr_func_ret = NULL; - - ASTNode *node = ast_create(NODE_FUNCTION); - node->token = name_tok; // Save definition location - node->func.name = name; - node->func.args = args; - node->func.ret_type = ret; - node->func.body = body; - - node->func.arg_types = arg_types; - node->func.param_names = param_names; - node->func.arg_count = count; - node->func.defaults = defaults; - node->func.ret_type_info = ret_type_obj; - node->func.is_varargs = is_varargs; - - if (gen_param) - { - register_func_template(ctx, name, gen_param, node); - return NULL; - } - if (!ctx->current_impl_struct) - { - add_to_func_list(ctx, node); - } - return node; -} - -char *patch_self_args(const char *args, const char *struct_name) -{ - if (!args) - { - return NULL; - } - char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10); - - // Check if it starts with "void* self" - if (strncmp(args, "void* self", 10) == 0) - { - sprintf(new_args, "%s* self%s", struct_name, args + 10); - } - else - { - strcpy(new_args, args); - } - return new_args; -} - ASTNode *parse_match(ParserContext *ctx, Lexer *l) { init_builtins(); @@ -956,591 +751,6 @@ ASTNode *parse_assert(ParserContext *ctx, Lexer *l) return n; } -// Helper for Value-Returning Defer -static void replace_it_with_var(ASTNode *node, char *var_name) -{ - if (!node) - { - return; - } - if (node->type == NODE_EXPR_VAR) - { - if (strcmp(node->var_ref.name, "it") == 0) - { - // Replace 'it' with var_name - node->var_ref.name = xstrdup(var_name); - } - } - else if (node->type == NODE_EXPR_CALL) - { - replace_it_with_var(node->call.callee, var_name); - ASTNode *arg = node->call.args; - while (arg) - { - replace_it_with_var(arg, var_name); - arg = arg->next; - } - } - else if (node->type == NODE_EXPR_MEMBER) - { - replace_it_with_var(node->member.target, var_name); - } - else if (node->type == NODE_EXPR_BINARY) - { - replace_it_with_var(node->binary.left, var_name); - replace_it_with_var(node->binary.right, var_name); - } - else if (node->type == NODE_EXPR_UNARY) - { - replace_it_with_var(node->unary.operand, var_name); - } - else if (node->type == NODE_BLOCK) - { - ASTNode *s = node->block.statements; - while (s) - { - replace_it_with_var(s, var_name); - s = s->next; - } - } -} - -ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat 'var' - - // Destructuring: var {x, y} = ... - if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN) - { - int is_struct = (lexer_peek(l).type == TOK_LBRACE); - lexer_next(l); - char **names = xmalloc(16 * sizeof(char *)); - int count = 0; - while (1) - { - Token t = lexer_next(l); - char *nm = token_strdup(t); - // UPDATE: Pass NULL to add_symbol - names[count++] = nm; - add_symbol(ctx, nm, "unknown", NULL); - Token next = lexer_next(l); - if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN)) - { - break; - } - if (next.type != TOK_COMMA) - { - zpanic_at(next, "Expected comma"); - } - } - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected ="); - } - ASTNode *init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = names; - n->destruct.count = count; - n->destruct.init_expr = init; - n->destruct.is_struct_destruct = is_struct; - return n; - } - - // Normal Declaration OR Named Struct Destructuring - Token name_tok = lexer_next(l); - char *name = token_strdup(name_tok); - - // Check for Struct Destructuring: var Point { x, y } - if (lexer_peek(l).type == TOK_LBRACE) - { - lexer_next(l); // eat { - char **names = xmalloc(16 * sizeof(char *)); - char **fields = xmalloc(16 * sizeof(char *)); - int count = 0; - - while (1) - { - // Parse field:name or just name - Token t = lexer_next(l); - char *ident = token_strdup(t); - - if (lexer_peek(l).type == TOK_COLON) - { - // field: var_name - lexer_next(l); // eat : - Token v = lexer_next(l); - fields[count] = ident; - names[count] = token_strdup(v); - } - else - { - // Shorthand: field (implies var name = field) - fields[count] = ident; - names[count] = ident; // Share pointer or duplicate? duplicate safer if we free - } - // Register symbol for variable - add_symbol(ctx, names[count], "unknown", NULL); - - count++; - - Token next = lexer_next(l); - if (next.type == TOK_RBRACE) - { - break; - } - if (next.type != TOK_COMMA) - { - zpanic_at(next, "Expected comma in struct pattern"); - } - } - - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected ="); - } - ASTNode *init = parse_expression(ctx, l); - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = names; - n->destruct.field_names = fields; - n->destruct.count = count; - n->destruct.init_expr = init; - n->destruct.is_struct_destruct = 1; - n->destruct.struct_name = name; // "Point" - return n; - } - - // Check for Guard Pattern: var Some(val) = opt else { ... } - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); // eat ( - Token val_tok = lexer_next(l); - char *val_name = token_strdup(val_tok); - - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected ')' in guard pattern"); - } - - if (lexer_next(l).type != TOK_OP) - { - zpanic_at(lexer_peek(l), "Expected '=' after guard pattern"); - } - - ASTNode *init = parse_expression(ctx, l); - - Token t = lexer_next(l); - if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) - { - zpanic_at(t, "Expected 'else' in guard statement"); - } - - ASTNode *else_blk; - if (lexer_peek(l).type == TOK_LBRACE) - { - else_blk = parse_block(ctx, l); - } - else - { - else_blk = ast_create(NODE_BLOCK); - else_blk->block.statements = parse_statement(ctx, l); - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *n = ast_create(NODE_DESTRUCT_VAR); - n->destruct.names = xmalloc(sizeof(char *)); - n->destruct.names[0] = val_name; - n->destruct.count = 1; - n->destruct.init_expr = init; - n->destruct.is_guard = 1; - n->destruct.guard_variant = name; - n->destruct.else_block = else_blk; - - add_symbol(ctx, val_name, "unknown", NULL); - - return n; - } - - char *type = NULL; - Type *type_obj = NULL; // --- NEW: Formal Type Object --- - - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); - // Hybrid Parse: Get Object AND String - type_obj = parse_type_formal(ctx, l); - type = type_to_string(type_obj); - } - - ASTNode *init = NULL; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) - { - lexer_next(l); - - // Peek for special initializers - Token next = lexer_peek(l); - if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) - { - init = parse_embed(ctx, l); - - if (!type && init->type_info) - { - type = type_to_string(init->type_info); - } - if (!type) - { - register_slice(ctx, "char"); - type = xstrdup("Slice_char"); - } - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else if (next.type == TOK_LBRACKET && type && strncmp(type, "Slice_", 6) == 0) - { - char *code = parse_array_literal(ctx, l, type); - init = ast_create(NODE_RAW_STMT); - init->raw_stmt.content = code; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else if (next.type == TOK_LPAREN && type && strncmp(type, "Tuple_", 6) == 0) - { - char *code = parse_tuple_literal(ctx, l, type); - init = ast_create(NODE_RAW_STMT); - init->raw_stmt.content = code; - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - } - else - { - init = parse_expression(ctx, l); - } - - if (init && type) - { - char *rhs_type = init->resolved_type; - if (!rhs_type && init->type_info) - { - rhs_type = type_to_string(init->type_info); - } - - if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) - { - // Strip stars to get struct names - char target_struct[256]; - strcpy(target_struct, type); - target_struct[strlen(target_struct) - 1] = 0; - char source_struct[256]; - strcpy(source_struct, rhs_type); - source_struct[strlen(source_struct) - 1] = 0; - - ASTNode *def = find_struct_def(ctx, source_struct); - - if (def && def->strct.parent && strcmp(def->strct.parent, target_struct) == 0) - { - // Create Cast Node - ASTNode *cast = ast_create(NODE_EXPR_CAST); - cast->cast.target_type = xstrdup(type); - cast->cast.expr = init; - cast->type_info = type_obj; // Inherit formal type - - init = cast; // Replace init with cast - } - } - } - - // ** Type Inference Logic ** - if (!type && init) - { - if (init->type_info) - { - type_obj = init->type_info; - type = type_to_string(type_obj); - } - else if (init->type == NODE_EXPR_SLICE) - { - zpanic_at(init->token, "Slice Node has NO Type Info!"); - } - // Fallbacks for literals - else if (init->type == NODE_EXPR_LITERAL) - { - if (init->literal.type_kind == 0) - { - type = xstrdup("int"); - type_obj = type_new(TYPE_INT); - } - else if (init->literal.type_kind == 1) - { - type = xstrdup("float"); - type_obj = type_new(TYPE_FLOAT); - } - else if (init->literal.type_kind == 2) - { - type = xstrdup("string"); - type_obj = type_new(TYPE_STRING); - } - } - else if (init->type == NODE_EXPR_STRUCT_INIT) - { - type = xstrdup(init->struct_init.struct_name); - type_obj = type_new(TYPE_STRUCT); - type_obj->name = xstrdup(type); - } - } - } - - if (!type && !init) - { - zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name); - } - - // Register in symbol table with actual token - add_symbol_with_token(ctx, name, type, type_obj, name_tok); - - // NEW: Capture Const Integer Values - if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == 0) - { - Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct - if (s) - { - s->is_const_value = 1; - s->const_int_val = init->literal.int_val; - } - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *n = ast_create(NODE_VAR_DECL); - n->token = name_tok; // Save location - n->var_decl.name = name; - n->var_decl.type_str = type; - n->type_info = type_obj; - - // Auto-construct Trait Object - if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY && - strcmp(init->unary.op, "&") == 0 && init->unary.operand->type == NODE_EXPR_VAR) - { - char *var_ref_name = init->unary.operand->var_ref.name; - char *struct_type = find_symbol_type(ctx, var_ref_name); - if (struct_type) - { - char *code = xmalloc(512); - sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type, var_ref_name, struct_type, - type); - ASTNode *wrapper = ast_create(NODE_RAW_STMT); - wrapper->raw_stmt.content = code; - init = wrapper; - } - } - - n->var_decl.init_expr = init; - - // Move Semantics Logic for Initialization - check_move_usage(ctx, init, init ? init->token : name_tok); - if (init && init->type == NODE_EXPR_VAR) - { - Type *t = find_symbol_type_info(ctx, init->var_ref.name); - if (!t) - { - Symbol *s = find_symbol_entry(ctx, init->var_ref.name); - if (s) - { - t = s->type_info; - } - } - if (!is_type_copy(ctx, t)) - { - Symbol *s = find_symbol_entry(ctx, init->var_ref.name); - if (s) - { - s->is_moved = 1; - } - } - } - - // Global detection: Either no scope (yet) OR root scope (no parent) - if (!ctx->current_scope || !ctx->current_scope->parent) - { - add_to_global_list(ctx, n); - } - - // Check for 'defer' (Value-Returning Defer) - // Only capture if it is NOT a block defer (defer { ... }) - // If it is a block defer, we leave it for the next parse_statement call. - if (lexer_peek(l).type == TOK_DEFER) - { - Lexer lookahead = *l; - lexer_next(&lookahead); // Eat defer - if (lexer_peek(&lookahead).type != TOK_LBRACE) - { - // Proceed to consume - lexer_next(l); // eat defer (real) - - // Parse the defer expression/statement - // Usually defer close(it); - // We parse expression. - ASTNode *expr = parse_expression(ctx, l); - - // Handle "it" substitution - replace_it_with_var(expr, name); - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *d = ast_create(NODE_DEFER); - d->defer_stmt.stmt = expr; - - // Chain it: var_decl -> defer - n->next = d; - } - } - - return n; -} - -ASTNode *parse_const(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // eat const - Token n = lexer_next(l); - - char *type_str = NULL; - Type *type_obj = NULL; - - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); - // Hybrid Parse - type_obj = parse_type_formal(ctx, l); - type_str = type_to_string(type_obj); - } - - char *ns = token_strdup(n); - if (!type_obj) - { - type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object - } - type_obj->is_const = 1; - add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj); - - ASTNode *i = 0; - if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) - { - lexer_next(l); - - // Check for constant integer literal - if (lexer_peek(l).type == TOK_INT) - { - Token val_tok = lexer_peek(l); - int val = atoi(token_strdup(val_tok)); // quick check - - Symbol *s = find_symbol_entry(ctx, ns); - if (s) - { - s->is_const_value = 1; - s->const_int_val = val; - - if (!s->type_name || strcmp(s->type_name, "unknown") == 0) - { - if (s->type_name) - { - free(s->type_name); - } - s->type_name = xstrdup("int"); - if (s->type_info) - { - free(s->type_info); - } - s->type_info = type_new(TYPE_INT); - } - } - } - - if (lexer_peek(l).type == TOK_LPAREN && type_str && strncmp(type_str, "Tuple_", 6) == 0) - { - char *code = parse_tuple_literal(ctx, l, type_str); - i = ast_create(NODE_RAW_STMT); - i->raw_stmt.content = code; - } - else - { - i = parse_expression(ctx, l); - } - } - else - { - lexer_next(l); - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - } - - ASTNode *o = ast_create(NODE_CONST); - o->var_decl.name = ns; - o->var_decl.type_str = type_str; - o->var_decl.init_expr = i; - - if (!ctx->current_scope || !ctx->current_scope->parent) - { - add_to_global_list(ctx, o); - } - - return o; -} - -ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); // consume 'type' or 'alias' - Token n = lexer_next(l); - if (n.type != TOK_IDENT) - { - zpanic_at(n, "Expected identifier for type alias"); - } - - lexer_next(l); // consume '=' - - char *o = parse_type(ctx, l); - - lexer_next(l); // consume ';' (parse_type doesn't consume it? parse_type calls parse_type_formal - // which doesn't consume ;?) - // Note: parse_type_stmt usually expects ; but parse_type just parses type expression. - // Check previous implementation: it had lexer_next(l) at end. This assumes ;. - - ASTNode *node = ast_create(NODE_TYPE_ALIAS); - node->type_alias.alias = xmalloc(n.len + 1); - strncpy(node->type_alias.alias, n.start, n.len); - node->type_alias.alias[n.len] = 0; - node->type_alias.original_type = o; - - register_type_alias(ctx, node->type_alias.alias, o); - - return node; -} - ASTNode *parse_return(ParserContext *ctx, Lexer *l) { Token return_token = lexer_next(l); // eat 'return' @@ -3294,935 +2504,6 @@ ASTNode *parse_block(ParserContext *ctx, Lexer *l) 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; - - // Generics <T> - char **generic_params = NULL; - int generic_count = 0; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - generic_params = xmalloc(sizeof(char *) * 8); // simplified - while (1) - { - Token p = lexer_next(l); - if (p.type != TOK_IDENT) - { - zpanic_at(p, "Expected generic parameter name"); - } - generic_params[generic_count] = xmalloc(p.len + 1); - strncpy(generic_params[generic_count], p.start, p.len); - generic_params[generic_count][p.len] = 0; - generic_count++; - - Token sep = lexer_peek(l); - if (sep.type == TOK_COMMA) - { - lexer_next(l); - continue; - } - else if (sep.type == TOK_RANGLE) - { - lexer_next(l); - break; - } - else - { - zpanic_at(sep, "Expected , or > in generic params"); - } - } - } - - if (generic_count > 0) - { - for (int i = 0; i < generic_count; i++) - { - register_generic(ctx, generic_params[i]); - } - } - - lexer_next(l); // eat { - - ASTNode *methods = NULL, *tail = NULL; - while (1) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - - // Parse method signature: fn name(args...) -> ret; - Token ft = lexer_next(l); - if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) - { - zpanic_at(ft, "Expected fn in trait"); - } - - Token mn = lexer_next(l); - char *mname = xmalloc(mn.len + 1); - strncpy(mname, mn.start, mn.len); - mname[mn.len] = 0; - - char **defaults = NULL; - int arg_count = 0; - Type **arg_types = NULL; - char **param_names = NULL; - int is_varargs = 0; - char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, - &is_varargs); - - char *ret = xstrdup("void"); - if (lexer_peek(l).type == TOK_ARROW) - { - lexer_next(l); - char *rt = parse_type(ctx, l); - free(ret); - ret = rt; - } - - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - ASTNode *m = ast_create(NODE_FUNCTION); - m->func.param_names = param_names; - m->func.name = mname; - m->func.args = args; - m->func.ret_type = ret; - m->func.body = NULL; - if (!methods) - { - methods = m; - } - else - { - tail->next = m; - } - tail = m; - } - else - { - // Default implementation? Not supported yet. - zpanic_at(lexer_peek(l), "Trait methods must end with ; for now"); - } - } - - ASTNode *n_node = ast_create(NODE_TRAIT); - n_node->trait.name = name; - n_node->trait.methods = methods; - n_node->trait.generic_params = generic_params; - n_node->trait.generic_param_count = generic_count; - register_trait(name); - return n_node; -} - -ASTNode *parse_impl(ParserContext *ctx, Lexer *l) -{ - - lexer_next(l); // eat impl - Token t1 = lexer_next(l); - char *name1 = token_strdup(t1); - - char *gen_param = NULL; - // Check for <T> on the struct name - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - Token gt = lexer_next(l); - gen_param = token_strdup(gt); - if (lexer_next(l).type != TOK_RANGLE) - { - zpanic_at(lexer_peek(l), "Expected >"); - } - } - - // Check for "for" (Trait impl) - Token pk = lexer_peek(l); - if (pk.type == TOK_FOR || - (pk.type == TOK_IDENT && strncmp(pk.start, "for", 3) == 0 && pk.len == 3)) - { - if (pk.type != TOK_FOR) - { - lexer_next(l); - } - else - { - lexer_next(l); // eat for - } - Token t2 = lexer_next(l); - char *name2 = token_strdup(t2); - - register_impl(ctx, name1, name2); - - // RAII: Check for "Drop" trait implementation - if (strcmp(name1, "Drop") == 0) - { - Symbol *s = find_symbol_entry(ctx, name2); - if (s && s->type_info) - { - s->type_info->traits.has_drop = 1; - } - else - { - // Try finding struct definition - ASTNode *def = find_struct_def(ctx, name2); - if (def && def->type_info) - { - def->type_info->traits.has_drop = 1; - } - } - } - - // Iterator: Check for "Iterable" trait implementation - else if (strcmp(name1, "Iterable") == 0) - { - Symbol *s = find_symbol_entry(ctx, name2); - if (s && s->type_info) - { - s->type_info->traits.has_iterable = 1; - } - else - { - // Try finding struct definition - ASTNode *def = find_struct_def(ctx, name2); - if (def && def->type_info) - { - def->type_info->traits.has_iterable = 1; - } - } - } - - ctx->current_impl_struct = name2; // Set context to prevent duplicate emission and prefixing - - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - while (1) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 0); - // Mangle: Type_Trait_Method - char *mangled = xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4); - sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name2); - free(f->func.args); - f->func.args = na; - - // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, - f->func.ret_type_info, f->func.is_varargs, f->func.is_async, - f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else if (lexer_peek(l).type == TOK_ASYNC) - { - lexer_next(l); // eat async - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - // Mangle: Type_Trait_Method - char *mangled = - xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 5); - sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name2); - free(f->func.args); - f->func.args = na; - - // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - f->func.is_async, f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } - else - { - lexer_next(l); - } - } - ctx->current_impl_struct = NULL; // Restore context - ASTNode *n = ast_create(NODE_IMPL_TRAIT); - n->impl_trait.trait_name = name1; - n->impl_trait.target_type = name2; - n->impl_trait.methods = h; - add_to_impl_list(ctx, n); - - // If target struct is generic, register this impl as a template - ASTNode *def = find_struct_def(ctx, name2); - if (def && ((def->type == NODE_STRUCT && def->strct.is_template) || - (def->type == NODE_ENUM && def->enm.is_template))) - { - const char *gp = "T"; - if (def->type == NODE_STRUCT && def->strct.generic_param_count > 0) - { - gp = def->strct.generic_params[0]; - } - // TODO: Enum generic params support if needed - register_impl_template(ctx, name2, gp, n); - } - - return n; - } - else - { - // Regular impl Struct (impl Box or impl Box<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; - } - - 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 {"); - } - char *full_struct_name = xmalloc(strlen(name1) + strlen(gen_param) + 3); - sprintf(full_struct_name, "%s<%s>", name1, gen_param); - - ASTNode *h = 0, *tl = 0; - ctx->current_impl_methods = NULL; - while (1) - { - ctx->current_impl_methods = h; - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 0); - // Standard Mangle for template: Box_method - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - // Update args string - char *na = patch_self_args(f->func.args, full_struct_name); - free(f->func.args); - f->func.args = na; - - // Manual Type construction for self: Foo<T>* - if (f->func.arg_count > 0 && f->func.param_names && - strcmp(f->func.param_names[0], "self") == 0) - { - Type *t_struct = type_new(TYPE_STRUCT); - t_struct->name = xstrdup(name1); - t_struct->arg_count = 1; - t_struct->args = xmalloc(sizeof(Type *)); - t_struct->args[0] = type_new(TYPE_GENERIC); - t_struct->args[0]->name = xstrdup(gen_param); - - Type *t_ptr = type_new(TYPE_POINTER); - t_ptr->inner = t_struct; - - f->func.arg_types[0] = t_ptr; - } - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else if (lexer_peek(l).type == TOK_ASYNC) - { - lexer_next(l); // eat async - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - char *na = patch_self_args(f->func.args, full_struct_name); - free(f->func.args); - f->func.args = na; - - if (f->func.arg_count > 0 && f->func.param_names && - strcmp(f->func.param_names[0], "self") == 0) - { - Type *t_struct = type_new(TYPE_STRUCT); - t_struct->name = xstrdup(name1); - t_struct->arg_count = 1; - t_struct->args = xmalloc(sizeof(Type *)); - t_struct->args[0] = type_new(TYPE_GENERIC); - t_struct->args[0]->name = xstrdup(gen_param); - - Type *t_ptr = type_new(TYPE_POINTER); - t_ptr->inner = t_struct; - - f->func.arg_types[0] = t_ptr; - } - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } - else - { - lexer_next(l); - } - } - free(full_struct_name); - // Register Template - ASTNode *n = ast_create(NODE_IMPL); - n->impl.struct_name = name1; - n->impl.methods = h; - register_impl_template(ctx, name1, gen_param, n); - ctx->current_impl_struct = NULL; - return NULL; // Do not emit generic template - } - else - { - // REGULAR IMPL - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - while (1) - { - skip_comments(l); - if (lexer_peek(l).type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 0); - - // Standard Mangle: Struct_method - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - - char *na = patch_self_args(f->func.args, name1); - free(f->func.args); - f->func.args = na; - - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, - f->token); - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else if (lexer_peek(l).type == TOK_ASYNC) - { - lexer_next(l); - if (lexer_peek(l).type == TOK_IDENT && - strncmp(lexer_peek(l).start, "fn", 2) == 0) - { - ASTNode *f = parse_function(ctx, l, 1); - f->func.is_async = 1; - char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); - sprintf(mangled, "%s__%s", name1, f->func.name); - free(f->func.name); - f->func.name = mangled; - char *na = patch_self_args(f->func.args, name1); - free(f->func.args); - f->func.args = na; - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - 1, f->token); - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - } - else - { - zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); - } - } - else - { - lexer_next(l); - } - } - ctx->current_impl_struct = NULL; - ASTNode *n = ast_create(NODE_IMPL); - n->impl.struct_name = name1; - n->impl.methods = h; - add_to_impl_list(ctx, n); - return n; - } - } -} - -ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) -{ - - lexer_next(l); // eat struct or union - Token n = lexer_next(l); - char *name = token_strdup(n); - - // Generic Params <T> or <K, V> - char **gps = NULL; - int gp_count = 0; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - while (1) - { - Token g = lexer_next(l); - gps = realloc(gps, sizeof(char *) * (gp_count + 1)); - gps[gp_count++] = token_strdup(g); - - Token next = lexer_peek(l); - if (next.type == TOK_COMMA) - { - lexer_next(l); // eat , - } - else if (next.type == TOK_RANGLE) - { - lexer_next(l); // eat > - break; - } - else - { - zpanic_at(next, "Expected ',' or '>' in generic parameter list"); - } - } - register_generic(ctx, name); - } - - // Check for prototype (forward declaration) - if (lexer_peek(l).type == TOK_SEMICOLON) - { - lexer_next(l); - ASTNode *n = ast_create(NODE_STRUCT); - n->strct.name = name; - n->strct.is_template = (gp_count > 0); - n->strct.generic_params = gps; - n->strct.generic_param_count = gp_count; - n->strct.is_union = is_union; - n->strct.fields = NULL; - n->strct.is_incomplete = 1; - - return n; - } - - lexer_next(l); // eat { - ASTNode *h = 0, *tl = 0; - - // Temp storage for used structs - char **temp_used_structs = NULL; - int temp_used_count = 0; - - while (1) - { - skip_comments(l); - Token t = lexer_peek(l); - // printf("DEBUG: parse_struct loop seeing '%.*s'\n", t.len, t.start); - if (t.type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } - - // Handle 'use' (Struct Embedding) - if (t.type == TOK_USE) - { - lexer_next(l); // eat use - - // Check for named use: use name: Type; - Token t1 = lexer_peek(l); - Token t2 = lexer_peek2(l); - - if (t1.type == TOK_IDENT && t2.type == TOK_COLON) - { - // Named use -> Composition (Add field, don't flatten) - Token field_name = lexer_next(l); - lexer_next(l); // eat : - char *field_type_str = parse_type(ctx, l); - expect(l, TOK_SEMICOLON, "Expected ;"); - - ASTNode *nf = ast_create(NODE_FIELD); - nf->field.name = token_strdup(field_name); - nf->field.type = field_type_str; - - if (!h) - { - h = nf; - } - else - { - tl->next = nf; - } - tl = nf; - continue; - } - - // Normal use -> Mixin (Flatten) - // Parse the type (e.g. Header<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) - { - if (!temp_used_structs) - { - temp_used_structs = xmalloc(sizeof(char *) * 8); - } - temp_used_structs[temp_used_count++] = xstrdup(use_name); - - ASTNode *f = def->strct.fields; - while (f) - { - ASTNode *nf = ast_create(NODE_FIELD); - nf->field.name = xstrdup(f->field.name); - nf->field.type = xstrdup(f->field.type); - if (!h) - { - h = nf; - } - else - { - tl->next = nf; - } - tl = nf; - f = f->next; - } - } - else - { - // If definition not found (e.g. user struct defined later), we can't - // embed fields yet. Compiler limitation: 'use' requires struct to be - // defined before. Fallback: Emit a placeholder field so compilation - // doesn't crash, but layout will be wrong. printf("Warning: Could not - // find struct '%s' for embedding.\n", use_name); - } - free(use_name); - continue; - } - // --------------------------------------- - - if (t.type == TOK_IDENT) - { - Token f_name = lexer_next(l); - expect(l, TOK_COLON, "Expected :"); - char *f_type = parse_type(ctx, l); - - ASTNode *f = ast_create(NODE_FIELD); - f->field.name = token_strdup(f_name); - f->field.type = f_type; - f->field.bit_width = 0; - - // Optional bit width: name: type : 3 - if (lexer_peek(l).type == TOK_COLON) - { - lexer_next(l); // eat : - Token width_tok = lexer_next(l); - if (width_tok.type != TOK_INT) - { - zpanic_at(width_tok, "Expected bit width integer"); - } - f->field.bit_width = atoi(token_strdup(width_tok)); - } - - if (!h) - { - h = f; - } - else - { - tl->next = f; - } - tl = f; - - if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - } - else - { - lexer_next(l); - } - } - - ASTNode *node = ast_create(NODE_STRUCT); - add_to_struct_list(ctx, node); - - // Auto-prefix struct name if in module context - if (ctx->current_module_prefix && gp_count == 0) - { // Don't prefix generic templates - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); - free(name); - name = prefixed_name; - } - - node->strct.name = name; - - // Initialize Type Info so we can track traits (like Drop) - node->type_info = type_new(TYPE_STRUCT); - node->type_info->name = xstrdup(name); - if (gp_count > 0) - { - node->type_info->kind = TYPE_GENERIC; - // TODO: track generic params - } - - node->strct.fields = h; - node->strct.generic_params = gps; - node->strct.generic_param_count = gp_count; - node->strct.is_union = is_union; - node->strct.used_structs = temp_used_structs; - node->strct.used_struct_count = temp_used_count; - - if (gp_count > 0) - { - node->strct.is_template = 1; - register_template(ctx, name, node); - } - - // Register definition for 'use' lookups and LSP - if (gp_count == 0) - { - register_struct_def(ctx, name, node); - } - - return node; -} - -Type *parse_type_obj(ParserContext *ctx, Lexer *l) -{ - // Parse the base type (int, U32, MyStruct, etc.) - Type *t = parse_type_base(ctx, l); - - // Handle Pointers - while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') - { - lexer_next(l); // eat * - // Wrap the current type in a Pointer type - Type *ptr = type_new(TYPE_POINTER); - ptr->inner = t; - t = ptr; - } - - return t; -} - -ASTNode *parse_enum(ParserContext *ctx, Lexer *l) -{ - lexer_next(l); - Token n = lexer_next(l); - - // 1. Check for Generic <T> - char *gp = NULL; - if (lexer_peek(l).type == TOK_LANGLE) - { - lexer_next(l); // eat < - Token g = lexer_next(l); - gp = token_strdup(g); - lexer_next(l); // eat > - register_generic(ctx, n.start ? token_strdup(n) : "anon"); - } - - lexer_next(l); // eat { - - ASTNode *h = 0, *tl = 0; - int v = 0; - char *ename = token_strdup(n); // Store enum name - - while (1) - { - skip_comments(l); - Token t = lexer_peek(l); - if (t.type == TOK_RBRACE) - { - lexer_next(l); - break; - } - if (t.type == TOK_COMMA) - { - lexer_next(l); - continue; - } - - if (t.type == TOK_IDENT) - { - Token vt = lexer_next(l); - char *vname = token_strdup(vt); - - // 2. Parse Payload Type (Ok(int)) - Type *payload = NULL; - if (lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - payload = parse_type_obj(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic_at(lexer_peek(l), "Expected )"); - } - } - - ASTNode *va = ast_create(NODE_ENUM_VARIANT); - va->variant.name = vname; - va->variant.tag_id = v++; // Use tag_id instead of value - va->variant.payload = payload; // Store Type* - - // Register Variant (Mangled name to avoid collisions: Result_Ok) - char mangled[256]; - sprintf(mangled, "%s_%s", ename, vname); - register_enum_variant(ctx, ename, mangled, va->variant.tag_id); - - // Handle explicit assignment: Ok = 5 - if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=') - { - lexer_next(l); - va->variant.tag_id = atoi(lexer_next(l).start); - v = va->variant.tag_id + 1; - } - - if (!h) - { - h = va; - } - else - { - tl->next = va; - } - tl = va; - } - else - { - lexer_next(l); - } - } - - // Auto-prefix enum name if in module context - if (ctx->current_module_prefix && !gp) - { // Don't prefix generic templates - char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2); - sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename); - free(ename); - ename = prefixed_name; - } - - ASTNode *node = ast_create(NODE_ENUM); - node->enm.name = ename; - - node->enm.variants = h; - node->enm.generic_param = gp; // 3. Store generic param - - if (gp) - { - node->enm.is_template = 1; - register_template(ctx, node->enm.name, node); - } - - add_to_enum_list(ctx, node); // Register globally - - return node; -} ASTNode *parse_include(ParserContext *ctx, Lexer *l) { lexer_next(l); // eat 'include' diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c new file mode 100644 index 0000000..600d60d --- /dev/null +++ b/src/parser/parser_struct.c @@ -0,0 +1,943 @@ + +#include "parser.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../ast/ast.h" +#include "../plugins/plugin_manager.h" +#include "../zen/zen_facts.h" +#include "zprep_plugin.h" +#include "../codegen/codegen.h" + +// Trait Parsing +ASTNode *parse_trait(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); // eat trait + Token n = lexer_next(l); + if (n.type != TOK_IDENT) + { + zpanic_at(n, "Expected trait name"); + } + char *name = xmalloc(n.len + 1); + strncpy(name, n.start, n.len); + name[n.len] = 0; + + // Generics <T> + char **generic_params = NULL; + int generic_count = 0; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + generic_params = xmalloc(sizeof(char *) * 8); // simplified + while (1) + { + Token p = lexer_next(l); + if (p.type != TOK_IDENT) + { + zpanic_at(p, "Expected generic parameter name"); + } + generic_params[generic_count] = xmalloc(p.len + 1); + strncpy(generic_params[generic_count], p.start, p.len); + generic_params[generic_count][p.len] = 0; + generic_count++; + + Token sep = lexer_peek(l); + if (sep.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + else if (sep.type == TOK_RANGLE) + { + lexer_next(l); + break; + } + else + { + zpanic_at(sep, "Expected , or > in generic params"); + } + } + } + + if (generic_count > 0) + { + for (int i = 0; i < generic_count; i++) + { + register_generic(ctx, generic_params[i]); + } + } + + lexer_next(l); // eat { + + ASTNode *methods = NULL, *tail = NULL; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + + // Parse method signature: fn name(args...) -> ret; + Token ft = lexer_next(l); + if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) + { + zpanic_at(ft, "Expected fn in trait"); + } + + Token mn = lexer_next(l); + char *mname = xmalloc(mn.len + 1); + strncpy(mname, mn.start, mn.len); + mname[mn.len] = 0; + + char **defaults = NULL; + int arg_count = 0; + Type **arg_types = NULL; + char **param_names = NULL; + int is_varargs = 0; + char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, + &is_varargs); + + char *ret = xstrdup("void"); + if (lexer_peek(l).type == TOK_ARROW) + { + lexer_next(l); + char *rt = parse_type(ctx, l); + free(ret); + ret = rt; + } + + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *m = ast_create(NODE_FUNCTION); + m->func.param_names = param_names; + m->func.name = mname; + m->func.args = args; + m->func.ret_type = ret; + m->func.body = NULL; + if (!methods) + { + methods = m; + } + else + { + tail->next = m; + } + tail = m; + } + else + { + // Default implementation? Not supported yet. + zpanic_at(lexer_peek(l), "Trait methods must end with ; for now"); + } + } + + ASTNode *n_node = ast_create(NODE_TRAIT); + n_node->trait.name = name; + n_node->trait.methods = methods; + n_node->trait.generic_params = generic_params; + n_node->trait.generic_param_count = generic_count; + register_trait(name); + return n_node; +} + +ASTNode *parse_impl(ParserContext *ctx, Lexer *l) +{ + + lexer_next(l); // eat impl + Token t1 = lexer_next(l); + char *name1 = token_strdup(t1); + + char *gen_param = NULL; + // Check for <T> on the struct name + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + Token gt = lexer_next(l); + gen_param = token_strdup(gt); + if (lexer_next(l).type != TOK_RANGLE) + { + zpanic_at(lexer_peek(l), "Expected >"); + } + } + + // Check for "for" (Trait impl) + Token pk = lexer_peek(l); + if (pk.type == TOK_FOR || + (pk.type == TOK_IDENT && strncmp(pk.start, "for", 3) == 0 && pk.len == 3)) + { + if (pk.type != TOK_FOR) + { + lexer_next(l); + } + else + { + lexer_next(l); // eat for + } + Token t2 = lexer_next(l); + char *name2 = token_strdup(t2); + + register_impl(ctx, name1, name2); + + // RAII: Check for "Drop" trait implementation + if (strcmp(name1, "Drop") == 0) + { + Symbol *s = find_symbol_entry(ctx, name2); + if (s && s->type_info) + { + s->type_info->traits.has_drop = 1; + } + else + { + // Try finding struct definition + ASTNode *def = find_struct_def(ctx, name2); + if (def && def->type_info) + { + def->type_info->traits.has_drop = 1; + } + } + } + + // Iterator: Check for "Iterable" trait implementation + else if (strcmp(name1, "Iterable") == 0) + { + Symbol *s = find_symbol_entry(ctx, name2); + if (s && s->type_info) + { + s->type_info->traits.has_iterable = 1; + } + else + { + // Try finding struct definition + ASTNode *def = find_struct_def(ctx, name2); + if (def && def->type_info) + { + def->type_info->traits.has_iterable = 1; + } + } + } + + ctx->current_impl_struct = name2; // Set context to prevent duplicate emission and prefixing + + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + // Mangle: Type_Trait_Method + char *mangled = xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4); + sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name2); + free(f->func.args); + f->func.args = na; + + // Register function for lookup + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, + f->func.ret_type_info, f->func.is_varargs, f->func.is_async, + f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); // eat async + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + // Mangle: Type_Trait_Method + char *mangled = + xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 5); + sprintf(mangled, "%s__%s_%s", name2, name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name2); + free(f->func.args); + f->func.args = na; + + // Register function for lookup + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + f->func.is_async, f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + ctx->current_impl_struct = NULL; // Restore context + ASTNode *n = ast_create(NODE_IMPL_TRAIT); + n->impl_trait.trait_name = name1; + n->impl_trait.target_type = name2; + n->impl_trait.methods = h; + add_to_impl_list(ctx, n); + + // If target struct is generic, register this impl as a template + ASTNode *def = find_struct_def(ctx, name2); + if (def && ((def->type == NODE_STRUCT && def->strct.is_template) || + (def->type == NODE_ENUM && def->enm.is_template))) + { + const char *gp = "T"; + if (def->type == NODE_STRUCT && def->strct.generic_param_count > 0) + { + gp = def->strct.generic_params[0]; + } + // TODO: Enum generic params support if needed + register_impl_template(ctx, name2, gp, n); + } + + return n; + } + else + { + // Regular impl Struct (impl Box or impl Box<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; + } + + 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 {"); + } + char *full_struct_name = xmalloc(strlen(name1) + strlen(gen_param) + 3); + sprintf(full_struct_name, "%s<%s>", name1, gen_param); + + ASTNode *h = 0, *tl = 0; + ctx->current_impl_methods = NULL; + while (1) + { + ctx->current_impl_methods = h; + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + // Standard Mangle for template: Box_method + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + // Update args string + char *na = patch_self_args(f->func.args, full_struct_name); + free(f->func.args); + f->func.args = na; + + // Manual Type construction for self: Foo<T>* + if (f->func.arg_count > 0 && f->func.param_names && + strcmp(f->func.param_names[0], "self") == 0) + { + Type *t_struct = type_new(TYPE_STRUCT); + t_struct->name = xstrdup(name1); + t_struct->arg_count = 1; + t_struct->args = xmalloc(sizeof(Type *)); + t_struct->args[0] = type_new(TYPE_GENERIC); + t_struct->args[0]->name = xstrdup(gen_param); + + Type *t_ptr = type_new(TYPE_POINTER); + t_ptr->inner = t_struct; + + f->func.arg_types[0] = t_ptr; + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); // eat async + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, full_struct_name); + free(f->func.args); + f->func.args = na; + + if (f->func.arg_count > 0 && f->func.param_names && + strcmp(f->func.param_names[0], "self") == 0) + { + Type *t_struct = type_new(TYPE_STRUCT); + t_struct->name = xstrdup(name1); + t_struct->arg_count = 1; + t_struct->args = xmalloc(sizeof(Type *)); + t_struct->args[0] = type_new(TYPE_GENERIC); + t_struct->args[0]->name = xstrdup(gen_param); + + Type *t_ptr = type_new(TYPE_POINTER); + t_ptr->inner = t_struct; + + f->func.arg_types[0] = t_ptr; + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + free(full_struct_name); + // Register Template + ASTNode *n = ast_create(NODE_IMPL); + n->impl.struct_name = name1; + n->impl.methods = h; + register_impl_template(ctx, name1, gen_param, n); + ctx->current_impl_struct = NULL; + return NULL; // Do not emit generic template + } + else + { + // REGULAR IMPL + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + while (1) + { + skip_comments(l); + if (lexer_peek(l).type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 0); + + // Standard Mangle: Struct_method + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, + f->token); + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else if (lexer_peek(l).type == TOK_ASYNC) + { + lexer_next(l); + if (lexer_peek(l).type == TOK_IDENT && + strncmp(lexer_peek(l).start, "fn", 2) == 0) + { + ASTNode *f = parse_function(ctx, l, 1); + f->func.is_async = 1; + char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 3); + sprintf(mangled, "%s__%s", name1, f->func.name); + free(f->func.name); + f->func.name = mangled; + char *na = patch_self_args(f->func.args, name1); + free(f->func.args); + f->func.args = na; + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + 1, f->token); + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + } + else + { + zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'"); + } + } + else + { + lexer_next(l); + } + } + ctx->current_impl_struct = NULL; + ASTNode *n = ast_create(NODE_IMPL); + n->impl.struct_name = name1; + n->impl.methods = h; + add_to_impl_list(ctx, n); + return n; + } + } +} + +ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) +{ + + lexer_next(l); // eat struct or union + Token n = lexer_next(l); + char *name = token_strdup(n); + + // Generic Params <T> or <K, V> + char **gps = NULL; + int gp_count = 0; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + while (1) + { + Token g = lexer_next(l); + gps = realloc(gps, sizeof(char *) * (gp_count + 1)); + gps[gp_count++] = token_strdup(g); + + Token next = lexer_peek(l); + if (next.type == TOK_COMMA) + { + lexer_next(l); // eat , + } + else if (next.type == TOK_RANGLE) + { + lexer_next(l); // eat > + break; + } + else + { + zpanic_at(next, "Expected ',' or '>' in generic parameter list"); + } + } + register_generic(ctx, name); + } + + // Check for prototype (forward declaration) + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *n = ast_create(NODE_STRUCT); + n->strct.name = name; + n->strct.is_template = (gp_count > 0); + n->strct.generic_params = gps; + n->strct.generic_param_count = gp_count; + n->strct.is_union = is_union; + n->strct.fields = NULL; + n->strct.is_incomplete = 1; + + return n; + } + + lexer_next(l); // eat { + ASTNode *h = 0, *tl = 0; + + // Temp storage for used structs + char **temp_used_structs = NULL; + int temp_used_count = 0; + + while (1) + { + skip_comments(l); + Token t = lexer_peek(l); + // printf("DEBUG: parse_struct loop seeing '%.*s'\n", t.len, t.start); + if (t.type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + + // Handle 'use' (Struct Embedding) + if (t.type == TOK_USE) + { + lexer_next(l); // eat use + + // Check for named use: use name: Type; + Token t1 = lexer_peek(l); + Token t2 = lexer_peek2(l); + + if (t1.type == TOK_IDENT && t2.type == TOK_COLON) + { + // Named use -> Composition (Add field, don't flatten) + Token field_name = lexer_next(l); + lexer_next(l); // eat : + char *field_type_str = parse_type(ctx, l); + expect(l, TOK_SEMICOLON, "Expected ;"); + + ASTNode *nf = ast_create(NODE_FIELD); + nf->field.name = token_strdup(field_name); + nf->field.type = field_type_str; + + if (!h) + { + h = nf; + } + else + { + tl->next = nf; + } + tl = nf; + continue; + } + + // Normal use -> Mixin (Flatten) + // Parse the type (e.g. Header<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) + { + if (!temp_used_structs) + { + temp_used_structs = xmalloc(sizeof(char *) * 8); + } + temp_used_structs[temp_used_count++] = xstrdup(use_name); + + ASTNode *f = def->strct.fields; + while (f) + { + ASTNode *nf = ast_create(NODE_FIELD); + nf->field.name = xstrdup(f->field.name); + nf->field.type = xstrdup(f->field.type); + if (!h) + { + h = nf; + } + else + { + tl->next = nf; + } + tl = nf; + f = f->next; + } + } + else + { + // If definition not found (e.g. user struct defined later), we can't + // embed fields yet. Compiler limitation: 'use' requires struct to be + // defined before. Fallback: Emit a placeholder field so compilation + // doesn't crash, but layout will be wrong. printf("Warning: Could not + // find struct '%s' for embedding.\n", use_name); + } + free(use_name); + continue; + } + // --------------------------------------- + + if (t.type == TOK_IDENT) + { + Token f_name = lexer_next(l); + expect(l, TOK_COLON, "Expected :"); + char *f_type = parse_type(ctx, l); + + ASTNode *f = ast_create(NODE_FIELD); + f->field.name = token_strdup(f_name); + f->field.type = f_type; + f->field.bit_width = 0; + + // Optional bit width: name: type : 3 + if (lexer_peek(l).type == TOK_COLON) + { + lexer_next(l); // eat : + Token width_tok = lexer_next(l); + if (width_tok.type != TOK_INT) + { + zpanic_at(width_tok, "Expected bit width integer"); + } + f->field.bit_width = atoi(token_strdup(width_tok)); + } + + if (!h) + { + h = f; + } + else + { + tl->next = f; + } + tl = f; + + if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + } + } + else + { + lexer_next(l); + } + } + + ASTNode *node = ast_create(NODE_STRUCT); + add_to_struct_list(ctx, node); + + // Auto-prefix struct name if in module context + if (ctx->current_module_prefix && gp_count == 0) + { // Don't prefix generic templates + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); + free(name); + name = prefixed_name; + } + + node->strct.name = name; + + // Initialize Type Info so we can track traits (like Drop) + node->type_info = type_new(TYPE_STRUCT); + node->type_info->name = xstrdup(name); + if (gp_count > 0) + { + node->type_info->kind = TYPE_GENERIC; + // TODO: track generic params + } + + node->strct.fields = h; + node->strct.generic_params = gps; + node->strct.generic_param_count = gp_count; + node->strct.is_union = is_union; + node->strct.used_structs = temp_used_structs; + node->strct.used_struct_count = temp_used_count; + + if (gp_count > 0) + { + node->strct.is_template = 1; + register_template(ctx, name, node); + } + + // Register definition for 'use' lookups and LSP + if (gp_count == 0) + { + register_struct_def(ctx, name, node); + } + + return node; +} + +Type *parse_type_obj(ParserContext *ctx, Lexer *l) +{ + // Parse the base type (int, U32, MyStruct, etc.) + Type *t = parse_type_base(ctx, l); + + // Handle Pointers + while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') + { + lexer_next(l); // eat * + // Wrap the current type in a Pointer type + Type *ptr = type_new(TYPE_POINTER); + ptr->inner = t; + t = ptr; + } + + return t; +} + +ASTNode *parse_enum(ParserContext *ctx, Lexer *l) +{ + lexer_next(l); + Token n = lexer_next(l); + + // 1. Check for Generic <T> + char *gp = NULL; + if (lexer_peek(l).type == TOK_LANGLE) + { + lexer_next(l); // eat < + Token g = lexer_next(l); + gp = token_strdup(g); + lexer_next(l); // eat > + register_generic(ctx, n.start ? token_strdup(n) : "anon"); + } + + lexer_next(l); // eat { + + ASTNode *h = 0, *tl = 0; + int v = 0; + char *ename = token_strdup(n); // Store enum name + + while (1) + { + skip_comments(l); + Token t = lexer_peek(l); + if (t.type == TOK_RBRACE) + { + lexer_next(l); + break; + } + if (t.type == TOK_COMMA) + { + lexer_next(l); + continue; + } + + if (t.type == TOK_IDENT) + { + Token vt = lexer_next(l); + char *vname = token_strdup(vt); + + // 2. Parse Payload Type (Ok(int)) + Type *payload = NULL; + if (lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); + payload = parse_type_obj(ctx, l); + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected )"); + } + } + + ASTNode *va = ast_create(NODE_ENUM_VARIANT); + va->variant.name = vname; + va->variant.tag_id = v++; // Use tag_id instead of value + va->variant.payload = payload; // Store Type* + + // Register Variant (Mangled name to avoid collisions: Result_Ok) + char mangled[256]; + sprintf(mangled, "%s_%s", ename, vname); + register_enum_variant(ctx, ename, mangled, va->variant.tag_id); + + // Handle explicit assignment: Ok = 5 + if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=') + { + lexer_next(l); + va->variant.tag_id = atoi(lexer_next(l).start); + v = va->variant.tag_id + 1; + } + + if (!h) + { + h = va; + } + else + { + tl->next = va; + } + tl = va; + } + else + { + lexer_next(l); + } + } + + // Auto-prefix enum name if in module context + if (ctx->current_module_prefix && !gp) + { // Don't prefix generic templates + char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2); + sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename); + free(ename); + ename = prefixed_name; + } + + ASTNode *node = ast_create(NODE_ENUM); + node->enm.name = ename; + + node->enm.variants = h; + node->enm.generic_param = gp; // 3. Store generic param + + if (gp) + { + node->enm.is_template = 1; + register_template(ctx, node->enm.name, node); + } + + add_to_enum_list(ctx, node); // Register globally + + return node; +} |
