stage1 重构代码
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "spl_comp.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- 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;
|
||||
}
|
||||
|
||||
@@ -10,108 +10,22 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ============================================================
|
||||
* 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);
|
||||
|
||||
203
stage1/spl_emit.c
Normal file
203
stage1/spl_emit.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/* spl_emit.c — IR emission: frame allocator + Layer 1/2 ops */
|
||||
|
||||
#include "spl_emit.h"
|
||||
#include "spl_type.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ============================================================
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
stage1/spl_emit.h
Normal file
138
stage1/spl_emit.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef __SPL_EMIT_H__
|
||||
#define __SPL_EMIT_H__
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "spl_type.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* ============================================================
|
||||
* 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__ */
|
||||
1011
stage1/spl_expr.c
1011
stage1/spl_expr.c
File diff suppressed because it is too large
Load Diff
@@ -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) \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
214
stage1/spl_type.h
Normal file
214
stage1/spl_type.h
Normal file
@@ -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__ */
|
||||
Reference in New Issue
Block a user