# L Language 架构分析与功能建议报告 > 日期: 2026-06-05 | 自动生成 > 项目: L Language v0.1 — 用 C17 实现的静态类型编译型语言 --- ## 1. 当前架构全景 ### 1.1 编译流水线 ``` 源码(.l) → Lexer(词法分析) → Parser(语法分析) → Sema(语义分析) → Codegen(LLVM IR) → 链接(.exe) Token[] AstNode* 类型标注 LLVMModuleRef GCC/ld ``` 一条经典的单趟编译器流水线,手写实现,无依赖生成器。 ### 1.2 模块深度分析 | 模块 | 行数(估) | 关键数据结构 | 现状评估 | |------|----------|-------------|---------| | lexer/ | ~200 | Token {kind, start, length, line, col} | 稳定,支持 25+ Token 类型、行/块注释 | | ast/ | ~150 | AstNode {kind, type, as{union}} | 14 种节点,工厂函数风格 | | parser/ | ~400 | Parser {tokens, pos, filename, arena} | 递归下降 + Pratt 优先级解析,成熟 | | sema/ | ~200 | Scope {head, parent}, Symbol {name, kind, type} | 作用域链、类型推断、类型检查 | | codegen/ | ~300 | CgCtx {builder, var_table, fn_table} | LLVM-C API 完整映射,含内置 printf | | util/ | ~80 | Arena bump allocator | 8MB 固定大小,贯穿全流水线 | | driver/ | ~150 | 流水线串联 | 带 --emit-ir、-o 参数 | ### 1.3 技术选择评估 | 选择 | 评价 | |------|------| | C17 + CMake | 轻量,适合学习编译器全流程 | | Arena allocator | 极简内存管理,无 GC 开销,无内存泄漏 | | LLVM-C API | 成熟稳定,但版本差异需在 CMake 中处理 | | 手写词法/语法分析 | 0 外部依赖,完整控制权 | | Pratt 解析 | 优雅处理表达式优先级,扩展方便 | | GCC 链接 | 简单粗暴,跨平台需额外处理 | --- ## 2. 当前功能清单与成熟度 ### 已实现 (v0.1) - [x] 基本类型: i64, f64, bool, void - [x] 算术运算: + - * / % - [x] 比较运算: == != < > <= >= - [x] 逻辑运算: && || ! - [x] 变量声明: let 不可变,支持类型标注和类型推断 - [x] 控制流: if/else, while - [x] 函数定义与调用: 参数、返回类型、递归 - [x] 内置函数: print_i64, print_f64, print_bool - [x] 注释: // 行注释, /* */ 块注释 - [x] 错误报告: 词法/语法即时终止, 语义错误收集后批量输出 - [x] 测试: 41(词法) + 15(语法) + 9(语义) 单元测试, 5 个集成测试程序 ### 未实现 (与 Rust 对标) - [ ] 字符串类型 (只有 printf 的内建打印) - [ ] 数组/切片 - [ ] 自定义结构体/枚举 - [ ] let mut 可变变量 + 赋值语句 - [ ] 复合赋值: += -= *= /= - [ ] for 循环 / loop 循环 - [ ] match 模式匹配 - [ ] 泛型 - [ ] 模块系统 / 多文件编译 - [ ] trait / 接口 - [ ] 所有权 / 借用 / 生命周期 - [ ] 类型别名 - [ ] 标准库 --- ## 3. 向 Rust 学习: 功能优先级建议 按"学习价值 + 实现难度 + 对编译器的收获"排序。 ### P0: 短期可加(1-2 天) #### 3.1 let mut + 赋值语句 Rust 启发: Rust 中 let 默认不可变,let mut 声明可变变量。 改动范围: - lexer: 新增 mut 关键字 Token - parser: let_stmt 添加 is_mut 标志; 新增 assignment_stmt - sema: 可变性检查(不可变变量不能赋值) - codegen: 赋值语句生成 store IR 学习价值: 理解"默认不可变"的设计哲学如何贯穿全编译流程。 #### 3.2 字符串类型 + 字符串字面量 Rust 启发: Rust 的 &str 和 String 区分, 但初期只需一个简单字符串。 改动范围: - lexer: 双引号字符串字面量 Token ("...") - parser: 字符串字面量 AST 节点 - sema: 新增 TYPE_STR 类型 - codegen: 字符串常量在 LLVM IR 中生成全局数组; print_str 内置函数 - driver: 字符串在 IR 中需 i8* 指针表示 学习价值: 掌握 LLVM 中常量和全局变量的处理。 ### P1: 中期(3-5 天) #### 3.3 数组类型 Rust 启发: [T; N] 固定大小数组。 改动范围: - lexer: [ ] Token 已有 - ast: AST_ARRAY_TYPE, AST_INDEX_EXPR 节点 - parser: 类型语法 [i64; 10], 索引表达式 arr[0] - sema: 数组类型检查, 索引必须为 i64, 边界检查 - codegen: LLVM alloca + GEP 指针计算 学习价值: 学习 GEP (GetElementPtr) 指针计算, 这是 LLVM 中最重要的概念之一。 #### 3.4 结构体 (struct) Rust 启发: Rust 的 struct 定义具名域。 改动范围: - lexer: struct 关键字, . 点号 - ast: AST_STRUCT_DECL, AST_FIELD_ACCESS 节点 - parser: struct 声明 + 初始化 + 字段访问 - sema: 结构体类型符号表, 字段类型解析 - codegen: LLVM 结构体类型 (LLVMStructType), GEP 访问字段 学习价值: 深入 LLVM 结构体类型与 GEP 偏移量的精确定位。 #### 3.5 枚举 + 简单模式匹配 Rust 启发: Rust 的 enum 是代数数据类型, 但初期可以先做 C 风格 enum。 改动范围: - lexer: enum, :: 作用域解析 - ast: AST_ENUM_DECL 节点 - parser: enum 声明 - sema: enum 类型符号表 - codegen: enum 在 LLVM 中用 i64 表示 (C 风格) 学习价值: 为后续 Rust 风格的代数类型和 match 做铺垫。 ### P2: 中后期(1-2 周) #### 3.6 match 表达式 Rust 启发: Rust 的 match 是穷举模式匹配, 表达式级别。 改动范围: - lexer: match, =>, _ 通配符 - ast: AST_MATCH_EXPR, AST_MATCH_ARM 节点 - parser: match 表达式解析 - sema: 穷举性检查, 模式变量绑定 - codegen: switch 或 if-else 链生成 学习价值: 模式匹配是 PL 设计中最重要的概念之一。 #### 3.7 for 循环 + Range Rust 启发: for i in 0..10 {} 改动范围: - lexer: for, in, .. Token - ast: AST_FOR_STMT, AST_RANGE_EXPR 节点 - parser: for 循环解析 - sema: 范围类型检查 - codegen: 翻译为 while 循环 IR 学习价值: 语法糖如何被编译为底层控制流, 理解"去糖" (desugaring)。 #### 3.8 类型别名 (type) Rust 启发: type Meters = i64; - lexer: type 关键字 - ast: AST_TYPE_ALIAS 节点 - parser: 类型别名声明 - sema: 类型别名展开 - codegen: 透明 (别名展开后与原类型相同) 学习价值: 类型系统的"名称等价"vs"结构等价"概念。 ### P3: 长期(2-4 周) #### 3.9 泛型 Rust 启发: Rust 的泛型是编译期单态化。 改动范围: - lexer: < > (已有) + 类型参数语法 - ast: AST_GENERIC_FN, AST_TYPE_PARAM 节点 - parser: 带类型参数的函数签名 - sema: 泛型符号表, 单态化展开 - codegen: 按具体类型展开生成代码 学习价值: 单态化 (monomorphization) 是 Rust 零成本抽象的基石。 #### 3.10 trait / 接口 Rust 启发: Rust 的 trait 定义行为契约。 改动范围: - ast: AST_TRAIT_DECL, AST_IMPL_BLOCK 节点 - parser: trait + impl 语法 - sema: trait 一致性检查, 虚表/静态分发 - codegen: 动态分发 (虚表指针) / 静态分发 (单态化) 学习价值: 理解"接口"在编译期/运行时的不同实现策略。 #### 3.11 模块系统 / 多文件编译 Rust 启发: Rust 的 mod + use 模块系统。 改动范围: - driver: 多文件读取 + 编译单元管理 - ast: AST_MODULE, AST_USE 节点 - parser: mod, use 关键字 - sema: 模块作用域, 可见性检查 - codegen: 链接多个编译单元 学习价值: 从单文件到多文件的跳跃, 理解链接过程和符号解析。 --- ## 4. 架构改进建议 ### 4.1 短期改进 1. **代码生成与链接解耦** - 当前: codegen.c 中混合了 LLVM IR 生成和初始化 LLVM 目标 - 建议: 抽取 target_init() 到独立的 target.c, codegen.c 只负责 AST->IR 2. **错误类型统一** - 当前: ErrorInfo (单个错误) 和 ErrorList (错误列表) 存在于多个模块 - 建议: 统一为 CompilerError { phase, message, line, col, filename } 结构体 3. **测试框架扩展** - 当前: 手写断言宏, 缺少 codegen 层测试 - 建议: 新增 test_codegen.c, 测试 AST->LLVM IR 是否正确 ### 4.2 中期改进 4. **IR 中间表示层** - 当前: AST 直接生成 LLVM IR, 跳过了"自己设计 IR"这一步 - 建议: 在 AST 和 LLVM IR 之间加一层自定义 IR (三地址码或 SSA 形式) - 学习价值: 理解"IR 是编译器的核心抽象"这句话的真正含义 5. **解释器模式** - 建议: 在 codegen/ 旁加一个 interp/ 模块, 直接 walk AST 解释执行 - 好处: 快速验证语义正确性, 无需 LLVM 编译; 区分编译和解释模式 6. **调用约定抽象** - 当前: codegen 直接调 LLVMBuildCall2 - 建议: 抽一层 abi.h, 封装参数传递和返回值处理 ### 4.3 长期改进 7. **自举准备** - 用 L Language 写一个 L Language 编译器的最小实现 - 分阶段: 先让 L 输出 AST -> 再输出 LLVM IR -> 最终用 L 编译自己 8. **增量编译** - 缓存已编译模块的时间戳和符号表 - 只重新编译有改动的文件 --- ## 5. Rust 设计哲学清单: 哪些值得吸收 | Rust 特性 | L 是否该加 | 理由 | |-----------|-----------|------| | 默认不可变 (let vs let mut) | 是 | 编译期检查, 几乎零实现成本 | | 表达式 vs 语句 | 是 | if/match/block 可作为表达式 | | 模式匹配 | 是 | 最优雅的控制流之一 | | 代数数据类型 (enum + 带字段) | 是 | 类型安全的设计模式 | | Option/Result 错误处理 | 有条件 | 需要泛型 + enum, 实现门槛高 | | 所有权 / 借用 | 暂不加 | 需要借用检查器, 复杂度远超当前阶段 | | 生命周期标注 | 暂不加 | 所有权系统的伴生概念 | | trait 系统 | 暂不加 | 需要泛型 + 虚方法表, 实现成本高 | | 宏系统 | 暂不加 | 极其复杂, 自举后才考虑 | | 模块系统 | 可以加简单版 | 简单 mod + 文件包含即可 | --- ## 6. 推荐开发路线图 ### 短期 (下一个迭代) | 优先级 | 功能 | 预计工时 | |--------|------|---------| | P0 | let mut + 赋值语句 | 1 天 | | P0 | 字符串类型 + 字面量 + print_str | 1 天 | | P1 | 数组 + arr[i] 索引 | 2 天 | | P0 | for 循环 + range | 1 天 | ### 中期 (下下个迭代) | 优先级 | 功能 | 预计工时 | |--------|------|---------| | P1 | 结构体 | 2-3 天 | | P1 | 枚举 (C 风格) | 1 天 | | P2 | match 表达式 | 2-3 天 | | P1 | 类型别名 | 0.5 天 | ### 长期 | 优先级 | 功能 | 预计工时 | |--------|------|---------| | P2 | 自定义 IR 层 | 3-5 天 | | P3 | 泛型 | 5-7 天 | | P2 | 模块系统 | 3-5 天 | | P3 | trait / 接口 | 5-7 天 | | P3 | 自举尝试 | 数周 | --- ## 7. 技术债务与风险 | 问题 | 影响 | 建议 | |------|------|------| | codegen.c 中 malloc 混用 arena | 内存管理不一致 | 优先使用 arena, 或实现统一的代码生成器分配器 | | system("gcc ...") 链接 | 平台不兼容 | 封装 Linker 接口, 支持直接调用 ld/lld | | 无 codegen 层单元测试 | IR 正确性无保障 | 添加 test_codegen.c, 用 LLVMVerifyModule 验证 | | 所有变量用 alloca | 效率低 | LLVM opt -mem2reg 可以优化, 但当前没跑优化 pass | | 无 .codegraphignore | 索引可能包含无关文件 | 添加 .codegraphignore 排除 build/ | --- *本报告由 Codex 自动生成。下次运行: 2 小时后。*