【编译原理】复习笔记(二)自顶向下的语法分析

885 阅读6分钟

本文将进入语法分析的知识点回顾,并通过例题将知识点串起来。大纲参见旁栏目录。同时,本文部分表达会比较口语化、而且逻辑顺序会和教程有出入(因为是学完之后的回顾梳理:某个问题该如何解决?因此引入了什么概念? 而不是有一个什么概念,它用来解决什么问题),望读者知悉。

自顶向下的语法分析

从直观上看,自顶向下的语法分析就是从给定文法的开始符号出发,一步步推导出与给定的目标字符串完全匹配的字符串。

从语法树的角度看,就是从语法树的根S开始,采用从左到右或从右到左的方式,最终生成所有的叶子结点。

比如下面这个例子:

image.png

image.png

消除左递归

左递归指的是形如

image.png 格式的。

为什么需要消除左递归?因为在产生式的左部会不断生成新的字符,和从一个非终结符出发,可以向右推出表达式的理念是相悖的。

消除直接左递归

最直接的方法是改成与之等价的右递归: image.png 通过观察上述文法得知,它会生成以α结尾,中间可以有多个α,而最终会以β作为开头的式子。

因此,可以等价写为: image.png

注意,这里的β不一定是终结符,可以把它看做是一个项,它可以被替换为别的形式,但实质不变。比如下面这个例子:

image.png

再比如,前面的例题消除了左递归后的产生式为:

image.png

消除间接左递归

基本思路是先将其转化为直接左递归,再按直接左递归的方法处理。

那么该如何转化为直接左递归呢?答案是对所有非终结符排序。来看一个例子:

image.png

消除回溯

为什么会产生回溯?原因是出现了形如S->aA|aB的表达,因为有多条路径可供选择,识别时先选择其中一条,但一旦走不通,又得重新回到上一步选择另一条路径重新识别,这样极大地浪费了时间。

相关概念

为了消除回溯,引入了以下几个概念:

FIRST()

FIRST(α):可以从α推导出的所有以终结符开始的串第一个终结符构成的集合。(如果α→ε,那么ε也在FIRST(α)中)

来看一个例子:

image.png

S的FIRST集不是{d},因为S这条产生式并不是以终结符开头,因此终结符d不能被用上;但可以使用推导出的结果(也就是不一定是直接得出),因此再使用A这条产生式,可以得到a和c两个元素。

消除回溯可使FIRST(α)∩FIRST(β)=∅。在实际操作层面,可以采用提取左公因子法。

FOLLOW()

FOLLOW(α)可直观理解为跟在α右部的第一个终结符。

因此,就有了它的最常用的寻找方式:寻找出现在产生式右部α右边的元素的FIRST集中的非空元素。以下是一个例子:

image.png

想必读者一定也注意到了这个问法中出现的两点特殊情况:

  • 如何求开始符号S的FOLLOW集?
  • 对于形如B->dB这样的B没有后接元素的产生式该如何处理?

于是可以引出如下定义:

  • {#}∈FOLLOW(S)
  • 若A->dB,则FOLLOW(A)中的元素∈FOLLOW(B)

(推导一下串的生成过程就明白了)

这里因为B->dB比较特殊,所以又回到了求B的FOLLOW集上,依然得借助第一条产生式。读者可以自行验证形如A->dB的普遍情况。

注意:有时形式不一定是A->dB,而是A->dBC,但C的FIRST集含有空元素,这时候一样按照A->dB处理,令FOLLOW(A)中的元素属于FOLLOW(B)。

SELECT()

SELECT()的定义很简单:

image.png

它表示的是选用某一产生式进行推导的可选符号集。

LL(1)文法定义

这里介绍一类具有如下特征的文法:从左到右扫描字符串(left),采用最左推导(left),每次只需向前查看(1)个符号,这类文法被称为LL(1)文法。

以下是一些等价的定义:

  • 不含左递归和回溯
  • FIRST(α)∩FIRST(β)=∅;对文法中的每个非终结符A,若它存在某个候选FIRST集包含eson(一个类似E的符号),则FIRST(αi)∩FOLLOW(A)=ф, i=1,2,....n
  • SELECT(A→α) ∩ SELECT(A→ β)=Ф ;

分析过程

接下来我们再看上述例题的第三问:

image.png

构造FIRST集和FOLLOW集

解题第一步,先找出这些非终结符对应集合的元素: image.png

image.png

image.png 所以它是LL(1)文法。

构造预测分析表

预测分析表的格式如下图所示:

image.png

行索引是产生式中的非终结符,列索引是产生式中的终结符。

关于表格中元素的具体含义,以第一行第一列为例,表示由行元素S推导出列元素a是由S->a这条产生式得出

但是往往有的结果并不是直接推出,也无法直观看出。为了防止遗漏,有以下的算法可以确保没有遗漏:

image.png

它表示的是:对于产生式A->α,

  • 若α不为空,则将产生式A->α放入A所在行、FIRST(α)中的元素所在的列中;
  • 若α为空,则将产生式A->α放入A所在行、FOLLOW(A)中的元素所在的列中

全部完毕后,其他还没有被标注的则是出错。

列出分析过程(对应元素:stack input expression)

image.png

匹配规则如下:

  • 若栈顶元素和输入串首字符相同,则匹配成功,消去。
  • 若栈顶元素是非终结符,输入串首字符是非终结符,则按照分析表中的匹配规则替换栈顶元素。

若到最后栈中没有元素,说明该串符合文法G。

注意事项:产生式右部的元素进栈时的顺序要和原式相反:比如S->(T),进栈替换后栈元素为#)T(。这是由栈先进后出的特性决定的,后进去的(被先匹配。

image.png

写出递归下降子程序

这一部分只是一个硬模拟的过程,对照着预测分析表把结果一条条列出即可。以E为例: image.png

结语

至此,本文已经涵盖了自顶向下语法分析方法最核心的几个知识点。也感谢你能看到这里!