本文为译文,点击 此处查看原文。有关语言学中使用的树,请参见 具体语法树。
在计算机科学中,抽象语法树(AST),或简称语法树,是用编程语言编写的源代码的抽象语法结构的树表示。树的每个节点表示源代码中出现的一个构造。语法是“抽象的”,因为它并不代表真实语法中出现的每个细节,而只是结构上的、与内容相关的细节。例如,分组括号在树结构中是隐式的,像if-condition-then表达式这样的语法结构可以通过具有三个分支的单个节点来表示。
这将抽象语法树与具体语法树(传统上指定的解析树)区分开来,具体语法树通常由解析器在源代码翻译和编译过程中构建。一旦构建完成,通过后续处理(例如上下文分析)将附加信息添加到AST。
抽象语法树也用于程序分析和程序转换系统。
下面欧几里得算法的抽象语法树如下所示:
while b ≠ 0
if a > b
a := a − b
else
b := b − a
return a
1. 在编译器中的应用程序
抽象语法树
是编译器中广泛使用的表示程序代码结构的数据结构
。AST
通常是编译器语法分析
阶段的结果。它通常通过编译器需要的几个阶段作为程序的中间表示,并且对编译器的最终输出有很大的影响。
1.1 动机
AST
有几个属性可以帮助编译过程的进一步操作:
- 可以使用包含的每个元素的属性和注释等信息编辑和增强一个
AST
。对于一个程序的源代码,这样的编辑和注释是不可能的,因为这意味着要修改它。 - 与源代码相比,
AST
不包含不必要的标点和分隔符(大括号、分号、圆括号等)。 - 由于编译器要进行连续的分析,
AST
通常包含关于程序的额外信息。例如,它可以存储每个元素在源代码中的位置,从而允许编译器打印有用的错误消息。
由于编程语言及其文档的固有特性,需要使用ASTs
。语言通常是模棱两可的。为了避免这种歧义,通常将编程语言指定为上下文无关语法(CFG)
。然而,CFG
通常不能表达编程语言的某些方面,但它们是该语言的一部分,并在其规范中进行了记录。这些细节需要上下文来确定它们的有效性和行为。例如,如果一种语言允许声明新类型,那么CFG
既不能预测这些类型的名称,也不能预测它们的使用方式。即使一种语言有一组预定义的类型,执行正确的使用通常也需要一些上下文。另一个例子是duck typing,其中元素的类型可以根据上下文进行更改。操作符重载是根据上下文确定正确用法和最终函数的另一种情况。Java提供了一个很好的例子,其中“+”
运算符既是数字加法,又是字符串连接。
虽然编译器的内部工作涉及到其他数据结构,但是AST
执行一个唯一的函数。在第一个阶段,语法分析阶段,编译器生成一个解析树(parse tree)。这个解析树可以通过语法制导翻译(syntax-directed translation)来执行编译器的几乎所有功能。虽然这种方法可以导致更高效的编译器,但它违背了编写和维护程序的软件工程原则。AST
相对于解析树
的另一个优势是大小,特别是AST
的高度和元素的数量更少。
1.2 设计
AST的设计通常与编译器的设计及其预期的特性密切相关。
核心要求包括:
- 必须保留
变量类型
以及源代码中每个声明的位置
。 可执行语句
的顺序必须显式表示并定义良好。- 必须存储和正确标识
二进制操作
的左右部分。 - 标识符及其赋值必须存储为
赋值语句
。
这些需求可用于为AST设计数据结构。
有些操作总是需要两个元素,比如加法的两项。然而,一些语言构造需要任意数量的子元素,例如从命令shell传递给程序的参数列表。因此,用于表示用这种语言编写的代码的AST
也必须足够灵活,以允许快速添加未知数量的子元素。
AST
的另一个主要设计要求是应该能够将AST
解解析为源代码形式。所生成的源代码在重新编译时,外观应与原始代码足够相似,执行时应完全相同。
1.3 设计模式
由于AST
需求的复杂性和编译器的总体复杂性,应用合理的软件开发原则是有益的。其中之一是使用经过验证的设计模式来增强模块化和易于开发。
不同的操作不一定有不同的类型,因此拥有一个合理的节点类层次结构
非常重要。随着编译器的进展,这对于创建和修改AST
是至关重要的。
因为编译器会遍历树几次以确定语法正确性,所以将遍历树作为一个简单的操作非常重要。编译器在到达节点时执行一组特定的操作,这些操作取决于每个节点的类型,因此使用visitor模式通常是有意义的。
1.4 使用
AST
在语义分析
中得到了广泛的使用,在语义分析中,编译器检查程序和语言元素的正确用法。在语义分析过程中,编译器还根据AST
生成符号表。对树的完整遍历允许验证程序的正确性。
在验证正确性之后,AST
作为代码生成的基础。AST
通常用于生成用于代码生成中的中间表示(intermediate representation,IR),有时称为中间语言。
2. 另请参阅
- 抽象语义图(Abstract semantic graph,ASG),又称术语图
- 复合模式
- 控制流图
- 文档对象模型(Document Object Model,DOM)
- 扩展Backus-Naur形式
- Lisp是一组写在树中的语言,使用宏来操作代码树
- 解析树,也称为具体语法树
- 语义解析树(Semantic resolution tree,SRT)
- 调车场算法
- 符号表
- TreeDL
3. 参考文献
- 本文基于2008年11月1日前从免费在线计算词典中获取的材料,并纳入GFDL 1.3或更高版本的“重新授权”术语。
4. 进一步的阅读
- Jones, Joel。“抽象语法树实现习惯用法(Abstract Syntax Tree Implementation Idioms)”。(各种语言家族中AST实现概述)
- Neamtiu, Iulian; Foster, Jeffrey S.; Hicks, Michael(2005年5月17日)。使用抽象语法树匹配理解源代码演化(Understanding Source Code Evolution Using Abstract Syntax Tree Matching)。MSR’05. Saint Louis, Missouri: ACM. CiteSeerX 10.1.1.88.5815.
- Baxter, Ira D.; Yahin, Andrew; Moura, Leonardo; Sant’ Anna, Marcelo; Bier, Lorraine(1998年11月16日- 19日)。使用抽象语法树进行克隆检测(Clone Detection Using Abstract Syntax Trees)。Proceedings of ICSM’98. Bethesda, Maryland: IEEE.
- Fluri, Beat; Würsch, Michael; Pinzger, Martin; Gall, Harald C。更改提取:用于细粒度源代码更改提取的树差异(Change Distilling: Tree Differencing for Fine-Grained Source Code Change Extraction)。
- Würsch, Michael。改进基于抽象语法树的源代码变更检测(Improving Abstract Syntax Tree based Source Code Change Detection,毕业论文)。
- Falleri, Jean-Rémy; Morandat, Floréal; Blanc, Xavier; Martinez, Matias; Monperrus, Martin。细粒度和精确的源代码差异(Fine-grained and Accurate Source Code Differencing)。Proceedings of ASE 2014. doi:10.1145/2642937.2642982。
- Lucas, Jason。“对Visual c++抽象语法树的思考(Thoughts on the Visual C++ Abstract Syntax Tree)”