stage1 重制18测试用例替换成match 完成现有全部测试

This commit is contained in:
zzy
2026-07-11 21:24:52 +08:00
parent 147f26e063
commit 53ae30f1ca
9 changed files with 729 additions and 143 deletions

View File

@@ -48,6 +48,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
ctx->current_func_idx = -1;
ctx->current_ret_type = NULL;
ctx->current_local_bytes = 0;
ctx->peak_local_bytes = 0;
ctx->in_loop = 0;
ctx->break_patch_count = 0;
ctx->break_patch_cap = 0;
@@ -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 ---- */
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))
aligned = sizeof(spl_val_t);
ctx->current_local_bytes += (int)aligned;
if (ctx->current_local_bytes > ctx->peak_local_bytes)
ctx->peak_local_bytes = ctx->current_local_bytes;
/* Add to current scope */
if (vec_size(ctx->scopes) > 0) {

View File

@@ -28,8 +28,9 @@ typedef enum {
TYPE_INFER, /* _ (to be inferred) */
} spl_type_kind_t;
/* Forward declaration */
/* Forward declarations */
typedef struct spl_type_info spl_type_info_t;
typedef struct spl_comp spl_comp_t;
/* Struct field */
typedef struct {
@@ -89,6 +90,15 @@ const char *spl_type_str(spl_type_info_t *t);
int spl_type_is_integer(spl_type_t bt);
spl_type_info_t *spl_type_clone(spl_type_info_t *t);
/* Determine the uniform type for LOAD/STORE codegen (i32→SPL_I32, ptr→SPL_PTR, else→SPL_PTR) */
spl_type_t spl_type_emit_type(spl_type_info_t *type);
/* Emit LOAD from [saved_ptr + byte_offset] and STORE to local var at var_offset.
* Stack: [] → []
* The saved_ptr is loaded from the 1-slot temp at ptr_slot_offset. */
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
spl_type_info_t *data_type, int var_offset);
/* ============================================================
* Scope / symbol table
* ============================================================ */
@@ -134,7 +144,7 @@ typedef struct {
int count_at_decl; /* defer_count when declared */
} spl_defer_entry_t;
typedef struct {
typedef struct spl_comp {
/* Lexer output */
spl_tok_vec_t toks;
usize tok_idx;
@@ -162,6 +172,7 @@ typedef struct {
int current_func_idx;
spl_type_info_t *current_ret_type;
int current_local_bytes; /* next free local byte offset */
int peak_local_bytes; /* peak current_local_bytes for ALLOC sizing */
const char *current_type_name; /* name of type whose body we're parsing (for method short-name
lookup) */

View File

@@ -329,13 +329,13 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
skip_nl(ctx);
/* Parse array length */
spl_tok_t *len_tok = advance(ctx);
if (len_tok->type != TOK_INT_LITERAL) {
int len_val;
if (!spl_parse_int_literal(ctx, &len_val)) {
spl_comp_error(ctx, "expected array length");
spl_expr_result_t r = {0};
return r;
}
usize len = (usize)strtoull(len_tok->lexeme, NULL, 0);
usize len = (usize)len_val;
skip_nl(ctx);
expect(ctx, TOK_R_BRACKET);
@@ -372,19 +372,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
return (spl_expr_result_t){arr_type, 0};
}
/* Determine correct STORE type for a type (handles ptr = 8 bytes). */
static spl_type_t spl_store_type(spl_type_info_t *type) {
if (!type)
return SPL_I32;
if (type->kind == TYPE_BASIC)
return type->basic_type;
if (type->kind == TYPE_PTR)
return SPL_PTR;
if (spl_type_size(type) <= sizeof(spl_val_t))
return SPL_PTR;
return SPL_I32;
}
/* Parse struct/enum literal: Type { .field = val, ... }
* Allocates temp slots for the value, returns lvalue (addr on stack).
* For types fitting in one slot, pushes the packed value directly. */
@@ -392,8 +379,12 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
usize sz = spl_type_size(type);
int base_offset = ctx->current_local_bytes;
ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1));
if (ctx->current_local_bytes > ctx->peak_local_bytes)
ctx->peak_local_bytes = ctx->current_local_bytes;
if (ctx->current_local_bytes - base_offset < (int)sizeof(spl_val_t))
ctx->current_local_bytes = base_offset + (int)sizeof(spl_val_t);
if (ctx->current_local_bytes > ctx->peak_local_bytes)
ctx->peak_local_bytes = ctx->current_local_bytes;
advance(ctx); /* { */
skip_nl(ctx);
@@ -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 */
advance(ctx); /* [ */
skip_nl(ctx);
spl_tok_t *len_tok = advance(ctx);
usize arr_len = (usize)strtoull(len_tok->lexeme, NULL, 0);
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);
@@ -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);
skip_nl(ctx);
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++) {
if (i > 0) {
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 {
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
(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;
}
@@ -649,7 +645,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *
} else {
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
(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;
}
@@ -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_PUSH, SPL_USIZE, DATA_OFFSET);
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_type_t bt = spl_store_type(v->data_type);
spl_type_t bt = spl_type_emit_type(v->data_type);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
}
@@ -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;
if (left.is_lvalue) {
spl_type_t bt = left.type ? (left.type->kind == TYPE_BASIC ? left.type->basic_type
: left.type->kind == TYPE_PTR ? SPL_PTR
: SPL_I32)
spl_type_t bt = left.type
? (left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_PTR)
: SPL_I32;
if (op == TOK_ASSIGN) {
/* Simple assignment: stack is [addr, rhs] */

View File

@@ -27,6 +27,23 @@ void skip_nl(spl_comp_t *ctx) {
advance(ctx);
}
int spl_parse_int_literal(spl_comp_t *ctx, int *val) {
int negate = 0;
if (peek(ctx)->type == TOK_SUB) {
negate = 1;
advance(ctx);
}
if (peek(ctx)->type != TOK_INT_LITERAL) {
if (negate)
ctx->tok_idx--; /* un-consume the SUB token */
return 0;
}
spl_tok_t *tok = advance(ctx);
long long v = strtoll(tok->lexeme, NULL, 0);
*val = negate ? -(int)v : (int)v;
return 1;
}
const char *spl_tok_type_name(spl_tok_type_t type) {
switch (type) {
#define X(name, enum_name, dummy) \

View File

@@ -9,4 +9,9 @@ int expect(spl_comp_t *ctx, spl_tok_type_t type);
int match(spl_comp_t *ctx, spl_tok_type_t type);
void skip_nl(spl_comp_t *ctx);
/* Parse an integer literal value, handling optional '-' prefix for negatives.
* Returns 1 on success (value in *val), 0 if current token isn't an integer.
* On success, advances past all consumed tokens. On failure, does not advance. */
int spl_parse_int_literal(spl_comp_t *ctx, int *val);
#endif /* __SPL_LEX_UTIL_H__ */

View File

@@ -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_ret_type = ret_type;
ctx->current_local_bytes = 0;
ctx->peak_local_bytes = 0;
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);
}
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);
}

View File

@@ -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,7 +642,22 @@ static void parse_match_stmt(spl_comp_t *ctx) {
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)
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);
vname[vnl] = '\0';
/* Find the variant in the enum type */
spl_enum_variant_t *variant = NULL;
vec_for(enum_type->variants, vi) {
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
@@ -649,37 +677,63 @@ static void parse_match_stmt(spl_comp_t *ctx) {
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
break;
}
arm_variant = variant;
/* Compare tag: load [addr_offset+0] == variant->value */
/* Load tag and compare */
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset);
/* 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);
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);
/* Parse binding list: (name, name, ...) */
int has_parens = 0;
if (peek(ctx)->type == TOK_L_PAREN) {
has_parens = 1;
advance(ctx); /* ( */
skip_nl(ctx);
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);
}
/* Push scope for bindings if there are any */
int scope_pushed = 0;
if (has_parens && peek(ctx)->type != TOK_R_PAREN) {
/* 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;
}
/* 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);
scope_pushed = 1;
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
/* Struct data: each struct field is a binding */
if (arm_variant->data_type && arm_variant->data_type->kind == TYPE_STRUCT) {
int bi = 0;
for (;;) {
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);
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;
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);
/* 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);
spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset);
bi++;
skip_nl(ctx);
@@ -719,34 +761,21 @@ static void parse_match_stmt(spl_comp_t *ctx) {
}
break;
}
} else if (variant->data_type) {
/* Simple data type: one binding */
} 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 = variant->data_type;
spl_type_info_t *btype = arm_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);
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
}
}
if (has_parens)
expect(ctx, TOK_R_PAREN);
}
skip_nl(ctx);
/* => (two tokens: = >) */
@@ -771,8 +800,15 @@ 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) */
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);
}

View File

@@ -1,6 +1,7 @@
/* spl_type.c — Type system implementation */
#include "spl_comp.h"
#include "spl_lex_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -369,12 +370,12 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
}
/* Parse array length as integer literal */
if (tok->type != TOK_INT_LITERAL) {
int len_val;
if (!spl_parse_int_literal(ctx, &len_val)) {
spl_comp_error(ctx, "expected array length");
return NULL;
}
usize len = (usize)strtoull(tok->lexeme, NULL, 0);
ctx->tok_idx++;
usize len = (usize)len_val;
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
spl_comp_error(ctx, "expected ']'");

488
stage1/test18_match.spl Normal file
View File

@@ -0,0 +1,488 @@
/* ===== 模块4match 语句 =====
* 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;
}