1 Commits

Author SHA1 Message Date
Serendipity 6ebe551ee3 fix: AST_PARAMETER 增加数组元素类型字段 + 五子棋集成测试
问题: 函数参数声明 i64[N] 只在 TypeInfo 存储数组信息, AST_PARAMETER
仅存 TypeKind(TYPE_ARRAY), 丢失元素类型和大小, 导致 sema 将参数
数组误判为 i32[N], codegen 生成 void GEP 而崩溃。

修复:
- AST_PARAMETER 新增 arr_elem_type/arr_elem_struct/arr_size 字段
- parser 传入 parse_type_expr 的完整数组信息
- sema 将数组信息从 AST 节点复制到 Symbol
- codegen 为数组参数生成正确的 LLVMArrayType

附加: 45_gomoku.l — 5x5 五子棋双AI对弈, 测试数组/函数/循环/字符串

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 18:48:04 +08:00
8 changed files with 110 additions and 14 deletions
+6 -1
View File
@@ -51,11 +51,16 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size
} }
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type,
const char* struct_type_name, bool is_out, SourceLoc loc) { const char* struct_type_name, bool is_out,
TypeKind arr_elem, const char* arr_elem_sname,
int64_t arr_size, SourceLoc loc) {
NEW(alloc, AST_PARAMETER); NEW(alloc, AST_PARAMETER);
n->as.parameter.name = name; n->as.parameter.type = type; n->as.parameter.name = name; n->as.parameter.type = type;
n->as.parameter.struct_type_name = struct_type_name; n->as.parameter.struct_type_name = struct_type_name;
n->as.parameter.is_out = is_out; n->as.parameter.is_out = is_out;
n->as.parameter.arr_elem_type = arr_elem;
n->as.parameter.arr_elem_struct = arr_elem_sname;
n->as.parameter.arr_size = arr_size;
return n; return n;
} }
+4 -2
View File
@@ -76,7 +76,8 @@ struct AstNode {
TypeKind* multi_ret_types; const char** multi_ret_snames; size_t multi_ret_count; TypeKind* multi_ret_types; const char** multi_ret_snames; size_t multi_ret_count;
const char** captured; TypeKind* cap_types; size_t cap_count; } function; const char** captured; TypeKind* cap_types; size_t cap_count; } function;
// AST_PARAMETER (也用作结构体字段: name + type) // AST_PARAMETER (也用作结构体字段: name + type)
struct { const char* name; TypeKind type; const char* struct_type_name; bool is_out; } parameter; struct { const char* name; TypeKind type; const char* struct_type_name; bool is_out;
TypeKind arr_elem_type; const char* arr_elem_struct; int64_t arr_size; } parameter;
// AST_BLOCK // AST_BLOCK
struct { struct AstNode** stmts; size_t stmt_count; } block; struct { struct AstNode** stmts; size_t stmt_count; } block;
// AST_LET_STMT // AST_LET_STMT
@@ -158,7 +159,8 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size
TypeKind ret, const char* ret_struct_name, AstNode* body, TypeKind ret, const char* ret_struct_name, AstNode* body,
bool is_pub, const char** type_params, size_t tp_count, bool is_pub, const char** type_params, size_t tp_count,
SourceLoc loc); SourceLoc loc);
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, bool is_out, SourceLoc loc); AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, bool is_out,
TypeKind arr_elem, const char* arr_elem_sname, int64_t arr_size, SourceLoc loc);
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc); AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc);
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
bool is_mut, AstNode* init, const char* struct_type_name, bool is_mut, AstNode* init, const char* struct_type_name,
+10
View File
@@ -416,6 +416,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
if (param->as.parameter.type == TYPE_STRUCT && if (param->as.parameter.type == TYPE_STRUCT &&
param->as.parameter.struct_type_name) { param->as.parameter.struct_type_name) {
inner_ty = find_struct_type(&ctx, param->as.parameter.struct_type_name); inner_ty = find_struct_type(&ctx, param->as.parameter.struct_type_name);
} else if (param->as.parameter.type == TYPE_ARRAY) {
LLVMTypeRef et = param->as.parameter.arr_elem_struct
? (find_struct_type(&ctx, param->as.parameter.arr_elem_struct) ?: LLVMInt64TypeInContext(ctx.context))
: to_llvm_type(&ctx, param->as.parameter.arr_elem_type);
inner_ty = LLVMArrayType(et, (unsigned)param->as.parameter.arr_size);
} else { } else {
inner_ty = to_llvm_type(&ctx, param->as.parameter.type); inner_ty = to_llvm_type(&ctx, param->as.parameter.type);
} }
@@ -480,6 +485,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
if (pnode->as.parameter.type == TYPE_STRUCT && if (pnode->as.parameter.type == TYPE_STRUCT &&
pnode->as.parameter.struct_type_name) { pnode->as.parameter.struct_type_name) {
param_ty = find_struct_type(&ctx, pnode->as.parameter.struct_type_name); param_ty = find_struct_type(&ctx, pnode->as.parameter.struct_type_name);
} else if (pnode->as.parameter.type == TYPE_ARRAY) {
LLVMTypeRef et = pnode->as.parameter.arr_elem_struct
? (find_struct_type(&ctx, pnode->as.parameter.arr_elem_struct) ?: LLVMInt64TypeInContext(ctx.context))
: to_llvm_type(&ctx, pnode->as.parameter.arr_elem_type);
param_ty = LLVMArrayType(et, (unsigned)pnode->as.parameter.arr_size);
} else { } else {
param_ty = to_llvm_type(&ctx, pnode->as.parameter.type); param_ty = to_llvm_type(&ctx, pnode->as.parameter.type);
} }
+3 -1
View File
@@ -338,7 +338,9 @@ AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error) {
if (pti.kind == TYPE_ERROR) return NULL; if (pti.kind == TYPE_ERROR) return NULL;
plist[pc++] = ast_make_parameter(p->arena, plist[pc++] = ast_make_parameter(p->arena,
arena_strdup_impl(p->arena, pname->start, pname->length), arena_strdup_impl(p->arena, pname->start, pname->length),
pti.kind, pti.struct_name, is_out, tok_loc(pname)); pti.kind, pti.struct_name, is_out,
pti.element_type, pti.element_struct_name, pti.array_size,
tok_loc(pname));
if (match(p, TOK_COMMA)) continue; else break; if (match(p, TOK_COMMA)) continue; else break;
} }
if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL; if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
+7 -3
View File
@@ -27,7 +27,9 @@ AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
} }
fields[fcount++] = ast_make_parameter(p->arena, fields[fcount++] = ast_make_parameter(p->arena,
arena_strdup_impl(p->arena, fname->start, fname->length), arena_strdup_impl(p->arena, fname->start, fname->length),
fti.kind, fti.struct_name, false, tok_loc(fname)); fti.kind, fti.struct_name, false,
fti.element_type, fti.element_struct_name, fti.array_size,
tok_loc(fname));
if (peek(p)->kind == TOK_COMMA) advance(p); if (peek(p)->kind == TOK_COMMA) advance(p);
else break; else break;
} }
@@ -356,7 +358,9 @@ AstNode* parse_function(Parser* p, bool is_pub, ErrorInfo* error) {
if (pti.kind == TYPE_ERROR) return NULL; if (pti.kind == TYPE_ERROR) return NULL;
params[pcount++] = ast_make_parameter(p->arena, params[pcount++] = ast_make_parameter(p->arena,
arena_strdup_impl(p->arena, pname->start, pname->length), arena_strdup_impl(p->arena, pname->start, pname->length),
pti.kind, pti.struct_name, is_out, tok_loc(pname)); pti.kind, pti.struct_name, is_out,
pti.element_type, pti.element_struct_name, pti.array_size,
tok_loc(pname));
if (match(p, TOK_COMMA)) continue; if (match(p, TOK_COMMA)) continue;
else break; else break;
} }
@@ -700,7 +704,7 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
arena_strdup_impl(p.arena, fbuf, strlen(fbuf)), arena_strdup_impl(p.arena, fbuf, strlen(fbuf)),
fn->as.function.multi_ret_types[i], fn->as.function.multi_ret_types[i],
fn->as.function.multi_ret_snames[i], fn->as.function.multi_ret_snames[i],
false, tok_loc(peek(&p))); false, 0, NULL, 0, tok_loc(peek(&p)));
} }
if (struct_count >= 64) { error->message = "结构体过多"; return NULL; } if (struct_count >= 64) { error->message = "结构体过多"; return NULL; }
structs[struct_count++] = ast_make_struct_decl(p.arena, structs[struct_count++] = ast_make_struct_decl(p.arena,
+10
View File
@@ -161,6 +161,11 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
AstNode* p = mono_fn->as.function.params[j]; AstNode* p = mono_fn->as.function.params[j];
Symbol* ps = scope_insert(fn_scope, a, p->as.parameter.name, SYM_PARAMETER, p->as.parameter.type); Symbol* ps = scope_insert(fn_scope, a, p->as.parameter.name, SYM_PARAMETER, p->as.parameter.type);
if (ps && p->as.parameter.is_out) { ps->kind = SYM_VARIABLE; ps->is_mut = true; } if (ps && p->as.parameter.is_out) { ps->kind = SYM_VARIABLE; ps->is_mut = true; }
if (ps && p->as.parameter.type == TYPE_ARRAY) {
ps->array_element_type = p->as.parameter.arr_elem_type;
ps->array_element_struct_name = p->as.parameter.arr_elem_struct;
ps->array_size = p->as.parameter.arr_size;
}
} }
// 分析函数体 // 分析函数体
current_return_type = mono_fn->as.function.return_type; current_return_type = mono_fn->as.function.return_type;
@@ -202,6 +207,11 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
if (sym && pt == TYPE_STRUCT && psn) { if (sym && pt == TYPE_STRUCT && psn) {
sym->struct_type_name = psn; sym->struct_type_name = psn;
} }
if (sym && pt == TYPE_ARRAY) {
sym->array_element_type = p->as.parameter.arr_elem_type;
sym->array_element_struct_name = p->as.parameter.arr_elem_struct;
sym->array_size = p->as.parameter.arr_size;
}
} }
// 解析返回类型的别名 // 解析返回类型的别名
const char* ret_sn = node->as.function.return_struct_type_name; const char* ret_sn = node->as.function.return_struct_type_name;
+63
View File
@@ -0,0 +1,63 @@
// === 五子棋 5x5 — L Language 综合测试 (带简单AI) ===
fn idx(x: i64, y: i64) -> i64 { return y * 5 + x; }
fn count_dir(b: i64[25], x: i64, y: i64, dx: i64, dy: i64, color: i64) -> i64 {
var cx = x + dx; var cy = y + dy; var n = 0;
while cx >= 0 { if cx > 4 { return n; } if cy < 0 { return n; } if cy > 4 { return n; }
if b[idx(cx, cy)] != color { return n; }
n = n + 1; cx = cx + dx; cy = cy + dy; }
return n;
}
fn score_pos(b: i64[25], x: i64, y: i64, c: i64) -> i64 {
if b[idx(x, y)] != 0 { return -1; }
var h = count_dir(b, x, y, 1, 0, c) + count_dir(b, x, y, -1, 0, c);
var v = count_dir(b, x, y, 0, 1, c) + count_dir(b, x, y, 0, -1, c);
var d = count_dir(b, x, y, 1, 1, c) + count_dir(b, x, y, -1, -1, c);
var e = count_dir(b, x, y, 1, -1, c) + count_dir(b, x, y, -1, 1, c);
if h > 2 { return 1000; } if v > 2 { return 1000; }
if d > 2 { return 1000; } if e > 2 { return 1000; }
return h + v + d + e;
}
fn ai(b: i64[25], c: i64) -> i64 {
var bx = 2; var by = 2; var bs = -1; var x = 0;
while x < 5 { var y = 0; while y < 5 {
var s = score_pos(b, x, y, c);
if s > bs { bs = s; bx = x; by = y; }
y = y + 1; } x = x + 1; }
return by * 100 + bx;
}
fn won(b: i64[25], x: i64, y: i64) -> bool {
var c = b[idx(x, y)]; if c == 0 { return false; }
if count_dir(b, x, y, 1, 0, c) + count_dir(b, x, y, -1, 0, c) > 2 { return true; }
if count_dir(b, x, y, 0, 1, c) + count_dir(b, x, y, 0, -1, c) > 2 { return true; }
if count_dir(b, x, y, 1, 1, c) + count_dir(b, x, y, -1, -1, c) > 2 { return true; }
if count_dir(b, x, y, 1, -1, c) + count_dir(b, x, y, -1, 1, c) > 2 { return true; }
return false;
}
fn print5(b: i64[25]) -> void {
var r = 0; while r < 5 {
var line = ""; var c = 0; while c < 5 {
var v = b[idx(c, r)];
if v == 0 { line = line + ". "; }
if v == 1 { line = line + "X "; }
if v == 2 { line = line + "O "; }
c = c + 1; } print_str(line); r = r + 1; }
}
fn main() -> void {
var b: i64[25] = b; var i = 0; while i < 25 { b[i] = 0; i = i + 1; }
b[idx(2, 2)] = 1; var turn = 2; var n = 1;
while n < 25 {
var mv = ai(b, turn); var y = mv / 100; var x = mv - y * 100;
b[idx(x, y)] = turn;
if turn == 1 { print_str("X: "); } else { print_str("O: "); }
print_i64(x); print_i64(y);
if won(b, x, y) { print5(b); if turn == 1 { print_str("X wins!"); } else { print_str("O wins!"); } return; }
n = n + 1; if turn == 1 { turn = 2; } else { turn = 1; }
}
print5(b); print_str("Draw");
}
+7 -7
View File
@@ -129,8 +129,8 @@ void test_codegen_struct_decl() {
/* 构造 AST: struct Point { x: i64, y: i64 } */ /* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2]; AstNode* fields[2];
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl }; AstNode* structs[] = { struct_decl };
@@ -185,8 +185,8 @@ void test_codegen_struct_field_access() {
/* 构造 AST: struct Point { x: i64, y: i64 } */ /* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2]; AstNode* fields[2];
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl }; AstNode* structs[] = { struct_decl };
@@ -356,13 +356,13 @@ void test_codegen_method_call() {
/* struct Point { x: i64, y: i64 } */ /* struct Point { x: i64, y: i64 } */
AstNode* fields[2]; AstNode* fields[2];
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl }; AstNode* structs[] = { struct_decl };
/* fn Point$get_x(self: Point) -> i64 { return self.x; } */ /* fn Point$get_x(self: Point) -> i64 { return self.x; } */
AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", false, loc_at(1, 1)); AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", false, 0, NULL, 0, loc_at(1, 1));
AstNode* params[] = { self_param }; AstNode* params[] = { self_param };
AstNode* self_ident = ast_make_ident(&a, "self", loc_at(1, 1)); AstNode* self_ident = ast_make_ident(&a, "self", loc_at(1, 1));
self_ident->type.kind = TYPE_STRUCT; self_ident->type.kind = TYPE_STRUCT;