Compare commits
2 Commits
463177d3be
...
53ae30f1ca
| Author | SHA1 | Date | |
|---|---|---|---|
| 53ae30f1ca | |||
| 147f26e063 |
@@ -118,6 +118,10 @@ static spl_val_t vm_fsize(int nargs, spl_val_t *args) {
|
||||
return (spl_val_t)(uintptr_t)sz;
|
||||
}
|
||||
|
||||
SYSCALL_0(vm_stdin, stdin)
|
||||
SYSCALL_0(vm_stdout, stdout)
|
||||
SYSCALL_0(vm_stderr, stderr)
|
||||
|
||||
/* vm_read_file — read entire file into a malloc'd, null-terminated buffer */
|
||||
static spl_val_t vm_read_file(int nargs, spl_val_t *args) {
|
||||
CHECK_NARGS("vm_read_file", 1);
|
||||
@@ -357,6 +361,9 @@ void spl_syscall_register(spl_prog_t *prog) {
|
||||
{"vm_fread", 0, vm_fread},
|
||||
{"vm_fwrite", 0, vm_fwrite},
|
||||
{"vm_fsize", 0, vm_fsize},
|
||||
{"vm_stdin", 0, vm_stdin},
|
||||
{"vm_stdout", 0, vm_stdout},
|
||||
{"vm_stderr", 0, vm_stderr},
|
||||
{"vm_read_file", 0, vm_read_file},
|
||||
{"vm_alloc", 0, vm_alloc},
|
||||
{"vm_free", 0, vm_free},
|
||||
|
||||
@@ -29,6 +29,18 @@
|
||||
* ================================================================ */
|
||||
|
||||
static int spl_is_float(spl_type_t t) { return t == SPL_F32 || t == SPL_F64; }
|
||||
static int spl_is_signed(spl_type_t t) {
|
||||
switch (t) {
|
||||
case SPL_I8:
|
||||
case SPL_I16:
|
||||
case SPL_I32:
|
||||
case SPL_I64:
|
||||
case SPL_ISIZE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static int spl_type_size(spl_type_t t) {
|
||||
switch (t) {
|
||||
case SPL_VOID:
|
||||
@@ -904,7 +916,13 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
case SPL_LOAD: {
|
||||
void *_addr = (void *)POP();
|
||||
spl_val_t _v = 0;
|
||||
memcpy(&_v, _addr, spl_type_size(ins->type));
|
||||
usize _sz = spl_type_size(ins->type);
|
||||
memcpy(&_v, _addr, _sz);
|
||||
/* Sign-extend signed integer types smaller than 64 bits */
|
||||
if (_sz > 0 && _sz < sizeof(spl_val_t) && spl_is_signed(ins->type)) {
|
||||
usize _shift = (sizeof(spl_val_t) - _sz) * 8;
|
||||
_v = (spl_val_t)(((isize)(_v << _shift)) >> _shift);
|
||||
}
|
||||
PUSH(_v);
|
||||
break;
|
||||
}
|
||||
@@ -1071,10 +1089,11 @@ void spl_vm_dump_instr(spl_vm_t *vm, spl_val_t ip) {
|
||||
void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp) {
|
||||
if (!vm)
|
||||
return;
|
||||
fprintf(stderr, " stack (sp=%zd, fp=%zd):\n", sp, vm->fp);
|
||||
spl_val_t start = sp > 16 ? sp - 16 : 0;
|
||||
for (spl_val_t i = start; i < sp; i++) {
|
||||
fprintf(stderr, " [%3zd] = 0x%016zx (%zd)\n", i, vm->stacks.data[i], vm->stacks.data[i]);
|
||||
fprintf(stderr, "stack dump (sp=%zd, fp=%zd):\n", sp, vm->fp);
|
||||
spl_val_t start = sp > 32 ? sp - 32 : 0;
|
||||
for (spl_val_t i = start; i <= sp; i++) {
|
||||
fprintf(stderr, " [%3zd] = [addr 0x%p] 0x%016zx (%zd)\n", i, &vm->stacks.data[i],
|
||||
vm->stacks.data[i], vm->stacks.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,7 +1101,7 @@ int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp) {
|
||||
if (!vm || !vm->prog)
|
||||
return -1;
|
||||
(void)fp;
|
||||
fprintf(stderr, "=== backtrace ===\n");
|
||||
fprintf(stderr, "backtrace: \n");
|
||||
for (isize i = vm->cp - 1; i >= 0; i--) {
|
||||
spl_val_t _saved_ip = vm->frames.data[i].saved_ip;
|
||||
spl_val_t _saved_fp = vm->frames.data[i].saved_fp;
|
||||
|
||||
@@ -48,6 +48,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type = NULL;
|
||||
ctx->current_local_bytes = 0;
|
||||
ctx->peak_local_bytes = 0;
|
||||
ctx->in_loop = 0;
|
||||
ctx->break_patch_count = 0;
|
||||
ctx->break_patch_cap = 0;
|
||||
@@ -96,6 +97,52 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) {
|
||||
spl_patch(ctx, addr, offset);
|
||||
}
|
||||
|
||||
/* ---- Multi-slot copy helper ---- */
|
||||
|
||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
||||
for (usize i = 0; i < nslots; i++) {
|
||||
if (i < nslots - 1)
|
||||
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||
if (i > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t));
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, dest_offset + (int)(i * sizeof(spl_val_t)));
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Uniform LOAD/STORE type helper ---- */
|
||||
|
||||
spl_type_t spl_type_emit_type(spl_type_info_t *type) {
|
||||
if (!type)
|
||||
return SPL_I32;
|
||||
if (type->kind == TYPE_BASIC)
|
||||
return type->basic_type;
|
||||
if (type->kind == TYPE_PTR)
|
||||
return SPL_PTR;
|
||||
return SPL_PTR;
|
||||
}
|
||||
|
||||
/* ---- Load from [saved_ptr+offset], store to local var ---- */
|
||||
|
||||
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
|
||||
spl_type_info_t *data_type, int var_offset) {
|
||||
spl_type_t bt = spl_type_emit_type(data_type);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
if (byte_offset > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
|
||||
/* ---- Scope management ---- */
|
||||
|
||||
void spl_push_scope(spl_comp_t *ctx) {
|
||||
@@ -134,6 +181,8 @@ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, in
|
||||
if (aligned < sizeof(spl_val_t))
|
||||
aligned = sizeof(spl_val_t);
|
||||
ctx->current_local_bytes += (int)aligned;
|
||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||
|
||||
/* Add to current scope */
|
||||
if (vec_size(ctx->scopes) > 0) {
|
||||
|
||||
@@ -23,13 +23,14 @@ typedef enum {
|
||||
TYPE_STRUCT,
|
||||
TYPE_UNION,
|
||||
TYPE_ENUM,
|
||||
TYPE_ENUM_VARIANT, /* enum variant with data */
|
||||
TYPE_NAME, /* named alias */
|
||||
TYPE_INFER, /* _ (to be inferred) */
|
||||
TYPE_ENUM_VARIANT, /* enum variant with data */
|
||||
TYPE_NAME, /* named alias */
|
||||
TYPE_INFER, /* _ (to be inferred) */
|
||||
} spl_type_kind_t;
|
||||
|
||||
/* Forward declaration */
|
||||
/* Forward declarations */
|
||||
typedef struct spl_type_info spl_type_info_t;
|
||||
typedef struct spl_comp spl_comp_t;
|
||||
|
||||
/* Struct field */
|
||||
typedef struct {
|
||||
@@ -50,23 +51,23 @@ 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 */
|
||||
int func_idx; /* index in ctx->funcs */
|
||||
} spl_method_info_t;
|
||||
typedef VEC(spl_method_info_t) spl_method_info_vec_t;
|
||||
|
||||
struct spl_type_info {
|
||||
spl_type_kind_t kind;
|
||||
spl_type_t basic_type; /* for TYPE_BASIC */
|
||||
spl_type_info_t *elem; /* for PTR/ARRAY/SLICE element type */
|
||||
usize array_len; /* for TYPE_ARRAY */
|
||||
char *name; /* type name for TYPE_NAME/STRUCT/ENUM */
|
||||
spl_field_vec_t fields; /* for TYPE_STRUCT */
|
||||
spl_type_t basic_type; /* for TYPE_BASIC */
|
||||
spl_type_info_t *elem; /* for PTR/ARRAY/SLICE element type */
|
||||
usize array_len; /* for TYPE_ARRAY */
|
||||
char *name; /* type name for TYPE_NAME/STRUCT/ENUM */
|
||||
spl_field_vec_t fields; /* for TYPE_STRUCT */
|
||||
spl_enum_variant_vec_t variants; /* for TYPE_ENUM */
|
||||
spl_method_info_vec_t methods; /* methods */
|
||||
usize byte_size; /* total byte size (cached) */
|
||||
usize slot_count; /* stack slot count */
|
||||
int is_pub; /* public visibility */
|
||||
int resolved; /* type fully resolved */
|
||||
spl_method_info_vec_t methods; /* methods */
|
||||
usize byte_size; /* total byte size (cached) */
|
||||
usize slot_count; /* stack slot count */
|
||||
int is_pub; /* public visibility */
|
||||
int resolved; /* type fully resolved */
|
||||
};
|
||||
|
||||
/* Type constructor helpers */
|
||||
@@ -89,6 +90,15 @@ const char *spl_type_str(spl_type_info_t *t);
|
||||
int spl_type_is_integer(spl_type_t bt);
|
||||
spl_type_info_t *spl_type_clone(spl_type_info_t *t);
|
||||
|
||||
/* Determine the uniform type for LOAD/STORE codegen (i32→SPL_I32, ptr→SPL_PTR, else→SPL_PTR) */
|
||||
spl_type_t spl_type_emit_type(spl_type_info_t *type);
|
||||
|
||||
/* Emit LOAD from [saved_ptr + byte_offset] and STORE to local var at var_offset.
|
||||
* Stack: [] → []
|
||||
* The saved_ptr is loaded from the 1-slot temp at ptr_slot_offset. */
|
||||
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
|
||||
spl_type_info_t *data_type, int var_offset);
|
||||
|
||||
/* ============================================================
|
||||
* Scope / symbol table
|
||||
* ============================================================ */
|
||||
@@ -96,9 +106,9 @@ spl_type_info_t *spl_type_clone(spl_type_info_t *t);
|
||||
typedef struct spl_var_info {
|
||||
char *name;
|
||||
spl_type_info_t *type;
|
||||
int offset; /* byte offset from fp */
|
||||
int offset; /* byte offset from fp */
|
||||
int is_const;
|
||||
int depth; /* scope depth */
|
||||
int depth; /* scope depth */
|
||||
} spl_var_info_t;
|
||||
typedef VEC(spl_var_info_t) spl_var_vec_t;
|
||||
|
||||
@@ -114,8 +124,8 @@ typedef struct spl_func_info {
|
||||
spl_type_info_t **param_types;
|
||||
char **param_names;
|
||||
int nparams;
|
||||
int func_idx; /* index in prog->funcs */
|
||||
int is_extern; /* #[extern("vm")] */
|
||||
int func_idx; /* index in prog->funcs */
|
||||
int is_extern; /* #[extern("vm")] */
|
||||
int is_pub;
|
||||
} spl_func_info_t;
|
||||
typedef VEC(spl_func_info_t) spl_func_info_vec_t;
|
||||
@@ -134,7 +144,7 @@ typedef struct {
|
||||
int count_at_decl; /* defer_count when declared */
|
||||
} spl_defer_entry_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct spl_comp {
|
||||
/* Lexer output */
|
||||
spl_tok_vec_t toks;
|
||||
usize tok_idx;
|
||||
@@ -161,15 +171,17 @@ typedef struct {
|
||||
/* Current function context */
|
||||
int current_func_idx;
|
||||
spl_type_info_t *current_ret_type;
|
||||
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) */
|
||||
int current_local_bytes; /* next free local byte offset */
|
||||
int peak_local_bytes; /* peak current_local_bytes for ALLOC sizing */
|
||||
const char *current_type_name; /* name of type whose body we're parsing (for method short-name
|
||||
lookup) */
|
||||
|
||||
/* Loop context for break/continue */
|
||||
int in_loop;
|
||||
usize *break_patches; /* instruction addresses to patch */
|
||||
usize *break_patches; /* instruction addresses to patch */
|
||||
usize break_patch_count;
|
||||
usize break_patch_cap;
|
||||
usize continue_target; /* ip to jump to for continue */
|
||||
usize continue_target; /* ip to jump to for continue */
|
||||
|
||||
/* Defer stack */
|
||||
spl_defer_entry_t defer_stack[DEFER_MAX];
|
||||
@@ -181,7 +193,7 @@ typedef struct {
|
||||
/* Const values */
|
||||
MAP(const char *, spl_val_t) const_values;
|
||||
|
||||
int addr_of_mode; /* 1 = inside & operator, suppress loads */
|
||||
int addr_of_mode; /* 1 = inside & operator, suppress loads */
|
||||
} spl_comp_t;
|
||||
|
||||
/* Initialize/destroy compiler context */
|
||||
@@ -210,16 +222,26 @@ void parse_type_decl(spl_comp_t *ctx);
|
||||
|
||||
/* Precedence levels for Pratt parser */
|
||||
enum {
|
||||
PREC_MIN = 0, PREC_ASSIGN = 1, PREC_LOGOR = 2, PREC_LOGAND = 3, PREC_OR = 4,
|
||||
PREC_XOR = 5, PREC_AND = 6, PREC_CMPEQ = 7, PREC_CMP = 8,
|
||||
PREC_SHIFT = 9, PREC_ADD = 10, PREC_MUL = 11, PREC_PREFIX = 12,
|
||||
PREC_MIN = 0,
|
||||
PREC_ASSIGN = 1,
|
||||
PREC_LOGOR = 2,
|
||||
PREC_LOGAND = 3,
|
||||
PREC_OR = 4,
|
||||
PREC_XOR = 5,
|
||||
PREC_AND = 6,
|
||||
PREC_CMPEQ = 7,
|
||||
PREC_CMP = 8,
|
||||
PREC_SHIFT = 9,
|
||||
PREC_ADD = 10,
|
||||
PREC_MUL = 11,
|
||||
PREC_PREFIX = 12,
|
||||
PREC_POSTFIX = 13
|
||||
};
|
||||
|
||||
/* Result of expression codegen */
|
||||
typedef struct {
|
||||
spl_type_info_t *type;
|
||||
int is_lvalue; /* 1 = address on stack, 0 = value on stack */
|
||||
int is_lvalue; /* 1 = address on stack, 0 = value on stack */
|
||||
} spl_expr_result_t;
|
||||
|
||||
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
|
||||
@@ -242,13 +264,19 @@ spl_val_t spl_emit_bz(spl_comp_t *ctx);
|
||||
spl_val_t spl_emit_bnz(spl_comp_t *ctx);
|
||||
void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr);
|
||||
|
||||
/* Copy multi-slot value from TOS (temp address) to frame-relative destination.
|
||||
* Stack: [..., temp_addr] → [...]
|
||||
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
|
||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
|
||||
|
||||
/* Variable management */
|
||||
int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const);
|
||||
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name);
|
||||
int spl_get_var_offset(spl_comp_t *ctx, const char *name);
|
||||
|
||||
/* Function management */
|
||||
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams, int is_extern, int is_pub);
|
||||
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_lookup_func(spl_comp_t *ctx, const char *name);
|
||||
|
||||
/* String/data management */
|
||||
|
||||
@@ -329,13 +329,13 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Parse array length */
|
||||
spl_tok_t *len_tok = advance(ctx);
|
||||
if (len_tok->type != TOK_INT_LITERAL) {
|
||||
int len_val;
|
||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||
spl_comp_error(ctx, "expected array length");
|
||||
spl_expr_result_t r = {0};
|
||||
return r;
|
||||
}
|
||||
usize len = (usize)strtoull(len_tok->lexeme, NULL, 0);
|
||||
usize len = (usize)len_val;
|
||||
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_R_BRACKET);
|
||||
@@ -372,19 +372,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
||||
return (spl_expr_result_t){arr_type, 0};
|
||||
}
|
||||
|
||||
/* Determine correct STORE type for a type (handles ptr = 8 bytes). */
|
||||
static spl_type_t spl_store_type(spl_type_info_t *type) {
|
||||
if (!type)
|
||||
return SPL_I32;
|
||||
if (type->kind == TYPE_BASIC)
|
||||
return type->basic_type;
|
||||
if (type->kind == TYPE_PTR)
|
||||
return SPL_PTR;
|
||||
if (spl_type_size(type) <= sizeof(spl_val_t))
|
||||
return SPL_PTR;
|
||||
return SPL_I32;
|
||||
}
|
||||
|
||||
/* Parse struct/enum literal: Type { .field = val, ... }
|
||||
* Allocates temp slots for the value, returns lvalue (addr on stack).
|
||||
* For types fitting in one slot, pushes the packed value directly. */
|
||||
@@ -392,8 +379,12 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
||||
usize sz = spl_type_size(type);
|
||||
int base_offset = ctx->current_local_bytes;
|
||||
ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1));
|
||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||
if (ctx->current_local_bytes - base_offset < (int)sizeof(spl_val_t))
|
||||
ctx->current_local_bytes = base_offset + (int)sizeof(spl_val_t);
|
||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
@@ -425,12 +416,114 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)fv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0);
|
||||
/* Handle inline slice initializer: { .ptr = expr, .len = expr } */
|
||||
if (f->type && f->type->kind == TYPE_SLICE &&
|
||||
peek(ctx)->type == TOK_L_BRACE) {
|
||||
/* Stack: [field_addr] — slice struct start addr */
|
||||
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 *sftok = advance(ctx);
|
||||
char sfname[256];
|
||||
usize sfnl = sftok->len < 255 ? sftok->len : 255;
|
||||
memcpy(sfname, sftok->lexeme, sfnl);
|
||||
sfname[sfnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_emit(ctx, SPL_DUP, SPL_VOID, 0); /* [addr, addr] */
|
||||
if (strcmp(sfname, "ptr") == 0) {
|
||||
spl_expr_result_t pv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)pv;
|
||||
/* Stack: [addr, addr, ptr_val] — STORE needs [addr, val] */
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0); /* [addr] */
|
||||
} else if (strcmp(sfname, "len") == 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t));
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); /* [addr, addr+8] */
|
||||
spl_expr_result_t lv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)lv;
|
||||
/* Stack: [addr, addr+8, len_val] — STORE needs [addr, val] */
|
||||
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); /* [addr] */
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop addr */
|
||||
} else if (f->type && f->type->kind == TYPE_ARRAY &&
|
||||
peek(ctx)->type == TOK_L_BRACKET) {
|
||||
/* Inline array initializer: [N]Type{val1, val2, ...}
|
||||
* Stack: [field_addr] — store each element at computed offsets */
|
||||
advance(ctx); /* [ */
|
||||
skip_nl(ctx);
|
||||
int len_val;
|
||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||
spl_comp_error(ctx, "expected array length");
|
||||
spl_expr_result_t r = {0};
|
||||
return r;
|
||||
}
|
||||
usize arr_len = (usize)len_val;
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_R_BRACKET);
|
||||
skip_nl(ctx);
|
||||
spl_type_info_t *elem_type = spl_parse_type(ctx);
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_L_BRACE);
|
||||
skip_nl(ctx);
|
||||
usize stride = spl_type_elem_stride(elem_type);
|
||||
spl_type_t st = spl_type_emit_type(elem_type);
|
||||
for (usize i = 0; i < arr_len; i++) {
|
||||
if (i > 0) {
|
||||
if (peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||
if (i > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * stride);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_parse_expr(ctx, PREC_MIN);
|
||||
spl_emit(ctx, SPL_STORE, st, 0);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
if (peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
} else if (f->type &&
|
||||
(f->type->kind == TYPE_STRUCT || f->type->kind == TYPE_ENUM) &&
|
||||
spl_type_size(f->type) > sizeof(spl_val_t)) {
|
||||
/* Multi-slot struct/enum value: copy temp → field slot by slot
|
||||
* Stack: [field_addr, temp_addr] */
|
||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)fv;
|
||||
usize nslots = (spl_type_size(f->type) + sizeof(spl_val_t) - 1) /
|
||||
sizeof(spl_val_t);
|
||||
spl_emit_copy_slots(ctx, base_offset + f->offset, nslots);
|
||||
/* Drop field_addr from stack (copy_slots consumed temp_addr) */
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
} else {
|
||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)fv;
|
||||
spl_emit(ctx, SPL_STORE, spl_type_emit_type(f->type), 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Unrecognized token (not .field or ,), advance to prevent infinite loop */
|
||||
if (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF)
|
||||
advance(ctx);
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
@@ -492,15 +585,85 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)sfv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0);
|
||||
/* Handle inline slice initializer */
|
||||
if (sf->type && sf->type->kind == TYPE_SLICE &&
|
||||
peek(ctx)->type == TOK_L_BRACE) {
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACE &&
|
||||
peek(ctx)->type != TOK_EOF) {
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
advance(ctx);
|
||||
spl_tok_t *ssftok = advance(ctx);
|
||||
char ssfname[256];
|
||||
usize ssfnl = ssftok->len < 255 ? ssftok->len : 255;
|
||||
memcpy(ssfname, ssftok->lexeme, ssfnl);
|
||||
ssfname[ssfnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||
if (strcmp(ssfname, "ptr") == 0) {
|
||||
spl_expr_result_t pv =
|
||||
spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)pv;
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
} else if (strcmp(ssfname, "len") == 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE,
|
||||
sizeof(spl_val_t));
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_expr_result_t lv =
|
||||
spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)lv;
|
||||
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
} else if (sf->type &&
|
||||
(sf->type->kind == TYPE_STRUCT ||
|
||||
sf->type->kind == TYPE_ENUM) &&
|
||||
spl_type_size(sf->type) > sizeof(spl_val_t)) {
|
||||
/* Multi-slot struct field in enum variant data */
|
||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)sfv;
|
||||
usize nslots =
|
||||
(spl_type_size(sf->type) + sizeof(spl_val_t) - 1) /
|
||||
sizeof(spl_val_t);
|
||||
spl_emit_copy_slots(
|
||||
ctx, base_offset + (int)DATA_OFFSET + sf->offset,
|
||||
nslots);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
} else {
|
||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)sfv;
|
||||
spl_emit(ctx, SPL_STORE, spl_type_emit_type(sf->type), 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
} else if (v->data_type &&
|
||||
(v->data_type->kind == TYPE_STRUCT ||
|
||||
v->data_type->kind == TYPE_ENUM) &&
|
||||
spl_type_size(v->data_type) > sizeof(spl_val_t)) {
|
||||
/* Multi-slot struct/enum data: copy from temp to enum data area */
|
||||
spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)dv;
|
||||
usize nslots = (spl_type_size(v->data_type) + sizeof(spl_val_t) - 1) /
|
||||
sizeof(spl_val_t);
|
||||
spl_emit_copy_slots(ctx, base_offset + (int)DATA_OFFSET, nslots);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
} else {
|
||||
/* Simple data: parse expression */
|
||||
spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN);
|
||||
@@ -508,7 +671,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_type_t bt = spl_store_type(v->data_type);
|
||||
spl_type_t bt = spl_type_emit_type(v->data_type);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
@@ -1090,11 +1253,11 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
/* Stack: [struct_addr, begin, end].
|
||||
* Inline ptr/len: ptr = data_ptr + begin*stride, len = end - begin. */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 2);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [s,b,e,data_ptr] */
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [s,b,e,data_ptr] */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* [s,b,e,d,begin] */
|
||||
spl_emit(ctx, SPL_PUSH, SPL_U64, stride);
|
||||
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
||||
spl_emit(ctx, SPL_ADD, SPL_U64, 0); /* [s,b,e,ptr] */
|
||||
spl_emit(ctx, SPL_ADD, SPL_U64, 0); /* [s,b,e,ptr] */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [s,b,e,ptr,end] */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 3); /* [s,b,e,ptr,end,begin] */
|
||||
spl_emit(ctx, SPL_SUB, SPL_USIZE, 0); /* [s,b,e,ptr,len] */
|
||||
@@ -1126,6 +1289,13 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
emit_slice_index(ctx, elem);
|
||||
} else {
|
||||
/* Stack arrays and pointer indexing */
|
||||
if (left.type->kind == TYPE_PTR && left.is_lvalue) {
|
||||
/* Stack: [addr_of_ptr, index]. Swap to get addr on top, load ptr value,
|
||||
* swap back */
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
}
|
||||
usize stride = spl_type_elem_stride(elem);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_U64, stride);
|
||||
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
||||
@@ -1194,10 +1364,9 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
||||
ctx->addr_of_mode = saved_addr_of_mode;
|
||||
|
||||
if (left.is_lvalue) {
|
||||
spl_type_t bt = left.type ? (left.type->kind == TYPE_BASIC ? left.type->basic_type
|
||||
: left.type->kind == TYPE_PTR ? SPL_PTR
|
||||
: SPL_I32)
|
||||
: SPL_I32;
|
||||
spl_type_t bt = left.type
|
||||
? (left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_PTR)
|
||||
: SPL_I32;
|
||||
if (op == TOK_ASSIGN) {
|
||||
/* Simple assignment: stack is [addr, rhs] */
|
||||
/* STORE pops TOS=value, TOS-1=address — already correct order */
|
||||
|
||||
@@ -27,6 +27,23 @@ void skip_nl(spl_comp_t *ctx) {
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
int spl_parse_int_literal(spl_comp_t *ctx, int *val) {
|
||||
int negate = 0;
|
||||
if (peek(ctx)->type == TOK_SUB) {
|
||||
negate = 1;
|
||||
advance(ctx);
|
||||
}
|
||||
if (peek(ctx)->type != TOK_INT_LITERAL) {
|
||||
if (negate)
|
||||
ctx->tok_idx--; /* un-consume the SUB token */
|
||||
return 0;
|
||||
}
|
||||
spl_tok_t *tok = advance(ctx);
|
||||
long long v = strtoll(tok->lexeme, NULL, 0);
|
||||
*val = negate ? -(int)v : (int)v;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *spl_tok_type_name(spl_tok_type_t type) {
|
||||
switch (type) {
|
||||
#define X(name, enum_name, dummy) \
|
||||
|
||||
@@ -9,4 +9,9 @@ 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);
|
||||
|
||||
/* Parse an integer literal value, handling optional '-' prefix for negatives.
|
||||
* Returns 1 on success (value in *val), 0 if current token isn't an integer.
|
||||
* On success, advances past all consumed tokens. On failure, does not advance. */
|
||||
int spl_parse_int_literal(spl_comp_t *ctx, int *val);
|
||||
|
||||
#endif /* __SPL_LEX_UTIL_H__ */
|
||||
|
||||
@@ -118,7 +118,7 @@ typedef enum {
|
||||
typedef struct {
|
||||
spl_tok_type_t type;
|
||||
const char *lexeme;
|
||||
usize len; /* token length in bytes */
|
||||
usize len; /* token length in bytes */
|
||||
const char *fname;
|
||||
usize offset;
|
||||
usize line;
|
||||
|
||||
@@ -13,7 +13,6 @@ spl_tok_t *advance(spl_comp_t *ctx) {
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================
|
||||
* Parse function definition
|
||||
* fn name(params) ret-type { body }
|
||||
@@ -28,6 +27,7 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *
|
||||
ctx->current_func_idx = fi;
|
||||
ctx->current_ret_type = ret_type;
|
||||
ctx->current_local_bytes = 0;
|
||||
ctx->peak_local_bytes = 0;
|
||||
|
||||
spl_push_scope(ctx);
|
||||
|
||||
@@ -48,7 +48,7 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
spl_patch(ctx, alloc_addr, ctx->current_local_bytes / (int)sizeof(spl_val_t) - nparams);
|
||||
spl_patch(ctx, alloc_addr, ctx->peak_local_bytes / (int)sizeof(spl_val_t) - nparams);
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,8 @@ static void parse_var_decl(spl_comp_t *ctx, int 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) {
|
||||
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) {
|
||||
@@ -144,18 +145,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
|
||||
} 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);
|
||||
}
|
||||
spl_emit_copy_slots(ctx, offset, nslots);
|
||||
}
|
||||
} else if (var_type && var_type->kind == TYPE_ARRAY) {
|
||||
/* Array initialization: store each element in reverse stack order */
|
||||
@@ -593,7 +583,9 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Match statement: match expr { .Variant(bindings) => stmt, ... }
|
||||
* Match statement:
|
||||
* Enum: match expr { .Variant(bindings) => stmt, ... }
|
||||
* Int: match expr { literal => stmt, ..., _ => stmt }
|
||||
* ============================================================ */
|
||||
|
||||
static void parse_match_stmt(spl_comp_t *ctx) {
|
||||
@@ -602,21 +594,33 @@ static void parse_match_stmt(spl_comp_t *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;
|
||||
/* Determine match type (enum or integer) */
|
||||
int is_enum_match = 0;
|
||||
int is_int_match = 0;
|
||||
spl_type_info_t *enum_type = NULL;
|
||||
|
||||
spl_type_info_t *t = expr.type;
|
||||
if (t && t->kind == TYPE_ENUM) {
|
||||
is_enum_match = 1;
|
||||
enum_type = t;
|
||||
} else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) {
|
||||
is_enum_match = 1;
|
||||
enum_type = t->elem;
|
||||
} else if (t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type)) {
|
||||
is_int_match = 1;
|
||||
}
|
||||
if (!enum_type || enum_type->kind != TYPE_ENUM) {
|
||||
spl_comp_error(ctx, "match expression must be an enum");
|
||||
|
||||
if (!is_enum_match && !is_int_match) {
|
||||
spl_comp_error(ctx, "match expression must be an enum or integer type");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the enum address (pointer value) to a temp slot */
|
||||
int addr_offset = ctx->current_local_bytes;
|
||||
/* Save the value/address to a temp slot */
|
||||
int val_offset = ctx->current_local_bytes;
|
||||
ctx->current_local_bytes += (int)sizeof(spl_val_t);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
|
||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
|
||||
@@ -638,125 +642,140 @@ static void parse_match_stmt(spl_comp_t *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';
|
||||
spl_val_t bz_addr = 0;
|
||||
int scope_pushed = 0;
|
||||
int has_parens = 0;
|
||||
spl_val_t body_start = 0;
|
||||
spl_val_t bnz_addrs[16];
|
||||
int n_bnz = 0;
|
||||
spl_enum_variant_t *arm_variant = NULL;
|
||||
|
||||
/* 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);
|
||||
/* --- Parse arm pattern(s): comma-separated values with fallthrough --- */
|
||||
if (peek(ctx)->type == KW_ANY) {
|
||||
/* _ default arm: always matches, no comparison */
|
||||
advance(ctx);
|
||||
} else {
|
||||
for (;;) {
|
||||
if (is_enum_match) {
|
||||
/* .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';
|
||||
|
||||
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;
|
||||
}
|
||||
arm_variant = variant;
|
||||
|
||||
/* Compare tag: load [val_offset] → addr → +0 → tag */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_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);
|
||||
|
||||
/* If bindings follow, this must be the last pattern */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
has_parens = 1;
|
||||
break;
|
||||
}
|
||||
} else if (is_int_match) {
|
||||
/* Parse expression as arm value */
|
||||
spl_parse_expr(ctx, PREC_MIN);
|
||||
/* Compare: load saved match value, then EQ */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
||||
}
|
||||
|
||||
/* Check for more comma-separated patterns */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
/* If another pattern follows (not ⇒), emit BNZ for fallthrough */
|
||||
if (peek(ctx)->type != TOK_ASSIGN) {
|
||||
bnz_addrs[n_bnz++] = spl_emit_bnz(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!variant) {
|
||||
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
||||
break;
|
||||
|
||||
/* Last pattern: BZ to skip arm if no value matched */
|
||||
if (!ctx->has_error) {
|
||||
bz_addr = spl_emit_bz(ctx);
|
||||
body_start = vec_size(ctx->prog.insns);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* --- Parse enum bindings (only for last variant) --- */
|
||||
if (is_enum_match && has_parens && arm_variant) {
|
||||
advance(ctx); /* ( */
|
||||
skip_nl(ctx);
|
||||
}
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
spl_push_scope(ctx);
|
||||
scope_pushed = 1;
|
||||
|
||||
/* 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 (arm_variant->data_type && arm_variant->data_type->kind == TYPE_STRUCT) {
|
||||
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';
|
||||
|
||||
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
|
||||
/* Struct data: each struct field is a binding */
|
||||
int bi = 0;
|
||||
for (;;) {
|
||||
spl_type_info_t *btype = spl_type_basic(SPL_I32);
|
||||
usize field_byte_off = 4;
|
||||
if ((usize)bi < vec_size(arm_variant->data_type->fields)) {
|
||||
btype = vec_at(arm_variant->data_type->fields, bi).type;
|
||||
field_byte_off = 4 + vec_at(arm_variant->data_type->fields, bi).offset;
|
||||
}
|
||||
|
||||
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
||||
spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset);
|
||||
|
||||
bi++;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (arm_variant->data_type) {
|
||||
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;
|
||||
}
|
||||
|
||||
spl_type_info_t *btype = arm_variant->data_type;
|
||||
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;
|
||||
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
}
|
||||
|
||||
if (has_parens)
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
skip_nl(ctx);
|
||||
|
||||
/* => (two tokens: = >) */
|
||||
@@ -781,7 +800,14 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
||||
jmp_to_end[n_jmps++] = spl_emit_jmp(ctx);
|
||||
|
||||
/* Patch BZ to here (next arm or end) */
|
||||
spl_patch_to_here(ctx, bz_addr);
|
||||
if (bz_addr)
|
||||
spl_patch_to_here(ctx, bz_addr);
|
||||
|
||||
/* Patch BNZs to body start (fallthrough values matched) */
|
||||
for (int i = 0; i < n_bnz; i++) {
|
||||
spl_val_t offset = body_start - bnz_addrs[i] - 1;
|
||||
spl_patch(ctx, bnz_addrs[i], offset);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* spl_type.c — Type system implementation */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -260,9 +261,7 @@ usize spl_type_slot_count(spl_type_info_t *t) {
|
||||
}
|
||||
|
||||
/* Byte stride between consecutive elements in storage */
|
||||
usize spl_type_elem_stride(spl_type_info_t *elem) {
|
||||
return spl_type_size(elem);
|
||||
}
|
||||
usize spl_type_elem_stride(spl_type_info_t *elem) { return spl_type_size(elem); }
|
||||
|
||||
const char *spl_type_str(spl_type_info_t *t) {
|
||||
if (!t)
|
||||
@@ -371,12 +370,12 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
|
||||
}
|
||||
|
||||
/* Parse array length as integer literal */
|
||||
if (tok->type != TOK_INT_LITERAL) {
|
||||
int len_val;
|
||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||
spl_comp_error(ctx, "expected array length");
|
||||
return NULL;
|
||||
}
|
||||
usize len = (usize)strtoull(tok->lexeme, NULL, 0);
|
||||
ctx->tok_idx++;
|
||||
usize len = (usize)len_val;
|
||||
|
||||
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
|
||||
spl_comp_error(ctx, "expected ']'");
|
||||
|
||||
488
stage1/test18_match.spl
Normal file
488
stage1/test18_match.spl
Normal file
@@ -0,0 +1,488 @@
|
||||
/* ===== 模块4:match 语句 =====
|
||||
* test18_match — match 枚举匹配 + 整数匹配(类似 switch)
|
||||
* 难度:3/5
|
||||
* 验证点:枚举匹配、带数据绑定、结构体字段绑定、枚举指针、
|
||||
* 整数自变量匹配、默认分支 _
|
||||
*/
|
||||
|
||||
/* ---- 简单枚举(无数据) ---- */
|
||||
type Color = enum {
|
||||
Red;
|
||||
Green;
|
||||
Blue;
|
||||
}
|
||||
|
||||
/* ---- 带 i32 数据的枚举 ---- */
|
||||
type Optional = enum {
|
||||
Some: i32;
|
||||
None;
|
||||
}
|
||||
|
||||
/* ---- 带结构体数据的枚举 ---- */
|
||||
type Point = struct {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
type Shape = enum {
|
||||
Circle: i32;
|
||||
Rect: Point;
|
||||
}
|
||||
|
||||
/* ---- 多变体枚举 ---- */
|
||||
type ActionResult = enum {
|
||||
Success: i32;
|
||||
NotFound;
|
||||
Timeout: i32;
|
||||
Error: *u8;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 1: 无数据枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_color_match() i32 {
|
||||
var c: Color = Color { .Red };
|
||||
var val: i32 = 0;
|
||||
|
||||
match c {
|
||||
.Red => { val = 1; },
|
||||
.Green => { val = 2; },
|
||||
.Blue => { val = 3; }
|
||||
}
|
||||
if val != 1 { ret 1; }
|
||||
|
||||
c = Color { .Green };
|
||||
match c {
|
||||
.Red => { val = 0; },
|
||||
.Green => { val = 2; },
|
||||
.Blue => { val = 0; }
|
||||
}
|
||||
if val != 2 { ret 2; }
|
||||
|
||||
c = Color { .Blue };
|
||||
match c {
|
||||
.Red => { val = 0; },
|
||||
.Green => { val = 0; },
|
||||
.Blue => { val = 3; }
|
||||
}
|
||||
if val != 3 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 2: 带 i32 数据枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_optional_match() i32 {
|
||||
var o: Optional = Optional { .Some = 42 };
|
||||
|
||||
match o {
|
||||
.Some(val) => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.None => {
|
||||
ret 2;
|
||||
}
|
||||
}
|
||||
|
||||
o = Optional { .None };
|
||||
var is_none: i32 = 0;
|
||||
match o {
|
||||
.Some(val) => {},
|
||||
.None => { is_none = 1; }
|
||||
}
|
||||
if is_none != 1 { ret 3; }
|
||||
|
||||
/* 多次提取不同值 */
|
||||
o = Optional { .Some = 99 };
|
||||
match o {
|
||||
.Some(val) => {
|
||||
if val != 99 { ret 4; }
|
||||
},
|
||||
.None => { ret 5; }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 3: 带结构体数据枚举匹配(多字段绑定)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_shape_match() i32 {
|
||||
/* Circle: 单数据 */
|
||||
var s: Shape = Shape { .Circle = 10 };
|
||||
match s {
|
||||
.Circle(r) => {
|
||||
if r != 10 { ret 1; }
|
||||
},
|
||||
.Rect(w, h) => {
|
||||
ret 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rect: 结构体数据,绑定为 (x, y) 对应 Point 的字段 */
|
||||
s = Shape { .Rect = Point { .x = 3, .y = 4 } };
|
||||
match s {
|
||||
.Circle(r) => { ret 3; },
|
||||
.Rect(w, h) => {
|
||||
if w != 3 { ret 4; }
|
||||
if h != 4 { ret 5; }
|
||||
}
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 4: 多数据变体枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_action_result_match() i32 {
|
||||
var r: ActionResult = ActionResult { .Success = 200 };
|
||||
match r {
|
||||
.Success(code) => {
|
||||
if code != 200 { ret 1; }
|
||||
},
|
||||
.NotFound => {
|
||||
ret 2;
|
||||
},
|
||||
.Timeout(ms) => {
|
||||
ret 3;
|
||||
},
|
||||
.Error(msg) => {
|
||||
ret 4;
|
||||
}
|
||||
}
|
||||
|
||||
r = ActionResult { .NotFound };
|
||||
var found: i32 = 1;
|
||||
match r {
|
||||
.Success(code) => { found = 0; },
|
||||
.NotFound => { },
|
||||
.Timeout(ms) => { found = 0; },
|
||||
.Error(msg) => { found = 0; }
|
||||
}
|
||||
if found != 1 { ret 5; }
|
||||
|
||||
r = ActionResult { .Timeout = 5000 };
|
||||
match r {
|
||||
.Success(code) => { ret 6; },
|
||||
.NotFound => { ret 7; },
|
||||
.Timeout(ms) => {
|
||||
if ms != 5000 { ret 8; }
|
||||
},
|
||||
.Error(msg) => { ret 9; }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 5: 枚举指针匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_ptr_match() i32 {
|
||||
var c: Color = Color { .Green };
|
||||
var p: *Color = &c;
|
||||
|
||||
match p {
|
||||
.Red => { ret 1; },
|
||||
.Green => { },
|
||||
.Blue => { ret 2; }
|
||||
}
|
||||
|
||||
/* 修改后通过指针匹配 */
|
||||
c = Color { .Blue };
|
||||
match p {
|
||||
.Red => { ret 3; },
|
||||
.Green => { ret 4; },
|
||||
.Blue => { }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 6: match 作为函数返回值
|
||||
* ============================================================ */
|
||||
|
||||
fn classify_color(c: Color) i32 {
|
||||
match c {
|
||||
.Red => { ret 1; },
|
||||
.Green => { ret 2; },
|
||||
.Blue => { ret 3; }
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_match_in_func() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = classify_color(Color { .Red });
|
||||
if r != 1 { ret 1; }
|
||||
|
||||
r = classify_color(Color { .Green });
|
||||
if r != 2 { ret 2; }
|
||||
|
||||
r = classify_color(Color { .Blue });
|
||||
if r != 3 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 7: match 嵌套在循环中
|
||||
* ============================================================ */
|
||||
|
||||
fn test_match_in_loop() i32 {
|
||||
var i: i32 = 0;
|
||||
var sum: i32 = 0;
|
||||
|
||||
while i < 3 {
|
||||
var o: Optional;
|
||||
if i == 0 {
|
||||
o = Optional { .Some = 10 };
|
||||
} else if i == 1 {
|
||||
o = Optional { .Some = 20 };
|
||||
} else {
|
||||
o = Optional { .Some = 30 };
|
||||
}
|
||||
|
||||
match o {
|
||||
.Some(val) => {
|
||||
sum = sum + val;
|
||||
},
|
||||
.None => { }
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
if sum != 60 { ret 1; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 8: 整数 match(类似 switch)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match() i32 {
|
||||
var x: i32 = 2;
|
||||
var result: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { result = 10; },
|
||||
2 => { result = 20; },
|
||||
3 => { result = 30; }
|
||||
}
|
||||
if result != 20 { ret 1; }
|
||||
|
||||
/* 匹配第一个值 */
|
||||
x = 1;
|
||||
match x {
|
||||
1 => { result = 100; },
|
||||
2 => { result = 200; },
|
||||
3 => { result = 300; }
|
||||
}
|
||||
if result != 100 { ret 2; }
|
||||
|
||||
/* 匹配最后一个值 */
|
||||
x = 3;
|
||||
match x {
|
||||
1 => { result = 1000; },
|
||||
2 => { result = 2000; },
|
||||
3 => { result = 3000; }
|
||||
}
|
||||
if result != 3000 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 9: 整数 match 带默认分支 _
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_default() i32 {
|
||||
var x: i32 = 99;
|
||||
var result: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
_ => { result = 99; }
|
||||
}
|
||||
if result != 99 { ret 1; }
|
||||
|
||||
/* 默认分支未触发 */
|
||||
x = 1;
|
||||
match x {
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
_ => { result = 99; }
|
||||
}
|
||||
if result != 1 { ret 2; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 10: 整数 match 多个值跳转到相同逻辑
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_multi() i32 {
|
||||
var x: i32 = 2;
|
||||
var result: i32 = 0;
|
||||
|
||||
/* 每个分支独立 */
|
||||
match x {
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
3 => { result = 3; }
|
||||
}
|
||||
if result != 2 { ret 1; }
|
||||
|
||||
/* 负数和零 */
|
||||
x = -1;
|
||||
match x {
|
||||
-1 => { result = -1; },
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; }
|
||||
}
|
||||
if result != -1 { ret 2; }
|
||||
|
||||
x = 0;
|
||||
match x {
|
||||
-1 => { result = -1; },
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; }
|
||||
}
|
||||
if result != 0 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 11a: match 多值 fallthrough: 1, 2, 3 => body
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_fallthrough() i32 {
|
||||
var x: i32;
|
||||
var r: i32;
|
||||
|
||||
/* 三个值映射到同一个 body */
|
||||
x = 1; r = 0;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 10 { ret 1; }
|
||||
|
||||
x = 3;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 10 { ret 2; }
|
||||
|
||||
x = 5;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 20 { ret 3; }
|
||||
|
||||
/* 单一值(非fallthrough)仍然正常 */
|
||||
x = 7; r = 0;
|
||||
match x {
|
||||
1 => { r = 1; },
|
||||
7 => { r = 7; }
|
||||
}
|
||||
if r != 7 { ret 4; }
|
||||
|
||||
/* 多个负数值 */
|
||||
x = -2; r = 0;
|
||||
match x {
|
||||
-3, -2, -1 => { r = 100; },
|
||||
0, 1 => { r = 200; }
|
||||
}
|
||||
if r != 100 { ret 5; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 11b: match with body 中修改变量
|
||||
* ============================================================ */
|
||||
|
||||
fn test_match_with_side_effects() i32 {
|
||||
var x: i32 = 3;
|
||||
var acc: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { acc = acc + 1; },
|
||||
2 => { acc = acc + 2; },
|
||||
3 => { acc = acc + 3; },
|
||||
4 => { acc = acc + 4; }
|
||||
}
|
||||
if acc != 3 { ret 1; }
|
||||
|
||||
/* 再次 match 同一个变量 */
|
||||
match x {
|
||||
1 => { acc = acc + 1; },
|
||||
2 => { acc = acc + 2; },
|
||||
3 => { acc = acc + 3; },
|
||||
4 => { acc = acc + 4; }
|
||||
}
|
||||
if acc != 6 { ret 2; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 主函数
|
||||
* ============================================================ */
|
||||
|
||||
fn main() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = test_color_match();
|
||||
if r != 0 { ret r; }
|
||||
|
||||
r = test_optional_match();
|
||||
if r != 0 { ret r + 10; }
|
||||
|
||||
r = test_shape_match();
|
||||
if r != 0 { ret r + 20; }
|
||||
|
||||
r = test_action_result_match();
|
||||
if r != 0 { ret r + 30; }
|
||||
|
||||
r = test_ptr_match();
|
||||
if r != 0 { ret r + 40; }
|
||||
|
||||
r = test_match_in_func();
|
||||
if r != 0 { ret r + 50; }
|
||||
|
||||
r = test_match_in_loop();
|
||||
if r != 0 { ret r + 60; }
|
||||
|
||||
r = test_int_match();
|
||||
if r != 0 { ret r + 70; }
|
||||
|
||||
r = test_int_match_default();
|
||||
if r != 0 { ret r + 80; }
|
||||
|
||||
r = test_int_match_multi();
|
||||
if r != 0 { ret r + 90; }
|
||||
|
||||
r = test_match_with_side_effects();
|
||||
if r != 0 { ret r + 100; }
|
||||
|
||||
r = test_int_match_fallthrough();
|
||||
if r != 0 { ret r + 110; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* ===== 模块6:函数 =====
|
||||
* test18_recursion — 递归函数
|
||||
* 难度:3/5
|
||||
* 验证点:递归调用、多路递归、递归终止条件
|
||||
*/
|
||||
fn fib(n: i32) i32 {
|
||||
if n <= 1 { ret n; }
|
||||
ret fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
fn fact(n: i32) i32 {
|
||||
if n <= 0 { ret 1; }
|
||||
ret n * fact(n - 1);
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* 斐波那契 */
|
||||
var f0 := fib(0);
|
||||
if f0 != 0 { ret 1; }
|
||||
var f1 := fib(1);
|
||||
if f1 != 1 { ret 2; }
|
||||
var f5 := fib(5);
|
||||
if f5 != 5 { ret 3; }
|
||||
var f10 := fib(10);
|
||||
if f10 != 55 { ret 4; }
|
||||
|
||||
/* 阶乘 */
|
||||
var fact0 := fact(0);
|
||||
if fact0 != 1 { ret 5; }
|
||||
var fact3 := fact(3);
|
||||
if fact3 != 6 { ret 6; }
|
||||
var fact5 := fact(5);
|
||||
if fact5 != 120 { ret 7; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
320
stage1/test20_complex.spl
Normal file
320
stage1/test20_complex.spl
Normal file
@@ -0,0 +1,320 @@
|
||||
/* ===== 复杂类型嵌套综合测试 =====
|
||||
* test20_complex — 结构体嵌套、切片、数组、方法、类型别名、枚举等
|
||||
* 难度:5/5
|
||||
*/
|
||||
/* ---- 基础结构体 ---- */
|
||||
type Point = struct {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
/* ---- 嵌套结构体 ---- */
|
||||
type Rect = struct {
|
||||
var min: Point;
|
||||
var max: Point;
|
||||
}
|
||||
|
||||
/* ---- 含切片字段的结构体 (核心 bug 测试) ---- */
|
||||
type Buffer = struct {
|
||||
var data: []u8;
|
||||
var len: usize;
|
||||
}
|
||||
|
||||
/* ---- 含数组字段的结构体 ---- */
|
||||
type MatrixRow = struct {
|
||||
var items: [4]i32;
|
||||
}
|
||||
|
||||
/* ---- 含指针字段的结构体 ---- */
|
||||
type Node = struct {
|
||||
var ptr: *i32;
|
||||
var val: i32;
|
||||
}
|
||||
|
||||
/* ---- 多层级嵌套:结构体里的结构体里的切片 ---- */
|
||||
type Bundle = struct {
|
||||
var name: *u8;
|
||||
var buf: Buffer;
|
||||
var row: MatrixRow;
|
||||
var pt: Point;
|
||||
}
|
||||
|
||||
/* ---- 枚举含数据 ---- */
|
||||
type Status = enum {
|
||||
Active: i32;
|
||||
Inactive;
|
||||
Pending: Point;
|
||||
}
|
||||
|
||||
/* ---- 含方法的结构体 (方法定义在结构体内部) ---- */
|
||||
type Counter = struct {
|
||||
var val: i32;
|
||||
|
||||
fn inc(self: *Counter) i32 {
|
||||
self.val = self.val + 1;
|
||||
ret self.val;
|
||||
}
|
||||
|
||||
fn add(self: *Counter, n: i32) i32 {
|
||||
self.val = self.val + n;
|
||||
ret self.val;
|
||||
}
|
||||
|
||||
fn reset(self: *Counter) {
|
||||
self.val = 0;
|
||||
}
|
||||
|
||||
fn get(self: *Counter) i32 {
|
||||
ret self.val;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 1: 切片在结构体内部初始化 (修复的核心 bug)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_slice_in_struct() i32 {
|
||||
var raw: [4]u8;
|
||||
raw[0] = 65; raw[1] = 66; raw[2] = 67; raw[3] = 68;
|
||||
|
||||
/* Bug fix: { .ptr = ..., .len = ... } inside struct literal */
|
||||
var b: Buffer = Buffer { .data = { .ptr = &raw[0], .len = 4 }, .len = 4 };
|
||||
|
||||
if b.len != 4 { ret 1; }
|
||||
if b.data[0] != 65 { ret 2; }
|
||||
if b.data[1] != 66 { ret 3; }
|
||||
if b.data[3] != 68 { ret 4; }
|
||||
|
||||
/* Modify through slice — verify reflection */
|
||||
b.data[0] = 90;
|
||||
if raw[0] != 90 { ret 5; }
|
||||
|
||||
/* Initialize with shorter slice */
|
||||
var b2: Buffer = Buffer { .data = { .ptr = &raw[2], .len = 2 }, .len = 2 };
|
||||
if b2.len != 2 { ret 6; }
|
||||
if b2.data[0] != 67 { ret 7; }
|
||||
|
||||
/* Slice field assignment via field access */
|
||||
b2.data.ptr = &raw[0];
|
||||
b2.data.len = 4;
|
||||
if b2.data[0] != 90 { ret 8; }
|
||||
if b2.data.len != 4 { ret 9; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 2: 结构体含数组字段
|
||||
* ============================================================ */
|
||||
|
||||
fn test_struct_with_array() i32 {
|
||||
var mr: MatrixRow = MatrixRow { .items = [4]i32{10, 20, 30, 40} };
|
||||
|
||||
if mr.items[0] != 10 { ret 1; }
|
||||
if mr.items[1] != 20 { ret 2; }
|
||||
if mr.items[2] != 30 { ret 3; }
|
||||
if mr.items[3] != 40 { ret 4; }
|
||||
|
||||
/* 修改数组元素 */
|
||||
mr.items[2] = 99;
|
||||
if mr.items[2] != 99 { ret 5; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 3: 嵌套结构体初始化
|
||||
* ============================================================ */
|
||||
|
||||
fn test_nested_struct() i32 {
|
||||
var p: Point = Point { .x = 5, .y = 10 };
|
||||
if p.x != 5 { ret 1; }
|
||||
if p.y != 10 { ret 2; }
|
||||
|
||||
/* 嵌套结构体字面量 */
|
||||
var r: Rect = Rect {
|
||||
.min = Point { .x = 1, .y = 2 },
|
||||
.max = Point { .x = 3, .y = 4 }
|
||||
};
|
||||
if r.min.x != 1 { ret 3; }
|
||||
if r.min.y != 2 { ret 4; }
|
||||
if r.max.x != 3 { ret 5; }
|
||||
if r.max.y != 4 { ret 6; }
|
||||
|
||||
/* 修改嵌套字段 */
|
||||
r.min.x = 100;
|
||||
if r.min.x != 100 { ret 7; }
|
||||
if r.min.y != 2 { ret 8; } /* unchanged */
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 4: 结构体成员方法 (实例方法调用)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_struct_method() i32 {
|
||||
var c: Counter = Counter { .val = 0 };
|
||||
|
||||
/* 实例方法调用 c.inc() */
|
||||
var r1: i32 = c.inc();
|
||||
if r1 != 1 { ret 1; }
|
||||
if c.val != 1 { ret 2; }
|
||||
|
||||
/* 带参数方法调用 c.add(n) */
|
||||
var r2: i32 = c.add(5);
|
||||
if r2 != 6 { ret 3; }
|
||||
if c.val != 6 { ret 4; }
|
||||
|
||||
/* 连续调用 */
|
||||
c.reset();
|
||||
if c.val != 0 { ret 5; }
|
||||
|
||||
c.add(10);
|
||||
c.inc();
|
||||
var r3: i32 = c.get();
|
||||
if r3 != 11 { ret 6; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 5: 结构体含指针字段
|
||||
* ============================================================ */
|
||||
|
||||
fn test_ptr_in_struct() i32 {
|
||||
var v: i32 = 42;
|
||||
var n: Node = Node { .ptr = &v, .val = 99 };
|
||||
|
||||
if n.val != 99 { ret 1; }
|
||||
if n.ptr[0] != 42 { ret 2; }
|
||||
|
||||
/* 通过指针修改 */
|
||||
v = 100;
|
||||
if n.ptr[0] != 100 { ret 3; }
|
||||
|
||||
/* 通过指针在结构体内修改 */
|
||||
n.ptr[0] = 200;
|
||||
if v != 200 { ret 4; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 6: 多层级复杂嵌套
|
||||
* ============================================================ */
|
||||
|
||||
fn test_complex_nesting() i32 {
|
||||
var str_data: [5]u8;
|
||||
str_data[0] = 72; str_data[1] = 101;
|
||||
str_data[2] = 108; str_data[3] = 108; str_data[4] = 111;
|
||||
|
||||
var bundle: Bundle = Bundle {
|
||||
.name = &str_data[0],
|
||||
.buf = Buffer { .data = { .ptr = &str_data[1], .len = 3 }, .len = 3 },
|
||||
.row = MatrixRow { .items = [4]i32{1, 2, 3, 4} },
|
||||
.pt = Point { .x = -5, .y = 15 }
|
||||
};
|
||||
|
||||
/* Verify name */
|
||||
if bundle.name[0] != 72 { ret 1; }
|
||||
if bundle.name[4] != 111 { ret 2; }
|
||||
|
||||
/* Verify nested slice */
|
||||
if bundle.buf.len != 3 { ret 3; }
|
||||
if bundle.buf.data[0] != 101 { ret 4; }
|
||||
if bundle.buf.data[2] != 108 { ret 5; }
|
||||
|
||||
/* Verify array field */
|
||||
if bundle.row.items[0] != 1 { ret 6; }
|
||||
if bundle.row.items[3] != 4 { ret 7; }
|
||||
|
||||
/* Verify nested struct field */
|
||||
if bundle.pt.x != -5 { ret 8; }
|
||||
if bundle.pt.y != 15 { ret 9; }
|
||||
|
||||
/* Modify nested slice */
|
||||
bundle.buf.data[1] = 87; /* 'W' */
|
||||
if str_data[2] != 87 { ret 10; }
|
||||
|
||||
/* Modify nested array */
|
||||
bundle.row.items[2] = 33;
|
||||
if bundle.row.items[2] != 33 { ret 11; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 7: 枚举含结构体数据
|
||||
* ============================================================ */
|
||||
|
||||
fn test_enum_complex() i32 {
|
||||
var s: Status = Status { .Active = 42 };
|
||||
|
||||
/* Verify active variant */
|
||||
match s {
|
||||
.Active(val) => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.Inactive => {
|
||||
ret 2;
|
||||
},
|
||||
.Pending(px, py) => {
|
||||
ret 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test Inactive variant */
|
||||
var s2: Status = Status { .Inactive };
|
||||
var is_inactive: i32 = 0;
|
||||
match s2 {
|
||||
.Active(val) => {},
|
||||
.Inactive => { is_inactive = 1; },
|
||||
.Pending(px, py) => {}
|
||||
}
|
||||
if is_inactive != 1 { ret 4; }
|
||||
|
||||
/* Test Pending variant with struct data */
|
||||
var s3: Status = Status { .Pending = Point { .x = 7, .y = 8 } };
|
||||
match s3 {
|
||||
.Active(val) => { ret 5; },
|
||||
.Inactive => { ret 6; },
|
||||
.Pending(px, py) => {
|
||||
if px != 7 { ret 7; }
|
||||
if py != 8 { ret 8; }
|
||||
}
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 主函数
|
||||
* ============================================================ */
|
||||
|
||||
fn main() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = test_slice_in_struct();
|
||||
if r != 0 { ret r; }
|
||||
|
||||
r = test_struct_with_array();
|
||||
if r != 0 { ret r + 100; }
|
||||
|
||||
r = test_nested_struct();
|
||||
if r != 0 { ret r + 200; }
|
||||
|
||||
r = test_struct_method();
|
||||
if r != 0 { ret r + 300; }
|
||||
|
||||
r = test_ptr_in_struct();
|
||||
if r != 0 { ret r + 400; }
|
||||
|
||||
r = test_complex_nesting();
|
||||
if r != 0 { ret r + 500; }
|
||||
|
||||
r = test_enum_complex();
|
||||
if r != 0 { ret r + 600; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
Reference in New Issue
Block a user