Files
l-language/docs/architecture-improvements.md
Serendipity 4046ab1875 refactor: tok_is_type 统一 + 架构改进文档
- parser.c: 删除重复的is_type_token, 统一使用token.c的tok_is_type
- docs/architecture-improvements.md: TypeKind解耦/Visitor/SourceLoc/去糖方案
2026-06-05 13:13:51 +08:00

167 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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_STRtoken 少了 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。