Compare commits

...

10 Commits

Author SHA1 Message Date
zzy
463177d3be stage1 修改部分测试 完成17-19测试 2026-07-07 12:11:49 +08:00
zzy
1df3e3bcb4 stage1 将数组和聚合类型使用byte offset而不是slot idx 2026-07-07 11:38:31 +08:00
zzy
3cf11f922e stage1 优化代码 完成16测试 2026-07-07 10:40:30 +08:00
zzy
69cea030dc stage1 完成15测试 2026-07-06 21:26:27 +08:00
zzy
0892c084ee stage1 完成14测试 2026-07-06 15:18:17 +08:00
zzy
ad473f245c stage1 完成11-13测试 2026-07-06 13:00:55 +08:00
zzy
0182b8ed5c stage1 完成09,10测试 2026-07-06 10:52:31 +08:00
zzy
777b6b42d1 stage1 完成07,08测试 2026-07-06 10:44:01 +08:00
zzy
5dadf6d6ee stage1 重构到 USIZE, 使得 vm 支持 usize, isize 2026-07-06 00:07:39 +08:00
zzy
67c8a137dd stage1 实现06测试 2026-07-05 22:47:00 +08:00
25 changed files with 2285 additions and 597 deletions

View File

@@ -4,10 +4,20 @@ vm = [
"stage0/spl_vm.c",
]
splc0_part = [
"stage1/spl_comp.c",
"stage1/spl_lexer.c",
"stage1/spl_type.c",
"stage1/spl_parser.c",
"stage1/spl_lex_util.c",
"stage1/spl_expr.c",
"stage1/spl_stmt.c",
]
exe = {
"spl_cli": ["stage0/spl_cli.c"] + vm,
"splc_cli": ["stage1/splc_cli.c"] + vm + ["stage1/spl_comp.c", "stage1/spl_lexer.c", "stage1/spl_type.c", "stage1/spl_parser.c", "stage1/spl_lex_util.c", "stage1/spl_expr.c", "stage1/spl_stmt.c"],
"splc0": ["stage1/splc0.c"] + vm + ["stage1/spl_comp.c", "stage1/spl_lexer.c", "stage1/spl_type.c", "stage1/spl_parser.c", "stage1/spl_lex_util.c", "stage1/spl_expr.c", "stage1/spl_stmt.c"],
"splc_cli": ["stage1/splc_cli.c"] + vm + splc0_part,
"splc0": ["stage1/splc0.c"] + vm + splc0_part,
"test": ["stage0/test_spl_vm.c"] + vm,
"spl_disasm": ["stage0/spl_disasm.c"] + vm,
}
@@ -35,7 +45,7 @@ pipeline = {
}
spl = {
"splc1": {"src": "stage1/splc1.spl", "pipeline": "splc0r"},
"splc1": {"src": "stage2/splc1.spl", "pipeline": "splc0r"},
"splc2": {"src": "stage2/splc2.spl", "pipeline": "splc1r"},
"splc3": {"src": "stage3/splc3.spl", "pipeline": "splc2r"},
"splc4": {"src": "stage4/splc4.spl", "pipeline": "splc3r"},

View File

@@ -207,7 +207,7 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
unsigned char *buf, *p;
int i;
spl_val_t i;
size_t total;
spl_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
usize nlen, pad;
@@ -458,6 +458,10 @@ const char *spl_type_name(spl_type_t type) {
return "f32";
case SPL_F64:
return "f64";
case SPL_USIZE:
return "usize";
case SPL_ISIZE:
return "isize";
case SPL_PTR:
return "ptr";
default:

View File

@@ -84,7 +84,7 @@ typedef enum {
X(SPL_RET, "ret", 0, 0, 0, "return from function") \
/* 栈帧局部变量 */ \
X(SPL_ALLOC, "alloc", 1, 0, 0, "allocate imm zero-slots on stack") \
X(SPL_LADDR, "laddr", 1, 0, 1, "push address of local at fp+imm") \
X(SPL_LADDR, "laddr", 1, 0, 1, "push address of local at fp + imm bytes") \
X(SPL_GADDR, "gaddr", 1, 0, 1, "push address of global at gp + imm") \
/* 间接内存访问 */ \
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
@@ -94,7 +94,7 @@ typedef enum {
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
/* 调试 */ \
X(SPL_BK, "breakpoint", 0, 0, 0, "break point when exec will stop run") \
X(SPL_DBG, "dbg", 0, 1, 1, "print top of stack as hex")
X(SPL_DBG, "dbg", 0, 0, 0, "print VM debug info (stack, backtrace, locals)")
/* clang-format on */

View File

@@ -8,6 +8,7 @@
*/
#include "spl_syscall.h"
#include "include/core_map.h"
#include "include/core_vec.h"
#include "spl_ir.h"
#include "spl_vm.h"
@@ -121,9 +122,15 @@ static spl_val_t vm_fsize(int nargs, spl_val_t *args) {
static spl_val_t vm_read_file(int nargs, spl_val_t *args) {
CHECK_NARGS("vm_read_file", 1);
const char *path = (const char *)(uintptr_t)args[0];
if (path == nullptr) {
fprintf(stderr, "filepath can't be null");
return 1;
}
FILE *f = fopen(path, "rb");
if (!f)
return 0;
if (!f) {
fprintf(stderr, "filepath %s can't be open", path);
return 1;
}
fseek(f, 0, SEEK_END);
long sz = ftell(f);
fseek(f, 0, SEEK_SET);
@@ -173,7 +180,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
int arg_idx = 0;
char tmp_buf[32];
memset(tmp_buf, 0, sizeof(tmp_buf));
for (int i = 0; i < fmt_len; ++i) {
for (usize i = 0; i < fmt_len; ++i) {
if (fmt[i] != '%') {
vec_push(buffer, fmt[i]);
continue;
@@ -185,7 +192,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
switch (fmt[i]) {
case 'd':
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]);
for (int j = 0; j < strlen(tmp_buf); ++j) {
for (usize j = 0; j < strlen(tmp_buf); ++j) {
vec_push(buffer, tmp_buf[j]);
}
break;
@@ -193,7 +200,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
vec_push(buffer, (char)args[arg_idx]);
break;
case 's':
for (int j = 0; j < strlen((const char *)args[arg_idx]); ++j) {
for (usize j = 0; j < strlen((const char *)args[arg_idx]); ++j) {
vec_push(buffer, ((const char *)args[arg_idx])[j]);
}
break;

View File

@@ -76,10 +76,10 @@ static int spl_type_size(spl_type_t t) {
do { \
if (vm->sp >= vm->config.max_stack_depth) \
VM_ERROR("stack overflow"); \
vm->stacks.data[vm->sp++] = (spl_val_t)(v); \
vm->stacks.data[(vm->sp)++] = (spl_val_t)(v); \
} while (0)
#define POP() vm->stacks.data[--vm->sp]
#define POP() vm->stacks.data[--(vm->sp)]
/* ================================================================
* Type-dispatch macros for arithmetic / comparison
@@ -140,6 +140,8 @@ static int spl_type_size(spl_type_t t) {
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
@@ -199,6 +201,8 @@ static int spl_type_size(spl_type_t t) {
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
default: \
@@ -238,6 +242,8 @@ static int spl_type_size(spl_type_t t) {
_r = (spl_val_t)((uint64_t)_a OP(uint64_t) _b); \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
@@ -274,6 +280,8 @@ static int spl_type_size(spl_type_t t) {
_r = (int64_t)_a OP(int64_t) _b; \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
case SPL_F32: { \
@@ -325,7 +333,9 @@ static int spl_type_size(spl_type_t t) {
_r = (int64_t)_a OP(int64_t) _b; \
break; \
case SPL_U64: \
_r = (int64_t)_a OP(int64_t) _b; \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
case SPL_F32: { \
float _fa, _fb; \
@@ -376,6 +386,8 @@ static int spl_type_size(spl_type_t t) {
_r = (uint64_t)_a OP(uint64_t) _b; \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
@@ -500,6 +512,8 @@ LONG WINAPI UnhandledExceptionFilterImpl(EXCEPTION_POINTERS *pExceptionInfo) {
void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
#ifdef _WIN32
SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl);
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
if (!vm)
return;
@@ -627,7 +641,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
return -1;
prog = vm->prog;
if (vm->ip < 0 || vm->ip >= vec_size(prog->insns)) {
if (vm->ip >= vec_size(prog->insns)) {
fprintf(stderr, "vm: ip=%zd out of bounds\n", vm->ip);
vm->exit_code = 1;
return -1;
@@ -673,7 +687,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
case SPL_PICK: {
isize _idx = ins->imm;
if (_idx >= vm->sp)
if ((usize)_idx >= vm->sp)
VM_ERROR("PICK: index out of range");
PUSH(vm->stacks.data[vm->sp - 1 - _idx]);
break;
@@ -875,7 +889,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
}
case SPL_LADDR:
PUSH(vm->stacks.data + vm->fp + ins->imm);
PUSH((spl_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
break;
case SPL_GADDR: {
@@ -983,8 +997,23 @@ int spl_vm_run_once(spl_vm_t *vm) {
/* ========== Debug ========== */
case SPL_DBG: {
fprintf(stderr, "---DGB: current ip %zu---\n", vm->ip);
spl_vm_dump_instr(vm, vm->ip - 6);
spl_vm_dump_instr(vm, vm->ip - 5);
spl_vm_dump_instr(vm, vm->ip - 4);
spl_vm_dump_instr(vm, vm->ip - 3);
spl_vm_dump_instr(vm, vm->ip - 2);
spl_vm_dump_instr(vm, vm->ip - 1);
spl_vm_dump_instr(vm, vm->ip);
spl_vm_dump_instr(vm, vm->ip + 1);
spl_vm_backtrace(vm, vm->fp);
return 2; /* breakpoint: pause execution */
spl_vm_stackdump(vm, vm->sp);
fprintf(stderr, "---DGB END ---\n");
break;
}
case SPL_BK: {
return 0; /* breakpoint: pause execution */
}
default:
@@ -1045,7 +1074,7 @@ void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp) {
fprintf(stderr, " stack (sp=%zd, fp=%zd):\n", sp, vm->fp);
spl_val_t start = sp > 16 ? sp - 16 : 0;
for (spl_val_t i = start; i < sp; i++) {
fprintf(stderr, " [%zd] = 0x%016zx (%zd)\n", i, vm->stacks.data[i], vm->stacks.data[i]);
fprintf(stderr, " [%3zd] = 0x%016zx (%zd)\n", i, vm->stacks.data[i], vm->stacks.data[i]);
}
}
@@ -1058,7 +1087,7 @@ int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp) {
spl_val_t _saved_ip = vm->frames.data[i].saved_ip;
spl_val_t _saved_fp = vm->frames.data[i].saved_fp;
const char *_fn = func_name_by_ip(vm->prog, _saved_ip - 1);
fprintf(stderr, " [%zd] %s (fp=%zd, ip=%zd, args=%zd)\n", i, _fn, _saved_fp, _saved_ip,
fprintf(stderr, " [%3zd] %s (fp=%zd, ip=%zd, args=%zd)\n", i, _fn, _saved_fp, _saved_ip,
vm->frames.data[i].nargs);
}
const char *_cur = func_name_by_ip(vm->prog, vm->ip);

View File

@@ -1,10 +1,10 @@
/* spl_comp.c — SPL compiler main logic and codegen helpers */
#include "spl_comp.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
/* ---- Compiler context init/drop ---- */
@@ -17,14 +17,14 @@ void spl_comp_init(spl_comp_t *ctx) {
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
spl_prog_init(&ctx->prog);
ctx->error_msg[0] = '\0';
ctx->current_type_name = NULL;
}
void spl_comp_drop(spl_comp_t *ctx) {
if (!ctx) return;
if (!ctx)
return;
spl_tok_vec_drop(&ctx->toks);
vec_for(ctx->scopes, i) {
vec_free(vec_at(ctx->scopes, i).vars);
}
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
vec_free(ctx->scopes);
vec_free(ctx->funcs);
/* Free type defs — complex, leak for now in bootstrap */
@@ -38,9 +38,7 @@ 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_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
vec_free(ctx->scopes);
vec_init(ctx->scopes);
ctx->scope_depth = 0;
@@ -49,7 +47,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
ctx->error_msg[0] = '\0';
ctx->current_func_idx = -1;
ctx->current_ret_type = NULL;
ctx->current_local_slot = 0;
ctx->current_local_bytes = 0;
ctx->in_loop = 0;
ctx->break_patch_count = 0;
ctx->break_patch_cap = 0;
@@ -57,6 +55,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
ctx->defer_count = 0;
ctx->next_gdata_idx = 0;
ctx->addr_of_mode = 0;
ctx->current_type_name = NULL;
free(ctx->break_patches);
ctx->break_patches = NULL;
}
@@ -86,13 +85,9 @@ spl_val_t spl_emit_jmp(spl_comp_t *ctx) {
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_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);
}
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) */
@@ -113,9 +108,7 @@ void spl_push_scope(spl_comp_t *ctx) {
void spl_pop_scope(spl_comp_t *ctx) {
if (vec_size(ctx->scopes) > 0) {
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
vec_for(scope->vars, i) {
free(vec_at(scope->vars, i).name);
}
vec_for(scope->vars, i) { free(vec_at(scope->vars, i).name); }
vec_free(scope->vars);
ctx->scope_depth--;
ctx->scopes.size--;
@@ -133,17 +126,21 @@ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, in
var.type = type;
var.is_const = is_const;
var.depth = ctx->scope_depth;
var.slot = ctx->current_local_slot;
var.offset = ctx->current_local_bytes;
usize slot_count = spl_type_slot_count(type);
ctx->current_local_slot += (int)slot_count;
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;
/* 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);
}
return var.slot;
return var.offset;
}
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name) {
@@ -159,15 +156,15 @@ spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name) {
return NULL;
}
int spl_get_var_slot(spl_comp_t *ctx, const char *name) {
int spl_get_var_offset(spl_comp_t *ctx, const char *name) {
spl_var_info_t *v = spl_lookup_var(ctx, name);
return v ? v->slot : -1;
return v ? v->offset : -1;
}
/* ---- Function management ---- */
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type,
int nparams, int is_extern, int is_pub) {
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams,
int is_extern, int is_pub) {
spl_func_info_t fi;
memset(&fi, 0, sizeof(fi));
fi.name = strdup(name);
@@ -197,7 +194,7 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) {
/* ---- String/data management ---- */
int spl_add_string(spl_comp_t *ctx, const char *str) {
return spl_add_global_data(ctx, (void*)str, strlen(str) + 1);
return spl_add_global_data(ctx, (void *)str, strlen(str) + 1);
}
int spl_add_global_data(spl_comp_t *ctx, void *data, usize size) {
@@ -207,19 +204,67 @@ int spl_add_global_data(spl_comp_t *ctx, void *data, usize size) {
/* ---- Defer ---- */
void spl_emit_defer(spl_comp_t *ctx) {
/* Record current ip for later patching */
if (ctx->defer_count < DEFER_MAX) {
ctx->defer_addrs[ctx->defer_count++] = vec_size(ctx->prog.insns);
}
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_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->depth = ctx->scope_depth;
e->count_at_decl = ctx->defer_count;
ctx->defer_count++;
}
void spl_emit_defer_epilogue(spl_comp_t *ctx) {
/* Emit deferコード in reverse order */
for (int i = ctx->defer_count - 1; i >= 0; i--) {
/* The code after defer_addrs[i] is the defer body */
/* We need to JMP over it, but the body is inline */
/* This is a simplified approach — just mark the range */
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);
}
/* === 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);
/* 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);
}
}
/* 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) {
ctx->defer_stack[new_count++] = ctx->defer_stack[i];
}
}
ctx->defer_count = new_count;
}
/* ---- Register runtime natives ---- */
@@ -247,6 +292,7 @@ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) {
/* Phase 2-4: Parse and codegen */
spl_parse_prog(ctx);
if (ctx->has_error) return -1;
if (ctx->has_error)
return -1;
return 0;
}

View File

@@ -21,6 +21,7 @@ typedef enum {
TYPE_ARRAY,
TYPE_SLICE,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM,
TYPE_ENUM_VARIANT, /* enum variant with data */
TYPE_NAME, /* named alias */
@@ -46,6 +47,13 @@ typedef struct {
} 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 */
@@ -54,6 +62,7 @@ struct spl_type_info {
char *name; /* type name for TYPE_NAME/STRUCT/ENUM */
spl_field_vec_t fields; /* for TYPE_STRUCT */
spl_enum_variant_vec_t variants; /* for TYPE_ENUM */
spl_method_info_vec_t methods; /* methods */
usize byte_size; /* total byte size (cached) */
usize slot_count; /* stack slot count */
int is_pub; /* public visibility */
@@ -66,12 +75,16 @@ 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);
@@ -83,7 +96,7 @@ spl_type_info_t *spl_type_clone(spl_type_info_t *t);
typedef struct spl_var_info {
char *name;
spl_type_info_t *type;
int slot; /* stack slot offset from fp */
int offset; /* byte offset from fp */
int is_const;
int depth; /* scope depth */
} spl_var_info_t;
@@ -114,6 +127,13 @@ typedef VEC(spl_func_info_t) spl_func_info_vec_t;
#define COMP_ERROR_MAX 256
#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 */
} spl_defer_entry_t;
typedef struct {
/* Lexer output */
spl_tok_vec_t toks;
@@ -141,7 +161,8 @@ typedef struct {
/* Current function context */
int current_func_idx;
spl_type_info_t *current_ret_type;
int current_local_slot; /* next free local slot */
int current_local_bytes; /* next free local byte offset */
const char *current_type_name; /* name of type whose body we're parsing (for method short-name lookup) */
/* Loop context for break/continue */
int in_loop;
@@ -151,7 +172,7 @@ typedef struct {
usize continue_target; /* ip to jump to for continue */
/* Defer stack */
usize defer_addrs[DEFER_MAX];
spl_defer_entry_t defer_stack[DEFER_MAX];
int defer_count;
/* Global data index for string literals */
@@ -224,7 +245,7 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr);
/* Variable management */
int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const);
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name);
int spl_get_var_slot(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 is_extern, int is_pub);
@@ -243,10 +264,14 @@ void spl_comp_register(spl_prog_t *prog);
/* Defer */
void spl_emit_defer(spl_comp_t *ctx);
void spl_emit_defer_epilogue(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);
/* Shared token helpers (defined in spl_parser.c) */
spl_tok_t *peek(spl_comp_t *ctx);
spl_tok_t *advance(spl_comp_t *ctx);
#endif /* __SPL_COMP_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,32 @@
/* spl_lex_util.c — Lexer utility functions */
#include "spl_lexer.h"
#include "spl_lex_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) {
advance(ctx);
return 1;
}
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type),
spl_tok_type_name(peek(ctx)->type));
return 0;
}
int match(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) {
advance(ctx);
return 1;
}
return 0;
}
void skip_nl(spl_comp_t *ctx) {
while (peek(ctx)->type == TOK_ENDLINE)
advance(ctx);
}
const char *spl_tok_type_name(spl_tok_type_t type) {
switch (type) {
#define X(name, enum_name, dummy) \
@@ -33,9 +55,7 @@ void spl_tok_dump(spl_tok_t *tok) {
buf[i] = ' ';
}
printf("%s:%zu:%zu %-20s '%s'",
tok->fname ? tok->fname : "",
tok->line, tok->col,
printf("%s:%zu:%zu %-20s '%s'", tok->fname ? tok->fname : "", tok->line, tok->col,
spl_tok_type_name(tok->type), buf);
if (tok->type == TOK_INT_LITERAL) {
@@ -67,3 +87,12 @@ void spl_tok_vec_drop(spl_tok_vec_t *toks) {
vec_free(*toks);
}
}
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size) {
if (!tok || !buf || buf_size == 0)
return 0;
usize nlen = tok->len < buf_size - 1 ? tok->len : buf_size - 1;
memcpy(buf, tok->lexeme, nlen);
buf[nlen] = '\0';
return nlen;
}

12
stage1/spl_lex_util.h Normal file
View File

@@ -0,0 +1,12 @@
/* spl_lex_util.h — Shared token helper utilities */
#ifndef __SPL_LEX_UTIL_H__
#define __SPL_LEX_UTIL_H__
#include "spl_comp.h"
/* Token matching helpers */
int expect(spl_comp_t *ctx, spl_tok_type_t type);
int match(spl_comp_t *ctx, spl_tok_type_t type);
void skip_nl(spl_comp_t *ctx);
#endif /* __SPL_LEX_UTIL_H__ */

View File

@@ -2,7 +2,6 @@
#include "spl_lexer.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

View File

@@ -135,6 +135,7 @@ const char *spl_tok_type_name(spl_tok_type_t type);
void spl_tok_dump(spl_tok_t *tok);
void spl_tok_vec_dump(spl_tok_vec_t *toks);
void spl_tok_vec_drop(spl_tok_vec_t *toks);
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size);
/* Decode escape sequence, advance *s past it. Returns 0 on success. */
int spl_decode_escape(const char **s, char *out);

View File

@@ -1,29 +1,18 @@
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
#include "spl_comp.h"
#include <stdio.h>
#include <stdlib.h>
#include "spl_lex_util.h"
#include <string.h>
static spl_tok_t *peek(spl_comp_t *ctx) {
return &vec_at(ctx->toks, ctx->tok_idx);
}
spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
static spl_tok_t *advance(spl_comp_t *ctx) {
spl_tok_t *advance(spl_comp_t *ctx) {
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
if (t->type != TOK_EOF) ctx->tok_idx++;
if (t->type != TOK_EOF)
ctx->tok_idx++;
return t;
}
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) { advance(ctx); return 1; }
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type), spl_tok_type_name(peek(ctx)->type));
return 0;
}
static void skip_nl(spl_comp_t *ctx) {
while (peek(ctx)->type == TOK_ENDLINE) advance(ctx);
}
/* ============================================================
* Parse function definition
@@ -31,6 +20,48 @@ static void skip_nl(spl_comp_t *ctx) {
* 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. */
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);
ctx->current_func_idx = fi;
ctx->current_ret_type = ret_type;
ctx->current_local_bytes = 0;
spl_push_scope(ctx);
for (int i = 0; i < nparams; i++) {
spl_declare_var(ctx, pnames[i], ptypes[i], 0);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE) {
advance(ctx); /* { */
skip_nl(ctx);
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_parse_stmt(ctx);
skip_nl(ctx);
}
spl_patch(ctx, alloc_addr, ctx->current_local_bytes / (int)sizeof(spl_val_t) - nparams);
expect(ctx, TOK_R_BRACE);
}
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
spl_prog_end_func(&ctx->prog, fi);
ctx->current_func_idx = -1;
ctx->current_ret_type = NULL;
return fi;
}
static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
advance(ctx); /* fn */
skip_nl(ctx);
@@ -38,7 +69,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
spl_tok_t *fname_tok = advance(ctx);
char fn_name[256];
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255;
memcpy(fn_name, fname_tok->lexeme, fnl); fn_name[fnl] = '\0';
memcpy(fn_name, fname_tok->lexeme, fnl);
fn_name[fnl] = '\0';
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
@@ -65,8 +97,15 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); continue; }
if (peek(ctx)->type == TOK_ELLIPSIS) { advance(ctx); skip_nl(ctx); }
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_ELLIPSIS) {
advance(ctx);
skip_nl(ctx);
}
break;
}
}
@@ -77,7 +116,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
spl_type_info_t *ret_type = spl_type_basic(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);
if (!ret_type)
ret_type = spl_type_basic(SPL_VOID);
skip_nl(ctx);
}
@@ -88,7 +128,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
int found = -1;
vec_for(ctx->prog.natives, ni) {
if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) {
found = (int)ni; break;
found = (int)ni;
break;
}
}
if (found < 0) {
@@ -99,7 +140,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
nat.impl_fn = NULL; /* resolved by VM at runtime */
vec_push(ctx->prog.natives, nat);
}
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
return;
}
@@ -111,79 +153,73 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
skip_nl(ctx);
/* Declare function in prog */
int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub);
ctx->current_func_idx = fi;
ctx->current_ret_type = ret_type;
ctx->current_local_slot = 0;
/* Push function scope for params + locals */
spl_push_scope(ctx);
/* Declare parameters as variables in the function scope */
for (int i = 0; i < nparams; i++) {
spl_declare_var(ctx, pnames[i], ptypes[i], 0);
}
/* Parse body */
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE) {
advance(ctx); /* { */
skip_nl(ctx);
/* Emit ALLOC placeholder to reserve stack space for locals.
* Without ALLOC, PUSH in expressions overwrites variable slots
* because the stack and locals share the same memory. */
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_parse_stmt(ctx);
skip_nl(ctx);
}
/* Patch ALLOC to local slot count only (excludes param slots) */
spl_patch(ctx, alloc_addr, ctx->current_local_slot - nparams);
expect(ctx, TOK_R_BRACE);
}
spl_pop_scope(ctx);
/* Emit implicit RET for void functions without explicit return */
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
/* End function */
spl_prog_end_func(&ctx->prog, fi);
ctx->current_func_idx = -1;
ctx->current_ret_type = NULL;
/* Use shared helper for function body parsing */
parse_fn_body(ctx, fn_name, ret_type, nparams, pnames, ptypes, is_pub);
}
/* ============================================================
* Parse type declaration
* type Name = struct/enum { ... };
* type Name = ExistingType;
* Parse struct/union body (shared for struct and union containers)
*
* Body supports:
* var name: type; — field declarations
* name: type, — field declarations (old-style)
* type Name = ...; — nested type declarations
* fn name(...) type { } — methods
* ============================================================ */
void parse_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */
spl_tok_t *name_tok = advance(ctx);
char tname[256];
usize tnl = name_tok->len < 255 ? name_tok->len : 255;
memcpy(tname, name_tok->lexeme, tnl); tname[tnl] = '\0';
skip_nl(ctx);
expect(ctx, TOK_ASSIGN);
skip_nl(ctx);
if (peek(ctx)->type == KW_STRUCT) {
advance(ctx);
spl_type_info_t *st = spl_type_struct(tname);
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE) {
static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
if (peek(ctx)->type != TOK_L_BRACE)
return;
advance(ctx); /* { */
/* === Pass 1: Parse all nested type declarations first ===
* This allows field declarations to reference types defined later in the body. */
{
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_TYPE && depth == 1) {
parse_type_decl(ctx);
} else {
advance(ctx);
}
}
/* Reset to start of body for second pass */
ctx->tok_idx = saved;
}
/* === Pass 2: Parse fields and methods === */
{
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
skip_nl(ctx);
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) {
advance(ctx);
break;
}
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
/* Nested type — already parsed in pass 1, skip by re-parsing */
parse_type_decl(ctx);
} else if (tt == KW_VAR && depth == 1) {
/* var name: type; */
advance(ctx); /* var */
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_tok_t *ftok = advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
@@ -192,24 +228,178 @@ void parse_type_decl(spl_comp_t *ctx) {
spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl); fname[fnl] = '\0';
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
spl_type_add_field(st, fname, ftype);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); }
}
if (peek(ctx)->type == TOK_R_BRACE) advance(ctx);
}
spl_type_compute_layout(st);
map_put(ctx->type_defs, strdup(tname), st);
} else if (peek(ctx)->type == KW_ENUM) {
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
advance(ctx);
spl_type_info_t *et = spl_type_enum(tname);
} else if (tt == TOK_IDENT && depth == 1) {
/* Old-style field: name: type, */
spl_tok_t *ftok = advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE) {
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
spl_type_add_field(st, 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) {
/* Method — parse properly using parse_fn_body */
advance(ctx); /* fn */
skip_nl(ctx);
spl_tok_t *mname_tok = advance(ctx);
char mname[256];
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
memcpy(mname, mname_tok->lexeme, mnl);
mname[mnl] = '\0';
/* Build qualified name: TypeName.method_name */
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", st->name ? st->name : "anon",
mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
/* Parse parameters */
int nparams = 0;
enum { MAX_PARAMS = 64 };
char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS];
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
usize pnl = pname->len < 255 ? pname->len : 255;
memcpy(pnames[nparams], pname->lexeme, pnl);
pnames[nparams][pnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
}
expect(ctx, TOK_R_PAREN);
skip_nl(ctx);
/* Return type (default: void) */
spl_type_info_t *ret_type = spl_type_basic(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);
skip_nl(ctx);
}
/* Set current_type_name for short-name resolution inside method body */
const char *saved_type_name = ctx->current_type_name;
ctx->current_type_name = st->name;
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
{
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
f->param_names = calloc(nparams, sizeof(char *));
for (int i = 0; i < nparams; i++) {
f->param_types[i] = ptypes[i];
f->param_names[i] = strdup(pnames[i]);
}
}
ctx->current_type_name = saved_type_name;
spl_type_add_method(st, mname, fi);
} else {
advance(ctx);
}
}
}
spl_type_compute_layout(st);
}
/* ============================================================
* Parse enum body
*
* Body supports:
* Name — simple variant
* Name: Type — variant with data
* var name: type; — field-style variant
* type Name = ...; — nested type declarations
* fn name(...) type { } — methods (skipped)
* ============================================================ */
static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
if (peek(ctx)->type != TOK_L_BRACE)
return;
advance(ctx); /* { */
/* === Pass 1: Parse all nested type declarations first === */
{
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_TYPE && depth == 1) {
parse_type_decl(ctx);
} else {
advance(ctx);
}
}
ctx->tok_idx = saved;
}
/* === Pass 2: Parse variants and methods === */
{
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
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) {
advance(ctx);
break;
}
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
/* Already parsed in pass 1, re-parse to skip */
parse_type_decl(ctx);
} else if (tt == TOK_IDENT && depth == 1) {
/* Variant: Name or Name: Type */
spl_tok_t *vtok = advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
@@ -218,22 +408,149 @@ void parse_type_decl(spl_comp_t *ctx) {
spl_type_info_t *dtype = spl_parse_type(ctx);
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl); vname[vnl] = '\0';
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(et, vname, dtype);
} else {
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl); vname[vnl] = '\0';
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(et, vname, NULL);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); }
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
advance(ctx);
} else if (tt == KW_FN && depth == 1) {
/* Method — parse properly using parse_fn_body */
advance(ctx); /* fn */
skip_nl(ctx);
spl_tok_t *mname_tok = advance(ctx);
char mname[256];
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
memcpy(mname, mname_tok->lexeme, mnl);
mname[mnl] = '\0';
/* Build qualified name: TypeName.method_name */
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", et->name ? et->name : "anon",
mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
/* Parse parameters */
int nparams = 0;
enum { MAX_PARAMS = 64 };
char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS];
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
usize pnl = pname->len < 255 ? pname->len : 255;
memcpy(pnames[nparams], pname->lexeme, pnl);
pnames[nparams][pnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
if (peek(ctx)->type == TOK_R_BRACE) advance(ctx);
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
}
expect(ctx, TOK_R_PAREN);
skip_nl(ctx);
/* Return type (default: void) */
spl_type_info_t *ret_type = spl_type_basic(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);
skip_nl(ctx);
}
/* Set current_type_name for short-name resolution inside method body */
const char *saved_type_name = ctx->current_type_name;
ctx->current_type_name = et->name;
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
ctx->current_type_name = saved_type_name;
{
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
f->param_names = calloc(nparams, sizeof(char *));
for (int i = 0; i < nparams; i++) {
f->param_types[i] = ptypes[i];
f->param_names[i] = strdup(pnames[i]);
}
}
spl_type_add_method(et, mname, fi);
} else {
advance(ctx);
}
}
}
spl_type_compute_layout(et);
}
/* ============================================================
* Parse type declaration
* type Name = struct { ... };
* type Name = union { ... };
* type Name = enum { ... };
* type Name = ExistingType;
* ============================================================ */
void parse_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */
spl_tok_t *name_tok = advance(ctx);
char tname[256];
usize tnl = name_tok->len < 255 ? name_tok->len : 255;
memcpy(tname, name_tok->lexeme, tnl);
tname[tnl] = '\0';
skip_nl(ctx);
expect(ctx, TOK_ASSIGN);
skip_nl(ctx);
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);
skip_nl(ctx);
parse_struct_body(ctx, st);
} 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);
skip_nl(ctx);
parse_struct_body(ctx, ut);
} 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);
} else if (peek(ctx)->type == TOK_IDENT || (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) {
skip_nl(ctx);
parse_enum_body(ctx, et);
} 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);
@@ -244,7 +561,8 @@ void parse_type_decl(spl_comp_t *ctx) {
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* ============================================================
@@ -254,7 +572,8 @@ void parse_type_decl(spl_comp_t *ctx) {
void spl_parse_prog(spl_comp_t *ctx) {
while (peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_EOF) break;
if (peek(ctx)->type == TOK_EOF)
break;
switch (peek(ctx)->type) {
case KW_FN:
@@ -266,8 +585,10 @@ void spl_parse_prog(spl_comp_t *ctx) {
case KW_PUB: {
advance(ctx); /* skip pub */
skip_nl(ctx);
if (peek(ctx)->type == KW_FN) parse_fn_decl(ctx, 0, 1);
else if (peek(ctx)->type == KW_TYPE) parse_type_decl(ctx);
if (peek(ctx)->type == KW_FN)
parse_fn_decl(ctx, 0, 1);
else if (peek(ctx)->type == KW_TYPE)
parse_type_decl(ctx);
break;
}
case TOK_SHARP:
@@ -275,18 +596,22 @@ void spl_parse_prog(spl_comp_t *ctx) {
advance(ctx); /* # */
if (peek(ctx)->type == TOK_L_BRACKET) {
advance(ctx); /* [ */
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF) advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET) advance(ctx);
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET)
advance(ctx);
}
skip_nl(ctx);
if (peek(ctx)->type == KW_FN) parse_fn_decl(ctx, 1, 0);
if (peek(ctx)->type == KW_FN)
parse_fn_decl(ctx, 1, 0);
break;
default: {
int prev = ctx->tok_idx;
/* Try to parse as a statement */
spl_parse_stmt(ctx);
/* Safety: prevent infinite loop on unrecognized tokens */
if (ctx->tok_idx == prev) advance(ctx);
if (ctx->tok_idx == prev)
advance(ctx);
break;
}
}

View File

@@ -1,35 +1,10 @@
/* spl_stmt.c — Statement parser + codegen */
#include "spl_comp.h"
#include <stdio.h>
#include "spl_lex_util.h"
#include <stdlib.h>
#include <string.h>
static spl_tok_t *peek(spl_comp_t *ctx) {
return &vec_at(ctx->toks, ctx->tok_idx);
}
static spl_tok_t *advance(spl_comp_t *ctx) {
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
if (t->type != TOK_EOF) ctx->tok_idx++;
return t;
}
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) { advance(ctx); return 1; }
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type), spl_tok_type_name(peek(ctx)->type));
return 0;
}
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) { advance(ctx); return 1; }
return 0;
}
static void skip_nl(spl_comp_t *ctx) {
while (peek(ctx)->type == TOK_ENDLINE) advance(ctx);
}
/* ============================================================
* Return statement: ret expr;
* ============================================================ */
@@ -38,6 +13,11 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
advance(ctx); /* ret */
skip_nl(ctx);
/* Before returning, execute all pending defers from innermost scope outward */
for (int d = ctx->scope_depth; d >= 1; d--) {
spl_emit_defer_epilogue(ctx, d);
}
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_R_BRACE ||
peek(ctx)->type == TOK_ENDLINE) {
/* void return */
@@ -45,10 +25,21 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
} else {
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
(void)val;
spl_type_t rt = ctx->current_ret_type ? ctx->current_ret_type->basic_type : SPL_VOID;
spl_type_t rt = SPL_VOID;
if (ctx->current_ret_type) {
if (ctx->current_ret_type->kind == TYPE_BASIC) {
rt = ctx->current_ret_type->basic_type;
} else if (ctx->current_ret_type->kind == TYPE_PTR) {
rt = SPL_PTR;
} else if (spl_type_size(ctx->current_ret_type) <= sizeof(spl_val_t)) {
/* 1-slot struct/enum/etc: use PTR type */
rt = SPL_PTR;
}
}
spl_emit(ctx, SPL_RET, rt, 0);
}
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* ============================================================
@@ -62,7 +53,8 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
spl_tok_t *name_tok = advance(ctx);
char vname[256];
usize nlen = name_tok->len < 255 ? name_tok->len : 255;
memcpy(vname, name_tok->lexeme, nlen); vname[nlen] = '\0';
memcpy(vname, name_tok->lexeme, nlen);
vname[nlen] = '\0';
spl_type_info_t *var_type = NULL;
int has_init = 0;
@@ -84,49 +76,119 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
if (peek(ctx)->type == TOK_COLON_ASSIGN || peek(ctx)->type == TOK_ASSIGN) {
has_init = 1;
if (peek(ctx)->type == TOK_COLON_ASSIGN) advance(ctx);
else advance(ctx); /* = */
if (peek(ctx)->type == TOK_COLON_ASSIGN)
advance(ctx);
else
advance(ctx); /* = */
}
/* Allocate stack slot */
if (!var_type) var_type = spl_type_basic(SPL_I32); /* default type */
int slot = spl_declare_var(ctx, vname, var_type, is_const);
/* Init expression: parse before declare to enable type inference */
spl_expr_result_t init = {0};
if (has_init) {
skip_nl(ctx);
init = spl_parse_expr(ctx, PREC_MIN);
}
if (!var_type) {
var_type = init.type ? init.type : spl_type_basic(SPL_I32);
}
int offset = spl_declare_var(ctx, vname, var_type, is_const);
/* Store init value */
if (has_init) {
/* Handle slice struct literal: { .ptr = ..., .len = ... } */
if (var_type && var_type->kind == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE && !init.type) {
advance(ctx); /* { */
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_DOT)
advance(ctx);
spl_tok_t *ftok = advance(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_ASSIGN)
advance(ctx);
skip_nl(ctx);
/* Init expression */
if (has_init) {
spl_expr_result_t init = spl_parse_expr(ctx, PREC_MIN);
(void)init;
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
(void)fv;
if (var_type && var_type->kind == TYPE_ARRAY) {
if (strcmp(fname, "ptr") == 0) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else if (strcmp(fname, "len") == 0) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)sizeof(spl_val_t));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
}
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
} else if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) {
/* Struct/enum initialization */
usize sz = spl_type_size(var_type);
if (sz <= sizeof(spl_val_t)) {
/* Fits in one slot: value on stack, store directly */
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else {
/* Multi-slot: copy from temp addr to var, slot by slot */
usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t);
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, offset + (int)(i * sizeof(spl_val_t)));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
}
}
} else if (var_type && var_type->kind == TYPE_ARRAY) {
/* Array initialization: store each element in reverse stack order */
spl_type_info_t *elem = var_type->elem;
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
usize stride = spl_type_elem_stride(elem);
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + i);
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)(i * stride));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
}
} else if (var_type && var_type->kind == TYPE_SLICE) {
/* Slice initialization: stack has [ptr, len] from slice expression.
* Store len at slot+1, then ptr at slot. */
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + 1);
* Store len at offset+8, then ptr at offset. */
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)sizeof(spl_val_t));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_I32, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot);
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else {
/* Single value store */
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot);
spl_type_t bt = var_type->kind == TYPE_BASIC ? var_type->basic_type : SPL_I32;
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_type_t bt = (var_type->kind == TYPE_BASIC) ? var_type->basic_type
: (var_type->kind == TYPE_PTR) ? SPL_PTR
: SPL_I32;
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
}
}
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* ============================================================
@@ -144,8 +206,10 @@ void spl_parse_block(spl_comp_t *ctx) {
skip_nl(ctx);
}
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
if (peek(ctx)->type == TOK_R_BRACE) advance(ctx);
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
} else {
/* Single statement */
spl_parse_stmt(ctx);
@@ -257,68 +321,217 @@ static void parse_for_stmt(spl_comp_t *ctx) {
skip_nl(ctx);
/* Parse the iteration expression(s) */
spl_expr_result_t start = spl_parse_expr(ctx, PREC_MIN);
(void)start;
spl_expr_result_t start_expr = spl_parse_expr(ctx, PREC_MIN);
if (peek(ctx)->type == TOK_RANGE) {
/* for begin..end as i { body } */
/* ===== Numeric range: for begin..end as i { body } ===== */
advance(ctx); /* .. */
spl_expr_result_t end = spl_parse_expr(ctx, PREC_MIN);
(void)end;
spl_parse_expr(ctx, PREC_MIN); /* end expression */
/* Stack: [begin, end] */
skip_nl(ctx);
if (peek(ctx)->type == KW_AS) {
if (peek(ctx)->type != KW_AS) {
skip_nl(ctx);
spl_parse_block(ctx);
return;
}
advance(ctx); /* as */
spl_tok_t *ivar = advance(ctx);
char iname[256];
usize inl = ivar->len < 255 ? ivar->len : 255;
memcpy(iname, ivar->lexeme, inl); iname[inl] = '\0';
memcpy(iname, ivar->lexeme, inl);
iname[inl] = '\0';
/* Declare loop variable */
spl_type_info_t *itype = spl_type_basic(SPL_I32);
int islot = spl_declare_var(ctx, iname, itype, 0);
spl_push_scope(ctx);
int ioffset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0);
/* Stack: [begin, end]; swap so TOS = begin */
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
/* 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);
/* Stack: [end] */
spl_val_t loop_start = vec_size(ctx->prog.insns);
/* Condition: i < end */
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);
/* The loop variable was already set up by the range operator */
/* Store initial value */
/* For now: we need to emit proper loop code. This is a simplification. */
int saved_loop = ctx->in_loop;
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = 0; /* will set after store */
ctx->continue_target = loop_start;
/* Loop: store begin to i, check i < end, body, i++ */
/* Stack has: begin, end (from parsing the range expression) */
/* Actually we need to emit proper code here. For bootstrap,
let me just handle the simple numeric for loop. */
skip_nl(ctx);
spl_parse_block(ctx); /* body */
/* For now, emit a basic loop body */
spl_parse_block(ctx);
/* 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);
/* JMP back to condition */
spl_val_t jmp_here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)jmp_here - 1));
/* Exit: patch bz, drop end */
spl_patch_to_here(ctx, bz_addr);
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
/* Patch breaks */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++)
spl_patch_to_here(ctx, ctx->break_patches[i]);
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
ctx->break_patch_count = saved_bp_count;
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
return;
}
} else if (peek(ctx)->type == TOK_COMMA) {
/* for slice, 0.. as val, idx { body } — skip for now */
/* ===== Slice iteration: for slice [as val |, range as val, idx] ===== */
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx); /* , */
spl_parse_expr(ctx, PREC_MIN); /* skip the range */
if (peek(ctx)->type == KW_AS) {
advance(ctx);
advance(ctx); /* val */
spl_parse_expr(ctx, PREC_MIN); /* parse start of range (e.g. 0) */
if (peek(ctx)->type == TOK_RANGE) {
advance(ctx); /* .. */
/* consume optional end expression */
if (peek(ctx)->type != KW_AS && peek(ctx)->type != TOK_COMMA &&
peek(ctx)->type != TOK_L_BRACE && peek(ctx)->type != TOK_ENDLINE &&
peek(ctx)->type != TOK_EOF)
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);
}
if (peek(ctx)->type != KW_AS) {
skip_nl(ctx);
spl_parse_block(ctx);
return;
}
advance(ctx); /* as */
/* Parse val variable name */
spl_tok_t *vtok = advance(ctx);
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
/* Parse optional idx variable name */
char iname[256] = {0};
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
advance(ctx); /* idx */
spl_tok_t *itok = advance(ctx);
usize inl = itok->len < 255 ? itok->len : 255;
memcpy(iname, itok->lexeme, inl);
iname[inl] = '\0';
}
/* Stack: [slice_addr] or whatever the slice expression left */
spl_push_scope(ctx);
int idx_offset = -1;
if (iname[0])
idx_offset = spl_declare_var(ctx, iname, spl_type_basic(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;
}
if (!elem_type)
elem_type = spl_type_basic(SPL_I32);
int val_offset = spl_declare_var(ctx, vname, elem_type, 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);
/* Stack: [ptr, len, idx=0] */
spl_val_t loop_start = vec_size(ctx->prog.insns);
/* 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);
/* 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);
}
/* 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 && elem_type->byte_size) ? elem_type->byte_size : 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);
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_I32, 0);
/* Loop context */
int saved_loop = ctx->in_loop;
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = loop_start;
skip_nl(ctx);
spl_parse_block(ctx);
} else {
/* for ident in ... — skip */
skip_nl(ctx);
spl_parse_block(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 */
/* JMP back to condition */
spl_val_t jmp_here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)jmp_here - 1));
/* 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 */
/* Patch breaks */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++)
spl_patch_to_here(ctx, ctx->break_patches[i]);
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
spl_pop_scope(ctx);
}
/* ============================================================
@@ -338,7 +551,8 @@ static void parse_break_stmt(spl_comp_t *ctx) {
ctx->break_patch_cap = new_cap;
}
ctx->break_patches[ctx->break_patch_count++] = addr;
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
static void parse_continue_stmt(spl_comp_t *ctx) {
@@ -349,7 +563,8 @@ 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));
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* ============================================================
@@ -360,7 +575,7 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
advance(ctx); /* defer */
skip_nl(ctx);
/* Record current position for defer */
/* Record current position for defer (emits a JMP skip placeholder) */
spl_emit_defer(ctx);
/* Parse the deferred statement/block */
@@ -369,6 +584,216 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
} else {
spl_parse_stmt(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);
}
}
/* ============================================================
* Match statement: match expr { .Variant(bindings) => stmt, ... }
* ============================================================ */
static void parse_match_stmt(spl_comp_t *ctx) {
advance(ctx); /* match */
skip_nl(ctx);
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
/* Determine the enum type (deref pointer if needed) */
spl_type_info_t *enum_type = expr.type;
if (enum_type && enum_type->kind == TYPE_PTR && enum_type->elem &&
enum_type->elem->kind == TYPE_ENUM) {
enum_type = enum_type->elem;
}
if (!enum_type || enum_type->kind != TYPE_ENUM) {
spl_comp_error(ctx, "match expression must be an enum");
return;
}
/* Save the enum address (pointer value) to a temp slot */
int addr_offset = ctx->current_local_bytes;
ctx->current_local_bytes += (int)sizeof(spl_val_t);
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE)
advance(ctx); /* { */
/* Collect JMP-to-end addresses for patching */
enum { MAX_MATCH_ARMS = 32 };
spl_val_t jmp_to_end[MAX_MATCH_ARMS];
int n_jmps = 0;
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
/* Parse .VariantName */
if (peek(ctx)->type == TOK_DOT)
advance(ctx);
spl_tok_t *vtok = advance(ctx);
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
/* Find the variant in the enum type */
spl_enum_variant_t *variant = NULL;
vec_for(enum_type->variants, vi) {
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
variant = &vec_at(enum_type->variants, vi);
break;
}
}
if (!variant) {
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
break;
}
/* Compare tag: load [addr_offset+0] == variant->value */
/* Load tag and compare */
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value);
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
spl_val_t bz_addr = spl_emit_bz(ctx);
/* This arm matches — parse bindings and body */
skip_nl(ctx);
/* Parse binding list: (name, name, ...) */
int has_parens = 0;
if (peek(ctx)->type == TOK_L_PAREN) {
has_parens = 1;
advance(ctx); /* ( */
skip_nl(ctx);
}
/* Push scope for bindings if there are any */
int scope_pushed = 0;
if (has_parens && peek(ctx)->type != TOK_R_PAREN) {
spl_push_scope(ctx);
scope_pushed = 1;
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
/* Struct data: each struct field is a binding */
int bi = 0;
for (;;) {
spl_tok_t *btok = advance(ctx);
char bname[256];
usize bnl = btok->len < 255 ? btok->len : 255;
memcpy(bname, btok->lexeme, bnl);
bname[bnl] = '\0';
spl_type_info_t *btype = spl_type_basic(SPL_I32);
usize field_byte_off = 4;
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 boffset = spl_declare_var(ctx, bname, btype, 0);
/* Load from enum data and store to variable */
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
spl_emit(ctx, SPL_PUSH, SPL_USIZE, field_byte_off);
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type
: (btype->kind == TYPE_PTR) ? SPL_PTR
: SPL_I32;
spl_emit(ctx, SPL_LOAD, bt, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, boffset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
bi++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
} else if (variant->data_type) {
/* Simple data type: one binding */
spl_tok_t *btok = advance(ctx);
char bname[256];
usize bnl = btok->len < 255 ? btok->len : 255;
memcpy(bname, btok->lexeme, bnl);
bname[bnl] = '\0';
spl_type_info_t *btype = variant->data_type;
int boffset = spl_declare_var(ctx, bname, btype, 0);
/* Load from enum data at offset 4 */
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 4);
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type
: (btype->kind == TYPE_PTR) ? SPL_PTR
: SPL_I32;
spl_emit(ctx, SPL_LOAD, bt, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, boffset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
}
}
if (has_parens)
expect(ctx, TOK_R_PAREN);
skip_nl(ctx);
/* => (two tokens: = >) */
if (peek(ctx)->type == TOK_ASSIGN) {
advance(ctx);
if (peek(ctx)->type == TOK_GT)
advance(ctx);
}
skip_nl(ctx);
/* Parse arm body statement */
spl_parse_stmt(ctx);
/* Pop scope if we pushed one */
if (scope_pushed) {
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
}
/* JMP to end (skip remaining arms) */
if (n_jmps < MAX_MATCH_ARMS)
jmp_to_end[n_jmps++] = spl_emit_jmp(ctx);
/* Patch BZ to here (next arm or end) */
spl_patch_to_here(ctx, bz_addr);
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
/* Patch all JMPs to end */
for (int i = 0; i < n_jmps; i++)
spl_patch_to_here(ctx, jmp_to_end[i]);
/* Release temp slot */
ctx->current_local_bytes -= (int)sizeof(spl_val_t);
}
/* ============================================================
@@ -379,8 +804,10 @@ static void parse_extern_decl(spl_comp_t *ctx) {
advance(ctx); /* # */
expect(ctx, TOK_L_BRACKET);
/* Skip extern("vm") or just look for fn */
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF) advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET) advance(ctx);
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET)
advance(ctx);
skip_nl(ctx);
@@ -390,7 +817,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
spl_tok_t *fname_tok = advance(ctx);
char fn_name[256];
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255;
memcpy(fn_name, fname_tok->lexeme, fnl); fn_name[fnl] = '\0';
memcpy(fn_name, fname_tok->lexeme, fnl);
fn_name[fnl] = '\0';
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
@@ -409,8 +837,15 @@ static void parse_extern_decl(spl_comp_t *ctx) {
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); continue; }
if (peek(ctx)->type == TOK_ELLIPSIS) { advance(ctx); skip_nl(ctx); } /* variadic */
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_ELLIPSIS) {
advance(ctx);
skip_nl(ctx);
} /* variadic */
break;
}
}
@@ -421,13 +856,15 @@ static void parse_extern_decl(spl_comp_t *ctx) {
spl_type_info_t *ret_type = spl_type_basic(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);
if (!ret_type)
ret_type = spl_type_basic(SPL_VOID);
skip_nl(ctx);
}
spl_declare_func(ctx, fn_name, ret_type, nparams, 1, 0);
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
}
@@ -437,10 +874,10 @@ static void parse_extern_decl(spl_comp_t *ctx) {
/* Check if token type is an assignment operator */
static int is_assign_op(spl_tok_type_t t) {
return t == TOK_ASSIGN || t == TOK_ASSIGN_ADD || t == TOK_ASSIGN_SUB ||
t == TOK_ASSIGN_MUL || t == TOK_ASSIGN_DIV || t == TOK_ASSIGN_MOD ||
t == TOK_ASSIGN_AND || t == TOK_ASSIGN_OR || t == TOK_ASSIGN_XOR ||
t == TOK_ASSIGN_L_SH || t == TOK_ASSIGN_R_SH;
return t == TOK_ASSIGN || t == TOK_ASSIGN_ADD || t == TOK_ASSIGN_SUB || t == TOK_ASSIGN_MUL ||
t == TOK_ASSIGN_DIV || t == TOK_ASSIGN_MOD || t == TOK_ASSIGN_AND ||
t == TOK_ASSIGN_OR || t == TOK_ASSIGN_XOR || t == TOK_ASSIGN_L_SH ||
t == TOK_ASSIGN_R_SH;
}
static void parse_expr_stmt(spl_comp_t *ctx) {
@@ -450,26 +887,41 @@ static void parse_expr_stmt(spl_comp_t *ctx) {
return;
}
int prev = ctx->tok_idx;
usize prev = ctx->tok_idx;
/* Look ahead past newlines for assignment operator */
/* Look ahead for assignment operator (scan past expression tokens) */
int is_assign = 0;
if (peek(ctx)->type == TOK_IDENT) {
usize look = ctx->tok_idx + 1;
while (look < vec_size(ctx->toks) && vec_at(ctx->toks, look).type == TOK_ENDLINE)
{
usize look = ctx->tok_idx;
while (look < vec_size(ctx->toks)) {
spl_tok_type_t t = vec_at(ctx->toks, look).type;
if (t == TOK_SEMICOLON || t == TOK_ENDLINE || t == TOK_L_BRACE || t == TOK_R_BRACE ||
t == TOK_EOF)
break;
if (t == TOK_L_PAREN)
break; /* function call, not assignment */
if (is_assign_op(t)) {
is_assign = 1;
break;
}
look++;
if (look < vec_size(ctx->toks))
is_assign = is_assign_op(vec_at(ctx->toks, look).type);
}
}
if (is_assign) ctx->addr_of_mode = 1;
if (is_assign)
ctx->addr_of_mode = 1;
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
if (is_assign) ctx->addr_of_mode = 0;
if (is_assign)
ctx->addr_of_mode = 0;
if (!is_assign && expr.type) spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC &&
expr.type->basic_type != SPL_VOID)
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
/* Safety: if no token was consumed, advance to prevent infinite loop */
if (ctx->tok_idx == prev) advance(ctx);
if (ctx->tok_idx == prev)
advance(ctx);
}
/* ============================================================
@@ -479,7 +931,8 @@ static void parse_expr_stmt(spl_comp_t *ctx) {
void spl_parse_stmt(spl_comp_t *ctx) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_EOF || peek(ctx)->type == TOK_R_BRACE) return;
if (peek(ctx)->type == TOK_EOF || peek(ctx)->type == TOK_R_BRACE)
return;
switch (peek(ctx)->type) {
case KW_RET:
@@ -512,6 +965,9 @@ void spl_parse_stmt(spl_comp_t *ctx) {
case KW_DEFER:
parse_defer_stmt(ctx);
break;
case KW_MATCH:
parse_match_stmt(ctx);
break;
case KW_TYPE:
parse_type_decl(ctx);
break;
@@ -521,7 +977,7 @@ void spl_parse_stmt(spl_comp_t *ctx) {
case TOK_LINE_COMMENT:
advance(ctx);
break;
case TOK_SHARP: /* #[extern(...)] */
case TOK_SHARP: /* #[extern(...)] */
parse_extern_decl(ctx);
break;
default:

View File

@@ -132,6 +132,18 @@ spl_type_info_t *spl_type_struct(const char *name) {
if (name)
t->name = strdup(name);
vec_init(t->fields);
vec_init(t->methods);
t->resolved = 0;
return t;
}
spl_type_info_t *spl_type_union(const char *name) {
spl_type_info_t *t = calloc(1, sizeof(spl_type_info_t));
t->kind = TYPE_UNION;
if (name)
t->name = strdup(name);
vec_init(t->fields);
vec_init(t->methods);
t->resolved = 0;
return t;
}
@@ -142,6 +154,7 @@ spl_type_info_t *spl_type_enum(const char *name) {
if (name)
t->name = strdup(name);
vec_init(t->variants);
vec_init(t->methods);
t->byte_size = 4;
t->slot_count = 1;
t->resolved = 0;
@@ -164,24 +177,46 @@ void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t
vec_push(et->variants, v);
}
void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx) {
spl_method_info_t m;
m.name = strdup(name);
m.func_idx = func_idx;
vec_push(t->methods, m);
}
void spl_type_compute_layout(spl_type_info_t *t) {
if (!t || t->resolved)
return;
if (t->kind == TYPE_STRUCT) {
if (t->kind == TYPE_STRUCT || t->kind == TYPE_UNION) {
usize offset = 0;
usize max_field_size = 0;
vec_for(t->fields, i) {
spl_field_t *f = &vec_at(t->fields, i);
if (f->type) {
spl_type_compute_layout(f->type);
if (t->kind == TYPE_UNION) {
/* Union: all fields at offset 0, size = max field size */
f->offset = 0;
if (f->type->byte_size > max_field_size)
max_field_size = f->type->byte_size;
} else {
/* Struct: sequential layout */
f->offset = offset;
offset += f->type->byte_size;
}
}
}
if (t->kind == TYPE_UNION) {
t->byte_size = max_field_size;
} else {
t->byte_size = offset;
}
/* Round up to slot alignment */
usize slot_sz = sizeof(spl_val_t);
t->slot_count = (offset + slot_sz - 1) / slot_sz;
t->slot_count = (t->byte_size + slot_sz - 1) / slot_sz;
if (t->slot_count < 1)
t->slot_count = 1;
t->resolved = 1;
} else if (t->kind == TYPE_ENUM) {
/* Enums with data need special handling */
@@ -224,6 +259,11 @@ usize spl_type_slot_count(spl_type_info_t *t) {
return t->slot_count;
}
/* Byte stride between consecutive elements in storage */
usize spl_type_elem_stride(spl_type_info_t *elem) {
return spl_type_size(elem);
}
const char *spl_type_str(spl_type_info_t *t) {
if (!t)
return "<null>";
@@ -281,6 +321,8 @@ const char *spl_type_str(spl_type_info_t *t) {
}
case TYPE_STRUCT:
return t->name ? t->name : "struct";
case TYPE_UNION:
return t->name ? t->name : "union";
case TYPE_ENUM:
return t->name ? t->name : "enum";
case TYPE_NAME:
@@ -349,6 +391,67 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
return spl_type_array(elem, len);
}
/* Inline struct/union/enum type: struct { field: type, ... } */
if (tok->type == KW_STRUCT || tok->type == KW_UNION) {
int is_union = (tok->type == KW_UNION);
ctx->tok_idx++;
spl_type_info_t *t = is_union ? spl_type_union(NULL) : spl_type_struct(NULL);
if (peek(ctx)->type == TOK_L_BRACE) {
advance(ctx); /* { */
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_tok_t *ftok = advance(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
spl_type_add_field(t, fname, ftype);
}
if (peek(ctx)->type == TOK_COMMA)
advance(ctx);
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
}
spl_type_compute_layout(t);
return t;
}
/* Inline enum type: enum { A, B, C, ... } */
if (tok->type == KW_ENUM) {
ctx->tok_idx++;
spl_type_info_t *t = spl_type_enum(NULL);
if (peek(ctx)->type == TOK_L_BRACE) {
advance(ctx); /* { */
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_tok_t *vtok = advance(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
spl_type_info_t *dtype = spl_parse_type(ctx);
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(t, vname, dtype);
} else {
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(t, vname, NULL);
}
if (peek(ctx)->type == TOK_COMMA)
advance(ctx);
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
}
spl_type_compute_layout(t);
return t;
}
/* Identifier: basic type or named type */
if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) {
const char *name = tok->lexeme;
@@ -391,7 +494,8 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
}
spl_type_info_t *spl_resolve_type(spl_comp_t *ctx, const char *name) {
if (!ctx || !name) return NULL;
if (!ctx || !name)
return NULL;
spl_type_info_t *found = NULL;
map_get(ctx->type_defs, name, &found);
return found;

View File

@@ -47,8 +47,18 @@ fn main() i32 {
/* ---- 指向数组元素的指针 ---- */
var buf: [4]i32 = [4]i32{1, 2, 3, 4};
// @dbg(); /* 数组初始化后的栈状态 */
var elem_ptr: *i32 = &buf[0];
if elem_ptr.* != 1 { ret 9; }
// @dbg(); /* &buf[0] 之后:栈上应有指针值 */
var loaded := elem_ptr.*;
// @dbg(); /* 解引用后loaded 值 */
if loaded != 1 { ret 9; }
elem_ptr = &buf[1];
// @dbg(); /* &buf[1] 之后 */
var loaded2 := elem_ptr.*;
// @dbg(); /* 解引用后loaded2 值 */
if loaded2 != 2 { ret 10; }
ret 0;
}

View File

@@ -34,8 +34,5 @@ fn main() i32 {
}
if s != 20 { ret 6; }
/* 多维效果:数组元素为数组 */
/* 目前只测试一维 */
ret 0;
}

View File

@@ -46,12 +46,12 @@ fn main() i32 {
if r != 25 { ret 6; }
/* 结构体嵌套 */
type Inner = struct {
val: i32,
}
type Outer = struct {
inner: Inner,
extra: i32,
type Inner = struct {
val: i32,
}
}
var o: Outer;

View File

@@ -12,11 +12,18 @@ type Color = enum {
}
type Expr = enum {
Int: i32,
Add: struct { left: *Expr, right: *Expr },
val: i32,
tag: Tag,
type Tag = enum {
TagA,
TagB,
TagC,
}
}
fn main() i32 {
vm_printf("enum values: %d %d %d\n", Color.Red, Color.Green, Color.Blue);
vm_printf("enum values: %d %d %d\n", Expr.Tag.TagA, Expr.Tag.TagB, Expr.Tag.TagC);
ret 0;
}

View File

@@ -19,6 +19,10 @@ fn identity(x: i32) i32 {
ret x;
}
fn addr(x: *i32) *i32 {
ret x;
}
fn main() i32 {
/* 函数调用 */
var r1 := add(3, 4);
@@ -42,5 +46,10 @@ fn main() i32 {
var r6 := add(add(1, 2), add(3, 4));
if r6 != 10 { ret 6; }
/* 返回地址 */
var r7 := addr(&r6);
if r7 != &r6 { ret 7; }
if r7.* != 10 { ret 8; }
ret 0;
}

View File

@@ -28,12 +28,18 @@ fn main() i32 {
with_cleanup();
vm_printf("after with_cleanup\n");
/* 多个 defer 应逆序执行出作用域应该立刻执行,包括循环作用域 */
{
defer vm_printf("block defer last\n");
defer vm_printf("block defer middle\n");
defer vm_printf("block defer first (should print third)\n");
}
/* 多个 defer 应逆序执行 */
defer vm_printf("defer last\n");
defer vm_printf("defer middle\n");
defer vm_printf("defer first (should print third)\n");
vm_printf("--- defer test end ---\n");
ret 0;
}

View File

@@ -40,7 +40,7 @@ fn main() i32 {
var expr_l := Expr { .Int = 3 };
var expr_r := Expr { .Int = 4 };
var expr := Expr { .Add = { .left = expr_l, .right = expr_r } };
var result := expr.eval();
var result := expr.eval(&expr);
vm_printf("eval result: %d\n", result);
if result != 7 { ret 1; }

View File

@@ -22,8 +22,6 @@ fn fib(n: i32) i32 {
}
fn main() i32 {
vm_printf("=== integration test ===\n");
/* 综合:指针 + 结构体 + 函数 */
var p1: Point;
p1.x = 3;
@@ -92,9 +90,6 @@ fn main() i32 {
}
j = j + 1;
}
if outer_sum != 9 { ret 8; }
vm_printf("=== all integration tests passed ===\n");
if outer_sum != 3 { ret 8; }
ret 0;
}

184
stage1/test19_hardarray.spl Normal file
View File

@@ -0,0 +1,184 @@
/* ===== 进阶数组与切片 =====
* test19_hardarray — 多维数组、多维切片、切片构造、字符串测试
* 难度4/5
* 验证点:多维数组索引与修改、多维切片、切片字段读写、扁平指针访问、字符串切片
*/
fn test_string() i32 {
/* ============================
* String test: str is []u8
* ============================ */
var data: *u8 = "hello";
var s: []u8 = { .ptr = data, .len = 5 };
if s.len != 5 { ret 100; }
if s[0] != 104 { ret 101; } /* 'h' */
if s[1] != 101 { ret 102; } /* 'e' */
if s[4] != 111 { ret 103; } /* 'o' */
/* Slice the string slice */
var sub: []u8 = s[1..4];
if sub.len != 3 { ret 104; }
if sub[0] != 101 { ret 105; } /* 'e' */
if sub[2] != 108 { ret 106; } /* 'l' */
/* Modify through slice → original changes */
s[0] = 72; /* 'H' */
if data[0] != 72 { ret 107; }
/* Full slice */
var full: []u8 = s[0..];
if full.len != 5 { ret 108; }
if full[0] != 72 { ret 109; }
/* Construct slice from pointer via field assignment */
var s2: []u8;
s2.ptr = data;
s2.len = 3;
if s2.len != 3 { ret 110; }
if s2[0] != 72 { ret 111; }
ret 0;
}
fn main() i32 {
/* ============================
* Part 1: 多维数组(逐元素初始化)
* ============================ */
var matrix: [2][3]i32;
matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
if matrix[0][0] != 1 { ret 1; }
if matrix[0][1] != 2 { ret 2; }
if matrix[0][2] != 3 { ret 3; }
if matrix[1][0] != 4 { ret 4; }
if matrix[1][1] != 5 { ret 5; }
if matrix[1][2] != 6 { ret 6; }
/* 元素修改 */
matrix[0][0] = 10;
matrix[1][2] = 60;
if matrix[0][0] != 10 { ret 7; }
if matrix[1][2] != 60 { ret 8; }
/* ============================
* Part 2: 嵌套循环遍历多维数组
* ============================ */
var big: [3][4]i32;
big[0][0] = 1; big[0][1] = 2; big[0][2] = 3; big[0][3] = 4;
big[1][0] = 5; big[1][1] = 6; big[1][2] = 7; big[1][3] = 8;
big[2][0] = 9; big[2][1] = 10; big[2][2] = 11; big[2][3] = 12;
var total: i32 = 0;
var i: i32 = 0;
while i < 3 {
var j: i32 = 0;
while j < 4 {
total = total + big[i][j];
j = j + 1;
}
i = i + 1;
}
if total != 78 { ret 9; }
/* ============================
* Part 3: &取地址 + 扁平指针访问
* ============================ */
var flat: *i32 = &matrix[0][0];
if flat[0] != 10 { ret 10; }
if flat[1] != 2 { ret 11; }
if flat[2] != 3 { ret 12; }
if flat[3] != 4 { ret 13; }
if flat[4] != 5 { ret 14; }
if flat[5] != 60 { ret 15; }
/* ============================
* Part 4: 多维数组切片
* ============================ */
var row: []i32 = matrix[0][0..3];
if row.len != 3 { ret 16; }
if row[0] != 10 { ret 17; }
if row[1] != 2 { ret 18; }
if row[2] != 3 { ret 19; }
/* 切取第二行 */
var row2: []i32 = matrix[1][0..];
if row2.len != 3 { ret 20; }
if row2[0] != 4 { ret 21; }
if row2[2] != 60 { ret 22; }
/* ============================
* Part 5: 切片的切片
* ============================ */
var sub: []i32 = row[1..3];
if sub.len != 2 { ret 23; }
if sub[0] != 2 { ret 24; }
if sub[1] != 3 { ret 25; }
/* ============================
* Part 6: 切片字段读写
* ============================ */
var arr: [4]i32;
arr[0] = 100; arr[1] = 200; arr[2] = 300; arr[3] = 400;
var custom: []i32;
custom.ptr = &arr[1];
custom.len = 2;
if custom.len != 2 { ret 26; }
if custom[0] != 200 { ret 27; }
if custom[1] != 300 { ret 28; }
/* 修改切片长度 */
custom.len = 3;
if custom.len != 3 { ret 29; }
if custom[2] != 400 { ret 30; }
/* ============================
* Part 7: 空范围 / 全切片
* ============================ */
var full: []i32 = arr[0..];
if full.len != 4 { ret 31; }
if full[0] != 100 { ret 32; }
if full[3] != 400 { ret 33; }
/* ============================
* Part 8: 从指针构造切片
* ============================ */
var data: [5]i32;
data[0] = 10; data[1] = 20; data[2] = 30; data[3] = 40; data[4] = 50;
var p: *i32 = &data[2];
var from_ptr: []i32 = { .ptr = p, .len = 2 };
var from_ptr2: []i32;
from_ptr2.ptr = p;
from_ptr2.len = 2;
if from_ptr.len != 2 { ret 34; }
if from_ptr[0] != 30 { ret 35; }
if from_ptr[1] != 40 { ret 36; }
if from_ptr2.len != 2 { ret 37; }
if from_ptr2[0] != 30 { ret 38; }
if from_ptr2[1] != 40 { ret 39; }
/* ============================
* Part 9: 切片元素修改反映到原始数组
* ============================ */
row2[0] = 99;
if matrix[1][0] != 99 { ret 40; }
/* ============================
* Part 10: 1D 数组字面量仍然正常
* ============================ */
var literal: [3]i32 = [3]i32{10, 20, 30};
if literal[0] != 10 { ret 41; }
if literal[1] != 20 { ret 42; }
if literal[2] != 30 { ret 43; }
/* ============================
* Part 11: 字符串切片测试
* ============================ */
var r: i32 = test_string();
if r != 0 { ret r; }
ret 0;
}

View File

@@ -1,32 +0,0 @@
/* ===== 模块5复合类型 =====
* test19_typealias — 类型别名
* 难度2/5
* 验证点type Name = struct/enum { ... } 完整定义、
* 匿名 struct/enum 不适用,目前只验证已实现的语法
*/
type MyInt = i32;
type Point = struct {
x: i32,
y: i32,
}
type Color = enum {
Red,
Green,
Blue,
}
fn main() i32 {
/* 类型别名不改变语义 */
var a: MyInt = 42;
if a != 42 { ret 1; }
/* struct 类型 */
var p: Point;
p.x = 10;
p.y = 20;
if p.x + p.y != 30 { ret 2; }
ret 0;
}