2923e7574d
- 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)
362 lines
13 KiB
C
362 lines
13 KiB
C
#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));
|
||
/* 手动设置类型(绕过 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, 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();
|
||
}
|