stage1 重制18测试用例替换成match 完成现有全部测试
This commit is contained in:
@@ -583,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) {
|
||||
@@ -592,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);
|
||||
|
||||
@@ -628,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: = >) */
|
||||
@@ -771,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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user