feat: 类型别名 type alias (P1 #10)

- lexer: TOK_TYPE 关键字
- ast: AST_TYPE_ALIAS + AST_PROGRAM aliases数组
- parser: parse_type_expr() 抽取, type Name = Type; 解析
- sema: 别名注册+解析, 类型标注/struct字段/函数参数均支持
- 新增测试: 15_type_alias.l, 16_type_alias_struct.l

测试: 112 通过 (41+15+41+15)
This commit is contained in:
2026-06-05 13:54:58 +08:00
parent da9a7065dd
commit ab88ea2753
13 changed files with 235 additions and 79 deletions
+13 -1
View File
@@ -9,12 +9,15 @@
n->loc = loc
AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
AstNode** structs, size_t struct_count, SourceLoc loc) {
AstNode** structs, size_t struct_count,
AstNode** aliases, size_t alias_count, SourceLoc loc) {
NEW(alloc, AST_PROGRAM);
n->as.program.functions = fns;
n->as.program.fn_count = fn_count;
n->as.program.structs = structs;
n->as.program.struct_count = struct_count;
n->as.program.type_aliases = aliases;
n->as.program.alias_count = alias_count;
return n;
}
@@ -165,3 +168,12 @@ AstNode* ast_make_field_access(void* alloc, AstNode* object, const char* field,
n->as.field_access.field_index = -1;
return n;
}
AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased,
const char* aliased_struct, SourceLoc loc) {
NEW(alloc, AST_TYPE_ALIAS);
n->as.type_alias.name = name;
n->as.type_alias.aliased_type = aliased;
n->as.type_alias.aliased_struct_name = aliased_struct;
return n;
}
+9 -2
View File
@@ -23,6 +23,7 @@ typedef enum {
AST_STRUCT_DECL, // struct Point { x: i64, y: i64 }
AST_STRUCT_INIT, // Point { x: 10, y: 20 }
AST_FIELD_ACCESS, // p.x
AST_TYPE_ALIAS, // type Meters = i64
} AstKind;
typedef enum {
@@ -48,7 +49,8 @@ struct AstNode {
union {
// AST_PROGRAM
struct { struct AstNode** functions; size_t fn_count;
struct AstNode** structs; size_t struct_count; } program;
struct AstNode** structs; size_t struct_count;
struct AstNode** type_aliases; size_t alias_count; } program;
// AST_FUNCTION
struct { const char* name; struct AstNode** params; size_t param_count;
TypeKind return_type; const char* return_struct_type_name;
@@ -87,12 +89,15 @@ struct AstNode {
struct AstNode** field_values; size_t field_count; } struct_init;
// AST_FIELD_ACCESS
struct { struct AstNode* object; const char* field; int field_index; } field_access;
// AST_TYPE_ALIAS
struct { const char* name; TypeKind aliased_type; const char* aliased_struct_name; } type_alias;
} as;
};
// 创建节点的辅助函数(内存来自 arena,通过 void* 传递避免循环依赖)
AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
AstNode** structs, size_t struct_count, SourceLoc loc);
AstNode** structs, size_t struct_count,
AstNode** aliases, size_t alias_count, SourceLoc loc);
AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size_t pcount,
TypeKind ret, const char* ret_struct_name, AstNode* body, SourceLoc loc);
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, SourceLoc loc);
@@ -115,5 +120,7 @@ AstNode* ast_make_ident(void* alloc, const char* name, SourceLoc loc);
AstNode* ast_make_struct_decl(void* alloc, const char* name, AstNode** fields, size_t count, SourceLoc loc);
AstNode* ast_make_struct_init(void* alloc, const char* type_name, const char** fnames, AstNode** fvals, size_t count, SourceLoc loc);
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);
#endif
+1 -1
View File
@@ -62,7 +62,7 @@ static TokenKind check_keyword(const Token* tok) {
KW("i64", TOK_I64); KW("f64", TOK_F64);
KW("bool", TOK_BOOL); KW("str", TOK_STR);
KW("void", TOK_VOID);
KW("struct", TOK_STRUCT);
KW("struct", TOK_STRUCT); KW("type", TOK_TYPE);
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
#undef KW
return TOK_IDENT;
+1 -1
View File
@@ -7,7 +7,7 @@
static const char* NAMES[] = {
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_MUT] = "mut", [TOK_IF] = "if",
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return",
[TOK_STRUCT] = "struct",
[TOK_STRUCT] = "struct", [TOK_TYPE] = "type",
[TOK_I64] = "i64", [TOK_F64] = "f64", [TOK_BOOL] = "bool", [TOK_STR] = "str", [TOK_VOID] = "void",
[TOK_INT_LIT] = "整数", [TOK_FLOAT_LIT] = "浮点数", [TOK_STR_LIT] = "字符串",
[TOK_TRUE] = "true", [TOK_FALSE] = "false",
+1 -1
View File
@@ -7,7 +7,7 @@
typedef enum {
// 关键字
TOK_FN, TOK_LET, TOK_MUT, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN,
TOK_STRUCT,
TOK_STRUCT, TOK_TYPE,
// 类型关键字
TOK_I64, TOK_F64, TOK_BOOL, TOK_STR, TOK_VOID,
// 字面量
+53 -49
View File
@@ -244,6 +244,25 @@ static TypeKind token_to_type(TokenKind k) {
default: return TYPE_VOID; }
}
// === 类型表达式解析(内置类型/结构体名)===
static TypeInfo parse_type_expr(Parser* p, ErrorInfo* error) {
const Token* t = peek(p);
TypeInfo ti = {0};
if (tok_is_type(t->kind)) {
advance(p);
ti.kind = token_to_type(t->kind);
} else if (t->kind == TOK_IDENT) {
advance(p);
ti.kind = TYPE_STRUCT;
ti.struct_name = arena_strdup_impl(p->arena, t->start, t->length);
} else {
error->message = "无效的类型"; error->filename = p->filename;
error->line = t->line; error->col = t->col;
ti.kind = TYPE_ERROR;
}
return ti;
}
// === 结构体声明解析 ===
static AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
const Token* s_tok = advance(p); // 跳过 'struct'
@@ -257,21 +276,14 @@ static AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) {
const Token* fname = expect(p, TOK_IDENT, error, "字段名");
if (!fname) return NULL;
if (!expect(p, TOK_COLON, error, "缺少 ':'")) return NULL;
const Token* ftype = advance(p);
TypeKind field_kind;
const char* field_struct_name = NULL;
if (tok_is_type(ftype->kind)) {
field_kind = token_to_type(ftype->kind);
} else if (ftype->kind == TOK_IDENT) {
field_kind = TYPE_STRUCT;
field_struct_name = arena_strdup_impl(p->arena, ftype->start, ftype->length);
} else {
error->message = "无效的字段类型"; error->filename = p->filename;
error->line = ftype->line; error->col = ftype->col; return NULL;
TypeInfo fti = parse_type_expr(p, error);
if (fti.kind == TYPE_ERROR) {
error->filename = p->filename;
return NULL;
}
fields[fcount++] = ast_make_parameter(p->arena,
arena_strdup_impl(p->arena, fname->start, fname->length),
field_kind, field_struct_name, tok_loc(fname));
fti.kind, fti.struct_name, tok_loc(fname));
if (peek(p)->kind == TOK_COMMA) advance(p);
else break;
}
@@ -322,18 +334,10 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
bool has_type_annot = false;
const char* struct_type_name = NULL;
if (match(p, TOK_COLON)) {
const Token* type_tok = advance(p);
if (!tok_is_type(type_tok->kind) && type_tok->kind != TOK_IDENT) {
error->message = "无效的类型标注"; error->filename = p->filename;
error->line = type_tok->line; error->col = type_tok->col; return NULL;
}
if (tok_is_type(type_tok->kind)) {
annot_type = token_to_type(type_tok->kind);
} else {
// struct 类型名
annot_type = TYPE_STRUCT;
struct_type_name = arena_strdup_impl(p->arena, type_tok->start, type_tok->length);
}
TypeInfo ti = parse_type_expr(p, error);
if (ti.kind == TYPE_ERROR) return NULL;
annot_type = ti.kind;
struct_type_name = ti.struct_name;
has_type_annot = true;
}
if (!expect(p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
@@ -511,21 +515,11 @@ static AstNode* parse_function(Parser* p, ErrorInfo* error) {
const Token* pname = expect(p, TOK_IDENT, error, "参数名");
if (!pname) return NULL;
if (!expect(p, TOK_COLON, error, "缺少 ':'")) return NULL;
const Token* ptype = advance(p);
TypeKind param_kind;
const char* param_struct_name = NULL;
if (tok_is_type(ptype->kind)) {
param_kind = token_to_type(ptype->kind);
} else if (ptype->kind == TOK_IDENT) {
param_kind = TYPE_STRUCT;
param_struct_name = arena_strdup_impl(p->arena, ptype->start, ptype->length);
} else {
error->message = "无效的参数类型"; error->filename = p->filename;
error->line = ptype->line; error->col = ptype->col; return NULL;
}
TypeInfo pti = parse_type_expr(p, error);
if (pti.kind == TYPE_ERROR) return NULL;
params[pcount++] = ast_make_parameter(p->arena,
arena_strdup_impl(p->arena, pname->start, pname->length),
param_kind, param_struct_name, tok_loc(pname));
pti.kind, pti.struct_name, tok_loc(pname));
if (match(p, TOK_COMMA)) continue;
else break;
}
@@ -535,16 +529,10 @@ static AstNode* parse_function(Parser* p, ErrorInfo* error) {
TypeKind ret = TYPE_VOID;
const char* ret_struct_name = NULL;
if (match(p, TOK_ARROW)) {
const Token* rt = advance(p);
if (tok_is_type(rt->kind)) {
ret = token_to_type(rt->kind);
} else if (rt->kind == TOK_IDENT) {
ret = TYPE_STRUCT;
ret_struct_name = arena_strdup_impl(p->arena, rt->start, rt->length);
} else {
error->message = "无效的返回类型"; error->filename = p->filename;
error->line = rt->line; error->col = rt->col; return NULL;
}
TypeInfo rti = parse_type_expr(p, error);
if (rti.kind == TYPE_ERROR) return NULL;
ret = rti.kind;
ret_struct_name = rti.struct_name;
}
AstNode* body = parse_block(p, error);
@@ -564,15 +552,28 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
.filename = filename, .arena = a};
AstNode* functions[256]; int fn_count = 0;
AstNode* structs[64]; int struct_count = 0;
AstNode* aliases[64]; int alias_count = 0;
while (peek(&p)->kind != TOK_EOF && !error->message) {
if (peek(&p)->kind == TOK_STRUCT) {
if (struct_count >= 64) { error->message = "结构体过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
structs[struct_count++] = parse_struct_decl(&p, error);
} else if (peek(&p)->kind == TOK_TYPE) {
if (alias_count >= 64) { error->message = "类型别名过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
const Token* type_tok = advance(&p); // 跳过 'type'
const Token* alias_name = expect(&p, TOK_IDENT, error, "type 后应为别名");
if (!alias_name) return NULL;
if (!expect(&p, TOK_ASSIGN, error, "缺少 '='")) return NULL;
TypeInfo rti = parse_type_expr(&p, error);
if (rti.kind == TYPE_ERROR) return NULL;
if (!expect(&p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
aliases[alias_count++] = ast_make_type_alias(a,
arena_strdup_impl(a, alias_name->start, alias_name->length),
rti.kind, rti.struct_name, tok_loc(type_tok));
} else if (peek(&p)->kind == TOK_FN) {
if (fn_count >= 256) { error->message = "函数过多 (最多256)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
functions[fn_count++] = parse_function(&p, error);
} else {
error->message = "顶层只允许 fnstruct";
error->message = "顶层只允许 fnstruct 或 type";
error->filename = p.filename;
error->line = peek(&p)->line;
error->col = peek(&p)->col;
@@ -584,5 +585,8 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
memcpy(fn_arr, functions, fn_count * sizeof(AstNode*));
AstNode** st_arr = arena_alloc_impl(a, struct_count * sizeof(AstNode*));
memcpy(st_arr, structs, struct_count * sizeof(AstNode*));
return ast_make_program(a, fn_arr, fn_count, st_arr, struct_count, loc_at(0, 0));
AstNode** al_arr = arena_alloc_impl(a, alias_count * sizeof(AstNode*));
memcpy(al_arr, aliases, alias_count * sizeof(AstNode*));
return ast_make_program(a, fn_arr, fn_count, st_arr, struct_count,
al_arr, alias_count, loc_at(0, 0));
}
+92 -18
View File
@@ -30,6 +30,10 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"未定义的变量 '%s'", node->as.ident.name);
node->type.kind = TYPE_ERROR;
} else if (sym->is_type_alias) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"'%s' 是类型别名,不能作为表达式使用", node->as.ident.name);
node->type.kind = TYPE_ERROR;
} else if (sym->kind == SYM_FUNCTION) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"'%s' 是函数,不能作为表达式使用", node->as.ident.name);
@@ -227,7 +231,18 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
case AST_STRUCT_INIT: {
Symbol* struct_sym = scope_lookup_struct(scope, node->as.struct_init.type_name);
const char* resolved_type_name = node->as.struct_init.type_name;
Symbol* struct_sym = scope_lookup_struct(scope, resolved_type_name);
if (!struct_sym) {
// 检查是否是类型别名指向结构体
Symbol* alias_sym = scope_lookup(scope, resolved_type_name);
if (alias_sym && alias_sym->is_type_alias && alias_sym->struct_type_name) {
resolved_type_name = alias_sym->struct_type_name;
struct_sym = scope_lookup_struct(scope, resolved_type_name);
// 更新 type_name 为真实结构体名(codegen 需要)
node->as.struct_init.type_name = resolved_type_name;
}
}
if (!struct_sym) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"未定义的结构体类型 '%s'", node->as.struct_init.type_name);
@@ -267,7 +282,7 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
if (node->type.kind != TYPE_ERROR) {
node->type.kind = TYPE_STRUCT;
node->type.struct_name = node->as.struct_init.type_name;
node->type.struct_name = resolved_type_name;
}
break;
}
@@ -281,6 +296,18 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
switch (node->kind) {
case AST_PROGRAM:
// Pass 0: 注册类型别名
for (size_t i = 0; i < node->as.program.alias_count; i++) {
AstNode* alias = node->as.program.type_aliases[i];
Symbol* sym = scope_insert(scope, a, alias->as.type_alias.name,
SYM_VARIABLE, alias->as.type_alias.aliased_type);
if (sym) {
sym->is_type_alias = true;
if (alias->as.type_alias.aliased_struct_name) {
sym->struct_type_name = alias->as.type_alias.aliased_struct_name;
}
}
}
// 第一遍:收集所有结构体定义
for (size_t i = 0; i < node->as.program.struct_count; i++) {
AstNode* sd = node->as.program.structs[i];
@@ -305,12 +332,31 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
TypeKind* pts = (TypeKind*)arena_alloc_impl(a, fn->as.function.param_count * sizeof(TypeKind));
const char** pstruct_names = (const char**)arena_alloc_impl(a, fn->as.function.param_count * sizeof(const char*));
for (size_t j = 0; j < fn->as.function.param_count; j++) {
pts[j] = fn->as.function.params[j]->as.parameter.type;
pstruct_names[j] = fn->as.function.params[j]->as.parameter.struct_type_name;
TypeKind pt = fn->as.function.params[j]->as.parameter.type;
const char* psn = fn->as.function.params[j]->as.parameter.struct_type_name;
// 解析参数类型的别名
if (psn) {
Symbol* as = scope_lookup(scope, psn);
if (as && as->is_type_alias) {
pt = as->type;
psn = as->struct_type_name;
}
}
pts[j] = pt;
pstruct_names[j] = psn;
}
// 解析返回类型的别名
TypeKind ret_t = fn->as.function.return_type;
const char* ret_sn = fn->as.function.return_struct_type_name;
if (ret_sn) {
Symbol* as = scope_lookup(scope, ret_sn);
if (as && as->is_type_alias) {
ret_t = as->type;
ret_sn = as->struct_type_name;
}
}
scope_insert_function(scope, a, fn->as.function.name,
fn->as.function.return_type,
fn->as.function.return_struct_type_name,
ret_t, ret_sn,
pts, pstruct_names,
fn->as.function.param_count);
}
@@ -322,12 +368,33 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
case AST_FUNCTION: {
Scope* fn_scope = scope_new(a, scope);
// 注册参数
// 注册参数(同时解析类型别名,更新 AST 节点供 codegen 使用)
for (size_t i = 0; i < node->as.function.param_count; i++) {
AstNode* p = node->as.function.params[i];
Symbol* sym = scope_insert(fn_scope, a, p->as.parameter.name, SYM_PARAMETER, p->as.parameter.type);
if (sym && p->as.parameter.type == TYPE_STRUCT && p->as.parameter.struct_type_name) {
sym->struct_type_name = p->as.parameter.struct_type_name;
TypeKind pt = p->as.parameter.type;
const char* psn = p->as.parameter.struct_type_name;
if (psn) {
Symbol* as = scope_lookup(scope, psn);
if (as && as->is_type_alias) {
pt = as->type;
psn = as->struct_type_name;
// 更新 AST 节点
p->as.parameter.type = pt;
p->as.parameter.struct_type_name = psn;
}
}
Symbol* sym = scope_insert(fn_scope, a, p->as.parameter.name, SYM_PARAMETER, pt);
if (sym && pt == TYPE_STRUCT && psn) {
sym->struct_type_name = psn;
}
}
// 解析返回类型的别名
const char* ret_sn = node->as.function.return_struct_type_name;
if (ret_sn) {
Symbol* as = scope_lookup(scope, ret_sn);
if (as && as->is_type_alias) {
node->as.function.return_type = as->type;
node->as.function.return_struct_type_name = as->struct_type_name;
}
}
TypeKind saved = current_return_type;
@@ -355,15 +422,22 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
if (node->as.let_stmt.has_type_annot) {
const char* annot_struct = node->as.let_stmt.struct_type_name;
if (annot_struct) {
// struct 类型标注
Symbol* st_sym = scope_lookup_struct(scope, annot_struct);
if (!st_sym) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"未定义的结构体类型 '%s'", annot_struct);
break;
// 先检查是否是类型别名
Symbol* alias_sym = scope_lookup(scope, annot_struct);
if (alias_sym && alias_sym->is_type_alias) {
var_type = alias_sym->type;
var_struct_name = alias_sym->struct_type_name;
} else {
// struct 类型标注
Symbol* st_sym = scope_lookup_struct(scope, annot_struct);
if (!st_sym) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"未定义的类型 '%s'", annot_struct);
break;
}
var_type = TYPE_STRUCT;
var_struct_name = annot_struct;
}
var_type = TYPE_STRUCT;
var_struct_name = annot_struct;
} else {
var_type = node->as.let_stmt.annot_type;
}
+3
View File
@@ -35,6 +35,7 @@ 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->is_type_alias = false;
sym->next = scope->head;
scope->head = sym;
return sym;
@@ -60,6 +61,7 @@ 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->is_type_alias = false;
sym->next = scope->head;
scope->head = sym;
return sym;
@@ -83,6 +85,7 @@ 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->is_type_alias = false;
sym->next = scope->head;
scope->head = sym;
return sym;
+2
View File
@@ -24,6 +24,8 @@ typedef struct Symbol {
size_t struct_field_count;
// 变量引用结构体类型时,记录具体类型名
const char* struct_type_name;
// 类型别名标记
bool is_type_alias;
// 链表(同一作用域内的下一个符号)
struct Symbol* next;
} Symbol;