附录 A2:常见陷阱与调试办法

编译器项目最容易让人疲劳的地方,不是代码多,而是错误会在不同阶段以完全不同的样子出现。下面这些坑最常见。

A2.1 token 看起来差一点点,其实后面会全歪

词法分析阶段如果把关键字认成标识符,或者把注释没有跳干净,parser 后面的错误通常会非常绕。
所以一旦 lexer 出问题,优先回到 token 输出,不要直接往 parser 找。

A2.2 AST 能打印,不代表结构一定对

很多语法分析错误不会让程序立刻崩,而是让树“长歪”。
最典型的是表达式优先级不对,例如把 x + y * z 解析成 (x + y) * z。这类问题一定要盯树形层次,不要只看“是不是打印出一棵树了”。

A2.3 语义错误和语法错误不要混着查

如果一段程序根本没被正确解析成 AST,那你后面看到的“未定义变量”或“类型不匹配”信息都可能是次生错误。
顺序一定要守住:

  1. lexer
  2. parser / AST
  3. semantic
  4. codegen

A2.4 代码生成阶段最容易错的是栈和跳转

后端最常见的 bug 有两个来源:

  • 压栈 / 弹栈不对,导致寄存器值串了
  • 标签跳转不对,导致控制流跑飞

这两个问题都不要只靠肉眼扫大段汇编。先拿一个非常小的样例,把每条指令对应到 AST 的哪一段讲清楚,再继续扩展。

A2.5 QEMU 返回码怎么看

在这门课里,很多测试程序直接通过 return 返回一个整数。
如果你运行:

qemu-riscv32 ./test.elf
echo $?

第二条命令打印出来的就是退出码。
它通常比“程序有没有打印东西”更适合拿来验证编译结果。