stage1 修复类型体内部函数前向引用时 CALL 地址错误
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ struct spl_type_info {
|
||||
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 */
|
||||
@@ -173,8 +174,7 @@ typedef struct spl_comp {
|
||||
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) */
|
||||
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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user