Files
l-language/docs/architecture-improvements.md
T
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

4.8 KiB
Raw Blame History

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_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。