AST:保存变量和绑定结构
本节阅读量:上一节说清楚了第二章的规则:
|
|
第一章已经有两个 AST 节点:
|
|
第二章增加两个节点:
|
|
AST 的作用仍然没变:
|
|
ExprKind 增加两类
代码在:
|
|
先看表达式分类:
|
|
解释器和编译器拿到一个通用的 Expr 时,会先看 kind,再用 switch 选择处理规则。
这和第一章一样,只是分类表变长了:
|
|
VarExpr 只保存名字
变量引用的节点很小:
|
|
例如源码:
|
|
对应 AST:
|
|
注意,VarExpr 里没有保存 x 的值。AST 只是结构,不是求值结果。
x 到底是多少,要等后面处理:
|
|
LetExpr 保存名字、value 和 body
let 节点保存三件事:
|
|
例如:
|
|
对应:
|
|
里面三部分是:
|
|
这个结构还保留了作用域边界:name 建立的新绑定只对 body 子树生效,不对 value 子树生效。AST 本身暂时不会把 Var(x) 直接连到某个 Let,但解释器和 lowerer 可以沿着这棵树,在进入 body 时扩展各自的环境。
value 和 body 都是 unique_ptr<Expr>,因为它们可以是任意表达式:
|
|
这里的 value 是一个加法表达式:
|
|
为什么不直接替换字符串
看到:
|
|
你可能会想:parser 能不能直接把 body 里的 x 替换成 40?
不要这样做。parser 不知道求值规则,也不应该修改含义。
shadowing 会让字符串替换马上出错:
|
|
这段程序里有两个 x 绑定:
|
|
内层 body 里的 x 应该指向 32,最后那个 x 应该指向 10。
所以 AST 只保留结构:
|
|
哪个 Var(x) 对应哪个绑定,留给解释器和 lowerer 通过各自的环境判断。parser 只负责保留名字和嵌套结构,不负责解析名字最终指向哪里。
观察 AST
运行:
|
|
输出:
|
|
再运行:
|
|
输出:
|
|
这说明 parser 已经保留了两个同名绑定的结构。下一节看 parser 怎样读出这些节点。
本节目录