diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 944baa6..87f41d7 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -155,6 +155,7 @@ Token* lex(Arena* a, const char* source, const char* filename, else if (c == '&' && peek_next(&l) == '&') { tokens[idx++] = make_token(&l, TOK_AND_AND, l.pos, 2); advance(&l); advance(&l); } else if (c == '|' && peek_next(&l) == '>') { tokens[idx++] = make_token(&l, TOK_PIPE, l.pos, 2); advance(&l); advance(&l); } else if (c == '|' && peek_next(&l) == '|') { tokens[idx++] = make_token(&l, TOK_PIPE_PIPE, l.pos, 2); advance(&l); advance(&l); } + else if (c == '?' && peek_next(&l) == '?') { tokens[idx++] = make_token(&l, TOK_QMARK_QMARK, l.pos, 2); advance(&l); advance(&l); } else if (c == '.') { tokens[idx++] = make_token(&l, TOK_DOT, l.pos, 1); advance(&l); } else if (c == '[') { tokens[idx++] = make_token(&l, TOK_LBRACKET, l.pos, 1); advance(&l); } else if (c == ']') { tokens[idx++] = make_token(&l, TOK_RBRACKET, l.pos, 1); advance(&l); } diff --git a/src/lexer/token.c b/src/lexer/token.c index dc959fc..eb1a293 100644 --- a/src/lexer/token.c +++ b/src/lexer/token.c @@ -30,6 +30,7 @@ static const char* NAMES[] = { [TOK_COMMA] = ",", [TOK_COLON] = ":", [TOK_SEMICOLON] = ";", [TOK_ASSIGN] = "=", [TOK_DOT] = ".", [TOK_COLON_COLON] = "::", [TOK_HASH] = "#", + [TOK_QMARK_QMARK] = "??", [TOK_EOF] = "EOF", [TOK_ERROR] = "错误", }; diff --git a/src/lexer/token.h b/src/lexer/token.h index 4082d2e..0bb48d8 100644 --- a/src/lexer/token.h +++ b/src/lexer/token.h @@ -26,7 +26,7 @@ typedef enum { TOK_LBRACKET, TOK_RBRACKET, TOK_COMMA, TOK_COLON, TOK_SEMICOLON, TOK_ASSIGN, // 特殊 - TOK_DOT, TOK_COLON_COLON, TOK_HASH, + TOK_DOT, TOK_COLON_COLON, TOK_HASH, TOK_QMARK_QMARK, TOK_EOF, TOK_ERROR, } TokenKind; diff --git a/src/parser/expr.c b/src/parser/expr.c index 22a0ae0..c220c8a 100644 --- a/src/parser/expr.c +++ b/src/parser/expr.c @@ -9,6 +9,7 @@ int parse_depth = 0; // === 运算符优先级 → Precedence 映射 === Precedence tok_to_prec(TokenKind kind) { switch (kind) { + case TOK_QMARK_QMARK: return PREC_COALESCE; case TOK_PIPE_PIPE: return PREC_OR; case TOK_AND_AND: return PREC_AND; case TOK_EQ_EQ: case TOK_BANG_EQ: @@ -419,6 +420,24 @@ AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error) { continue; } + // ?? 空值合并: a ?? b → if a != 0 { a; } else { b; } + if (kind == TOK_QMARK_QMARK) { + if (PREC_COALESCE <= min_prec) break; + advance(p); + AstNode* right = parse_expr_prec(p, PREC_COALESCE, error); + if (!right) return NULL; + AstNode* cond = ast_make_binary(p->arena, OP_NE, left, + ast_make_literal_i64(p->arena, 0, left->loc), left->loc); + AstNode** t_arr = (AstNode**)arena_alloc_impl(p->arena, sizeof(AstNode*)); + t_arr[0] = ast_make_expr_stmt(p->arena, left, left->loc); + AstNode* then_block = ast_make_block(p->arena, t_arr, 1, left->loc); + AstNode** e_arr = (AstNode**)arena_alloc_impl(p->arena, sizeof(AstNode*)); + e_arr[0] = ast_make_expr_stmt(p->arena, right, right->loc); + AstNode* else_block = ast_make_block(p->arena, e_arr, 1, right->loc); + left = ast_make_if(p->arena, cond, then_block, else_block, left->loc); + continue; + } + // 中缀运算符 Precedence prec = tok_to_prec(kind); if (prec <= min_prec) break; diff --git a/src/parser/parse_internal.h b/src/parser/parse_internal.h index 0c8ac45..97f357d 100644 --- a/src/parser/parse_internal.h +++ b/src/parser/parse_internal.h @@ -38,6 +38,7 @@ static inline const Token* expect(Parser* p, TokenKind k, ErrorInfo* e, const ch typedef enum { PREC_NONE = 0, PREC_PIPE = 10, + PREC_COALESCE = 15, PREC_OR = 20, PREC_AND = 30, PREC_COMPARE = 40,