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:
+3
-1
@@ -63,7 +63,8 @@ static TokenKind check_keyword(const Token* tok) {
|
|||||||
KW("bool", TOK_BOOL); KW("str", TOK_STR);
|
KW("bool", TOK_BOOL); KW("str", TOK_STR);
|
||||||
KW("void", TOK_VOID);
|
KW("void", TOK_VOID);
|
||||||
KW("struct", TOK_STRUCT); KW("type", TOK_TYPE);
|
KW("struct", TOK_STRUCT); KW("type", TOK_TYPE);
|
||||||
KW("enum", TOK_ENUM); KW("impl", TOK_IMPL);
|
KW("enum", TOK_ENUM); KW("impl", TOK_IMPL); KW("match", TOK_MATCH);
|
||||||
|
KW("_", TOK_UNDERSCORE);
|
||||||
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
|
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
|
||||||
#undef KW
|
#undef KW
|
||||||
return TOK_IDENT;
|
return TOK_IDENT;
|
||||||
@@ -126,6 +127,7 @@ Token* lex(Arena* a, const char* source, const char* filename,
|
|||||||
else if (c == '/') { tokens[idx++] = make_token(&l, TOK_SLASH, l.pos, 1); advance(&l); }
|
else if (c == '/') { tokens[idx++] = make_token(&l, TOK_SLASH, l.pos, 1); advance(&l); }
|
||||||
else if (c == '%') { tokens[idx++] = make_token(&l, TOK_PERCENT, l.pos, 1); advance(&l); }
|
else if (c == '%') { tokens[idx++] = make_token(&l, TOK_PERCENT, l.pos, 1); advance(&l); }
|
||||||
else if (c == '=' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_EQ_EQ, l.pos, 2); advance(&l); advance(&l); }
|
else if (c == '=' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_EQ_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||||
|
else if (c == '=' && peek_next(&l) == '>') { tokens[idx++] = make_token(&l, TOK_MATCH_ARROW, l.pos, 2); advance(&l); advance(&l); }
|
||||||
else if (c == '=') { tokens[idx++] = make_token(&l, TOK_ASSIGN, l.pos, 1); advance(&l); }
|
else if (c == '=') { tokens[idx++] = make_token(&l, TOK_ASSIGN, l.pos, 1); advance(&l); }
|
||||||
else if (c == '!' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_BANG_EQ, l.pos, 2); advance(&l); advance(&l); }
|
else if (c == '!' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_BANG_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||||
else if (c == '!') { tokens[idx++] = make_token(&l, TOK_BANG, l.pos, 1); advance(&l); }
|
else if (c == '!') { tokens[idx++] = make_token(&l, TOK_BANG, l.pos, 1); advance(&l); }
|
||||||
|
|||||||
+3
-2
@@ -8,16 +8,17 @@ static const char* NAMES[] = {
|
|||||||
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_MUT] = "mut", [TOK_IF] = "if",
|
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_MUT] = "mut", [TOK_IF] = "if",
|
||||||
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return",
|
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return",
|
||||||
[TOK_STRUCT] = "struct", [TOK_TYPE] = "type", [TOK_ENUM] = "enum", [TOK_IMPL] = "impl",
|
[TOK_STRUCT] = "struct", [TOK_TYPE] = "type", [TOK_ENUM] = "enum", [TOK_IMPL] = "impl",
|
||||||
|
[TOK_MATCH] = "match",
|
||||||
[TOK_I64] = "i64", [TOK_F64] = "f64", [TOK_BOOL] = "bool", [TOK_STR] = "str", [TOK_VOID] = "void",
|
[TOK_I64] = "i64", [TOK_F64] = "f64", [TOK_BOOL] = "bool", [TOK_STR] = "str", [TOK_VOID] = "void",
|
||||||
[TOK_INT_LIT] = "整数", [TOK_FLOAT_LIT] = "浮点数", [TOK_STR_LIT] = "字符串",
|
[TOK_INT_LIT] = "整数", [TOK_FLOAT_LIT] = "浮点数", [TOK_STR_LIT] = "字符串",
|
||||||
[TOK_TRUE] = "true", [TOK_FALSE] = "false",
|
[TOK_TRUE] = "true", [TOK_FALSE] = "false",
|
||||||
[TOK_IDENT] = "标识符",
|
[TOK_IDENT] = "标识符", [TOK_UNDERSCORE] = "_",
|
||||||
[TOK_PLUS] = "+", [TOK_MINUS] = "-", [TOK_STAR] = "*",
|
[TOK_PLUS] = "+", [TOK_MINUS] = "-", [TOK_STAR] = "*",
|
||||||
[TOK_SLASH] = "/", [TOK_PERCENT] = "%",
|
[TOK_SLASH] = "/", [TOK_PERCENT] = "%",
|
||||||
[TOK_EQ_EQ] = "==", [TOK_BANG_EQ] = "!=",
|
[TOK_EQ_EQ] = "==", [TOK_BANG_EQ] = "!=",
|
||||||
[TOK_LT] = "<", [TOK_GT] = ">", [TOK_LT_EQ] = "<=", [TOK_GT_EQ] = ">=",
|
[TOK_LT] = "<", [TOK_GT] = ">", [TOK_LT_EQ] = "<=", [TOK_GT_EQ] = ">=",
|
||||||
[TOK_AND_AND] = "&&", [TOK_PIPE_PIPE] = "||", [TOK_BANG] = "!",
|
[TOK_AND_AND] = "&&", [TOK_PIPE_PIPE] = "||", [TOK_BANG] = "!",
|
||||||
[TOK_ARROW] = "->", [TOK_DOT_DOT] = "..",
|
[TOK_ARROW] = "->", [TOK_DOT_DOT] = "..", [TOK_MATCH_ARROW] = "=>",
|
||||||
[TOK_PLUS_EQ] = "+=", [TOK_MINUS_EQ] = "-=", [TOK_STAR_EQ] = "*=", [TOK_SLASH_EQ] = "/=",
|
[TOK_PLUS_EQ] = "+=", [TOK_MINUS_EQ] = "-=", [TOK_STAR_EQ] = "*=", [TOK_SLASH_EQ] = "/=",
|
||||||
[TOK_LPAREN] = "(", [TOK_RPAREN] = ")",
|
[TOK_LPAREN] = "(", [TOK_RPAREN] = ")",
|
||||||
[TOK_LBRACE] = "{", [TOK_RBRACE] = "}",
|
[TOK_LBRACE] = "{", [TOK_RBRACE] = "}",
|
||||||
|
|||||||
+3
-3
@@ -7,18 +7,18 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
// 关键字
|
// 关键字
|
||||||
TOK_FN, TOK_LET, TOK_MUT, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN,
|
TOK_FN, TOK_LET, TOK_MUT, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN,
|
||||||
TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_IMPL,
|
TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_IMPL, TOK_MATCH,
|
||||||
// 类型关键字
|
// 类型关键字
|
||||||
TOK_I64, TOK_F64, TOK_BOOL, TOK_STR, TOK_VOID,
|
TOK_I64, TOK_F64, TOK_BOOL, TOK_STR, TOK_VOID,
|
||||||
// 字面量
|
// 字面量
|
||||||
TOK_INT_LIT, TOK_FLOAT_LIT, TOK_TRUE, TOK_FALSE, TOK_STR_LIT,
|
TOK_INT_LIT, TOK_FLOAT_LIT, TOK_TRUE, TOK_FALSE, TOK_STR_LIT,
|
||||||
// 标识符
|
// 标识符
|
||||||
TOK_IDENT,
|
TOK_IDENT, TOK_UNDERSCORE,
|
||||||
// 运算符
|
// 运算符
|
||||||
TOK_PLUS, TOK_MINUS, TOK_STAR, TOK_SLASH, TOK_PERCENT,
|
TOK_PLUS, TOK_MINUS, TOK_STAR, TOK_SLASH, TOK_PERCENT,
|
||||||
TOK_EQ_EQ, TOK_BANG_EQ, TOK_LT, TOK_GT, TOK_LT_EQ, TOK_GT_EQ,
|
TOK_EQ_EQ, TOK_BANG_EQ, TOK_LT, TOK_GT, TOK_LT_EQ, TOK_GT_EQ,
|
||||||
TOK_AND_AND, TOK_PIPE_PIPE, TOK_BANG,
|
TOK_AND_AND, TOK_PIPE_PIPE, TOK_BANG,
|
||||||
TOK_ARROW, TOK_DOT_DOT,
|
TOK_ARROW, TOK_DOT_DOT, TOK_MATCH_ARROW,
|
||||||
TOK_PLUS_EQ, TOK_MINUS_EQ, TOK_STAR_EQ, TOK_SLASH_EQ,
|
TOK_PLUS_EQ, TOK_MINUS_EQ, TOK_STAR_EQ, TOK_SLASH_EQ,
|
||||||
// 分隔符
|
// 分隔符
|
||||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE,
|
TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE,
|
||||||
|
|||||||
@@ -356,6 +356,95 @@ static AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
|
|||||||
farr, fcount, tok_loc(s_tok));
|
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) {
|
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));
|
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) {
|
if (t->kind == TOK_RETURN) {
|
||||||
advance(p);
|
advance(p);
|
||||||
if (match(p, TOK_SEMICOLON)) {
|
if (match(p, TOK_SEMICOLON)) {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
enum Color { Red, Green, Blue }
|
||||||
|
|
||||||
|
fn main() -> i64 {
|
||||||
|
let c = Color::Green;
|
||||||
|
match c {
|
||||||
|
Color::Red => { print_i64(10); }
|
||||||
|
Color::Green => { print_i64(20); }
|
||||||
|
_ => { print_i64(0); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
enum Color { Red, Green, Blue }
|
||||||
|
|
||||||
|
fn main() -> i64 {
|
||||||
|
let c = Color::Red;
|
||||||
|
match c {
|
||||||
|
Color::Red => { print_i64(100); }
|
||||||
|
Color::Green => { print_i64(200); }
|
||||||
|
_ => { print_i64(0); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fn main() -> i64 {
|
||||||
|
let x = 5;
|
||||||
|
match x {
|
||||||
|
1 => { print_i64(10); }
|
||||||
|
2 => { print_i64(20); }
|
||||||
|
5 => { print_i64(50); }
|
||||||
|
_ => { print_i64(99); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fn main() -> i64 {
|
||||||
|
let x = 99;
|
||||||
|
match x {
|
||||||
|
1 => { print_i64(10); }
|
||||||
|
2 => { print_i64(20); }
|
||||||
|
_ => { print_i64(999); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user