diff --git a/stage0/spl_disasm.c b/stage0/spl_disasm.c index 8716b7c..092d9b5 100644 --- a/stage0/spl_disasm.c +++ b/stage0/spl_disasm.c @@ -54,7 +54,7 @@ int main(int argc, const char **argv) { spl_ins_t *ins = &vec_at(prog.insns, i); printf("%4zd: %s", i, spl_opcode_name((spl_opcode_t)ins->opcode)); if (ins->type != SPL_VOID) - printf(" %s", spl_type_name((spl_type_t)ins->type)); + printf(" %s", spl_type_tag_name((spl_type_t)ins->type)); printf(" %zd", ins->imm); /* annotate jumps / calls */ diff --git a/stage0/spl_ir.c b/stage0/spl_ir.c index 8a5fb8b..6671c89 100644 --- a/stage0/spl_ir.c +++ b/stage0/spl_ir.c @@ -443,7 +443,7 @@ const char *opcode_name[] = { #undef X }; const char *spl_opcode_name(spl_opcode_t opcode) { return opcode_name[opcode]; } -const char *spl_type_name(spl_type_t type) { +const char *spl_type_tag_name(spl_type_t type) { switch (type) { case SPL_VOID: return "void"; @@ -481,7 +481,7 @@ const char *spl_type_name(spl_type_t type) { void spl_ins_dump(spl_ins_t *ins, spl_val_t addr) { printf("%4zu: %s", addr, spl_opcode_name((spl_opcode_t)ins->opcode)); if (ins->type != SPL_VOID) - printf(" %s", spl_type_name((spl_type_t)ins->type)); + printf(" %s", spl_type_tag_name((spl_type_t)ins->type)); printf(" %zd:%zx", ins->imm, ins->imm); printf("\n"); } diff --git a/stage0/spl_ir.h b/stage0/spl_ir.h index 71919b4..0974cab 100644 --- a/stage0/spl_ir.h +++ b/stage0/spl_ir.h @@ -216,7 +216,7 @@ int spl_prog_add_str(spl_prog_t *prog, const char *str); /* Opcode name lookup for debugging/dumping */ const char *spl_opcode_name(spl_opcode_t opcode); -const char *spl_type_name(spl_type_t type); +const char *spl_type_tag_name(spl_type_t type); void spl_ins_dump(spl_ins_t *ins, spl_val_t addr); diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index a8fc133..1e6e602 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -665,7 +665,7 @@ int spl_vm_run_once(spl_vm_t *vm) { if (vm->trace) { fprintf(stderr, "vm: ip=%zd op=%s type=%s imm=%zu sp=%zd fp=%zd\n", vm->ip - 1, - spl_opcode_name(ins->opcode), spl_type_name(ins->type), ins->imm, vm->sp, vm->fp); + spl_opcode_name(ins->opcode), spl_type_tag_name(ins->type), ins->imm, vm->sp, vm->fp); } switch (ins->opcode) { @@ -1084,7 +1084,7 @@ void spl_vm_dump_instr(spl_vm_t *vm, spl_val_t ip) { return; spl_ins_t *ins = &vec_at(vm->prog->insns, ip); fprintf(stderr, " instr at ip=%zd: op=%s type=%s imm=%zu\n", ip, spl_opcode_name(ins->opcode), - spl_type_name(ins->type), ins->imm); + spl_type_tag_name(ins->type), ins->imm); } void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp) { diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index 76e1fb8..94a484c 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -3,7 +3,6 @@ #include "spl_comp.h" #include #include -#include #include /* ---- Compiler context init/drop ---- */ @@ -13,13 +12,12 @@ 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'; + spl_emit_init(&ctx->emit, &ctx->prog); + spl_type_ctx_init(&ctx->tctx); + ctx->current_ret_type_idx = -1; } void spl_comp_drop(spl_comp_t *ctx) { @@ -28,34 +26,32 @@ void spl_comp_drop(spl_comp_t *ctx) { spl_tok_vec_drop(&ctx->toks); vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); } vec_free(ctx->scopes); + vec_for(ctx->funcs, i) { + free(ctx->funcs.data[i].name); + free(ctx->funcs.data[i].param_type_indices); + free(ctx->funcs.data[i].param_names); + } 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); + spl_emit_drop(&ctx->emit); + spl_type_ctx_drop(&ctx->tctx); } void spl_comp_reset(spl_comp_t *ctx) { - /* Keep prog, reset everything else */ spl_tok_vec_drop(&ctx->toks); vec_init(ctx->toks); 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; ctx->error_msg[0] = '\0'; ctx->current_func_idx = -1; - ctx->current_ret_type = NULL; - ctx->current_local_bytes = 0; - ctx->peak_local_bytes = 0; + ctx->current_ret_type_idx = -1; + fa_init(&ctx->emit.frame); ctx->in_loop = 0; ctx->break_patch_count = 0; ctx->break_patch_cap = 0; @@ -65,10 +61,10 @@ void spl_comp_reset(spl_comp_t *ctx) { ctx->addr_of_mode = 0; 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); + vec_free(ctx->emit.call_fixups); + vec_init(ctx->emit.call_fixups); + vec_free(ctx->emit.call_fixup_funcs); + vec_init(ctx->emit.call_fixup_funcs); } void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) { @@ -79,135 +75,6 @@ void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) { ctx->has_error = 1; } -/* ---- Codegen helpers ---- */ - -spl_val_t spl_emit(spl_comp_t *ctx, uint16_t opcode, uint16_t type, spl_val_t imm) { - return spl_prog_emit(&ctx->prog, opcode, type, imm); -} - -void spl_patch(spl_comp_t *ctx, spl_val_t addr, spl_val_t target) { - if (addr < vec_size(ctx->prog.insns)) { - vec_at(ctx->prog.insns, addr).imm = target; - } -} - -spl_val_t spl_emit_jmp(spl_comp_t *ctx) { - /* Emit JMP with placeholder 0, return address to patch */ - return spl_emit(ctx, SPL_JMP, SPL_VOID, 0); -} - -spl_val_t spl_emit_bz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BZ, SPL_VOID, 0); } - -spl_val_t spl_emit_bnz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0); } - -void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) { - /* Compute relative offset: target - (source + 1) */ - spl_val_t here = vec_size(ctx->prog.insns); - spl_val_t offset = here - addr - 1; - spl_patch(ctx, addr, offset); -} - -/* ---- Multi-slot copy helpers ---- */ - -void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) { - for (usize i = 0; i < nslots; i++) { - if (i < nslots - 1) - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); - if (i > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, dest_offset + (int)(i * sizeof(spl_val_t))); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); - } -} - -/* Copy nslots from source address to destination address. - * Stack before: [..., depth_items..., dest_addr, src_addr] (src TOS) - * Stack after: [..., depth_items...] - * depth: number of items below dest_addr that must be preserved (e.g. 1 if - * there's a base pointer between the rest of the stack and dest_addr). */ -void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth) { - for (usize i = 0; i < nslots; i++) { - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); - if (i > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PICK, SPL_VOID, 2 + depth); - if (i > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); - } - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); -} - -/* ---- Uniform LOAD/STORE type helper ---- */ - -spl_type_t spl_type_emit_type(spl_type_info_t *type) { - if (!type) - return SPL_I32; - if (type->kind == TYPE_BASIC) - return type->basic_type; - if (type->kind == TYPE_PTR) - return SPL_PTR; - if (type->kind == TYPE_ENUM) { - /* Simple enum (no data variants): tag is I32 */ - vec_for(type->variants, i) { - if (vec_at(type->variants, i).data_type) - return SPL_PTR; /* has data — treat as aggregate */ - } - return SPL_I32; - } - return SPL_PTR; -} - -/* Scalar types fit in one slot, are auto-loaded on field access, - * and support == / != comparison directly. */ -int spl_type_is_scalar(spl_type_info_t *type) { - if (!type) - return 1; - if (type->kind == TYPE_BASIC || type->kind == TYPE_PTR) - return 1; - if (type->kind == TYPE_ENUM) { - vec_for(type->variants, i) { - if (vec_at(type->variants, i).data_type) - return 0; /* has data — aggregate */ - } - return 1; /* simple enum — scalar */ - } - return 0; -} - -/* ---- Load from [saved_ptr+offset], store to local var ---- */ - -void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, - spl_type_info_t *data_type, int var_offset) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - if (byte_offset > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - if (data_type && !spl_type_is_scalar(data_type) && - spl_type_size(data_type) > sizeof(spl_val_t)) { - spl_emit_copy_slots(ctx, var_offset, spl_type_slot_count(data_type)); - } else { - spl_type_t bt = spl_type_emit_type(data_type); - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); - } -} - /* ---- Scope management ---- */ void spl_push_scope(spl_comp_t *ctx) { @@ -229,39 +96,17 @@ 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) { +int spl_declare_var(spl_comp_t *ctx, const char *name, int type_idx, int is_const) { spl_var_info_t var; memset(&var, 0, sizeof(var)); var.name = strdup(name); - var.type = type; + var.type_idx = type_idx; var.is_const = is_const; var.depth = ctx->scope_depth; - var.offset = ctx->current_local_bytes; + var.offset = fa_alloc(&ctx->emit.frame, spl_type_size(&ctx->tctx, type_idx)); - usize var_size = spl_type_size(type); - /* Round up to sizeof(spl_val_t) alignment so params (pushed as spl_val_t) align */ - usize aligned = (var_size + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1); - if (aligned < sizeof(spl_val_t)) - aligned = sizeof(spl_val_t); - ctx->current_local_bytes += (int)aligned; - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; - - /* Add to current scope */ if (vec_size(ctx->scopes) > 0) { spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1); vec_push(scope->vars, var); @@ -272,11 +117,9 @@ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, in spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name) { for (int i = (int)vec_size(ctx->scopes) - 1; i >= 0; i--) { spl_scope_t *scope = &vec_at(ctx->scopes, i); - /* Search in reverse order so later declarations shadow earlier ones */ for (int j = (int)vec_size(scope->vars) - 1; j >= 0; j--) { - if (strcmp(vec_at(scope->vars, j).name, name) == 0) { + if (strcmp(vec_at(scope->vars, j).name, name) == 0) return &vec_at(scope->vars, j); - } } } return NULL; @@ -289,13 +132,12 @@ int spl_get_var_offset(spl_comp_t *ctx, const char *name) { /* ---- Function management ---- */ -int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams, +int spl_declare_func(spl_comp_t *ctx, const char *name, int ret_type_idx, 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->ret_type_idx = ret_type_idx; existing->nparams = nparams; existing->is_extern = is_extern; existing->is_pub = is_pub; @@ -308,32 +150,37 @@ int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_typ spl_func_info_t fi; memset(&fi, 0, sizeof(fi)); fi.name = strdup(name); - fi.ret_type = ret_type; + fi.ret_type_idx = ret_type_idx; fi.nparams = nparams; fi.is_extern = is_extern; fi.is_pub = is_pub; - if (is_extern) { + if (is_extern) fi.func_idx = -1; - } else { + else fi.func_idx = spl_prog_add_func_simple(&ctx->prog, name, nparams); - } vec_push(ctx->funcs, fi); return (int)vec_size(ctx->funcs) - 1; } int spl_lookup_func(spl_comp_t *ctx, const char *name) { - /* 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; + { + int current = ctx->tctx.current_type_idx; + while (current >= 0) { + spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, current); + if (items) { + vec_for(*items, j) { + spl_type_item_t *it = &vec_at(*items, j); + if (it->item_kind == ITEM_METHOD && it->name && + strcmp(it->name, name) == 0) + return it->method.func_idx; + } + } + current = vec_at(ctx->tctx.types, current).parent_type_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; @@ -341,8 +188,6 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) { return -1; } -/* Ensure a native function is registered for NCALL dispatch. - * Returns the native index. */ int spl_ensure_native(spl_comp_t *ctx, const char *name) { vec_for(ctx->prog.natives, ni) { if (strcmp(vec_at(ctx->prog.natives, ni).name, name) == 0) @@ -372,109 +217,69 @@ void spl_emit_defer(spl_comp_t *ctx) { if (ctx->defer_count >= DEFER_MAX) return; - /* Emit JMP placeholder (will skip defer body during normal execution). - * This JMP is at the current position. The defer body starts right after. */ - spl_val_t jmp_skip = spl_emit_jmp(ctx); + spl_val_t jmp_skip = emit_jmp_here(&ctx->emit); spl_defer_entry_t *e = &ctx->defer_stack[ctx->defer_count]; - e->body_start = jmp_skip + 1; /* instruction right after the JMP = body start */ - e->jmp_exit = 0; /* set after body is parsed */ + e->body_start = jmp_skip + 1; + e->jmp_exit = 0; e->depth = ctx->scope_depth; e->count_at_decl = ctx->defer_count; ctx->defer_count++; } void spl_emit_defer_epilogue(spl_comp_t *ctx, int depth) { - /* === Pass 1: patch skip JMPs to jump past their defer body === - * During normal execution, the skip JMP at body_start-1 must jump - * over the defer body (to jmp_exit + 1 = the code after the defer). */ for (int i = 0; i < ctx->defer_count; i++) { if (ctx->defer_stack[i].depth != depth) continue; spl_defer_entry_t *e = &ctx->defer_stack[i]; - spl_val_t skip_addr = e->body_start - 1; /* the JMP L_skip instruction */ - spl_val_t skip_target = e->jmp_exit + 1; /* instruction right after defer body */ - spl_val_t skip_offset = skip_target - skip_addr - 1; - spl_patch(ctx, skip_addr, skip_offset); + spl_val_t skip_addr = e->body_start - 1; + spl_val_t skip_target = e->jmp_exit + 1; + emit_patch(&ctx->emit, skip_addr, skip_target - skip_addr - 1); } - /* === Pass 2: at scope exit, emit backwards JMPs to each defer body === - * Process in reverse order so the LAST declared defer runs FIRST at scope exit. - * Each defer body's trailing JMP_exit gets patched to jump to right after - * the scope-exit JMP we just emitted, so control chains through correctly. */ for (int i = ctx->defer_count - 1; i >= 0; i--) { if (ctx->defer_stack[i].depth != depth) continue; spl_defer_entry_t *e = &ctx->defer_stack[i]; - /* Emit JMP backwards to the defer body */ spl_val_t here = vec_size(ctx->prog.insns); spl_val_t jmp_offset = (spl_val_t)((isize)e->body_start - (isize)here - 1); - spl_emit(ctx, SPL_JMP, SPL_VOID, jmp_offset); + emit_jmp(&ctx->emit, jmp_offset); - /* Patch the body's trailing JMP_exit to jump to here+1 (right after the - * backwards JMP we just emitted). This chains to the next defer or exits. */ - if (e->jmp_exit > 0) { - spl_val_t exit_target = here + 1; - spl_val_t offset = exit_target - e->jmp_exit - 1; - spl_patch(ctx, e->jmp_exit, offset); - } + if (e->jmp_exit > 0) + emit_patch(&ctx->emit, e->jmp_exit, here + 1 - e->jmp_exit - 1); } - /* Remove processed defers from stack */ int new_count = 0; for (int i = 0; i < ctx->defer_count; i++) { - if (ctx->defer_stack[i].depth != depth) { + if (ctx->defer_stack[i].depth != depth) ctx->defer_stack[new_count++] = ctx->defer_stack[i]; - } } ctx->defer_count = new_count; } /* ---- Register runtime natives ---- */ -void spl_comp_register(spl_prog_t *prog) { - /* Runtime support functions for compiled SPL programs go here. - * For stage1 bootstrap, most operations are handled inline. - * We register basic runtime helpers if needed. */ - (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; - } - } -} +void spl_comp_register(spl_prog_t *prog) { (void)prog; } /* ---- Main compilation ---- */ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) { - /* Phase 1: Lex */ ctx->toks = spl_lex(source, fname); ctx->tok_idx = 0; ctx->fname = fname; ctx->source = source; - /* Skip initial newlines */ while (ctx->tok_idx < vec_size(ctx->toks) && vec_at(ctx->toks, ctx->tok_idx).type == TOK_ENDLINE) ctx->tok_idx++; - /* Phase 2-4: Parse and codegen */ spl_parse_prog(ctx); if (ctx->has_error) return -1; - /* Phase 5: Patch all function call fixups (forward references) */ - spl_patch_call_fixups(ctx); + emit_patch_call_fixups(&ctx->emit, &ctx->prog); return 0; } diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index e36dc4b..dd8b555 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -10,108 +10,22 @@ #include #include -/* ============================================================ - * Type representation - * ============================================================ */ - -typedef enum { - TYPE_VOID, - TYPE_BASIC, - TYPE_PTR, - TYPE_ARRAY, - TYPE_SLICE, - TYPE_STRUCT, - TYPE_UNION, - TYPE_ENUM, - TYPE_ENUM_VARIANT, /* enum variant with data */ - TYPE_NAME, /* named alias */ - TYPE_INFER, /* _ (to be inferred) */ -} spl_type_kind_t; +#include "spl_type.h" +#include "spl_emit.h" /* Forward declarations */ -typedef struct spl_type_info spl_type_info_t; typedef struct spl_comp spl_comp_t; -/* Struct field */ -typedef struct { - char *name; - spl_type_info_t *type; - usize offset; /* byte offset in struct layout */ -} spl_field_t; -typedef VEC(spl_field_t) spl_field_vec_t; - -/* Enum variant */ -typedef struct { - char *name; - spl_type_info_t *data_type; /* NULL for simple enum */ - int value; /* constant index */ -} spl_enum_variant_t; -typedef VEC(spl_enum_variant_t) spl_enum_variant_vec_t; - -/* Method info (struct/enum methods) */ -typedef struct { - char *name; - int func_idx; /* index in ctx->funcs */ -} spl_method_info_t; -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 */ - 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 */ -spl_type_info_t *spl_type_basic(spl_type_t bt); -spl_type_info_t *spl_type_ptr(spl_type_info_t *elem); -spl_type_info_t *spl_type_array(spl_type_info_t *elem, usize len); -spl_type_info_t *spl_type_slice(spl_type_info_t *elem); -spl_type_info_t *spl_type_struct(const char *name); -spl_type_info_t *spl_type_union(const char *name); -spl_type_info_t *spl_type_enum(const char *name); -void spl_type_add_field(spl_type_info_t *st, const char *name, spl_type_info_t *ftype); -void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t *dtype); -void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx); -void spl_type_compute_layout(spl_type_info_t *t); -usize spl_type_size(spl_type_info_t *t); -usize spl_type_slot_count(spl_type_info_t *t); -/* Byte stride between consecutive elements in storage (slot-based layout) */ -usize spl_type_elem_stride(spl_type_info_t *elem); -const char *spl_type_str(spl_type_info_t *t); -int spl_type_is_integer(spl_type_t bt); -spl_type_info_t *spl_type_clone(spl_type_info_t *t); - -/* Determine the uniform type for LOAD/STORE codegen */ -spl_type_t spl_type_emit_type(spl_type_info_t *type); -/* Returns 1 if type is scalar (fits in one slot, auto-loadable, comparable with ==/!=) */ -int spl_type_is_scalar(spl_type_info_t *type); - -/* Emit LOAD from [saved_ptr + byte_offset] and STORE to local var at var_offset. - * Stack: [] → [] - * The saved_ptr is loaded from the 1-slot temp at ptr_slot_offset. */ -void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, - spl_type_info_t *data_type, int var_offset); - /* ============================================================ * Scope / symbol table * ============================================================ */ typedef struct spl_var_info { char *name; - spl_type_info_t *type; - int offset; /* byte offset from fp */ + int type_idx; + int offset; int is_const; - int depth; /* scope depth */ + int depth; } spl_var_info_t; typedef VEC(spl_var_info_t) spl_var_vec_t; @@ -123,12 +37,12 @@ typedef VEC(spl_scope_t) spl_scope_vec_t; typedef struct spl_func_info { char *name; - spl_type_info_t *ret_type; - spl_type_info_t **param_types; + int ret_type_idx; + int *param_type_indices; char **param_names; int nparams; - int func_idx; /* index in prog->funcs */ - int is_extern; /* #[extern("vm")] */ + int func_idx; + int is_extern; int is_pub; } spl_func_info_t; typedef VEC(spl_func_info_t) spl_func_info_vec_t; @@ -141,10 +55,10 @@ typedef VEC(spl_func_info_t) spl_func_info_vec_t; #define DEFER_MAX 64 typedef struct { - usize body_start; /* IP where defer body starts (after skip-JMP) */ - usize jmp_exit; /* IP of the JMP after the body (to patch at scope exit) */ - int depth; /* scope depth */ - int count_at_decl; /* defer_count when declared */ + usize body_start; + usize jmp_exit; + int depth; + int count_at_decl; } spl_defer_entry_t; typedef struct spl_comp { @@ -157,6 +71,9 @@ typedef struct spl_comp { /* Program output */ spl_prog_t prog; + /* IR emission + frame allocator */ + spl_emit_t emit; + /* Error state */ char error_msg[COMP_ERROR_MAX]; int has_error; @@ -168,22 +85,19 @@ typedef struct spl_comp { /* Function table */ spl_func_info_vec_t funcs; - /* Type definitions (name → spl_type_info_t*) */ - MAP(const char *, spl_type_info_t *) type_defs; + /* Type context — first-class type arena + namespace */ + spl_type_ctx_t tctx; /* 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 */ - VEC(spl_type_info_t *) ns_chain; /* type namespace scope chain */ + int current_ret_type_idx; /* Loop context for break/continue */ int in_loop; - usize *break_patches; /* instruction addresses to patch */ + usize *break_patches; usize break_patch_count; usize break_patch_cap; - usize continue_target; /* ip to jump to for continue */ + usize continue_target; /* Defer stack */ spl_defer_entry_t defer_stack[DEFER_MAX]; @@ -195,13 +109,7 @@ typedef struct spl_comp { /* Const values */ 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 */ + int addr_of_mode; } spl_comp_t; /* Initialize/destroy compiler context */ @@ -228,7 +136,6 @@ void parse_type_decl(spl_comp_t *ctx); * Expression functions (spl_expr.c) * ============================================================ */ -/* Precedence levels for Pratt parser */ enum { PREC_MIN = 0, PREC_ASSIGN = 1, @@ -246,22 +153,17 @@ enum { PREC_POSTFIX = 13 }; -/* Result of expression codegen */ typedef struct { - spl_type_info_t *type; - int is_lvalue; /* 1 = address on stack, 0 = value on stack */ + int type_idx; + int is_lvalue; } spl_expr_result_t; spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec); -spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type); +spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx); -/* Match arm pattern comparison — emit code to compare saved match value - * against a parsed pattern. Comparison logic belongs in expr layer - * so that match arms behave as "enhanced if-conditions". - * For enum: parses .VariantName, emits tag comparison, returns variant. - * For value: parses expression, emits equality comparison. */ -spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset, int by_value); +/* Match arm pattern comparison */ +int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, + int by_value); void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset); /* ============================================================ @@ -273,35 +175,21 @@ void spl_parse_block(spl_comp_t *ctx); spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx); /* ============================================================ - * Codegen helpers (spl_comp.c) + * Remaining helpers in spl_comp.c * ============================================================ */ -spl_val_t spl_emit(spl_comp_t *ctx, uint16_t opcode, uint16_t type, spl_val_t imm); -void spl_patch(spl_comp_t *ctx, spl_val_t addr, spl_val_t target); -spl_val_t spl_emit_jmp(spl_comp_t *ctx); -spl_val_t spl_emit_bz(spl_comp_t *ctx); -spl_val_t spl_emit_bnz(spl_comp_t *ctx); -void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr); - -/* Copy multi-slot value from TOS (temp address) to frame-relative destination. - * Stack: [..., temp_addr] → [...] - * Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */ -void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots); -void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth); -void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type); -void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type); +void spl_emit_ret(spl_comp_t *ctx, int ret_type_idx); +void spl_emit_store_init(spl_comp_t *ctx, int var_offset, int var_type_idx); /* Variable management */ -int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const); +int spl_declare_var(spl_comp_t *ctx, const char *name, int type_idx, int is_const); spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name); int spl_get_var_offset(spl_comp_t *ctx, const char *name); /* Function management */ -int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams, +int spl_declare_func(spl_comp_t *ctx, const char *name, int ret_type_idx, int nparams, int is_extern, int is_pub); int spl_lookup_func(spl_comp_t *ctx, const char *name); - -/* Ensure a native function is registered for NCALL dispatch, returns index */ int spl_ensure_native(spl_comp_t *ctx, const char *name); /* String/data management */ @@ -312,23 +200,12 @@ 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); -/* Type lookup and parsing */ -spl_type_info_t *spl_resolve_type(spl_comp_t *ctx, const char *name); -spl_type_info_t *spl_parse_type(spl_comp_t *ctx); +/* Register runtime natives (stub) */ +void spl_comp_register(spl_prog_t *prog); /* Shared token helpers (defined in spl_parser.c) */ spl_tok_t *peek(spl_comp_t *ctx); diff --git a/stage1/spl_emit.c b/stage1/spl_emit.c new file mode 100644 index 0000000..7481a25 --- /dev/null +++ b/stage1/spl_emit.c @@ -0,0 +1,203 @@ +/* spl_emit.c — IR emission: frame allocator + Layer 1/2 ops */ + +#include "spl_emit.h" +#include "spl_type.h" +#include + +/* ============================================================ + * Lifecycle + * ============================================================ */ + +void spl_emit_init(spl_emit_t *e, spl_prog_t *prog) { + memset(e, 0, sizeof(*e)); + e->prog = prog; + vec_init(e->call_fixups); + vec_init(e->call_fixup_funcs); +} + +void spl_emit_drop(spl_emit_t *e) { + if (!e) + return; + vec_free(e->call_fixups); + vec_free(e->call_fixup_funcs); +} + +/* ============================================================ + * Frame allocator — non-inline + * ============================================================ */ + +int fa_alloc_type(spl_frame_alloc_t *fa, spl_type_ctx_t *tctx, int type_idx) { + return fa_alloc(fa, spl_type_size(tctx, type_idx)); +} + +static spl_val_t emit_raw(spl_emit_t *e, uint16_t opcode, uint16_t type, + spl_val_t imm) { + return spl_prog_emit(e->prog, opcode, type, imm); +} + +/* ============================================================ + * Layer 1 — single-instruction semantic wrappers + * ============================================================ */ + +void emit_drop(spl_emit_t *e) { emit_raw(e, SPL_DROP, SPL_VOID, 0); } +void emit_dup(spl_emit_t *e) { emit_raw(e, SPL_DUP, SPL_VOID, 0); } +void emit_swap(spl_emit_t *e) { emit_raw(e, SPL_SWAP, SPL_VOID, 0); } +void emit_rot(spl_emit_t *e) { emit_raw(e, SPL_ROT, SPL_VOID, 0); } +void emit_pick(spl_emit_t *e, int n) { emit_raw(e, SPL_PICK, SPL_VOID, n); } + +void emit_push_i32(spl_emit_t *e, int v) { emit_raw(e, SPL_PUSH, SPL_I32, (spl_val_t)v); } +void emit_push_usize(spl_emit_t *e, usize v) { + emit_raw(e, SPL_PUSH, SPL_USIZE, (spl_val_t)v); +} +void emit_push_u64(spl_emit_t *e, uint64_t v) { + emit_raw(e, SPL_PUSH, SPL_U64, (spl_val_t)v); +} +void emit_push_f64(spl_emit_t *e, uint64_t v) { + emit_raw(e, SPL_PUSH, SPL_F64, (spl_val_t)v); +} +void emit_push_ptr(spl_emit_t *e, spl_val_t v) { emit_raw(e, SPL_PUSH, SPL_PTR, v); } +void emit_push_type(spl_emit_t *e, uint16_t bt, spl_val_t v) { + emit_raw(e, SPL_PUSH, bt, v); +} + +void emit_load_ptr(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_PTR, 0); } +void emit_load_usize(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_USIZE, 0); } +void emit_load_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_LOAD, bt, 0); } + +void emit_store_i32(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_I32, 0); } +void emit_store_ptr(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_PTR, 0); } +void emit_store_usize(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_USIZE, 0); } +void emit_store_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_STORE, bt, 0); } + +void emit_add_usize(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_USIZE, 0); } +void emit_add_u64(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_U64, 0); } +void emit_mul_u64(spl_emit_t *e) { emit_raw(e, SPL_MUL, SPL_U64, 0); } +void emit_sub_usize(spl_emit_t *e) { emit_raw(e, SPL_SUB, SPL_USIZE, 0); } + +void emit_ult_usize(spl_emit_t *e) { emit_raw(e, SPL_ULT, SPL_USIZE, 0); } + +void emit_jmp(spl_emit_t *e, spl_val_t offset) { emit_raw(e, SPL_JMP, SPL_VOID, offset); } +spl_val_t emit_jmp_here(spl_emit_t *e) { return emit_raw(e, SPL_JMP, SPL_VOID, 0); } +spl_val_t emit_bz_here(spl_emit_t *e) { return emit_raw(e, SPL_BZ, SPL_VOID, 0); } +spl_val_t emit_bnz_here(spl_emit_t *e) { return emit_raw(e, SPL_BNZ, SPL_VOID, 0); } + +void emit_patch(spl_emit_t *e, spl_val_t addr, spl_val_t target) { + if (addr < vec_size(e->prog->insns)) + vec_at(e->prog->insns, addr).imm = target; +} + +void emit_patch_here(spl_emit_t *e, spl_val_t addr) { + spl_val_t here = vec_size(e->prog->insns); + emit_patch(e, addr, here - addr - 1); +} + +void emit_laddr(spl_emit_t *e, int offset) { emit_raw(e, SPL_LADDR, SPL_PTR, offset); } +void emit_gaddr(spl_emit_t *e, int idx) { emit_raw(e, SPL_GADDR, SPL_PTR, idx); } + +void emit_call(spl_emit_t *e, int nargs) { emit_raw(e, SPL_CALL, SPL_VOID, nargs); } +void emit_ncall(spl_emit_t *e, int nargs) { + emit_raw(e, SPL_NCALL, SPL_VOID, nargs); +} + +void emit_alloc(spl_emit_t *e, int slots) { emit_raw(e, SPL_ALLOC, SPL_VOID, slots); } +void emit_binop(spl_emit_t *e, uint16_t op, uint16_t bt) { emit_raw(e, op, bt, 0); } +void emit_dbg_void(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_VOID, 0); } +void emit_dbg_usize(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_USIZE, 0); } + +/* ============================================================ + * Layer 2 — semantic-level helpers + * ============================================================ */ + +void emit_frame_copy(spl_emit_t *e, int dest_offset, usize nslots) { + for (usize i = 0; i < nslots; i++) { + if (i < nslots - 1) + emit_dup(e); + if (i > 0) { + emit_push_usize(e, i * sizeof(spl_val_t)); + emit_add_usize(e); + } + emit_load_ptr(e); + emit_laddr(e, dest_offset + (int)(i * sizeof(spl_val_t))); + emit_swap(e); + emit_store_ptr(e); + } +} + +void emit_copy_addr_to_addr(spl_emit_t *e, usize nslots, int depth) { + for (usize i = 0; i < nslots; i++) { + emit_dup(e); + if (i > 0) { + emit_push_usize(e, i * sizeof(spl_val_t)); + emit_add_usize(e); + } + emit_load_ptr(e); + emit_pick(e, 2 + depth); + if (i > 0) { + emit_push_usize(e, i * sizeof(spl_val_t)); + emit_add_usize(e); + } + emit_swap(e); + emit_store_ptr(e); + } + emit_drop(e); + emit_drop(e); +} + +void emit_load_to_var(spl_emit_t *e, spl_type_ctx_t *tctx, int ptr_slot_offset, + usize byte_offset, int data_type_idx, int var_offset) { + emit_laddr(e, ptr_slot_offset); + emit_load_ptr(e); + emit_ptr_add(e, byte_offset); + if (data_type_idx >= 0 && !spl_type_is_scalar(tctx, data_type_idx) && + spl_type_size(tctx, data_type_idx) > sizeof(spl_val_t)) { + emit_frame_copy(e, var_offset, spl_type_slot_count(tctx, data_type_idx)); + } else { + uint16_t bt = spl_type_emit_type(tctx, data_type_idx); + emit_load_type(e, bt); + emit_laddr(e, var_offset); + emit_swap(e); + emit_store_type(e, bt); + } +} + +void emit_store_to_laddr(spl_emit_t *e, int offset, uint16_t type) { + emit_laddr(e, offset); + emit_swap(e); + emit_store_type(e, type); +} + +void emit_ptr_add(spl_emit_t *e, usize byte_off) { + if (byte_off > 0) { + emit_push_usize(e, byte_off); + emit_add_usize(e); + } +} + +spl_val_t emit_call_with_fixup(spl_emit_t *e, int nargs, int func_idx) { + spl_val_t fixup_addr = emit_raw(e, SPL_PUSH, SPL_PTR, 0); + emit_call(e, nargs); + vec_push(e->call_fixups, fixup_addr); + vec_push(e->call_fixup_funcs, func_idx); + return fixup_addr; +} + +void emit_return(spl_emit_t *e, spl_type_ctx_t *tctx, int ret_type_idx) { + if (spl_type_needs_multi_slot(tctx, ret_type_idx)) { + emit_laddr(e, (int)sizeof(spl_val_t)); + emit_raw(e, SPL_RET, SPL_PTR, 0); + } else { + uint16_t rt = spl_type_emit_type(tctx, ret_type_idx); + emit_raw(e, SPL_RET, rt, 0); + } +} + +void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog) { + for (usize i = 0; i < vec_size(e->call_fixups); i++) { + spl_val_t insn_idx = vec_at(e->call_fixups, i); + int pfi = vec_at(e->call_fixup_funcs, i); + if (pfi >= 0 && pfi < (int)vec_size(prog->funcs)) { + spl_val_t addr = vec_at(prog->funcs, pfi).address; + vec_at(prog->insns, insn_idx).imm = addr; + } + } +} diff --git a/stage1/spl_emit.h b/stage1/spl_emit.h new file mode 100644 index 0000000..0f8dac3 --- /dev/null +++ b/stage1/spl_emit.h @@ -0,0 +1,138 @@ +#ifndef __SPL_EMIT_H__ +#define __SPL_EMIT_H__ + +#include "../stage0/spl_ir.h" +#include "spl_type.h" +#include + +/* ============================================================ + * Frame allocator + * ============================================================ */ + +typedef struct { + int current_bytes; + int peak_bytes; +} spl_frame_alloc_t; + +static inline void fa_init(spl_frame_alloc_t *fa) { + fa->current_bytes = 0; + fa->peak_bytes = 0; +} + +static inline int fa_alloc(spl_frame_alloc_t *fa, usize byte_size) { + usize aligned = (byte_size + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1); + if (aligned < sizeof(spl_val_t)) + aligned = sizeof(spl_val_t); + int offset = fa->current_bytes; + fa->current_bytes += (int)aligned; + if (fa->current_bytes > fa->peak_bytes) + fa->peak_bytes = fa->current_bytes; + return offset; +} + +int fa_alloc_type(spl_frame_alloc_t *fa, spl_type_ctx_t *tctx, int type_idx); + +static inline void fa_reset_to(spl_frame_alloc_t *fa, int mark) { fa->current_bytes = mark; } + +static inline int fa_alloc_slots(spl_frame_alloc_t *fa, usize nslots) { + return fa_alloc(fa, nslots * sizeof(spl_val_t)); +} + +static inline int fa_alloc_temp(spl_frame_alloc_t *fa, usize nslots) { + return fa_alloc_slots(fa, nslots); +} + +static inline void fa_free(spl_frame_alloc_t *fa, int mark) { fa->current_bytes = mark; } + +/* ============================================================ + * Emit context + * ============================================================ */ + +typedef struct { + spl_prog_t *prog; + spl_frame_alloc_t frame; + VEC(spl_val_t) call_fixups; + VEC(int) call_fixup_funcs; +} spl_emit_t; + +void spl_emit_init(spl_emit_t *e, spl_prog_t *prog); +void spl_emit_drop(spl_emit_t *e); + +/* ============================================================ + * Layer 1 — single-instruction semantic wrappers + * ============================================================ */ + +/* Stack ops */ +void emit_drop(spl_emit_t *e); +void emit_dup(spl_emit_t *e); +void emit_swap(spl_emit_t *e); +void emit_rot(spl_emit_t *e); +void emit_pick(spl_emit_t *e, int n); + +/* Push */ +void emit_push_i32(spl_emit_t *e, int v); +void emit_push_usize(spl_emit_t *e, usize v); +void emit_push_u64(spl_emit_t *e, uint64_t v); +void emit_push_f64(spl_emit_t *e, uint64_t v); +void emit_push_ptr(spl_emit_t *e, spl_val_t v); +void emit_push_type(spl_emit_t *e, uint16_t bt, spl_val_t v); + +/* Load */ +void emit_load_ptr(spl_emit_t *e); +void emit_load_usize(spl_emit_t *e); +void emit_load_type(spl_emit_t *e, uint16_t bt); + +/* Store */ +void emit_store_i32(spl_emit_t *e); +void emit_store_ptr(spl_emit_t *e); +void emit_store_usize(spl_emit_t *e); +void emit_store_type(spl_emit_t *e, uint16_t bt); + +/* Arithmetic */ +void emit_add_usize(spl_emit_t *e); +void emit_add_u64(spl_emit_t *e); +void emit_mul_u64(spl_emit_t *e); +void emit_sub_usize(spl_emit_t *e); + +/* Compare */ +void emit_ult_usize(spl_emit_t *e); + +/* Branch */ +void emit_jmp(spl_emit_t *e, spl_val_t offset); +spl_val_t emit_jmp_here(spl_emit_t *e); +spl_val_t emit_bz_here(spl_emit_t *e); +spl_val_t emit_bnz_here(spl_emit_t *e); +void emit_patch(spl_emit_t *e, spl_val_t addr, spl_val_t target); +void emit_patch_here(spl_emit_t *e, spl_val_t addr); + +/* Address */ +void emit_laddr(spl_emit_t *e, int offset); +void emit_gaddr(spl_emit_t *e, int idx); + +/* Call */ +void emit_call(spl_emit_t *e, int nargs); +void emit_ncall(spl_emit_t *e, int nargs); + +/* Misc */ +void emit_alloc(spl_emit_t *e, int slots); +void emit_binop(spl_emit_t *e, uint16_t op, uint16_t bt); +void emit_dbg_void(spl_emit_t *e); +void emit_dbg_usize(spl_emit_t *e); + +/* ============================================================ + * Layer 2 — semantic-level helpers + * ============================================================ */ + +void emit_frame_copy(spl_emit_t *e, int dest_offset, usize nslots); +void emit_copy_addr_to_addr(spl_emit_t *e, usize nslots, int depth); +void emit_load_to_var(spl_emit_t *e, spl_type_ctx_t *tctx, int ptr_slot_offset, + usize byte_offset, int data_type_idx, int var_offset); +void emit_store_to_laddr(spl_emit_t *e, int offset, uint16_t type); +void emit_ptr_add(spl_emit_t *e, usize byte_off); +spl_val_t emit_call_with_fixup(spl_emit_t *e, int nargs, int func_idx); +void emit_return(spl_emit_t *e, spl_type_ctx_t *tctx, int ret_type_idx); + +/* Patch all fixups */ +void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog); + +#endif /* __SPL_EMIT_H__ */ diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index e79d5fb..92c65d1 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -1,15 +1,9 @@ -/* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */ - #include "spl_comp.h" #include "spl_lex_util.h" #include #include #include -/* ============================================================ - * Precedence table - * ============================================================ */ - static int tok_prec(spl_tok_type_t t) { switch (t) { case TOK_OR_OR: @@ -57,99 +51,69 @@ static int tok_prec(spl_tok_type_t t) { } } -/* ============================================================ - * Forward declarations - * ============================================================ */ - static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, spl_tok_type_t op); static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, - spl_type_info_t *type); + int type_idx); static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left); -/* Slice creation from array/slice range expression. - * Stack in: [base_addr, begin, end] - * Stack out: [ptr, len] - * - * Uses pure stack operations — no temp slots needed. - * ptr = base + begin * stride - * len = end - begin - * - * Strategy: PICK copies of values we need, compute results on stack, - * then ROT inaccessible values to TOS and DROP them. */ static void emit_slice_create(spl_comp_t *ctx, usize stride) { - /* Stack: [base, begin, end] */ + emit_pick(&ctx->emit, 2); + emit_pick(&ctx->emit, 2); + emit_push_u64(&ctx->emit, stride); + emit_mul_u64(&ctx->emit); + emit_add_u64(&ctx->emit); - /* --- Compute ptr = base + begin * stride --- */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* [base, begin, end, base] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* [base, begin, end, base, begin] */ - spl_emit(ctx, SPL_PUSH, SPL_U64, stride); - spl_emit(ctx, SPL_MUL, SPL_U64, 0); /* [base, begin, end, base, begin*stride] */ - spl_emit(ctx, SPL_ADD, SPL_U64, 0); /* [base, begin, end, ptr] */ - - /* --- Compute len = end - begin --- */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [base, begin, end, ptr, end] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 3); /* [base, begin, end, ptr, end, begin] */ - spl_emit(ctx, SPL_SUB, SPL_USIZE, 0); /* [base, begin, end, ptr, len] */ - - /* --- Cleanup: drop [base, begin, end], keep [ptr, len] --- */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [base, begin, ptr, len, end] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [base, begin, ptr, len] */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [base, ptr, len, begin] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [base, ptr, len] */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [ptr, len, base] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [ptr, len] */ + emit_pick(&ctx->emit, 1); + emit_pick(&ctx->emit, 3); + emit_sub_usize(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); } -/* Emit code to access slice element by index. - * For TYPE_SLICE, the stack has [slice_struct_addr, index]. - * We need to load the data ptr from the struct before indexing. - * Stack in: [slice_struct_addr, index] - * Stack out: [element_addr] */ -static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) { - usize elem_size = spl_type_elem_stride(elem); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [index, slice_struct_addr] */ - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [index, data_ptr] */ - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [data_ptr, index] */ - spl_emit(ctx, SPL_PUSH, SPL_U64, elem_size); - spl_emit(ctx, SPL_MUL, SPL_U64, 0); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); /* [data_ptr + index * elem_size] */ +static void emit_slice_index(spl_comp_t *ctx, int elem_type_idx) { + usize elem_size = spl_type_elem_stride(&ctx->tctx, elem_type_idx); + emit_swap(&ctx->emit); + emit_load_ptr(&ctx->emit); + emit_swap(&ctx->emit); + emit_push_u64(&ctx->emit, elem_size); + emit_mul_u64(&ctx->emit); + emit_add_u64(&ctx->emit); } -/* Try to resolve Type.Member for compile-time type access: - * - Enum variants → push variant integer value - * - Nested types (struct/enum/union) → return nested type - * Uses qualified name (TypeName.Member) first for namespace isolation, - * then falls back to simple name lookup. - * Returns 1 if resolved. */ -static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const char *field, +static int spl_resolve_type_member(spl_comp_t *ctx, int type_idx, const char *field, spl_expr_result_t *result) { - /* For enum: check variants first */ - if (type->kind == TYPE_ENUM) { - vec_for(type->variants, i) { - if (strcmp(vec_at(type->variants, i).name, field) == 0) { - spl_emit(ctx, SPL_PUSH, SPL_I32, vec_at(type->variants, i).value); - *result = (spl_expr_result_t){type, 0}; + if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM) { + spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, type_idx); + vec_for(*items, i) { + spl_type_item_t *it = &vec_at(*items, i); + if (it->item_kind != ITEM_VARIANT) continue; + if (strcmp(it->name, field) == 0) { + emit_push_i32(&ctx->emit, it->enum_field.value); + *result = (spl_expr_result_t){type_idx, 0}; return 1; } } } - /* Check nested types — try qualified name (Type.field) first for namespace isolation */ - if (type->name) { + const char *type_name = spl_type_name(&ctx->tctx, type_idx); + if (type_name) { char qualified[512]; - int qlen = snprintf(qualified, sizeof(qualified), "%s.%s", type->name, field); + int qlen = snprintf(qualified, sizeof(qualified), "%s.%s", type_name, field); if (qlen > 0 && (usize)qlen < sizeof(qualified)) { - spl_type_info_t *nested = spl_resolve_type(ctx, qualified); - if (nested) { + int nested = spl_type_resolve(&ctx->tctx, qualified); + if (nested >= 0) { *result = (spl_expr_result_t){nested, 1}; return 1; } } } - /* Fallback: try simple name lookup */ - spl_type_info_t *nested = spl_resolve_type(ctx, field); - if (nested) { + int nested = spl_type_resolve(&ctx->tctx, field); + if (nested >= 0) { *result = (spl_expr_result_t){nested, 1}; return 1; } @@ -157,11 +121,6 @@ static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const return 0; } -/* ============================================================ - * SIR opcode for binary operator - * ============================================================ */ - -/* Map assignment operator token (e.g. +=) to corresponding binary operator (e.g. +) */ static spl_tok_type_t assign_to_binop(spl_tok_type_t t) { switch (t) { case TOK_ASSIGN_ADD: @@ -229,10 +188,6 @@ static int binop_to_sir(spl_tok_type_t t, spl_type_t bt) { } } -/* ============================================================ - * Parse integer literal from lexeme - * ============================================================ */ - static int64_t parse_int(const char *s, usize len) { char buf[64]; usize clen = len < 63 ? len : 63; @@ -249,15 +204,11 @@ static int64_t parse_int(const char *s, usize len) { return (int64_t)strtoll(buf, NULL, 10); } -/* ============================================================ - * Prefix expression parsers - * ============================================================ */ - static spl_expr_result_t parse_int_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); int64_t val = parse_int(t->lexeme, t->len); - spl_emit(ctx, SPL_PUSH, SPL_I32, (spl_val_t)val); - spl_expr_result_t r = {spl_type_basic(SPL_I32), 0}; + emit_push_i32(&ctx->emit, (spl_val_t)val); + spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_I32), 0}; return r; } @@ -268,9 +219,9 @@ static spl_expr_result_t parse_float_literal(spl_comp_t *ctx) { memcpy(buf, t->lexeme, clen); buf[clen] = '\0'; double val = strtod(buf, NULL); - spl_emit(ctx, SPL_PUSH, SPL_F64, (spl_val_t)(int64_t)val); + emit_push_f64(&ctx->emit, (spl_val_t)(int64_t)val); (void)val; - spl_expr_result_t r = {spl_type_basic(SPL_F64), 0}; + spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_F64), 0}; return r; } @@ -289,8 +240,8 @@ static spl_expr_result_t parse_char_literal(spl_comp_t *ctx) { val = (unsigned char)s[1]; } } - spl_emit(ctx, SPL_PUSH, SPL_I32, (spl_val_t)val); - spl_expr_result_t r = {spl_type_basic(SPL_I32), 0}; + emit_push_i32(&ctx->emit, (spl_val_t)val); + spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_I32), 0}; return r; } @@ -316,20 +267,19 @@ static spl_expr_result_t parse_string_literal(spl_comp_t *ctx) { decoded[di] = '\0'; int gdi = spl_add_global_data(ctx, decoded, di + 1); free(decoded); - spl_emit(ctx, SPL_GADDR, SPL_PTR, gdi - 1); - spl_expr_result_t r = {spl_type_ptr(spl_type_basic(SPL_U8)), 0}; + emit_gaddr(&ctx->emit, gdi - 1); + spl_expr_result_t r = {spl_type_ptr(&ctx->tctx, spl_type_basic(&ctx->tctx, SPL_U8)), 0}; return r; } -/* Parse array literal: [N]Type{val1, val2, ...} */ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { - advance(ctx); /* skip [ */ + advance(ctx); skip_nl(ctx); int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } usize len = (usize)len_val; @@ -338,9 +288,9 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { expect(ctx, TOK_R_BRACKET); skip_nl(ctx); - spl_type_info_t *elem_type = spl_parse_type(ctx); - if (!elem_type) { - spl_expr_result_t r = {0}; + int elem_type_idx = spl_type_parse(&ctx->tctx, ctx); + if (elem_type_idx < 0) { + spl_expr_result_t r = {-1, 0}; return r; } @@ -363,14 +313,12 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { skip_nl(ctx); expect(ctx, TOK_R_BRACE); - spl_type_info_t *arr_type = spl_type_array(elem_type, len); - return (spl_expr_result_t){arr_type, 0}; + int arr_type_idx = spl_type_array(&ctx->tctx, elem_type_idx, len); + return (spl_expr_result_t){arr_type_idx, 0}; } -/* Parse slice inline literal: { .ptr = expr, .len = expr } - * Expects base address on TOS, consumes it. */ static void parse_slice_inline(spl_comp_t *ctx) { - advance(ctx); /* { */ + advance(ctx); skip_nl(ctx); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { if (peek(ctx)->type == TOK_COMMA) { @@ -388,23 +336,22 @@ static void parse_slice_inline(spl_comp_t *ctx) { advance(ctx); skip_nl(ctx); - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); + emit_dup(&ctx->emit); if (strcmp(fname, "ptr") == 0) { spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + emit_store_ptr(&ctx->emit); } else if (strcmp(fname, "len") == 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + emit_ptr_add(&ctx->emit, sizeof(spl_val_t)); spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); + emit_store_usize(&ctx->emit); } skip_nl(ctx); } expect(ctx, TOK_R_BRACE); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_drop(&ctx->emit); } -static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int base_offset, +static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, int base_offset, usize extra_offset) { if (peek(ctx)->type == TOK_DOT) advance(ctx); @@ -416,18 +363,19 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int b advance(ctx); skip_nl(ctx); - vec_for(*fields, fi) { - spl_field_t *f = &vec_at(*fields, fi); - if (strcmp(f->name, fname) == 0) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - usize byte_off = extra_offset + f->offset; - if (byte_off > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - if (f->type && f->type->kind == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE) { + vec_for(*items, fi) { + spl_type_item_t *it = &vec_at(*items, fi); + if (it->item_kind != ITEM_FIELD) continue; + if (strcmp(it->name, fname) == 0) { + emit_laddr(&ctx->emit, base_offset); + usize byte_off = extra_offset + it->aggregate_field.offset; + emit_ptr_add(&ctx->emit, byte_off); + int ft_idx = it->aggregate_field.type_idx; + if (ft_idx >= 0 && spl_type_kind(&ctx->tctx, ft_idx) == TYPE_SLICE && + peek(ctx)->type == TOK_L_BRACE) { parse_slice_inline(ctx); - } else if (f->type && f->type->kind == TYPE_ARRAY && peek(ctx)->type == TOK_L_BRACKET) { + } else if (ft_idx >= 0 && spl_type_kind(&ctx->tctx, ft_idx) == TYPE_ARRAY && + peek(ctx)->type == TOK_L_BRACKET) { advance(ctx); skip_nl(ctx); int len_val; @@ -439,28 +387,25 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int b skip_nl(ctx); expect(ctx, TOK_R_BRACKET); skip_nl(ctx); - spl_type_info_t *elem_type = spl_parse_type(ctx); + int elem_type_idx = spl_type_parse(&ctx->tctx, ctx); skip_nl(ctx); expect(ctx, TOK_L_BRACE); skip_nl(ctx); - usize stride = spl_type_elem_stride(elem_type); - spl_type_t st = spl_type_emit_type(elem_type); + usize stride = spl_type_elem_stride(&ctx->tctx, elem_type_idx); + spl_type_t st = spl_type_emit_type(&ctx->tctx, elem_type_idx); for (usize i = 0; i < arr_len; i++) { if (i > 0) { if (peek(ctx)->type == TOK_COMMA) advance(ctx); skip_nl(ctx); } - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); - if (i > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * stride); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } + emit_dup(&ctx->emit); + emit_ptr_add(&ctx->emit, i * stride); spl_parse_expr(ctx, PREC_MIN); - if (elem_type && !spl_type_is_scalar(elem_type)) { - spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem_type), 1); + if (elem_type_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_type_idx)) { + emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, elem_type_idx), 1); } else { - spl_emit(ctx, SPL_STORE, st, 0); + emit_store_type(&ctx->emit, st); } skip_nl(ctx); } @@ -468,44 +413,39 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int b advance(ctx); skip_nl(ctx); expect(ctx, TOK_R_BRACE); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); - } else if (f->type && (f->type->kind == TYPE_STRUCT || f->type->kind == TYPE_ENUM) && - spl_type_size(f->type) > sizeof(spl_val_t)) { + emit_drop(&ctx->emit); + } else if (ft_idx >= 0 && + (spl_type_kind(&ctx->tctx, ft_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, ft_idx) == TYPE_ENUM) && + spl_type_size(&ctx->tctx, ft_idx) > sizeof(spl_val_t)) { spl_expr_result_t fv; if (peek(ctx)->type == TOK_L_BRACE) { - fv = spl_parse_struct_literal(ctx, f->type); + fv = spl_parse_struct_literal(ctx, ft_idx); } else { fv = spl_parse_expr(ctx, PREC_MIN); } (void)fv; - usize nslots = (spl_type_size(f->type) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); - spl_emit_copy_slots(ctx, base_offset + (int)extra_offset + f->offset, nslots); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + usize nslots = (spl_type_size(&ctx->tctx, ft_idx) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); + emit_frame_copy(&ctx->emit, base_offset + (int)extra_offset + (int)it->aggregate_field.offset, nslots); + emit_drop(&ctx->emit); } else { spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN); (void)fv; - spl_emit(ctx, SPL_STORE, spl_type_emit_type(f->type), 0); + emit_store_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, ft_idx)); } break; } } } -spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) { - usize sz = spl_type_size(type); - int base_offset = ctx->current_local_bytes; - ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1)); - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; - if (ctx->current_local_bytes - base_offset < (int)sizeof(spl_val_t)) - ctx->current_local_bytes = base_offset + (int)sizeof(spl_val_t); - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; +spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) { + usize sz = spl_type_size(&ctx->tctx, type_idx); + int base_offset = fa_alloc(&ctx->emit.frame, sz); - advance(ctx); /* { */ + advance(ctx); skip_nl(ctx); - if (type->kind == TYPE_STRUCT) { + if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_STRUCT) { while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { if (peek(ctx)->type == TOK_COMMA) { advance(ctx); @@ -518,10 +458,10 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ skip_nl(ctx); continue; } - parse_one_field_init(ctx, &type->fields, base_offset, 0); + parse_one_field_init(ctx, spl_type_items(&ctx->tctx, type_idx), base_offset, 0); skip_nl(ctx); } - } else if (type->kind == TYPE_SLICE) { + } else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_SLICE) { while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { if (peek(ctx)->type == TOK_COMMA) { advance(ctx); @@ -538,22 +478,18 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ advance(ctx); skip_nl(ctx); if (strcmp(fname, "ptr") == 0) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); + emit_laddr(&ctx->emit, base_offset); spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + emit_store_ptr(&ctx->emit); } else if (strcmp(fname, "len") == 0) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + emit_laddr(&ctx->emit, base_offset); + emit_ptr_add(&ctx->emit, sizeof(spl_val_t)); spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); + emit_store_usize(&ctx->emit); } skip_nl(ctx); } - } else if (type->kind == TYPE_ENUM) { - /* Parse variant name with optional type qualifier(s): - * .name, TypeName.name, or bare name (same pattern as - * spl_emit_match_enum_cmp for consistency) */ + } else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM) { spl_tok_t *vtok; if (peek(ctx)->type == TOK_DOT) { advance(ctx); @@ -573,18 +509,22 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ skip_nl(ctx); int found = 0; - vec_for(type->variants, vi) { - spl_enum_variant_t *v = &vec_at(type->variants, vi); + spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, type_idx); + vec_for(*items, vi) { + spl_type_item_t *v = &vec_at(*items, vi); + if (v->item_kind != ITEM_VARIANT) continue; if (strcmp(v->name, vname) == 0) { found = 1; - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - spl_emit(ctx, SPL_PUSH, SPL_I32, v->value); - spl_emit(ctx, SPL_STORE, SPL_I32, 0); + emit_laddr(&ctx->emit, base_offset); + emit_push_i32(&ctx->emit, v->enum_field.value); + emit_store_i32(&ctx->emit); - const usize DATA_OFFSET = 4; - if (v->data_type) { - if (v->data_type->kind == TYPE_STRUCT && peek(ctx)->type == TOK_L_BRACE) { - advance(ctx); /* { */ + const usize DATA_OFFSET = ENUM_TAG_SIZE; + int dt_idx = v->enum_field.type_idx; + if (dt_idx >= 0) { + if (spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT && + peek(ctx)->type == TOK_L_BRACE) { + advance(ctx); skip_nl(ctx); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { if (peek(ctx)->type == TOK_COMMA) { @@ -594,35 +534,34 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ } if (peek(ctx)->type != TOK_DOT) break; - parse_one_field_init(ctx, &v->data_type->fields, base_offset, - DATA_OFFSET); + parse_one_field_init(ctx, spl_type_items(&ctx->tctx, dt_idx), + base_offset, DATA_OFFSET); skip_nl(ctx); } expect(ctx, TOK_R_BRACE); - } else if (v->data_type && - (v->data_type->kind == TYPE_STRUCT || - v->data_type->kind == TYPE_ENUM) && - spl_type_size(v->data_type) > sizeof(spl_val_t)) { + } else if (dt_idx >= 0 && + (spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, dt_idx) == TYPE_ENUM) && + spl_type_size(&ctx->tctx, dt_idx) > sizeof(spl_val_t)) { spl_expr_result_t dv; if (peek(ctx)->type == TOK_L_BRACE) { - dv = spl_parse_struct_literal(ctx, v->data_type); + dv = spl_parse_struct_literal(ctx, dt_idx); } else { dv = spl_parse_expr(ctx, PREC_MIN); } (void)dv; - usize nslots = (spl_type_size(v->data_type) + sizeof(spl_val_t) - 1) / + usize nslots = (spl_type_size(&ctx->tctx, dt_idx) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); - spl_emit_copy_slots(ctx, base_offset + (int)DATA_OFFSET, nslots); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_frame_copy(&ctx->emit, base_offset + (int)DATA_OFFSET, nslots); + emit_drop(&ctx->emit); } else { spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN); (void)dv; - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_type_t bt = spl_type_emit_type(v->data_type); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); + emit_laddr(&ctx->emit, base_offset); + emit_ptr_add(&ctx->emit, DATA_OFFSET); + spl_type_t bt = spl_type_emit_type(&ctx->tctx, dt_idx); + emit_swap(&ctx->emit); + emit_store_type(&ctx->emit, bt); } } break; @@ -636,12 +575,12 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ expect(ctx, TOK_R_BRACE); if (sz <= sizeof(spl_val_t)) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - spl_emit(ctx, SPL_LOAD, spl_type_emit_type(type), 0); - return (spl_expr_result_t){type, 0}; + emit_laddr(&ctx->emit, base_offset); + emit_load_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, type_idx)); + return (spl_expr_result_t){type_idx, 0}; } else { - spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - return (spl_expr_result_t){type, 1}; + emit_laddr(&ctx->emit, base_offset); + return (spl_expr_result_t){type_idx, 1}; } } @@ -661,52 +600,50 @@ static int parse_call_args(spl_comp_t *ctx) { return nargs; } -/* Type check: warn when a value is passed where a pointer of the same type is expected. - * This catches bugs like passing `tok` (Token) where `*Token` is expected. */ -static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, spl_type_info_t *arg_type, - spl_type_info_t *param_type, int arg_idx) { - if (!arg_type || !param_type) +static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, int arg_type_idx, + int param_type_idx, int arg_idx) { + if (arg_type_idx < 0 || param_type_idx < 0) return; - if (param_type->kind == TYPE_PTR && param_type->elem && arg_type->kind != TYPE_PTR && - arg_type->name && param_type->elem->name && - strcmp(arg_type->name, param_type->elem->name) == 0) { + if (spl_type_kind(&ctx->tctx, param_type_idx) == TYPE_PTR && + spl_type_elem_type(&ctx->tctx, param_type_idx) >= 0 && + spl_type_kind(&ctx->tctx, arg_type_idx) != TYPE_PTR && + spl_type_name(&ctx->tctx, arg_type_idx) && + spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)) && + strcmp(spl_type_name(&ctx->tctx, arg_type_idx), + spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx))) == 0) { fprintf(stderr, "%s: warning: argument %d of '%s' expects '%s*', " "got '%s' (missing '&'?)\n", - ctx->fname, arg_idx + 1, fname, param_type->elem->name, arg_type->name); + ctx->fname, arg_idx + 1, fname, + spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)), + spl_type_name(&ctx->tctx, arg_type_idx)); } } -/* Wrapper: parse_call_args with type checking against parameter types */ static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, - spl_type_info_t **param_types, int nparams) { - int nargs = 0; /* physical slot count (for CALL) */ - int nlogical = 0; /* logical arg index (for param_types lookup) */ + int *param_type_indices, int nparams) { + int nargs = 0; + int nlogical = 0; if (peek(ctx)->type != TOK_R_PAREN) { for (;;) { spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN); - if (param_types && nlogical < nparams && param_types[nlogical]) - spl_check_arg_type(ctx, fname, arg.type, param_types[nlogical], nlogical); + if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0) + spl_check_arg_type(ctx, fname, arg.type_idx, param_type_indices[nlogical], nlogical); - /* For multi-slot struct/enum by-value parameters, expand - * the address left on the stack into multiple data slots. */ int arg_slots = 1; - if (param_types && nlogical < nparams && param_types[nlogical] && arg.type && - param_types[nlogical]->kind != TYPE_PTR && - !spl_type_is_scalar(param_types[nlogical]) && - spl_type_size(param_types[nlogical]) > sizeof(spl_val_t)) { - usize nslots = (spl_type_size(param_types[nlogical]) + sizeof(spl_val_t) - 1) / - sizeof(spl_val_t); + if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0 && + arg.type_idx >= 0 && + spl_type_kind(&ctx->tctx, param_type_indices[nlogical]) != TYPE_PTR && + spl_type_needs_multi_slot(&ctx->tctx, param_type_indices[nlogical])) { + usize nslots = (spl_type_size(&ctx->tctx, param_type_indices[nlogical]) + + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); for (usize i = 0; i < nslots; i++) { - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); - if (i > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + emit_dup(&ctx->emit); + emit_ptr_add(&ctx->emit, i * sizeof(spl_val_t)); + emit_load_ptr(&ctx->emit); + emit_swap(&ctx->emit); } - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_drop(&ctx->emit); arg_slots = (int)nslots; } @@ -727,105 +664,90 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { char name[256]; spl_tok_copy_name(t, name, sizeof(name)); - /* 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) { spl_comp_error(ctx, "unknown function '%s'", name); - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } spl_func_info_t *f = &vec_at(ctx->funcs, fi); - advance(ctx); /* skip ( */ + advance(ctx); - int nargs = parse_call_args_checked(ctx, f->name, f->param_types, f->nparams); + int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams); expect(ctx, TOK_R_PAREN); if (f->is_extern) { int nidx = spl_ensure_native(ctx, f->name); - spl_emit(ctx, SPL_PUSH, SPL_I32, nidx); - spl_emit(ctx, SPL_NCALL, SPL_VOID, nargs); + emit_push_i32(&ctx->emit, nidx); + emit_ncall(&ctx->emit, nargs); } else { - /* 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); + emit_call_with_fixup(&ctx->emit, nargs, f->func_idx); } - spl_expr_result_t r = {f->ret_type, 0}; + spl_expr_result_t r = {f->ret_type_idx, 0}; - /* Multi-slot struct return: copy from returned address to caller's - * temp space. The returned address points to fp[1] in callee's frame - * (fp[0] is a gap). After RET, sp = callee_fp, so the first PUSH in - * emit_copy_slots (DUP) writes to the gap slot, not data. */ - if (f->ret_type && !spl_type_is_scalar(f->ret_type) && - spl_type_size(f->ret_type) > sizeof(spl_val_t)) { - usize nslots = spl_type_slot_count(f->ret_type); - int temp_offset = ctx->current_local_bytes; - ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t)); - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; - spl_emit_copy_slots(ctx, temp_offset, nslots); - spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset); + if (spl_type_needs_multi_slot(&ctx->tctx, f->ret_type_idx)) { + usize nslots = spl_type_slot_count(&ctx->tctx, f->ret_type_idx); + int temp_offset = fa_alloc_temp(&ctx->emit.frame, nslots); + emit_frame_copy(&ctx->emit, temp_offset, nslots); + emit_laddr(&ctx->emit, temp_offset); r.is_lvalue = 1; } return r; } - /* Variable reference */ spl_var_info_t *v = spl_lookup_var(ctx, name); if (v) { spl_val_t cv = 0; if (map_get(ctx->const_values, name, &cv)) { - spl_emit(ctx, SPL_PUSH, spl_type_emit_type(v->type), cv); - spl_expr_result_t r = {v->type, 0}; + emit_push_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, v->type_idx), cv); + spl_expr_result_t r = {v->type_idx, 0}; return r; } - spl_type_info_t *vt = v->type; - spl_emit(ctx, SPL_LADDR, SPL_PTR, v->offset); + int vt_idx = v->type_idx; + emit_laddr(&ctx->emit, v->offset); spl_expr_result_t r; - emit_load_or_addr_type(ctx, &r, vt); + emit_load_or_addr_type(ctx, &r, vt_idx); return r; } - /* Check if it's a type name (for enum variant access like Color.Red) */ - spl_type_info_t *ttype = spl_resolve_type(ctx, name); - if (ttype) { + int ttype_idx = spl_type_resolve(&ctx->tctx, name); + if (ttype_idx >= 0) { if (peek(ctx)->type == TOK_L_BRACE && - (ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) { - return spl_parse_struct_literal(ctx, ttype); + (spl_type_kind(&ctx->tctx, ttype_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, ttype_idx) == TYPE_ENUM)) { + return spl_parse_struct_literal(ctx, ttype_idx); } - spl_expr_result_t r = {ttype, 0}; + spl_expr_result_t r = {ttype_idx, 0}; return r; } spl_comp_error(ctx, "undefined variable '%s'", name); - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } static spl_expr_result_t parse_group(spl_comp_t *ctx) { - advance(ctx); /* ( */ + advance(ctx); spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN); expect(ctx, TOK_R_PAREN); return r; } static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, - spl_type_info_t *type) { - if (spl_type_is_scalar(type)) { + int type_idx) { + if (spl_type_is_scalar(&ctx->tctx, type_idx)) { if (!ctx->addr_of_mode) { - spl_emit(ctx, SPL_LOAD, spl_type_emit_type(type), 0); - *result = (spl_expr_result_t){type, 0}; + emit_load_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, type_idx)); + *result = (spl_expr_result_t){type_idx, 0}; return; } } - *result = (spl_expr_result_t){type, 1}; + *result = (spl_expr_result_t){type_idx, 1}; } static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { @@ -839,14 +761,14 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { switch (op->type) { case TOK_SUB: - spl_emit(ctx, SPL_NEG, spl_type_emit_type(right.type), 0); + emit_binop(&ctx->emit, SPL_NEG, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_NOT: - spl_emit(ctx, SPL_PUSH, SPL_I32, 0); - spl_emit(ctx, SPL_EQ, spl_type_emit_type(right.type), 0); + emit_push_i32(&ctx->emit, 0); + emit_binop(&ctx->emit, SPL_EQ, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_BIT_NOT: - spl_emit(ctx, SPL_NOT, spl_type_emit_type(right.type), 0); + emit_binop(&ctx->emit, SPL_NOT, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_AND: if (!right.is_lvalue) { @@ -854,11 +776,13 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { } break; case TOK_MUL: - if (right.type && right.type->kind == TYPE_PTR && right.type->elem) { + if (right.type_idx >= 0 && + spl_type_kind(&ctx->tctx, right.type_idx) == TYPE_PTR && + spl_type_elem_type(&ctx->tctx, right.type_idx) >= 0) { if (right.is_lvalue) { - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + emit_load_ptr(&ctx->emit); } - emit_load_or_addr_type(ctx, &right, right.type->elem); + emit_load_or_addr_type(ctx, &right, spl_type_elem_type(&ctx->tctx, right.type_idx)); } break; default: @@ -868,14 +792,10 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { return right; } -/* ============================================================ - * Primary expression (prefix part of Pratt parser) - * ============================================================ */ - static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { spl_tok_t *tok = peek(ctx); if (!tok) { - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } @@ -890,16 +810,16 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { return parse_string_literal(ctx); case KW_TRUE: advance(ctx); - spl_emit(ctx, SPL_PUSH, SPL_I32, 1); - return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + emit_push_i32(&ctx->emit, 1); + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; case KW_FALSE: advance(ctx); - spl_emit(ctx, SPL_PUSH, SPL_I32, 0); - return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + emit_push_i32(&ctx->emit, 0); + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; case KW_NULL: advance(ctx); - spl_emit(ctx, SPL_PUSH, SPL_PTR, 0); - return (spl_expr_result_t){spl_type_basic(SPL_PTR), 0}; + emit_push_ptr(&ctx->emit, 0); + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_PTR), 0}; case TOK_IDENT: case KW_BOOL: case KW_VOID: @@ -923,7 +843,7 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { tok = peek(ctx); if (!tok || tok->type != TOK_IDENT) { spl_comp_error(ctx, "expected builtin name after '@'"); - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } char bname[256]; @@ -936,18 +856,18 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { int nargs = parse_call_args(ctx); expect(ctx, TOK_R_PAREN); for (int i = 0; i < nargs; i++) { - spl_emit(ctx, SPL_DBG, SPL_USIZE, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_dbg_usize(&ctx->emit); + emit_drop(&ctx->emit); } if (nargs == 0) - spl_emit(ctx, SPL_DBG, SPL_VOID, 0); + emit_dbg_void(&ctx->emit); } else { - spl_emit(ctx, SPL_DBG, SPL_VOID, 0); + emit_dbg_void(&ctx->emit); } - return (spl_expr_result_t){spl_type_basic(SPL_VOID), 0}; + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0}; } else { spl_comp_error(ctx, "unknown builtin '@%s'", bname); - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } } @@ -955,65 +875,63 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) { return parse_ident(ctx); } - spl_expr_result_t r = {0}; + spl_expr_result_t r = {-1, 0}; return r; } } -/* ============================================================ - * Postfix expression parser (.field, [index], [begin..end]) - * ============================================================ */ - static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left) { - if (!left.type) + if (left.type_idx < 0) return left; for (;;) { skip_nl(ctx); spl_tok_type_t opt = peek(ctx)->type; - /* Postfix . operator */ if (opt == TOK_DOT) { advance(ctx); spl_tok_t *field = advance(ctx); char fname[256]; spl_tok_copy_name(field, fname, sizeof(fname)); - /* Compile-time type member resolution (enum variants, nested types) */ - if (left.type) { - spl_expr_result_t mresult = {0}; - if (spl_resolve_type_member(ctx, left.type, fname, &mresult)) { + if (left.type_idx >= 0) { + spl_expr_result_t mresult = {-1, 0}; + if (spl_resolve_type_member(ctx, left.type_idx, fname, &mresult)) { left = mresult; continue; } } - /* Method call on type/instance: left.field(args) */ - if (left.type && peek(ctx)->type == TOK_L_PAREN) { - spl_type_info_t *methods_type = left.type; + if (left.type_idx >= 0 && peek(ctx)->type == TOK_L_PAREN) { + int methods_type_idx = left.type_idx; int is_ptr_self = 0; - if (methods_type->kind == TYPE_PTR && methods_type->elem && - (methods_type->elem->kind == TYPE_STRUCT || - methods_type->elem->kind == TYPE_ENUM)) { - is_ptr_self = 1; - methods_type = methods_type->elem; + if (spl_type_kind(&ctx->tctx, methods_type_idx) == TYPE_PTR) { + int elem_idx = spl_type_elem_type(&ctx->tctx, methods_type_idx); + if (elem_idx >= 0 && + (spl_type_kind(&ctx->tctx, elem_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, elem_idx) == TYPE_ENUM)) { + is_ptr_self = 1; + methods_type_idx = elem_idx; + } } int found_method = 0; - vec_for(methods_type->methods, mi) { - if (strcmp(vec_at(methods_type->methods, mi).name, fname) == 0) { - spl_method_info_t *method = &vec_at(methods_type->methods, mi); - spl_func_info_t *func = &vec_at(ctx->funcs, method->func_idx); + spl_type_item_vec_t *m_items = spl_type_items(&ctx->tctx, methods_type_idx); + vec_for(*m_items, mi) { + spl_type_item_t *mit = &vec_at(*m_items, mi); + if (mit->item_kind != ITEM_METHOD) continue; + if (strcmp(mit->name, fname) == 0) { + spl_func_info_t *func = &vec_at(ctx->funcs, mit->method.func_idx); - advance(ctx); /* ( */ + advance(ctx); found_method = 1; int nargs = 0; int is_instance = 0; - if (func->nparams > 0 && func->param_types[0] && - func->param_types[0]->kind == TYPE_PTR && - func->param_types[0]->elem == methods_type) { + if (func->nparams > 0 && func->param_type_indices[0] >= 0 && + spl_type_kind(&ctx->tctx, func->param_type_indices[0]) == TYPE_PTR && + spl_type_elem_type(&ctx->tctx, func->param_type_indices[0]) == methods_type_idx) { is_instance = 1; } @@ -1022,14 +940,12 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l spl_comp_error(ctx, "cannot call instance method '%s' on type", fname); } - /* Drop implicit self from left operand — user passes - * self explicitly as first argument */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_drop(&ctx->emit); nargs = 0; } { - spl_type_info_t **cp = func->param_types; + int *cp = func->param_type_indices; int cn = func->nparams; if (is_instance && cn > 0) { cp++; @@ -1039,25 +955,15 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l } expect(ctx, TOK_R_PAREN); - /* 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); + emit_call_with_fixup(&ctx->emit, nargs, func->func_idx); - left = (spl_expr_result_t){func->ret_type, 0}; + left = (spl_expr_result_t){func->ret_type_idx, 0}; - /* Multi-slot return: copy to temp, keep address */ - if (func->ret_type && !spl_type_is_scalar(func->ret_type) && - spl_type_size(func->ret_type) > sizeof(spl_val_t)) { - usize nslots = spl_type_slot_count(func->ret_type); - int temp_offset = ctx->current_local_bytes; - ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t)); - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; - spl_emit_copy_slots(ctx, temp_offset, nslots); - spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset); + if (spl_type_needs_multi_slot(&ctx->tctx, func->ret_type_idx)) { + usize nslots = spl_type_slot_count(&ctx->tctx, func->ret_type_idx); + int temp_offset = fa_alloc_temp(&ctx->emit.frame, nslots); + emit_frame_copy(&ctx->emit, temp_offset, nslots); + emit_laddr(&ctx->emit, temp_offset); left.is_lvalue = 1; } @@ -1068,78 +974,77 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l continue; } - /* Struct field access */ - if (left.type && left.type->kind == TYPE_STRUCT) { - vec_for(left.type->fields, fi) { - if (strcmp(vec_at(left.type->fields, fi).name, fname) == 0) { - spl_field_t *f = &vec_at(left.type->fields, fi); - if (f->offset > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - emit_load_or_addr_type(ctx, &left, f->type); + if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_STRUCT) { + spl_type_item_vec_t *f_items = spl_type_items(&ctx->tctx, left.type_idx); + vec_for(*f_items, fi) { + spl_type_item_t *fit = &vec_at(*f_items, fi); + if (fit->item_kind != ITEM_FIELD) continue; + if (strcmp(fit->name, fname) == 0) { + emit_ptr_add(&ctx->emit, fit->aggregate_field.offset); + emit_load_or_addr_type(ctx, &left, fit->aggregate_field.type_idx); break; } } continue; } - /* Pointer auto-deref: if left is a pointer to struct, deref first */ - if (left.type && left.type->kind == TYPE_PTR && left.type->elem && - left.type->elem->kind == TYPE_STRUCT) { - spl_type_info_t *st = left.type->elem; - if (left.is_lvalue) { - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - } - vec_for(st->fields, fi) { - if (strcmp(vec_at(st->fields, fi).name, fname) == 0) { - spl_field_t *f = &vec_at(st->fields, fi); - if (f->offset > 0) { - spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - } - emit_load_or_addr_type(ctx, &left, f->type); - break; + if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR) { + int elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); + if (elem_idx >= 0 && spl_type_kind(&ctx->tctx, elem_idx) == TYPE_STRUCT) { + if (left.is_lvalue) { + emit_load_ptr(&ctx->emit); } + spl_type_item_vec_t *st_items = spl_type_items(&ctx->tctx, elem_idx); + vec_for(*st_items, si) { + spl_type_item_t *sit = &vec_at(*st_items, si); + if (sit->item_kind != ITEM_FIELD) continue; + if (strcmp(sit->name, fname) == 0) { + emit_ptr_add(&ctx->emit, sit->aggregate_field.offset); + emit_load_or_addr_type(ctx, &left, sit->aggregate_field.type_idx); + break; + } + } + continue; } - continue; } - /* Slice .len or .ptr */ - if (left.type && left.type->kind == TYPE_SLICE) { + if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { if (strcmp(fname, "len") == 0) { if (ctx->addr_of_mode) { - spl_emit(ctx, SPL_PUSH, SPL_U64, (spl_val_t)sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - left = (spl_expr_result_t){spl_type_basic(SPL_USIZE), 1}; + emit_push_u64(&ctx->emit, (spl_val_t)sizeof(spl_val_t)); + emit_add_u64(&ctx->emit); + left = (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 1}; } else { - spl_emit(ctx, SPL_PUSH, SPL_U64, (spl_val_t)sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); - left = (spl_expr_result_t){spl_type_basic(SPL_USIZE), 0}; + emit_push_u64(&ctx->emit, (spl_val_t)sizeof(spl_val_t)); + emit_add_u64(&ctx->emit); + emit_load_usize(&ctx->emit); + left = (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0}; } } else if (strcmp(fname, "ptr") == 0) { + int sl_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); if (ctx->addr_of_mode) { - left = (spl_expr_result_t){left.type->elem ? spl_type_ptr(left.type->elem) - : spl_type_basic(SPL_PTR), - 1}; + left = (spl_expr_result_t){sl_elem_idx >= 0 + ? spl_type_ptr(&ctx->tctx, sl_elem_idx) + : spl_type_basic(&ctx->tctx, SPL_PTR), 1}; } else { - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - left = (spl_expr_result_t){left.type->elem ? spl_type_ptr(left.type->elem) - : spl_type_basic(SPL_PTR), - 0}; + emit_load_ptr(&ctx->emit); + left = (spl_expr_result_t){sl_elem_idx >= 0 + ? spl_type_ptr(&ctx->tctx, sl_elem_idx) + : spl_type_basic(&ctx->tctx, SPL_PTR), 0}; } } continue; } - /* Postfix dereference: expr.* */ - if (strcmp(fname, "*") == 0 && left.type && left.type->kind == TYPE_PTR && - left.type->elem) { - if (left.is_lvalue) { - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + if (strcmp(fname, "*") == 0 && left.type_idx >= 0 && + spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR) { + int elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); + if (elem_idx >= 0) { + if (left.is_lvalue) { + emit_load_ptr(&ctx->emit); + } + emit_load_or_addr_type(ctx, &left, elem_idx); } - emit_load_or_addr_type(ctx, &left, left.type->elem); continue; } @@ -1147,9 +1052,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l continue; } - /* Array/slice indexing: expr[expr] or expr[begin..end] */ if (opt == TOK_L_BRACKET) { - advance(ctx); /* skip [ */ + advance(ctx); if (peek(ctx)->type == TOK_R_BRACKET) { advance(ctx); continue; @@ -1159,72 +1063,80 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l if (peek(ctx)->type == TOK_RANGE) { advance(ctx); - spl_expr_result_t end_expr = {0}; + spl_expr_result_t end_expr = {-1, 0}; int has_explicit_end = (peek(ctx)->type != TOK_R_BRACKET); if (has_explicit_end) { end_expr = spl_parse_expr(ctx, PREC_MIN); } + (void)end_expr; expect(ctx, TOK_R_BRACKET); if (!has_explicit_end) { - if (left.type && left.type->kind == TYPE_ARRAY) { - spl_emit(ctx, SPL_PUSH, SPL_U64, left.type->array_len); - } else if (left.type && left.type->kind == TYPE_SLICE) { - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); - spl_emit(ctx, SPL_PUSH, SPL_U64, sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); + if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY) { + emit_push_u64(&ctx->emit, spl_type_array_len(&ctx->tctx, left.type_idx)); + } else if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { + emit_pick(&ctx->emit, 1); + emit_push_u64(&ctx->emit, sizeof(spl_val_t)); + emit_add_u64(&ctx->emit); + emit_load_usize(&ctx->emit); } } - if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_SLICE)) { - usize stride = spl_type_elem_stride(left.type->elem); + if (left.type_idx >= 0 && + (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY || + spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE)) { + int rng_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); + usize stride = spl_type_elem_stride(&ctx->tctx, rng_elem_idx); - if (left.type->kind == TYPE_SLICE) { - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); - spl_emit(ctx, SPL_PUSH, SPL_U64, stride); - spl_emit(ctx, SPL_MUL, SPL_U64, 0); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); - spl_emit(ctx, SPL_PICK, SPL_VOID, 3); - spl_emit(ctx, SPL_SUB, SPL_USIZE, 0); - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { + emit_pick(&ctx->emit, 2); + emit_load_ptr(&ctx->emit); + emit_pick(&ctx->emit, 2); + emit_push_u64(&ctx->emit, stride); + emit_mul_u64(&ctx->emit); + emit_add_u64(&ctx->emit); + emit_pick(&ctx->emit, 1); + emit_pick(&ctx->emit, 3); + emit_sub_usize(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); + emit_rot(&ctx->emit); + emit_drop(&ctx->emit); } else { emit_slice_create(ctx, stride); } } - left = (spl_expr_result_t){left.type ? spl_type_slice(left.type->elem) : NULL, 0}; + left = (spl_expr_result_t){left.type_idx >= 0 + ? spl_type_slice(&ctx->tctx, spl_type_elem_type(&ctx->tctx, left.type_idx)) + : -1, 0}; continue; } expect(ctx, TOK_R_BRACKET); - if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_PTR || - left.type->kind == TYPE_SLICE)) { - spl_type_info_t *elem = left.type->elem; + if (left.type_idx >= 0 && + (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY || + spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR || + spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE)) { + int idx_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); - if (left.type->kind == TYPE_SLICE) { - emit_slice_index(ctx, elem); + if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { + emit_slice_index(ctx, idx_elem_idx); } else { - if (left.type->kind == TYPE_PTR && left.is_lvalue) { - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR && left.is_lvalue) { + emit_swap(&ctx->emit); + emit_load_ptr(&ctx->emit); + emit_swap(&ctx->emit); } - usize stride = spl_type_elem_stride(elem); - spl_emit(ctx, SPL_PUSH, SPL_U64, stride); - spl_emit(ctx, SPL_MUL, SPL_U64, 0); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); + usize stride = spl_type_elem_stride(&ctx->tctx, idx_elem_idx); + emit_push_u64(&ctx->emit, stride); + emit_mul_u64(&ctx->emit); + emit_add_u64(&ctx->emit); } - if (elem) - emit_load_or_addr_type(ctx, &left, elem); + if (idx_elem_idx >= 0) + emit_load_or_addr_type(ctx, &left, idx_elem_idx); } continue; } @@ -1234,24 +1146,18 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l return left; } -/* ============================================================ - * Main expression parser (top-level) - * ============================================================ */ - spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { skip_nl(ctx); spl_expr_result_t left = parse_primary_expr(ctx); - if (!left.type) + if (left.type_idx < 0) return left; - /* Infix parsing (precedence climbing) */ while (1) { left = parse_postfix_expr(ctx, left); skip_nl(ctx); spl_tok_type_t opt = peek(ctx)->type; - /* Binary operators */ int prec = tok_prec(opt); if (prec == 0 || prec < min_prec) break; @@ -1262,29 +1168,23 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { return left; } -/* ============================================================ - * Infix operators - * ============================================================ */ - static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, spl_tok_type_t op) { int prec = tok_prec(op); int next_prec = prec + 1; - /* Short-circuit logical operators */ if (op == TOK_AND_AND) { - spl_val_t bz_addr = spl_emit_bz(ctx); + spl_val_t bz_addr = emit_bz_here(&ctx->emit); spl_parse_expr(ctx, next_prec); - spl_patch_to_here(ctx, bz_addr); - return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + emit_patch_here(&ctx->emit, bz_addr); + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; } if (op == TOK_OR_OR) { - spl_val_t bnz_addr = spl_emit_bnz(ctx); + spl_val_t bnz_addr = emit_bnz_here(&ctx->emit); spl_parse_expr(ctx, next_prec); - spl_patch_to_here(ctx, bnz_addr); - return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; + emit_patch_here(&ctx->emit, bnz_addr); + return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; } - /* Assignment operators */ if (op == TOK_ASSIGN || op == TOK_ASSIGN_ADD || op == TOK_ASSIGN_SUB || op == TOK_ASSIGN_MUL || op == TOK_ASSIGN_DIV || op == TOK_ASSIGN_MOD || op == TOK_ASSIGN_AND || op == TOK_ASSIGN_OR || op == TOK_ASSIGN_XOR || op == TOK_ASSIGN_L_SH || @@ -1296,83 +1196,61 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp ctx->addr_of_mode = saved_addr_of_mode; if (left.is_lvalue) { - spl_type_t bt = spl_type_emit_type(left.type); - if (op == TOK_ASSIGN && left.type && !spl_type_is_scalar(left.type) && + spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx); + if (op == TOK_ASSIGN && left.type_idx >= 0 && + !spl_type_is_scalar(&ctx->tctx, left.type_idx) && right.is_lvalue) { - spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(left.type), 0); + emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, left.type_idx), 0); } else if (op == TOK_ASSIGN) { - spl_emit(ctx, SPL_STORE, bt, 0); + emit_store_type(&ctx->emit, bt); } else { - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + emit_pick(&ctx->emit, 1); + emit_load_type(&ctx->emit, bt); + emit_swap(&ctx->emit); int sop = binop_to_sir(assign_to_binop(op), bt); if (sop >= 0) - spl_emit(ctx, sop, bt, 0); - spl_emit(ctx, SPL_STORE, bt, 0); + emit_binop(&ctx->emit, sop, bt); + emit_store_type(&ctx->emit, bt); } } return right; } - /* Regular binary op */ spl_parse_expr(ctx, next_prec); - /* Forbid comparison of non-scalar types (struct, enum-with-data) */ - if ((op == TOK_EQ || op == TOK_NEQ) && left.type && !spl_type_is_scalar(left.type)) { - spl_comp_error(ctx, "type '%s' does not support comparison", spl_type_str(left.type)); + if ((op == TOK_EQ || op == TOK_NEQ) && left.type_idx >= 0 && + !spl_type_is_scalar(&ctx->tctx, left.type_idx)) { + spl_comp_error(ctx, "type '%s' does not support comparison", + spl_type_str(&ctx->tctx, left.type_idx)); } - spl_type_t bt = spl_type_emit_type(left.type); + spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx); int sop = binop_to_sir(op, bt); if (sop >= 0) { - spl_emit(ctx, sop, bt, 0); + emit_binop(&ctx->emit, sop, bt); } - /* Comparisons return boolean (I32), other binary ops match operand type */ int is_compare = (op == TOK_EQ || op == TOK_NEQ || op == TOK_LT || op == TOK_GT || op == TOK_LE || op == TOK_GE); - spl_type_info_t *result_type = is_compare ? spl_type_basic(SPL_I32) : left.type; - return (spl_expr_result_t){result_type, 0}; + int result_type_idx = is_compare ? spl_type_basic(&ctx->tctx, SPL_I32) : left.type_idx; + return (spl_expr_result_t){result_type_idx, 0}; } -/* ============================================================ - * Match arm pattern comparison - * - * These functions handle the expression-level comparison between - * a saved match value and an arm pattern. They belong here in - * the expr layer so match arms compose as "enhanced if-conditions" - * rather than requiring stmt.c to emit raw comparison instructions. - * ============================================================ */ - -spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset, int by_value) { +int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, + int val_offset, int by_value) { char vname[256]; - /* 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); /* . */ + 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); /* . */ + int qt_idx = spl_type_resolve(&ctx->tctx, vname); + (void)qt_idx; + advance(ctx); tok = advance(ctx); spl_tok_copy_name(tok, vname, sizeof(vname)); } - /* Now vname holds the final segment — the variant name */ goto lookup; } @@ -1382,96 +1260,79 @@ spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *en } lookup: - vec_for(enum_type->variants, vi) { - spl_enum_variant_t *v = &vec_at(enum_type->variants, vi); - if (strcmp(v->name, vname) == 0) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - if (by_value) { - /* Scalar enum: raw tag stored directly in slot */ - spl_emit(ctx, SPL_LOAD, SPL_I32, 0); - } else { - /* Enum with data or *Enum: slot has a pointer, dereference it */ - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_emit(ctx, SPL_LOAD, SPL_I32, 0); + { + spl_type_item_vec_t *e_items = spl_type_items(&ctx->tctx, enum_type_idx); + vec_for(*e_items, vi) { + spl_type_item_t *vit = &vec_at(*e_items, vi); + if (vit->item_kind != ITEM_VARIANT) continue; + if (strcmp(vit->name, vname) == 0) { + emit_laddr(&ctx->emit, val_offset); + if (by_value) { + emit_load_type(&ctx->emit, SPL_I32); + } else { + emit_load_ptr(&ctx->emit); + emit_ptr_add(&ctx->emit, 0); + emit_load_type(&ctx->emit, SPL_I32); + } + emit_push_i32(&ctx->emit, vit->enum_field.value); + emit_binop(&ctx->emit, SPL_EQ, SPL_I32); + return (int)vi; } - spl_emit(ctx, SPL_PUSH, SPL_I32, v->value); - spl_emit(ctx, SPL_EQ, SPL_I32, 0); - return v; } } spl_comp_error(ctx, "unknown variant '%s' in match", vname); - return NULL; + return -1; } void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + emit_laddr(&ctx->emit, val_offset); + emit_load_ptr(&ctx->emit); spl_parse_expr(ctx, PREC_LOGOR); - spl_emit(ctx, SPL_EQ, SPL_I32, 0); + emit_binop(&ctx->emit, SPL_EQ, SPL_I32); } -/* ============================================================ - * Return instruction — emit SPL_RET with correct type - * ============================================================ */ - -void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type) { - if (ret_type && !spl_type_is_scalar(ret_type) && spl_type_size(ret_type) > sizeof(spl_val_t)) { - /* Multi-slot struct return: copy to fp[0..nslots-1] (above saved_sp), - * then return address of fp[0]. Caller copies from that address to its - * own temp space (see parse_ident / method call handling). */ - usize nslots = spl_type_slot_count(ret_type); - /* Store at fp[1..nslots] (skip fp[0] as gap) so that RET's PUSH - * and caller's LADDR both write to the gap slot, not the data. */ - spl_emit_copy_slots(ctx, (int)sizeof(spl_val_t), nslots); - spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t)); - spl_emit(ctx, SPL_RET, SPL_PTR, 0); - } else { - spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID; - spl_emit(ctx, SPL_RET, rt, 0); +void spl_emit_ret(spl_comp_t *ctx, int ret_type_idx) { + if (spl_type_needs_multi_slot(&ctx->tctx, ret_type_idx)) { + /* Stack: [src_addr] — copy from src into fp+sizeof(spl_val_t) return area */ + emit_laddr(&ctx->emit, (int)sizeof(spl_val_t)); + emit_swap(&ctx->emit); + emit_copy_addr_to_addr(&ctx->emit, + spl_type_slot_count(&ctx->tctx, ret_type_idx), 0); + /* Stack empty after copy. emit_return pushes laddr + ret. */ } + emit_return(&ctx->emit, &ctx->tctx, ret_type_idx); } -/* ============================================================ - * Variable init store — store value from stack to variable - * ============================================================ */ - -void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type) { - if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) { - usize sz = spl_type_size(var_type); +void spl_emit_store_init(spl_comp_t *ctx, int var_offset, int var_type_idx) { + if (var_type_idx >= 0 && + (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ENUM)) { + usize sz = spl_type_size(&ctx->tctx, var_type_idx); if (sz <= sizeof(spl_val_t)) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, spl_type_emit_type(var_type), 0); + emit_store_to_laddr(&ctx->emit, var_offset, spl_type_emit_type(&ctx->tctx, var_type_idx)); } else { usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); - spl_emit_copy_slots(ctx, var_offset, nslots); + emit_frame_copy(&ctx->emit, var_offset, nslots); } - } else if (var_type && var_type->kind == TYPE_ARRAY) { - spl_type_info_t *elem = var_type->elem; - spl_type_t bt = spl_type_emit_type(elem); - usize stride = spl_type_elem_stride(elem); - for (int i = (int)var_type->array_len - 1; i >= 0; i--) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride)); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - if (elem && !spl_type_is_scalar(elem)) { - spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem), 0); + } else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ARRAY) { + int elem_idx = spl_type_elem_type(&ctx->tctx, var_type_idx); + spl_type_t bt = spl_type_emit_type(&ctx->tctx, elem_idx); + usize stride = spl_type_elem_stride(&ctx->tctx, elem_idx); + int arr_len = (int)spl_type_array_len(&ctx->tctx, var_type_idx); + for (int i = arr_len - 1; i >= 0; i--) { + emit_laddr(&ctx->emit, var_offset + (int)(i * stride)); + emit_swap(&ctx->emit); + if (elem_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_idx)) { + emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, elem_idx), 0); } else { - spl_emit(ctx, SPL_STORE, bt, 0); + emit_store_type(&ctx->emit, bt); } } - } else if (var_type && var_type->kind == TYPE_SLICE) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)sizeof(spl_val_t)); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + } else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE) { + emit_store_to_laddr(&ctx->emit, var_offset + (int)sizeof(spl_val_t), SPL_USIZE); + emit_store_to_laddr(&ctx->emit, var_offset, SPL_PTR); } else { - spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); - spl_type_t bt = spl_type_emit_type(var_type); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); + spl_type_t bt = spl_type_emit_type(&ctx->tctx, var_type_idx); + emit_store_to_laddr(&ctx->emit, var_offset, bt); } } diff --git a/stage1/spl_lexer.h b/stage1/spl_lexer.h index 050f3c6..4631b5c 100644 --- a/stage1/spl_lexer.h +++ b/stage1/spl_lexer.h @@ -17,7 +17,6 @@ X(defer , KW_DEFER , SPL_V0) \ X(else , KW_ELSE , SPL_V0) \ X(enum , KW_ENUM , SPL_V0) \ - X(errdefer , KW_ERRDEFER , SPL_V0) \ X(false , KW_FALSE , SPL_V0) \ X(fn , KW_FN , SPL_V0) \ X(for , KW_FOR , SPL_V0) \ diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 5e88e36..d61d8fd 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -15,18 +15,11 @@ spl_tok_t *advance(spl_comp_t *ctx) { /* ============================================================ * Parse function definition - * fn name(params) ret-type { body } - * or fn name(params) ret-type; (forward decl, not used for stage1) * ============================================================ */ -/* Shared helper: register a function, declare params, parse body, end function. - * Used by both top-level fn decl and methods inside type bodies. */ -/* Shared helper: parse function/method parameter list: (name: type, name: type, ...) - * Returns number of params parsed. Stores names in pnames and types in ptypes - * (both must be MAX_PARAMS-sized arrays). */ enum { MAX_PARAMS = 64 }; -static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], spl_type_info_t *ptypes[]) { +static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[]) { int nparams = 0; skip_nl(ctx); if (peek(ctx)->type != TOK_R_PAREN) { @@ -35,11 +28,11 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], spl_type_info_ spl_tok_copy_name(pname, pnames[nparams], 256); skip_nl(ctx); if (peek(ctx)->type == TOK_COLON) { - advance(ctx); /* : */ + advance(ctx); skip_nl(ctx); - ptypes[nparams] = spl_parse_type(ctx); + ptypes[nparams] = spl_type_parse(&ctx->tctx, ctx); } else { - ptypes[nparams] = spl_type_basic(SPL_I32); + ptypes[nparams] = spl_type_basic(&ctx->tctx, SPL_I32); } nparams++; skip_nl(ctx); @@ -59,13 +52,12 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], spl_type_info_ return nparams; } -static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *ret_type, - int nparams, char pnames[][256], spl_type_info_t *ptypes[], int is_pub) { - int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub); +static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx, + int nparams, char pnames[][256], int ptypes[], int is_pub) { + int fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 0, is_pub); ctx->current_func_idx = fi; - ctx->current_ret_type = ret_type; - ctx->current_local_bytes = 0; - ctx->peak_local_bytes = 0; + ctx->current_ret_type_idx = ret_type_idx; + fa_init(&ctx->emit.frame); spl_push_scope(ctx); @@ -75,63 +67,50 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t * skip_nl(ctx); if (peek(ctx)->type == TOK_L_BRACE) { - advance(ctx); /* { */ + advance(ctx); skip_nl(ctx); spl_val_t alloc_addr = vec_size(ctx->prog.insns); - spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0); + emit_alloc(&ctx->emit, 0); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { spl_parse_stmt(ctx); skip_nl(ctx); } - /* Compute physical param slot count (1 slot = 8 bytes). - * Multi-slot struct params occupy ceil(size/8) slots; scalar params occupy 1 each. */ int total_phys_slots = 0; for (int i = 0; i < nparams; i++) { - usize psz = spl_type_size(ptypes[i]); + usize psz = spl_type_size(&ctx->tctx, ptypes[i]); total_phys_slots += (int)((psz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t)); } - /* Ensure ALLOC provides enough space for multi-slot return value - * pre-placed at fp[0..nslots-1] (above saved_sp). */ - if (ret_type && !spl_type_is_scalar(ret_type) && - spl_type_size(ret_type) > sizeof(spl_val_t)) { - usize min_bytes = spl_type_slot_count(ret_type) * sizeof(spl_val_t); - if ((usize)ctx->peak_local_bytes < min_bytes) - ctx->peak_local_bytes = (int)min_bytes; + if (spl_type_needs_multi_slot(&ctx->tctx, ret_type_idx)) { + usize min_bytes = + spl_type_slot_count(&ctx->tctx, ret_type_idx) * sizeof(spl_val_t); + if ((usize)ctx->emit.frame.peak_bytes < min_bytes) + ctx->emit.frame.peak_bytes = (int)min_bytes; } - spl_patch(ctx, alloc_addr, - ctx->peak_local_bytes / (int)sizeof(spl_val_t) - total_phys_slots); + emit_patch(&ctx->emit, alloc_addr, + ctx->emit.frame.peak_bytes / (int)sizeof(spl_val_t) - total_phys_slots); expect(ctx, TOK_R_BRACE); } spl_emit_defer_epilogue(ctx, ctx->scope_depth); spl_pop_scope(ctx); - /* Store param_types for call-site type checking and multi-slot expansion */ { spl_func_info_t *f = &vec_at(ctx->funcs, fi); - f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); + f->param_type_indices = calloc(nparams, sizeof(int)); f->param_names = calloc(nparams, sizeof(char *)); for (int i = 0; i < nparams; i++) { - f->param_types[i] = ptypes[i]; + f->param_type_indices[i] = ptypes[i]; f->param_names[i] = strdup(pnames[i]); } } - /* Epilogue return: for multi-slot returns, return address of fp[1] - * (fp[0] is a gap slot that absorbs the RET PUSH, data at fp[1..nslots]). */ - if (ctx->current_ret_type && !spl_type_is_scalar(ctx->current_ret_type) && - spl_type_size(ctx->current_ret_type) > sizeof(spl_val_t)) { - spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t)); - spl_emit(ctx, SPL_RET, SPL_PTR, 0); - } else { - spl_emit(ctx, SPL_RET, SPL_VOID, 0); - } + emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx); spl_prog_end_func(&ctx->prog, fi); ctx->current_func_idx = -1; - ctx->current_ret_type = NULL; + ctx->current_ret_type_idx = -1; return fi; } @@ -145,31 +124,27 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { skip_nl(ctx); expect(ctx, TOK_L_PAREN); - /* Parse parameters — collect names and types */ char pnames[MAX_PARAMS][256]; - spl_type_info_t *ptypes[MAX_PARAMS]; + int ptypes[MAX_PARAMS]; int nparams = parse_params_decl(ctx, pnames, ptypes); skip_nl(ctx); - /* Return type (default: void) */ - spl_type_info_t *ret_type = spl_type_basic(SPL_VOID); + int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) { - ret_type = spl_parse_type(ctx); - if (!ret_type) - ret_type = spl_type_basic(SPL_VOID); + ret_type_idx = spl_type_parse(&ctx->tctx, ctx); + if (ret_type_idx < 0) + ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); skip_nl(ctx); } - /* Extern function: register as native, no body */ if (is_extern) { - spl_declare_func(ctx, fn_name, ret_type, nparams, 1, is_pub); + spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, is_pub); spl_ensure_native(ctx, fn_name); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); return; } - /* Check for forward declaration (just semicolon, skip) */ if (peek(ctx)->type == TOK_SEMICOLON) { advance(ctx); return; @@ -177,66 +152,48 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { skip_nl(ctx); - /* Use shared helper for function body parsing */ - parse_fn_body(ctx, fn_name, ret_type, nparams, pnames, ptypes, is_pub); + parse_fn_body(ctx, fn_name, ret_type_idx, nparams, pnames, ptypes, is_pub); } /* ============================================================ - * Shared helper: parse a method declaration inside a type body. - * Used by both struct_body and enum_body parsers. - * fn name(params) ret-type { body } + * Parse method declaration inside a type body * ============================================================ */ -static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) { +static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) { advance(ctx); /* fn */ skip_nl(ctx); spl_tok_t *mname_tok = advance(ctx); char mname[256]; spl_tok_copy_name(mname_tok, mname, sizeof(mname)); - /* Build qualified name: TypeName.method_name */ + const char *cname = spl_type_name(&ctx->tctx, container_type_idx); char qualified[512]; - snprintf(qualified, sizeof(qualified), "%s.%s", container->name ? container->name : "anon", - mname); + snprintf(qualified, sizeof(qualified), "%s.%s", cname ? cname : "anon", mname); skip_nl(ctx); expect(ctx, TOK_L_PAREN); - /* Parse parameters using shared helper */ char pnames[MAX_PARAMS][256]; - spl_type_info_t *ptypes[MAX_PARAMS]; + int ptypes[MAX_PARAMS]; int nparams = parse_params_decl(ctx, pnames, ptypes); skip_nl(ctx); - /* Return type (default: void) */ - spl_type_info_t *ret_type = spl_type_basic(SPL_VOID); + int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) { - ret_type = spl_parse_type(ctx); - if (!ret_type) - ret_type = spl_type_basic(SPL_VOID); + ret_type_idx = spl_type_parse(&ctx->tctx, ctx); + if (ret_type_idx < 0) + ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); skip_nl(ctx); } - int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0); + int fi = parse_fn_body(ctx, qualified, ret_type_idx, nparams, pnames, ptypes, 0); - spl_type_add_method(container, mname, fi); + spl_type_add_method(&ctx->tctx, container_type_idx, mname, fi); } /* ============================================================ * Parse type container body (shared for struct, union, enum) - * - * For struct/union: fields are added for identifiers - * For enum: variants are added for identifiers - * - * Body supports: - * var name: type; — field declarations (struct/union only) - * name: type, — field/variant declarations - * name: Type, — enum variant with data - * name, — simple enum variant - * type Name = ...; — nested type declarations - * fn name(...) type { } — methods * ============================================================ */ -/* Skip past a nested type declaration in pass 2 without re-parsing */ static void skip_type_decl(spl_comp_t *ctx) { advance(ctx); /* type */ advance(ctx); /* name */ @@ -270,16 +227,15 @@ static void skip_type_decl(spl_comp_t *ctx) { advance(ctx); } -static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_enum) { +static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum) { if (peek(ctx)->type != TOK_L_BRACE) return; advance(ctx); /* { */ - /* Push type onto namespace chain for short-name resolution inside body */ - spl_ns_push(ctx, container); + int saved_current = ctx->tctx.current_type_idx; + ctx->tctx.current_type_idx = container_type_idx; - /* === Pass 1: Parse all nested type declarations first === - * This allows fields/variants to reference types defined later. */ + /* === Pass 1: Parse all nested type declarations first === */ { usize saved = ctx->tok_idx; int depth = 1; @@ -303,35 +259,37 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ } /* === 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); + { + const char *cname = spl_type_name(&ctx->tctx, container_type_idx); + if (cname) { + 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", cname, mname); + int fi = spl_declare_func(ctx, qualified, -1, 0, 0, 0); + spl_type_add_method(&ctx->tctx, container_type_idx, mname, fi); + } else { + advance(ctx); + } } + ctx->tok_idx = saved; } - ctx->tok_idx = saved; } /* === Pass 2: Parse fields/variants and methods === */ @@ -353,7 +311,6 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ } else if (tt == KW_TYPE && depth == 1) { skip_type_decl(ctx); } else if (tt == KW_VAR && depth == 1 && !is_enum) { - /* var name: type; (struct/union only) */ advance(ctx); /* var */ skip_nl(ctx); spl_tok_t *ftok = advance(ctx); @@ -361,68 +318,63 @@ static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_ if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); - spl_type_info_t *ftype = spl_parse_type(ctx); + int ftype = spl_type_parse(&ctx->tctx, ctx); char fname[256]; spl_tok_copy_name(ftok, fname, sizeof(fname)); - spl_type_add_field(container, fname, ftype); + if (ftype >= 0) + spl_type_add_field(&ctx->tctx, container_type_idx, fname, ftype); } skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); } else if (tt == TOK_IDENT && depth == 1) { if (is_enum) { - /* Variant: Name or Name: Type */ spl_tok_t *vtok = advance(ctx); skip_nl(ctx); if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); - spl_type_info_t *dtype = spl_parse_type(ctx); + int dtype = spl_type_parse(&ctx->tctx, ctx); char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); - spl_type_add_variant(container, vname, dtype); + spl_type_add_variant(&ctx->tctx, container_type_idx, vname, + dtype >= 0 ? dtype : -1); } else { char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); - spl_type_add_variant(container, vname, NULL); + spl_type_add_variant(&ctx->tctx, container_type_idx, vname, -1); } } else { - /* Old-style field: name: type, */ spl_tok_t *ftok = advance(ctx); skip_nl(ctx); if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); - spl_type_info_t *ftype = spl_parse_type(ctx); + int ftype = spl_type_parse(&ctx->tctx, ctx); char fname[256]; spl_tok_copy_name(ftok, fname, sizeof(fname)); - spl_type_add_field(container, fname, ftype); + if (ftype >= 0) + spl_type_add_field(&ctx->tctx, container_type_idx, fname, ftype); } } skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); } else if (tt == KW_FN && depth == 1) { - /* Compute layout before compiling methods so - * field offsets are correct during codegen */ - spl_type_compute_layout(container); - parse_method_decl(ctx, container); + spl_type_compute_layout(&ctx->tctx, container_type_idx); + parse_method_decl(ctx, container_type_idx); } else { advance(ctx); } } } - spl_ns_pop(ctx); - spl_type_compute_layout(container); + ctx->tctx.current_type_idx = saved_current; + spl_type_compute_layout(&ctx->tctx, container_type_idx); } /* ============================================================ * Parse type declaration - * type Name = struct { ... }; - * type Name = union { ... }; - * type Name = enum { ... }; - * type Name = ExistingType; * ============================================================ */ void parse_type_decl(spl_comp_t *ctx) { @@ -435,44 +387,34 @@ 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; + int parent_type_idx = ctx->tctx.current_type_idx; 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); + int ti = spl_type_struct(&ctx->tctx, tname); + if (parent_type_idx >= 0) + spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti); skip_nl(ctx); - parse_type_body(ctx, st, 0); + parse_type_body(ctx, ti, 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); + int ti = spl_type_union(&ctx->tctx, tname); + if (parent_type_idx >= 0) + spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti); skip_nl(ctx); - parse_type_body(ctx, ut, 0); + parse_type_body(ctx, ti, 1); } else if (peek(ctx)->type == KW_ENUM) { advance(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); + int ti = spl_type_enum(&ctx->tctx, tname); + if (parent_type_idx >= 0) + spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti); skip_nl(ctx); - parse_type_body(ctx, et, 1); + parse_type_body(ctx, ti, 1); } else if (peek(ctx)->type == TOK_IDENT || (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) { - spl_type_info_t *base = spl_parse_type(ctx); - if (base) { - spl_type_info_t *alias = spl_type_clone(base); - alias->name = strdup(tname); - alias->kind = TYPE_NAME; - map_put(ctx->type_defs, strdup(tname), alias); + int base = spl_type_parse(&ctx->tctx, ctx); + if (base >= 0) { + spl_type_alias(&ctx->tctx, tname, base); } } @@ -508,7 +450,6 @@ void spl_parse_prog(spl_comp_t *ctx) { break; } case TOK_SHARP: - /* #[extern("vm")] fn ... */ advance(ctx); /* # */ if (peek(ctx)->type == TOK_L_BRACKET) { advance(ctx); /* [ */ @@ -522,10 +463,8 @@ void spl_parse_prog(spl_comp_t *ctx) { parse_fn_decl(ctx, 1, 0); break; default: { - int prev = ctx->tok_idx; /* Safety: prevent infinite loop */ - /* Try to parse as a statement */ + int prev = ctx->tok_idx; spl_parse_stmt(ctx); - /* Safety: prevent infinite loop on unrecognized tokens */ if (ctx->tok_idx == prev) advance(ctx); break; diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index e71eb21..eed691e 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -20,11 +20,11 @@ static void parse_ret_stmt(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_R_BRACE || peek(ctx)->type == TOK_ENDLINE) { /* void return */ - spl_emit(ctx, SPL_RET, SPL_VOID, 0); + emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx); } else { spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN); (void)val; - spl_emit_ret(ctx, ctx->current_ret_type); + spl_emit_ret(ctx, ctx->current_ret_type_idx); } if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); @@ -42,7 +42,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) { char vname[256]; spl_tok_copy_name(name_tok, vname, sizeof(vname)); - spl_type_info_t *var_type = NULL; + int var_type_idx = -1; int has_init = 0; skip_nl(ctx); @@ -55,7 +55,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) { /* := is colon-assign */ has_init = 1; } else { - var_type = spl_parse_type(ctx); + var_type_idx = spl_type_parse(&ctx->tctx, ctx); skip_nl(ctx); } } @@ -75,35 +75,37 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) { skip_nl(ctx); /* Inline struct/enum/slice literal: var x: Type = { .field = val } * Don't call spl_parse_expr — { would be consumed as block expression */ - if (var_type && peek(ctx)->type == TOK_L_BRACE && - (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM || - var_type->kind == TYPE_SLICE)) { + if (var_type_idx >= 0 && peek(ctx)->type == TOK_L_BRACE && + (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ENUM || + spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE)) { inline_lit = 1; } else { init = spl_parse_expr(ctx, PREC_MIN); } } - if (!var_type) { - var_type = init.type ? init.type : spl_type_basic(SPL_I32); + if (var_type_idx < 0) { + var_type_idx = init.type_idx >= 0 ? init.type_idx : spl_type_basic(&ctx->tctx, SPL_I32); } - int offset = spl_declare_var(ctx, vname, var_type, is_const); + int offset = spl_declare_var(ctx, vname, var_type_idx, is_const); /* Store init value */ if (has_init) { - if (inline_lit && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM || - var_type->kind == TYPE_SLICE)) { + if (inline_lit && (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_STRUCT || + spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ENUM || + spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE)) { /* Inline struct/enum/slice literal: var x: Type = { .field = val } * Use spl_parse_struct_literal to handle field parsing uniformly. */ - spl_parse_struct_literal(ctx, var_type); - if (var_type->kind == TYPE_SLICE) { + spl_parse_struct_literal(ctx, var_type_idx); + if (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE) { /* Slice: copy 2 temp slots to variable */ - spl_emit_copy_slots(ctx, offset, 2); + emit_frame_copy(&ctx->emit, offset, 2); } else { - spl_emit_store_init(ctx, offset, var_type); + spl_emit_store_init(ctx, offset, var_type_idx); } } else { - spl_emit_store_init(ctx, offset, var_type); + spl_emit_store_init(ctx, offset, var_type_idx); } } @@ -182,8 +184,9 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) { skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) { /* Expression statement: drop value, consume ; */ - if (!is_assign && expr.type && spl_type_emit_type(expr.type) != SPL_VOID) - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + if (!is_assign && expr.type_idx >= 0 && + spl_type_emit_type(&ctx->tctx, expr.type_idx) != SPL_VOID) + emit_drop(&ctx->emit); while (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) advance(ctx); result = (spl_expr_result_t){0}; @@ -211,21 +214,21 @@ static void parse_if_stmt(spl_comp_t *ctx) { spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); (void)cond; - spl_val_t bz_addr = spl_emit_bz(ctx); + spl_val_t bz_addr = emit_bz_here(&ctx->emit); skip_nl(ctx); spl_parse_block(ctx); spl_val_t jmp_addr = 0; skip_nl(ctx); if (peek(ctx)->type == KW_ELSE) { - jmp_addr = spl_emit_jmp(ctx); - spl_patch_to_here(ctx, bz_addr); + jmp_addr = emit_jmp_here(&ctx->emit); + emit_patch_here(&ctx->emit, bz_addr); advance(ctx); /* else */ skip_nl(ctx); spl_parse_block(ctx); - spl_patch_to_here(ctx, jmp_addr); + emit_patch_here(&ctx->emit, jmp_addr); } else { - spl_patch_to_here(ctx, bz_addr); + emit_patch_here(&ctx->emit, bz_addr); } } @@ -251,9 +254,9 @@ static void loop_enter(spl_comp_t *ctx, spl_loop_save_t *save) { static void loop_exit(spl_comp_t *ctx, spl_loop_save_t *save) { spl_val_t here = vec_size(ctx->prog.insns); - spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)save->loop_start - (isize)here - 1)); + emit_jmp(&ctx->emit, (spl_val_t)((isize)save->loop_start - (isize)here - 1)); for (usize i = save->saved_bp_count; i < ctx->break_patch_count; i++) - spl_patch_to_here(ctx, ctx->break_patches[i]); + emit_patch_here(&ctx->emit, ctx->break_patches[i]); ctx->break_patch_count = save->saved_bp_count; ctx->in_loop = save->saved_loop; ctx->continue_target = save->saved_continue; @@ -271,11 +274,11 @@ static void parse_while_stmt(spl_comp_t *ctx) { loop_enter(ctx, &save); spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); (void)cond; - spl_val_t bz_addr = spl_emit_bz(ctx); + spl_val_t bz_addr = emit_bz_here(&ctx->emit); skip_nl(ctx); spl_parse_block(ctx); loop_exit(ctx, &save); - spl_patch_to_here(ctx, bz_addr); + emit_patch_here(&ctx->emit, bz_addr); } /* ============================================================ @@ -321,41 +324,39 @@ static void parse_for_stmt(spl_comp_t *ctx) { spl_tok_copy_name(ivar, iname, sizeof(iname)); spl_push_scope(ctx); - int ioffset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0); + int ioffset = spl_declare_var(ctx, iname, spl_type_basic(&ctx->tctx, SPL_USIZE), 0); /* Stack: [begin, end]; swap so TOS = begin */ - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + emit_swap(&ctx->emit); /* Store begin to i */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); + emit_store_to_laddr(&ctx->emit, ioffset, SPL_USIZE); /* Stack: [end] */ /* Condition: i < end */ spl_loop_save_t save; loop_enter(ctx, &save); - spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset); - spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); - spl_emit(ctx, SPL_ULT, SPL_USIZE, 0); - spl_val_t bz_addr = spl_emit_bz(ctx); + emit_laddr(&ctx->emit, ioffset); + emit_load_usize(&ctx->emit); + emit_pick(&ctx->emit, 1); + emit_ult_usize(&ctx->emit); + spl_val_t bz_addr = emit_bz_here(&ctx->emit); skip_nl(ctx); spl_parse_block(ctx); /* body */ /* Increment: i = i + 1 */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset); - spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset); - spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 1); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); + emit_laddr(&ctx->emit, ioffset); + emit_laddr(&ctx->emit, ioffset); + emit_load_usize(&ctx->emit); + emit_push_usize(&ctx->emit, 1); + emit_add_usize(&ctx->emit); + emit_store_usize(&ctx->emit); loop_exit(ctx, &save); /* Exit: patch bz, drop end */ - spl_patch_to_here(ctx, bz_addr); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_patch_here(&ctx->emit, bz_addr); + emit_drop(&ctx->emit); spl_emit_defer_epilogue(ctx, ctx->scope_depth); spl_pop_scope(ctx); @@ -375,7 +376,7 @@ static void parse_for_stmt(spl_comp_t *ctx) { spl_parse_expr(ctx, PREC_MIN); } /* Range pushed a value; we manage our own idx, drop it */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + emit_drop(&ctx->emit); } if (peek(ctx)->type != KW_AS) { @@ -403,82 +404,78 @@ static void parse_for_stmt(spl_comp_t *ctx) { int idx_offset = -1; if (iname[0]) - idx_offset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0); + idx_offset = spl_declare_var(ctx, iname, spl_type_basic(&ctx->tctx, SPL_USIZE), 0); - spl_type_info_t *elem_type = NULL; - if (start_expr.type) { - if (start_expr.type->kind == TYPE_SLICE) - elem_type = start_expr.type->elem; - else if (start_expr.type->kind == TYPE_PTR && start_expr.type->elem) - elem_type = start_expr.type->elem; + int elem_type_idx = -1; + if (start_expr.type_idx >= 0) { + if (spl_type_kind(&ctx->tctx, start_expr.type_idx) == TYPE_SLICE) + elem_type_idx = spl_type_elem_type(&ctx->tctx, start_expr.type_idx); + else if (spl_type_kind(&ctx->tctx, start_expr.type_idx) == TYPE_PTR && + spl_type_elem_type(&ctx->tctx, start_expr.type_idx) >= 0) + elem_type_idx = spl_type_elem_type(&ctx->tctx, start_expr.type_idx); } - if (!elem_type) - elem_type = spl_type_basic(SPL_I32); - int val_offset = spl_declare_var(ctx, vname, elem_type, 0); + if (elem_type_idx < 0) + elem_type_idx = spl_type_basic(&ctx->tctx, SPL_I32); + int val_offset = spl_declare_var(ctx, vname, elem_type_idx, 0); /* Extract ptr and len from slice, push index=0: stack [ptr, len, idx] */ - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_PUSH, SPL_U64, sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); + emit_dup(&ctx->emit); + emit_load_ptr(&ctx->emit); + emit_swap(&ctx->emit); + emit_ptr_add(&ctx->emit, sizeof(spl_val_t)); + emit_load_usize(&ctx->emit); + emit_push_usize(&ctx->emit, 0); /* Stack: [ptr, len, idx=0] */ spl_loop_save_t save; loop_enter(ctx, &save); /* Condition: idx < len */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy len */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy idx */ - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [idx, len] → [len, idx] → SWAP → [idx, len] */ - spl_emit(ctx, SPL_ULT, SPL_USIZE, 0); - spl_val_t bz_addr = spl_emit_bz(ctx); + emit_pick(&ctx->emit, 1); /* copy len */ + emit_pick(&ctx->emit, 1); /* copy idx */ + emit_swap(&ctx->emit); /* [idx, len] → [len, idx] → SWAP → [idx, len] */ + emit_ult_usize(&ctx->emit); + spl_val_t bz_addr = emit_bz_here(&ctx->emit); /* Store current idx to idx variable */ if (idx_offset >= 0) { - spl_emit(ctx, SPL_PICK, SPL_VOID, 0); /* copy idx (TOS) */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, idx_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); + emit_pick(&ctx->emit, 0); /* copy idx (TOS) */ + emit_store_to_laddr(&ctx->emit, idx_offset, SPL_USIZE); } /* Load slice[idx] and store to val */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* copy ptr: [ptr, len, idx, ptr] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy idx: [ptr, len, idx, ptr, idx] */ - usize elem_byte_size = elem_type ? spl_type_size(elem_type) : 4; - spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size); - spl_emit(ctx, SPL_MUL, SPL_U64, 0); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); - if (elem_type && !spl_type_is_scalar(elem_type) && - spl_type_size(elem_type) > sizeof(spl_val_t)) { - spl_emit_copy_slots(ctx, val_offset, spl_type_slot_count(elem_type)); + emit_pick(&ctx->emit, 2); /* copy ptr: [ptr, len, idx, ptr] */ + emit_pick(&ctx->emit, 1); /* copy idx: [ptr, len, idx, ptr, idx] */ + usize elem_byte_size = elem_type_idx >= 0 ? spl_type_size(&ctx->tctx, elem_type_idx) : 4; + emit_push_u64(&ctx->emit, elem_byte_size); + emit_mul_u64(&ctx->emit); + emit_add_u64(&ctx->emit); + if (elem_type_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_type_idx) && + spl_type_size(&ctx->tctx, elem_type_idx) > sizeof(spl_val_t)) { + emit_frame_copy(&ctx->emit, val_offset, spl_type_slot_count(&ctx->tctx, elem_type_idx)); } else { - spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32; - spl_emit(ctx, SPL_LOAD, elem_bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, elem_bt, 0); + spl_type_t elem_bt = elem_type_idx >= 0 ? spl_type_emit_type(&ctx->tctx, elem_type_idx) : SPL_I32; + emit_load_type(&ctx->emit, elem_bt); + emit_store_to_laddr(&ctx->emit, val_offset, elem_bt); } skip_nl(ctx); spl_parse_block(ctx); /* body */ /* Increment idx */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 0); /* copy idx */ - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 1); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* replace old idx with new */ + emit_pick(&ctx->emit, 0); /* copy idx */ + emit_push_usize(&ctx->emit, 1); + emit_add_usize(&ctx->emit); + emit_swap(&ctx->emit); + emit_drop(&ctx->emit); /* replace old idx with new */ loop_exit(ctx, &save); /* Exit: patch bz, drop idx, len, ptr */ - spl_patch_to_here(ctx, bz_addr); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop idx */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop len */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop ptr */ + emit_patch_here(&ctx->emit, bz_addr); + emit_drop(&ctx->emit); /* drop idx */ + emit_drop(&ctx->emit); /* drop len */ + emit_drop(&ctx->emit); /* drop ptr */ spl_pop_scope(ctx); } @@ -493,7 +490,7 @@ static void parse_break_stmt(spl_comp_t *ctx) { spl_comp_error(ctx, "break outside loop"); } /* Emit JMP with placeholder, add to patch list */ - spl_val_t addr = spl_emit_jmp(ctx); + spl_val_t addr = emit_jmp_here(&ctx->emit); if (ctx->break_patch_cap <= ctx->break_patch_count) { usize new_cap = ctx->break_patch_cap ? ctx->break_patch_cap * 2 : 8; ctx->break_patches = realloc(ctx->break_patches, new_cap * sizeof(usize)); @@ -511,7 +508,7 @@ static void parse_continue_stmt(spl_comp_t *ctx) { } /* Emit JMP with relative offset to continue_target */ spl_val_t here = vec_size(ctx->prog.insns); - spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)ctx->continue_target - (isize)here - 1)); + emit_jmp(&ctx->emit, (spl_val_t)((isize)ctx->continue_target - (isize)here - 1)); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } @@ -537,7 +534,7 @@ static void parse_defer_stmt(spl_comp_t *ctx) { /* Emit JMP exit placeholder — will be patched at scope exit */ if (ctx->defer_count > 0) { spl_defer_entry_t *e = &ctx->defer_stack[ctx->defer_count - 1]; - e->jmp_exit = spl_emit_jmp(ctx); + e->jmp_exit = emit_jmp_here(&ctx->emit); } } @@ -547,10 +544,10 @@ static void parse_defer_stmt(spl_comp_t *ctx) { /* Parse an enum variant pattern in a match arm: .VariantName * Delegates comparison to expr layer (spl_emit_match_enum_cmp). - * Returns the variant for binding parsing, or NULL on error. */ -static spl_enum_variant_t *parse_match_enum_variant(spl_comp_t *ctx, spl_type_info_t *enum_type, - int val_offset, int by_value) { - return spl_emit_match_enum_cmp(ctx, enum_type, val_offset, by_value); + * Returns the variant item index, or -1 on error. */ +static int parse_match_enum_variant(spl_comp_t *ctx, int enum_type_idx, + int val_offset, int by_value) { + return spl_emit_match_enum_cmp(ctx, enum_type_idx, val_offset, by_value); } /* Parse a value pattern in a match arm (non-enum). @@ -564,7 +561,8 @@ static void parse_match_value_pattern(spl_comp_t *ctx, int val_offset) { * Declares local variables and loads corresponding field data * from the matched value's data area (offset 4+). * Sets *scope_pushed = 1 if bindings declared. */ -static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *variant, int val_offset, +static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, + int variant_item_idx, int val_offset, int *scope_pushed) { advance(ctx); /* ( */ skip_nl(ctx); @@ -576,22 +574,36 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *varia spl_push_scope(ctx); *scope_pushed = 1; - if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) { + spl_type_item_t *var_item = spl_type_item_at(&ctx->tctx, enum_type_idx, variant_item_idx); + int data_type_idx = var_item ? var_item->enum_field.type_idx : -1; + + if (data_type_idx >= 0 && spl_type_kind(&ctx->tctx, data_type_idx) == TYPE_STRUCT) { /* Multi-field struct destructuring: one binding per struct field */ + spl_type_item_vec_t *data_items = spl_type_items(&ctx->tctx, data_type_idx); int bi = 0; for (;;) { spl_tok_t *btok = advance(ctx); char bname[256]; spl_tok_copy_name(btok, bname, sizeof(bname)); - spl_type_info_t *btype = spl_type_basic(SPL_I32); - usize field_byte_off = 4; /* skip tag */ - if ((usize)bi < vec_size(variant->data_type->fields)) { - btype = vec_at(variant->data_type->fields, bi).type; - field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset; + int btype_idx = spl_type_basic(&ctx->tctx, SPL_I32); + usize field_byte_off = ENUM_TAG_SIZE; /* skip tag */ + { + int fcount = 0; + vec_for(*data_items, di) { + spl_type_item_t *fit = &vec_at(*data_items, di); + if (fit->item_kind == ITEM_FIELD) { + if (fcount == bi) { + btype_idx = fit->aggregate_field.type_idx; + field_byte_off = ENUM_TAG_SIZE + fit->aggregate_field.offset; + break; + } + fcount++; + } + } } - int boffset = spl_declare_var(ctx, bname, btype, 0); - spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset); + int boffset = spl_declare_var(ctx, bname, btype_idx, 0); + emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, field_byte_off, btype_idx, boffset); bi++; skip_nl(ctx); @@ -602,15 +614,14 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *varia } break; } - } else if (variant->data_type) { + } else if (data_type_idx >= 0) { /* Single-value binding */ spl_tok_t *btok = advance(ctx); char bname[256]; spl_tok_copy_name(btok, bname, sizeof(bname)); - spl_type_info_t *btype = variant->data_type; - int boffset = spl_declare_var(ctx, bname, btype, 0); - spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset); + int boffset = spl_declare_var(ctx, bname, data_type_idx, 0); + emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx, boffset); } expect(ctx, TOK_R_PAREN); @@ -633,37 +644,36 @@ static void parse_match_stmt(spl_comp_t *ctx) { /* Determine match type */ int is_enum_match = 0; - spl_type_info_t *enum_type = NULL; - spl_type_info_t *t = expr.type; - if (t && t->kind == TYPE_ENUM) { + int enum_type_idx = -1; + int type_idx = expr.type_idx; + if (type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM) { is_enum_match = 1; - enum_type = t; - } else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) { + enum_type_idx = type_idx; + } else if (type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_PTR && + spl_type_elem_type(&ctx->tctx, type_idx) >= 0 && + spl_type_kind(&ctx->tctx, spl_type_elem_type(&ctx->tctx, type_idx)) == TYPE_ENUM) { is_enum_match = 1; - enum_type = t->elem; - } else if (!(t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type))) { + enum_type_idx = spl_type_elem_type(&ctx->tctx, type_idx); + } else if (!(type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_BASIC && + spl_type_is_integer(spl_type_basic_type(&ctx->tctx, type_idx)))) { spl_comp_error(ctx, "match expression must be an enum or integer type"); return; } /* Scalar enums store the raw tag directly; others store a pointer */ - int match_by_value = (t && t->kind == TYPE_ENUM && spl_type_is_scalar(t)); + int match_by_value = (type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM && + spl_type_is_scalar(&ctx->tctx, type_idx)); /* Save match value to temp slot */ - int val_offset = ctx->current_local_bytes; - ctx->current_local_bytes += (int)sizeof(spl_val_t); - if (ctx->current_local_bytes > ctx->peak_local_bytes) - ctx->peak_local_bytes = ctx->current_local_bytes; - spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, spl_type_emit_type(t), 0); + int val_offset = fa_alloc_temp(&ctx->emit.frame, 1); + emit_store_to_laddr(&ctx->emit, val_offset, spl_type_emit_type(&ctx->tctx, type_idx)); skip_nl(ctx); if (peek(ctx)->type == TOK_L_BRACE) advance(ctx); /* { */ /* Collect JMP-to-end addresses for patching */ - enum { MAX_MATCH_ARMS = 32 }; + enum { MAX_MATCH_ARMS = 128 }; spl_val_t jmp_to_end[MAX_MATCH_ARMS]; int n_jmps = 0; @@ -680,9 +690,9 @@ static void parse_match_stmt(spl_comp_t *ctx) { int scope_pushed = 0; int has_parens = 0; spl_val_t body_start = 0; - spl_val_t bnz_addrs[16]; + spl_val_t bnz_addrs[128]; int n_bnz = 0; - spl_enum_variant_t *arm_variant = NULL; + int arm_variant_item = -1; /* --- Parse arm pattern(s): comma-separated with fallthrough --- */ if (peek(ctx)->type == KW_ANY) { @@ -691,8 +701,8 @@ static void parse_match_stmt(spl_comp_t *ctx) { } else { for (;;) { if (is_enum_match) { - arm_variant = - parse_match_enum_variant(ctx, enum_type, val_offset, match_by_value); + arm_variant_item = + parse_match_enum_variant(ctx, enum_type_idx, val_offset, match_by_value); if (ctx->has_error) break; skip_nl(ctx); @@ -710,7 +720,8 @@ static void parse_match_stmt(spl_comp_t *ctx) { advance(ctx); skip_nl(ctx); if (peek(ctx)->type != TOK_ASSIGN) { - bnz_addrs[n_bnz++] = spl_emit_bnz(ctx); /* fallthrough to body */ + if (n_bnz < 128) + bnz_addrs[n_bnz++] = emit_bnz_here(&ctx->emit); /* fallthrough to body */ continue; } break; @@ -720,14 +731,15 @@ static void parse_match_stmt(spl_comp_t *ctx) { /* Last pattern: BZ past body if no match */ if (!ctx->has_error) { - bz_addr = spl_emit_bz(ctx); + bz_addr = emit_bz_here(&ctx->emit); body_start = vec_size(ctx->prog.insns); } } /* --- Parse enum bindings (only for last variant) --- */ - if (is_enum_match && has_parens && arm_variant) { - parse_match_enum_bindings(ctx, arm_variant, val_offset, &scope_pushed); + if (is_enum_match && has_parens && arm_variant_item >= 0) { + parse_match_enum_bindings(ctx, enum_type_idx, arm_variant_item, + val_offset, &scope_pushed); } skip_nl(ctx); @@ -751,16 +763,16 @@ static void parse_match_stmt(spl_comp_t *ctx) { /* JMP to end (skip remaining arms) */ if (n_jmps < MAX_MATCH_ARMS) - jmp_to_end[n_jmps++] = spl_emit_jmp(ctx); + jmp_to_end[n_jmps++] = emit_jmp_here(&ctx->emit); /* Patch BZ to here (next arm or end) */ if (bz_addr) - spl_patch_to_here(ctx, bz_addr); + emit_patch_here(&ctx->emit, bz_addr); /* Patch BNZ fallthroughs to body start */ for (int i = 0; i < n_bnz; i++) { spl_val_t offset = body_start - bnz_addrs[i] - 1; - spl_patch(ctx, bnz_addrs[i], offset); + emit_patch(&ctx->emit, bnz_addrs[i], offset); } skip_nl(ctx); @@ -770,10 +782,10 @@ static void parse_match_stmt(spl_comp_t *ctx) { /* Patch all JMPs to end */ for (int i = 0; i < n_jmps; i++) - spl_patch_to_here(ctx, jmp_to_end[i]); + emit_patch_here(&ctx->emit, jmp_to_end[i]); /* Release temp slot */ - ctx->current_local_bytes -= (int)sizeof(spl_val_t); + fa_free(&ctx->emit.frame, val_offset); } /* ============================================================ @@ -811,7 +823,7 @@ static void parse_extern_decl(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); - spl_parse_type(ctx); /* skip type */ + spl_type_parse(&ctx->tctx, ctx); /* skip type */ } nparams++; skip_nl(ctx); @@ -831,15 +843,15 @@ static void parse_extern_decl(spl_comp_t *ctx) { skip_nl(ctx); /* Return type */ - spl_type_info_t *ret_type = spl_type_basic(SPL_VOID); + int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) { - ret_type = spl_parse_type(ctx); - if (!ret_type) - ret_type = spl_type_basic(SPL_VOID); + ret_type_idx = spl_type_parse(&ctx->tctx, ctx); + if (ret_type_idx < 0) + ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID); skip_nl(ctx); } - spl_declare_func(ctx, fn_name, ret_type, nparams, 1, 0); + spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); @@ -895,8 +907,9 @@ static void parse_expr_stmt(spl_comp_t *ctx) { if (is_assign) ctx->addr_of_mode = 0; - if (!is_assign && expr.type && spl_type_emit_type(expr.type) != SPL_VOID) - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + if (!is_assign && expr.type_idx >= 0 && + spl_type_emit_type(&ctx->tctx, expr.type_idx) != SPL_VOID) + emit_drop(&ctx->emit); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); /* Safety: if no token was consumed, advance to prevent infinite loop */ diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 88f50d8..865de52 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -1,12 +1,63 @@ -/* spl_type.c — Type system implementation */ +/* spl_type.c — Type system: arena, constructors, layout, accessors */ +#include "spl_type.h" #include "spl_comp.h" #include "spl_lex_util.h" #include #include #include -/* Basic type sizes */ +/* ============================================================ + * Lifecycle + * ============================================================ */ + +void spl_type_ctx_init(spl_type_ctx_t *tctx) { + memset(tctx, 0, sizeof(*tctx)); + memset(tctx->basic_cache, 0, sizeof(tctx->basic_cache)); + vec_init(tctx->types); + map_init(tctx->type_map, MAP_HASH_STR, MAP_CMP_STR); + + /* Create root type (compilation unit) */ + spl_type_info_t root; + memset(&root, 0, sizeof(root)); + root.name = strdup("$root"); + root.kind = TYPE_STRUCT; + root.resolved = 1; + root.byte_size = 0; + root.slot_count = 0; + root.parent_type_idx = -1; + vec_init(root.items); + vec_push(tctx->types, root); + tctx->root_type_idx = 0; + tctx->current_type_idx = 0; +} + +void spl_type_ctx_drop(spl_type_ctx_t *tctx) { + if (!tctx) + return; + vec_for(tctx->types, i) { + spl_type_info_t *t = &vec_at(tctx->types, i); + free(t->name); + vec_for(t->items, j) { free((void *)vec_at(t->items, j).name); } + vec_free(t->items); + } + vec_free(tctx->types); + map_free(tctx->type_map); +} + +/* ============================================================ + * Internal: add a type to arena, return index + * ============================================================ */ + +static int spl_type_add(spl_type_ctx_t *tctx, spl_type_info_t t) { + vec_push(tctx->types, t); + return (int)vec_size(tctx->types) - 1; +} + +/* ============================================================ + * Basic type → type_idx lookup (memoized singleton per basic type) + * ============================================================ */ + static int spl_basic_byte_size(spl_type_t bt) { switch (bt) { case SPL_VOID: @@ -34,6 +85,33 @@ static int spl_basic_byte_size(spl_type_t bt) { } } +int spl_type_basic(spl_type_ctx_t *tctx, spl_type_t bt) { + if (bt >= SPL_TYPE_COUNT) + return -1; + + /* Use memoized singletons for basic types */ + if (bt < SPL_TYPE_COUNT && tctx->basic_cache[bt] != 0) + return tctx->basic_cache[bt]; + + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_BASIC; + t.basic_type = bt; + t.byte_size = (usize)spl_basic_byte_size(bt); + t.slot_count = t.byte_size == 0 ? 0 : 1; + t.resolved = 1; + vec_init(t.items); + + int idx = spl_type_add(tctx, t); + if (bt < SPL_TYPE_COUNT) + tctx->basic_cache[bt] = idx; + return idx; +} + +/* ============================================================ + * Name → basic type + * ============================================================ */ + int spl_type_is_integer(spl_type_t bt) { switch (bt) { case SPL_I8: @@ -86,197 +164,432 @@ static spl_type_t name_to_basic_type(const char *name, usize len) { return SPL_TYPE_COUNT; } -spl_type_info_t *spl_type_basic(spl_type_t bt) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_BASIC; - t->basic_type = bt; - t->byte_size = (usize)spl_basic_byte_size(bt); - t->slot_count = t->byte_size == 0 ? 0 : 1; - t->resolved = 1; - return t; +/* ============================================================ + * Constructors — PTR / ARRAY / SLICE + * + * Encode element info in items[0].aggregate_field: + * type_idx = element type index + * offset = array length (for ARRAY) + * ============================================================ */ + +int spl_type_ptr(spl_type_ctx_t *tctx, int elem_type_idx) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_PTR; + t.byte_size = sizeof(spl_val_t); + t.slot_count = 1; + t.resolved = 1; + vec_init(t.items); + + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.item_kind = ITEM_FIELD; + item.aggregate_field.type_idx = elem_type_idx; + item.aggregate_field.offset = 0; + vec_push(t.items, item); + + return spl_type_add(tctx, t); } -spl_type_info_t *spl_type_ptr(spl_type_info_t *elem) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_PTR; - t->elem = elem; - t->byte_size = sizeof(spl_val_t); - t->slot_count = 1; - t->resolved = 1; - return t; +int spl_type_array(spl_type_ctx_t *tctx, int elem_type_idx, usize len) { + spl_type_info_t *elem = (elem_type_idx >= 0) + ? &vec_at(tctx->types, elem_type_idx) + : NULL; + + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_ARRAY; + t.byte_size = elem ? elem->byte_size * len : 0; + t.slot_count = elem ? elem->slot_count * len : 0; + t.resolved = 1; + vec_init(t.items); + + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.item_kind = ITEM_FIELD; + item.aggregate_field.type_idx = elem_type_idx; + item.aggregate_field.offset = len; + vec_push(t.items, item); + + return spl_type_add(tctx, t); } -spl_type_info_t *spl_type_array(spl_type_info_t *elem, usize len) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_ARRAY; - t->elem = elem; - t->array_len = len; - t->byte_size = elem->byte_size * len; - t->slot_count = elem->slot_count * len; - t->resolved = 1; - return t; +int spl_type_slice(spl_type_ctx_t *tctx, int elem_type_idx) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_SLICE; + t.byte_size = sizeof(spl_val_t) * 2; + t.slot_count = 2; + t.resolved = 1; + vec_init(t.items); + + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.item_kind = ITEM_FIELD; + item.aggregate_field.type_idx = elem_type_idx; + item.aggregate_field.offset = 0; + vec_push(t.items, item); + + return spl_type_add(tctx, t); } -spl_type_info_t *spl_type_slice(spl_type_info_t *elem) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_SLICE; - t->elem = elem; - t->byte_size = sizeof(spl_val_t) * 2; /* ptr + len */ - t->slot_count = 2; - t->resolved = 1; - return t; -} +/* ============================================================ + * Constructors — STRUCT / UNION / ENUM + * ============================================================ */ -spl_type_info_t *spl_type_struct(const char *name) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_STRUCT; +int spl_type_struct(spl_type_ctx_t *tctx, const char *name) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_STRUCT; if (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; -} + t.name = strdup(name); + t.resolved = 0; + vec_init(t.items); -spl_type_info_t *spl_type_union(const char *name) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_UNION; + int idx = spl_type_add(tctx, t); if (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; + map_put(tctx->type_map, strdup(name), idx); + return idx; } -spl_type_info_t *spl_type_enum(const char *name) { - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_ENUM; +int spl_type_union(spl_type_ctx_t *tctx, const char *name) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_UNION; if (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; - return t; + t.name = strdup(name); + t.resolved = 0; + vec_init(t.items); + + int idx = spl_type_add(tctx, t); + if (name) + map_put(tctx->type_map, strdup(name), idx); + return idx; } -void spl_type_add_field(spl_type_info_t *st, const char *name, spl_type_info_t *ftype) { - spl_field_t f; - f.name = strdup(name); - f.type = ftype; - f.offset = 0; - vec_push(st->fields, f); +int spl_type_enum(spl_type_ctx_t *tctx, const char *name) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_ENUM; + if (name) + t.name = strdup(name); + t.byte_size = ENUM_TAG_SIZE; /* tag */ + t.slot_count = 1; + t.resolved = 0; + vec_init(t.items); + + int idx = spl_type_add(tctx, t); + if (name) + map_put(tctx->type_map, strdup(name), idx); + return idx; } -void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t *dtype) { - spl_enum_variant_t v; - v.name = strdup(name); - v.data_type = dtype; - v.value = (int)vec_size(et->variants); - vec_push(et->variants, v); +int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_NAME; + if (name) + t.name = strdup(name); + t.resolved = 0; + vec_init(t.items); + + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.item_kind = ITEM_FIELD; + item.aggregate_field.type_idx = target_type_idx; + item.aggregate_field.offset = 0; + vec_push(t.items, item); + + int idx = spl_type_add(tctx, t); + if (name) + map_put(tctx->type_map, strdup(name), idx); + return idx; } -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; +/* ============================================================ + * Item management + * ============================================================ */ + +void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, + int field_type_idx) { + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.name = strdup(name); + item.item_kind = ITEM_FIELD; + item.aggregate_field.type_idx = field_type_idx; + item.aggregate_field.offset = 0; + vec_push(t->items, item); +} + +void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, + int data_type_idx) { + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.name = strdup(name); + item.item_kind = ITEM_VARIANT; + item.enum_field.type_idx = data_type_idx; + item.enum_field.value = vec_size(t->items); + vec_push(t->items, item); +} + +void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, + int func_idx) { + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + + /* Deduplicate: update existing method entry */ + vec_for(t->items, i) { + spl_type_item_t *it = &vec_at(t->items, i); + if (it->item_kind == ITEM_METHOD && it->name && + strcmp(it->name, name) == 0) { + it->method.func_idx = func_idx; return; } } - spl_method_info_t m; - m.name = strdup(name); - m.func_idx = func_idx; - vec_push(t->methods, m); + + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.name = strdup(name); + item.item_kind = ITEM_METHOD; + item.method.func_idx = func_idx; + vec_push(t->items, item); } -void spl_type_compute_layout(spl_type_info_t *t) { - if (!t || t->resolved) +void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name, + int child_type_idx) { + spl_type_info_t *t = &vec_at(tctx->types, parent_idx); + spl_type_item_t item; + memset(&item, 0, sizeof(item)); + item.name = strdup(name); + item.item_kind = ITEM_NESTED_TYPE; + item.nested_type.type_idx = child_type_idx; + vec_push(t->items, item); + + vec_at(tctx->types, child_type_idx).parent_type_idx = parent_idx; +} + +/* ============================================================ + * Layout computation + * ============================================================ */ + +void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (t->resolved) return; - if (t->kind == TYPE_STRUCT || t->kind == TYPE_UNION) { + switch (t->kind) { + case TYPE_STRUCT: { usize offset = 0; - usize max_field_size = 0; - vec_for(t->fields, i) { - spl_field_t *f = &vec_at(t->fields, i); - if (f->type) { - spl_type_compute_layout(f->type); - if (t->kind == TYPE_UNION) { - /* Union: all fields at offset 0, size = max field size */ - f->offset = 0; - if (f->type->byte_size > max_field_size) - max_field_size = f->type->byte_size; - } else { - /* Struct: sequential layout */ - f->offset = offset; - offset += f->type->byte_size; - } + vec_for(t->items, i) { + spl_type_item_t *it = &vec_at(t->items, i); + if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) { + spl_type_compute_layout(tctx, it->aggregate_field.type_idx); + spl_type_info_t *ft = + &vec_at(tctx->types, it->aggregate_field.type_idx); + it->aggregate_field.offset = offset; + offset += ft->byte_size; } } - if (t->kind == TYPE_UNION) { - t->byte_size = max_field_size; - } else { - t->byte_size = offset; - } - /* Round up to slot alignment */ + t->byte_size = offset; usize slot_sz = sizeof(spl_val_t); - t->slot_count = (t->byte_size + slot_sz - 1) / slot_sz; + t->slot_count = (offset + slot_sz - 1) / slot_sz; if (t->slot_count < 1) t->slot_count = 1; t->resolved = 1; - } else if (t->kind == TYPE_ENUM) { - /* Enums with data need special handling */ - usize max_dsize = 0; - vec_for(t->variants, i) { - spl_enum_variant_t *v = &vec_at(t->variants, i); - if (v->data_type) { - spl_type_compute_layout(v->data_type); - if (v->data_type->byte_size > max_dsize) - max_dsize = v->data_type->byte_size; + break; + } + case TYPE_UNION: { + usize max_sz = 0; + vec_for(t->items, i) { + spl_type_item_t *it = &vec_at(t->items, i); + if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) { + spl_type_compute_layout(tctx, it->aggregate_field.type_idx); + spl_type_info_t *ft = + &vec_at(tctx->types, it->aggregate_field.type_idx); + it->aggregate_field.offset = 0; + if (ft->byte_size > max_sz) + max_sz = ft->byte_size; } } - /* Enum layout: tag (4 bytes) + max data */ - usize total = 4 + max_dsize; + t->byte_size = max_sz; + usize slot_sz = sizeof(spl_val_t); + t->slot_count = (max_sz + slot_sz - 1) / slot_sz; + if (t->slot_count < 1) + t->slot_count = 1; + t->resolved = 1; + break; + } + case TYPE_ENUM: { + usize max_dsize = 0; + vec_for(t->items, i) { + spl_type_item_t *it = &vec_at(t->items, i); + if (it->item_kind == ITEM_VARIANT && + it->enum_field.type_idx >= 0) { + spl_type_compute_layout(tctx, it->enum_field.type_idx); + spl_type_info_t *dt = + &vec_at(tctx->types, it->enum_field.type_idx); + if (dt->byte_size > max_dsize) + max_dsize = dt->byte_size; + } + } + usize total = ENUM_TAG_SIZE + max_dsize; usize slot_sz = sizeof(spl_val_t); t->byte_size = total; t->slot_count = (total + slot_sz - 1) / slot_sz; if (t->slot_count < 1) t->slot_count = 1; t->resolved = 1; - } else if (t->kind == TYPE_NAME) { - /* Type alias - should already be resolved to underlying type */ + break; + } + case TYPE_NAME: { + int target = spl_type_elem_type(tctx, type_idx); + if (target >= 0) { + spl_type_compute_layout(tctx, target); + spl_type_info_t *at = &vec_at(tctx->types, target); + t->byte_size = at->byte_size; + t->slot_count = at->slot_count; + } t->resolved = 1; + break; + } + default: + break; } } -usize spl_type_size(spl_type_info_t *t) { - if (!t) +/* ============================================================ + * Accessors + * ============================================================ */ + +spl_type_kind_t spl_type_kind(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0 || (usize)type_idx >= vec_size(tctx->types)) + return TYPE_VOID; + return vec_at(tctx->types, type_idx).kind; +} + +spl_type_t spl_type_basic_type(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0 || (usize)type_idx >= vec_size(tctx->types)) + return SPL_I32; + return vec_at(tctx->types, type_idx).basic_type; +} + +const char *spl_type_name(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0 || (usize)type_idx >= vec_size(tctx->types)) + return NULL; + return vec_at(tctx->types, type_idx).name; +} + +usize spl_type_size(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) return 0; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); if (!t->resolved) - spl_type_compute_layout(t); + spl_type_compute_layout(tctx, type_idx); return t->byte_size; } -usize spl_type_slot_count(spl_type_info_t *t) { - if (!t) +usize spl_type_slot_count(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) return 0; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); if (!t->resolved) - spl_type_compute_layout(t); + spl_type_compute_layout(tctx, type_idx); return t->slot_count; } -/* Byte stride between consecutive elements in storage */ -usize spl_type_elem_stride(spl_type_info_t *elem) { return spl_type_size(elem); } +usize spl_type_elem_stride(spl_type_ctx_t *tctx, int elem_type_idx) { + return spl_type_size(tctx, elem_type_idx); +} -const char *spl_type_str(spl_type_info_t *t) { - if (!t) +int spl_type_elem_type(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return -1; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (vec_size(t->items) > 0) + return vec_at(t->items, 0).aggregate_field.type_idx; + return -1; +} + +usize spl_type_array_len(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return 0; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (t->kind == TYPE_ARRAY && vec_size(t->items) > 0) + return vec_at(t->items, 0).aggregate_field.offset; + return 0; +} + +/* ============================================================ + * Classification helpers + * ============================================================ */ + +int spl_type_is_scalar(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return 1; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (t->kind == TYPE_BASIC || t->kind == TYPE_PTR) + return 1; + if (t->kind == TYPE_ENUM) { + vec_for(t->items, i) { + if (vec_at(t->items, i).enum_field.type_idx >= 0) + return 0; /* has data — aggregate */ + } + return 1; /* simple enum — scalar */ + } + return 0; +} + +int spl_type_needs_multi_slot(spl_type_ctx_t *tctx, int type_idx) { + return !spl_type_is_scalar(tctx, type_idx) && + spl_type_slot_count(tctx, type_idx) > 1; +} + +int spl_type_is_aggregate(spl_type_ctx_t *tctx, int type_idx) { + return !spl_type_is_scalar(tctx, type_idx); +} + +int spl_type_has_inline_literal(spl_type_ctx_t *tctx, int type_idx) { + spl_type_kind_t k = spl_type_kind(tctx, type_idx); + return k == TYPE_STRUCT || k == TYPE_ENUM || k == TYPE_SLICE; +} + +int spl_type_resolve_underlying(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return -1; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (t->kind == TYPE_NAME) { + int target = spl_type_elem_type(tctx, type_idx); + if (target >= 0) + return spl_type_resolve_underlying(tctx, target); + } + return type_idx; +} + +spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return SPL_I32; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (t->kind == TYPE_BASIC) + return t->basic_type; + if (t->kind == TYPE_PTR) + return SPL_PTR; + if (t->kind == TYPE_ENUM) { + vec_for(t->items, i) { + if (vec_at(t->items, i).enum_field.type_idx >= 0) + return SPL_PTR; + } + return SPL_I32; + } + return SPL_PTR; +} + +const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) return ""; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); switch (t->kind) { case TYPE_VOID: return "void"; @@ -316,25 +629,27 @@ const char *spl_type_str(spl_type_info_t *t) { } case TYPE_PTR: { static char buf[64]; - snprintf(buf, sizeof(buf), "*%s", spl_type_str(t->elem)); + snprintf(buf, sizeof(buf), "*%s", + spl_type_str(tctx, spl_type_elem_type(tctx, type_idx))); return buf; } case TYPE_ARRAY: { static char buf[64]; - snprintf(buf, sizeof(buf), "[%zu]%s", t->array_len, spl_type_str(t->elem)); + snprintf(buf, sizeof(buf), "[%zu]%s", + spl_type_array_len(tctx, type_idx), + spl_type_str(tctx, spl_type_elem_type(tctx, type_idx))); return buf; } case TYPE_SLICE: { static char buf[64]; - snprintf(buf, sizeof(buf), "[]%s", spl_type_str(t->elem)); + snprintf(buf, sizeof(buf), "[]%s", + spl_type_str(tctx, spl_type_elem_type(tctx, type_idx))); return buf; } case TYPE_STRUCT: - return t->name ? t->name : "struct"; case TYPE_UNION: - return t->name ? t->name : "union"; case TYPE_ENUM: - return t->name ? t->name : "enum"; + return t->name ? t->name : ""; case TYPE_NAME: return t->name ? t->name : ""; default: @@ -342,82 +657,167 @@ const char *spl_type_str(spl_type_info_t *t) { } } -spl_type_info_t *spl_type_clone(spl_type_info_t *t) { - if (!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 — shallow is fine for our use. - * Clone doesn't own children (aliases never need them). */ - memset(&c->children, 0, sizeof(c->children)); - return c; +/* ============================================================ + * Item iteration + * ============================================================ */ + +int spl_type_item_count(spl_type_ctx_t *tctx, int type_idx, + spl_type_item_kind_t kind) { + if (type_idx < 0) + return 0; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + int count = 0; + vec_for(t->items, i) { + if (vec_at(t->items, i).item_kind == kind) + count++; + } + return count; } -/* Parse a type from the token stream and return a type_info. - * This is used by the parser for type annotations. */ -spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { +spl_type_item_t *spl_type_item_at(spl_type_ctx_t *tctx, int type_idx, + int item_index) { + if (type_idx < 0) + return NULL; + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + if (item_index < 0 || (usize)item_index >= vec_size(t->items)) + return NULL; + return &vec_at(t->items, item_index); +} + +spl_type_item_t *spl_type_first_of_kind(spl_type_ctx_t *tctx, int type_idx, + spl_type_item_kind_t kind, + int *count) { + if (type_idx < 0) { + if (count) + *count = 0; + return NULL; + } + spl_type_info_t *t = &vec_at(tctx->types, type_idx); + int found = 0; + spl_type_item_t *first = NULL; + vec_for(t->items, i) { + if (vec_at(t->items, i).item_kind == kind) { + if (!first) + first = &vec_at(t->items, i); + found++; + } + } + if (count) + *count = found; + return first; +} + +spl_type_item_vec_t *spl_type_items(spl_type_ctx_t *tctx, int type_idx) { + if (type_idx < 0) + return NULL; + return &vec_at(tctx->types, type_idx).items; +} + +/* ============================================================ + * Type resolution + * ============================================================ */ + +int spl_type_resolve(spl_type_ctx_t *tctx, const char *name) { + if (!tctx || !name) + return -1; + + /* 1. Walk from current type up through parent chain */ + int current = tctx->current_type_idx; + while (current >= 0) { + int found = spl_type_resolve_in(tctx, current, name); + if (found >= 0) + return found; + current = vec_at(tctx->types, current).parent_type_idx; + } + + /* 2. Fallback to flat type_map */ + int found = -1; + if (map_get(tctx->type_map, name, &found)) + return found; + return -1; +} + +int spl_type_resolve_in(spl_type_ctx_t *tctx, int parent_idx, + const char *name) { + if (parent_idx < 0 || !name) + return -1; + spl_type_info_t *t = &vec_at(tctx->types, parent_idx); + vec_for(t->items, i) { + spl_type_item_t *it = &vec_at(t->items, i); + if (it->item_kind == ITEM_NESTED_TYPE && it->name && + strcmp(it->name, name) == 0) + return it->nested_type.type_idx; + } + return -1; +} + +/* ============================================================ + * Parse type from token stream + * + * Needs both tctx (for type construction/resolution) and + * spl_comp_t (for token stream access). + * ============================================================ */ + +int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) { spl_tok_t *tok = &vec_at(ctx->toks, ctx->tok_idx); /* Pointer type: '*T' */ if (tok->type == TOK_MUL) { ctx->tok_idx++; - spl_type_info_t *elem = spl_parse_type(ctx); - if (!elem) - return NULL; - return spl_type_ptr(elem); + int elem = spl_type_parse(tctx, ctx); + if (elem < 0) + return -1; + return spl_type_ptr(tctx, elem); } - /* Array type: '[N]T' */ + /* Array / Slice type: '[N]T' or '[]T' */ if (tok->type == TOK_L_BRACKET) { ctx->tok_idx++; tok = &vec_at(ctx->toks, ctx->tok_idx); - /* Check for empty brackets: []T (slice type) */ if (tok->type == TOK_R_BRACKET) { ctx->tok_idx++; - tok = &vec_at(ctx->toks, ctx->tok_idx); - spl_type_info_t *elem = spl_parse_type(ctx); - if (!elem) - return NULL; - return spl_type_slice(elem); + int elem = spl_type_parse(tctx, ctx); + if (elem < 0) + return -1; + return spl_type_slice(tctx, elem); } - /* Parse array length as integer literal */ int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); - return NULL; + return -1; } usize len = (usize)len_val; if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) { spl_comp_error(ctx, "expected ']'"); - return NULL; + return -1; } - ctx->tok_idx++; /* skip ] */ + ctx->tok_idx++; - tok = &vec_at(ctx->toks, ctx->tok_idx); - spl_type_info_t *elem = spl_parse_type(ctx); - if (!elem) - return NULL; - return spl_type_array(elem, len); + int elem = spl_type_parse(tctx, ctx); + if (elem < 0) + return -1; + return spl_type_array(tctx, elem, len); } - /* Inline struct/union/enum type: struct { field: type, ... } */ + /* Inline struct/union: struct { field: type, ... } */ if (tok->type == KW_STRUCT || tok->type == KW_UNION) { int is_union = (tok->type == KW_UNION); ctx->tok_idx++; - spl_type_info_t *t = is_union ? spl_type_union(NULL) : spl_type_struct(NULL); + int ti = is_union ? spl_type_union(tctx, NULL) : spl_type_struct(tctx, NULL); if (peek(ctx)->type == TOK_L_BRACE) { - advance(ctx); /* { */ + advance(ctx); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { spl_tok_t *ftok = advance(ctx); if (peek(ctx)->type == TOK_COLON) { - advance(ctx); /* : */ - spl_type_info_t *ftype = spl_parse_type(ctx); + advance(ctx); + int ftype = spl_type_parse(tctx, ctx); char fname[256]; spl_tok_copy_name(ftok, fname, sizeof(fname)); - spl_type_add_field(t, fname, ftype); + if (ftype >= 0) + spl_type_add_field(tctx, ti, fname, ftype); } if (peek(ctx)->type == TOK_COMMA) advance(ctx); @@ -425,28 +825,28 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_R_BRACE) advance(ctx); } - spl_type_compute_layout(t); - return t; + spl_type_compute_layout(tctx, ti); + return ti; } - /* Inline enum type: enum { A, B, C, ... } */ + /* Inline enum: enum { A, B, C, ... } */ if (tok->type == KW_ENUM) { ctx->tok_idx++; - spl_type_info_t *t = spl_type_enum(NULL); + int ti = spl_type_enum(tctx, NULL); if (peek(ctx)->type == TOK_L_BRACE) { - advance(ctx); /* { */ + advance(ctx); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { spl_tok_t *vtok = advance(ctx); if (peek(ctx)->type == TOK_COLON) { - advance(ctx); /* : */ - spl_type_info_t *dtype = spl_parse_type(ctx); + advance(ctx); + int dtype = spl_type_parse(tctx, ctx); char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); - spl_type_add_variant(t, vname, dtype); + spl_type_add_variant(tctx, ti, vname, dtype); } else { char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); - spl_type_add_variant(t, vname, NULL); + spl_type_add_variant(tctx, ti, vname, -1); } if (peek(ctx)->type == TOK_COMMA) advance(ctx); @@ -454,36 +854,31 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_R_BRACE) advance(ctx); } - spl_type_compute_layout(t); - return t; + spl_type_compute_layout(tctx, ti); + return ti; } /* Identifier: basic type or named type */ - if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) { + if (tok->type == TOK_IDENT || + ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) { const char *name = tok->lexeme; usize len = tok->len; - /* Check if it's a basic type name */ spl_type_t bt = name_to_basic_type(name, len); if (bt != SPL_TYPE_COUNT) { ctx->tok_idx++; - return spl_type_basic(bt); + return spl_type_basic(tctx, bt); } - /* Check named type definitions */ char id_buf[256]; usize cplen = len < 255 ? len : 255; memcpy(id_buf, name, cplen); id_buf[cplen] = '\0'; - spl_type_info_t *found = spl_resolve_type(ctx, id_buf); - if (found) { + int found = spl_type_resolve(tctx, id_buf); + if (found >= 0) { 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) @@ -500,55 +895,35 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { 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)) { + int qfound = spl_type_resolve(tctx, qualified); + if (qfound >= 0) { 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; + int bfound = spl_type_resolve(tctx, part); + if (bfound >= 0) { + found = bfound; + strncpy(id_buf, part, sizeof(id_buf) - 1); + ctx->tok_idx += 2; + } else { + break; + } } } return found; } - /* _ (wildcard / infer) */ if (tok->type == KW_ANY) { ctx->tok_idx++; - spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t)); - t->kind = TYPE_INFER; - t->resolved = 1; - return t; + int ti = spl_type_basic(tctx, SPL_VOID); /* placeholder */ + return ti; } spl_comp_error(ctx, "unknown type '%s'", id_buf); - return NULL; + return -1; } spl_comp_error(ctx, "expected type"); - return NULL; -} - -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; + return -1; } diff --git a/stage1/spl_type.h b/stage1/spl_type.h new file mode 100644 index 0000000..afd1aed --- /dev/null +++ b/stage1/spl_type.h @@ -0,0 +1,214 @@ +#ifndef __SPL_TYPE_H__ +#define __SPL_TYPE_H__ + +#include "../stage0/spl_ir.h" +#include "spl_lexer.h" + +/* ============================================================ + * Type kinds + * ============================================================ */ + +typedef enum { + TYPE_VOID, + TYPE_BASIC, + TYPE_PTR, + TYPE_ARRAY, + TYPE_SLICE, + TYPE_STRUCT, + TYPE_UNION, + TYPE_ENUM, + TYPE_NAME, + TYPE_COUNT, +} spl_type_kind_t; + +#define ENUM_TAG_SIZE 4 /* discriminator tag byte size */ + +/* ============================================================ + * Item kinds — unified member storage + * ============================================================ */ + +typedef enum { + ITEM_FIELD, /* aggregate_field : struct/union field */ + ITEM_VARIANT, /* enum_field : enum variant */ + ITEM_METHOD, /* method : type method */ + ITEM_NESTED_TYPE, /* nested_type : child type decl */ +} spl_type_item_kind_t; + +/* ============================================================ + * Type item — one member/variant/method/nested-type + * ============================================================ */ + +typedef struct { + const char *name; + spl_type_item_kind_t item_kind; + union { + struct { + int type_idx; /* field type */ + usize offset; /* byte offset (for array: array_len) */ + } aggregate_field; + struct { + int type_idx; /* data type, -1 if none */ + isize value; /* discriminator value */ + } enum_field; + struct { + int func_idx; /* index in spl_comp_t.funcs */ + } method; + struct { + int type_idx; /* child type index */ + } nested_type; + }; +} spl_type_item_t; + +typedef VEC(spl_type_item_t) spl_type_item_vec_t; + +/* ============================================================ + * Type info — one type definition + * + * PTR : items[0].aggregate_field.type_idx = elem + * ARRAY : items[0].aggregate_field.type_idx = elem, + * items[0].aggregate_field.offset = len + * SLICE : same as PTR + * STRUCT : items = ITEM_FIELD for each field + * UNION : items = ITEM_FIELD for each field + * ENUM : items = ITEM_VARIANT for each variant + * NAME : items[0].aggregate_field.type_idx = alias target + * ============================================================ */ + +typedef struct spl_type_info { + char *name; + spl_type_kind_t kind; + spl_type_t basic_type; /* for TYPE_BASIC */ + spl_type_item_vec_t items; + usize byte_size; /* total byte size (cached) */ + usize slot_count; /* stack slot count (cached) */ + int resolved; /* layout computed */ + int parent_type_idx; /* enclosing type, -1 for root */ +} spl_type_info_t; + +typedef VEC(spl_type_info_t) spl_type_info_vec_t; + +/* ============================================================ + * Type context — type arena + namespace + * + * types[0] = root (compilation unit type) + * Types form a tree via parent_type_idx on each type info. + * current_type_idx tracks the innermost type being parsed. + * ============================================================ */ + +typedef struct { + spl_type_info_vec_t types; + MAP(const char *, int) type_map; /* name → type_idx 加速查找 */ + int root_type_idx; /* 编译单元自身 = 0 */ + int current_type_idx; /* 当前作用域类型 */ + int basic_cache[SPL_TYPE_COUNT]; /* memoized basic-type → type_idx */ +} spl_type_ctx_t; + +/* ============================================================ + * Lifecycle + * ============================================================ */ + +void spl_type_ctx_init(spl_type_ctx_t *tctx); +void spl_type_ctx_drop(spl_type_ctx_t *tctx); + +/* ============================================================ + * Constructors — all return type_idx (index into tctx->types) + * ============================================================ */ + +int spl_type_basic(spl_type_ctx_t *tctx, spl_type_t bt); +int spl_type_ptr(spl_type_ctx_t *tctx, int elem_type_idx); +int spl_type_array(spl_type_ctx_t *tctx, int elem_type_idx, usize len); +int spl_type_slice(spl_type_ctx_t *tctx, int elem_type_idx); +int spl_type_struct(spl_type_ctx_t *tctx, const char *name); +int spl_type_union(spl_type_ctx_t *tctx, const char *name); +int spl_type_enum(spl_type_ctx_t *tctx, const char *name); +int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx); + +/* ============================================================ + * Item management — add members to aggregate types + * ============================================================ */ + +void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, + int field_type_idx); +void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, + int data_type_idx); +void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, + int func_idx); +void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name, + int child_type_idx); + +/* ============================================================ + * Layout — compute byte_size / slot_count, set resolved + * ============================================================ */ + +void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx); + +/* ============================================================ + * Accessors — get info from type_idx + * ============================================================ */ + +spl_type_kind_t spl_type_kind(spl_type_ctx_t *tctx, int type_idx); +spl_type_t spl_type_basic_type(spl_type_ctx_t *tctx, int type_idx); +const char *spl_type_name(spl_type_ctx_t *tctx, int type_idx); +usize spl_type_size(spl_type_ctx_t *tctx, int type_idx); +usize spl_type_slot_count(spl_type_ctx_t *tctx, int type_idx); +usize spl_type_elem_stride(spl_type_ctx_t *tctx, int elem_type_idx); + +/* Element type for PTR / ARRAY / SLICE / NAME (reads items[0]) */ +int spl_type_elem_type(spl_type_ctx_t *tctx, int type_idx); +usize spl_type_array_len(spl_type_ctx_t *tctx, int type_idx); + +/* Scalar: fits in one slot, supports ==/!= directly */ +int spl_type_is_scalar(spl_type_ctx_t *tctx, int type_idx); + +/* Integer type check */ +int spl_type_is_integer(spl_type_t bt); + +/* True for types that support inline literal {} syntax */ +int spl_type_has_inline_literal(spl_type_ctx_t *tctx, int type_idx); + +/* True for multi-slot type — alias for needs_multi_slot for semantic clarity */ +int spl_type_is_aggregate(spl_type_ctx_t *tctx, int type_idx); + +/* Multi-slot: aggregate type needing >1 stack slots */ +int spl_type_needs_multi_slot(spl_type_ctx_t *tctx, int type_idx); + +/* Follow alias chain to underlying type */ +int spl_type_resolve_underlying(spl_type_ctx_t *tctx, int type_idx); + +/* SPL_IR opcode type tag for LOAD/STORE */ +spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx); + +/* Debug string */ +const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx); + +/* ============================================================ + * Item iteration — filter by item_kind + * ============================================================ */ + +int spl_type_item_count(spl_type_ctx_t *tctx, int type_idx, + spl_type_item_kind_t kind); +spl_type_item_t *spl_type_item_at(spl_type_ctx_t *tctx, int type_idx, + int item_index); +/* Returns pointer to first item of given kind, with *count set */ +spl_type_item_t *spl_type_first_of_kind(spl_type_ctx_t *tctx, int type_idx, + spl_type_item_kind_t kind, int *count); +/* Get raw items vec for direct iteration (avoids double-lookup) */ +spl_type_item_vec_t *spl_type_items(spl_type_ctx_t *tctx, int type_idx); + +/* ============================================================ + * Type resolution — name → type_idx + * ============================================================ */ + +/* Walk current → parent chain, then type_map */ +int spl_type_resolve(spl_type_ctx_t *tctx, const char *name); +/* Resolve within a specific parent type's ITEM_NESTED_TYPE items */ +int spl_type_resolve_in(spl_type_ctx_t *tctx, int parent_idx, const char *name); + +/* ============================================================ + * Forward declaration for spl_parse_type + * ============================================================ */ + +struct spl_comp; +int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx); + +#endif /* __SPL_TYPE_H__ */