Compare commits
10 Commits
e2e0ebc21f
...
463177d3be
| Author | SHA1 | Date | |
|---|---|---|---|
| 463177d3be | |||
| 1df3e3bcb4 | |||
| 3cf11f922e | |||
| 69cea030dc | |||
| 0892c084ee | |||
| ad473f245c | |||
| 0182b8ed5c | |||
| 777b6b42d1 | |||
| 5dadf6d6ee | |||
| 67c8a137dd |
@@ -4,10 +4,20 @@ vm = [
|
|||||||
"stage0/spl_vm.c",
|
"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 = {
|
exe = {
|
||||||
"spl_cli": ["stage0/spl_cli.c"] + vm,
|
"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"],
|
"splc_cli": ["stage1/splc_cli.c"] + vm + splc0_part,
|
||||||
"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"],
|
"splc0": ["stage1/splc0.c"] + vm + splc0_part,
|
||||||
"test": ["stage0/test_spl_vm.c"] + vm,
|
"test": ["stage0/test_spl_vm.c"] + vm,
|
||||||
"spl_disasm": ["stage0/spl_disasm.c"] + vm,
|
"spl_disasm": ["stage0/spl_disasm.c"] + vm,
|
||||||
}
|
}
|
||||||
@@ -35,7 +45,7 @@ pipeline = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
spl = {
|
spl = {
|
||||||
"splc1": {"src": "stage1/splc1.spl", "pipeline": "splc0r"},
|
"splc1": {"src": "stage2/splc1.spl", "pipeline": "splc0r"},
|
||||||
"splc2": {"src": "stage2/splc2.spl", "pipeline": "splc1r"},
|
"splc2": {"src": "stage2/splc2.spl", "pipeline": "splc1r"},
|
||||||
"splc3": {"src": "stage3/splc3.spl", "pipeline": "splc2r"},
|
"splc3": {"src": "stage3/splc3.spl", "pipeline": "splc2r"},
|
||||||
"splc4": {"src": "stage4/splc4.spl", "pipeline": "splc3r"},
|
"splc4": {"src": "stage4/splc4.spl", "pipeline": "splc3r"},
|
||||||
|
|||||||
@@ -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) {
|
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||||
unsigned char *buf, *p;
|
unsigned char *buf, *p;
|
||||||
int i;
|
spl_val_t i;
|
||||||
size_t total;
|
size_t total;
|
||||||
spl_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
spl_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
||||||
usize nlen, pad;
|
usize nlen, pad;
|
||||||
@@ -458,6 +458,10 @@ const char *spl_type_name(spl_type_t type) {
|
|||||||
return "f32";
|
return "f32";
|
||||||
case SPL_F64:
|
case SPL_F64:
|
||||||
return "f64";
|
return "f64";
|
||||||
|
case SPL_USIZE:
|
||||||
|
return "usize";
|
||||||
|
case SPL_ISIZE:
|
||||||
|
return "isize";
|
||||||
case SPL_PTR:
|
case SPL_PTR:
|
||||||
return "ptr";
|
return "ptr";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ typedef enum {
|
|||||||
X(SPL_RET, "ret", 0, 0, 0, "return from function") \
|
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_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_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") \
|
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_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_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 */
|
/* clang-format on */
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "spl_syscall.h"
|
#include "spl_syscall.h"
|
||||||
|
#include "include/core_map.h"
|
||||||
#include "include/core_vec.h"
|
#include "include/core_vec.h"
|
||||||
#include "spl_ir.h"
|
#include "spl_ir.h"
|
||||||
#include "spl_vm.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) {
|
static spl_val_t vm_read_file(int nargs, spl_val_t *args) {
|
||||||
CHECK_NARGS("vm_read_file", 1);
|
CHECK_NARGS("vm_read_file", 1);
|
||||||
const char *path = (const char *)(uintptr_t)args[0];
|
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");
|
FILE *f = fopen(path, "rb");
|
||||||
if (!f)
|
if (!f) {
|
||||||
return 0;
|
fprintf(stderr, "filepath %s can't be open", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
long sz = ftell(f);
|
long sz = ftell(f);
|
||||||
fseek(f, 0, SEEK_SET);
|
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;
|
int arg_idx = 0;
|
||||||
char tmp_buf[32];
|
char tmp_buf[32];
|
||||||
memset(tmp_buf, 0, sizeof(tmp_buf));
|
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] != '%') {
|
if (fmt[i] != '%') {
|
||||||
vec_push(buffer, fmt[i]);
|
vec_push(buffer, fmt[i]);
|
||||||
continue;
|
continue;
|
||||||
@@ -185,7 +192,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
|
|||||||
switch (fmt[i]) {
|
switch (fmt[i]) {
|
||||||
case 'd':
|
case 'd':
|
||||||
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]);
|
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]);
|
vec_push(buffer, tmp_buf[j]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -193,7 +200,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
|
|||||||
vec_push(buffer, (char)args[arg_idx]);
|
vec_push(buffer, (char)args[arg_idx]);
|
||||||
break;
|
break;
|
||||||
case 's':
|
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]);
|
vec_push(buffer, ((const char *)args[arg_idx])[j]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -76,10 +76,10 @@ static int spl_type_size(spl_type_t t) {
|
|||||||
do { \
|
do { \
|
||||||
if (vm->sp >= vm->config.max_stack_depth) \
|
if (vm->sp >= vm->config.max_stack_depth) \
|
||||||
VM_ERROR("stack overflow"); \
|
VM_ERROR("stack overflow"); \
|
||||||
vm->stacks.data[vm->sp++] = (spl_val_t)(v); \
|
vm->stacks.data[(vm->sp)++] = (spl_val_t)(v); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define POP() vm->stacks.data[--vm->sp]
|
#define POP() vm->stacks.data[--(vm->sp)]
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* Type-dispatch macros for arithmetic / comparison
|
* 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); \
|
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
_r = _a OP _b; \
|
_r = _a OP _b; \
|
||||||
break; \
|
break; \
|
||||||
default: \
|
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); \
|
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
|
_r = (spl_val_t)((int64_t)_a OP(int64_t) _b); \
|
||||||
break; \
|
break; \
|
||||||
default: \
|
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); \
|
_r = (spl_val_t)((uint64_t)_a OP(uint64_t) _b); \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
_r = _a OP _b; \
|
_r = _a OP _b; \
|
||||||
break; \
|
break; \
|
||||||
default: \
|
default: \
|
||||||
@@ -274,6 +280,8 @@ static int spl_type_size(spl_type_t t) {
|
|||||||
_r = (int64_t)_a OP(int64_t) _b; \
|
_r = (int64_t)_a OP(int64_t) _b; \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
_r = _a OP _b; \
|
_r = _a OP _b; \
|
||||||
break; \
|
break; \
|
||||||
case SPL_F32: { \
|
case SPL_F32: { \
|
||||||
@@ -325,7 +333,9 @@ static int spl_type_size(spl_type_t t) {
|
|||||||
_r = (int64_t)_a OP(int64_t) _b; \
|
_r = (int64_t)_a OP(int64_t) _b; \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
_r = (int64_t)_a OP(int64_t) _b; \
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
|
_r = _a OP _b; \
|
||||||
break; \
|
break; \
|
||||||
case SPL_F32: { \
|
case SPL_F32: { \
|
||||||
float _fa, _fb; \
|
float _fa, _fb; \
|
||||||
@@ -376,6 +386,8 @@ static int spl_type_size(spl_type_t t) {
|
|||||||
_r = (uint64_t)_a OP(uint64_t) _b; \
|
_r = (uint64_t)_a OP(uint64_t) _b; \
|
||||||
break; \
|
break; \
|
||||||
case SPL_U64: \
|
case SPL_U64: \
|
||||||
|
case SPL_USIZE: \
|
||||||
|
case SPL_ISIZE: \
|
||||||
_r = _a OP _b; \
|
_r = _a OP _b; \
|
||||||
break; \
|
break; \
|
||||||
default: \
|
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) {
|
void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl);
|
SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl);
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
SetConsoleCP(CP_UTF8);
|
||||||
#endif
|
#endif
|
||||||
if (!vm)
|
if (!vm)
|
||||||
return;
|
return;
|
||||||
@@ -627,7 +641,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
return -1;
|
return -1;
|
||||||
prog = vm->prog;
|
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);
|
fprintf(stderr, "vm: ip=%zd out of bounds\n", vm->ip);
|
||||||
vm->exit_code = 1;
|
vm->exit_code = 1;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -673,7 +687,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
|
|
||||||
case SPL_PICK: {
|
case SPL_PICK: {
|
||||||
isize _idx = ins->imm;
|
isize _idx = ins->imm;
|
||||||
if (_idx >= vm->sp)
|
if ((usize)_idx >= vm->sp)
|
||||||
VM_ERROR("PICK: index out of range");
|
VM_ERROR("PICK: index out of range");
|
||||||
PUSH(vm->stacks.data[vm->sp - 1 - _idx]);
|
PUSH(vm->stacks.data[vm->sp - 1 - _idx]);
|
||||||
break;
|
break;
|
||||||
@@ -875,7 +889,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SPL_LADDR:
|
case SPL_LADDR:
|
||||||
PUSH(vm->stacks.data + vm->fp + ins->imm);
|
PUSH((spl_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SPL_GADDR: {
|
case SPL_GADDR: {
|
||||||
@@ -983,8 +997,23 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
|
|
||||||
/* ========== Debug ========== */
|
/* ========== Debug ========== */
|
||||||
case SPL_DBG: {
|
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);
|
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:
|
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);
|
fprintf(stderr, " stack (sp=%zd, fp=%zd):\n", sp, vm->fp);
|
||||||
spl_val_t start = sp > 16 ? sp - 16 : 0;
|
spl_val_t start = sp > 16 ? sp - 16 : 0;
|
||||||
for (spl_val_t i = start; i < sp; i++) {
|
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_ip = vm->frames.data[i].saved_ip;
|
||||||
spl_val_t _saved_fp = vm->frames.data[i].saved_fp;
|
spl_val_t _saved_fp = vm->frames.data[i].saved_fp;
|
||||||
const char *_fn = func_name_by_ip(vm->prog, _saved_ip - 1);
|
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);
|
vm->frames.data[i].nargs);
|
||||||
}
|
}
|
||||||
const char *_cur = func_name_by_ip(vm->prog, vm->ip);
|
const char *_cur = func_name_by_ip(vm->prog, vm->ip);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* spl_comp.c — SPL compiler main logic and codegen helpers */
|
/* spl_comp.c — SPL compiler main logic and codegen helpers */
|
||||||
|
|
||||||
#include "spl_comp.h"
|
#include "spl_comp.h"
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
/* ---- Compiler context init/drop ---- */
|
/* ---- 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);
|
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
||||||
spl_prog_init(&ctx->prog);
|
spl_prog_init(&ctx->prog);
|
||||||
ctx->error_msg[0] = '\0';
|
ctx->error_msg[0] = '\0';
|
||||||
|
ctx->current_type_name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spl_comp_drop(spl_comp_t *ctx) {
|
void spl_comp_drop(spl_comp_t *ctx) {
|
||||||
if (!ctx) return;
|
if (!ctx)
|
||||||
|
return;
|
||||||
spl_tok_vec_drop(&ctx->toks);
|
spl_tok_vec_drop(&ctx->toks);
|
||||||
vec_for(ctx->scopes, i) {
|
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||||
vec_free(vec_at(ctx->scopes, i).vars);
|
|
||||||
}
|
|
||||||
vec_free(ctx->scopes);
|
vec_free(ctx->scopes);
|
||||||
vec_free(ctx->funcs);
|
vec_free(ctx->funcs);
|
||||||
/* Free type defs — complex, leak for now in bootstrap */
|
/* 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 */
|
/* Keep prog, reset everything else */
|
||||||
spl_tok_vec_drop(&ctx->toks);
|
spl_tok_vec_drop(&ctx->toks);
|
||||||
vec_init(ctx->toks);
|
vec_init(ctx->toks);
|
||||||
vec_for(ctx->scopes, i) {
|
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||||
vec_free(vec_at(ctx->scopes, i).vars);
|
|
||||||
}
|
|
||||||
vec_free(ctx->scopes);
|
vec_free(ctx->scopes);
|
||||||
vec_init(ctx->scopes);
|
vec_init(ctx->scopes);
|
||||||
ctx->scope_depth = 0;
|
ctx->scope_depth = 0;
|
||||||
@@ -49,7 +47,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
|||||||
ctx->error_msg[0] = '\0';
|
ctx->error_msg[0] = '\0';
|
||||||
ctx->current_func_idx = -1;
|
ctx->current_func_idx = -1;
|
||||||
ctx->current_ret_type = NULL;
|
ctx->current_ret_type = NULL;
|
||||||
ctx->current_local_slot = 0;
|
ctx->current_local_bytes = 0;
|
||||||
ctx->in_loop = 0;
|
ctx->in_loop = 0;
|
||||||
ctx->break_patch_count = 0;
|
ctx->break_patch_count = 0;
|
||||||
ctx->break_patch_cap = 0;
|
ctx->break_patch_cap = 0;
|
||||||
@@ -57,6 +55,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
|||||||
ctx->defer_count = 0;
|
ctx->defer_count = 0;
|
||||||
ctx->next_gdata_idx = 0;
|
ctx->next_gdata_idx = 0;
|
||||||
ctx->addr_of_mode = 0;
|
ctx->addr_of_mode = 0;
|
||||||
|
ctx->current_type_name = NULL;
|
||||||
free(ctx->break_patches);
|
free(ctx->break_patches);
|
||||||
ctx->break_patches = NULL;
|
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);
|
return spl_emit(ctx, SPL_JMP, SPL_VOID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_val_t spl_emit_bz(spl_comp_t *ctx) {
|
spl_val_t spl_emit_bz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BZ, SPL_VOID, 0); }
|
||||||
return spl_emit(ctx, SPL_BZ, SPL_VOID, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_val_t spl_emit_bnz(spl_comp_t *ctx) {
|
spl_val_t spl_emit_bnz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0); }
|
||||||
return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) {
|
void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) {
|
||||||
/* Compute relative offset: target - (source + 1) */
|
/* 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) {
|
void spl_pop_scope(spl_comp_t *ctx) {
|
||||||
if (vec_size(ctx->scopes) > 0) {
|
if (vec_size(ctx->scopes) > 0) {
|
||||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||||
vec_for(scope->vars, i) {
|
vec_for(scope->vars, i) { free(vec_at(scope->vars, i).name); }
|
||||||
free(vec_at(scope->vars, i).name);
|
|
||||||
}
|
|
||||||
vec_free(scope->vars);
|
vec_free(scope->vars);
|
||||||
ctx->scope_depth--;
|
ctx->scope_depth--;
|
||||||
ctx->scopes.size--;
|
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.type = type;
|
||||||
var.is_const = is_const;
|
var.is_const = is_const;
|
||||||
var.depth = ctx->scope_depth;
|
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);
|
usize var_size = spl_type_size(type);
|
||||||
ctx->current_local_slot += (int)slot_count;
|
/* 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 */
|
/* Add to current scope */
|
||||||
if (vec_size(ctx->scopes) > 0) {
|
if (vec_size(ctx->scopes) > 0) {
|
||||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||||
vec_push(scope->vars, var);
|
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) {
|
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;
|
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);
|
spl_var_info_t *v = spl_lookup_var(ctx, name);
|
||||||
return v ? v->slot : -1;
|
return v ? v->offset : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Function management ---- */
|
/* ---- Function management ---- */
|
||||||
|
|
||||||
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type,
|
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams,
|
||||||
int nparams, int is_extern, int is_pub) {
|
int is_extern, int is_pub) {
|
||||||
spl_func_info_t fi;
|
spl_func_info_t fi;
|
||||||
memset(&fi, 0, sizeof(fi));
|
memset(&fi, 0, sizeof(fi));
|
||||||
fi.name = strdup(name);
|
fi.name = strdup(name);
|
||||||
@@ -197,7 +194,7 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) {
|
|||||||
/* ---- String/data management ---- */
|
/* ---- String/data management ---- */
|
||||||
|
|
||||||
int spl_add_string(spl_comp_t *ctx, const char *str) {
|
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) {
|
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 ---- */
|
/* ---- Defer ---- */
|
||||||
|
|
||||||
void spl_emit_defer(spl_comp_t *ctx) {
|
void spl_emit_defer(spl_comp_t *ctx) {
|
||||||
/* Record current ip for later patching */
|
if (ctx->defer_count >= DEFER_MAX)
|
||||||
if (ctx->defer_count < DEFER_MAX) {
|
return;
|
||||||
ctx->defer_addrs[ctx->defer_count++] = vec_size(ctx->prog.insns);
|
|
||||||
}
|
/* 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) {
|
void spl_emit_defer_epilogue(spl_comp_t *ctx, int depth) {
|
||||||
/* Emit deferコード in reverse order */
|
/* === Pass 1: patch skip JMPs to jump past their defer body ===
|
||||||
for (int i = ctx->defer_count - 1; i >= 0; i--) {
|
* During normal execution, the skip JMP at body_start-1 must jump
|
||||||
/* The code after defer_addrs[i] is the defer body */
|
* over the defer body (to jmp_exit + 1 = the code after the defer). */
|
||||||
/* We need to JMP over it, but the body is inline */
|
for (int i = 0; i < ctx->defer_count; i++) {
|
||||||
/* This is a simplified approach — just mark the range */
|
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 ---- */
|
/* ---- 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 */
|
/* Phase 2-4: Parse and codegen */
|
||||||
spl_parse_prog(ctx);
|
spl_parse_prog(ctx);
|
||||||
if (ctx->has_error) return -1;
|
if (ctx->has_error)
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ typedef enum {
|
|||||||
TYPE_ARRAY,
|
TYPE_ARRAY,
|
||||||
TYPE_SLICE,
|
TYPE_SLICE,
|
||||||
TYPE_STRUCT,
|
TYPE_STRUCT,
|
||||||
|
TYPE_UNION,
|
||||||
TYPE_ENUM,
|
TYPE_ENUM,
|
||||||
TYPE_ENUM_VARIANT, /* enum variant with data */
|
TYPE_ENUM_VARIANT, /* enum variant with data */
|
||||||
TYPE_NAME, /* named alias */
|
TYPE_NAME, /* named alias */
|
||||||
@@ -46,6 +47,13 @@ typedef struct {
|
|||||||
} spl_enum_variant_t;
|
} spl_enum_variant_t;
|
||||||
typedef VEC(spl_enum_variant_t) spl_enum_variant_vec_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 {
|
struct spl_type_info {
|
||||||
spl_type_kind_t kind;
|
spl_type_kind_t kind;
|
||||||
spl_type_t basic_type; /* for TYPE_BASIC */
|
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 */
|
char *name; /* type name for TYPE_NAME/STRUCT/ENUM */
|
||||||
spl_field_vec_t fields; /* for TYPE_STRUCT */
|
spl_field_vec_t fields; /* for TYPE_STRUCT */
|
||||||
spl_enum_variant_vec_t variants; /* for TYPE_ENUM */
|
spl_enum_variant_vec_t variants; /* for TYPE_ENUM */
|
||||||
|
spl_method_info_vec_t methods; /* methods */
|
||||||
usize byte_size; /* total byte size (cached) */
|
usize byte_size; /* total byte size (cached) */
|
||||||
usize slot_count; /* stack slot count */
|
usize slot_count; /* stack slot count */
|
||||||
int is_pub; /* public visibility */
|
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_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_slice(spl_type_info_t *elem);
|
||||||
spl_type_info_t *spl_type_struct(const char *name);
|
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);
|
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_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_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);
|
void spl_type_compute_layout(spl_type_info_t *t);
|
||||||
usize spl_type_size(spl_type_info_t *t);
|
usize spl_type_size(spl_type_info_t *t);
|
||||||
usize spl_type_slot_count(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);
|
const char *spl_type_str(spl_type_info_t *t);
|
||||||
int spl_type_is_integer(spl_type_t bt);
|
int spl_type_is_integer(spl_type_t bt);
|
||||||
spl_type_info_t *spl_type_clone(spl_type_info_t *t);
|
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 {
|
typedef struct spl_var_info {
|
||||||
char *name;
|
char *name;
|
||||||
spl_type_info_t *type;
|
spl_type_info_t *type;
|
||||||
int slot; /* stack slot offset from fp */
|
int offset; /* byte offset from fp */
|
||||||
int is_const;
|
int is_const;
|
||||||
int depth; /* scope depth */
|
int depth; /* scope depth */
|
||||||
} spl_var_info_t;
|
} 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 COMP_ERROR_MAX 256
|
||||||
#define DEFER_MAX 64
|
#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 {
|
typedef struct {
|
||||||
/* Lexer output */
|
/* Lexer output */
|
||||||
spl_tok_vec_t toks;
|
spl_tok_vec_t toks;
|
||||||
@@ -141,7 +161,8 @@ typedef struct {
|
|||||||
/* Current function context */
|
/* Current function context */
|
||||||
int current_func_idx;
|
int current_func_idx;
|
||||||
spl_type_info_t *current_ret_type;
|
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 */
|
/* Loop context for break/continue */
|
||||||
int in_loop;
|
int in_loop;
|
||||||
@@ -151,7 +172,7 @@ typedef struct {
|
|||||||
usize continue_target; /* ip to jump to for continue */
|
usize continue_target; /* ip to jump to for continue */
|
||||||
|
|
||||||
/* Defer stack */
|
/* Defer stack */
|
||||||
usize defer_addrs[DEFER_MAX];
|
spl_defer_entry_t defer_stack[DEFER_MAX];
|
||||||
int defer_count;
|
int defer_count;
|
||||||
|
|
||||||
/* Global data index for string literals */
|
/* 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 */
|
/* 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, spl_type_info_t *type, int is_const);
|
||||||
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name);
|
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 */
|
/* 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);
|
||||||
@@ -243,10 +264,14 @@ void spl_comp_register(spl_prog_t *prog);
|
|||||||
|
|
||||||
/* Defer */
|
/* Defer */
|
||||||
void spl_emit_defer(spl_comp_t *ctx);
|
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 */
|
/* Type lookup and parsing */
|
||||||
spl_type_info_t *spl_resolve_type(spl_comp_t *ctx, const char *name);
|
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);
|
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__ */
|
#endif /* __SPL_COMP_H__ */
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,36 @@
|
|||||||
/* spl_lex_util.c — Lexer utility functions */
|
/* spl_lex_util.c — Lexer utility functions */
|
||||||
|
|
||||||
#include "spl_lexer.h"
|
#include "spl_lex_util.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.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) {
|
const char *spl_tok_type_name(spl_tok_type_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
#define X(name, enum_name, dummy) \
|
#define X(name, enum_name, dummy) \
|
||||||
case enum_name: \
|
case enum_name: \
|
||||||
return #name;
|
return #name;
|
||||||
KEYWORD_TABLE
|
KEYWORD_TABLE
|
||||||
TOKEN_TABLE
|
TOKEN_TABLE
|
||||||
@@ -33,9 +55,7 @@ void spl_tok_dump(spl_tok_t *tok) {
|
|||||||
buf[i] = ' ';
|
buf[i] = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s:%zu:%zu %-20s '%s'",
|
printf("%s:%zu:%zu %-20s '%s'", tok->fname ? tok->fname : "", tok->line, tok->col,
|
||||||
tok->fname ? tok->fname : "",
|
|
||||||
tok->line, tok->col,
|
|
||||||
spl_tok_type_name(tok->type), buf);
|
spl_tok_type_name(tok->type), buf);
|
||||||
|
|
||||||
if (tok->type == TOK_INT_LITERAL) {
|
if (tok->type == TOK_INT_LITERAL) {
|
||||||
@@ -67,3 +87,12 @@ void spl_tok_vec_drop(spl_tok_vec_t *toks) {
|
|||||||
vec_free(*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
12
stage1/spl_lex_util.h
Normal 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__ */
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "spl_lexer.h"
|
#include "spl_lexer.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|||||||
@@ -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_dump(spl_tok_t *tok);
|
||||||
void spl_tok_vec_dump(spl_tok_vec_t *toks);
|
void spl_tok_vec_dump(spl_tok_vec_t *toks);
|
||||||
void spl_tok_vec_drop(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. */
|
/* Decode escape sequence, advance *s past it. Returns 0 on success. */
|
||||||
int spl_decode_escape(const char **s, char *out);
|
int spl_decode_escape(const char **s, char *out);
|
||||||
|
|||||||
@@ -1,29 +1,18 @@
|
|||||||
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
|
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
|
||||||
|
|
||||||
#include "spl_comp.h"
|
#include "spl_comp.h"
|
||||||
#include <stdio.h>
|
#include "spl_lex_util.h"
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static spl_tok_t *peek(spl_comp_t *ctx) {
|
spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
|
||||||
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);
|
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;
|
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
|
* 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)
|
* 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) {
|
static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
||||||
advance(ctx); /* fn */
|
advance(ctx); /* fn */
|
||||||
skip_nl(ctx);
|
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);
|
spl_tok_t *fname_tok = advance(ctx);
|
||||||
char fn_name[256];
|
char fn_name[256];
|
||||||
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255;
|
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);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_PAREN);
|
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++;
|
nparams++;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); continue; }
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
if (peek(ctx)->type == TOK_ELLIPSIS) { advance(ctx); skip_nl(ctx); }
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
}
|
||||||
break;
|
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);
|
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
|
||||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||||
ret_type = spl_parse_type(ctx);
|
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);
|
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;
|
int found = -1;
|
||||||
vec_for(ctx->prog.natives, ni) {
|
vec_for(ctx->prog.natives, ni) {
|
||||||
if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) {
|
if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) {
|
||||||
found = (int)ni; break;
|
found = (int)ni;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found < 0) {
|
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 */
|
nat.impl_fn = NULL; /* resolved by VM at runtime */
|
||||||
vec_push(ctx->prog.natives, nat);
|
vec_push(ctx->prog.natives, nat);
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
|
advance(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,79 +153,73 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
|||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Declare function in prog */
|
/* Use shared helper for function body parsing */
|
||||||
int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub);
|
parse_fn_body(ctx, fn_name, ret_type, nparams, pnames, ptypes, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Parse type declaration
|
* Parse struct/union body (shared for struct and union containers)
|
||||||
* type Name = struct/enum { ... };
|
*
|
||||||
* type Name = ExistingType;
|
* 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) {
|
static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||||
advance(ctx); /* type */
|
if (peek(ctx)->type != TOK_L_BRACE)
|
||||||
spl_tok_t *name_tok = advance(ctx);
|
return;
|
||||||
char tname[256];
|
advance(ctx); /* { */
|
||||||
usize tnl = name_tok->len < 255 ? name_tok->len : 255;
|
|
||||||
memcpy(tname, name_tok->lexeme, tnl); tname[tnl] = '\0';
|
|
||||||
|
|
||||||
skip_nl(ctx);
|
/* === Pass 1: Parse all nested type declarations first ===
|
||||||
expect(ctx, TOK_ASSIGN);
|
* This allows field declarations to reference types defined later in the body. */
|
||||||
skip_nl(ctx);
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (peek(ctx)->type == KW_STRUCT) {
|
/* === Pass 2: Parse fields and methods === */
|
||||||
advance(ctx);
|
{
|
||||||
spl_type_info_t *st = spl_type_struct(tname);
|
int depth = 1;
|
||||||
skip_nl(ctx);
|
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
|
||||||
advance(ctx); /* { */
|
|
||||||
skip_nl(ctx);
|
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) {
|
||||||
|
/* 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);
|
||||||
spl_tok_t *ftok = advance(ctx);
|
spl_tok_t *ftok = advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_COLON) {
|
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);
|
spl_type_info_t *ftype = spl_parse_type(ctx);
|
||||||
char fname[256];
|
char fname[256];
|
||||||
usize fnl = ftok->len < 255 ? ftok->len : 255;
|
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);
|
spl_type_add_field(st, fname, ftype);
|
||||||
}
|
}
|
||||||
skip_nl(ctx);
|
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 == TOK_IDENT && depth == 1) {
|
||||||
|
/* 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);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
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) {
|
spl_type_compute_layout(st);
|
||||||
advance(ctx);
|
}
|
||||||
spl_type_info_t *et = spl_type_enum(tname);
|
|
||||||
skip_nl(ctx);
|
/* ============================================================
|
||||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
* Parse enum body
|
||||||
advance(ctx); /* { */
|
*
|
||||||
|
* 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);
|
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);
|
spl_tok_t *vtok = advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_COLON) {
|
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);
|
spl_type_info_t *dtype = spl_parse_type(ctx);
|
||||||
char vname[256];
|
char vname[256];
|
||||||
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
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);
|
spl_type_add_variant(et, vname, dtype);
|
||||||
} else {
|
} else {
|
||||||
char vname[256];
|
char vname[256];
|
||||||
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
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);
|
spl_type_add_variant(et, vname, NULL);
|
||||||
}
|
}
|
||||||
skip_nl(ctx);
|
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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_R_BRACE) advance(ctx);
|
|
||||||
}
|
}
|
||||||
spl_type_compute_layout(et);
|
}
|
||||||
|
|
||||||
|
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);
|
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);
|
spl_type_info_t *base = spl_parse_type(ctx);
|
||||||
if (base) {
|
if (base) {
|
||||||
spl_type_info_t *alias = spl_type_clone(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);
|
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) {
|
void spl_parse_prog(spl_comp_t *ctx) {
|
||||||
while (peek(ctx)->type != TOK_EOF) {
|
while (peek(ctx)->type != TOK_EOF) {
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_EOF) break;
|
if (peek(ctx)->type == TOK_EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
switch (peek(ctx)->type) {
|
switch (peek(ctx)->type) {
|
||||||
case KW_FN:
|
case KW_FN:
|
||||||
@@ -266,8 +585,10 @@ void spl_parse_prog(spl_comp_t *ctx) {
|
|||||||
case KW_PUB: {
|
case KW_PUB: {
|
||||||
advance(ctx); /* skip pub */
|
advance(ctx); /* skip pub */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == KW_FN) parse_fn_decl(ctx, 0, 1);
|
if (peek(ctx)->type == KW_FN)
|
||||||
else if (peek(ctx)->type == KW_TYPE) parse_type_decl(ctx);
|
parse_fn_decl(ctx, 0, 1);
|
||||||
|
else if (peek(ctx)->type == KW_TYPE)
|
||||||
|
parse_type_decl(ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TOK_SHARP:
|
case TOK_SHARP:
|
||||||
@@ -275,18 +596,22 @@ void spl_parse_prog(spl_comp_t *ctx) {
|
|||||||
advance(ctx); /* # */
|
advance(ctx); /* # */
|
||||||
if (peek(ctx)->type == TOK_L_BRACKET) {
|
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
advance(ctx); /* [ */
|
advance(ctx); /* [ */
|
||||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF) advance(ctx);
|
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||||
if (peek(ctx)->type == TOK_R_BRACKET) advance(ctx);
|
advance(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||||
|
advance(ctx);
|
||||||
}
|
}
|
||||||
skip_nl(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;
|
break;
|
||||||
default: {
|
default: {
|
||||||
int prev = ctx->tok_idx;
|
int prev = ctx->tok_idx;
|
||||||
/* Try to parse as a statement */
|
/* Try to parse as a statement */
|
||||||
spl_parse_stmt(ctx);
|
spl_parse_stmt(ctx);
|
||||||
/* Safety: prevent infinite loop on unrecognized tokens */
|
/* Safety: prevent infinite loop on unrecognized tokens */
|
||||||
if (ctx->tok_idx == prev) advance(ctx);
|
if (ctx->tok_idx == prev)
|
||||||
|
advance(ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,10 @@
|
|||||||
/* spl_stmt.c — Statement parser + codegen */
|
/* spl_stmt.c — Statement parser + codegen */
|
||||||
|
|
||||||
#include "spl_comp.h"
|
#include "spl_comp.h"
|
||||||
#include <stdio.h>
|
#include "spl_lex_util.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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;
|
* Return statement: ret expr;
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@@ -38,6 +13,11 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
|
|||||||
advance(ctx); /* ret */
|
advance(ctx); /* ret */
|
||||||
skip_nl(ctx);
|
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 ||
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_R_BRACE ||
|
||||||
peek(ctx)->type == TOK_ENDLINE) {
|
peek(ctx)->type == TOK_ENDLINE) {
|
||||||
/* void return */
|
/* void return */
|
||||||
@@ -45,10 +25,21 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
|
|||||||
} else {
|
} else {
|
||||||
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)val;
|
(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);
|
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);
|
spl_tok_t *name_tok = advance(ctx);
|
||||||
char vname[256];
|
char vname[256];
|
||||||
usize nlen = name_tok->len < 255 ? name_tok->len : 255;
|
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;
|
spl_type_info_t *var_type = NULL;
|
||||||
int has_init = 0;
|
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) {
|
if (peek(ctx)->type == TOK_COLON_ASSIGN || peek(ctx)->type == TOK_ASSIGN) {
|
||||||
has_init = 1;
|
has_init = 1;
|
||||||
if (peek(ctx)->type == TOK_COLON_ASSIGN) advance(ctx);
|
if (peek(ctx)->type == TOK_COLON_ASSIGN)
|
||||||
else advance(ctx); /* = */
|
advance(ctx);
|
||||||
|
else
|
||||||
|
advance(ctx); /* = */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate stack slot */
|
/* Init expression: parse before declare to enable type inference */
|
||||||
if (!var_type) var_type = spl_type_basic(SPL_I32); /* default type */
|
spl_expr_result_t init = {0};
|
||||||
int slot = spl_declare_var(ctx, vname, var_type, is_const);
|
|
||||||
|
|
||||||
skip_nl(ctx);
|
|
||||||
|
|
||||||
/* Init expression */
|
|
||||||
if (has_init) {
|
if (has_init) {
|
||||||
spl_expr_result_t init = spl_parse_expr(ctx, PREC_MIN);
|
skip_nl(ctx);
|
||||||
(void)init;
|
init = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
if (var_type && var_type->kind == TYPE_ARRAY) {
|
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);
|
||||||
|
|
||||||
|
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
(void)fv;
|
||||||
|
|
||||||
|
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 */
|
/* Array initialization: store each element in reverse stack order */
|
||||||
spl_type_info_t *elem = var_type->elem;
|
spl_type_info_t *elem = var_type->elem;
|
||||||
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
|
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--) {
|
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_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
}
|
}
|
||||||
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
||||||
/* Slice initialization: stack has [ptr, len] from slice expression.
|
/* Slice initialization: stack has [ptr, len] from slice expression.
|
||||||
* Store len at slot+1, then ptr at slot. */
|
* Store len at offset+8, then ptr at offset. */
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + 1);
|
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_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, SPL_I32, 0);
|
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot);
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
} else {
|
} else {
|
||||||
/* Single value store */
|
/* Single value store */
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot);
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
|
||||||
spl_type_t bt = var_type->kind == TYPE_BASIC ? var_type->basic_type : SPL_I32;
|
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_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, bt, 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);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
if (peek(ctx)->type == TOK_R_BRACE) advance(ctx);
|
if (peek(ctx)->type == TOK_R_BRACE)
|
||||||
|
advance(ctx);
|
||||||
} else {
|
} else {
|
||||||
/* Single statement */
|
/* Single statement */
|
||||||
spl_parse_stmt(ctx);
|
spl_parse_stmt(ctx);
|
||||||
@@ -257,68 +321,217 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Parse the iteration expression(s) */
|
/* Parse the iteration expression(s) */
|
||||||
spl_expr_result_t start = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t start_expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)start;
|
|
||||||
|
|
||||||
if (peek(ctx)->type == TOK_RANGE) {
|
if (peek(ctx)->type == TOK_RANGE) {
|
||||||
/* for begin..end as i { body } */
|
/* ===== Numeric range: for begin..end as i { body } ===== */
|
||||||
advance(ctx); /* .. */
|
advance(ctx); /* .. */
|
||||||
spl_expr_result_t end = spl_parse_expr(ctx, PREC_MIN);
|
spl_parse_expr(ctx, PREC_MIN); /* end expression */
|
||||||
(void)end;
|
/* Stack: [begin, end] */
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == KW_AS) {
|
if (peek(ctx)->type != KW_AS) {
|
||||||
advance(ctx); /* as */
|
skip_nl(ctx);
|
||||||
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';
|
|
||||||
|
|
||||||
/* Declare loop variable */
|
|
||||||
spl_type_info_t *itype = spl_type_basic(SPL_I32);
|
|
||||||
int islot = spl_declare_var(ctx, iname, itype, 0);
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
|
|
||||||
/* For now, emit a basic loop body */
|
|
||||||
spl_parse_block(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';
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
ctx->in_loop = saved_loop;
|
|
||||||
ctx->continue_target = saved_continue;
|
|
||||||
ctx->break_patch_count = saved_bp_count;
|
|
||||||
}
|
|
||||||
} else if (peek(ctx)->type == TOK_COMMA) {
|
|
||||||
/* for slice, 0.. as val, idx { body } — skip for now */
|
|
||||||
advance(ctx); /* , */
|
|
||||||
spl_parse_expr(ctx, PREC_MIN); /* skip the range */
|
|
||||||
if (peek(ctx)->type == KW_AS) {
|
|
||||||
advance(ctx);
|
|
||||||
advance(ctx); /* val */
|
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
|
||||||
advance(ctx);
|
|
||||||
advance(ctx); /* idx */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx);
|
spl_parse_block(ctx); /* body */
|
||||||
} else {
|
|
||||||
/* for ident in ... — skip */
|
/* Increment: i = i + 1 */
|
||||||
skip_nl(ctx);
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset);
|
||||||
spl_parse_block(ctx);
|
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;
|
||||||
|
|
||||||
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
|
spl_pop_scope(ctx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Slice iteration: for slice [as val |, range as val, idx] ===== */
|
||||||
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
|
advance(ctx); /* , */
|
||||||
|
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);
|
||||||
|
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); /* 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_patch_cap = new_cap;
|
||||||
}
|
}
|
||||||
ctx->break_patches[ctx->break_patch_count++] = addr;
|
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) {
|
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 */
|
/* Emit JMP with relative offset to continue_target */
|
||||||
spl_val_t here = vec_size(ctx->prog.insns);
|
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));
|
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 */
|
advance(ctx); /* defer */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Record current position for defer */
|
/* Record current position for defer (emits a JMP skip placeholder) */
|
||||||
spl_emit_defer(ctx);
|
spl_emit_defer(ctx);
|
||||||
|
|
||||||
/* Parse the deferred statement/block */
|
/* Parse the deferred statement/block */
|
||||||
@@ -369,6 +584,216 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
|
|||||||
} else {
|
} else {
|
||||||
spl_parse_stmt(ctx);
|
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); /* # */
|
advance(ctx); /* # */
|
||||||
expect(ctx, TOK_L_BRACKET);
|
expect(ctx, TOK_L_BRACKET);
|
||||||
/* Skip extern("vm") or just look for fn */
|
/* Skip extern("vm") or just look for fn */
|
||||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF) advance(ctx);
|
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||||
if (peek(ctx)->type == TOK_R_BRACKET) advance(ctx);
|
advance(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||||
|
advance(ctx);
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
@@ -390,7 +817,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
|
|||||||
spl_tok_t *fname_tok = advance(ctx);
|
spl_tok_t *fname_tok = advance(ctx);
|
||||||
char fn_name[256];
|
char fn_name[256];
|
||||||
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255;
|
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);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_PAREN);
|
expect(ctx, TOK_L_PAREN);
|
||||||
@@ -409,8 +837,15 @@ static void parse_extern_decl(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
nparams++;
|
nparams++;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); continue; }
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
if (peek(ctx)->type == TOK_ELLIPSIS) { advance(ctx); skip_nl(ctx); } /* variadic */
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
} /* variadic */
|
||||||
break;
|
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);
|
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
|
||||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||||
ret_type = spl_parse_type(ctx);
|
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);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_declare_func(ctx, fn_name, ret_type, nparams, 1, 0);
|
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 */
|
/* Check if token type is an assignment operator */
|
||||||
static int is_assign_op(spl_tok_type_t t) {
|
static int is_assign_op(spl_tok_type_t t) {
|
||||||
return t == TOK_ASSIGN || t == TOK_ASSIGN_ADD || t == TOK_ASSIGN_SUB ||
|
return t == TOK_ASSIGN || t == TOK_ASSIGN_ADD || t == TOK_ASSIGN_SUB || t == TOK_ASSIGN_MUL ||
|
||||||
t == TOK_ASSIGN_MUL || t == TOK_ASSIGN_DIV || t == TOK_ASSIGN_MOD ||
|
t == TOK_ASSIGN_DIV || t == TOK_ASSIGN_MOD || t == TOK_ASSIGN_AND ||
|
||||||
t == TOK_ASSIGN_AND || t == TOK_ASSIGN_OR || t == TOK_ASSIGN_XOR ||
|
t == TOK_ASSIGN_OR || t == TOK_ASSIGN_XOR || t == TOK_ASSIGN_L_SH ||
|
||||||
t == TOK_ASSIGN_L_SH || t == TOK_ASSIGN_R_SH;
|
t == TOK_ASSIGN_R_SH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_expr_stmt(spl_comp_t *ctx) {
|
static void parse_expr_stmt(spl_comp_t *ctx) {
|
||||||
@@ -450,26 +887,41 @@ static void parse_expr_stmt(spl_comp_t *ctx) {
|
|||||||
return;
|
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;
|
int is_assign = 0;
|
||||||
if (peek(ctx)->type == TOK_IDENT) {
|
{
|
||||||
usize look = ctx->tok_idx + 1;
|
usize look = ctx->tok_idx;
|
||||||
while (look < vec_size(ctx->toks) && vec_at(ctx->toks, look).type == TOK_ENDLINE)
|
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++;
|
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);
|
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 (!is_assign && expr.type && expr.type->kind == TYPE_BASIC &&
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx);
|
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 */
|
/* 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) {
|
void spl_parse_stmt(spl_comp_t *ctx) {
|
||||||
skip_nl(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) {
|
switch (peek(ctx)->type) {
|
||||||
case KW_RET:
|
case KW_RET:
|
||||||
@@ -512,6 +965,9 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
|||||||
case KW_DEFER:
|
case KW_DEFER:
|
||||||
parse_defer_stmt(ctx);
|
parse_defer_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
|
case KW_MATCH:
|
||||||
|
parse_match_stmt(ctx);
|
||||||
|
break;
|
||||||
case KW_TYPE:
|
case KW_TYPE:
|
||||||
parse_type_decl(ctx);
|
parse_type_decl(ctx);
|
||||||
break;
|
break;
|
||||||
@@ -521,7 +977,7 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
|||||||
case TOK_LINE_COMMENT:
|
case TOK_LINE_COMMENT:
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
break;
|
break;
|
||||||
case TOK_SHARP: /* #[extern(...)] */
|
case TOK_SHARP: /* #[extern(...)] */
|
||||||
parse_extern_decl(ctx);
|
parse_extern_decl(ctx);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -132,6 +132,18 @@ spl_type_info_t *spl_type_struct(const char *name) {
|
|||||||
if (name)
|
if (name)
|
||||||
t->name = strdup(name);
|
t->name = strdup(name);
|
||||||
vec_init(t->fields);
|
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;
|
t->resolved = 0;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@@ -142,6 +154,7 @@ spl_type_info_t *spl_type_enum(const char *name) {
|
|||||||
if (name)
|
if (name)
|
||||||
t->name = strdup(name);
|
t->name = strdup(name);
|
||||||
vec_init(t->variants);
|
vec_init(t->variants);
|
||||||
|
vec_init(t->methods);
|
||||||
t->byte_size = 4;
|
t->byte_size = 4;
|
||||||
t->slot_count = 1;
|
t->slot_count = 1;
|
||||||
t->resolved = 0;
|
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);
|
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) {
|
void spl_type_compute_layout(spl_type_info_t *t) {
|
||||||
if (!t || t->resolved)
|
if (!t || t->resolved)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (t->kind == TYPE_STRUCT) {
|
if (t->kind == TYPE_STRUCT || t->kind == TYPE_UNION) {
|
||||||
usize offset = 0;
|
usize offset = 0;
|
||||||
|
usize max_field_size = 0;
|
||||||
vec_for(t->fields, i) {
|
vec_for(t->fields, i) {
|
||||||
spl_field_t *f = &vec_at(t->fields, i);
|
spl_field_t *f = &vec_at(t->fields, i);
|
||||||
if (f->type) {
|
if (f->type) {
|
||||||
spl_type_compute_layout(f->type);
|
spl_type_compute_layout(f->type);
|
||||||
f->offset = offset;
|
if (t->kind == TYPE_UNION) {
|
||||||
offset += f->type->byte_size;
|
/* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t->byte_size = offset;
|
if (t->kind == TYPE_UNION) {
|
||||||
|
t->byte_size = max_field_size;
|
||||||
|
} else {
|
||||||
|
t->byte_size = offset;
|
||||||
|
}
|
||||||
/* Round up to slot alignment */
|
/* Round up to slot alignment */
|
||||||
usize slot_sz = sizeof(spl_val_t);
|
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;
|
t->resolved = 1;
|
||||||
} else if (t->kind == TYPE_ENUM) {
|
} else if (t->kind == TYPE_ENUM) {
|
||||||
/* Enums with data need special handling */
|
/* Enums with data need special handling */
|
||||||
@@ -224,6 +259,11 @@ usize spl_type_slot_count(spl_type_info_t *t) {
|
|||||||
return t->slot_count;
|
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) {
|
const char *spl_type_str(spl_type_info_t *t) {
|
||||||
if (!t)
|
if (!t)
|
||||||
return "<null>";
|
return "<null>";
|
||||||
@@ -281,6 +321,8 @@ const char *spl_type_str(spl_type_info_t *t) {
|
|||||||
}
|
}
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
return t->name ? t->name : "struct";
|
return t->name ? t->name : "struct";
|
||||||
|
case TYPE_UNION:
|
||||||
|
return t->name ? t->name : "union";
|
||||||
case TYPE_ENUM:
|
case TYPE_ENUM:
|
||||||
return t->name ? t->name : "enum";
|
return t->name ? t->name : "enum";
|
||||||
case TYPE_NAME:
|
case TYPE_NAME:
|
||||||
@@ -349,6 +391,67 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
|
|||||||
return spl_type_array(elem, len);
|
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 */
|
/* Identifier: basic type or named type */
|
||||||
if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) {
|
if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) {
|
||||||
const char *name = tok->lexeme;
|
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) {
|
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;
|
spl_type_info_t *found = NULL;
|
||||||
map_get(ctx->type_defs, name, &found);
|
map_get(ctx->type_defs, name, &found);
|
||||||
return found;
|
return found;
|
||||||
|
|||||||
@@ -47,8 +47,18 @@ fn main() i32 {
|
|||||||
|
|
||||||
/* ---- 指向数组元素的指针 ---- */
|
/* ---- 指向数组元素的指针 ---- */
|
||||||
var buf: [4]i32 = [4]i32{1, 2, 3, 4};
|
var buf: [4]i32 = [4]i32{1, 2, 3, 4};
|
||||||
|
// @dbg(); /* 数组初始化后的栈状态 */
|
||||||
var elem_ptr: *i32 = &buf[0];
|
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;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,5 @@ fn main() i32 {
|
|||||||
}
|
}
|
||||||
if s != 20 { ret 6; }
|
if s != 20 { ret 6; }
|
||||||
|
|
||||||
/* 多维效果:数组元素为数组 */
|
|
||||||
/* 目前只测试一维 */
|
|
||||||
|
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ fn main() i32 {
|
|||||||
if r != 25 { ret 6; }
|
if r != 25 { ret 6; }
|
||||||
|
|
||||||
/* 结构体嵌套 */
|
/* 结构体嵌套 */
|
||||||
type Inner = struct {
|
|
||||||
val: i32,
|
|
||||||
}
|
|
||||||
type Outer = struct {
|
type Outer = struct {
|
||||||
inner: Inner,
|
inner: Inner,
|
||||||
extra: i32,
|
extra: i32,
|
||||||
|
type Inner = struct {
|
||||||
|
val: i32,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var o: Outer;
|
var o: Outer;
|
||||||
|
|||||||
@@ -12,11 +12,18 @@ type Color = enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Expr = enum {
|
type Expr = enum {
|
||||||
Int: i32,
|
val: i32,
|
||||||
Add: struct { left: *Expr, right: *Expr },
|
tag: Tag,
|
||||||
|
|
||||||
|
type Tag = enum {
|
||||||
|
TagA,
|
||||||
|
TagB,
|
||||||
|
TagC,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() i32 {
|
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", 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;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ fn identity(x: i32) i32 {
|
|||||||
ret x;
|
ret x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn addr(x: *i32) *i32 {
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
|
|
||||||
fn main() i32 {
|
fn main() i32 {
|
||||||
/* 函数调用 */
|
/* 函数调用 */
|
||||||
var r1 := add(3, 4);
|
var r1 := add(3, 4);
|
||||||
@@ -42,5 +46,10 @@ fn main() i32 {
|
|||||||
var r6 := add(add(1, 2), add(3, 4));
|
var r6 := add(add(1, 2), add(3, 4));
|
||||||
if r6 != 10 { ret 6; }
|
if r6 != 10 { ret 6; }
|
||||||
|
|
||||||
|
/* 返回地址 */
|
||||||
|
var r7 := addr(&r6);
|
||||||
|
if r7 != &r6 { ret 7; }
|
||||||
|
if r7.* != 10 { ret 8; }
|
||||||
|
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,18 @@ fn main() i32 {
|
|||||||
with_cleanup();
|
with_cleanup();
|
||||||
vm_printf("after with_cleanup\n");
|
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 应逆序执行 */
|
||||||
defer vm_printf("defer last\n");
|
defer vm_printf("defer last\n");
|
||||||
defer vm_printf("defer middle\n");
|
defer vm_printf("defer middle\n");
|
||||||
defer vm_printf("defer first (should print third)\n");
|
defer vm_printf("defer first (should print third)\n");
|
||||||
|
|
||||||
vm_printf("--- defer test end ---\n");
|
vm_printf("--- defer test end ---\n");
|
||||||
|
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ fn main() i32 {
|
|||||||
var expr_l := Expr { .Int = 3 };
|
var expr_l := Expr { .Int = 3 };
|
||||||
var expr_r := Expr { .Int = 4 };
|
var expr_r := Expr { .Int = 4 };
|
||||||
var expr := Expr { .Add = { .left = expr_l, .right = expr_r } };
|
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);
|
vm_printf("eval result: %d\n", result);
|
||||||
if result != 7 { ret 1; }
|
if result != 7 { ret 1; }
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ fn fib(n: i32) i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() i32 {
|
fn main() i32 {
|
||||||
vm_printf("=== integration test ===\n");
|
|
||||||
|
|
||||||
/* 综合:指针 + 结构体 + 函数 */
|
/* 综合:指针 + 结构体 + 函数 */
|
||||||
var p1: Point;
|
var p1: Point;
|
||||||
p1.x = 3;
|
p1.x = 3;
|
||||||
@@ -92,9 +90,6 @@ fn main() i32 {
|
|||||||
}
|
}
|
||||||
j = j + 1;
|
j = j + 1;
|
||||||
}
|
}
|
||||||
if outer_sum != 9 { ret 8; }
|
if outer_sum != 3 { ret 8; }
|
||||||
|
|
||||||
vm_printf("=== all integration tests passed ===\n");
|
|
||||||
|
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
184
stage1/test19_hardarray.spl
Normal file
184
stage1/test19_hardarray.spl
Normal 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;
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user