抽象语法树为什么抽象

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树

一颗抽象语法树

我们已经自己压缩了一棵解析树,通过上面几个步骤的精简,可以总结一些解析树和抽象语法树的不同之处:

  1. AST不含有语法细节,比如冒号、括号、分号

  2. AST会压缩单继承节点

  3. 操作符会变成内部节点,不再会以叶子节点出现在树的末端。

有了抽象语法树,我们基于它可以建立清晰的代码描述,非常有利于后续阶段的修改、变换。

历史文章导读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值