4046ab1875
- parser.c: 删除重复的is_type_token, 统一使用token.c的tok_is_type - docs/architecture-improvements.md: TypeKind解耦/Visitor/SourceLoc/去糖方案
4.8 KiB
4.8 KiB
L Language 架构改进路线图
2026-06-05 | 基于架构审查报告
1. TypeKind 耦合问题 (HIGH)
当前状态: 新增一个类型需要修改 7+ 个文件。
TypeKind 修改 → l_lang.h (枚举 + type_name switch)
→ token.h (TokenKind 枚举)
→ token.c (tok_is_type)
→ lexer.c (KW 宏)
→ parser.c (is_type_token + token_to_type)
→ sema.c (promote, is_numeric, 各运算符类型检查)
→ codegen.c (to_llvm_type, to_llvm_const)
建议方案: 引入类型注册表。
// types.h
typedef struct {
const char* name; // "i64"
TypeKind kind; // TYPE_I64
TokenKind tok; // TOK_I64
LLVMTypeRef (*to_llvm)(LLVMContextRef); // 代码生成回调
bool is_numeric; // 可用于算术
bool is_comparable; // 可用于比较
bool promote_to; // 可隐式转换的目标类型
} TypeDef;
// 集中定义所有类型
static const TypeDef TYPES[] = {
{ "i64", TYPE_I64, TOK_I64, /*...*/, .is_numeric=true, .is_comparable=true },
{ "f64", TYPE_F64, TOK_F64, /*...*/, .is_numeric=true, .is_comparable=true },
{ "bool", TYPE_BOOL, TOK_BOOL, /*...*/, .is_comparable=true },
{ "str", TYPE_STR, TOK_STR, /*...*/ },
{ "void", TYPE_VOID, TOK_VOID, /*...*/ },
};
加新类型只需往 TYPES[] 数组加一行。预计改动量: 新增 types.h/c,各模块改为查表。工时: 2 天。
2. AST Visitor 模式缺失 (HIGH)
当前状态: 每个模块手写 switch-case 遍历 AST。新 AST 节点需要修改所有 switch。
新节点 AstNewNode → sema.c:analyze_expr() switch
→ sema.c:analyze_node() switch
→ codegen.c:codegen_expr() switch
→ codegen.c:codegen_stmt() switch
至少 4 处
建议方案: 宏驱动的 Visitor。
// ast_visitor.h
#define AST_VISIT_CASES(V) \
V(AST_PROGRAM) V(AST_FUNCTION) V(AST_PARAMETER) \
V(AST_BLOCK) V(AST_LET_STMT) V(AST_ASSIGN_STMT) \
...
// sema.c
switch (node->kind) {
#define V(k) case k: analyze_##k(node, scope, errors); break;
AST_VISIT_CASES(V)
#undef V
}
更完整的方案: 传函数指针的 ast_walk(AstNode*, Visitor* v) 接口。工时: 3 天。
3. SourceLoc 抽象缺失 (HIGH)
当前状态: 每个 AST 工厂函数有独立的 int line, int col 参数。
// 当前: 参数膨胀
AstNode* ast_make_let(void* alloc, const char* name,
TypeKind annot_type, bool has_type_annot,
bool is_mut, AstNode* init, int line, int col); // 8 个参数
// 建议: SourceLoc 聚合
typedef struct { int line; int col; const char* file; } SourceLoc;
AstNode* ast_make_let(void* alloc, const char* name,
TypeKind annot_type, bool has_type_annot,
bool is_mut, AstNode* init, SourceLoc loc); // 7 个参数
额外收益: 错误报告可以统一使用 SourceLoc,不再分别传 file/line/col。工时: 1 天。
4. 去糖逻辑应独立 (MEDIUM)
当前状态: for 循环和复合赋值的去糖逻辑直接在 parser.c 中。
parser.c:parse_statement()
├── TOK_FOR → 直接构造 let mut + while + assign AST (~60行)
└── TOK_IDENT += → 直接构造 assign(binary) AST (~25行)
建议方案: 独立 parser_desugar.c pass。
// parser_desugar.h
AstNode* desugar(AstNode* raw_ast, Arena* a, ErrorInfo* error);
// 职责: 将 FOR_STMT、COMPOUND_ASSIGN 等语法糖节点
// 转换为已有原语节点 (let mut + while + assign 等)
// 在 sema 之前运行
架构收益:
- parser.c 保持纯解析逻辑,缩短 ~85 行
- 去糖逻辑可独立测试
- 新增语法糖只需改 desugar.c,不动 parser
工时: 1 天。
5. tok_is_type 重复定义 (MEDIUM)
当前状态:
// token.c:34
bool tok_is_type(TokenKind kind) {
return kind == TOK_I64 || kind == TOK_F64 || kind == TOK_BOOL || kind == TOK_VOID;
}
// parser.c:236 — 完全相同的逻辑
static bool is_type_token(TokenKind k) {
return k == TOK_I64 || k == TOK_F64 || k == TOK_BOOL || k == TOK_VOID || k == TOK_STR;
}
两处不同步 (parser 多了 TOK_STR,token 少了 TOK_STR)。若加 TOK_U8 很可能只改一处。
修复: 删除 is_type_token(),统一使用 tok_is_type(),并在 token.c 中补上 TOK_STR。工时: 10 分钟。
6. 实施优先级
| 优先级 | 项目 | 工时 | 收益 |
|---|---|---|---|
| 1 | tok_is_type 统一 | 10 分钟 | 消除 bug 源 |
| 2 | SourceLoc 抽象 | 1 天 | 减少参数,可加文件名 |
| 3 | 去糖独立 pass | 1 天 | parser 解耦 |
| 4 | TypeDef 注册表 | 2 天 | 新类型本地化 |
| 5 | AST Visitor | 3 天 | 新节点安全 |
建议 v0.5 按 1→2→3 顺序,v0.6 做 4→5。