Appearance
编译器流水线
实现语言选择
推荐使用 Rust 实现 Qlang 工具链。不是因为“Rust 最潮”,而是因为它在这件事上工程收益最高:
- 内存安全和并发安全更适合长期维护大型编译器
- 生态中已有较成熟的 parsing、diagnostic、arena、interner、LSP、LLVM 绑定方案
- 对构建 CLI、增量缓存和测试工具也足够友好
总体流水线
text
source
-> lexer
-> parser
-> AST
-> name resolution
-> HIR
-> type / trait / effect checking
-> MIR
-> ownership / borrow / escape analysis
-> monomorphization
-> LLVM IR
-> object / library / executable分层原则
AST
- 保留接近源码的结构
- 方便格式化器和语法级诊断
- 不承载复杂语义
item/type/stmt/pattern/expr节点都应直接携带 span,避免后续 diagnostics 与 LSP 反向逼迫 AST 重构- 控制流头部表达式与普通表达式要允许有不同解析约束,例如
if/while/for/match头部不能被结构体字面量歧义污染
当前前端实现已经按职责拆开 parser 入口:
item负责顶层声明expr负责表达式和 postfix 规则pattern负责绑定与match模式stmt负责 block 和控制流语句- 函数签名抽象可同时服务普通函数、trait item 和 extern item
这个边界要继续保持,避免未来把 parser 重新写回一个巨型文件。
HIR
- 解析名称、作用域和类型引用
- 作为 LSP 语义查询的重要基础
- 对上层工具最友好
MIR
- 明确控制流和所有权动作
- 适合 borrow、escape、drop、effect lowering
- 适合后续优化和代码生成准备
这种分层能避免“一套 AST 硬扛一切”导致的后期崩塌。
查询系统
编译器、LSP 和文档生成都应该建立在统一查询系统上。推荐引入增量查询架构,核心思想:
- 每个分析结果都是可缓存 query
- 输入变更后只重新计算受影响节点
- 编译器命令行与 LSP 共用数据库
这样做的好处非常直接:
ql check和qlsp不会各自维护一套语义逻辑- IDE 响应速度更可控
- 后续重构和诊断增强成本更低
诊断架构
诊断系统应单独成层,而不是散落在各模块里。建议包含:
- span 与文件映射
- 错误码
- 主错误与附加说明
- fix-it 建议
- 机器可读 JSON 输出
LLVM 后端边界
不要让 LLVM 污染整个编译器架构。建议:
- 语义分析和中间表示与 LLVM 解耦
- LLVM 只在 codegen 层出现
- 为未来可能的解释器、WASM 或自定义后端保留空间
测试策略
编译器项目不能只靠单元测试。必须同时具备:
- parser fixture / snapshot tests
- typecheck tests
- UI diagnostics tests
- codegen golden tests
- 运行时集成测试
- FFI 集成测试
这也是目录结构设计要前置考虑的原因。