抽象语法树为什么抽象
从具体到抽象
Abstract Syntax Tree抽象语法树(通常被简写成AST)实际上只是一个解析树(parse tree)的一个精简版本。在编译器设计的语境中,"AST" 和 "语法树"(syntax tree)是可以互换的。
什么是解析树呢?我们知道一棵解析树是包含代码所有语法信息的树型结构,它是代码的直接翻译。所以解析树,也被成为具象语法树(Concret Syntax Tree, 简称CST);而抽象语法树,忽略了一些解析树包含的一些语法信息,剥离掉一些不重要的细节,所以它看起并不像解析树那么事无巨细,这也是AST名字中抽象一词的由来。
在继续下一步之前,我们先统一一下文中的概念表达形式,以便更好的理解内容
解析树 = Parse Tree = CST 抽象语法树 = Syntax Tree = AST
我们先分析一段简短的代码js代码:5 + (1 x 12)
, 回忆一下编译器的工作过程
词法分析
编译的第一个阶段是扫描源代码文本,scanner会从左到右扫描文本,把文本拆成一些单词。然后,这些单词传入分词器,经过一系列的识别器(关键字识别器、标识符识别器、常量识别器、操作符识别器等),确定这些单词的词性,这一过程的产物是token序列。token在机内一般用<type, value>形似的二元组来表示,type表示一个单词种类,value为属性值,比如var这个单词,在js语言里是一个关键字,一种语言的关键字集合是事先可以确定的,所以它的type本身就可表示这个关键字,不再需要属性值, 用二元组表示就是<VAR, ->;再看我们的示例5 + (1 x 12)
中, 12
也是其中的一个单词, 它实际上是一个常量,用二元组表示就是<CONST, 12>。分词和所使用的语言种类密切相关,分解后的token序列为5, +, (, 1, x, 12, )
。
<CONST, 5>
<OPT, +>
<SLP, ->
<CONST, 1>
<OPT, *>
<CONST, 12>
<RLP, ->
scanner和分词器所做的工作,构成了编译器的词法分析阶段。
语法分析
分词阶段完成以后,token序列会经过我们的解析器,由解析器识别出代码中的各类短语,会根据语言的文法规则(rules of grammar)输出解析树,这棵树是对代码的树形描述。文法是什么呢?想想我们学英语的过程中,老师是如何教我们划分句子解构的,比如一个简单的英文自然语言例子:
Little girl ate apple
它由【名词短语】和【动词短语】组成, 再往下【名词短语】由【形容词】和【名词构成】,【动词短语】由【动词】和【名词短语】构成。【动词】和【名词】又可以由具体的单词构成。
我们会觉得语言描述冗长,而且并不直观,可以借助一些符号进行描述:
<句子> -> <名词短语><动词短语>
<名词短语> -> <形容词><名词>
<动词短语> -> <动词> <名词短语>
<形容词> -> little
<名词> -> girl | apple
<动词> -> ate
用<>包裹起来的部分称为语法规则,未用<>包括起来的部分(如little、girl等),就是该语言的基本符号。
用更抽象的形式化语言定义,文法可表示为:
- T表示终结符的集合(如little、girl等,即词法分析中提到的token)
- N表示非终结符的集合(如<>里包括的部分,表示了语法成分, 因为它们可以推导出其他句子成分,所以称为非终结符)
- P表示产生式集合(上面分析英语句子的每一条规则都是一个产生式,如<动词短语> -> <动词> <名词短语>, 就是一个产生式)
- S表示开始符号(S属于N的子元素,是一个特殊的非终结符)
可以看出,文法用简单的符号解决了无穷语言的有穷表述问题。
那么解析树具体长什么样呢?
2 + (12 * 1)
根据对应的文法生成的解析树
解析树
你可能会非常疑惑为什么会有EXP->1这种形式的存在,是不是感觉非常冗余?
我们来从文法的角度来解释这个问题,2 + (12 * 1)
是一个四则运算的表达式,根据我们小学学过的内容,大家都会很轻易的知道它的运算规则,那我们可以根据前面提到的文法规则公式,用形式语言把这些规则简写出来:
// 每条产生式前面的序号只为了更好的在下文引用,并不是产生式的一部分
1) E -> E + E
2) E -> E * E
3) E -> (E)
4) E -> number
你很快会发现,上图的分析树就是根据这些规则生成的,而EXP->1这种形式正是第四条产生式的一个应用。
总结一下解析树的一些性质
- 解析树的根节点为文法开始符号
- 解析树内部节点表示一个产生式的应用
- 叶节点既可以是非终结符也可以是终结符。从左到右的叶节点得到的符号串成为这颗树的产出(yield)。
精简一棵解析树
我们现在知道具象语法树和抽象语法树的概念,而且知道AST是CST的精简版本,那么AST它是如何生成的呢?
我们现在知道,根据文法规则生成的解析树会非常冗余。我们有过疑问,EXP->1这种结点,是不是看上去有点冗余?我们把这种结点叫做单继承节点,实际上我们并不会关心EXP是什么,只会关心继承它的那个值,这里即1。
压缩单继承节点
另外,我们发现括号似乎也是冗余的,可以隐藏在树的结构中。
去掉括号
甚至,我们可以看到,蓝色方框中的内部结点也不含有关键信息,可以用操作符号(在这里是 + 和 *)把它们替换掉。
将操作符压进内部节点
继续把冗余的层修剪掉,我们可以得到一颗AST树
一颗抽象语法树
我们已经自己压缩了一棵解析树,通过上面几个步骤的精简,可以总结一些解析树和抽象语法树的不同之处:
- AST不含有语法细节,比如冒号、括号、分号
- AST会压缩单继承节点
- 操作符会变成内部节点,不再会以叶子节点出现在树的末端。
有了抽象语法树,我们基于它可以建立清晰的代码描述,非常有利于后续阶段的修改、变换。
- HDU 2186 悼念512汶川大地震遇难同胞——一定要记住我爱你
- Hadoop数据分析平台实战——150Flume介绍离线数据分析平台实战——150Flume介绍
- Codeforces 714A Meeting of Old Friends
- Code forces 719A Vitya in the Countryside
- Hadoop数据分析平台实战——190Highcharts介绍离线数据分析平台实战——190Highcharts介绍
- HUST 1555 A Math Homework
- HUST 1541 Student’s question
- HDU 3785 寻找大富翁
- Hadoop数据分析平台实战——250JSSDK数据收集引擎编写离线数据分析平台实战——250JSSDK数据收集引擎编写
- HDU 2564 词组缩写
- 约瑟夫问题方法总结
- 欧里几德及扩展欧里几德算法
- Hadoop数据分析平台实战——260用户数据ETL离线数据分析平台实战——260用户数据ETL
- # Hadoop离线数据分析平台实战——230项目数据存储结构设计Hadoop离线数据分析平台实战——230项目数据存储结构设计
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- GitLab安装与基础使用
- apt命令使用一览表
- Hexo博客框架初识入门到进阶.md
- dubbo SPI 主要配置技术解读
- 三维城市建模之开发视角,10万场景资源+用户样例
- 3分钟短文:Laravel应用跟用户打交道,就从拿到他们的数据开始!
- leetcode之字符串压缩
- Android如何获取屏幕、状态栏及标题栏的高度详解
- Android中FloatingActionButton的显示与隐藏示例
- Android 中RecyclerView顶部刷新实现详解
- Android开发实现图片平移、缩放、倒影及旋转功能的方法
- Grafana + InfluxDB 实现 Jmeter 压测的图形化监控
- Android 浮动编辑框的具体实现代码
- Android实现Path平滑的涂鸦效果实例
- Android CameraManager类详解