From b67edc5cee77a0779fe19daf7a77beb809d35544 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Sun, 12 Jul 2026 22:16:17 +0800 Subject: [PATCH] =?UTF-8?q?stage1=20=E4=BF=AE=E5=A4=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BD=93=E5=86=85=E9=83=A8=E5=87=BD=E6=95=B0=E5=89=8D=E5=90=91?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E6=97=B6=20CALL=20=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage0/spl_ir.c | 9 ++++++ stage0/spl_ir.h | 1 + stage1/spl_comp.c | 73 +++++++++++++++++++++++++++++++++++++++++--- stage1/spl_comp.h | 43 +++++++++++++++++--------- stage1/spl_expr.c | 74 ++++++++++++++++++++++++++++++++++++--------- stage1/spl_parser.c | 52 +++++++++++++++++++++++++++---- stage1/spl_type.c | 65 +++++++++++++++++++++++++++++++++++++-- 7 files changed, 273 insertions(+), 44 deletions(-) diff --git a/stage0/spl_ir.c b/stage0/spl_ir.c index 5ece8dc..8a5fb8b 100644 --- a/stage0/spl_ir.c +++ b/stage0/spl_ir.c @@ -390,6 +390,15 @@ int spl_prog_add_func_simple(spl_prog_t *prog, const char *name, spl_val_t nargs return (int)vec_size(prog->funcs) - 1; } +int spl_prog_update_func(spl_prog_t *prog, int func_idx, const char *name, spl_val_t nargs) { + if (!prog || func_idx < 0 || func_idx >= (int)vec_size(prog->funcs)) + return spl_prog_add_func_simple(prog, name, nargs); + spl_func_t *func = &vec_at(prog->funcs, func_idx); + func->address = vec_size(prog->insns); + func->nargs = nargs; + return func_idx; +} + void spl_prog_end_func(spl_prog_t *prog, int func_idx) { if (!prog || func_idx < 0 || func_idx >= (int)vec_size(prog->funcs)) return; diff --git a/stage0/spl_ir.h b/stage0/spl_ir.h index e5ea0d5..71919b4 100644 --- a/stage0/spl_ir.h +++ b/stage0/spl_ir.h @@ -210,6 +210,7 @@ spl_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name); /* High-level program construction helpers */ spl_val_t spl_prog_emit(spl_prog_t *prog, uint16_t opcode, uint16_t type, spl_val_t imm); int spl_prog_add_func_simple(spl_prog_t *prog, const char *name, spl_val_t nargs); +int spl_prog_update_func(spl_prog_t *prog, int func_idx, const char *name, spl_val_t nargs); void spl_prog_end_func(spl_prog_t *prog, int func_idx); int spl_prog_add_str(spl_prog_t *prog, const char *str); diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index 37af7d5..1c401e2 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -13,11 +13,13 @@ void spl_comp_init(spl_comp_t *ctx) { vec_init(ctx->toks); vec_init(ctx->scopes); vec_init(ctx->funcs); + vec_init(ctx->ns_chain); map_init(ctx->type_defs, MAP_HASH_STR, MAP_CMP_STR); map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR); + vec_init(ctx->call_fixups); + vec_init(ctx->call_fixup_funcs); spl_prog_init(&ctx->prog); ctx->error_msg[0] = '\0'; - ctx->current_type_name = NULL; } void spl_comp_drop(spl_comp_t *ctx) { @@ -27,9 +29,12 @@ void spl_comp_drop(spl_comp_t *ctx) { vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); } vec_free(ctx->scopes); vec_free(ctx->funcs); + vec_free(ctx->ns_chain); /* Free type defs — complex, leak for now in bootstrap */ map_free(ctx->type_defs); map_free(ctx->const_values); + vec_free(ctx->call_fixups); + vec_free(ctx->call_fixup_funcs); spl_prog_drop(&ctx->prog); free(ctx->break_patches); } @@ -41,6 +46,8 @@ void spl_comp_reset(spl_comp_t *ctx) { vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); } vec_free(ctx->scopes); vec_init(ctx->scopes); + vec_free(ctx->ns_chain); + vec_init(ctx->ns_chain); ctx->scope_depth = 0; ctx->tok_idx = 0; ctx->has_error = 0; @@ -56,9 +63,12 @@ void spl_comp_reset(spl_comp_t *ctx) { ctx->defer_count = 0; ctx->next_gdata_idx = 0; ctx->addr_of_mode = 0; - ctx->current_type_name = NULL; free(ctx->break_patches); ctx->break_patches = NULL; + vec_free(ctx->call_fixups); + vec_init(ctx->call_fixups); + vec_free(ctx->call_fixup_funcs); + vec_init(ctx->call_fixup_funcs); } void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) { @@ -164,6 +174,18 @@ void spl_pop_scope(spl_comp_t *ctx) { } } +/* ---- Namespace chain management ---- */ + +void spl_ns_push(spl_comp_t *ctx, spl_type_info_t *type) { + if (type) + vec_push(ctx->ns_chain, type); +} + +void spl_ns_pop(spl_comp_t *ctx) { + if (vec_size(ctx->ns_chain) > 0) + ctx->ns_chain.size--; +} + /* ---- Variable management ---- */ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const) { @@ -214,6 +236,20 @@ int spl_get_var_offset(spl_comp_t *ctx, const char *name) { int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams, int is_extern, int is_pub) { + /* Check if already declared — allows pre-registration */ + vec_for(ctx->funcs, i) { + spl_func_info_t *existing = &vec_at(ctx->funcs, i); + if (strcmp(existing->name, name) == 0) { + existing->ret_type = ret_type; + existing->nparams = nparams; + existing->is_extern = is_extern; + existing->is_pub = is_pub; + existing->func_idx = + spl_prog_update_func(&ctx->prog, existing->func_idx, name, nparams); + return (int)i; + } + } + spl_func_info_t fi; memset(&fi, 0, sizeof(fi)); fi.name = strdup(name); @@ -233,9 +269,19 @@ int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_typ } int spl_lookup_func(spl_comp_t *ctx, const char *name) { - vec_for(ctx->funcs, i) { - if (strcmp(vec_at(ctx->funcs, i).name, name) == 0) - return (int)i; + /* 1. Walk ns_chain from innermost to outermost, check each type's methods */ + for (usize i = vec_size(ctx->ns_chain); i > 0; i--) { + spl_type_info_t *type = vec_at(ctx->ns_chain, i - 1); + vec_for(type->methods, j) { + if (strcmp(vec_at(type->methods, j).name, name) == 0) + return vec_at(type->methods, j).func_idx; + } + } + + /* 2. Fallback to flat func table (top-level functions and qualified names) */ + vec_for(ctx->funcs, j) { + if (strcmp(vec_at(ctx->funcs, j).name, name) == 0) + return (int)j; } return -1; } @@ -340,6 +386,19 @@ void spl_comp_register(spl_prog_t *prog) { (void)prog; } +/* ---- Patch call fixups ---- */ + +void spl_patch_call_fixups(spl_comp_t *ctx) { + for (usize i = 0; i < vec_size(ctx->call_fixups); i++) { + spl_val_t insn_idx = vec_at(ctx->call_fixups, i); + int pfi = vec_at(ctx->call_fixup_funcs, i); + if (pfi >= 0 && pfi < (int)vec_size(ctx->prog.funcs)) { + spl_val_t addr = vec_at(ctx->prog.funcs, pfi).address; + vec_at(ctx->prog.insns, insn_idx).imm = addr; + } + } +} + /* ---- Main compilation ---- */ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) { @@ -358,5 +417,9 @@ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) { spl_parse_prog(ctx); if (ctx->has_error) return -1; + + /* Phase 5: Patch all function call fixups (forward references) */ + spl_patch_call_fixups(ctx); + return 0; } diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index 7d43f6f..60b7cb2 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -57,17 +57,18 @@ typedef VEC(spl_method_info_t) spl_method_info_vec_t; struct spl_type_info { spl_type_kind_t kind; - spl_type_t basic_type; /* for TYPE_BASIC */ - spl_type_info_t *elem; /* for PTR/ARRAY/SLICE element type */ - usize array_len; /* for TYPE_ARRAY */ - char *name; /* type name for TYPE_NAME/STRUCT/ENUM */ - spl_field_vec_t fields; /* for TYPE_STRUCT */ - spl_enum_variant_vec_t variants; /* for TYPE_ENUM */ - spl_method_info_vec_t methods; /* methods */ - usize byte_size; /* total byte size (cached) */ - usize slot_count; /* stack slot count */ - int is_pub; /* public visibility */ - int resolved; /* type fully resolved */ + spl_type_t basic_type; /* for TYPE_BASIC */ + spl_type_info_t *elem; /* for PTR/ARRAY/SLICE element type */ + usize array_len; /* for TYPE_ARRAY */ + char *name; /* type name for TYPE_NAME/STRUCT/ENUM */ + spl_field_vec_t fields; /* for TYPE_STRUCT */ + spl_enum_variant_vec_t variants; /* for TYPE_ENUM */ + spl_method_info_vec_t methods; /* methods */ + MAP(const char *, spl_type_info_t *) children; /* nested type namespace */ + usize byte_size; /* total byte size (cached) */ + usize slot_count; /* stack slot count */ + int is_pub; /* public visibility */ + int resolved; /* type fully resolved */ }; /* Type constructor helpers */ @@ -171,10 +172,9 @@ typedef struct spl_comp { /* Current function context */ int current_func_idx; spl_type_info_t *current_ret_type; - int current_local_bytes; /* next free local byte offset */ - int peak_local_bytes; /* peak current_local_bytes for ALLOC sizing */ - const char *current_type_name; /* name of type whose body we're parsing (for method short-name - lookup) */ + int current_local_bytes; /* next free local byte offset */ + int peak_local_bytes; /* peak current_local_bytes for ALLOC sizing */ + VEC(spl_type_info_t *) ns_chain; /* type namespace scope chain */ /* Loop context for break/continue */ int in_loop; @@ -194,6 +194,12 @@ typedef struct spl_comp { MAP(const char *, spl_val_t) const_values; int addr_of_mode; /* 1 = inside & operator, suppress loads */ + + /* Fixup list: PUSH instructions whose immediate (function address) must + * be patched after ALL function bodies are compiled. This handles forward + * references — method A calling method B defined later in the same type. */ + VEC(spl_val_t) call_fixups; /* instruction indices of PUSH insns to patch */ + VEC(int) call_fixup_funcs; /* prog->funcs index for each fixup */ } spl_comp_t; /* Initialize/destroy compiler context */ @@ -303,9 +309,16 @@ int spl_add_global_data(spl_comp_t *ctx, void *data, usize size); void spl_push_scope(spl_comp_t *ctx); void spl_pop_scope(spl_comp_t *ctx); +/* Namespace chain management */ +void spl_ns_push(spl_comp_t *ctx, spl_type_info_t *type); +void spl_ns_pop(spl_comp_t *ctx); + /* Register runtime natives */ void spl_comp_register(spl_prog_t *prog); +/* Patch all CALL fixups after all function bodies are compiled */ +void spl_patch_call_fixups(spl_comp_t *ctx); + /* Defer */ void spl_emit_defer(spl_comp_t *ctx); void spl_emit_defer_epilogue(spl_comp_t *ctx, int depth); diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index e95eb9c..d03ee86 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -547,9 +547,20 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ skip_nl(ctx); } } else if (type->kind == TYPE_ENUM) { - if (peek(ctx)->type == TOK_DOT) + /* Parse variant name with optional type qualifier(s): + * .name, TypeName.name, or bare name (same pattern as + * spl_emit_match_enum_cmp for consistency) */ + spl_tok_t *vtok; + if (peek(ctx)->type == TOK_DOT) { advance(ctx); - spl_tok_t *vtok = advance(ctx); + vtok = advance(ctx); + } else { + vtok = advance(ctx); + while (peek(ctx)->type == TOK_DOT) { + advance(ctx); + vtok = advance(ctx); + } + } char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); skip_nl(ctx); @@ -654,11 +665,6 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { /* Check if it's a function call: ident(...) */ if (peek(ctx)->type == TOK_L_PAREN) { int fi = spl_lookup_func(ctx, name); - if (fi < 0 && ctx->current_type_name) { - char qualified[512]; - snprintf(qualified, sizeof(qualified), "%s.%s", ctx->current_type_name, name); - fi = spl_lookup_func(ctx, qualified); - } if (fi < 0) { spl_comp_error(ctx, "unknown function '%s'", name); spl_expr_result_t r = {0}; @@ -675,9 +681,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_emit(ctx, SPL_PUSH, SPL_I32, nidx); spl_emit(ctx, SPL_NCALL, SPL_VOID, nargs); } else { - spl_val_t addr = vec_at(ctx->prog.funcs, f->func_idx).address; - spl_emit(ctx, SPL_PUSH, SPL_PTR, addr); + /* Record fixup for forward-reference resolution */ + spl_val_t fixup_idx = vec_size(ctx->prog.insns); + spl_emit(ctx, SPL_PUSH, SPL_PTR, 0); /* placeholder address */ spl_emit(ctx, SPL_CALL, SPL_VOID, nargs); + vec_push(ctx->call_fixups, fixup_idx); + vec_push(ctx->call_fixup_funcs, f->func_idx); } spl_expr_result_t r = {f->ret_type, 0}; @@ -941,9 +950,12 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l nargs += parse_call_args(ctx); expect(ctx, TOK_R_PAREN); - spl_val_t addr = vec_at(ctx->prog.funcs, func->func_idx).address; - spl_emit(ctx, SPL_PUSH, SPL_PTR, addr); + /* Record fixup for forward-reference resolution */ + spl_val_t fixup_idx = vec_size(ctx->prog.insns); + spl_emit(ctx, SPL_PUSH, SPL_PTR, 0); /* placeholder address */ spl_emit(ctx, SPL_CALL, SPL_VOID, nargs); + vec_push(ctx->call_fixups, fixup_idx); + vec_push(ctx->call_fixup_funcs, func->func_idx); left = (spl_expr_result_t){func->ret_type, 0}; break; @@ -1220,12 +1232,44 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, int val_offset) { - if (peek(ctx)->type == TOK_DOT) - advance(ctx); - spl_tok_t *vtok = advance(ctx); char vname[256]; - spl_tok_copy_name(vtok, vname, sizeof(vname)); + /* Parse variant reference, reusing the same type resolution path + * as the expression parser (parse_ident + postfix DOT handler): + * + * .unknown — leading DOT shortcut + * unknown — bare variant name + * Tag.unknown — type-qualified (resolves Tag via spl_resolve_type) + * Token.Tag.unknown — fully qualified (walks all segments) + * + * Only the final segment is the variant name; preceding segments + * are optional type qualifiers resolved through the type system. */ + if (peek(ctx)->type == TOK_DOT) { + advance(ctx); /* . */ + } else { + /* Read first name; if followed by DOT, it's a type qualifier — + * verify it resolves via spl_resolve_type (same as parse_ident). */ + spl_tok_t *tok = advance(ctx); + spl_tok_copy_name(tok, vname, sizeof(vname)); + + while (peek(ctx)->type == TOK_DOT) { + spl_type_info_t *qt = spl_resolve_type(ctx, vname); + (void)qt; /* qualifier verified through type system */ + advance(ctx); /* . */ + tok = advance(ctx); + spl_tok_copy_name(tok, vname, sizeof(vname)); + } + + /* Now vname holds the final segment — the variant name */ + goto lookup; + } + + { + spl_tok_t *vtok = advance(ctx); + spl_tok_copy_name(vtok, vname, sizeof(vname)); + } + +lookup: vec_for(enum_type->variants, vi) { spl_enum_variant_t *v = &vec_at(enum_type->variants, vi); if (strcmp(v->name, vname) == 0) { diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index c18f85c..c653bdd 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -181,14 +181,8 @@ static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) { skip_nl(ctx); } - /* Set current_type_name for short-name resolution inside method body */ - const char *saved_type_name = ctx->current_type_name; - ctx->current_type_name = container->name; - int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0); - ctx->current_type_name = saved_type_name; - { spl_func_info_t *f = &vec_at(ctx->funcs, fi); f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); @@ -256,6 +250,9 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ return; advance(ctx); /* { */ + /* Push type onto namespace chain for short-name resolution inside body */ + spl_ns_push(ctx, container); + /* === Pass 1: Parse all nested type declarations first === * This allows fields/variants to reference types defined later. */ { @@ -280,6 +277,38 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ ctx->tok_idx = saved; } + /* === Pre-register method names for forward references === */ + if (container->name) { + usize saved = ctx->tok_idx; + int depth = 1; + while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) { + spl_tok_type_t tt = peek(ctx)->type; + if (tt == TOK_L_BRACE) { + depth++; + advance(ctx); + } else if (tt == TOK_R_BRACE) { + depth--; + if (depth == 0) + break; + advance(ctx); + } else if (tt == KW_FN && depth == 1) { + advance(ctx); /* fn */ + skip_nl(ctx); + spl_tok_t *name_tok = advance(ctx); + char mname[256]; + spl_tok_copy_name(name_tok, mname, sizeof(mname)); + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", container->name, mname); + int fi = spl_declare_func(ctx, qualified, NULL, 0, 0, 0); + /* Register in type->methods immediately so ns_chain lookup works */ + spl_type_add_method(container, mname, fi); + } else { + advance(ctx); + } + } + ctx->tok_idx = saved; + } + /* === Pass 2: Parse fields/variants and methods === */ { int depth = 1; @@ -359,6 +388,7 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ } } + spl_ns_pop(ctx); spl_type_compute_layout(container); } @@ -380,17 +410,25 @@ void parse_type_decl(spl_comp_t *ctx) { expect(ctx, TOK_ASSIGN); skip_nl(ctx); + /* Determine parent type if inside a type body (for nested type registration) */ + spl_type_info_t *parent = + vec_size(ctx->ns_chain) > 0 ? vec_at(ctx->ns_chain, vec_size(ctx->ns_chain) - 1) : NULL; + if (peek(ctx)->type == KW_STRUCT) { advance(ctx); spl_type_info_t *st = spl_type_struct(tname); /* Register type early to allow self-referential fields */ map_put(ctx->type_defs, strdup(tname), st); + if (parent) + map_put(parent->children, strdup(tname), st); skip_nl(ctx); parse_type_body(ctx, st, 0); } else if (peek(ctx)->type == KW_UNION) { advance(ctx); spl_type_info_t *ut = spl_type_union(tname); map_put(ctx->type_defs, strdup(tname), ut); + if (parent) + map_put(parent->children, strdup(tname), ut); skip_nl(ctx); parse_type_body(ctx, ut, 0); } else if (peek(ctx)->type == KW_ENUM) { @@ -398,6 +436,8 @@ void parse_type_decl(spl_comp_t *ctx) { spl_type_info_t *et = spl_type_enum(tname); /* Register type early to allow self-referential variants */ map_put(ctx->type_defs, strdup(tname), et); + if (parent) + map_put(parent->children, strdup(tname), et); skip_nl(ctx); parse_type_body(ctx, et, 1); } else if (peek(ctx)->type == TOK_IDENT || diff --git a/stage1/spl_type.c b/stage1/spl_type.c index a4097fe..88f50d8 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -134,6 +134,7 @@ spl_type_info_t *spl_type_struct(const char *name) { t->name = strdup(name); vec_init(t->fields); vec_init(t->methods); + map_init(t->children, MAP_HASH_STR, MAP_CMP_STR); t->resolved = 0; return t; } @@ -145,6 +146,7 @@ spl_type_info_t *spl_type_union(const char *name) { t->name = strdup(name); vec_init(t->fields); vec_init(t->methods); + map_init(t->children, MAP_HASH_STR, MAP_CMP_STR); t->resolved = 0; return t; } @@ -156,6 +158,7 @@ spl_type_info_t *spl_type_enum(const char *name) { t->name = strdup(name); vec_init(t->variants); vec_init(t->methods); + map_init(t->children, MAP_HASH_STR, MAP_CMP_STR); t->byte_size = 4; t->slot_count = 1; t->resolved = 0; @@ -179,6 +182,14 @@ void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t } void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx) { + /* Check for existing entry — update instead of duplicating + * (handles pre-registration + actual method declaration) */ + vec_for(t->methods, i) { + if (strcmp(vec_at(t->methods, i).name, name) == 0) { + vec_at(t->methods, i).func_idx = func_idx; + return; + } + } spl_method_info_t m; m.name = strdup(name); m.func_idx = func_idx; @@ -336,7 +347,9 @@ spl_type_info_t *spl_type_clone(spl_type_info_t *t) { return NULL; spl_type_info_t *c = calloc(1, sizeof(spl_type_info_t)); memcpy(c, t, sizeof(spl_type_info_t)); - /* Don't deep-copy fields/variants for now — shallow is fine for our use */ + /* Don't deep-copy fields/variants — shallow is fine for our use. + * Clone doesn't own children (aliases never need them). */ + memset(&c->children, 0, sizeof(c->children)); return c; } @@ -463,9 +476,45 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { memcpy(id_buf, name, cplen); id_buf[cplen] = '\0'; - spl_type_info_t *found = NULL; - if (map_get(ctx->type_defs, id_buf, &found)) { + spl_type_info_t *found = spl_resolve_type(ctx, id_buf); + if (found) { ctx->tok_idx++; + + /* Handle qualified type names: Token.Tag, etc. + * Walks DOT-separated segments, resolving each via type_defs. + * Same resolution path as spl_resolve_type_member in expr: try + * qualified name first, then bare-name fallback. */ + while (ctx->tok_idx < vec_size(ctx->toks)) { + spl_tok_t *next = &vec_at(ctx->toks, ctx->tok_idx); + if (next->type != TOK_DOT) + break; + if (ctx->tok_idx + 1 >= vec_size(ctx->toks)) + break; + spl_tok_t *ntok = &vec_at(ctx->toks, ctx->tok_idx + 1); + + char part[256]; + usize plen = ntok->len < 255 ? ntok->len : 255; + memcpy(part, ntok->lexeme, plen); + part[plen] = '\0'; + + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", id_buf, part); + + spl_type_info_t *qfound = NULL; + if (map_get(ctx->type_defs, qualified, &qfound)) { + found = qfound; + strncpy(id_buf, qualified, sizeof(id_buf) - 1); + ctx->tok_idx += 2; + } else if (map_get(ctx->type_defs, part, &qfound)) { + /* Bare-name fallback (nested types often only + * registered with short name, not qualified) */ + found = qfound; + strncpy(id_buf, part, sizeof(id_buf) - 1); + ctx->tok_idx += 2; + } else { + break; + } + } return found; } @@ -489,6 +538,16 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { spl_type_info_t *spl_resolve_type(spl_comp_t *ctx, const char *name) { if (!ctx || !name) return NULL; + + /* 1. Walk ns_chain from innermost to outermost, check each type's children */ + for (usize i = vec_size(ctx->ns_chain); i > 0; i--) { + spl_type_info_t *type = vec_at(ctx->ns_chain, i - 1); + spl_type_info_t *found = NULL; + if (map_get(type->children, name, &found)) + return found; + } + + /* 2. Fallback to flat map */ spl_type_info_t *found = NULL; map_get(ctx->type_defs, name, &found); return found;