4046ab1875
- parser.c: 删除重复的is_type_token, 统一使用token.c的tok_is_type - docs/architecture-improvements.md: TypeKind解耦/Visitor/SourceLoc/去糖方案
167 lines
4.8 KiB
Markdown
167 lines
4.8 KiB
Markdown
# 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)
|
||
```
|
||
|
||
**建议方案**: 引入类型注册表。
|
||
|
||
```c
|
||
// 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。
|
||
|
||
```c
|
||
// 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` 参数。
|
||
|
||
```c
|
||
// 当前: 参数膨胀
|
||
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。
|
||
|
||
```c
|
||
// 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)
|
||
|
||
**当前状态**:
|
||
|
||
```c
|
||
// 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。
|