本章总结

本节阅读量:

第二章在第一章的整数和加法之外,加入了两种表达式:

1
2
变量引用
(let name value body)

程序从此不只是在计算数字,也开始处理名字和作用域。

前端为此增加了 VarExprLetExpr。parser 看到 identifier 时生成变量节点,看到 let 时读出名字、value 和 body。AST 只保存结构,不在 parser 里查找或替换变量。

解释器增加了 environment:

1
用户变量名 -> 运行时值

解释 let 时,先在旧环境中计算 value,再加入新的绑定并计算 body,最后移除这个绑定。查找变量时从后往前找,因此更近的同名绑定会暂时遮住外层绑定。

编译器也有环境,但保存的内容不同:

1
用户变量名 -> IR 操作数

每个 let 绑定都会得到一个新的内部名字。即使源码里有两个同名的 x,lowering 后也可以变成 x0x1,不会混在一起。

为了表示一次绑定,IR 新增了 copy

1
x0 = 40

汇编生成器沿用第一章的栈槽分配和固定栈帧,只增加一个 copy 分支,把操作数保存到目标栈槽。

这一章新增的两条路径可以写成:

1
2
变量名 -> 解释器 environment -> 运行时值
变量名 -> lowerer environment -> IR 内部名字 -> 栈槽

它们实现的是同一套语言语义:

1
2
3
4
let 的 value 在旧环境中求值
let 的 body 在加入新绑定后的环境中求值
同名时,更近的绑定优先
离开作用域后,外层绑定重新可见

下一章会加入判断和 if。那时新的问题不再只是“名字指向哪里”,还包括“程序应该选择哪一条分支”。


2.6 汇编:为 copy 增加翻译规则

上一节

2.8 练习

下一节


本节目录