测试与验证¶
为什么这个项目要把测试讲清楚¶
这个项目不是只想证明“偶尔能跑一个示例”,而是想把编译器各阶段的行为尽量固定下来。
如果没有一层层测试,编译器项目很容易变成这样:
- 你加了一个新特性
- 某个旧行为悄悄变了
- 最后只能抱着端到端错误往回倒推,猜到底是哪一层先出的岔子
所以测试在这个项目里不是可有可无的配件,它就是结构的一部分。少了它,你后面很多“我是不是改坏了什么”的问题都只能靠猜。
当前测试在验证什么¶
从现有测试和 TESTING.md 来看,当前测试重点覆盖:
- Token 是否正确
- AST 是否正确
- 语义错误是否会提前暴露
- IR 是否稳定
- 后端输出结构是否合理
- CLI 是否按预期处理参数
- 端到端编译能否真正产出可执行文件
测试为什么要分层¶
编译器不是单函数程序,而是多阶段流水线。
分层测试的价值在于:
- 词法错了,不需要等到后端才发现
- AST 结构错了,不需要靠最终二进制行为猜
- IR 错了,可以直接看 lowering 结果
这样一来,出问题时你不用从整条链路里盲找,范围会小很多。
当前测试层次怎么理解¶
1. 词法测试¶
验证:
- 关键字
- 标识符
- 整数
- 注释
- 空白
- 运算符
- 非法字符处理
2. 语法测试¶
验证:
- 表达式优先级
- 函数定义
- 声明
- 赋值
- 控制流
- 函数调用
3. 语义测试¶
验证:
- 重复定义
- 未定义符号
- 参数数量不匹配
- 非法赋值目标
4. IR 测试¶
验证:
- 条件分支 lowering
- 循环 lowering
- 函数调用与返回
- 标签与跳转结构
5. 后端测试¶
验证:
- 生成代码中的关键结构是否存在
- 标签、局部变量、临时变量是否合理
- 返回路径是否完整
6. CLI 测试¶
验证:
- 参数解析
--emit-*调试输出- 基本命令行行为
7. 端到端测试¶
验证:
- 能否真正生成可执行文件
- 运行结果是否符合预期
端到端测试为什么依赖系统工具链¶
因为这个项目最终要调用 clang 或 cc 生成可执行文件。
所以端到端测试会先看看本机有没有可用工具链。要是没有,它一般会把需要真生成二进制的那部分跳过去,而不是气急败坏地把整套测试全判死刑。
建议怎么跑测试¶
最简单的完整命令是:
如果你只是在改某一层,也别每次都上来全量跑到底,先盯自己动到的那一块更省劲:
- 改 Lexer,先看 Token 相关测试
- 改 Parser,先看 AST 和语法测试
- 改 IRBuilder,重点看 IR 与端到端行为
- 改后端,重点看生成代码和端到端测试
文档和测试应该一起看¶
这个项目特别适合“文档和测试对照看”:
- 看完 词法分析与 Token,就去看对应词法测试
- 看完 语法分析与 AST,就去看解析与 AST 测试
- 看完 IR 与 Lowering,就去看 IR 测试
这样你会更容易感觉到,这个项目不是嘴上说“这一层应该怎样”,而是真的拿测试把这些边界一根一根钉住了。文档负责告诉你它想干什么,测试负责盯着它别跑偏。