stage1 重制18测试用例替换成match 完成现有全部测试
This commit is contained in:
@@ -48,6 +48,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
|||||||
ctx->current_func_idx = -1;
|
ctx->current_func_idx = -1;
|
||||||
ctx->current_ret_type = NULL;
|
ctx->current_ret_type = NULL;
|
||||||
ctx->current_local_bytes = 0;
|
ctx->current_local_bytes = 0;
|
||||||
|
ctx->peak_local_bytes = 0;
|
||||||
ctx->in_loop = 0;
|
ctx->in_loop = 0;
|
||||||
ctx->break_patch_count = 0;
|
ctx->break_patch_count = 0;
|
||||||
ctx->break_patch_cap = 0;
|
ctx->break_patch_cap = 0;
|
||||||
@@ -113,6 +114,35 @@ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 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 ---- */
|
/* ---- Scope management ---- */
|
||||||
|
|
||||||
void spl_push_scope(spl_comp_t *ctx) {
|
void spl_push_scope(spl_comp_t *ctx) {
|
||||||
@@ -151,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))
|
if (aligned < sizeof(spl_val_t))
|
||||||
aligned = sizeof(spl_val_t);
|
aligned = sizeof(spl_val_t);
|
||||||
ctx->current_local_bytes += (int)aligned;
|
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 */
|
/* Add to current scope */
|
||||||
if (vec_size(ctx->scopes) > 0) {
|
if (vec_size(ctx->scopes) > 0) {
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ typedef enum {
|
|||||||
TYPE_INFER, /* _ (to be inferred) */
|
TYPE_INFER, /* _ (to be inferred) */
|
||||||
} spl_type_kind_t;
|
} spl_type_kind_t;
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declarations */
|
||||||
typedef struct spl_type_info spl_type_info_t;
|
typedef struct spl_type_info spl_type_info_t;
|
||||||
|
typedef struct spl_comp spl_comp_t;
|
||||||
|
|
||||||
/* Struct field */
|
/* Struct field */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -89,6 +90,15 @@ const char *spl_type_str(spl_type_info_t *t);
|
|||||||
int spl_type_is_integer(spl_type_t bt);
|
int spl_type_is_integer(spl_type_t bt);
|
||||||
spl_type_info_t *spl_type_clone(spl_type_info_t *t);
|
spl_type_info_t *spl_type_clone(spl_type_info_t *t);
|
||||||
|
|
||||||
|
/* 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
|
* Scope / symbol table
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@@ -134,7 +144,7 @@ typedef struct {
|
|||||||
int count_at_decl; /* defer_count when declared */
|
int count_at_decl; /* defer_count when declared */
|
||||||
} spl_defer_entry_t;
|
} spl_defer_entry_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct spl_comp {
|
||||||
/* Lexer output */
|
/* Lexer output */
|
||||||
spl_tok_vec_t toks;
|
spl_tok_vec_t toks;
|
||||||
usize tok_idx;
|
usize tok_idx;
|
||||||
@@ -162,6 +172,7 @@ typedef struct {
|
|||||||
int current_func_idx;
|
int current_func_idx;
|
||||||
spl_type_info_t *current_ret_type;
|
spl_type_info_t *current_ret_type;
|
||||||
int current_local_bytes; /* next free local byte offset */
|
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
|
const char *current_type_name; /* name of type whose body we're parsing (for method short-name
|
||||||
lookup) */
|
lookup) */
|
||||||
|
|
||||||
|
|||||||
@@ -329,13 +329,13 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Parse array length */
|
/* Parse array length */
|
||||||
spl_tok_t *len_tok = advance(ctx);
|
int len_val;
|
||||||
if (len_tok->type != TOK_INT_LITERAL) {
|
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||||
spl_comp_error(ctx, "expected array length");
|
spl_comp_error(ctx, "expected array length");
|
||||||
spl_expr_result_t r = {0};
|
spl_expr_result_t r = {0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
usize len = (usize)strtoull(len_tok->lexeme, NULL, 0);
|
usize len = (usize)len_val;
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACKET);
|
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};
|
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, ... }
|
/* 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. */
|
||||||
@@ -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);
|
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));
|
||||||
|
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))
|
if (ctx->current_local_bytes - base_offset < (int)sizeof(spl_val_t))
|
||||||
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); /* { */
|
advance(ctx); /* { */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -473,8 +464,13 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
* Stack: [field_addr] — store each element at computed offsets */
|
* Stack: [field_addr] — store each element at computed offsets */
|
||||||
advance(ctx); /* [ */
|
advance(ctx); /* [ */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_tok_t *len_tok = advance(ctx);
|
int len_val;
|
||||||
usize arr_len = (usize)strtoull(len_tok->lexeme, NULL, 0);
|
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);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACKET);
|
expect(ctx, TOK_R_BRACKET);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -483,7 +479,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
expect(ctx, TOK_L_BRACE);
|
expect(ctx, TOK_L_BRACE);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
usize stride = spl_type_elem_stride(elem_type);
|
usize stride = spl_type_elem_stride(elem_type);
|
||||||
spl_type_t st = spl_store_type(elem_type);
|
spl_type_t st = spl_type_emit_type(elem_type);
|
||||||
for (usize i = 0; i < arr_len; i++) {
|
for (usize i = 0; i < arr_len; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
if (peek(ctx)->type == TOK_COMMA)
|
if (peek(ctx)->type == TOK_COMMA)
|
||||||
@@ -519,7 +515,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
} else {
|
} else {
|
||||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)fv;
|
(void)fv;
|
||||||
spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0);
|
spl_emit(ctx, SPL_STORE, spl_type_emit_type(f->type), 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -649,7 +645,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
|
|||||||
} else {
|
} else {
|
||||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)sfv;
|
(void)sfv;
|
||||||
spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0);
|
spl_emit(ctx, SPL_STORE, spl_type_emit_type(sf->type), 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -675,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_LADDR, SPL_PTR, base_offset);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET);
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET);
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
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_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
}
|
}
|
||||||
@@ -1368,9 +1364,8 @@ 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;
|
ctx->addr_of_mode = saved_addr_of_mode;
|
||||||
|
|
||||||
if (left.is_lvalue) {
|
if (left.is_lvalue) {
|
||||||
spl_type_t bt = left.type ? (left.type->kind == TYPE_BASIC ? left.type->basic_type
|
spl_type_t bt = left.type
|
||||||
: left.type->kind == TYPE_PTR ? SPL_PTR
|
? (left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_PTR)
|
||||||
: SPL_I32)
|
|
||||||
: SPL_I32;
|
: SPL_I32;
|
||||||
if (op == TOK_ASSIGN) {
|
if (op == TOK_ASSIGN) {
|
||||||
/* Simple assignment: stack is [addr, rhs] */
|
/* Simple assignment: stack is [addr, rhs] */
|
||||||
|
|||||||
@@ -27,6 +27,23 @@ void skip_nl(spl_comp_t *ctx) {
|
|||||||
advance(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) {
|
const char *spl_tok_type_name(spl_tok_type_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
#define X(name, enum_name, dummy) \
|
#define X(name, enum_name, dummy) \
|
||||||
|
|||||||
@@ -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);
|
int match(spl_comp_t *ctx, spl_tok_type_t type);
|
||||||
void skip_nl(spl_comp_t *ctx);
|
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__ */
|
#endif /* __SPL_LEX_UTIL_H__ */
|
||||||
|
|||||||
@@ -27,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_func_idx = fi;
|
||||||
ctx->current_ret_type = ret_type;
|
ctx->current_ret_type = ret_type;
|
||||||
ctx->current_local_bytes = 0;
|
ctx->current_local_bytes = 0;
|
||||||
|
ctx->peak_local_bytes = 0;
|
||||||
|
|
||||||
spl_push_scope(ctx);
|
spl_push_scope(ctx);
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *
|
|||||||
skip_nl(ctx);
|
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);
|
expect(ctx, TOK_R_BRACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
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);
|
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
|
||||||
/* Determine the enum type (deref pointer if needed) */
|
/* Determine match type (enum or integer) */
|
||||||
spl_type_info_t *enum_type = expr.type;
|
int is_enum_match = 0;
|
||||||
if (enum_type && enum_type->kind == TYPE_PTR && enum_type->elem &&
|
int is_int_match = 0;
|
||||||
enum_type->elem->kind == TYPE_ENUM) {
|
spl_type_info_t *enum_type = NULL;
|
||||||
enum_type = enum_type->elem;
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the enum address (pointer value) to a temp slot */
|
/* Save the value/address to a temp slot */
|
||||||
int addr_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);
|
||||||
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_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
|
|
||||||
@@ -628,7 +642,22 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse .VariantName */
|
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;
|
||||||
|
|
||||||
|
/* --- 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)
|
if (peek(ctx)->type == TOK_DOT)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
spl_tok_t *vtok = advance(ctx);
|
spl_tok_t *vtok = advance(ctx);
|
||||||
@@ -637,7 +666,6 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
memcpy(vname, vtok->lexeme, vnl);
|
memcpy(vname, vtok->lexeme, vnl);
|
||||||
vname[vnl] = '\0';
|
vname[vnl] = '\0';
|
||||||
|
|
||||||
/* Find the variant in the enum type */
|
|
||||||
spl_enum_variant_t *variant = NULL;
|
spl_enum_variant_t *variant = NULL;
|
||||||
vec_for(enum_type->variants, vi) {
|
vec_for(enum_type->variants, vi) {
|
||||||
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
|
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
|
||||||
@@ -649,37 +677,63 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
arm_variant = variant;
|
||||||
|
|
||||||
/* Compare tag: load [addr_offset+0] == variant->value */
|
/* Compare tag: load [val_offset] → addr → +0 → tag */
|
||||||
/* Load tag and compare */
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
|
|
||||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
|
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value);
|
spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value);
|
||||||
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
||||||
spl_val_t bz_addr = spl_emit_bz(ctx);
|
|
||||||
|
|
||||||
/* This arm matches — parse bindings and body */
|
/* If bindings follow, this must be the last pattern */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Parse binding list: (name, name, ...) */
|
|
||||||
int has_parens = 0;
|
|
||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
has_parens = 1;
|
has_parens = 1;
|
||||||
advance(ctx); /* ( */
|
break;
|
||||||
skip_nl(ctx);
|
}
|
||||||
|
} 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push scope for bindings if there are any */
|
/* Check for more comma-separated patterns */
|
||||||
int scope_pushed = 0;
|
skip_nl(ctx);
|
||||||
if (has_parens && peek(ctx)->type != TOK_R_PAREN) {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 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);
|
spl_push_scope(ctx);
|
||||||
scope_pushed = 1;
|
scope_pushed = 1;
|
||||||
|
|
||||||
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
|
if (arm_variant->data_type && arm_variant->data_type->kind == TYPE_STRUCT) {
|
||||||
/* Struct data: each struct field is a binding */
|
|
||||||
int bi = 0;
|
int bi = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
spl_tok_t *btok = advance(ctx);
|
spl_tok_t *btok = advance(ctx);
|
||||||
@@ -690,25 +744,13 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
spl_type_info_t *btype = spl_type_basic(SPL_I32);
|
spl_type_info_t *btype = spl_type_basic(SPL_I32);
|
||||||
usize field_byte_off = 4;
|
usize field_byte_off = 4;
|
||||||
if ((usize)bi < vec_size(variant->data_type->fields)) {
|
if ((usize)bi < vec_size(arm_variant->data_type->fields)) {
|
||||||
btype = vec_at(variant->data_type->fields, bi).type;
|
btype = vec_at(arm_variant->data_type->fields, bi).type;
|
||||||
field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset;
|
field_byte_off = 4 + vec_at(arm_variant->data_type->fields, bi).offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
||||||
|
spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset);
|
||||||
/* 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++;
|
bi++;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -719,34 +761,21 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (variant->data_type) {
|
} else if (arm_variant->data_type) {
|
||||||
/* Simple data type: one binding */
|
|
||||||
spl_tok_t *btok = advance(ctx);
|
spl_tok_t *btok = advance(ctx);
|
||||||
char bname[256];
|
char bname[256];
|
||||||
usize bnl = btok->len < 255 ? btok->len : 255;
|
usize bnl = btok->len < 255 ? btok->len : 255;
|
||||||
memcpy(bname, btok->lexeme, bnl);
|
memcpy(bname, btok->lexeme, bnl);
|
||||||
bname[bnl] = '\0';
|
bname[bnl] = '\0';
|
||||||
|
|
||||||
spl_type_info_t *btype = variant->data_type;
|
spl_type_info_t *btype = arm_variant->data_type;
|
||||||
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
int boffset = spl_declare_var(ctx, bname, btype, 0);
|
||||||
|
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
|
||||||
/* Load from enum data at offset 4 */
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
|
|
||||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 4);
|
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
|
||||||
spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type
|
|
||||||
: (btype->kind == TYPE_PTR) ? SPL_PTR
|
|
||||||
: SPL_I32;
|
|
||||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, boffset);
|
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_parens)
|
|
||||||
expect(ctx, TOK_R_PAREN);
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
}
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* => (two tokens: = >) */
|
/* => (two tokens: = >) */
|
||||||
@@ -771,8 +800,15 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
jmp_to_end[n_jmps++] = spl_emit_jmp(ctx);
|
jmp_to_end[n_jmps++] = spl_emit_jmp(ctx);
|
||||||
|
|
||||||
/* Patch BZ to here (next arm or end) */
|
/* Patch BZ to here (next arm or end) */
|
||||||
|
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) */
|
||||||
|
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);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* spl_type.c — Type system implementation */
|
/* spl_type.c — Type system implementation */
|
||||||
|
|
||||||
#include "spl_comp.h"
|
#include "spl_comp.h"
|
||||||
|
#include "spl_lex_util.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -369,12 +370,12 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Parse array length as integer literal */
|
/* 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");
|
spl_comp_error(ctx, "expected array length");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
usize len = (usize)strtoull(tok->lexeme, NULL, 0);
|
usize len = (usize)len_val;
|
||||||
ctx->tok_idx++;
|
|
||||||
|
|
||||||
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
|
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
|
||||||
spl_comp_error(ctx, "expected ']'");
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user