【编译原理】Up-Down-算符优先分析

宏观自下而上分析.

所谓自下而上分析,就是从输入串开始,逐步进行"归约",直至归约到文法的开始符号;或者说,从语法树的末端开始,步步向上"归约",直到根结点。我们所讨论的自下而上分析法是一种"移进-归约"法,这种方法的大意是,用一个寄存符号的FILO栈,把输入符号一个一个地移入栈中,当栈顶形成某个产生式的候选式时,就把栈顶这一部分替换为(归约为)该产生式的左部符号。我们权把栈顶上的这一串符号称为"可归约串"。而精确定义"可归约串"这个概念就是自下而上分析的基本问题,也是各种不同的自下而上分析的区别所在。在算符优先分析中,我们用"最左素短语"来刻画"可归约串";在规范归约分析中,我们用"句柄"来刻画"可归约串"。自下而上分析的中心问题是,怎样判断栈顶符号的可归约性,以及如何进行归约。这是在算符优先分析以及后续的LR分析中要讨论的问题。但各种不同的自下而上分析法都有着一个共同的特点:边移进输入单词符号(压入符号栈),边进行归约。也就是在从左到右移进输入串的过程中,一旦发现栈顶呈现可归约串就立即进行归约。这个过程对于编译实现是一个十分自然的过程,因为我们不能设想把整个源程序都输入完以后,才开始对它进行归约。

算符优先分析简述.

算符优先分析相较于我们后续要介绍的LR分析法,它更加简单直观、分析效率也高,特别有利于进行表达式分析,适合于手工实现。有着这些优点的同时,算符优先分析也付出了一些代价。它能够分析的文法比LR分析的范围小得多,因为算符优先分析需要对文法做出限制,这就意味着某种程度上,算符优先分析的能力要比LR分析弱。
所谓算符优先分析,就是定义算符(准确地说,是终结符V T _T )之间的某种优先关系,借助于这种优先关系来寻找"可归约串"以及后续进行归约。两个可能相继出现的终结符a和b的优先关系可能有三种:

  • a<b:a的优先级低于b
  • a=b:a的优先级等于b
  • a>b:a的优先级高于b

需要注意的是,这三个关系并不是数值比较中的大于、小于、等于,例如a<b不意味着b>a,a=b也不意味着b=a.
一个文法,如果他的所有产生式的有部,都不含有两个相继的非终结符,即不含有形如A→…PQ…这样的产生式,我们就称该文法为算符文法。而如果G是一个不含空产生式的算符文法,并且对于该文法中的任意一对终结符a、b,我们规定它们的大小关系规则如下:

  1. a=b,当且仅当文法G中含有形如A→…ab…或A→…aPb的产生式;
  2. a<b,当且仅当文法G中含有形如A→…aR…的产生式,并且R⇒b…或R⇒Qb…;
  3. a>b,当且仅当文法G中含有形如A→…Rb…的产生式,并且R⇒…a或R⇒…aQ。

如果一个算符文法G中的任何终结符二元组<a,b>至多只满足上述三种关系之一,那么我们称G是一个算符优先文法。

算符优先分析表.

通过检查一个算符优先文法G的产生式,我们可以找出所有满足a=b的终结符二元组。而对于满足<和>的终结符二元组,我们需要对文法G的每个非终结符X构造两个集合:FIRSTVT§和LASTVT§.有了这两个集合之后,我们就可以通过检查产生式来确定满足<和>的终结符二元组。

  • 若有一个产生式形如A→…aP…,那么对于∀b∈FIRSTVT(P)都有a<b;
  • 若有一个产生式形如A→…Pb…,那么对于∀a∈LASTVT(P)都有a>b.

有了这两条规则,我们只需要计算出FIRSTVT集合与LASTVT集合,就能够构造出一个文法G的算符优先分析表。关于这两个集合的计算方法,我们给出如下规则:

  1. 若有产生式P→a…或P→Qa…,则有a∈FIRSTVT(P)
  2. 若有产生式P→Q…,则对于∀a∈FIRSTVT(Q),都有a∈FIRSTVT(P)
  3. 若有产生式P→…a或P→…aQ,则有a∈LASTVT(P)
  4. 若有产生式P→…Q,则对于∀a∈LASTVT(Q),都有a∈LASTVT(P).

至此,我们已经可以开始构造一个算符优先文法G的分析表,我们以下述文法为例,来叙述构造过程:

【下图引用自中南大学徐德智老师的编译原理2020年授课PPT】

在这里插入图片描述
首先第一步,构造文法G[E]中每一个非终结符的FIRSTVT集合与LASTVT集合。最终计算出的集合如下图所示,不折不扣地依赖于我们上面给出的计算规则:

【下图引用自中南大学徐德智老师的编译原理2020年授课PPT】

在这里插入图片描述
在这之后,我们就可以着手填写算符优先文法G[E]的分析表了,填写的依据就是我们前面给出的关系确定规则以及上面计算出的FIRSTVT和LASTVT集合。最终填写完成的分析表如下图所示:

【下图引用自中南大学徐德智老师的编译原理2020年授课PPT】

在这里插入图片描述
唯一需要注意的就是关于#符号,它是一个特殊的用于标识句子结束的终结符,关于它的优先级判定,我们只需要对文法进行拓展。针对我们举例使用的文法G[E]来说,就只需要加入产生式E’→#E#,后续的优先级判定依旧满足我们给出的规则。

算符分析过程.

在叙述具体的算符优先分析法的执行过程之前,我们需要给出算符分析过程中用于刻画"可归约串"概念的"最左素短语"。

素短语是这样的一个短语,它至少含有一个终结符,并且除了它本身以外,不含有更小的素短语。

最左素短语顾名思义,就是整棵语法分析树中,位于最左端的那个素短语。
而至于短语的概念,我们可以这样来描述:
在这里插入图片描述
针对上面给出的文法G[E],我们已经完成了分析表的构造。那么,最后一步就是如何来使用这一分析表的问题了。我们扫描整个输入串,持续地压入输入字符,直到当前栈顶的终结字符优先级大于待输入的字符优先级,我们就以当前栈顶的终结字符为依据,来进行依次归约。还是上面给出的文法,我们针对输入串a*(b+c)来进行这一过程的叙述。
在这里插入图片描述
在输入串合法的情况下,算法工作完毕时,符号栈S应该呈现:#N#这样的状态。为了后续的说明,我们这里需要给出算符优先分析法的算法伪代码描述:
在这里插入图片描述
在上述算法工作的过程中,若出现j-1后的值小于等于0的情况,则意味着输入串非法。另外,在算法中的“将[S[j+1]…S[k]]归约为某个N”,我们并没有指出将已经找到的最左素短语归约到哪个非终结符N。N是指那样一个产生式的左部符号,其产生式的右部和[S[j+1]…S[k]]构成如下的一一对应关系:从左至右,终结符对应终结符,非终结符对应非终结符,并且最为重要的是,对应的终结符必须相同。由于非终结符对于归约没有任何影响,所以实际上,非终结符在算符优先分析法中,根本无需进入符号栈S。
算符优先分析法的规约过程一般不会等价于规范归约的过程,这是由于算符优先分析法并没有对文法的非终结符定义优先关系,所以根本无法发现由单个非终结符组成的“可归约串”。换句话说,在算符优先分析的过程中,我们根本无法使用右部仅有单个非终结符的“单非产生式”进行归约(例如P→Q)。而从另一个方面来看,算符优先分析法也比规范归约要快得多,原因就在于它跳过了所有的单非产生式所对应的归约步骤。

猜你喜欢

转载自blog.csdn.net/weixin_44246009/article/details/106980285