购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.1
抽象语法树

编译器使用抽象语法树来表示程序,因为它们经常需要问这样的问题:对于程序的给定部分,它具有什么样的语言特性?它的子部分是什么?考虑(1.1)中左边的程序和右边的AST。这个程序是一个加法操作,它有两个子部分,一个读操作和一个取负操作。负号还有一个子部分,即整型常数8。通过使用树来表示程序,我们可以很容易地按照图中的连接从程序的一个部分走到它的子部分。

我们使用树的标准术语来描述AST:上面的每个矩形称为节点;箭头将一个 节点 连接到它的 子节点 ,这些子节点也是节点。最上面的节点是 根节点 。除了根节点之外,每个节点都有一个 父节点 (其子节点就是当前节点)。如果一个节点没有子节点,它就是 叶节点 ,否则为 内部节点

我们为每种类型的节点定义了一个Racket结构体。在本章中,我们只需要两种节点:一种用于整型常量(也就是字面值),另一种用于基本操作。下面是整型常量的结构体定义

整型节点只包含一个东西:整数值。我们建立了结构体名称(如Int)首字母大写的约定。要为整数8创建AST节点,我们写入(Int 8)。

我们说(Int 8)创建的值是Int结构体的一个 实例

下面是原始操作的结构体定义:

原始操作节点包括一个操作符符号op和称为args的子参数列表。例如,要创建一个对数字8取负的AST,我们编写如下代码。

原始操作可以有零个或多个子操作节点。read操作符有零个子节点:

加法运算符有两个子运算节点:

我们对Prim结构体进行了设计选择。我们可以为所有操作定义一个结构体,而不是为许多不同的操作(read、+和-)各自使用一个结构体,如下所示:

我们选择只使用一个结构的理由是,编译器的许多部分可以对不同的基本操作符使用相同的代码,因此我们不妨通过使用单个结构体编写一次代码。

要编译像前文(1.1)这样的程序,我们需要知道与根节点相关联的操作是加法,并且还需要能够访问它的两个子节点。正如我们将在1.3节看到的,Racket提供了模式匹配来支持这类查询。

即使我们实际上想到的是AST,我们也经常写下程序的具体语法,因为具体语法更简洁。我们建议你始终将程序视为抽象语法树。 gQneEuJMxa420zSRysA0JeoPmK4Am1xRkR161Q8jzWN+AnGwswxpz9Il3kGv3K0z

点击中间区域
呼出菜单
上一章
目录
下一章
×