编译技术教程:抽象语法树
系列导航: 《编译技术教程:前言》 《编译技术教程:词法分析》 《编译技术教程:抽象语法树》 《编译技术教程:语法分析》 《编译技术教程:符号表与类型系统》 《编译技术教程:语义分析》 《编译技术教程:附录 - 项目测试》 在词法分析阶段,我们将字符序列解析为单词序列,其中每一个单词都是一个最小的语法单元。词法分析的分析结果是非结构化的序列,无法表达程序的复杂语法结构。因此我们还需进行语义分析,用给定的上下文无关文法识别单词序列。而语义分析的最终结果,就是抽象语法树。 一、语法树的表示方法 文法反映了句子中不同结构之间的组成关系。例如下面的文法: VarDecl: BType Ident Dims [ '=' InitVals ] 便反映了变量定义由类型、标识符、维度以及可选的初始化值组成。 而对于组成这一概念,十分常见的建模方法便是将整体定义为类,而将各子部分定义为其属性。因此对于上面的文法,可以将其建模为如下形式: struct VarDecl { BType btype; Ident ident; Dims dims; InitVals *init_vals; }; 同理,我们也可以进一步定义 BType、Ident、Dims 以及 InitVals。如此继续下去,我们便可以对文法中的任意符号定义对应的类型,从而实现对源程序的结构化表示。我们所定义的这一系列类型,实际上也就是文法所对应的语法树中的可能节点。 在定义节点的时候,需要注意某一非终结符对应多条规则的情况。例如下面的规则: Stmt: 'if' '(' Cond ')' Stmt [ 'else' Stmt ] | 'while' '(' Cond ')' Stmt 这种情况下我们会发现,if 语句和 while 语句的结构并不相同,然而不管是 if 语句还是 while 语句,它们都是语句。 对是(is-a)关系的一种很容易想到的建模方法便是继承。因此,我们可以用如下方法表示 Stmt 符号对应的两条规则: struct Stmt {}; struct IfStmt : public Stmt {}; struct WhileStmt : public Stmt {}; 然而,继承不仅意味着变量多态,还意味着函数多态,可是在语法树的表示中,我们实际上只是希望用同一个名称指代不同的数据类型,而并不需要同一个函数拥有不同行为。因此,继承在此处似乎太 “重” 了。...