stage1 优化重构代码
This commit is contained in:
@@ -245,6 +245,16 @@ typedef struct {
|
|||||||
} spl_expr_result_t;
|
} spl_expr_result_t;
|
||||||
|
|
||||||
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
|
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
|
||||||
|
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type);
|
||||||
|
|
||||||
|
/* Match arm pattern comparison — emit code to compare saved match value
|
||||||
|
* against a parsed pattern. Comparison logic belongs in expr layer
|
||||||
|
* so that match arms behave as "enhanced if-conditions".
|
||||||
|
* For enum: parses .VariantName, emits tag comparison, returns variant.
|
||||||
|
* For value: parses expression, emits equality comparison. */
|
||||||
|
spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx,
|
||||||
|
spl_type_info_t *enum_type, int val_offset);
|
||||||
|
void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset);
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Statement functions (spl_stmt.c)
|
* Statement functions (spl_stmt.c)
|
||||||
@@ -252,6 +262,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
|
|||||||
|
|
||||||
void spl_parse_stmt(spl_comp_t *ctx);
|
void spl_parse_stmt(spl_comp_t *ctx);
|
||||||
void spl_parse_block(spl_comp_t *ctx);
|
void spl_parse_block(spl_comp_t *ctx);
|
||||||
|
spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx);
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Codegen helpers (spl_comp.c)
|
* Codegen helpers (spl_comp.c)
|
||||||
@@ -268,6 +279,8 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr);
|
|||||||
* Stack: [..., temp_addr] → [...]
|
* Stack: [..., temp_addr] → [...]
|
||||||
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
|
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
|
||||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
|
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
|
||||||
|
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type);
|
||||||
|
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type);
|
||||||
|
|
||||||
/* 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);
|
||||||
|
|||||||
@@ -372,10 +372,133 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
return (spl_expr_result_t){arr_type, 0};
|
return (spl_expr_result_t){arr_type, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse slice inline literal: { .ptr = expr, .len = expr }
|
||||||
|
* Expects base address on TOS, consumes it. Stores ptr to base+0, len to base+sizeof(spl_val_t). */
|
||||||
|
static void parse_slice_inline(spl_comp_t *ctx) {
|
||||||
|
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_emit(ctx, SPL_DUP, SPL_VOID, 0); /* [addr, addr] */
|
||||||
|
if (strcmp(fname, "ptr") == 0) {
|
||||||
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0); /* [addr] */
|
||||||
|
} else if (strcmp(fname, "len") == 0) {
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); /* [addr, addr+8] */
|
||||||
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a single .field = value in a struct literal.
|
||||||
|
* Iterates fields to find the match, emits LADDR + offset, and handles
|
||||||
|
* value parsing (slice inline, array inline, multi-slot struct, basic store).
|
||||||
|
* Reused by both TYPE_STRUCT and TYPE_ENUM variant struct data. */
|
||||||
|
static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields,
|
||||||
|
int base_offset, usize extra_offset) {
|
||||||
|
if (peek(ctx)->type == TOK_DOT)
|
||||||
|
advance(ctx);
|
||||||
|
spl_tok_t *ftok = advance(ctx);
|
||||||
|
char fname[256];
|
||||||
|
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_ASSIGN)
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
|
||||||
|
vec_for(*fields, fi) {
|
||||||
|
spl_field_t *f = &vec_at(*fields, fi);
|
||||||
|
if (strcmp(f->name, fname) == 0) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
||||||
|
usize byte_off = extra_offset + f->offset;
|
||||||
|
if (byte_off > 0) {
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off);
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
|
}
|
||||||
|
if (f->type && f->type->kind == TYPE_SLICE &&
|
||||||
|
peek(ctx)->type == TOK_L_BRACE) {
|
||||||
|
parse_slice_inline(ctx);
|
||||||
|
} else if (f->type && f->type->kind == TYPE_ARRAY &&
|
||||||
|
peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
|
/* Inline array initializer: [N]Type{val1, val2, ...} */
|
||||||
|
advance(ctx); /* [ */
|
||||||
|
skip_nl(ctx);
|
||||||
|
int len_val;
|
||||||
|
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||||
|
spl_comp_error(ctx, "expected array length");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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)) {
|
||||||
|
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 + (int)extra_offset + f->offset, nslots);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse struct/enum literal: Type { .field = val, ... }
|
/* Parse struct/enum literal: Type { .field = val, ... }
|
||||||
* Allocates temp slots for the value, returns lvalue (addr on stack).
|
* Allocates temp slots for the value, returns lvalue (addr on stack).
|
||||||
* For types fitting in one slot, pushes the packed value directly. */
|
* For types fitting in one slot, pushes the packed value directly. */
|
||||||
static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) {
|
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) {
|
||||||
usize sz = spl_type_size(type);
|
usize sz = spl_type_size(type);
|
||||||
int base_offset = ctx->current_local_bytes;
|
int base_offset = ctx->current_local_bytes;
|
||||||
ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1));
|
ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1));
|
||||||
@@ -396,134 +519,41 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_DOT) {
|
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);
|
|
||||||
|
|
||||||
vec_for(type->fields, fi) {
|
|
||||||
spl_field_t *f = &vec_at(type->fields, fi);
|
|
||||||
if (strcmp(f->name, fname) == 0) {
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
|
||||||
if (f->offset > 0) {
|
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset);
|
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 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)
|
if (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parse_one_field_init(ctx, &type->fields, base_offset, 0);
|
||||||
|
skip_nl(ctx);
|
||||||
|
}
|
||||||
|
} else if (type->kind == TYPE_SLICE) {
|
||||||
|
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];
|
||||||
|
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_ASSIGN)
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (strcmp(fname, "ptr") == 0) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
||||||
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
|
} else if (strcmp(fname, "len") == 0) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||||
}
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
@@ -564,92 +594,9 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_DOT)
|
if (peek(ctx)->type != TOK_DOT)
|
||||||
advance(ctx);
|
break;
|
||||||
spl_tok_t *sftok = advance(ctx);
|
parse_one_field_init(ctx, &v->data_type->fields, base_offset, DATA_OFFSET);
|
||||||
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);
|
|
||||||
|
|
||||||
vec_for(v->data_type->fields, sfi) {
|
|
||||||
spl_field_t *sf = &vec_at(v->data_type->fields, sfi);
|
|
||||||
if (strcmp(sf->name, sfname) == 0) {
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset);
|
|
||||||
usize byte_off = DATA_OFFSET + sf->offset;
|
|
||||||
if (byte_off > 0) {
|
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off);
|
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 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);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_BRACE);
|
expect(ctx, TOK_R_BRACE);
|
||||||
@@ -805,7 +752,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
/* Struct/enum literal: Type { .field = val, ... } */
|
/* Struct/enum literal: Type { .field = val, ... } */
|
||||||
if (peek(ctx)->type == TOK_L_BRACE &&
|
if (peek(ctx)->type == TOK_L_BRACE &&
|
||||||
(ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) {
|
(ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) {
|
||||||
return parse_struct_literal(ctx, ttype);
|
return spl_parse_struct_literal(ctx, ttype);
|
||||||
}
|
}
|
||||||
spl_expr_result_t r = {ttype, 0};
|
spl_expr_result_t r = {ttype, 0};
|
||||||
return r;
|
return r;
|
||||||
@@ -880,84 +827,71 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Main expression parser (top-level)
|
* Primary expression (prefix part of Pratt parser)
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
|
||||||
skip_nl(ctx);
|
|
||||||
|
|
||||||
spl_tok_t *tok = peek(ctx);
|
spl_tok_t *tok = peek(ctx);
|
||||||
if (!tok) {
|
if (!tok) {
|
||||||
spl_expr_result_t r = {0};
|
spl_expr_result_t r = {0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_expr_result_t left = {0};
|
|
||||||
|
|
||||||
switch (tok->type) {
|
switch (tok->type) {
|
||||||
case TOK_INT_LITERAL:
|
case TOK_INT_LITERAL:
|
||||||
left = parse_int_literal(ctx);
|
return parse_int_literal(ctx);
|
||||||
break;
|
|
||||||
case TOK_FLOAT_LITERAL:
|
case TOK_FLOAT_LITERAL:
|
||||||
left = parse_float_literal(ctx);
|
return parse_float_literal(ctx);
|
||||||
break;
|
|
||||||
case TOK_CHAR_LITERAL:
|
case TOK_CHAR_LITERAL:
|
||||||
left = parse_char_literal(ctx);
|
return parse_char_literal(ctx);
|
||||||
break;
|
|
||||||
case TOK_STRING_LITERAL:
|
case TOK_STRING_LITERAL:
|
||||||
left = parse_string_literal(ctx);
|
return parse_string_literal(ctx);
|
||||||
break;
|
|
||||||
case KW_TRUE:
|
case KW_TRUE:
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_I32, 1);
|
spl_emit(ctx, SPL_PUSH, SPL_I32, 1);
|
||||||
left = (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
return (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
||||||
break;
|
|
||||||
case KW_FALSE:
|
case KW_FALSE:
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_I32, 0);
|
spl_emit(ctx, SPL_PUSH, SPL_I32, 0);
|
||||||
left = (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
return (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
||||||
break;
|
|
||||||
case KW_NULL:
|
case KW_NULL:
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_PTR, 0);
|
spl_emit(ctx, SPL_PUSH, SPL_PTR, 0);
|
||||||
left = (spl_expr_result_t){spl_type_basic(SPL_PTR), 0};
|
return (spl_expr_result_t){spl_type_basic(SPL_PTR), 0};
|
||||||
break;
|
|
||||||
case TOK_IDENT:
|
case TOK_IDENT:
|
||||||
case KW_BOOL:
|
case KW_BOOL:
|
||||||
case KW_VOID:
|
case KW_VOID:
|
||||||
case KW_ANY:
|
case KW_ANY:
|
||||||
left = parse_ident(ctx);
|
return parse_ident(ctx);
|
||||||
break;
|
|
||||||
case TOK_L_PAREN:
|
case TOK_L_PAREN:
|
||||||
left = parse_group(ctx);
|
return parse_group(ctx);
|
||||||
break;
|
|
||||||
case TOK_SUB:
|
case TOK_SUB:
|
||||||
case TOK_NOT:
|
case TOK_NOT:
|
||||||
case TOK_BIT_NOT:
|
case TOK_BIT_NOT:
|
||||||
case TOK_AND:
|
case TOK_AND:
|
||||||
case TOK_MUL:
|
case TOK_MUL:
|
||||||
left = parse_prefix_op(ctx);
|
return parse_prefix_op(ctx);
|
||||||
break;
|
|
||||||
case TOK_L_BRACKET:
|
case TOK_L_BRACKET:
|
||||||
left = parse_array_literal(ctx);
|
return parse_array_literal(ctx);
|
||||||
break;
|
case TOK_L_BRACE:
|
||||||
|
return spl_parse_block_expr(ctx);
|
||||||
case TOK_AT: {
|
case TOK_AT: {
|
||||||
/* @builtin(...) — compiler intrinsic */
|
/* @builtin(...) — compiler intrinsic */
|
||||||
advance(ctx); /* consume @ */
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
tok = peek(ctx);
|
tok = peek(ctx);
|
||||||
if (!tok || tok->type != TOK_IDENT) {
|
if (!tok || tok->type != TOK_IDENT) {
|
||||||
spl_comp_error(ctx, "expected builtin name after '@'");
|
spl_comp_error(ctx, "expected builtin name after '@'");
|
||||||
break;
|
spl_expr_result_t r = {0};
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
char bname[256];
|
char bname[256];
|
||||||
spl_tok_copy_name(tok, bname, sizeof bname);
|
spl_tok_copy_name(tok, bname, sizeof bname);
|
||||||
advance(ctx); /* consume builtin name */
|
advance(ctx);
|
||||||
|
|
||||||
if (strcmp(bname, "dbg") == 0) {
|
if (strcmp(bname, "dbg") == 0) {
|
||||||
/* @dbg(...) — print VM debug info */
|
|
||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
advance(ctx); /* skip ( */
|
advance(ctx);
|
||||||
int nargs = 0;
|
int nargs = 0;
|
||||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -971,37 +905,40 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_PAREN);
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
|
||||||
/* Emit SPL_DBG and DROP for each arg */
|
|
||||||
for (int i = 0; i < nargs; i++) {
|
for (int i = 0; i < nargs; i++) {
|
||||||
spl_emit(ctx, SPL_DBG, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_DBG, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||||
}
|
}
|
||||||
if (nargs == 0) {
|
if (nargs == 0)
|
||||||
spl_emit(ctx, SPL_DBG, SPL_VOID, 0);
|
spl_emit(ctx, SPL_DBG, SPL_VOID, 0);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* @dbg with no parens — just debug */
|
|
||||||
spl_emit(ctx, SPL_DBG, SPL_VOID, 0);
|
spl_emit(ctx, SPL_DBG, SPL_VOID, 0);
|
||||||
}
|
}
|
||||||
/* @dbg is a void expression */
|
return (spl_expr_result_t){spl_type_basic(SPL_VOID), 0};
|
||||||
left = (spl_expr_result_t){spl_type_basic(SPL_VOID), 0};
|
|
||||||
} else {
|
} else {
|
||||||
spl_comp_error(ctx, "unknown builtin '@%s'", bname);
|
spl_comp_error(ctx, "unknown builtin '@%s'", bname);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
/* If it's a keyword-as-type (i32, u8, etc.), parse as function call target or type
|
|
||||||
* constructor */
|
|
||||||
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
|
|
||||||
left = parse_ident(ctx);
|
|
||||||
} else {
|
|
||||||
spl_expr_result_t r = {0};
|
spl_expr_result_t r = {0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
|
||||||
|
return parse_ident(ctx);
|
||||||
|
}
|
||||||
|
spl_expr_result_t r = {0};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Main expression parser (top-level)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||||
|
skip_nl(ctx);
|
||||||
|
spl_expr_result_t left = parse_primary_expr(ctx);
|
||||||
|
if (!left.type)
|
||||||
|
return left;
|
||||||
|
|
||||||
/* Infix parsing (precedence climbing) */
|
/* Infix parsing (precedence climbing) */
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -1220,8 +1157,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse index/begin expression before checking for range */
|
||||||
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
|
||||||
/* Check for slice: expr[begin..end] or expr[begin..] */
|
/* Check for slice: expr[begin..end] or expr[begin..] */
|
||||||
spl_expr_result_t index = spl_parse_expr(ctx, PREC_MIN);
|
|
||||||
if (peek(ctx)->type == TOK_RANGE) {
|
if (peek(ctx)->type == TOK_RANGE) {
|
||||||
advance(ctx); /* skip .. */
|
advance(ctx); /* skip .. */
|
||||||
spl_expr_result_t end_expr = {0};
|
spl_expr_result_t end_expr = {0};
|
||||||
@@ -1394,3 +1333,105 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|||||||
}
|
}
|
||||||
return (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
return (spl_expr_result_t){spl_type_basic(SPL_I32), 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Match arm pattern comparison
|
||||||
|
*
|
||||||
|
* These functions handle the expression-level comparison between
|
||||||
|
* a saved match value and an arm pattern. They belong here in
|
||||||
|
* the expr layer so match arms compose as "enhanced if-conditions"
|
||||||
|
* rather than requiring stmt.c to emit raw comparison instructions.
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type,
|
||||||
|
int val_offset) {
|
||||||
|
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';
|
||||||
|
|
||||||
|
vec_for(enum_type->variants, vi) {
|
||||||
|
spl_enum_variant_t *v = &vec_at(enum_type->variants, vi);
|
||||||
|
if (strcmp(v->name, vname) == 0) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||||
|
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, v->value);
|
||||||
|
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||||
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||||
|
spl_parse_expr(ctx, PREC_LOGOR);
|
||||||
|
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Return instruction — emit SPL_RET with correct type
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type) {
|
||||||
|
spl_type_t rt = SPL_VOID;
|
||||||
|
if (ret_type) {
|
||||||
|
if (ret_type->kind == TYPE_BASIC) {
|
||||||
|
rt = ret_type->basic_type;
|
||||||
|
} else if (ret_type->kind == TYPE_PTR) {
|
||||||
|
rt = SPL_PTR;
|
||||||
|
} else if (spl_type_size(ret_type) <= sizeof(spl_val_t)) {
|
||||||
|
rt = SPL_PTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_RET, rt, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Variable init store — store value from stack to variable
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type) {
|
||||||
|
if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) {
|
||||||
|
usize sz = spl_type_size(var_type);
|
||||||
|
if (sz <= sizeof(spl_val_t)) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
|
} else {
|
||||||
|
usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t);
|
||||||
|
spl_emit_copy_slots(ctx, var_offset, nslots);
|
||||||
|
}
|
||||||
|
} else if (var_type && var_type->kind == TYPE_ARRAY) {
|
||||||
|
spl_type_info_t *elem = var_type->elem;
|
||||||
|
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
|
||||||
|
usize stride = spl_type_elem_stride(elem);
|
||||||
|
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride));
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
|
}
|
||||||
|
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
|
} else {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||||
|
spl_type_t bt = (var_type->kind == TYPE_BASIC) ? var_type->basic_type
|
||||||
|
: (var_type->kind == TYPE_PTR) ? SPL_PTR
|
||||||
|
: SPL_I32;
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,18 +25,7 @@ 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 = SPL_VOID;
|
spl_emit_ret(ctx, ctx->current_ret_type);
|
||||||
if (ctx->current_ret_type) {
|
|
||||||
if (ctx->current_ret_type->kind == TYPE_BASIC) {
|
|
||||||
rt = ctx->current_ret_type->basic_type;
|
|
||||||
} else if (ctx->current_ret_type->kind == TYPE_PTR) {
|
|
||||||
rt = SPL_PTR;
|
|
||||||
} else if (spl_type_size(ctx->current_ret_type) <= sizeof(spl_val_t)) {
|
|
||||||
/* 1-slot struct/enum/etc: use PTR type */
|
|
||||||
rt = SPL_PTR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spl_emit(ctx, SPL_RET, rt, 0);
|
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
@@ -84,9 +73,18 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
|
|||||||
|
|
||||||
/* Init expression: parse before declare to enable type inference */
|
/* Init expression: parse before declare to enable type inference */
|
||||||
spl_expr_result_t init = {0};
|
spl_expr_result_t init = {0};
|
||||||
|
int inline_lit = 0;
|
||||||
if (has_init) {
|
if (has_init) {
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
init = spl_parse_expr(ctx, PREC_MIN);
|
/* Inline struct/enum/slice literal: var x: Type = { .field = val }
|
||||||
|
* Don't call spl_parse_expr — { would be consumed as block expression */
|
||||||
|
if (var_type && peek(ctx)->type == TOK_L_BRACE &&
|
||||||
|
(var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM ||
|
||||||
|
var_type->kind == TYPE_SLICE)) {
|
||||||
|
inline_lit = 1;
|
||||||
|
} else {
|
||||||
|
init = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!var_type) {
|
if (!var_type) {
|
||||||
@@ -96,84 +94,19 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
|
|||||||
|
|
||||||
/* Store init value */
|
/* Store init value */
|
||||||
if (has_init) {
|
if (has_init) {
|
||||||
/* Handle slice struct literal: { .ptr = ..., .len = ... } */
|
if (inline_lit && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM ||
|
||||||
if (var_type && var_type->kind == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE &&
|
var_type->kind == TYPE_SLICE)) {
|
||||||
!init.type) {
|
/* Inline struct/enum/slice literal: var x: Type = { .field = val }
|
||||||
advance(ctx); /* { */
|
* Use spl_parse_struct_literal to handle field parsing uniformly. */
|
||||||
skip_nl(ctx);
|
spl_parse_struct_literal(ctx, var_type);
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
if (var_type->kind == TYPE_SLICE) {
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
/* Slice: copy 2 temp slots to variable */
|
||||||
advance(ctx);
|
spl_emit_copy_slots(ctx, offset, 2);
|
||||||
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 {
|
} else {
|
||||||
/* Multi-slot: copy from temp addr to var, slot by slot */
|
spl_emit_store_init(ctx, offset, var_type);
|
||||||
usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t);
|
|
||||||
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 */
|
|
||||||
spl_type_info_t *elem = var_type->elem;
|
|
||||||
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
|
|
||||||
usize stride = spl_type_elem_stride(elem);
|
|
||||||
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)(i * stride));
|
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
|
||||||
}
|
|
||||||
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
|
||||||
/* Slice initialization: stack has [ptr, len] from slice expression.
|
|
||||||
* Store len at offset+8, then ptr at offset. */
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)sizeof(spl_val_t));
|
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
|
||||||
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
|
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
|
||||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
|
||||||
} else {
|
} else {
|
||||||
/* Single value store */
|
spl_emit_store_init(ctx, offset, var_type);
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
|
|
||||||
spl_type_t bt = (var_type->kind == TYPE_BASIC) ? var_type->basic_type
|
|
||||||
: (var_type->kind == TYPE_PTR) ? SPL_PTR
|
|
||||||
: SPL_I32;
|
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +139,88 @@ void spl_parse_block(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Forward declaration for block expr */
|
||||||
|
static int is_assign_op(spl_tok_type_t t);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Block expression: { stmts; [trailing_expr] }
|
||||||
|
* Parses a block and returns the trailing expression type.
|
||||||
|
* Like Rust/Zig: the last expression without semicolon is the block's value.
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
|
||||||
|
advance(ctx); /* { */
|
||||||
|
spl_push_scope(ctx);
|
||||||
|
|
||||||
|
spl_expr_result_t result = {0}; /* void by default */
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_type_t t = peek(ctx)->type;
|
||||||
|
int is_keyword = (t == KW_RET || t == KW_VAR || t == KW_CONST || t == KW_IF ||
|
||||||
|
t == KW_WHILE || t == KW_LOOP || t == KW_FOR || t == KW_BREAK ||
|
||||||
|
t == KW_CONTINUE || t == KW_DEFER || t == KW_MATCH || t == KW_TYPE ||
|
||||||
|
t == TOK_L_BRACE || t == TOK_SHARP || t == TOK_LINE_COMMENT);
|
||||||
|
|
||||||
|
if (is_keyword) {
|
||||||
|
/* Keyword statement — produces void */
|
||||||
|
spl_parse_stmt(ctx);
|
||||||
|
result = (spl_expr_result_t){0};
|
||||||
|
} else {
|
||||||
|
/* Expression — look ahead for assignment */
|
||||||
|
int is_assign = 0;
|
||||||
|
{
|
||||||
|
usize look = ctx->tok_idx;
|
||||||
|
while (look < vec_size(ctx->toks)) {
|
||||||
|
spl_tok_type_t lt = vec_at(ctx->toks, look).type;
|
||||||
|
if (lt == TOK_SEMICOLON || lt == TOK_ENDLINE || lt == TOK_R_BRACE ||
|
||||||
|
lt == TOK_EOF)
|
||||||
|
break;
|
||||||
|
if (lt == TOK_L_PAREN)
|
||||||
|
break;
|
||||||
|
if (is_assign_op(lt)) {
|
||||||
|
is_assign = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
look++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int saved_addr = ctx->addr_of_mode;
|
||||||
|
if (is_assign)
|
||||||
|
ctx->addr_of_mode = 1;
|
||||||
|
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
ctx->addr_of_mode = saved_addr;
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
||||||
|
/* Expression statement: drop value, consume ; */
|
||||||
|
if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC &&
|
||||||
|
expr.type->basic_type != SPL_VOID)
|
||||||
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||||
|
while (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE)
|
||||||
|
advance(ctx);
|
||||||
|
result = (spl_expr_result_t){0};
|
||||||
|
} else {
|
||||||
|
/* Trailing expression — this is the block's value */
|
||||||
|
result = expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skip_nl(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
|
spl_pop_scope(ctx);
|
||||||
|
expect(ctx, TOK_R_BRACE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* If statement: if expr { ... } [else { ... }]
|
* If statement: if expr { ... } [else { ... }]
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@@ -234,41 +249,53 @@ static void parse_if_stmt(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Loop context helpers (save/restore loop state for while/loop/for)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int saved_loop;
|
||||||
|
usize saved_continue;
|
||||||
|
usize saved_bp_count;
|
||||||
|
spl_val_t loop_start;
|
||||||
|
} spl_loop_save_t;
|
||||||
|
|
||||||
|
static void loop_enter(spl_comp_t *ctx, spl_loop_save_t *save) {
|
||||||
|
save->loop_start = vec_size(ctx->prog.insns);
|
||||||
|
save->saved_loop = ctx->in_loop;
|
||||||
|
save->saved_continue = ctx->continue_target;
|
||||||
|
save->saved_bp_count = ctx->break_patch_count;
|
||||||
|
ctx->in_loop = 1;
|
||||||
|
ctx->continue_target = save->loop_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loop_exit(spl_comp_t *ctx, spl_loop_save_t *save) {
|
||||||
|
spl_val_t here = vec_size(ctx->prog.insns);
|
||||||
|
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)save->loop_start - (isize)here - 1));
|
||||||
|
for (usize i = save->saved_bp_count; i < ctx->break_patch_count; i++)
|
||||||
|
spl_patch_to_here(ctx, ctx->break_patches[i]);
|
||||||
|
ctx->break_patch_count = save->saved_bp_count;
|
||||||
|
ctx->in_loop = save->saved_loop;
|
||||||
|
ctx->continue_target = save->saved_continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* While statement: while expr { ... }
|
* While statement: while expr { ... }
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void parse_while_stmt(spl_comp_t *ctx) {
|
static void parse_while_stmt(spl_comp_t *ctx) {
|
||||||
spl_val_t loop_start = vec_size(ctx->prog.insns);
|
|
||||||
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;
|
|
||||||
|
|
||||||
advance(ctx); /* while */
|
advance(ctx); /* while */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)cond;
|
(void)cond;
|
||||||
|
|
||||||
|
spl_loop_save_t save;
|
||||||
|
loop_enter(ctx, &save);
|
||||||
spl_val_t bz_addr = spl_emit_bz(ctx);
|
spl_val_t bz_addr = spl_emit_bz(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx);
|
spl_parse_block(ctx);
|
||||||
|
loop_exit(ctx, &save);
|
||||||
/* JMP back to loop condition — use relative offset */
|
|
||||||
spl_val_t here = vec_size(ctx->prog.insns);
|
|
||||||
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)here - 1));
|
|
||||||
spl_patch_to_here(ctx, bz_addr);
|
spl_patch_to_here(ctx, bz_addr);
|
||||||
|
|
||||||
/* Patch break statements — compute relative offset */
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -276,30 +303,12 @@ static void parse_while_stmt(spl_comp_t *ctx) {
|
|||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void parse_loop_stmt(spl_comp_t *ctx) {
|
static void parse_loop_stmt(spl_comp_t *ctx) {
|
||||||
spl_val_t loop_start = vec_size(ctx->prog.insns);
|
spl_loop_save_t save;
|
||||||
int saved_loop = ctx->in_loop;
|
loop_enter(ctx, &save);
|
||||||
usize saved_continue = ctx->continue_target;
|
|
||||||
usize saved_bp_count = ctx->break_patch_count;
|
|
||||||
|
|
||||||
ctx->in_loop = 1;
|
|
||||||
ctx->continue_target = loop_start;
|
|
||||||
|
|
||||||
advance(ctx); /* loop */
|
advance(ctx); /* loop */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx);
|
spl_parse_block(ctx);
|
||||||
|
loop_exit(ctx, &save);
|
||||||
/* JMP back to loop start — use relative offset */
|
|
||||||
spl_val_t here = vec_size(ctx->prog.insns);
|
|
||||||
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)here - 1));
|
|
||||||
|
|
||||||
/* Patch break statements — compute relative offset */
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -344,21 +353,15 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||||
/* Stack: [end] */
|
/* Stack: [end] */
|
||||||
|
|
||||||
spl_val_t loop_start = vec_size(ctx->prog.insns);
|
|
||||||
|
|
||||||
/* Condition: i < end */
|
/* Condition: i < end */
|
||||||
|
spl_loop_save_t save;
|
||||||
|
loop_enter(ctx, &save);
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset);
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset);
|
||||||
spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1);
|
spl_emit(ctx, SPL_PICK, SPL_VOID, 1);
|
||||||
spl_emit(ctx, SPL_ULT, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_ULT, SPL_USIZE, 0);
|
||||||
spl_val_t bz_addr = spl_emit_bz(ctx);
|
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;
|
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx); /* body */
|
spl_parse_block(ctx); /* body */
|
||||||
|
|
||||||
@@ -370,21 +373,12 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
|
||||||
|
|
||||||
/* JMP back to condition */
|
loop_exit(ctx, &save);
|
||||||
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 */
|
/* Exit: patch bz, drop end */
|
||||||
spl_patch_to_here(ctx, bz_addr);
|
spl_patch_to_here(ctx, bz_addr);
|
||||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
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_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
return;
|
return;
|
||||||
@@ -458,7 +452,8 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
|
||||||
/* Stack: [ptr, len, idx=0] */
|
/* Stack: [ptr, len, idx=0] */
|
||||||
|
|
||||||
spl_val_t loop_start = vec_size(ctx->prog.insns);
|
spl_loop_save_t save;
|
||||||
|
loop_enter(ctx, &save);
|
||||||
|
|
||||||
/* Condition: idx < len */
|
/* Condition: idx < len */
|
||||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy len */
|
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy len */
|
||||||
@@ -487,13 +482,6 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
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_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);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx); /* body */
|
spl_parse_block(ctx); /* body */
|
||||||
|
|
||||||
@@ -504,9 +492,7 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* replace old idx with new */
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* replace old idx with new */
|
||||||
|
|
||||||
/* JMP back to condition */
|
loop_exit(ctx, &save);
|
||||||
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 */
|
/* Exit: patch bz, drop idx, len, ptr */
|
||||||
spl_patch_to_here(ctx, bz_addr);
|
spl_patch_to_here(ctx, bz_addr);
|
||||||
@@ -514,13 +500,6 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop len */
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop len */
|
||||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop ptr */
|
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);
|
spl_pop_scope(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,10 +561,92 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Match statement helpers
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
/* Parse an enum variant pattern in a match arm: .VariantName
|
||||||
|
* Delegates comparison to expr layer (spl_emit_match_enum_cmp).
|
||||||
|
* Returns the variant for binding parsing, or NULL on error. */
|
||||||
|
static spl_enum_variant_t *parse_match_enum_variant(spl_comp_t *ctx, spl_type_info_t *enum_type,
|
||||||
|
int val_offset) {
|
||||||
|
return spl_emit_match_enum_cmp(ctx, enum_type, val_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a value pattern in a match arm (non-enum).
|
||||||
|
* Delegates comparison to expr layer (spl_emit_match_value_cmp).
|
||||||
|
* Uses PREC_LOGOR internally to prevent => from being consumed as assignment. */
|
||||||
|
static void parse_match_value_pattern(spl_comp_t *ctx, int val_offset) {
|
||||||
|
spl_emit_match_value_cmp(ctx, val_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse enum variant data bindings: (name1, name2, ...)
|
||||||
|
* Declares local variables and loads corresponding field data
|
||||||
|
* from the matched value's data area (offset 4+).
|
||||||
|
* Sets *scope_pushed = 1 if bindings declared. */
|
||||||
|
static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *variant, int val_offset,
|
||||||
|
int *scope_pushed) {
|
||||||
|
advance(ctx); /* ( */
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_PAREN) {
|
||||||
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_push_scope(ctx);
|
||||||
|
*scope_pushed = 1;
|
||||||
|
|
||||||
|
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
|
||||||
|
/* Multi-field struct destructuring: one binding per struct field */
|
||||||
|
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; /* skip tag */
|
||||||
|
if ((usize)bi < vec_size(variant->data_type->fields)) {
|
||||||
|
btype = vec_at(variant->data_type->fields, bi).type;
|
||||||
|
field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset;
|
||||||
|
}
|
||||||
|
int 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 (variant->data_type) {
|
||||||
|
/* Single-value 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);
|
||||||
|
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Match statement:
|
* Match statement:
|
||||||
* Enum: match expr { .Variant(bindings) => stmt, ... }
|
* Enum: match expr { .Variant(bindings) => stmt, ... }
|
||||||
* Int: match expr { literal => stmt, ..., _ => stmt }
|
* Value: match expr { lit, lit => stmt, ..., _ => stmt }
|
||||||
|
*
|
||||||
|
* Built as enhanced if-else: each arm is a condition chain.
|
||||||
|
* Fallthrough (comma-separated patterns) uses BNZ for OR.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void parse_match_stmt(spl_comp_t *ctx) {
|
static void parse_match_stmt(spl_comp_t *ctx) {
|
||||||
@@ -594,11 +655,9 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
|
||||||
/* Determine match type (enum or integer) */
|
/* Determine match type */
|
||||||
int is_enum_match = 0;
|
int is_enum_match = 0;
|
||||||
int is_int_match = 0;
|
|
||||||
spl_type_info_t *enum_type = NULL;
|
spl_type_info_t *enum_type = NULL;
|
||||||
|
|
||||||
spl_type_info_t *t = expr.type;
|
spl_type_info_t *t = expr.type;
|
||||||
if (t && t->kind == TYPE_ENUM) {
|
if (t && t->kind == TYPE_ENUM) {
|
||||||
is_enum_match = 1;
|
is_enum_match = 1;
|
||||||
@@ -606,16 +665,12 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
} else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) {
|
} else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) {
|
||||||
is_enum_match = 1;
|
is_enum_match = 1;
|
||||||
enum_type = t->elem;
|
enum_type = t->elem;
|
||||||
} else if (t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type)) {
|
} else if (!(t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type))) {
|
||||||
is_int_match = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_enum_match && !is_int_match) {
|
|
||||||
spl_comp_error(ctx, "match expression must be an enum or integer type");
|
spl_comp_error(ctx, "match expression must be an enum or integer type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the value/address to a temp slot */
|
/* Save match value to temp slot */
|
||||||
int val_offset = ctx->current_local_bytes;
|
int val_offset = ctx->current_local_bytes;
|
||||||
ctx->current_local_bytes += (int)sizeof(spl_val_t);
|
ctx->current_local_bytes += (int)sizeof(spl_val_t);
|
||||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||||
@@ -650,57 +705,23 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
int n_bnz = 0;
|
int n_bnz = 0;
|
||||||
spl_enum_variant_t *arm_variant = NULL;
|
spl_enum_variant_t *arm_variant = NULL;
|
||||||
|
|
||||||
/* --- Parse arm pattern(s): comma-separated values with fallthrough --- */
|
/* --- Parse arm pattern(s): comma-separated with fallthrough --- */
|
||||||
if (peek(ctx)->type == KW_ANY) {
|
if (peek(ctx)->type == KW_ANY) {
|
||||||
/* _ default arm: always matches, no comparison */
|
/* _ default arm: always matches, skip comparison */
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
} else {
|
} else {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (is_enum_match) {
|
if (is_enum_match) {
|
||||||
/* .VariantName */
|
arm_variant = parse_match_enum_variant(ctx, enum_type, val_offset);
|
||||||
if (peek(ctx)->type == TOK_DOT)
|
if (ctx->has_error)
|
||||||
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;
|
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);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
has_parens = 1;
|
has_parens = 1;
|
||||||
break;
|
break; /* bindings → must be last in fallthrough group */
|
||||||
}
|
}
|
||||||
} else if (is_int_match) {
|
} else {
|
||||||
/* Parse expression as arm value */
|
parse_match_value_pattern(ctx, val_offset);
|
||||||
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 */
|
/* Check for more comma-separated patterns */
|
||||||
@@ -708,9 +729,8 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
/* If another pattern follows (not ⇒), emit BNZ for fallthrough */
|
|
||||||
if (peek(ctx)->type != TOK_ASSIGN) {
|
if (peek(ctx)->type != TOK_ASSIGN) {
|
||||||
bnz_addrs[n_bnz++] = spl_emit_bnz(ctx);
|
bnz_addrs[n_bnz++] = spl_emit_bnz(ctx); /* fallthrough to body */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -718,7 +738,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Last pattern: BZ to skip arm if no value matched */
|
/* Last pattern: BZ past body if no match */
|
||||||
if (!ctx->has_error) {
|
if (!ctx->has_error) {
|
||||||
bz_addr = spl_emit_bz(ctx);
|
bz_addr = spl_emit_bz(ctx);
|
||||||
body_start = vec_size(ctx->prog.insns);
|
body_start = vec_size(ctx->prog.insns);
|
||||||
@@ -727,53 +747,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
/* --- Parse enum bindings (only for last variant) --- */
|
/* --- Parse enum bindings (only for last variant) --- */
|
||||||
if (is_enum_match && has_parens && arm_variant) {
|
if (is_enum_match && has_parens && arm_variant) {
|
||||||
advance(ctx); /* ( */
|
parse_match_enum_bindings(ctx, arm_variant, val_offset, &scope_pushed);
|
||||||
skip_nl(ctx);
|
|
||||||
if (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';
|
|
||||||
|
|
||||||
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 = arm_variant->data_type;
|
|
||||||
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
|
||||||
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(ctx, TOK_R_PAREN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -786,10 +760,10 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Parse arm body statement */
|
/* Parse arm body (reuses stmt infrastructure) */
|
||||||
spl_parse_stmt(ctx);
|
spl_parse_stmt(ctx);
|
||||||
|
|
||||||
/* Pop scope if we pushed one */
|
/* Pop scope if bindings were declared */
|
||||||
if (scope_pushed) {
|
if (scope_pushed) {
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
@@ -803,7 +777,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
if (bz_addr)
|
if (bz_addr)
|
||||||
spl_patch_to_here(ctx, bz_addr);
|
spl_patch_to_here(ctx, bz_addr);
|
||||||
|
|
||||||
/* Patch BNZs to body start (fallthrough values matched) */
|
/* Patch BNZ fallthroughs to body start */
|
||||||
for (int i = 0; i < n_bnz; i++) {
|
for (int i = 0; i < n_bnz; i++) {
|
||||||
spl_val_t offset = body_start - bnz_addrs[i] - 1;
|
spl_val_t offset = body_start - bnz_addrs[i] - 1;
|
||||||
spl_patch(ctx, bnz_addrs[i], offset);
|
spl_patch(ctx, bnz_addrs[i], offset);
|
||||||
|
|||||||
Reference in New Issue
Block a user