feat: 装饰器 #[attr] 语法 — parser 解析并跳过

Token(74): +TOK_HASH, lexer 识别 '#', parser 在 fn/struct/enum 前解析 #[ident]
语法就位, 后续可扩展存储属性到 AST 节点

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:00:42 +08:00
parent 443b22bdf1
commit 06c8773fac
4 changed files with 10 additions and 2 deletions
+1
View File
@@ -158,6 +158,7 @@ Token* lex(Arena* a, const char* source, const char* filename,
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); }
else if (c == '#') { tokens[idx++] = make_token(&l, TOK_HASH, l.pos, 1); advance(&l); }
else if (c == '(') { tokens[idx++] = make_token(&l, TOK_LPAREN, l.pos, 1); advance(&l); }
else if (c == ')') { tokens[idx++] = make_token(&l, TOK_RPAREN, l.pos, 1); advance(&l); }
else if (c == '{') { tokens[idx++] = make_token(&l, TOK_LBRACE, l.pos, 1); advance(&l); }
+1 -1
View File
@@ -29,7 +29,7 @@ static const char* NAMES[] = {
[TOK_LBRACKET] = "[", [TOK_RBRACKET] = "]",
[TOK_COMMA] = ",", [TOK_COLON] = ":", [TOK_SEMICOLON] = ";",
[TOK_ASSIGN] = "=",
[TOK_DOT] = ".", [TOK_COLON_COLON] = "::",
[TOK_DOT] = ".", [TOK_COLON_COLON] = "::", [TOK_HASH] = "#",
[TOK_EOF] = "EOF", [TOK_ERROR] = "错误",
};
+1 -1
View File
@@ -26,7 +26,7 @@ typedef enum {
TOK_LBRACKET, TOK_RBRACKET,
TOK_COMMA, TOK_COLON, TOK_SEMICOLON, TOK_ASSIGN,
// 特殊
TOK_DOT, TOK_COLON_COLON,
TOK_DOT, TOK_COLON_COLON, TOK_HASH,
TOK_EOF, TOK_ERROR,
} TokenKind;
+7
View File
@@ -413,6 +413,13 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
if (peek(&p)->kind == TOK_PUB) {
is_pub = true; advance(&p);
}
// 装饰器 #[attr]
while (peek(&p)->kind == TOK_HASH) {
advance(&p); // 跳过 #
if (!expect(&p, TOK_LBRACKET, error, "# 后应为 '['")) return NULL;
if (!expect(&p, TOK_IDENT, error, "装饰器名")) return NULL;
if (!expect(&p, TOK_RBRACKET, error, "缺少 ']'")) return NULL;
}
if (peek(&p)->kind == TOK_TRAIT) {
const Token* tt = advance(&p);
const Token* tname = expect(&p, TOK_IDENT, error, "trait 后应为接口名");