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)
This commit is contained in:
2026-06-05 14:19:01 +08:00
parent 5237398245
commit 2923e7574d
14 changed files with 512 additions and 58 deletions
+122 -20
View File
@@ -12,6 +12,7 @@ static int codegen_depth = 0;
typedef struct VarEntry {
const char* name;
LLVMValueRef alloca;
LLVMTypeRef alloca_type; // 分配的类型(GEP 需要)
struct VarEntry* next;
} VarEntry;
@@ -86,10 +87,10 @@ static LLVMValueRef find_var(CgCtx* ctx, const char* name) {
return NULL;
}
static void add_var(CgCtx* ctx, const char* name, LLVMValueRef alloca) {
static void add_var(CgCtx* ctx, const char* name, LLVMValueRef alloca, LLVMTypeRef alloca_type) {
VarEntry* e = arena_alloc(ctx->arena, sizeof(*e));
if (!e) return;
e->name = name; e->alloca = alloca; e->next = ctx->var_table;
e->name = name; e->alloca = alloca; e->alloca_type = alloca_type; e->next = ctx->var_table;
ctx->var_table = e;
}
@@ -126,6 +127,27 @@ static LLVMTypeRef find_struct_type(CgCtx* ctx, const char* name) {
return NULL;
}
// 从 TypeInfo 生成 LLVM 类型(支持数组、结构体等复合类型)
static LLVMTypeRef type_info_to_llvm(CgCtx* ctx, const TypeInfo* ti) {
switch (ti->kind) {
case TYPE_ARRAY: {
TypeInfo elem = { .kind = ti->element_type, .struct_name = ti->element_struct_name };
LLVMTypeRef elem_ty = type_info_to_llvm(ctx, &elem);
return LLVMArrayType(elem_ty, (unsigned)ti->array_size);
}
case TYPE_STRUCT:
if (ti->struct_name) {
LLVMTypeRef st = find_struct_type(ctx, ti->struct_name);
if (st) return st;
}
return LLVMVoidTypeInContext(ctx->context);
case TYPE_ENUM:
return LLVMInt64TypeInContext(ctx->context);
default:
return to_llvm_type(ctx, ti->kind);
}
}
// === 向前声明 ===
static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node);
static void codegen_stmt(CgCtx* ctx, AstNode* node);
@@ -144,13 +166,7 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
case AST_IDENT_EXPR: {
LLVMValueRef ptr = find_var(ctx, node->as.ident.name);
if (!ptr) return NULL;
LLVMTypeRef load_ty;
if (node->type.kind == TYPE_STRUCT && node->type.struct_name) {
load_ty = find_struct_type(ctx, node->type.struct_name);
if (!load_ty) load_ty = to_llvm_type(ctx, node->type.kind);
} else {
load_ty = to_llvm_type(ctx, node->type.kind);
}
LLVMTypeRef load_ty = type_info_to_llvm(ctx, &node->type);
return LLVMBuildLoad2(ctx->builder, load_ty, ptr, "load");
}
@@ -352,6 +368,47 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
return LLVMConstInt(LLVMInt64TypeInContext(ctx->context),
(unsigned long long)node->as.enum_variant.variant_index, true);
case AST_INDEX_EXPR: {
// 获取数组变量的指针
AstNode* arr_node = node->as.index_expr.array;
LLVMValueRef arr_ptr = NULL;
LLVMTypeRef arr_gp_type = NULL;
if (arr_node->kind == AST_IDENT_EXPR) {
arr_ptr = find_var(ctx, arr_node->as.ident.name);
// 从变量表获取数组类型用于 GEP
for (VarEntry* e = ctx->var_table; e; e = e->next) {
if (strcmp(e->name, arr_node->as.ident.name) == 0) {
arr_gp_type = e->alloca_type; break;
}
}
}
if (!arr_ptr || !arr_gp_type) return NULL;
// 生成索引值
LLVMValueRef idx_val = codegen_expr(ctx, node->as.index_expr.index);
if (!idx_val) return NULL;
// GEP 索引必须是 i32,但 L 使用 i64。截断。
LLVMValueRef idx_i32 = LLVMBuildTrunc(ctx->builder, idx_val,
LLVMInt32TypeInContext(ctx->context), "idx32");
LLVMValueRef indices[] = {
LLVMConstInt(LLVMInt32TypeInContext(ctx->context), 0, false),
idx_i32
};
LLVMValueRef elem_ptr = LLVMBuildGEP2(ctx->builder, arr_gp_type, arr_ptr, indices, 2, "arr_elem");
LLVMTypeRef elem_load_ty;
if (node->type.kind == TYPE_STRUCT && node->type.struct_name) {
elem_load_ty = find_struct_type(ctx, node->type.struct_name);
if (!elem_load_ty) elem_load_ty = to_llvm_type(ctx, node->type.kind);
} else {
elem_load_ty = type_info_to_llvm(ctx, &node->type);
}
return LLVMBuildLoad2(ctx->builder, elem_load_ty, elem_ptr, "arr_load");
}
default:
return NULL;
}
@@ -390,22 +447,38 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
switch (node->kind) {
case AST_LET_STMT: {
LLVMValueRef init_val = codegen_expr(ctx, node->as.let_stmt.init);
if (!init_val) return;
// 使用节点的完整类型信息来确定 LLVM 类型
// 如果 sema 未运行 (node->type.kind == TYPE_UNKNOWN),回退到 init 的类型
LLVMTypeRef var_type;
if (node->as.let_stmt.init->type.kind == TYPE_STRUCT &&
node->as.let_stmt.init->type.struct_name) {
var_type = find_struct_type(ctx, node->as.let_stmt.init->type.struct_name);
if (!var_type) var_type = to_llvm_type(ctx, node->as.let_stmt.init->type.kind);
if (node->type.kind == TYPE_UNKNOWN) {
// 回退到旧行为:使用 init 表达式的类型
AstNode* init_node = node->as.let_stmt.init;
if (init_node->type.kind == TYPE_STRUCT && init_node->type.struct_name) {
var_type = find_struct_type(ctx, init_node->type.struct_name);
if (!var_type) var_type = to_llvm_type(ctx, init_node->type.kind);
} else {
var_type = to_llvm_type(ctx, init_node->type.kind);
}
} else {
var_type = to_llvm_type(ctx, node->as.let_stmt.init->type.kind);
var_type = type_info_to_llvm(ctx, &node->type);
}
if (!var_type) return;
LLVMValueRef alloca = LLVMBuildAlloca(ctx->builder,
var_type, node->as.let_stmt.name);
LLVMBuildStore(ctx->builder, init_val, alloca);
add_var(ctx, node->as.let_stmt.name, alloca);
// 尝试生成 init 值;数组类型可能 init 失败 (自引用占位符)
LLVMValueRef init_val = codegen_expr(ctx, node->as.let_stmt.init);
if (init_val) {
LLVMBuildStore(ctx->builder, init_val, alloca);
} else if (node->type.kind == TYPE_ARRAY) {
// 数组声明: init 失败是预期的 (自引用), 存储零初始化
LLVMValueRef zero_init = LLVMConstNull(var_type);
LLVMBuildStore(ctx->builder, zero_init, alloca);
} else {
return;
}
add_var(ctx, node->as.let_stmt.name, alloca, var_type);
// 自动内存管理: 只追踪 str 堆分配 (拼接/malloc)
// struct 是栈上值类型,不能 free();含 str 字段时 v0.5 扩展
@@ -503,6 +576,35 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
break;
}
case AST_ARRAY_ASSIGN_STMT: {
LLVMValueRef arr_ptr = find_var(ctx, node->as.array_assign.name);
if (!arr_ptr) return;
// 获取数组的 LLVM 类型(从变量表中)
VarEntry* ve = NULL;
for (VarEntry* e = ctx->var_table; e; e = e->next)
if (strcmp(e->name, node->as.array_assign.name) == 0) { ve = e; break; }
LLVMValueRef idx_val = codegen_expr(ctx, node->as.array_assign.index);
if (!idx_val) return;
LLVMValueRef val_val = codegen_expr(ctx, node->as.array_assign.value);
if (!val_val) return;
// i64 → i32 截断
LLVMValueRef idx_i32 = LLVMBuildTrunc(ctx->builder, idx_val,
LLVMInt32TypeInContext(ctx->context), "idx32");
LLVMValueRef indices[] = {
LLVMConstInt(LLVMInt32TypeInContext(ctx->context), 0, false),
idx_i32
};
LLVMValueRef elem_ptr = LLVMBuildGEP2(ctx->builder, ve->alloca_type, arr_ptr, indices, 2, "arr_assign_elem");
LLVMBuildStore(ctx->builder, val_val, elem_ptr);
break;
}
case AST_WHILE_STMT: {
LLVMBasicBlockRef cur_bb = LLVMGetInsertBlock(ctx->builder);
LLVMValueRef cur_fn = LLVMGetBasicBlockParent(cur_bb);
@@ -659,7 +761,7 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
param_ty, pnode->as.parameter.name);
LLVMBuildStore(ctx.builder, param, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca, param_ty);
}
codegen_stmt(&ctx, fn->as.function.body);