#include "test_utils.h" #include "codegen.h" #include "sema.h" #include "ast.h" #include "arena.h" #include 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, 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, 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, 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, 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, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, 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)); /* 手动设置类型(绕过 sema,codegen 需要读取 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, 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, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, 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, 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, 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, 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, loc_at(1, 1)); fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, 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", 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, 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, 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, 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); 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(); }