Files
l-language/test/test_codegen.c
T
Serendipity 2923e7574d feat: 数组+索引 [T;N], arr[i] (P1 #6)
- lexer: TOK_LBRACKET, TOK_RBRACKET
- type: TYPE_ARRAY + TypeInfo扩展(element_type/array_size)
- ast: AST_INDEX_EXPR, AST_ARRAY_ASSIGN_STMT
- parser: parse_type_expr()支持[T;N], Pratt加[索引], 数组元素赋值
- sema: 数组类型检查, 索引必须i64, 元素赋值类型匹配
- codegen: type_info_to_llvm(TYPE_ARRAY), GEP+load/store
- 新增集成测试: 18_array.l

测试: 136 通过 (41+15+59+21)
2026-06-05 14:19:01 +08:00

362 lines
13 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, 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, 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, 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, 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, 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, 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);
}
/* === 数组代码生成测试 === */
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, 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, 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_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);
}
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);
return test_summary();
}