Files
l-language/test/test_codegen.c
T
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

526 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "test_utils.h"
#include "codegen.h"
#include "sema.h"
#include "ast.h"
#include "arena.h"
#include <llvm-c/Analysis.h>
void test_codegen_simple_function() {
Arena a = arena_create(1);
// 构造 AST: fn main() -> i64 { return 42; }
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 42, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { ret };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
// 验证模块
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
void test_codegen_if_else() {
Arena a = arena_create(1);
// fn main() -> i64 { if true { return 1; } else { return 0; } }
AstNode* then_ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
AstNode* then_stmts[] = { then_ret };
AstNode* then_block = ast_make_block(&a, then_stmts, 1, loc_at(1, 1));
AstNode* else_ret = ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
AstNode* else_stmts[] = { else_ret };
AstNode* else_block = ast_make_block(&a, else_stmts, 1, loc_at(1, 1));
AstNode* if_stmt = ast_make_if(&a,
ast_make_literal_bool(&a, true, loc_at(1, 1)), then_block, else_block, loc_at(1, 1));
AstNode* stmts[] = { if_stmt };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx2 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod2", &err, &ctx2);
ASSERT(mod != NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx2);
arena_destroy(&a);
}
void test_codegen_binary_ops() {
Arena a = arena_create(1);
// fn main() -> i64 { return 1 + 2 * 3; }
AstNode* expr = ast_make_binary(&a, OP_ADD,
ast_make_literal_i64(&a, 1, loc_at(1, 1)),
ast_make_binary(&a, OP_MUL,
ast_make_literal_i64(&a, 2, loc_at(1, 1)),
ast_make_literal_i64(&a, 3, loc_at(1, 1)), loc_at(1, 1)),
loc_at(1, 1));
AstNode* ret = ast_make_return(&a, expr, loc_at(1, 1));
AstNode* stmts[] = { ret };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx3 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod3", &err, &ctx3);
ASSERT(mod != NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx3);
arena_destroy(&a);
}
void test_codegen_while_loop() {
Arena a = arena_create(1);
// fn main() -> i64 { while true { return 0; } return 1; }
AstNode* while_body_stmts[] = {
ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1))
};
AstNode* while_body = ast_make_block(&a, while_body_stmts, 1, loc_at(1, 1));
AstNode* while_stmt = ast_make_while(&a,
ast_make_literal_bool(&a, true, loc_at(1, 1)), while_body, loc_at(1, 1));
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { while_stmt, ret };
AstNode* fn_body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, fn_body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx4 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_while", &err, &ctx4);
ASSERT(mod != NULL);
char* verify_err = NULL;
ASSERT(!LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err));
LLVMDisposeModule(mod);
LLVMContextDispose(ctx4);
arena_destroy(&a);
}
/* === struct IR 生成测试 === */
void test_codegen_struct_decl() {
Arena a = arena_create(1);
/* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2];
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, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl };
/* 构造 fn main() -> i64 { let p = Point { x: 1, y: 2 }; return p.x; } */
const char* fnames[] = {"x", "y"};
AstNode* fvals[] = {
ast_make_literal_i64(&a, 1, loc_at(1, 1)),
ast_make_literal_i64(&a, 2, loc_at(1, 1))
};
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
/* 手动设置类型(绕过 semacodegen 需要读取 type 字段) */
init->type.kind = TYPE_STRUCT;
init->type.struct_name = "Point";
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
init, NULL, 0, NULL, 0, loc_at(1, 1));
/* return p.x; */
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
p_ident->type.kind = TYPE_STRUCT;
p_ident->type.struct_name = "Point";
AstNode* field_x = ast_make_field_access(&a, p_ident, "x", loc_at(1, 1));
field_x->as.field_access.field_index = 0; /* x 是第 0 个字段 */
field_x->type.kind = TYPE_I64;
AstNode* ret = ast_make_return(&a, field_x, loc_at(1, 1));
AstNode* stmts[] = { let_stmt, ret };
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_struct_decl", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
void test_codegen_struct_field_access() {
Arena a = arena_create(1);
/* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2];
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, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl };
/* 构造 fn main() -> i64 { let p = Point { x: 5, y: 10 }; return p.y; } */
const char* fnames[] = {"x", "y"};
AstNode* fvals[] = {
ast_make_literal_i64(&a, 5, loc_at(1, 1)),
ast_make_literal_i64(&a, 10, loc_at(1, 1))
};
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
init->type.kind = TYPE_STRUCT;
init->type.struct_name = "Point";
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
init, NULL, 0, NULL, 0, loc_at(1, 1));
/* return p.y; */
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
p_ident->type.kind = TYPE_STRUCT;
p_ident->type.struct_name = "Point";
AstNode* field_y = ast_make_field_access(&a, p_ident, "y", loc_at(1, 1));
field_y->as.field_access.field_index = 1; /* y 是第 1 个字段 */
field_y->type.kind = TYPE_I64;
AstNode* ret = ast_make_return(&a, field_y, loc_at(1, 1));
AstNode* stmts[] = { let_stmt, ret };
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_struct_field", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
/* === enum 代码生成测试 === */
void test_codegen_enum() {
Arena a = arena_create(1);
/* 构造 AST: enum Color { Red, Green, Blue }
fn main() -> i64 { let c = Color::Green; print_i64(c); return 0; } */
const char* cvariants[] = {"Red", "Green", "Blue"};
AstNode* enum_decl = ast_make_enum_decl(&a, "Color", cvariants, NULL, NULL, 3, loc_at(1, 1));
AstNode* enums[] = { enum_decl };
/* let c = Color::Green; */
AstNode* cv = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
cv->as.enum_variant.variant_index = 1; /* Green = index 1 */
cv->type.kind = TYPE_ENUM;
AstNode* let_stmt = ast_make_let(&a, "c", TYPE_UNKNOWN, false, false,
cv, NULL, 0, NULL, 0, loc_at(1, 1));
/* print_i64(c); */
AstNode* c_ident = ast_make_ident(&a, "c", loc_at(1, 1));
c_ident->type.kind = TYPE_ENUM;
AstNode* args[] = { c_ident };
AstNode* print_call = ast_make_call(&a, "print_i64", args, NULL, 1, loc_at(1, 1));
/* return 0; */
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { let_stmt, print_call, ret };
AstNode* body = ast_make_block(&a, stmts, 3, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_enum", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
/* === 数组代码生成测试 === */
void test_codegen_array() {
Arena a = arena_create(1);
/* 构造 AST:
fn main() -> i64 {
let arr: i64[3] = arr;
arr[0] = 10;
print_i64(arr[0]);
return 0;
}
*/
// let arr: i64[3] = arr;
AstNode* arr_init = ast_make_ident(&a, "arr", loc_at(1, 1));
AstNode* let_stmt = ast_make_let(&a, "arr", TYPE_ARRAY, true, false,
arr_init, NULL, TYPE_I64, NULL, 3, loc_at(1, 1));
// 手动设置 LET_STMT 的类型(绕过 sema
let_stmt->type.kind = TYPE_ARRAY;
let_stmt->type.element_type = TYPE_I64;
let_stmt->type.array_size = 3;
// arr[0] = 10;
AstNode* arr_assign = ast_make_array_assign(&a, "arr",
ast_make_literal_i64(&a, 0, loc_at(1, 1)),
ast_make_literal_i64(&a, 10, loc_at(1, 1)), loc_at(1, 1));
// arr[0] 表达式(print_i64 的参数)
AstNode* arr_ident = ast_make_ident(&a, "arr", loc_at(1, 1));
arr_ident->type.kind = TYPE_ARRAY;
arr_ident->type.element_type = TYPE_I64;
arr_ident->type.array_size = 3;
AstNode* idx_expr = ast_make_index_expr(&a, arr_ident,
ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
idx_expr->type.kind = TYPE_I64; // 元素类型
// print_i64(arr[0]);
AstNode* args[] = { idx_expr };
AstNode* print_call = ast_make_call(&a, "print_i64", args, NULL, 1, loc_at(1, 1));
// return 0;
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { let_stmt, arr_assign, print_call, ret };
AstNode* body = ast_make_block(&a, stmts, 4, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_array", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
/* === 方法调用代码生成测试 === */
void test_codegen_method_call() {
Arena a = arena_create(1);
/* struct Point { x: i64, y: i64 } */
AstNode* fields[2];
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, 0, NULL, 0, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl };
/* fn Point$get_x(self: Point) -> i64 { return self.x; } */
AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", false, 0, NULL, 0, loc_at(1, 1));
AstNode* params[] = { self_param };
AstNode* self_ident = ast_make_ident(&a, "self", loc_at(1, 1));
self_ident->type.kind = TYPE_STRUCT;
self_ident->type.struct_name = "Point";
AstNode* field_x = ast_make_field_access(&a, self_ident, "x", loc_at(1, 1));
field_x->as.field_access.field_index = 0;
field_x->type.kind = TYPE_I64;
AstNode* ret_body = ast_make_return(&a, field_x, loc_at(1, 1));
AstNode* ret_stmts[] = { ret_body };
AstNode* body = ast_make_block(&a, ret_stmts, 1, loc_at(1, 1));
AstNode* get_x_fn = ast_make_function(&a, "Point$get_x", params, 1, TYPE_I64, NULL, body, false, NULL, 0, loc_at(1, 1));
/* fn main() -> i64 {
let p = Point { x: 42, y: 0 };
return p.get_x();
} */
const char* fnames[] = {"x", "y"};
AstNode* fvals[] = {
ast_make_literal_i64(&a, 42, loc_at(1, 1)),
ast_make_literal_i64(&a, 0, loc_at(1, 1))
};
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
init->type.kind = TYPE_STRUCT;
init->type.struct_name = "Point";
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
init, NULL, 0, NULL, 0, loc_at(1, 1));
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
p_ident->type.kind = TYPE_STRUCT;
p_ident->type.struct_name = "Point";
AstNode* method_call = ast_make_method_call(&a, p_ident, "get_x", NULL, NULL, 0, loc_at(1, 1));
method_call->type.kind = TYPE_I64;
AstNode* ret_main = ast_make_return(&a, method_call, loc_at(1, 1));
AstNode* main_stmts[] = { let_stmt, ret_main };
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { get_x_fn, main_fn };
AstNode* prog = ast_make_program(&a, fns, 2, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_method", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
/* === match 代码生成测试 === */
void test_codegen_match() {
Arena a = arena_create(1);
/* enum Color { Red, Green, Blue } */
const char* variants[] = {"Red", "Green", "Blue"};
AstNode* enum_decl = ast_make_enum_decl(&a, "Color", variants, NULL, NULL, 3, loc_at(1, 1));
AstNode* enums[] = { enum_decl };
/* fn main() -> i64 {
let c = Color::Green;
match c {
Color::Red => { return 1; }
Color::Green => { return 2; }
_ => { return 0; }
}
return 0;
}
但 match 在 parser 去糖, 这里直接构造去糖后的 AST:
{ let __match_val = Color::Green;
if __match_val == Color::Red { return 1; }
else if __match_val == Color::Green { return 2; }
else { return 0; }
}
*/
AstNode* match_val = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
match_val->type.kind = TYPE_ENUM;
AstNode* let_stmt = ast_make_let(&a, "__match_val", TYPE_UNKNOWN, false, false,
match_val, NULL, 0, NULL, 0, loc_at(1, 1));
let_stmt->type.kind = TYPE_ENUM;
AstNode* match_ident = ast_make_ident(&a, "__match_val", loc_at(1, 1));
match_ident->type.kind = TYPE_ENUM;
// if __match_val == Color::Red { return 1; }
AstNode* red_variant = ast_make_enum_variant(&a, "Color", "Red", NULL, loc_at(1, 1));
red_variant->type.kind = TYPE_ENUM;
AstNode* red_cond = ast_make_binary(&a, OP_EQ, match_ident, red_variant, loc_at(1, 1));
red_cond->type.kind = TYPE_BOOL;
AstNode* red_ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
AstNode* red_stmts[] = { red_ret };
AstNode* red_block = ast_make_block(&a, red_stmts, 1, loc_at(1, 1));
// if __match_val == Color::Green { return 2; }
AstNode* match_ident2 = ast_make_ident(&a, "__match_val", loc_at(1, 1));
match_ident2->type.kind = TYPE_ENUM;
AstNode* green_variant = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
green_variant->type.kind = TYPE_ENUM;
AstNode* green_cond = ast_make_binary(&a, OP_EQ, match_ident2, green_variant, loc_at(1, 1));
green_cond->type.kind = TYPE_BOOL;
AstNode* green_ret = ast_make_return(&a, ast_make_literal_i64(&a, 2, loc_at(1, 1)), loc_at(1, 1));
AstNode* green_stmts[] = { green_ret };
AstNode* green_block = ast_make_block(&a, green_stmts, 1, loc_at(1, 1));
// else { return 0; }
AstNode* else_ret = ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
AstNode* else_stmts[] = { else_ret };
AstNode* else_block = ast_make_block(&a, else_stmts, 1, loc_at(1, 1));
// if-else 链: if red_cond { red_block } else if green_cond { green_block } else { else_block }
AstNode* inner_if = ast_make_if(&a, green_cond, green_block, else_block, loc_at(1, 1));
AstNode* outer_if = ast_make_if(&a, red_cond, red_block, inner_if, loc_at(1, 1));
AstNode* main_stmts[] = { let_stmt, outer_if };
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, false, NULL, 0, loc_at(1, 1));
AstNode* fns[] = { main_fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
ErrorList errors; error_init(&errors, &a);
sema_analyze(prog, &errors, &a);
ASSERT(errors.count == 0);
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_match", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
int main(void) {
TEST_RUN(test_codegen_simple_function);
TEST_RUN(test_codegen_if_else);
TEST_RUN(test_codegen_binary_ops);
TEST_RUN(test_codegen_while_loop);
TEST_RUN(test_codegen_struct_decl);
TEST_RUN(test_codegen_struct_field_access);
TEST_RUN(test_codegen_enum);
TEST_RUN(test_codegen_array);
TEST_RUN(test_codegen_method_call);
TEST_RUN(test_codegen_match);
return test_summary();
}