有无相生,前后相随

本节阅读量:

前两章里的程序总是一路算到底:

1
(let x 40 (+ x 2))

第三章加入选择:

1
(if (eq? 0 0) 42 7)

这个程序先判断两个 0 是否相等。条件成立就走 then 分支,结果是 42;否则才走 else 分支。

本章最重要的一句话是:解释器运行 if 时,只求值条件选中的分支;编译器会生成两个分支,但编译后的程序也只执行其中一个。

程序的执行路线从这里分开,又在后面汇合,这就是本章要处理的控制流。至于编译器怎样表示这两条路线,等走到 IR 时再看。

在第二章的基础上

第二章已经跑通两条路径:

1
2
源码 -> AST -> 解释器 -> 整数结果
源码 -> AST -> IR -> 栈槽 -> 汇编

前两章已经支持的写法在第三章仍然有效,包括整数、负整数、加法、变量、let 和同名遮蔽(shadowing)。解释器仍然通过环境查找变量,编译器也沿用 IR 内部命名和栈槽分配。

因此,新语法可以直接和已有写法组合:

1
2
(let x 40
    (if (eq? x 0) 0 (+ x 2)))

这个程序的结果是 42

在语言这一层,第三章只增加两样东西:

1
2
eq?
if

代码变化在哪里

第三章仍然沿用原来的目录和接口:

1
2
3
4
5
front/ast.h/.cpp           增加 eq? 和 if 节点
front/parser.cpp           识别 eq? 和 if
interp/interpreter.cpp     只求值 if 选中的分支
compile/ir.h/.cpp          增加比较、branch、jump 和 label
compile/assembly.cpp       把新的 IR 操作翻译成比较和跳转

compile 仍然只输出 x86-64 System V ABI 的 AT&T 汇编:

1
AST -> IR -> assembly

它不会调用 cc,也不负责链接。

读完本章后

你应该理解:

  • 为什么 eq? 只返回 01
  • 为什么 if 只求值一个分支。
  • then 和 else 两条路径怎样在 end 汇合。
  • IR 中的 branch、jump 和 label 怎样变成 cmpqjejmp

2.8 练习

上一节

3.1 从顺序计算到条件选择

下一节