feat: match 表达式 (P1 #8 收官)
- lexer: TOK_MATCH, TOK_MATCH_ARROW, TOK_UNDERSCORE - parser: parse_match_stmt() desugar → let+if-else链 - 零 sema/codegen 改动 - 4个集成测试: enum/int literal/wildcard match P1 全部完成: type alias + enum + array + impl + match
This commit is contained in:
@@ -356,6 +356,95 @@ static AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
|
||||
farr, fcount, tok_loc(s_tok));
|
||||
}
|
||||
|
||||
// === match 语句解析(脱糖为 let + if-else 链)===
|
||||
// match <expr> { pat1 => { body1 }, pat2 => { body2 }, _ => { body_default } }
|
||||
// → { let __match_val = <expr>; if __match_val == pat1 { body1 } else if __match_val == pat2 { body2 } else { body_default } }
|
||||
static AstNode* parse_match_stmt(Parser* p, ErrorInfo* error) {
|
||||
const Token* match_tok = advance(p); // 跳过 'match'
|
||||
|
||||
// 解析被匹配的表达式
|
||||
AstNode* matched = parse_expr(p, error);
|
||||
if (!matched) return NULL;
|
||||
|
||||
if (!expect(p, TOK_LBRACE, error, "match 后缺少 '{'")) return NULL;
|
||||
|
||||
// 分配临时变量名
|
||||
const char* varname = arena_strdup_impl(p->arena, "__match_val", 12);
|
||||
|
||||
// 收集所有分支
|
||||
enum { MAX_ARMS = 64 };
|
||||
bool arm_is_wildcard[MAX_ARMS];
|
||||
AstNode* arm_pattern[MAX_ARMS];
|
||||
AstNode* arm_body[MAX_ARMS];
|
||||
int arm_count = 0;
|
||||
|
||||
while (peek(p)->kind != TOK_RBRACE && !error->message) {
|
||||
if (arm_count >= MAX_ARMS) {
|
||||
error->message = "match 分支过多 (最多64)";
|
||||
error->filename = p->filename;
|
||||
error->line = peek(p)->line; error->col = peek(p)->col;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (peek(p)->kind == TOK_UNDERSCORE) {
|
||||
arm_is_wildcard[arm_count] = true;
|
||||
arm_pattern[arm_count] = NULL;
|
||||
advance(p); // 跳过 '_'
|
||||
} else {
|
||||
arm_is_wildcard[arm_count] = false;
|
||||
arm_pattern[arm_count] = parse_expr(p, error);
|
||||
if (!arm_pattern[arm_count]) return NULL;
|
||||
}
|
||||
|
||||
// 解析 '=>'
|
||||
if (!expect(p, TOK_MATCH_ARROW, error, "match 分支缺少 '=>'")) return NULL;
|
||||
|
||||
// 解析分支体(必须是一个代码块)
|
||||
arm_body[arm_count] = parse_block(p, error);
|
||||
if (!arm_body[arm_count]) return NULL;
|
||||
|
||||
arm_count++;
|
||||
|
||||
// 跳过可选逗号
|
||||
if (peek(p)->kind == TOK_COMMA) advance(p);
|
||||
}
|
||||
|
||||
if (!expect(p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
|
||||
|
||||
if (arm_count == 0) {
|
||||
error->message = "match 表达式至少需要一个分支";
|
||||
error->filename = p->filename;
|
||||
error->line = match_tok->line; error->col = match_tok->col;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 从最后一个分支往前构建 if-else 链(最后一个分支 = 最内层 else)
|
||||
AstNode* result = NULL;
|
||||
for (int i = arm_count - 1; i >= 0; i--) {
|
||||
if (arm_is_wildcard[i]) {
|
||||
// 通配符分支:if (true) { body } else { result }
|
||||
AstNode* true_cond = ast_make_literal_bool(p->arena, true, tok_loc(match_tok));
|
||||
result = ast_make_if(p->arena, true_cond, arm_body[i], result, tok_loc(match_tok));
|
||||
} else {
|
||||
// if (__match_val == pattern) { body } else { result }
|
||||
AstNode* cond = ast_make_binary(p->arena, OP_EQ,
|
||||
ast_make_ident(p->arena, varname, tok_loc(match_tok)),
|
||||
arm_pattern[i], tok_loc(match_tok));
|
||||
result = ast_make_if(p->arena, cond, arm_body[i], result, tok_loc(match_tok));
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 let __match_val = <matched>;
|
||||
AstNode* let_stmt = ast_make_let(p->arena, varname, TYPE_UNKNOWN,
|
||||
false, false, matched, NULL, 0, NULL, 0, tok_loc(match_tok));
|
||||
|
||||
// 包装为代码块: { let __match_val = <expr>; <if-else 链> }
|
||||
AstNode* stmts_arr[2] = { let_stmt, result };
|
||||
AstNode** stmts = arena_alloc_impl(p->arena, 2 * sizeof(AstNode*));
|
||||
memcpy(stmts, stmts_arr, 2 * sizeof(AstNode*));
|
||||
return ast_make_block(p->arena, stmts, 2, tok_loc(match_tok));
|
||||
}
|
||||
|
||||
// === 语句解析 ===
|
||||
|
||||
static AstNode* parse_block(Parser* p, ErrorInfo* error) {
|
||||
@@ -507,6 +596,10 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
||||
return ast_make_block(p->arena, stmts, 2, tok_loc(t));
|
||||
}
|
||||
|
||||
if (t->kind == TOK_MATCH) {
|
||||
return parse_match_stmt(p, error);
|
||||
}
|
||||
|
||||
if (t->kind == TOK_RETURN) {
|
||||
advance(p);
|
||||
if (match(p, TOK_SEMICOLON)) {
|
||||
|
||||
Reference in New Issue
Block a user