- CHANGELOG: 新增 v0.4.0 (struct+RAII) 和 v0.5.0 (alias+enum+array+impl+match) - README: 版本0.5.0, 测试145, 完整功能列表+运算符表 - CLAUDE: v0.5 已知限制更新
17 KiB
L Language 架构分析报告 — P1 全线收官 + match 表达式 (v0.5)
日期: 2026-06-05 15:09 | 自动生成 | 有源代码变更 上次代码分析基线:
1d4fb27(2026-06-05 13:08 报告, v0.4) 当前 HEAD:a15cd9dfeat: match 表达式 (P1 #8 收官)新增提交 (自上次源代码基线
1d4fb27):
af0725cfix: 全面代码审查 — 修复 3 CRITICAL + 4 HIGH 问题a7fca59fix: 5项立即修复 + 2项尽快修复4046ab1refactor: tok_is_type 统一 + 架构改进文档da9a706feat: struct参数/返回值 + SourceLoc + 测试补全 (P1 #5 完成)ab88ea2feat: 类型别名 type alias (P1 #10 完成)5237398feat: 枚举 enum (P1 #7 完成)2923e75feat: 数组+索引 [T;N], arr[i] (P1 #6 完成)9f6e695feat: struct方法 impl (P1 #9 完成)a15cd9dfeat: match 表达式 (P1 #8 收官)
本次是自 v0.4 以来最大规模的变更: 9 个提交, P1 全部 6 项需求一次性落地。
变更摘要 (自上次代码基线)
27 个文件变更, +2596 / -284 行。 P1 路线图全覆盖:
| 变更类别 | 详情 | 影响范围 |
|---|---|---|
| P1 #5: struct 参数/返回值 | parser 扩展 param/return type 解析; SourceLoc 抽象落地 | parser/sema/codegen/driver |
| P1 #10: 类型别名 | type Meters = i64; / type P = Point; 全流水线 |
parser/sema(alias 展开链) |
| P1 #7: 枚举 enum | enum Color { Red, Green, Blue }, Color::Green 变体 |
lexer/parser/sema/codegen |
| P1 #6: 数组+索引 | let arr: [i64; 3], arr[i], arr[i] = expr |
lexer/parser/sema/codegen(GEP) |
| P1 #9: struct 方法 impl | impl Point { fn get_x(self: Point) ... }, p.method() 调用 |
parser/sema(改名 mangle)/codegen |
| P1 #8: match 表达式 | match x { pat => { body } _ => { body } }, parser 脱糖 |
parser(desugar 为 let+if-else) |
| 基础设施重构 | SourceLoc 抽象, tok_is_type 统一, 架构改进文档, CRITICAL bug 修复 | 全项目 |
| 测试扩展 | sema: 9→21 tests; codegen: 4→9 tests; 集成: 13→23 programs | test/ |
1. 当前架构全景
1.1 编译流水线
源码(.l) → Lexer(词法) → Parser(语法) → Sema(语义) → Codegen(LLVM IR) → Target(obj) → GCC 链接(.exe)
50 Token 25 AST 节点 类型标注 LLVMModuleRef .o 文件
流水线结构无变化,但数据流丰富度大幅提升: AST 节点从 18 增至 25, Token 从 39 增至 50, TypeKind 从 8 增至 10。
1.2 模块清单与指标
| 模块 | 关键职责 | 本次变更 |
|---|---|---|
| include/ | TypeKind 枚举 (10 种: +TYPE_ENUM, +TYPE_ARRAY), SourceLoc 抽象 | +15 行 |
| lexer/ | 手写状态机, 50 Token 类型 | +38 行 |
| ast/ | 25 种节点类型 (+7: TYPE_ALIAS, ENUM_DECL, ENUM_VARIANT, INDEX_EXPR, ARRAY_ASSIGN, IMPL_BLOCK, METHOD_CALL) | +218 行 |
| parser/ | 递归下降 + Pratt, 新增 enum/impl/type/match 解析, parse_type_expr() 统一类型表达式 |
+425 行 |
| sema/ | 类型推断, 5 种 SymbolKind (+SYM_ENUM), impl 方法改名 mangle, alias 展开链 | +542 行 |
| codegen/ | LLVM struct/array/enum/match, GEP, method call, 清理表动态扩容 | +269 行 |
| driver/ | 流水线串联, SourceLoc 适配 | +11 行 |
| test/ | 30 单元测试函数 (+11), 10 集成程序新增 | +636 行 |
| 汇总指标 | 上次 (v0.4, 1d4fb27) |
当前 (v0.5, a15cd9d) |
Δ |
|---|---|---|---|
| 实现代码 (.c) | 2,221 行 | ~3,480 行 | +~1,259 |
| 头文件 (.h) | 323 行 | ~450 行 | +~127 |
| 测试代码 | 361 行 | ~997 行 | +636 |
| 总代码量 | 2,905 行 | ~4,927 行 | +2,022 |
| Token 类型 | 39 | 50 | +11 |
| AST 节点类型 | 18 | 25 | +7 |
| TypeKind | 8 | 10 | +2 |
| SymbolKind | 4 | 5 | +1 |
| 单元测试函数 | 19 | 30 | +11 |
| 集成测试程序 | 13 | 23 | +10 |
| P0 完成度 | 4/4 (100%) | 4/4 (100%) | — |
| P1 完成度 | 0/6 (0%) | 6/6 (100%) | ✅ |
| Git 提交数 | 13 | 22 | +9 |
1.3 关键架构决策 (v0.5 新增)
| 决策 | 描述 | 技术理由 |
|---|---|---|
| impl 改名 mangle | impl Point { fn get_x } 在 sema 阶段将函数名改为 Point$get_x, 自动插入 self 参数 |
零入侵 codegen: 方法调用退化为普通函数调用 |
| match 脱糖为 let+if-else | parser 阶段将 match 转为 { let __match_val=expr; if ... else ... } |
复用现有 if-else codegen, 零新增 LLVM IR 路径 |
| 数组类型自引用声明 | let arr: [i64; 3] = arr; 以自身标识符为初始化占位符 |
ArrayType 固定大小, sema/codegen 识别 TYPE_ARRAY 标注后跳过 init 分析 |
| parse_type_expr 统一入口 | 所有类型标注解析通过同一函数 | 消除分散类型解析, 支持 [T; N] 嵌套 |
| cleanup_list 动态扩容 | 从固定 64 槽改为 realloc 式动态扩容 (16→x2) | 消除溢出风险 |
| SourceLoc 聚合 | typedef struct { int line; int col; } 替代所有 int line, int col 参数对 |
减少参数数量, 为后续添加 file 做准备 |
2. P1 全线功能详解
2.1 struct 作为函数参数/返回值 (P1 #5)
parser 的 parse_function 参数类型解析从 token_to_type() 改为 parse_type_expr(), 支持 fn f(p: Point) -> Point。Codegen 中 struct 参数通过 alloca+store 转为栈变量, struct 返回值通过直接值传递 (LLVM struct 类型)。
测试: 14_struct_fn.l — Point 作为参数和返回值。
2.2 类型别名 type alias (P1 #10)
parse_type_expr() 识别 TOK_IDENT 时查找已注册别名; sema Pass 0 提前注册所有别名。
语法: type Meters = i64;
type P = Point;
别名展开链:
parser: 解析 `let x: P` → annot struct_type_name="P"
sema: scope_lookup("P") → 别名符号 → 获取真实 type=STRUCT/struct_type_name="Point"
测试: 15_type_alias.l, 16_type_alias_struct.l
2.3 枚举 enum (P1 #7)
新增 TOK_ENUM/TOK_COLON_COLON Token; AST_ENUM_DECL/AST_ENUM_VARIANT AST 节点; SYM_ENUM SymbolKind。枚举变体映射为 i64 常量 (0, 1, 2...)。
语法: enum Color { Red, Green, Blue }
let c = Color::Green; // codegen: LLVMConstInt(LLVMInt64Type, 1)
测试: 17_enum.l, sema: test_enum_ok/test_enum_bad_variant
2.4 数组 + 索引 (P1 #6)
新增 [i64; N] 语法 (parse_type_expr 中解析), AST_INDEX_EXPR (读取), AST_ARRAY_ASSIGN_STMT (写入)。
语法: let arr: [i64; 3] = arr;
arr[0] = 10;
print_i64(arr[0]);
Codegen: LLVMArrayType(elem_ty, N) → alloca → GEP(0, i) → load/store
索引值 i64 → i32 截断 (LLVM GEP 要求 i32)
测试: 18_array.l, sema 4 测试, codegen: test_codegen_array
2.5 struct 方法 impl (P1 #9)
parse 处理 impl StructName { ... } 块; sema 将方法函数名改为 StructName$methodName 并追加 self 参数。
语法: impl Point {
fn get_x(self: Point) -> i64 { return self.x; }
}
p.get_x(); // AST_METHOD_CALL
Mangle 流程:
parser: impl → AST_IMPL_BLOCK { struct_name, methods }
sema: 遍历 impl → 改名 "Point$get_x", 插入前导 self 参数, 追加到 functions 数组
codegen: find_fn("Point$get_x") + call(receiver, args...) — 退化为普通函数调用
测试: 19_struct_method.l, sema 2 测试, codegen: test_codegen_method_call
2.6 match 表达式 (P1 #8)
新增 TOK_MATCH/TOK_MATCH_ARROW/TOK_UNDERSCORE Token; parser 内 parse_match_stmt() 将 match 脱糖为 let+if-else 链。
语法: match expr {
pat1 => { body1; }
_ => { default_body; }
}
脱糖逻辑 (parser 阶段):
match c { Color::Green => { print_i64(20); } _ => { print_i64(0); } }
→ {
let __match_val = c;
if __match_val == Color::Green { print_i64(20); }
else { print_i64(0); }
}
臂数 64 上限, 从最后一个臂往前构建 if-else 链
关键设计: match 不产生新 AST 节点 — 直接生成 AST_BLOCK + AST_LET_STMT + AST_IF_STMT 链; codegen/sema 零改动。
测试: 4 个集成测试 (20_match.l~23_match_wildcard.l)
3. 架构重构与基础设施
3.1 SourceLoc 抽象
// include/l_lang.h
typedef struct { int line; int col; } SourceLoc;
所有 AST 工厂函数从 int line, int col 参数对改为 SourceLoc loc 单参数。全模块已适配。
3.2 tok_is_type 统一
删除 parser.c 中重复的 is_type_token() 函数, 统一使用 token.c:tok_is_type() (已补 TOK_STR)。
3.3 CRITICAL Bug 修复清单
| 严重度 | 问题 | 修复 |
|---|---|---|
| CRITICAL | parser parse_struct_init 字段名数组栈变量悬垂 |
arena 分配 + memcpy |
| CRITICAL | codegen 隐式尾部 return 缺 struct 类型处理 | 添加 LLVMConstNull(st_ty) |
| CRITICAL | str 字面量 tok 未 NUL 终止 | arena_strdup + \0 |
| HIGH | codegen return 前 cleanup 可能 free 返回值 | 从 cleanup_list 移除 |
| HIGH | match 通配符 if-else 链条件 | if (true) { body } |
| HIGH | sema impl 处理 new_fns 数组大小计算时机 |
预先统计 extra_fn |
4. 功能清单与成熟度
4.1 已实现功能 (全版本)
- v0.1: 基本类型/算术/控制流/函数/内置函数/类型推断
- v0.2: let mut/赋值/str/字符串拼接/复合赋值
- v0.3: for + range
- v0.4: struct 声明+初始化+字段访问/嵌套结构体/RAII 自动内存管理
- v0.5: P1 全线 — struct 参数/返回值/类型别名/枚举/数组+索引/impl 方法/match 表达式
4.2 当前已知限制
| 限制 | 严重度 | 说明 |
|---|---|---|
数组不支持 struct 元素类型 [Point; N] |
中 | parse_type_expr TYPE_ARRAY 路径未传 element_struct_name |
| enum 无关联数据 (代数数据类型) | 中 | 仅 C 风格枚举 |
| 无表达式作为值 (expression-oriented) | 中 | if/match/block 不能出现在表达式位置 |
| match 仅单值匹配 | 低 | 不支持 range pattern / guard |
数组声明自引用语法 let arr: [i64; 3] = arr; |
低 | 必须写两遍 arr |
| 无字符串方法 (len/slice) | 低 | str 仅拼接+打印 |
5. 与 Rust 对标: 缺失功能清单及优先级
已完成 (P0 + P1 全通过)
| # | 功能 | Rust 启发 | 版本 |
|---|---|---|---|
| P0-1~4 | 复合赋值/str拼接/for+range/struct | — | v0.2–v0.4 |
| P1-5 | struct 参数/返回值 | 值语义 | v0.5 |
| P1-6 | 数组+索引 | [T; N], arr[i] |
v0.5 |
| P1-7 | 枚举 | enum | v0.5 |
| P1-8 | match 表达式 | 模式匹配 (简化) | v0.5 |
| P1-9 | struct 方法 | impl block | v0.5 |
| P1-10 | 类型别名 | type alias | v0.5 |
P2: 中后期
| # | 功能 | 预计工时 | 状态 |
|---|---|---|---|
| 11 | 表达式作为值 (expression-oriented) | 2–3 天 | 待实施 |
| 12 | 模块系统 mod + use |
3–5 天 | 待实施 |
| 13 | 泛型 (单态化) | 5–7 天 | 待实施 |
| 14 | 枚举关联数据 (代数数据类型) | 2 天 | 待实施 |
| 15 | trait / 接口 | 5–7 天 | 待实施 |
P3: 长期
| # | 功能 | 预计工时 |
|---|---|---|
| 16 | 所有权 / 借用检查 | 2–4 周 |
| 17 | 自举 (L 编译 L) | 4–6 周 |
| 18 | 标准库 (prelude) | 4–6 周 |
Rust 设计哲学吸收进度
| Rust 特性 | 状态 | 备注 |
|---|---|---|
| 默认不可变 (let vs let mut) | ✅ | 编译期检查 |
| 复合赋值 | ✅ | parser desugar |
| for + range | ✅ | desugar 为 while |
| struct 具名域 | ✅ | LLVM 命名结构体 |
| impl 方法 | ✅ | sema mangle |
| enum + match (简化) | ✅ | C 风格枚举 + 单值 match |
| 类型别名 | ✅ | alias 展开链 |
| 数组固定大小 | ✅ | GEP |
| RAII 自动释放 | ✅ | cleanup_list |
| 表达式 vs 语句 | ❌ | P2 优先 |
| 代数数据类型 | ❌ | P2 优先 |
| 泛型 (单态化) | ❌ | P2 优先 |
| trait / 接口 | ❌ | P2 优先 |
| 所有权 / 借用 | ❌ | P3 |
| 模块系统 | ❌ | P2 优先 |
6. 测试覆盖分析
6.1 单元测试
| 测试文件 | 测试函数 | 覆盖范围 |
|---|---|---|
| test_lexer.c | 3 | token 识别, 关键字, 操作符, 注释 |
| test_parser.c | 5 | 算术, let, if, while, 函数 |
| test_sema.c | 21 (+12) | 类型错误, let mut, str, struct 字段, 类型别名, enum, 数组, 方法调用 |
| test_codegen.c | 9 (+5) | 基础路径, struct decl/field, enum, array, method call |
| 合计 | 38 (+11) |
6.2 集成测试 (23 程序)
- 01–11: v0.1–v0.3 基线
- 12–13: struct + 嵌套 struct (v0.4)
- 14–23: v0.5 新增 10 程序 (struct_fn/type_alias/enum/array/struct_method/4×match)
6.3 测试缺口
| 缺口 | 严重度 |
|---|---|
| match/sema 无独立单元测试 | 高 |
| 数组 struct 元素类型无测试 | 中 |
| RAII + 新功能交互无验证 | 中 |
| parser 错误恢复无测试 | 低 |
7. 技术债务与风险
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| 1 | analyze_expr 持续膨胀 (200+→350+ 行) | 高 | ENUM_VARIANT/INDEX_EXPR/METHOD_CALL 等 |
| 2 | parser.c 单文件 939 行 | 中 | +415 行: enum/impl/type/match/match 脱糖 |
| 3 | codegen.c 单文件 854 行 | 中 | 3 个 switch 持续增长 |
| 4 | match 脱糖在 parser, 非独立 pass | 中 | arch-improvements #4 建议未实施 |
| 5 | 数组 struct 元素类型未实现 | 中 | [Point; N] |
| 6 | TypeKind 耦合 (arch-improvements #1) | 低 | 加新类型需改 7+ 文件 |
| 7 | AST Visitor 缺失 (arch-improvements #2) | 低 | 加新节点需改 4 个 switch |
| 8 | CHANGELOG 未更新 v0.4/v0.5 | 低 | 仅到 v0.3 |
| 9 | LLVM 22 无 mem2reg C API | 低 | 已记录 |
| 10 | system("gcc ...") 平台绑定 |
低 | Windows/MinGW |
8. 推荐开发路线图
v0.6 目标 (下一迭代, 2–3 天)
| 优先级 | 功能 | 预计工时 |
|---|---|---|
| P1 | parser 独立 desugar pass (for/match/compound assign) | 0.5 天 |
| P1 | 数组 struct 元素类型 [Point; N] |
0.5 天 |
| P1 | match/sema 单元测试 | 0.5 天 |
| P2 | CHANGELOG 更新 v0.4/v0.5 | 0.5 天 |
v0.7 目标
| 优先级 | 功能 | 预计工时 |
|---|---|---|
| P2 | 表达式作为值 (expression-oriented) | 2–3 天 |
| P2 | 枚举关联数据 | 2 天 |
| P2 | AST Visitor 宏驱动 | 2 天 |
长期方向
| 优先级 | 功能 | 预计工时 |
|---|---|---|
| P2 | 模块系统 / 泛型 / trait | 各 3–7 天 |
| P3 | 所有权 / 自举 / 标准库 | 2–6 周 |
9. 代码质量评估
9.1 模块内聚度
| 模块 | 评价 |
|---|---|
| lexer | 高 — 11 新 Token 一字插入 |
| parser | 中 — +415 行, parse_match_stmt(80行) + parse_type_expr(40行) + impl/enum/type 内联 |
| ast | 高 — 7 新节点遵循工厂函数模式 |
| sema | 中高 — +542 行: impl mangle/alias 展开/enum/array/method 语义; analyze_expr switch 350+ 行 |
| codegen | 中 — +269 行: enum/array/index/method/match; 3 个 switch 持续增长 |
| driver | 高 — 仅 SourceLoc 适配 |
| util | 高 — 无变更 |
9.2 代码风格亮点
- impl mangle 设计: sema 阶段转换, codegen 零修改 — 干净解耦
- match 脱糖: 转为 AST 原语链, sema/codegen 完全复用 (但耦合在 parser 中)
- parse_type_expr 统一: 消除分散类型解析, 天然支持
[T; N] - cleanup_list 动态扩容: arena 分配保持 zero-overhead
9.3 架构债务
| 债务 | 说明 |
|---|---|
| parser.c 939 行 | 含 enum/impl/type/match + 脱糖, 建议 P2 模块化 |
| sema analyze_expr 350+ 行 | CASE 从 5 增至 10 |
| codegen 3 个 switch 增长 | 新增 3 case (ENUM_VARIANT/METHOD_CALL/INDEX_EXPR) |
| impl mangle 隐式命名约定 | codegen 通过 $ 识别方法, 非结构化标记 |
10. 度量汇总 (对比)
| 指标 | v0.4 (1d4fb27) |
v0.5 (a15cd9d) |
Δ |
|---|---|---|---|
| 实现代码 | 2,221 行 | ~3,480 行 | +~1,259 |
| 头文件 | 323 行 | ~450 行 | +~127 |
| 测试代码 | 361 行 | ~997 行 | +636 |
| 总代码量 | 2,905 行 | ~4,927 行 | +2,022 |
| Token 类型 | 39 | 50 | +11 |
| AST 节点类型 | 18 | 25 | +7 |
| TypeKind | 8 | 10 | +2 |
| SymbolKind | 4 | 5 | +1 |
| 单元测试函数 | 19 | 30 | +11 |
| 集成测试程序 | 13 | 23 | +10 |
| P0 完成度 | 4/4 (100%) | 4/4 (100%) | — |
| P1 完成度 | 0/6 (0%) | 6/6 (100%) | ✅ |
| Git 提交数 | 13 | 22 | +9 |
本报告由 Codex 自动生成于 2026-06-05 15:09。自上次代码基线 1d4fb27 (v0.4) 后有 9 个源代码提交 (+2596/-284 行): P1 路线图全线收官 — struct 参数/返回值、类型别名、枚举、数组+索引、struct 方法 impl、match 表达式全部落地。v0.5 实现了 Rust 启发语言的完整单片编译器能力; 下一迭代应关注 parser 独立 desugar pass、数组 struct 元素类型补全、表达式作为值。