refactor: 拆分 sema analyze_expr 为 9 个辅助函数 + 调度器
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# L Language 架构分析报告 v0.6
|
||||
|
||||
> 日期: 2026-06-06 00:50 | 自动生成 | **重大代码变更**
|
||||
> 上次代码基线: `a15cd9d` (2026-06-05 17:08 报告, v0.5)
|
||||
> 当前 HEAD: `de91886` fix: CreateProcess 替代 system() 调 ld.lld,消除 shell 转义问题
|
||||
> **版本: v0.6**
|
||||
|
||||
---
|
||||
|
||||
## 代码变更检查
|
||||
|
||||
```
|
||||
git log a15cd9d..HEAD → 20 新提交, 36 文件变更, +2483/-163 行
|
||||
```
|
||||
|
||||
自 v0.5 以来累计 20 个提交,涵盖 6 个新语言特性、语法差异化调整、链接器重构、安装包系统。
|
||||
|
||||
详细提交链:
|
||||
|
||||
| # | 提交 | 类别 |
|
||||
|---|------|------|
|
||||
| 1-2 | CHANGELOG/语言参考文档 | 文档 |
|
||||
| 3-4 | CLAUDE.md 语言设计哲学 | 文档 |
|
||||
| 5 | 数组初始化 `= arr` 语法说明 | 文档 |
|
||||
| 6 | match/sema 单元测试 (145→158) | 测试 |
|
||||
| 7 | 数组支持 struct 元素类型 `Point[N]` | P1-5 修复 |
|
||||
| 8 | 测试计数更新 145→158 + 23→24 | 测试 |
|
||||
| 9 | for..in 范围语法 `..` → `to` | 语法差异化 |
|
||||
| 10 | `let mut` → `var` | 语法差异化 |
|
||||
| 11 | `[T; N]` → `T[N]` 后置语法 | 语法差异化 |
|
||||
| 12 | `impl` → `extend` | 语法差异化 |
|
||||
| 13 | 新增 i32/u64/char 类型 + 字符字面量 | 新功能 |
|
||||
| 14 | guard 语句 | 新功能 |
|
||||
| 15 | 命名参数 | 新功能 |
|
||||
| 16 | 管道 `|>` + 字符串插值 `\(expr)` — P0 四特性收官 | 新功能 |
|
||||
| 17 | README/CHANGELOG/语言参考更新至 v0.6 | 文档 |
|
||||
| 18 | 链接器 gcc → clang + lld | 基础设施 |
|
||||
| 19 | NSIS 安装包脚本 | 基础设施 |
|
||||
| 20 | CreateProcess 替代 system() 调 ld.lld | 基础设施 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前架构快照 (v0.6)
|
||||
|
||||
### 1.1 编译流水线
|
||||
|
||||
```
|
||||
源码(.l) → Lexer(词法) → Parser(语法) → Sema(语义) → Codegen(LLVM IR) → Target(obj) → clang+lld 链接(.exe)
|
||||
58 Token 25 AST 节点 类型标注 LLVMModuleRef .o 文件 优先 clang→fallback gcc
|
||||
```
|
||||
|
||||
v0.5 的 GCC 链接器已被 clang + lld 替代(自动 fallback)。`system()` 调用被 `CreateProcess` 替代,消除 shell 转义问题。
|
||||
|
||||
### 1.2 模块清单与行数
|
||||
|
||||
| 模块 | 文件 | 行数 | vs v0.5 | 职责 |
|
||||
|------|------|------|---------|------|
|
||||
| include/ | l_lang.h | 55 | -2 | TypeKind (13), SourceLoc, 公共类型 |
|
||||
| lexer/ | lexer.c + token.c (+ .h) | 168+53+47 = 268 | +11 | 手写状态机, 58 Token 类型 |
|
||||
| ast/ | ast.c + ast.h | 221+152 = 373 | -31 | 25 种 AST 节点, 工厂函数 (arg_names 扩展) |
|
||||
| parser/ | parser.c + parser.h | 851+10 = 861 | +23 | 递归下降+Pratt, guard/pipe/插值去糖 |
|
||||
| sema/ | sema.c + symbol.c (+ .h) | 871+148+63 = 1082 | +77 | 类型推断, can_implicit_convert, 命名参数重排 |
|
||||
| codegen/ | codegen.c + target.c (+ .h) | 764+30+15 = 809 | -55 | LLVM IR 生成, coerce_int 隐式转换 |
|
||||
| driver/ | main.c + error.c (+ .h) | 205+46+18 = 269 | +58 | 流水线串联, CreateProcess 链接 |
|
||||
| util/ | arena.c + arena.h | 39+13 = 52 | 不变 | Bump allocator (8MB) |
|
||||
| **实现总计** | | **~3,769 行** | **+434 vs v0.5** | |
|
||||
|
||||
> 注意: codegen.c 从 824 行收缩到 764 行(-7.3%),说明新加入的 pipe/guard/插值等功能去糖后不需要 codegen 层改动。
|
||||
|
||||
### 1.3 核心类型系统变化
|
||||
|
||||
| 枚举 | v0.5 | v0.6 | 变更 |
|
||||
|------|------|------|------|
|
||||
| TypeKind | 10 | **13** | +TYPE_I32, TYPE_U64, TYPE_CHAR |
|
||||
| TokenKind | 50 | **58** | +TOK_VAR, TOK_GUARD, TOK_I32, TOK_U64, TOK_CHAR, TOK_CHAR_LIT, TOK_PIPE, TOK_TO; TOK_IMPL→TOK_EXTEND |
|
||||
| AstNodeKind | 25 | **25** | 不变 — 所有新功能经去糖复用现有节点 |
|
||||
| SymbolKind | 5 | **5** | 不变 |
|
||||
|
||||
**TypeKind 完整列表**: TYPE_I32, TYPE_I64, TYPE_U64, TYPE_F64, TYPE_BOOL, TYPE_CHAR, TYPE_STR, TYPE_VOID, TYPE_STRUCT, TYPE_ENUM, TYPE_ARRAY, TYPE_UNKNOWN, TYPE_ERROR
|
||||
|
||||
**Token 新增**:
|
||||
|
||||
| 新增 Token | 用途 |
|
||||
|------------|------|
|
||||
| TOK_VAR | `var` 关键字 (替代 `let mut`) |
|
||||
| TOK_GUARD | `guard` 关键字 |
|
||||
| TOK_I32 / TOK_U64 / TOK_CHAR | 新基本类型 |
|
||||
| TOK_CHAR_LIT | 字符字面量 `'"'"'A'"'"'` |
|
||||
| TOK_PIPE | 管道运算符 `|>` |
|
||||
| TOK_TO | 范围运算符 `to` (替代 `..`) |
|
||||
| TOK_EXTEND | `extend` 关键字 (替代 `impl`) |
|
||||
|
||||
**AST 结构体扩展**:
|
||||
- `AST_CALL_EXPR.call` 新增 `arg_names` 字段 — 支持命名参数
|
||||
- `AST_METHOD_CALL.method_call` 新增 `arg_names` 字段 — 支持命名参数
|
||||
- `AST_LITERAL_EXPR.literal` 新增 `lit_type` + union 扩展 — 支持 char 字面量
|
||||
|
||||
**Symbol 扩展**:
|
||||
- `Symbol.param_names` 新增 — 支持命名参数匹配
|
||||
|
||||
---
|
||||
|
||||
## 2. 功能完成度总览
|
||||
|
||||
### 2.1 P0 (v0.1-v0.4) — 100%
|
||||
|
||||
全 12 项保持完成状态。
|
||||
|
||||
### 2.2 P1 (v0.5) — 100%
|
||||
|
||||
全 6 项保持完成,P1-5 (数组 struct 元素类型) 在本轮修复。
|
||||
|
||||
### 2.3 P0-扩展 (v0.6 新增) — 100%
|
||||
|
||||
| # | 功能 | 去糖策略 | 灵感来源 | 影响层 |
|
||||
|---|------|---------|---------|--------|
|
||||
| 13 | `i32` / `u64` / `char` 类型 | TypeKind 扩展 + can_implicit_convert | Rust/ML | lexer/ast/sema/codegen |
|
||||
| 14 | `guard x >= 0 else { return -1; }` | parser 去糖为 if-else | Swift | parser |
|
||||
| 15 | `draw_rect(width: 10, height: 20)` | sema 重排序为位置参数 | Python/Swift | parser/sema |
|
||||
| 16 | `10 |> double() |> add(5)` | parser 去糖为 `add(double(10), 5)` | F#/Elixir | parser |
|
||||
| 17 | `"Hello, \(name)!"` | lexer+parser 去糖为 `str+str` | Swift/Rust | lexer/parser |
|
||||
| 18 | `extend` 方法块 | 即原 `impl` mangle,改名 | 差异化 | lexer |
|
||||
|
||||
### 2.4 语法差异化 (L 语言独有)
|
||||
|
||||
| L 语法 | Rust 对应 | 理由 |
|
||||
|--------|----------|------|
|
||||
| `var` | `let mut` | 更简洁,2 字符 vs 6 字符 |
|
||||
| `T[N]` | `[T; N]` | C 风格直觉,减少括号 |
|
||||
| `to` | `..` | 明确语义 (省略终值 `to` vs 闭区间 `..=`) |
|
||||
| `extend` | `impl` | 避免 Rust 预留词 |
|
||||
| `guard` | 无直接对应 | Swift 灵感,前条件守卫 |
|
||||
| `|>` | 无直接对应 | F#/Elixir 灵感,函数组合 |
|
||||
| `\(expr)` | 无直接对应 | Swift 风格字符串插值 |
|
||||
|
||||
---
|
||||
|
||||
## 3. Rust 对标缺失清单
|
||||
|
||||
### 3.1 已完成
|
||||
|
||||
| 特性 | 状态 | 版本 |
|
||||
|------|------|------|
|
||||
| 默认不可变 (let) / 可变 (var) | ✅ | v0.1-v0.6 |
|
||||
| 复合赋值 | ✅ | v0.2 |
|
||||
| for + range | ✅ | v0.3 |
|
||||
| struct 具名域 | ✅ | v0.4 |
|
||||
| impl/extend 方法 | ✅ | v0.5 |
|
||||
| enum + match | ✅ | v0.5 |
|
||||
| 类型别名 | ✅ | v0.5 |
|
||||
| 数组固定大小 | ✅ | v0.5 |
|
||||
| 数组 struct 元素类型 | ✅ | v0.6 |
|
||||
| RAII 自动释放 | ✅ | v0.4 |
|
||||
| 命名参数 | ✅ | v0.6 |
|
||||
| 管道运算符 | ✅ | v0.6 |
|
||||
| 字符串插值 | ✅ | v0.6 |
|
||||
| guard 守卫语句 | ✅ | v0.6 |
|
||||
| i32/u64/char 多种整数类型 | ✅ | v0.6 |
|
||||
|
||||
### 3.2 缺失 (P2 — 中期, 按难度排序)
|
||||
|
||||
| # | 功能 | 预计工时 | 优先级 | Rust 对应 | 说明 |
|
||||
|---|------|---------|--------|----------|------|
|
||||
| 19 | 表达式作为值 (块返回最后表达式) | 2-3 天 | **高** | 块是表达式 | sema/codegen 需处理块类型推断, 当前 block→void |
|
||||
| 20 | 枚举关联数据 (ADT) | 2-3 天 | **高** | `enum Option<T>` | 当前仅简单常量枚举, 需 payload + 模式匹配扩展 |
|
||||
| 21 | `if let` / `while let` | 1 天 | 中 | `if let Some(x) = opt` | 基于枚举关联数据 |
|
||||
| 22 | 模块系统 `mod` + `use` | 3-5 天 | **高** | `mod`, `use`, `pub` | 多文件编译, 符号可见性 |
|
||||
| 23 | 泛型 (单态化) | 5-7 天 | 中 | `fn<T>(x: T)` | sema 类型参数收集, codegen 复制 |
|
||||
| 24 | trait / 接口 | 5-7 天 | 中 | `trait`, `impl Trait` | vtable 或单态化分派 |
|
||||
| 25 | 引用/切片 | 7-10 天 | 中 | `&T`, `&[T]` | 需指针层次, 生命周期标注 |
|
||||
|
||||
### 3.3 缺失 (P3 — 长期)
|
||||
|
||||
| # | 功能 | 预计工时 | Rust 对应 |
|
||||
|---|------|---------|----------|
|
||||
| 26 | 所有权 / 借用检查 | 2-4 周 | borrow checker |
|
||||
| 27 | 闭包 (lambda) | 3-5 天 | `|x| x+1` |
|
||||
| 28 | 自举 (L 编译 L) | 4-6 周 | — |
|
||||
| 29 | 标准库 (prelude) | 4-6 周 | std |
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试覆盖
|
||||
|
||||
| 测试文件 | 测试函数 | vs v0.5 | 覆盖范围 |
|
||||
|---------|----------|---------|---------|
|
||||
| test_lexer.c | 3 | 不变 | token 识别 |
|
||||
| test_parser.c | 5 | 不变 | 语法解析基本路径 |
|
||||
| test_sema.c | 24 | +3 | i32/u64/char 转换, named args, guard, 管道, 插值 |
|
||||
| test_codegen.c | 10 | +1 | 隐式转换, 命名参数, i32/u64/char codegen |
|
||||
| **单元合计** | **42** | **+4** | |
|
||||
| 集成 .l 程序 | **29** | **+6** | 24_array_struct, 25_new_types, 26_guard, 27_named_args, 28_pipe, 29_interp |
|
||||
| **测试总计** | **71** | **+10** | |
|
||||
|
||||
新集成测试覆盖:
|
||||
- `24_array_struct.l` — struct 数组 (P1-5 修复验证)
|
||||
- `25_new_types.l` — i32/u64/char 类型 + 隐式转换
|
||||
- `26_guard.l` — guard 守卫语句
|
||||
- `27_named_args.l` — 命名参数任意顺序
|
||||
- `28_pipe.l` — 管道 |> 函数组合
|
||||
- `29_interp.l` — 字符串插值
|
||||
|
||||
---
|
||||
|
||||
## 5. 代码审查 — 风险区域
|
||||
|
||||
### 5.1 去糖策略的评价
|
||||
|
||||
v0.6 延续了 v0.5 的去糖哲学: pipe/guard/插值/named_args 全在 parser 层去糖为现有 AST 节点。优势是 sema/codegen 改动极小(codegen 甚至缩水 60 行),但代价是:
|
||||
|
||||
1. **parser.c 进一步膨胀**: 825→851 行,去糖逻辑散落在 `parse_expr` 和 `parse_statement` 之间
|
||||
2. **错误消息质量下降**: 去糖后生成的 AST 丢失原始语法信息,错误报告指向去糖后的 if-else 而非原始 `guard` 语句
|
||||
3. **调试/IR 可读性**: 用户写 `10 |> double()` 但 IR 中看到的是 `double(10)`,不利于学习
|
||||
|
||||
**建议**: v0.7 考虑将 guard/match/named_args/pipe 去糖抽取为独立 desugar pass,在 parser 生成 AST 之后、sema 之前运行。这既能保持"去糖"优势,又能改善错误消息质量。
|
||||
|
||||
### 5.2 sema.c 持续膨胀
|
||||
|
||||
sema.c 从 809→871 行 (v0.5→v0.6),analyze_expr 仍然是单函数怪兽 (~380 行)。新增的 `can_implicit_convert()` 是好的模块化进步,但类型检查逻辑仍需拆分。
|
||||
|
||||
### 5.3 TypeKind 耦合未改善
|
||||
|
||||
TypeKind 从 10 增加到 13,修改范围涉及 7+ 文件。加新类型仍是全局搜索替换作业。
|
||||
|
||||
### 5.4 链接器变更风险
|
||||
|
||||
gcc→clang+lld 变更是正确方向,但 fallback 链 (clang→gcc) 增加了 CI 配置复杂度。自包含安装包 (39MB) 和 NSIS 安装包 (62MB) 是两个并行方案,后续需统一。
|
||||
|
||||
---
|
||||
|
||||
## 6. 技术债务更新
|
||||
|
||||
### 6.1 已解决
|
||||
|
||||
| # | 原问题 | 解决提交 |
|
||||
|---|--------|---------|
|
||||
| 5 | 数组 struct 元素类型 `[Point; N]` 未实现 | `a45f7d8` |
|
||||
| 6 | match/sema 无独立单元测试 | `beac40f` |
|
||||
| 9 | CHANGELOG 未更新 v0.4/v0.5 | `5a0bf60` + `2baf762` |
|
||||
|
||||
### 6.2 当前债务 (优先级排序)
|
||||
|
||||
| # | 问题 | 严重度 | 现状 | 备注 |
|
||||
|---|------|--------|------|------|
|
||||
| 1 | analyze_expr 膨胀 (~380 行) | **高** | 未修复, 反而增加 | sema.c 871 行, +62 vs v0.5 |
|
||||
| 2 | parser.c 单文件 851 行 | **高** | 未修复, +26 行 | 去糖逻辑散落 |
|
||||
| 3 | 去糖无独立 pass, 错误消息受损 | **中** | 需求显现 | v0.6 新暴露 |
|
||||
| 4 | codegen.c 824→764 行, 但仍有优化空间 | 中 | 改善中 | struct/element_type 逻辑可精简 |
|
||||
| 5 | TypeKind 耦合 (改 7+ 文件) | 低 | 未修复 | 13 种类型, 加新类型成本递增 |
|
||||
| 6 | AST Visitor 缺失 | 低 | 未修复 | 加节点需改 4 个 switch |
|
||||
| 7 | LLVM 22 无 mem2reg C API | 低 | 平台限制 | IR 含冗余 alloca/store/load |
|
||||
|
||||
---
|
||||
|
||||
## 7. 基础设施变更
|
||||
|
||||
| 组件 | v0.5 | v0.6 | 影响 |
|
||||
|------|------|------|------|
|
||||
| 链接器 | GCC (外部依赖) | clang + lld (优先), fallback gcc | 用户需安装 LLVM 或将 ld.lld 打包 |
|
||||
| 进程启动 | `system()` | `CreateProcess` | 消除 shell 注入/转义风险 |
|
||||
| 发布 | 无 | NSIS 安装包 (62MB) / 自包含包 (39MB) | 零依赖部署 |
|
||||
| 文档 | README + CHANGELOG | +语言参考手册 (643 行) + CLAUDE.md | 开发者友好 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 与 PRD 对照
|
||||
|
||||
PRD v0.1 设定了"学习编译器全流程"的短期目标。对照 PRD 中的非目标列表:
|
||||
|
||||
| PRD 非目标 (v0.1 不做) | 当前状态 |
|
||||
|------------------------|---------|
|
||||
| 字符串类型 | ✅ v0.2 实现 `str` |
|
||||
| 数组/切片/结构体 | ✅ v0.4-v0.5 实现 struct/array |
|
||||
| 模块系统 | ❌ 仍缺失 |
|
||||
| 泛型/trait | ❌ 仍缺失 |
|
||||
| 模式匹配 | ✅ v0.5 match (简化) |
|
||||
| 标准库 | ❌ 仍缺失 |
|
||||
| 垃圾回收 | ❌ 不做 (采用 RAII) |
|
||||
|
||||
v0.6 已大幅超出 v0.1 PRD 范围。建议更新 PRD 到 v0.6 以反映当前功能边界。
|
||||
|
||||
---
|
||||
|
||||
## 9. 推荐开发路线图
|
||||
|
||||
### v0.7 (短期, 2-3 天)
|
||||
|
||||
1. **独立 desugar pass** — 将 guard/pipe/named_args 去糖从 parser 移到独立 pass,改善错误消息和 parser 复杂度
|
||||
2. **analyze_expr 拆分** — 按节点类型拆分为 4-6 个辅助函数
|
||||
3. **枚举关联数据 (ADT)** — `enum Option { Some(i64), None }`,打开类型安全编程新范式
|
||||
4. **表达式作为值** — `let x = if a { 1 } else { 2 }`,函数体最后表达式即返回值
|
||||
|
||||
### v0.8 (中期, 1 周)
|
||||
|
||||
1. **模块系统** — `mod` + `use` + `pub`,多文件编译
|
||||
2. `if let` / `while let` — 简化 ADT 匹配
|
||||
3. 标准库起步: `io`, `convert`, `vec`
|
||||
|
||||
### v0.9 (中期, 1-2 周)
|
||||
|
||||
1. 泛型 (单态化)
|
||||
2. trait / 接口
|
||||
3. AST Visitor 宏驱动
|
||||
|
||||
---
|
||||
|
||||
*本报告由 Codex 自动生成于 2026-06-06 00:50。自上次代码基线 `a15cd9d` (v0.5) 以来共 20 个提交, +2483/-163 行。P0 扩展 6 个新特性全部实现, 语法完成差异化定型, 链接器/进程启动基础设施升级。v0.7 应聚焦 parser 结构优化和枚举 ADT。*
|
||||
Reference in New Issue
Block a user