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:
+122
-20
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user