feat: 枚举关联数据 ADT — enum Option { Some(i64), None }

This commit is contained in:
2026-06-06 14:21:43 +08:00
parent 0ec3c3d65f
commit 0e1f8c5795
9 changed files with 172 additions and 37 deletions
+5 -1
View File
@@ -200,19 +200,23 @@ AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased,
// === 枚举相关工厂函数 ===
AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants,
TypeKind* payload_types, const char** payload_struct_names,
size_t count, SourceLoc loc) {
NEW(alloc, AST_ENUM_DECL);
n->as.enum_decl.name = name;
n->as.enum_decl.variants = variants;
n->as.enum_decl.variant_payload_types = payload_types;
n->as.enum_decl.variant_payload_struct_names = payload_struct_names;
n->as.enum_decl.variant_count = count;
return n;
}
AstNode* ast_make_enum_variant(void* alloc, const char* enum_name,
const char* variant_name, SourceLoc loc) {
const char* variant_name, AstNode* payload, SourceLoc loc) {
NEW(alloc, AST_ENUM_VARIANT);
n->as.enum_variant.enum_name = enum_name;
n->as.enum_variant.variant_name = variant_name;
n->as.enum_variant.payload = payload;
n->as.enum_variant.variant_index = -1;
return n;
}
+12 -4
View File
@@ -104,9 +104,14 @@ struct AstNode {
// AST_TYPE_ALIAS
struct { const char* name; TypeKind aliased_type; const char* aliased_struct_name; } type_alias;
// AST_ENUM_DECL
struct { const char* name; const char** variants; size_t variant_count; } enum_decl;
struct { const char* name; const char** variants;
TypeKind* variant_payload_types; // 每个变体的 payload 类型(TYPE_VOID=无)
const char** variant_payload_struct_names; // payload 为 struct 时的类型名
size_t variant_count; } enum_decl;
// 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;
AstNode* payload; // payload 表达式 (NULL=无 payload)
int variant_index; } enum_variant;
// AST_INDEX_EXPR
struct { struct AstNode* array; struct AstNode* index; } index_expr;
// AST_ARRAY_ASSIGN_STMT
@@ -150,8 +155,11 @@ AstNode* ast_make_struct_init(void* alloc, const char* type_name, const char** f
AstNode* ast_make_field_access(void* alloc, AstNode* object, const char* field, SourceLoc loc);
AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased,
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_variant(void* alloc, const char* enum_name, const char* variant_name, SourceLoc loc);
AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants,
TypeKind* payload_types, const char** payload_struct_names,
size_t count, SourceLoc loc);
AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* variant_name,
AstNode* payload, 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);
AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** methods, size_t count, SourceLoc loc);
+58 -18
View File
@@ -67,7 +67,12 @@ static LLVMTypeRef to_llvm_type(CgCtx* ctx, TypeKind kind) {
case TYPE_CHAR: return LLVMInt8TypeInContext(ctx->context);
case TYPE_STR: return LLVMPointerType(LLVMInt8TypeInContext(ctx->context), 0);
case TYPE_STRUCT:
case TYPE_ENUM: return LLVMInt64TypeInContext(ctx->context);
case TYPE_ENUM: {
// tagged union: { i64 tag, i64 payload }
LLVMTypeRef fields[] = { LLVMInt64TypeInContext(ctx->context),
LLVMInt64TypeInContext(ctx->context) };
return LLVMStructTypeInContext(ctx->context, fields, 2, false);
}
case TYPE_UNKNOWN:
case TYPE_ERROR:
default: return LLVMVoidTypeInContext(ctx->context);
@@ -159,8 +164,11 @@ static LLVMTypeRef type_info_to_llvm(CgCtx* ctx, const TypeInfo* ti) {
if (st) return st;
}
return LLVMVoidTypeInContext(ctx->context);
case TYPE_ENUM:
return LLVMInt64TypeInContext(ctx->context);
case TYPE_ENUM: {
LLVMTypeRef f[] = { LLVMInt64TypeInContext(ctx->context),
LLVMInt64TypeInContext(ctx->context) };
return LLVMStructTypeInContext(ctx->context, f, 2, false);
}
default:
return to_llvm_type(ctx, ti->kind);
}
@@ -258,23 +266,36 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
case OP_MOD:
return LLVMBuildSRem(ctx->builder, l, r, "srem");
case OP_EQ:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOEQ, l, r, "feq")
: LLVMBuildICmp(ctx->builder, LLVMIntEQ, l, r, "ieq");
case OP_NE:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealONE, l, r, "fne")
: LLVMBuildICmp(ctx->builder, LLVMIntNE, l, r, "ine");
case OP_LT:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOLT, l, r, "flt")
: LLVMBuildICmp(ctx->builder, LLVMIntSLT, l, r, "ilt");
case OP_GT:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOGT, l, r, "fgt")
: LLVMBuildICmp(ctx->builder, LLVMIntSGT, l, r, "igt");
case OP_LE:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOLE, l, r, "fle")
: LLVMBuildICmp(ctx->builder, LLVMIntSLE, l, r, "ile");
case OP_GE:
return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOGE, l, r, "fge")
: LLVMBuildICmp(ctx->builder, LLVMIntSGE, l, r, "ige");
case OP_GE: {
// 枚举比较: 提取 tag 字段再比较
LLVMValueRef cl = l, cr = r;
if (node->as.binary.left->type.kind == TYPE_ENUM) {
cl = LLVMBuildExtractValue(ctx->builder, l, 0, "enum_tag_l");
}
if (node->as.binary.right->type.kind == TYPE_ENUM) {
cr = LLVMBuildExtractValue(ctx->builder, r, 0, "enum_tag_r");
}
LLVMIntPredicate pred;
switch (node->as.binary.op) {
case OP_EQ: pred = LLVMIntEQ; break;
case OP_NE: pred = LLVMIntNE; break;
case OP_LT: pred = LLVMIntSLT; break;
case OP_GT: pred = LLVMIntSGT; break;
case OP_LE: pred = LLVMIntSLE; break;
case OP_GE: pred = LLVMIntSGE; break;
default: return NULL;
}
if (is_float)
return LLVMBuildFCmp(ctx->builder, pred == LLVMIntEQ ? LLVMRealOEQ :
pred == LLVMIntNE ? LLVMRealONE : pred == LLVMIntSLT ? LLVMRealOLT :
pred == LLVMIntSGT ? LLVMRealOGT : pred == LLVMIntSLE ? LLVMRealOLE :
LLVMRealOGE, cl, cr, "fcmp");
return LLVMBuildICmp(ctx->builder, pred, cl, cr, "icmp");
}
case OP_AND:
return LLVMBuildAnd(ctx->builder, l, r, "and");
case OP_OR:
@@ -289,6 +310,9 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
if (strcmp(node->as.call.name, "print_i64") == 0) {
LLVMValueRef arg = codegen_expr(ctx, node->as.call.args[0]);
if (!arg) return NULL;
// 枚举类型: 提取 tag 字段
if (node->as.call.args[0]->type.kind == TYPE_ENUM)
arg = LLVMBuildExtractValue(ctx->builder, arg, 0, "tag");
LLVMTypeRef i64_ty = LLVMInt64TypeInContext(ctx->context);
arg = coerce_int(ctx, arg, LLVMTypeOf(arg), i64_ty);
LLVMValueRef fmt = LLVMBuildGlobalStringPtr(ctx->builder, "%lld\n", "fmt_i64");
@@ -384,9 +408,25 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
return LLVMBuildLoad2(ctx->builder, struct_ty, alloca, "struct_val");
}
case AST_ENUM_VARIANT:
return LLVMConstInt(LLVMInt64TypeInContext(ctx->context),
case AST_ENUM_VARIANT: {
// tagged union: { tag, payload }
LLVMValueRef tag = LLVMConstInt(LLVMInt64TypeInContext(ctx->context),
(unsigned long long)node->as.enum_variant.variant_index, true);
LLVMValueRef payload = LLVMConstInt(LLVMInt64TypeInContext(ctx->context), 0, true);
if (node->as.enum_variant.payload) {
LLVMValueRef pv = codegen_expr(ctx, node->as.enum_variant.payload);
if (pv) {
// 将 payload 强制转换为 i64
LLVMTypeRef pv_ty = LLVMTypeOf(pv);
LLVMTypeRef i64_ty = LLVMInt64TypeInContext(ctx->context);
if (pv_ty != i64_ty && LLVMGetTypeKind(pv_ty) == LLVMIntegerTypeKind)
pv = coerce_int(ctx, pv, pv_ty, i64_ty);
payload = pv;
}
}
LLVMValueRef fields[] = { tag, payload };
return LLVMConstStruct(fields, 2, false);
}
case AST_METHOD_CALL: {
const char* struct_name = node->as.method_call.receiver->type.struct_name;
+32 -5
View File
@@ -187,15 +187,21 @@ static AstNode* parse_struct_init(Parser* p, const Token* name, ErrorInfo* error
static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
const Token* name = advance(p);
// 枚举变体引用: Name::Variant
// 枚举变体引用: Name::Variant 或 Name::Variant(payload)
if (peek(p)->kind == TOK_COLON_COLON) {
advance(p); // 跳过 ::
const Token* variant = expect(p, TOK_IDENT, error, "枚举变体名");
if (!variant) return NULL;
AstNode* payload = NULL;
if (match(p, TOK_LPAREN)) {
payload = parse_expr(p, error);
if (!payload) return NULL;
if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
}
return ast_make_enum_variant(p->arena,
arena_strdup_impl(p->arena, name->start, name->length),
arena_strdup_impl(p->arena, variant->start, variant->length),
tok_loc(name));
payload, tok_loc(name));
}
// 结构体初始化: Name { field: val, ... }
@@ -898,18 +904,39 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
const Token* name = expect(&p, TOK_IDENT, error, "enum 后应为枚举名");
if (!name) return NULL;
if (!expect(&p, TOK_LBRACE, error, "缺少 '{'")) return NULL;
const char* variants[64]; int vcount = 0;
const char* variants[64];
TypeKind payload_types[64];
const char* payload_snames[64];
int vcount = 0;
while (peek(&p)->kind != TOK_RBRACE && !error->message) {
if (vcount >= 64) { error->message = "枚举变体过多(最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
const Token* vname = expect(&p, TOK_IDENT, error, "变体名");
if (!vname) return NULL;
variants[vcount++] = arena_strdup_impl(p.arena, vname->start, vname->length);
variants[vcount] = arena_strdup_impl(p.arena, vname->start, vname->length);
payload_types[vcount] = TYPE_VOID;
payload_snames[vcount] = NULL;
// 可选 payload: Variant(Type)
if (peek(&p)->kind == TOK_LPAREN) {
advance(&p);
TypeInfo pti = parse_type_expr(&p, error);
if (pti.kind == TYPE_ERROR) return NULL;
payload_types[vcount] = pti.kind;
payload_snames[vcount] = pti.struct_name;
if (!expect(&p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
}
vcount++;
if (peek(&p)->kind == TOK_COMMA) advance(&p); else break;
}
if (!expect(&p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
const char** v_arr = arena_alloc_impl(p.arena, vcount * sizeof(const char*));
memcpy(v_arr, variants, vcount * sizeof(const char*));
AstNode* enum_decl = ast_make_enum_decl(p.arena, arena_strdup_impl(p.arena, name->start, name->length), v_arr, vcount, tok_loc(name));
TypeKind* pt_arr = arena_alloc_impl(p.arena, vcount * sizeof(TypeKind));
memcpy(pt_arr, payload_types, vcount * sizeof(TypeKind));
const char** ps_arr = arena_alloc_impl(p.arena, vcount * sizeof(const char*));
memcpy(ps_arr, payload_snames, vcount * sizeof(const char*));
AstNode* enum_decl = ast_make_enum_decl(p.arena,
arena_strdup_impl(p.arena, name->start, name->length),
v_arr, pt_arr, ps_arr, vcount, tok_loc(name));
if (enum_count >= 64) { error->message = "枚举过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
enums[enum_count++] = enum_decl;
} else if (peek(&p)->kind == TOK_EXTEND) {
+26
View File
@@ -346,6 +346,30 @@ static void analyze_enum_variant(AstNode* node, Scope* scope, ErrorList* errors,
node->type.kind = TYPE_ERROR; return;
}
node->as.enum_variant.variant_index = vi;
// ADT: 检查 payload
TypeKind expected_pt = TYPE_VOID;
if (enum_sym->variant_payload_types)
expected_pt = enum_sym->variant_payload_types[vi];
if (node->as.enum_variant.payload) {
if (expected_pt == TYPE_VOID && enum_sym->variant_payload_types) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"枚举变体 '%s::%s' 不接受 payload",
node->as.enum_variant.enum_name, node->as.enum_variant.variant_name);
node->type.kind = TYPE_ERROR; return;
}
analyze_expr(node->as.enum_variant.payload, scope, errors, a);
TypeKind actual = node->as.enum_variant.payload->type.kind;
if (actual != TYPE_ERROR && actual != expected_pt) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"枚举变体 payload 类型不匹配: 期望 '%s',得到 '%s'",
type_name(expected_pt), type_name(actual));
}
} else if (expected_pt != TYPE_VOID) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"枚举变体 '%s::%s' 需要 payload 类型 '%s'",
node->as.enum_variant.enum_name, node->as.enum_variant.variant_name,
type_name(expected_pt));
}
node->type.kind = TYPE_ENUM;
}
@@ -451,6 +475,8 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
AstNode* ed = node->as.program.enums[i];
scope_insert_enum(scope, a, ed->as.enum_decl.name,
ed->as.enum_decl.variants,
ed->as.enum_decl.variant_payload_types,
ed->as.enum_decl.variant_payload_struct_names,
ed->as.enum_decl.variant_count);
}
// 第一遍:收集所有结构体定义
+10 -1
View File
@@ -35,6 +35,8 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
sym->struct_field_types = NULL;
sym->struct_field_count = 0;
sym->struct_type_name = NULL;
sym->variant_payload_types = NULL;
sym->variant_payload_struct_names = NULL;
sym->array_element_type = 0;
sym->array_element_struct_name = NULL;
sym->array_size = 0;
@@ -66,6 +68,8 @@ Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name,
sym->struct_field_types = NULL;
sym->struct_field_count = 0;
sym->struct_type_name = NULL;
sym->variant_payload_types = NULL;
sym->variant_payload_struct_names = NULL;
sym->array_element_type = 0;
sym->array_element_struct_name = NULL;
sym->array_size = 0;
@@ -93,6 +97,8 @@ Symbol* scope_insert_struct(Scope* scope, void* alloc, const char* name,
sym->struct_field_struct_names = fstruct_names;
sym->struct_field_count = fc;
sym->struct_type_name = NULL;
sym->variant_payload_types = NULL;
sym->variant_payload_struct_names = NULL;
sym->array_element_type = 0;
sym->array_element_struct_name = NULL;
sym->array_size = 0;
@@ -122,7 +128,8 @@ int scope_struct_field_index(const Symbol* sym, const char* field_name) {
}
Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name,
const char** vnames, size_t vc) {
const char** vnames, TypeKind* ptypes,
const char** pstruct_names, size_t vc) {
if (scope->head) {
for (Symbol* sym = scope->head; sym; sym = sym->next) {
if (strcmp(sym->name, name) == 0) return NULL;
@@ -138,6 +145,8 @@ Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name,
sym->struct_field_struct_names = NULL;
sym->struct_field_count = vc;
sym->struct_type_name = NULL;
sym->variant_payload_types = ptypes;
sym->variant_payload_struct_names = pstruct_names;
sym->array_element_type = 0;
sym->array_element_struct_name = NULL;
sym->array_size = 0;
+5 -1
View File
@@ -29,6 +29,9 @@ typedef struct Symbol {
TypeKind array_element_type;
const char* array_element_struct_name;
int64_t array_size;
// 枚举特有(ADT payload
TypeKind* variant_payload_types;
const char** variant_payload_struct_names;
// 类型别名标记
bool is_type_alias;
// 链表(同一作用域内的下一个符号)
@@ -69,7 +72,8 @@ int scope_struct_field_index(const Symbol* sym, const char* field_name);
// 插入枚举符号
Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name,
const char** vnames, size_t vc);
const char** vnames, TypeKind* ptypes,
const char** pstruct_names, size_t vc);
// 在枚举符号中查找变体索引(返回 -1 表示未找到)
int scope_enum_variant_index(const Symbol* sym, const char* variant_name);
+17
View File
@@ -0,0 +1,17 @@
enum Option { Some(i64), None }
fn main() -> i64 {
let s = Option::Some(42);
let n = Option::None;
// 打印 tag 值 (Some=0, None=1)
print_i64(s);
print_i64(n);
// match 判断是 Some 还是 None(只比较 tag
match s {
Option::None => { print_i64(999); }
_ => { print_i64(100); }
}
return 0;
}
+6 -6
View File
@@ -243,11 +243,11 @@ void test_codegen_enum() {
/* 构造 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* enum_decl = ast_make_enum_decl(&a, "Color", cvariants, NULL, NULL, 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));
AstNode* cv = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
cv->as.enum_variant.variant_index = 1; /* Green = index 1 */
cv->type.kind = TYPE_ENUM;
@@ -426,7 +426,7 @@ void test_codegen_match() {
/* enum Color { Red, Green, Blue } */
const char* variants[] = {"Red", "Green", "Blue"};
AstNode* enum_decl = ast_make_enum_decl(&a, "Color", variants, 3, loc_at(1, 1));
AstNode* enum_decl = ast_make_enum_decl(&a, "Color", variants, NULL, NULL, 3, loc_at(1, 1));
AstNode* enums[] = { enum_decl };
/* fn main() -> i64 {
@@ -445,7 +445,7 @@ void test_codegen_match() {
else { return 0; }
}
*/
AstNode* match_val = ast_make_enum_variant(&a, "Color", "Green", loc_at(1, 1));
AstNode* match_val = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
match_val->type.kind = TYPE_ENUM;
AstNode* let_stmt = ast_make_let(&a, "__match_val", TYPE_UNKNOWN, false, false,
@@ -456,7 +456,7 @@ void test_codegen_match() {
match_ident->type.kind = TYPE_ENUM;
// if __match_val == Color::Red { return 1; }
AstNode* red_variant = ast_make_enum_variant(&a, "Color", "Red", loc_at(1, 1));
AstNode* red_variant = ast_make_enum_variant(&a, "Color", "Red", NULL, loc_at(1, 1));
red_variant->type.kind = TYPE_ENUM;
AstNode* red_cond = ast_make_binary(&a, OP_EQ, match_ident, red_variant, loc_at(1, 1));
red_cond->type.kind = TYPE_BOOL;
@@ -467,7 +467,7 @@ void test_codegen_match() {
// if __match_val == Color::Green { return 2; }
AstNode* match_ident2 = ast_make_ident(&a, "__match_val", loc_at(1, 1));
match_ident2->type.kind = TYPE_ENUM;
AstNode* green_variant = ast_make_enum_variant(&a, "Color", "Green", loc_at(1, 1));
AstNode* green_variant = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1));
green_variant->type.kind = TYPE_ENUM;
AstNode* green_cond = ast_make_binary(&a, OP_EQ, match_ident2, green_variant, loc_at(1, 1));
green_cond->type.kind = TYPE_BOOL;