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:
@@ -14,6 +14,7 @@ typedef enum {
|
|||||||
TYPE_VOID,
|
TYPE_VOID,
|
||||||
TYPE_STRUCT, // 结构体类型
|
TYPE_STRUCT, // 结构体类型
|
||||||
TYPE_ENUM, // 枚举类型
|
TYPE_ENUM, // 枚举类型
|
||||||
|
TYPE_ARRAY, // 固定大小数组类型
|
||||||
TYPE_UNKNOWN, // 尚未推断
|
TYPE_UNKNOWN, // 尚未推断
|
||||||
TYPE_ERROR, // 类型错误
|
TYPE_ERROR, // 类型错误
|
||||||
} TypeKind;
|
} TypeKind;
|
||||||
@@ -27,6 +28,7 @@ static inline const char* type_name(TypeKind kind) {
|
|||||||
case TYPE_VOID: return "void";
|
case TYPE_VOID: return "void";
|
||||||
case TYPE_STRUCT: return "struct";
|
case TYPE_STRUCT: return "struct";
|
||||||
case TYPE_ENUM: return "enum";
|
case TYPE_ENUM: return "enum";
|
||||||
|
case TYPE_ARRAY: return "array";
|
||||||
default: return "<unknown>";
|
default: return "<unknown>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-1
@@ -6,6 +6,8 @@
|
|||||||
AstNode* n = (AstNode*)arena_alloc_impl(alloc, sizeof(AstNode)); \
|
AstNode* n = (AstNode*)arena_alloc_impl(alloc, sizeof(AstNode)); \
|
||||||
if (!n) return NULL; \
|
if (!n) return NULL; \
|
||||||
n->kind = (k); n->type.kind = TYPE_UNKNOWN; n->type.struct_name = NULL; \
|
n->kind = (k); n->type.kind = TYPE_UNKNOWN; n->type.struct_name = NULL; \
|
||||||
|
n->type.element_type = 0; n->type.element_struct_name = NULL; \
|
||||||
|
n->type.array_size = 0; \
|
||||||
n->loc = loc
|
n->loc = loc
|
||||||
|
|
||||||
AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
|
AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
|
||||||
@@ -49,12 +51,16 @@ AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
||||||
bool is_mut, AstNode* init, const char* struct_type_name, SourceLoc loc) {
|
bool is_mut, AstNode* init, const char* struct_type_name,
|
||||||
|
TypeKind annot_elem_type, const char* annot_elem_struct, int64_t annot_array_size, SourceLoc loc) {
|
||||||
NEW(alloc, AST_LET_STMT);
|
NEW(alloc, AST_LET_STMT);
|
||||||
n->as.let_stmt.name = name; n->as.let_stmt.annot_type = annot_type;
|
n->as.let_stmt.name = name; n->as.let_stmt.annot_type = annot_type;
|
||||||
n->as.let_stmt.has_type_annot = has_type_annot; n->as.let_stmt.is_mut = is_mut;
|
n->as.let_stmt.has_type_annot = has_type_annot; n->as.let_stmt.is_mut = is_mut;
|
||||||
n->as.let_stmt.init = init;
|
n->as.let_stmt.init = init;
|
||||||
n->as.let_stmt.struct_type_name = struct_type_name;
|
n->as.let_stmt.struct_type_name = struct_type_name;
|
||||||
|
n->as.let_stmt.annot_element_type = annot_elem_type;
|
||||||
|
n->as.let_stmt.annot_element_struct_name = annot_elem_struct;
|
||||||
|
n->as.let_stmt.annot_array_size = annot_array_size;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,3 +206,18 @@ AstNode* ast_make_enum_variant(void* alloc, const char* enum_name,
|
|||||||
n->as.enum_variant.variant_index = -1;
|
n->as.enum_variant.variant_index = -1;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstNode* ast_make_index_expr(void* alloc, AstNode* array, AstNode* index, SourceLoc loc) {
|
||||||
|
NEW(alloc, AST_INDEX_EXPR);
|
||||||
|
n->as.index_expr.array = array;
|
||||||
|
n->as.index_expr.index = index;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc) {
|
||||||
|
NEW(alloc, AST_ARRAY_ASSIGN_STMT);
|
||||||
|
n->as.array_assign.name = name;
|
||||||
|
n->as.array_assign.index = index;
|
||||||
|
n->as.array_assign.value = value;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|||||||
+16
-3
@@ -26,6 +26,8 @@ typedef enum {
|
|||||||
AST_TYPE_ALIAS, // type Meters = i64
|
AST_TYPE_ALIAS, // type Meters = i64
|
||||||
AST_ENUM_DECL, // enum Color { Red, Green, Blue }
|
AST_ENUM_DECL, // enum Color { Red, Green, Blue }
|
||||||
AST_ENUM_VARIANT, // Color::Red
|
AST_ENUM_VARIANT, // Color::Red
|
||||||
|
AST_INDEX_EXPR, // arr[i]
|
||||||
|
AST_ARRAY_ASSIGN_STMT,// arr[i] = expr
|
||||||
} AstKind;
|
} AstKind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -38,7 +40,10 @@ typedef enum {
|
|||||||
// 类型信息(语义分析阶段填充)
|
// 类型信息(语义分析阶段填充)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TypeKind kind;
|
TypeKind kind;
|
||||||
const char* struct_name; // TYPE_STRUCT 时的结构体类型名
|
const char* struct_name; // TYPE_STRUCT / TYPE_ENUM
|
||||||
|
TypeKind element_type; // TYPE_ARRAY: 元素类型的 TypeKind
|
||||||
|
const char* element_struct_name; // TYPE_ARRAY: 元素为 struct 时的类型名
|
||||||
|
int64_t array_size; // TYPE_ARRAY: 固定大小
|
||||||
} TypeInfo;
|
} TypeInfo;
|
||||||
|
|
||||||
// AST 节点
|
// AST 节点
|
||||||
@@ -64,7 +69,8 @@ struct AstNode {
|
|||||||
struct { struct AstNode** stmts; size_t stmt_count; } block;
|
struct { struct AstNode** stmts; size_t stmt_count; } block;
|
||||||
// AST_LET_STMT
|
// AST_LET_STMT
|
||||||
struct { const char* name; TypeKind annot_type; bool has_type_annot; bool is_mut; struct AstNode* init;
|
struct { const char* name; TypeKind annot_type; bool has_type_annot; bool is_mut; struct AstNode* init;
|
||||||
const char* struct_type_name; } let_stmt;
|
const char* struct_type_name;
|
||||||
|
TypeKind annot_element_type; const char* annot_element_struct_name; int64_t annot_array_size; } let_stmt;
|
||||||
// AST_ASSIGN_STMT
|
// AST_ASSIGN_STMT
|
||||||
struct { const char* name; struct AstNode* value; } assign_stmt;
|
struct { const char* name; struct AstNode* value; } assign_stmt;
|
||||||
// AST_IF_STMT
|
// AST_IF_STMT
|
||||||
@@ -98,6 +104,10 @@ struct AstNode {
|
|||||||
struct { const char* name; const char** variants; size_t variant_count; } enum_decl;
|
struct { const char* name; const char** variants; size_t variant_count; } enum_decl;
|
||||||
// AST_ENUM_VARIANT
|
// AST_ENUM_VARIANT
|
||||||
struct { const char* enum_name; const char* variant_name; int variant_index; } enum_variant;
|
struct { const char* enum_name; const char* variant_name; int variant_index; } enum_variant;
|
||||||
|
// AST_INDEX_EXPR
|
||||||
|
struct { struct AstNode* array; struct AstNode* index; } index_expr;
|
||||||
|
// AST_ARRAY_ASSIGN_STMT
|
||||||
|
struct { const char* name; struct AstNode* index; struct AstNode* value; } array_assign;
|
||||||
} as;
|
} as;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,7 +121,8 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size
|
|||||||
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, SourceLoc loc);
|
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, SourceLoc loc);
|
||||||
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc);
|
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc);
|
||||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
||||||
bool is_mut, AstNode* init, const char* struct_type_name, SourceLoc loc);
|
bool is_mut, AstNode* init, const char* struct_type_name,
|
||||||
|
TypeKind annot_elem_type, const char* annot_elem_struct, int64_t annot_array_size, SourceLoc loc);
|
||||||
AstNode* ast_make_assign(void* alloc, const char* name, AstNode* value, SourceLoc loc);
|
AstNode* ast_make_assign(void* alloc, const char* name, AstNode* value, SourceLoc loc);
|
||||||
AstNode* ast_make_if(void* alloc, AstNode* cond, AstNode* then_b, AstNode* else_b, SourceLoc loc);
|
AstNode* ast_make_if(void* alloc, AstNode* cond, AstNode* then_b, AstNode* else_b, SourceLoc loc);
|
||||||
AstNode* ast_make_while(void* alloc, AstNode* cond, AstNode* body, SourceLoc loc);
|
AstNode* ast_make_while(void* alloc, AstNode* cond, AstNode* body, SourceLoc loc);
|
||||||
@@ -132,5 +143,7 @@ AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased,
|
|||||||
const char* aliased_struct, SourceLoc loc);
|
const char* aliased_struct, SourceLoc loc);
|
||||||
AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants, size_t count, SourceLoc loc);
|
AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants, size_t count, SourceLoc loc);
|
||||||
AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* variant_name, SourceLoc loc);
|
AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* variant_name, SourceLoc loc);
|
||||||
|
AstNode* ast_make_index_expr(void* alloc, AstNode* array, AstNode* index, SourceLoc loc);
|
||||||
|
AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+121
-19
@@ -12,6 +12,7 @@ static int codegen_depth = 0;
|
|||||||
typedef struct VarEntry {
|
typedef struct VarEntry {
|
||||||
const char* name;
|
const char* name;
|
||||||
LLVMValueRef alloca;
|
LLVMValueRef alloca;
|
||||||
|
LLVMTypeRef alloca_type; // 分配的类型(GEP 需要)
|
||||||
struct VarEntry* next;
|
struct VarEntry* next;
|
||||||
} VarEntry;
|
} VarEntry;
|
||||||
|
|
||||||
@@ -86,10 +87,10 @@ static LLVMValueRef find_var(CgCtx* ctx, const char* name) {
|
|||||||
return NULL;
|
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));
|
VarEntry* e = arena_alloc(ctx->arena, sizeof(*e));
|
||||||
if (!e) return;
|
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;
|
ctx->var_table = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +127,27 @@ static LLVMTypeRef find_struct_type(CgCtx* ctx, const char* name) {
|
|||||||
return NULL;
|
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 LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node);
|
||||||
static void codegen_stmt(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: {
|
case AST_IDENT_EXPR: {
|
||||||
LLVMValueRef ptr = find_var(ctx, node->as.ident.name);
|
LLVMValueRef ptr = find_var(ctx, node->as.ident.name);
|
||||||
if (!ptr) return NULL;
|
if (!ptr) return NULL;
|
||||||
LLVMTypeRef load_ty;
|
LLVMTypeRef load_ty = type_info_to_llvm(ctx, &node->type);
|
||||||
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);
|
|
||||||
}
|
|
||||||
return LLVMBuildLoad2(ctx->builder, load_ty, ptr, "load");
|
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),
|
return LLVMConstInt(LLVMInt64TypeInContext(ctx->context),
|
||||||
(unsigned long long)node->as.enum_variant.variant_index, true);
|
(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:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -390,22 +447,38 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
|||||||
|
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case AST_LET_STMT: {
|
case AST_LET_STMT: {
|
||||||
LLVMValueRef init_val = codegen_expr(ctx, node->as.let_stmt.init);
|
// 使用节点的完整类型信息来确定 LLVM 类型
|
||||||
if (!init_val) return;
|
// 如果 sema 未运行 (node->type.kind == TYPE_UNKNOWN),回退到 init 的类型
|
||||||
|
|
||||||
LLVMTypeRef var_type;
|
LLVMTypeRef var_type;
|
||||||
if (node->as.let_stmt.init->type.kind == TYPE_STRUCT &&
|
if (node->type.kind == TYPE_UNKNOWN) {
|
||||||
node->as.let_stmt.init->type.struct_name) {
|
// 回退到旧行为:使用 init 表达式的类型
|
||||||
var_type = find_struct_type(ctx, node->as.let_stmt.init->type.struct_name);
|
AstNode* init_node = node->as.let_stmt.init;
|
||||||
if (!var_type) var_type = to_llvm_type(ctx, node->as.let_stmt.init->type.kind);
|
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 {
|
} else {
|
||||||
var_type = to_llvm_type(ctx, node->as.let_stmt.init->type.kind);
|
var_type = to_llvm_type(ctx, init_node->type.kind);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var_type = type_info_to_llvm(ctx, &node->type);
|
||||||
|
}
|
||||||
|
if (!var_type) return;
|
||||||
|
|
||||||
LLVMValueRef alloca = LLVMBuildAlloca(ctx->builder,
|
LLVMValueRef alloca = LLVMBuildAlloca(ctx->builder,
|
||||||
var_type, node->as.let_stmt.name);
|
var_type, node->as.let_stmt.name);
|
||||||
|
|
||||||
|
// 尝试生成 init 值;数组类型可能 init 失败 (自引用占位符)
|
||||||
|
LLVMValueRef init_val = codegen_expr(ctx, node->as.let_stmt.init);
|
||||||
|
if (init_val) {
|
||||||
LLVMBuildStore(ctx->builder, init_val, alloca);
|
LLVMBuildStore(ctx->builder, init_val, alloca);
|
||||||
add_var(ctx, node->as.let_stmt.name, 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)
|
// 自动内存管理: 只追踪 str 堆分配 (拼接/malloc)
|
||||||
// struct 是栈上值类型,不能 free();含 str 字段时 v0.5 扩展
|
// struct 是栈上值类型,不能 free();含 str 字段时 v0.5 扩展
|
||||||
@@ -503,6 +576,35 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
|||||||
break;
|
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: {
|
case AST_WHILE_STMT: {
|
||||||
LLVMBasicBlockRef cur_bb = LLVMGetInsertBlock(ctx->builder);
|
LLVMBasicBlockRef cur_bb = LLVMGetInsertBlock(ctx->builder);
|
||||||
LLVMValueRef cur_fn = LLVMGetBasicBlockParent(cur_bb);
|
LLVMValueRef cur_fn = LLVMGetBasicBlockParent(cur_bb);
|
||||||
@@ -659,7 +761,7 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
|||||||
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
|
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
|
||||||
param_ty, pnode->as.parameter.name);
|
param_ty, pnode->as.parameter.name);
|
||||||
LLVMBuildStore(ctx.builder, param, alloca);
|
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);
|
codegen_stmt(&ctx, fn->as.function.body);
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ Token* lex(Arena* a, const char* source, const char* filename,
|
|||||||
else if (c == '|' && peek_next(&l) == '|') { tokens[idx++] = make_token(&l, TOK_PIPE_PIPE, l.pos, 2); advance(&l); advance(&l); }
|
else if (c == '|' && peek_next(&l) == '|') { tokens[idx++] = make_token(&l, TOK_PIPE_PIPE, l.pos, 2); advance(&l); advance(&l); }
|
||||||
else if (c == '.' && peek_next(&l) == '.') { tokens[idx++] = make_token(&l, TOK_DOT_DOT, l.pos, 2); advance(&l); advance(&l); }
|
else if (c == '.' && peek_next(&l) == '.') { tokens[idx++] = make_token(&l, TOK_DOT_DOT, l.pos, 2); advance(&l); advance(&l); }
|
||||||
else if (c == '.') { tokens[idx++] = make_token(&l, TOK_DOT, l.pos, 1); advance(&l); }
|
else if (c == '.') { tokens[idx++] = make_token(&l, TOK_DOT, l.pos, 1); advance(&l); }
|
||||||
|
else if (c == '[') { tokens[idx++] = make_token(&l, TOK_LBRACKET, l.pos, 1); advance(&l); }
|
||||||
|
else if (c == ']') { tokens[idx++] = make_token(&l, TOK_RBRACKET, l.pos, 1); advance(&l); }
|
||||||
else if (c == '(') { tokens[idx++] = make_token(&l, TOK_LPAREN, l.pos, 1); advance(&l); }
|
else if (c == '(') { tokens[idx++] = make_token(&l, TOK_LPAREN, l.pos, 1); advance(&l); }
|
||||||
else if (c == ')') { tokens[idx++] = make_token(&l, TOK_RPAREN, l.pos, 1); advance(&l); }
|
else if (c == ')') { tokens[idx++] = make_token(&l, TOK_RPAREN, l.pos, 1); advance(&l); }
|
||||||
else if (c == '{') { tokens[idx++] = make_token(&l, TOK_LBRACE, l.pos, 1); advance(&l); }
|
else if (c == '{') { tokens[idx++] = make_token(&l, TOK_LBRACE, l.pos, 1); advance(&l); }
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ static const char* NAMES[] = {
|
|||||||
[TOK_PLUS_EQ] = "+=", [TOK_MINUS_EQ] = "-=", [TOK_STAR_EQ] = "*=", [TOK_SLASH_EQ] = "/=",
|
[TOK_PLUS_EQ] = "+=", [TOK_MINUS_EQ] = "-=", [TOK_STAR_EQ] = "*=", [TOK_SLASH_EQ] = "/=",
|
||||||
[TOK_LPAREN] = "(", [TOK_RPAREN] = ")",
|
[TOK_LPAREN] = "(", [TOK_RPAREN] = ")",
|
||||||
[TOK_LBRACE] = "{", [TOK_RBRACE] = "}",
|
[TOK_LBRACE] = "{", [TOK_RBRACE] = "}",
|
||||||
|
[TOK_LBRACKET] = "[", [TOK_RBRACKET] = "]",
|
||||||
[TOK_COMMA] = ",", [TOK_COLON] = ":", [TOK_SEMICOLON] = ";",
|
[TOK_COMMA] = ",", [TOK_COLON] = ":", [TOK_SEMICOLON] = ";",
|
||||||
[TOK_ASSIGN] = "=",
|
[TOK_ASSIGN] = "=",
|
||||||
[TOK_DOT] = ".", [TOK_COLON_COLON] = "::",
|
[TOK_DOT] = ".", [TOK_COLON_COLON] = "::",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ typedef enum {
|
|||||||
TOK_PLUS_EQ, TOK_MINUS_EQ, TOK_STAR_EQ, TOK_SLASH_EQ,
|
TOK_PLUS_EQ, TOK_MINUS_EQ, TOK_STAR_EQ, TOK_SLASH_EQ,
|
||||||
// 分隔符
|
// 分隔符
|
||||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE,
|
TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE,
|
||||||
|
TOK_LBRACKET, TOK_RBRACKET,
|
||||||
TOK_COMMA, TOK_COLON, TOK_SEMICOLON, TOK_ASSIGN,
|
TOK_COMMA, TOK_COLON, TOK_SEMICOLON, TOK_ASSIGN,
|
||||||
// 特殊
|
// 特殊
|
||||||
TOK_DOT, TOK_COLON_COLON,
|
TOK_DOT, TOK_COLON_COLON,
|
||||||
|
|||||||
+72
-3
@@ -231,6 +231,16 @@ static AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 后置索引: expr[expr]
|
||||||
|
if (kind == TOK_LBRACKET) {
|
||||||
|
const Token* lbrack = advance(p); // 跳过 '['
|
||||||
|
AstNode* index = parse_expr(p, error);
|
||||||
|
if (!index) return NULL;
|
||||||
|
if (!expect(p, TOK_RBRACKET, error, "缺少 ']'")) return NULL;
|
||||||
|
left = ast_make_index_expr(p->arena, left, index, tok_loc(lbrack));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 中缀运算符
|
// 中缀运算符
|
||||||
Precedence prec = tok_to_prec(kind);
|
Precedence prec = tok_to_prec(kind);
|
||||||
if (prec <= min_prec) break;
|
if (prec <= min_prec) break;
|
||||||
@@ -255,9 +265,32 @@ static TypeKind token_to_type(TokenKind k) {
|
|||||||
default: return TYPE_VOID; }
|
default: return TYPE_VOID; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// === 类型表达式解析(内置类型/结构体名)===
|
// === 类型表达式解析(内置类型/结构体名/数组类型)===
|
||||||
static TypeInfo parse_type_expr(Parser* p, ErrorInfo* error) {
|
static TypeInfo parse_type_expr(Parser* p, ErrorInfo* error) {
|
||||||
const Token* t = peek(p);
|
const Token* t = peek(p);
|
||||||
|
|
||||||
|
// 数组类型: [element_type; size]
|
||||||
|
if (t->kind == TOK_LBRACKET) {
|
||||||
|
advance(p); // 跳过 '['
|
||||||
|
TypeInfo elem = parse_type_expr(p, error);
|
||||||
|
if (elem.kind == TYPE_ERROR) return elem;
|
||||||
|
if (!expect(p, TOK_SEMICOLON, error, "数组类型中缺少 ';'")) {
|
||||||
|
TypeInfo ti = {0}; ti.kind = TYPE_ERROR; return ti;
|
||||||
|
}
|
||||||
|
const Token* size_tok = expect(p, TOK_INT_LIT, error, "数组大小必须是整数常量");
|
||||||
|
if (!size_tok) { TypeInfo ti = {0}; ti.kind = TYPE_ERROR; return ti; }
|
||||||
|
int64_t size = tok_int_value(size_tok);
|
||||||
|
if (!expect(p, TOK_RBRACKET, error, "缺少 ']'")) {
|
||||||
|
TypeInfo ti = {0}; ti.kind = TYPE_ERROR; return ti;
|
||||||
|
}
|
||||||
|
TypeInfo ti = {0};
|
||||||
|
ti.kind = TYPE_ARRAY;
|
||||||
|
ti.element_type = elem.kind;
|
||||||
|
ti.element_struct_name = elem.struct_name;
|
||||||
|
ti.array_size = size;
|
||||||
|
return ti;
|
||||||
|
}
|
||||||
|
|
||||||
TypeInfo ti = {0};
|
TypeInfo ti = {0};
|
||||||
if (tok_is_type(t->kind)) {
|
if (tok_is_type(t->kind)) {
|
||||||
advance(p);
|
advance(p);
|
||||||
@@ -344,11 +377,17 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
|||||||
TypeKind annot_type = TYPE_UNKNOWN;
|
TypeKind annot_type = TYPE_UNKNOWN;
|
||||||
bool has_type_annot = false;
|
bool has_type_annot = false;
|
||||||
const char* struct_type_name = NULL;
|
const char* struct_type_name = NULL;
|
||||||
|
TypeKind annot_elem_type = 0;
|
||||||
|
const char* annot_elem_struct = NULL;
|
||||||
|
int64_t annot_arr_size = 0;
|
||||||
if (match(p, TOK_COLON)) {
|
if (match(p, TOK_COLON)) {
|
||||||
TypeInfo ti = parse_type_expr(p, error);
|
TypeInfo ti = parse_type_expr(p, error);
|
||||||
if (ti.kind == TYPE_ERROR) return NULL;
|
if (ti.kind == TYPE_ERROR) return NULL;
|
||||||
annot_type = ti.kind;
|
annot_type = ti.kind;
|
||||||
struct_type_name = ti.struct_name;
|
struct_type_name = ti.struct_name;
|
||||||
|
annot_elem_type = ti.element_type;
|
||||||
|
annot_elem_struct = ti.element_struct_name;
|
||||||
|
annot_arr_size = ti.array_size;
|
||||||
has_type_annot = true;
|
has_type_annot = true;
|
||||||
}
|
}
|
||||||
if (!expect(p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
|
if (!expect(p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
|
||||||
@@ -357,7 +396,8 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
|||||||
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||||
return ast_make_let(p->arena,
|
return ast_make_let(p->arena,
|
||||||
arena_strdup_impl(p->arena, name->start, name->length),
|
arena_strdup_impl(p->arena, name->start, name->length),
|
||||||
annot_type, has_type_annot, is_mut, init, struct_type_name, tok_loc(t));
|
annot_type, has_type_annot, is_mut, init, struct_type_name,
|
||||||
|
annot_elem_type, annot_elem_struct, annot_arr_size, tok_loc(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t->kind == TOK_IF) {
|
if (t->kind == TOK_IF) {
|
||||||
@@ -418,7 +458,7 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
|||||||
const char* vname = arena_strdup_impl(p->arena, var_name->start, var_name->length);
|
const char* vname = arena_strdup_impl(p->arena, var_name->start, var_name->length);
|
||||||
|
|
||||||
// 构建: let mut i = start;
|
// 构建: let mut i = start;
|
||||||
AstNode* let_stmt = ast_make_let(p->arena, vname, TYPE_UNKNOWN, false, true, start_expr, NULL, tok_loc(var_name));
|
AstNode* let_stmt = ast_make_let(p->arena, vname, TYPE_UNKNOWN, false, true, start_expr, NULL, 0, NULL, 0, tok_loc(var_name));
|
||||||
|
|
||||||
// 构建: i < end (while 条件)
|
// 构建: i < end (while 条件)
|
||||||
AstNode* cond = ast_make_binary(p->arena, OP_LT,
|
AstNode* cond = ast_make_binary(p->arena, OP_LT,
|
||||||
@@ -462,6 +502,35 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
|||||||
return ast_make_return(p->arena, expr, tok_loc(t));
|
return ast_make_return(p->arena, expr, tok_loc(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 数组元素赋值: ident[expr] = expr ;
|
||||||
|
// 需要前瞻: 检查 ']' 后面是否是 '=' (而非 ';' 或其它)
|
||||||
|
if (t->kind == TOK_IDENT && (t + 1)->kind == TOK_LBRACKET) {
|
||||||
|
// 向前扫描找到对应的 ']'(不支持嵌套 '[' 在索引中)
|
||||||
|
int ahead_idx = 2;
|
||||||
|
int bracket_depth = 1;
|
||||||
|
while (bracket_depth > 0 && (t + ahead_idx)->kind != TOK_EOF) {
|
||||||
|
if ((t + ahead_idx)->kind == TOK_LBRACKET) bracket_depth++;
|
||||||
|
else if ((t + ahead_idx)->kind == TOK_RBRACKET) bracket_depth--;
|
||||||
|
if (bracket_depth > 0) ahead_idx++;
|
||||||
|
}
|
||||||
|
// 检查 ']' 后是否是 '='
|
||||||
|
if ((t + ahead_idx + 1)->kind == TOK_ASSIGN) {
|
||||||
|
const Token* name = advance(p); // 消费标识符
|
||||||
|
advance(p); // 消费 '['
|
||||||
|
AstNode* index = parse_expr(p, error);
|
||||||
|
if (!index) return NULL;
|
||||||
|
if (!expect(p, TOK_RBRACKET, error, "缺少 ']'")) return NULL;
|
||||||
|
if (!expect(p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
|
||||||
|
AstNode* value = parse_expr(p, error);
|
||||||
|
if (!value) return NULL;
|
||||||
|
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||||
|
return ast_make_array_assign(p->arena,
|
||||||
|
arena_strdup_impl(p->arena, name->start, name->length),
|
||||||
|
index, value, tok_loc(name));
|
||||||
|
}
|
||||||
|
// 否则: 不是数组赋值, 回退到下方表达式语句处理
|
||||||
|
}
|
||||||
|
|
||||||
// 赋值语句: ident = expr ;
|
// 赋值语句: ident = expr ;
|
||||||
if (t->kind == TOK_IDENT && (t + 1)->kind == TOK_ASSIGN) {
|
if (t->kind == TOK_IDENT && (t + 1)->kind == TOK_ASSIGN) {
|
||||||
const Token* name = advance(p); // 消费标识符
|
const Token* name = advance(p); // 消费标识符
|
||||||
|
|||||||
+80
-2
@@ -51,6 +51,11 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
if (sym->type == TYPE_STRUCT && sym->struct_type_name) {
|
if (sym->type == TYPE_STRUCT && sym->struct_type_name) {
|
||||||
node->type.struct_name = sym->struct_type_name;
|
node->type.struct_name = sym->struct_type_name;
|
||||||
}
|
}
|
||||||
|
if (sym->type == TYPE_ARRAY) {
|
||||||
|
node->type.element_type = sym->array_element_type;
|
||||||
|
node->type.element_struct_name = sym->array_element_struct_name;
|
||||||
|
node->type.array_size = sym->array_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -316,6 +321,30 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AST_INDEX_EXPR: {
|
||||||
|
analyze_expr(node->as.index_expr.array, scope, errors, a);
|
||||||
|
analyze_expr(node->as.index_expr.index, scope, errors, a);
|
||||||
|
AstNode* arr = node->as.index_expr.array;
|
||||||
|
AstNode* idx = node->as.index_expr.index;
|
||||||
|
|
||||||
|
if (arr->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; break; }
|
||||||
|
if (arr->type.kind != TYPE_ARRAY) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"类型 '%s' 不支持索引操作", type_name(arr->type.kind));
|
||||||
|
node->type.kind = TYPE_ERROR; break;
|
||||||
|
}
|
||||||
|
if (idx->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; break; }
|
||||||
|
if (idx->type.kind != TYPE_I64) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"数组索引必须是 i64 类型, 得到 '%s'", type_name(idx->type.kind));
|
||||||
|
node->type.kind = TYPE_ERROR; break;
|
||||||
|
}
|
||||||
|
// 结果类型 = 元素类型
|
||||||
|
node->type.kind = arr->type.element_type;
|
||||||
|
node->type.struct_name = arr->type.element_struct_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,12 +479,18 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case AST_LET_STMT: {
|
case AST_LET_STMT: {
|
||||||
analyze_expr(node->as.let_stmt.init, scope, errors, a);
|
|
||||||
TypeKind inferred = node->as.let_stmt.init->type.kind;
|
|
||||||
TypeKind var_type;
|
TypeKind var_type;
|
||||||
const char* var_struct_name = NULL;
|
const char* var_struct_name = NULL;
|
||||||
|
bool is_array_type = false;
|
||||||
|
|
||||||
if (node->as.let_stmt.has_type_annot) {
|
if (node->as.let_stmt.has_type_annot) {
|
||||||
|
if (node->as.let_stmt.annot_type == TYPE_ARRAY) {
|
||||||
|
// 数组类型标注: 跳过 init 分析 (init 是自引用的占位符)
|
||||||
|
is_array_type = true;
|
||||||
|
var_type = TYPE_ARRAY;
|
||||||
|
} else {
|
||||||
|
analyze_expr(node->as.let_stmt.init, scope, errors, a);
|
||||||
|
TypeKind inferred = node->as.let_stmt.init->type.kind;
|
||||||
const char* annot_struct = node->as.let_stmt.struct_type_name;
|
const char* annot_struct = node->as.let_stmt.struct_type_name;
|
||||||
if (annot_struct) {
|
if (annot_struct) {
|
||||||
// 先检查是否是类型别名
|
// 先检查是否是类型别名
|
||||||
@@ -484,7 +519,10 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
annot_struct ? annot_struct : type_name(var_type),
|
annot_struct ? annot_struct : type_name(var_type),
|
||||||
type_name(inferred));
|
type_name(inferred));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
analyze_expr(node->as.let_stmt.init, scope, errors, a);
|
||||||
|
TypeKind inferred = node->as.let_stmt.init->type.kind;
|
||||||
// 类型推断
|
// 类型推断
|
||||||
if (inferred == TYPE_ERROR || inferred == TYPE_VOID) {
|
if (inferred == TYPE_ERROR || inferred == TYPE_VOID) {
|
||||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
@@ -499,6 +537,11 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
|
|
||||||
node->type.kind = var_type;
|
node->type.kind = var_type;
|
||||||
node->type.struct_name = var_struct_name;
|
node->type.struct_name = var_struct_name;
|
||||||
|
if (is_array_type) {
|
||||||
|
node->type.element_type = node->as.let_stmt.annot_element_type;
|
||||||
|
node->type.element_struct_name = node->as.let_stmt.annot_element_struct_name;
|
||||||
|
node->type.array_size = node->as.let_stmt.annot_array_size;
|
||||||
|
}
|
||||||
Symbol* sym = scope_insert(scope, a, node->as.let_stmt.name, SYM_VARIABLE, var_type);
|
Symbol* sym = scope_insert(scope, a, node->as.let_stmt.name, SYM_VARIABLE, var_type);
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
@@ -509,6 +552,11 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
sym->type = TYPE_STRUCT;
|
sym->type = TYPE_STRUCT;
|
||||||
sym->struct_type_name = var_struct_name;
|
sym->struct_type_name = var_struct_name;
|
||||||
}
|
}
|
||||||
|
if (is_array_type) {
|
||||||
|
sym->array_element_type = node->type.element_type;
|
||||||
|
sym->array_element_struct_name = node->type.element_struct_name;
|
||||||
|
sym->array_size = node->type.array_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -545,6 +593,36 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AST_ARRAY_ASSIGN_STMT: {
|
||||||
|
Symbol* sym = scope_lookup(scope, node->as.array_assign.name);
|
||||||
|
if (!sym) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"未定义的变量 '%s'", node->as.array_assign.name);
|
||||||
|
node->type.kind = TYPE_ERROR; break;
|
||||||
|
}
|
||||||
|
if (sym->type != TYPE_ARRAY) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"'%s' 不是数组类型,不能使用索引赋值", node->as.array_assign.name);
|
||||||
|
node->type.kind = TYPE_ERROR; break;
|
||||||
|
}
|
||||||
|
analyze_expr(node->as.array_assign.index, scope, errors, a);
|
||||||
|
analyze_expr(node->as.array_assign.value, scope, errors, a);
|
||||||
|
AstNode* idx = node->as.array_assign.index;
|
||||||
|
AstNode* val = node->as.array_assign.value;
|
||||||
|
if (idx->type.kind != TYPE_ERROR && idx->type.kind != TYPE_I64) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"数组索引必须是 i64 类型, 得到 '%s'", type_name(idx->type.kind));
|
||||||
|
}
|
||||||
|
TypeKind elem_kind = sym->type == TYPE_ARRAY ? TYPE_I64 : sym->type;
|
||||||
|
if (val->type.kind != TYPE_ERROR && val->type.kind != elem_kind) {
|
||||||
|
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||||
|
"数组元素类型不匹配: 期望 '%s',得到 '%s'",
|
||||||
|
type_name(elem_kind), type_name(val->type.kind));
|
||||||
|
}
|
||||||
|
node->type.kind = TYPE_VOID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case AST_IF_STMT:
|
case AST_IF_STMT:
|
||||||
analyze_expr(node->as.if_stmt.cond, scope, errors, a);
|
analyze_expr(node->as.if_stmt.cond, scope, errors, a);
|
||||||
if (node->as.if_stmt.cond->type.kind != TYPE_BOOL &&
|
if (node->as.if_stmt.cond->type.kind != TYPE_BOOL &&
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
|
|||||||
sym->struct_field_types = NULL;
|
sym->struct_field_types = NULL;
|
||||||
sym->struct_field_count = 0;
|
sym->struct_field_count = 0;
|
||||||
sym->struct_type_name = NULL;
|
sym->struct_type_name = NULL;
|
||||||
|
sym->array_element_type = 0;
|
||||||
|
sym->array_element_struct_name = NULL;
|
||||||
|
sym->array_size = 0;
|
||||||
sym->is_type_alias = false;
|
sym->is_type_alias = false;
|
||||||
sym->next = scope->head;
|
sym->next = scope->head;
|
||||||
scope->head = sym;
|
scope->head = sym;
|
||||||
@@ -61,6 +64,9 @@ Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name,
|
|||||||
sym->struct_field_types = NULL;
|
sym->struct_field_types = NULL;
|
||||||
sym->struct_field_count = 0;
|
sym->struct_field_count = 0;
|
||||||
sym->struct_type_name = NULL;
|
sym->struct_type_name = NULL;
|
||||||
|
sym->array_element_type = 0;
|
||||||
|
sym->array_element_struct_name = NULL;
|
||||||
|
sym->array_size = 0;
|
||||||
sym->is_type_alias = false;
|
sym->is_type_alias = false;
|
||||||
sym->next = scope->head;
|
sym->next = scope->head;
|
||||||
scope->head = sym;
|
scope->head = sym;
|
||||||
@@ -85,6 +91,9 @@ Symbol* scope_insert_struct(Scope* scope, void* alloc, const char* name,
|
|||||||
sym->struct_field_struct_names = fstruct_names;
|
sym->struct_field_struct_names = fstruct_names;
|
||||||
sym->struct_field_count = fc;
|
sym->struct_field_count = fc;
|
||||||
sym->struct_type_name = NULL;
|
sym->struct_type_name = NULL;
|
||||||
|
sym->array_element_type = 0;
|
||||||
|
sym->array_element_struct_name = NULL;
|
||||||
|
sym->array_size = 0;
|
||||||
sym->is_type_alias = false;
|
sym->is_type_alias = false;
|
||||||
sym->next = scope->head;
|
sym->next = scope->head;
|
||||||
scope->head = sym;
|
scope->head = sym;
|
||||||
@@ -127,6 +136,9 @@ Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name,
|
|||||||
sym->struct_field_struct_names = NULL;
|
sym->struct_field_struct_names = NULL;
|
||||||
sym->struct_field_count = vc;
|
sym->struct_field_count = vc;
|
||||||
sym->struct_type_name = NULL;
|
sym->struct_type_name = NULL;
|
||||||
|
sym->array_element_type = 0;
|
||||||
|
sym->array_element_struct_name = NULL;
|
||||||
|
sym->array_size = 0;
|
||||||
sym->is_type_alias = false;
|
sym->is_type_alias = false;
|
||||||
sym->next = scope->head;
|
sym->next = scope->head;
|
||||||
scope->head = sym;
|
scope->head = sym;
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ typedef struct Symbol {
|
|||||||
size_t struct_field_count;
|
size_t struct_field_count;
|
||||||
// 变量引用结构体类型时,记录具体类型名
|
// 变量引用结构体类型时,记录具体类型名
|
||||||
const char* struct_type_name;
|
const char* struct_type_name;
|
||||||
|
// 数组变量特有(TYPE_ARRAY)
|
||||||
|
TypeKind array_element_type;
|
||||||
|
const char* array_element_struct_name;
|
||||||
|
int64_t array_size;
|
||||||
// 类型别名标记
|
// 类型别名标记
|
||||||
bool is_type_alias;
|
bool is_type_alias;
|
||||||
// 链表(同一作用域内的下一个符号)
|
// 链表(同一作用域内的下一个符号)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fn main() -> i64 {
|
||||||
|
let arr: [i64; 3] = arr;
|
||||||
|
arr[0] = 10;
|
||||||
|
arr[1] = 20;
|
||||||
|
arr[2] = 30;
|
||||||
|
print_i64(arr[0]);
|
||||||
|
print_i64(arr[1]);
|
||||||
|
print_i64(arr[2]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+68
-3
@@ -145,7 +145,7 @@ void test_codegen_struct_decl() {
|
|||||||
init->type.struct_name = "Point";
|
init->type.struct_name = "Point";
|
||||||
|
|
||||||
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
|
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
|
||||||
init, NULL, loc_at(1, 1));
|
init, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
/* return p.x; */
|
/* return p.x; */
|
||||||
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
|
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
|
||||||
@@ -200,7 +200,7 @@ void test_codegen_struct_field_access() {
|
|||||||
init->type.struct_name = "Point";
|
init->type.struct_name = "Point";
|
||||||
|
|
||||||
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
|
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
|
||||||
init, NULL, loc_at(1, 1));
|
init, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
/* return p.y; */
|
/* return p.y; */
|
||||||
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
|
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
|
||||||
@@ -251,7 +251,7 @@ void test_codegen_enum() {
|
|||||||
cv->type.kind = TYPE_ENUM;
|
cv->type.kind = TYPE_ENUM;
|
||||||
|
|
||||||
AstNode* let_stmt = ast_make_let(&a, "c", TYPE_UNKNOWN, false, false,
|
AstNode* let_stmt = ast_make_let(&a, "c", TYPE_UNKNOWN, false, false,
|
||||||
cv, NULL, loc_at(1, 1));
|
cv, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
/* print_i64(c); */
|
/* print_i64(c); */
|
||||||
AstNode* c_ident = ast_make_ident(&a, "c", loc_at(1, 1));
|
AstNode* c_ident = ast_make_ident(&a, "c", loc_at(1, 1));
|
||||||
@@ -284,6 +284,70 @@ void test_codegen_enum() {
|
|||||||
arena_destroy(&a);
|
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) {
|
int main(void) {
|
||||||
TEST_RUN(test_codegen_simple_function);
|
TEST_RUN(test_codegen_simple_function);
|
||||||
TEST_RUN(test_codegen_if_else);
|
TEST_RUN(test_codegen_if_else);
|
||||||
@@ -292,5 +356,6 @@ int main(void) {
|
|||||||
TEST_RUN(test_codegen_struct_decl);
|
TEST_RUN(test_codegen_struct_decl);
|
||||||
TEST_RUN(test_codegen_struct_field_access);
|
TEST_RUN(test_codegen_struct_field_access);
|
||||||
TEST_RUN(test_codegen_enum);
|
TEST_RUN(test_codegen_enum);
|
||||||
|
TEST_RUN(test_codegen_array);
|
||||||
return test_summary();
|
return test_summary();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,76 @@ void test_enum_bad_variant() {
|
|||||||
arena_destroy(&a);
|
arena_destroy(&a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === 数组语义分析测试 === */
|
||||||
|
|
||||||
|
void test_array_ok() {
|
||||||
|
Arena a = arena_create(1);
|
||||||
|
size_t tc; ErrorInfo lex_err = {0};
|
||||||
|
Token* toks = lex(&a,
|
||||||
|
"fn main() { let arr: [i64; 3] = arr; arr[0]; return; }",
|
||||||
|
"test", &tc, &lex_err);
|
||||||
|
ASSERT(toks != NULL);
|
||||||
|
ErrorInfo parse_err = {0};
|
||||||
|
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||||
|
ASSERT(ast != NULL);
|
||||||
|
|
||||||
|
ErrorList errors; error_init(&errors);
|
||||||
|
sema_analyze(ast, &errors, &a);
|
||||||
|
ASSERT(errors.count == 0);
|
||||||
|
arena_destroy(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_array_index_type_error() {
|
||||||
|
Arena a = arena_create(1);
|
||||||
|
size_t tc; ErrorInfo lex_err = {0};
|
||||||
|
Token* toks = lex(&a,
|
||||||
|
"fn main() { let arr: [i64; 3] = arr; arr[true]; return; }",
|
||||||
|
"test", &tc, &lex_err);
|
||||||
|
ASSERT(toks != NULL);
|
||||||
|
ErrorInfo parse_err = {0};
|
||||||
|
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||||
|
ASSERT(ast != NULL);
|
||||||
|
|
||||||
|
ErrorList errors; error_init(&errors);
|
||||||
|
sema_analyze(ast, &errors, &a);
|
||||||
|
ASSERT(errors.count > 0); // true 不是 i64
|
||||||
|
arena_destroy(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_array_not_indexable() {
|
||||||
|
Arena a = arena_create(1);
|
||||||
|
size_t tc; ErrorInfo lex_err = {0};
|
||||||
|
Token* toks = lex(&a,
|
||||||
|
"fn main() { let x: i64 = 0; x[0]; return; }",
|
||||||
|
"test", &tc, &lex_err);
|
||||||
|
ASSERT(toks != NULL);
|
||||||
|
ErrorInfo parse_err = {0};
|
||||||
|
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||||
|
ASSERT(ast != NULL);
|
||||||
|
|
||||||
|
ErrorList errors; error_init(&errors);
|
||||||
|
sema_analyze(ast, &errors, &a);
|
||||||
|
ASSERT(errors.count > 0); // i64 不是数组
|
||||||
|
arena_destroy(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_array_assign_ok() {
|
||||||
|
Arena a = arena_create(1);
|
||||||
|
size_t tc; ErrorInfo lex_err = {0};
|
||||||
|
Token* toks = lex(&a,
|
||||||
|
"fn main() { let arr: [i64; 3] = arr; arr[0] = 42; return; }",
|
||||||
|
"test", &tc, &lex_err);
|
||||||
|
ASSERT(toks != NULL);
|
||||||
|
ErrorInfo parse_err = {0};
|
||||||
|
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||||
|
ASSERT(ast != NULL);
|
||||||
|
|
||||||
|
ErrorList errors; error_init(&errors);
|
||||||
|
sema_analyze(ast, &errors, &a);
|
||||||
|
ASSERT(errors.count == 0);
|
||||||
|
arena_destroy(&a);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
TEST_RUN(test_type_error);
|
TEST_RUN(test_type_error);
|
||||||
TEST_RUN(test_undefined_var);
|
TEST_RUN(test_undefined_var);
|
||||||
@@ -275,5 +345,9 @@ int main(void) {
|
|||||||
TEST_RUN(test_type_alias_struct);
|
TEST_RUN(test_type_alias_struct);
|
||||||
TEST_RUN(test_enum_ok);
|
TEST_RUN(test_enum_ok);
|
||||||
TEST_RUN(test_enum_bad_variant);
|
TEST_RUN(test_enum_bad_variant);
|
||||||
|
TEST_RUN(test_array_ok);
|
||||||
|
TEST_RUN(test_array_index_type_error);
|
||||||
|
TEST_RUN(test_array_not_indexable);
|
||||||
|
TEST_RUN(test_array_assign_ok);
|
||||||
return test_summary();
|
return test_summary();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user