feat: 结构体 struct — 最后一项 P0 功能
- lexer: TOK_STRUCT, TOK_DOT 关键字和运算符
- ast: AST_STRUCT_DECL/STRUCT_INIT/FIELD_ACCESS 3 种新节点
- parser: struct 声明 + .field 访问 + Name{field:val} 初始化
- sema: struct 类型符号表,字段类型解析,初始化字段检查
- codegen: LLVMStructType + extractvalue/insertvalue 字段操作
- 新增集成测试: 12_struct.l, 13_struct_nested.l
- 基于 Codex 分析报告 P0 #4
所有 P0 功能已全部完成。
This commit is contained in:
+134
-12
@@ -33,6 +33,7 @@ typedef enum {
|
||||
PREC_TERM = 50,
|
||||
PREC_FACTOR = 60,
|
||||
PREC_UNARY = 70,
|
||||
PREC_POSTFIX = 80, // .field, call()
|
||||
} Precedence;
|
||||
|
||||
static Precedence tok_to_prec(TokenKind kind) {
|
||||
@@ -64,6 +65,8 @@ static BinaryOp tok_to_binop(TokenKind kind) {
|
||||
static AstNode* parse_expr(Parser* p, ErrorInfo* error);
|
||||
static AstNode* parse_expr_prec(Parser* p, Precedence prec, ErrorInfo* error);
|
||||
static AstNode* parse_block(Parser* p, ErrorInfo* error);
|
||||
static AstNode* parse_statement(Parser* p, ErrorInfo* error);
|
||||
static AstNode* parse_function(Parser* p, ErrorInfo* error);
|
||||
|
||||
// === 前缀解析 ===
|
||||
static AstNode* parse_unary(Parser* p, ErrorInfo* error) {
|
||||
@@ -99,10 +102,58 @@ static AstNode* parse_literal(Parser* p) {
|
||||
}
|
||||
}
|
||||
|
||||
// === 结构体初始化解析: Name { field: val, ... } ===
|
||||
static AstNode* parse_struct_init(Parser* p, const Token* name, ErrorInfo* error) {
|
||||
advance(p); // 跳过 '{'
|
||||
const char* fnames[32];
|
||||
AstNode* fvals[32];
|
||||
int fcount = 0;
|
||||
|
||||
while (peek(p)->kind != TOK_RBRACE && !error->message) {
|
||||
const Token* fname = expect(p, TOK_IDENT, error, "字段名");
|
||||
if (!fname) return NULL;
|
||||
if (!expect(p, TOK_COLON, error, "缺少 ':'")) return NULL;
|
||||
AstNode* val = parse_expr(p, error);
|
||||
if (!val) return NULL;
|
||||
|
||||
fnames[fcount] = arena_strdup_impl(p->arena, fname->start, fname->length);
|
||||
fvals[fcount] = val;
|
||||
fcount++;
|
||||
|
||||
if (peek(p)->kind == TOK_COMMA) advance(p);
|
||||
else break;
|
||||
}
|
||||
if (!expect(p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
|
||||
|
||||
const char** n_arr = arena_alloc_impl(p->arena, fcount * sizeof(const char*));
|
||||
memcpy(n_arr, fnames, fcount * sizeof(const char*));
|
||||
AstNode** v_arr = arena_alloc_impl(p->arena, fcount * sizeof(AstNode*));
|
||||
memcpy(v_arr, fvals, fcount * sizeof(AstNode*));
|
||||
|
||||
return ast_make_struct_init(p->arena,
|
||||
arena_strdup_impl(p->arena, name->start, name->length),
|
||||
n_arr, v_arr, fcount, name->line, name->col);
|
||||
}
|
||||
|
||||
// === 标识符 / 函数调用 / 结构体初始化 ===
|
||||
static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
|
||||
const Token* name = advance(p);
|
||||
|
||||
// 结构体初始化: Name { field: val, ... }
|
||||
// 用提前看来区别 struct init 和 block:
|
||||
// struct init → { IDENT COLON ... ;block → { 可能是 let/if/while/...
|
||||
if (peek(p)->kind == TOK_LBRACE) {
|
||||
const Token* after_brace = &p->tokens[p->pos + 1];
|
||||
if (after_brace->kind == TOK_IDENT) {
|
||||
const Token* after_fname = &p->tokens[p->pos + 2];
|
||||
if (after_fname->kind == TOK_COLON) {
|
||||
return parse_struct_init(p, name, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 函数调用: name(...)
|
||||
if (match(p, TOK_LPAREN)) {
|
||||
// 函数调用
|
||||
AstNode* args[16]; int arg_count = 0;
|
||||
while (peek(p)->kind != TOK_RPAREN && !error->message) {
|
||||
if (arg_count >= 16) {
|
||||
@@ -149,9 +200,22 @@ static AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error
|
||||
}
|
||||
if (!left) return NULL;
|
||||
|
||||
// 中缀解析循环
|
||||
// 中缀/后置解析循环
|
||||
while (!error->message) {
|
||||
TokenKind kind = peek(p)->kind;
|
||||
|
||||
// 后置字段访问: expr.field
|
||||
if (kind == TOK_DOT) {
|
||||
advance(p); // 跳过 '.'
|
||||
const Token* field = expect(p, TOK_IDENT, error, "缺少字段名");
|
||||
if (!field) return NULL;
|
||||
left = ast_make_field_access(p->arena, left,
|
||||
arena_strdup_impl(p->arena, field->start, field->length),
|
||||
field->line, field->col);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 中缀运算符
|
||||
Precedence prec = tok_to_prec(kind);
|
||||
if (prec <= min_prec) break;
|
||||
|
||||
@@ -179,8 +243,46 @@ static TypeKind token_to_type(TokenKind k) {
|
||||
default: return TYPE_VOID; }
|
||||
}
|
||||
|
||||
// === 结构体声明解析 ===
|
||||
static AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
|
||||
const Token* s_tok = advance(p); // 跳过 'struct'
|
||||
const Token* name = expect(p, TOK_IDENT, error, "struct 后应为结构体名");
|
||||
if (!name) return NULL;
|
||||
if (!expect(p, TOK_LBRACE, error, "缺少 '{'")) return NULL;
|
||||
|
||||
AstNode* fields[32]; int fcount = 0;
|
||||
while (peek(p)->kind != TOK_RBRACE && !error->message) {
|
||||
const Token* fname = expect(p, TOK_IDENT, error, "字段名");
|
||||
if (!fname) return NULL;
|
||||
if (!expect(p, TOK_COLON, error, "缺少 ':'")) return NULL;
|
||||
const Token* ftype = advance(p);
|
||||
TypeKind field_kind;
|
||||
const char* field_struct_name = NULL;
|
||||
if (is_type_token(ftype->kind)) {
|
||||
field_kind = token_to_type(ftype->kind);
|
||||
} else if (ftype->kind == TOK_IDENT) {
|
||||
field_kind = TYPE_STRUCT;
|
||||
field_struct_name = arena_strdup_impl(p->arena, ftype->start, ftype->length);
|
||||
} else {
|
||||
error->message = "无效的字段类型"; error->filename = p->filename;
|
||||
error->line = ftype->line; error->col = ftype->col; return NULL;
|
||||
}
|
||||
fields[fcount++] = ast_make_parameter(p->arena,
|
||||
arena_strdup_impl(p->arena, fname->start, fname->length),
|
||||
field_kind, field_struct_name, fname->line, fname->col);
|
||||
if (peek(p)->kind == TOK_COMMA) advance(p);
|
||||
else break;
|
||||
}
|
||||
if (!expect(p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
|
||||
|
||||
AstNode** farr = arena_alloc_impl(p->arena, fcount * sizeof(AstNode*));
|
||||
memcpy(farr, fields, fcount * sizeof(AstNode*));
|
||||
return ast_make_struct_decl(p->arena,
|
||||
arena_strdup_impl(p->arena, name->start, name->length),
|
||||
farr, fcount, s_tok->line, s_tok->col);
|
||||
}
|
||||
|
||||
// === 语句解析 ===
|
||||
static AstNode* parse_statement(Parser* p, ErrorInfo* error);
|
||||
|
||||
static AstNode* parse_block(Parser* p, ErrorInfo* error) {
|
||||
const Token* open = peek(p);
|
||||
@@ -209,13 +311,20 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
||||
// 可选的类型标注
|
||||
TypeKind annot_type = TYPE_UNKNOWN;
|
||||
bool has_type_annot = false;
|
||||
const char* struct_type_name = NULL;
|
||||
if (match(p, TOK_COLON)) {
|
||||
const Token* type_tok = advance(p);
|
||||
if (!is_type_token(type_tok->kind)) {
|
||||
if (!is_type_token(type_tok->kind) && type_tok->kind != TOK_IDENT) {
|
||||
error->message = "无效的类型标注"; error->filename = p->filename;
|
||||
error->line = type_tok->line; error->col = type_tok->col; return NULL;
|
||||
}
|
||||
annot_type = token_to_type(type_tok->kind);
|
||||
if (is_type_token(type_tok->kind)) {
|
||||
annot_type = token_to_type(type_tok->kind);
|
||||
} else {
|
||||
// struct 类型名
|
||||
annot_type = TYPE_STRUCT;
|
||||
struct_type_name = arena_strdup_impl(p->arena, type_tok->start, type_tok->length);
|
||||
}
|
||||
has_type_annot = true;
|
||||
}
|
||||
if (!expect(p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
|
||||
@@ -224,7 +333,7 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
||||
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||
return ast_make_let(p->arena,
|
||||
arena_strdup_impl(p->arena, name->start, name->length),
|
||||
annot_type, has_type_annot, is_mut, init, t->line, t->col);
|
||||
annot_type, has_type_annot, is_mut, init, struct_type_name, t->line, t->col);
|
||||
}
|
||||
|
||||
if (t->kind == TOK_IF) {
|
||||
@@ -285,7 +394,7 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
||||
const char* vname = arena_strdup_impl(p->arena, var_name->start, var_name->length);
|
||||
|
||||
// 构建: let mut i = start;
|
||||
AstNode* let_stmt = ast_make_let(p->arena, vname, TYPE_UNKNOWN, false, true, start_expr, var_name->line, var_name->col);
|
||||
AstNode* let_stmt = ast_make_let(p->arena, vname, TYPE_UNKNOWN, false, true, start_expr, NULL, var_name->line, var_name->col);
|
||||
|
||||
// 构建: i < end (while 条件)
|
||||
AstNode* cond = ast_make_binary(p->arena, OP_LT,
|
||||
@@ -399,7 +508,7 @@ static AstNode* parse_function(Parser* p, ErrorInfo* error) {
|
||||
}
|
||||
params[pcount++] = ast_make_parameter(p->arena,
|
||||
arena_strdup_impl(p->arena, pname->start, pname->length),
|
||||
token_to_type(ptype->kind), pname->line, pname->col);
|
||||
token_to_type(ptype->kind), NULL, pname->line, pname->col);
|
||||
if (match(p, TOK_COMMA)) continue;
|
||||
else break;
|
||||
}
|
||||
@@ -432,11 +541,24 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
|
||||
Parser p = {.tokens = tokens, .count = count, .pos = 0,
|
||||
.filename = filename, .arena = a};
|
||||
AstNode* functions[256]; int fn_count = 0;
|
||||
AstNode* structs[64]; int struct_count = 0;
|
||||
while (peek(&p)->kind != TOK_EOF && !error->message) {
|
||||
functions[fn_count++] = parse_function(&p, error);
|
||||
if (peek(&p)->kind == TOK_STRUCT) {
|
||||
structs[struct_count++] = parse_struct_decl(&p, error);
|
||||
} else if (peek(&p)->kind == TOK_FN) {
|
||||
functions[fn_count++] = parse_function(&p, error);
|
||||
} else {
|
||||
error->message = "顶层只允许 fn 或 struct";
|
||||
error->filename = p.filename;
|
||||
error->line = peek(&p)->line;
|
||||
error->col = peek(&p)->col;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (error->message) return NULL;
|
||||
AstNode** arr = arena_alloc_impl(a, fn_count * sizeof(AstNode*));
|
||||
memcpy(arr, functions, fn_count * sizeof(AstNode*));
|
||||
return ast_make_program(a, arr, fn_count, 0, 0);
|
||||
AstNode** fn_arr = arena_alloc_impl(a, fn_count * sizeof(AstNode*));
|
||||
memcpy(fn_arr, functions, fn_count * sizeof(AstNode*));
|
||||
AstNode** st_arr = arena_alloc_impl(a, struct_count * sizeof(AstNode*));
|
||||
memcpy(st_arr, structs, struct_count * sizeof(AstNode*));
|
||||
return ast_make_program(a, fn_arr, fn_count, st_arr, struct_count, 0, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user