



编译器使用AST来表示程序,因为它们经常需要问这样的问题:对于程序的给定部分,它有什么样的语言特性?它的子部分是什么?请观察规则(1.1)中左侧的示例程序和右侧的AST。这个程序是一个加法操作,它有两个子部分,一个输入操作和一个取负操作。取负还有一个子部分,即整数常数8。通过使用树来表示程序,我们可以很容易地沿下面的连接从程序的一个部分走到其他的子部分。
我们使用树的标准术语来描述AST:上面的每个矩形称为 节点 ;箭头将一个节点连接到它的 子节点 ,这些子节点也是节点。最上面的节点是 根节点 。除了根节点之外,每个节点都有一个 父节点 (当前节点是其子节点)。如果一个节点没有子节点,它就是 叶节点 ,否则为 内部节点 。
我们为每一种节点使用一个Python 类 。以下是Python的ast模块中常量(也就是字面值)的类定义:
整型常量节点只包含一个东西:整数值。要为整数8创建AST节点,将其写为Constant(8)。
我们说Constant(8)创建的值是Constant类的一个 实例 。
下面是一元操作符的类定义:
一元操作符的具体操作是由参数op来指定的。例如,类USub用于取负运算(更多一元操作符将在后面的章节中介绍)。要创建一个对数字8取负的AST,我们编写如下代码:
对input_int函数的调用由Call类和Name类来表示:
要创建一个调用input_int的AST节点,可以写成这样:
最后,为了表示规则(1.1)中的加法,我们使用BinOp类来表示二元操作符。
与一元操作符UnaryOp类似,二元操作符中的具体操作也由op参数指定,该参数目前只有Add类的一个实例。因此,要创建AST节点,为某些用户输入添加-8,我们编写以下代码:
要编译像规则(1.1)这样的程序,我们需要知道与根节点相关联的操作是加法,并且需要能够访问它的两个子节点。Python提供了模式匹配来支持这类查询,我们将在1.3节中看到。
我们经常需要写下程序的具体语法,即使实际中我们想到的是AST,这是因为具体语法更简洁。我们建议你始终将程序看作AST。