Files
l-language/test/test_codegen.c
T
Serendipity 5237398245 feat: 枚举 enum (P1 #7)
- lexer: TOK_ENUM, TOK_COLON_COLON
- ast: AST_ENUM_DECL, AST_ENUM_VARIANT + AST_PROGRAM enums数组
- parser: enum Name { A, B } + Enum::Variant语法
- sema: SYM_ENUM, 变体验证, enum→i64类型兼容
- codegen: TYPE_ENUM→i64, 变体→ConstInt(索引值)
- 新增集成测试: 17_enum.l

测试: 121 通过 (41+15+47+18)
2026-06-05 14:01:47 +08:00

297 lines
11 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 "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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 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_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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, 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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, 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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, 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));
/* 手动设置类型(绕过 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, 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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, 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, 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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, 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, 3, loc_at(1, 1));
AstNode* enums[] = { enum_decl };
/* let c = Color::Green; */
AstNode* cv = ast_make_enum_variant(&a, "Color", "Green", 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, 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, 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, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, 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);
}
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);
return test_summary();
}