Skip to content

测试与验证

为什么这个项目要把测试讲清楚

这个项目不是只想证明“偶尔能跑一个示例”,而是想把编译器各阶段的行为尽量固定下来。

如果没有一层层测试,编译器项目很容易变成这样:

  • 你加了一个新特性
  • 某个旧行为悄悄变了
  • 最后只能抱着端到端错误往回倒推,猜到底是哪一层先出的岔子

所以测试在这个项目里不是可有可无的配件,它就是结构的一部分。少了它,你后面很多“我是不是改坏了什么”的问题都只能靠猜。

当前测试在验证什么

从现有测试和 TESTING.md 来看,当前测试重点覆盖:

  • Token 是否正确
  • AST 是否正确
  • 语义错误是否会提前暴露
  • IR 是否稳定
  • 后端输出结构是否合理
  • CLI 是否按预期处理参数
  • 端到端编译能否真正产出可执行文件

测试为什么要分层

编译器不是单函数程序,而是多阶段流水线。

分层测试的价值在于:

  • 词法错了,不需要等到后端才发现
  • AST 结构错了,不需要靠最终二进制行为猜
  • IR 错了,可以直接看 lowering 结果

这样一来,出问题时你不用从整条链路里盲找,范围会小很多。

当前测试层次怎么理解

1. 词法测试

验证:

  • 关键字
  • 标识符
  • 整数
  • 注释
  • 空白
  • 运算符
  • 非法字符处理

2. 语法测试

验证:

  • 表达式优先级
  • 函数定义
  • 声明
  • 赋值
  • 控制流
  • 函数调用

3. 语义测试

验证:

  • 重复定义
  • 未定义符号
  • 参数数量不匹配
  • 非法赋值目标

4. IR 测试

验证:

  • 条件分支 lowering
  • 循环 lowering
  • 函数调用与返回
  • 标签与跳转结构

5. 后端测试

验证:

  • 生成代码中的关键结构是否存在
  • 标签、局部变量、临时变量是否合理
  • 返回路径是否完整

6. CLI 测试

验证:

  • 参数解析
  • --emit-* 调试输出
  • 基本命令行行为

7. 端到端测试

验证:

  • 能否真正生成可执行文件
  • 运行结果是否符合预期

端到端测试为什么依赖系统工具链

因为这个项目最终要调用 clangcc 生成可执行文件。

所以端到端测试会先看看本机有没有可用工具链。要是没有,它一般会把需要真生成二进制的那部分跳过去,而不是气急败坏地把整套测试全判死刑。

建议怎么跑测试

最简单的完整命令是:

python3 -m unittest discover -s tests -v

如果你只是在改某一层,也别每次都上来全量跑到底,先盯自己动到的那一块更省劲:

  • 改 Lexer,先看 Token 相关测试
  • 改 Parser,先看 AST 和语法测试
  • 改 IRBuilder,重点看 IR 与端到端行为
  • 改后端,重点看生成代码和端到端测试

文档和测试应该一起看

这个项目特别适合“文档和测试对照看”:

这样你会更容易感觉到,这个项目不是嘴上说“这一层应该怎样”,而是真的拿测试把这些边界一根一根钉住了。文档负责告诉你它想干什么,测试负责盯着它别跑偏。