自底向上分析总结

1 前言

这章和上一章自顶向下是语法分析的两种方法。然而自底向上的分析方法更为强大一些,主要有:

  • LR(0)
  • SLR(1)
  • LR(1)
  • LALR(1)

我们仍然是以例子的方式,带大家慢慢了解这些知识点。

引用的课本: 《编译原理及实践》 Kenneth.C.Louden 机械工业出版社 2000.3



2 LR(0)

2.1 LR(0) 例子

我们考虑下列文法,并分析一下用该文法分析 ((a))
A → ( A )   ∣   a A → (A) ~|~ a A(A)  a

首先,我们我们必须明确一个事情,自底向上分析文法的时候,文法开始符号不能有两个入口。
但是它不用消去左递归和左公因子,这一点和自顶向下的分析方法有很大区别。

所以这里我们需要扩充文法
A ′ → A    A → ( A ) A → a      A' → A ~~\\ A → (A) \\ A → a ~~~~ AA  A(A)Aa    
接着绘制LR(0) 的DFA图:(摘自课本159页)
在这里插入图片描述这里简要说一下这个图绘制的思路(编号只是为了方便表示还有方便我们后期写算法,编号顺序无所谓):

  • 我们看编号为0的。这里第一条A’ → .A,点号代表我们当前位置。然后改规则我们发现,下一个是A,是非终结符号来的,所以我们需要把A的相关的文法规则放进去,这里有两条,一个是 A → .(A), 另一个是 A → .a, 关于这个点号,我们会发现其实每一次都是放在最左边的,因为我们是想要继续移进或者规约。
  • 从编号为0开始,如果后面一个字符是A, 则我们可以跳转到编号为1的结点。这个时候如果是$结束符号的话,进入接受状态并宣告成功,否则报错。
  • 然后还有一点要说明的是,比如我们走到编号为2的结点,这时候发现已经是规约态了,需要回溯,那么回溯多少步呢,看规则: A → a. , .前只有一个字符(准确来说应该是一个词法单元,即token),所以回退一步就够了。同理,如果是编号为5,则需要回退三步。那么计算机如何实现回退呢,这就涉及到栈了。

分析 ((a)) 的过程,这里大家再看一下P159 表5-3就会明白了:
在这里插入图片描述注: 这里第8的分析栈书上有误,应该是 $ 0 ( 3 A 4 ) 5
如果不用DFA图解决问题的话,其实也可以使用分析表去解决这个问题:课本P160页 表5-4
在这里插入图片描述



2.2 LR(0)的问题

实际上,能用LR(0) 解决的文法是非常少的。比如我们看个例子:
G(E)
E → E   +   n   ∣   n E → E ~+~ n ~|~ n EE + n  n
试分析 n+n+n 。
我们仍然先画出LR(0)的DFA图,看看会有什么问题:
在这里插入图片描述
画此图的时候,仍然需要实现文法扩充,以下若是需要此步骤的我就不赘述了。
这里我们发现,在结点1的时候,我们是要进行 E‘ → E. 规约呢,还是要 E → E. + n移进呢?这就发生了 规约-移进冲突 ! 为此,我们引出 SLR(1) 的方法,其中S代表简单simple的意思, 1代表超前查看一个token。



3 SLR(1)

3.1 SLR(1)

我们继续考虑上一个问题:
在这里插入图片描述
结点一选择哪一个规则,其实我们可以看到,当要分析的字符创如果后面是 + 号的话, 我们应该是 移进 而不是规约。因此我们可以求出 Follow(E), Follow集合的求解方法在我上一篇博客文法规则自顶向下分析有提到,若忘记的朋友可以去回顾一下。
因此这里我们就可以解决这个 移进-规约冲突 了。
这里解决问题依旧可以使用LR(0) 的DFA图解决,或者也可以使用分析表的方式。其实看完本文我们会知道,四种方法中,唯独就 SLR(1) 没有专门的DFA图。



3.2 SLR(1) 问题1

我们看下这个文法:
s t a t e m e n t → i f − s t m t ∣ o t h e r                                                                   i f − s t m t → i f ( e x p )   s t a t e m e n t   ∣   i f ( e x p )   s t a t e m e n t   e l s e   s t a t e m e n t e x p → 0 ∣ 1                                                                                                     statement → if-stmt | other ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\ if-stmt → if ( exp ) ~statement~|~if (exp)~statement~else ~statement \\ exp → 0 | 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ statementifstmtother                                                                 ifstmtif(exp) statement  if(exp) statement else statementexp01                                                                                                   
化简一下即为:
S → I   ∣   o t h e r               I → i f   S   ∣   i f   S   e l s e   S S → I ~|~ other~~~~~~~~~~~~~ \\ I → if ~ S ~|~ if ~ S ~ else ~ S SI  other             Iif S  if S else S
绘制出LR(0) 的DFA图(课本P163 图5-6)就会发现状态5那里,有移进-规约冲突。
在这里插入图片描述
对于规约项:Follow (I) = {$, else}
对于移进项的下一个符号为 else,。那么当出现else怎么办呢?
我们引入 最长串匹配 规则可以解决这个问题,也就是说,当出现else的时候,就选择移进。
分析表 课本P164 表5-9(书中的表有错误,更正如下截图,来自老师PPT)
在这里插入图片描述
正确的表应该如下:
在这里插入图片描述

但是,它只能解决 移进-规约冲突,却不能解决 规约-规约冲突



3.3 SLR(1) 问题2

这里直接截图了:
在这里插入图片描述
画出来图后会发现,此DFA图有可能产生 规约-规约冲突. 而且
Follow(S) = {$}, Follow(V) = {$, :=}, 那么当出现后续是 $ 号时,我们是规约出 S 还是规约出 V 呢?此时SLR(1) 就解决不了了。尽管如此,SLR(1)还是可以分析日常生活中的许多文法。



4 LR(1)

4.1 解决SLR(1) 的规约冲突问题

我们想想刚刚为何会出现这个问题呢,原因是我们考虑后续的符号太晚了。
在这里插入图片描述

当然我们也可以考虑SLR(K),但是K一般不会大于2,不然回溯成本太高,所以实际上SLR(K)用的不多,这里就介绍了,大家有兴趣可以看看课本P165或者上网查查资料。
然后我们可以下面这样搞:(截图截自老师ppt)

在这里插入图片描述
这里第一行的S’ -> .S,$ 的$是由follow(S’)得出。
S → .id 的follow项是 $ 是由 S’ → .S 得出来的,V → .id 的follow项 := 是由 S → .V := E 得出来的。
右边状态的 S → id. 的 $ 是由左边的 S → .id 得来的。
可以看到此时规约S和V就不会产生矛盾了。

4.2 LR(1) 的问题

考虑文法规则
A → ( A )   ∣   a A → (A) ~|~ a A(A)  a
画出其LR(1) 的DFA图(课本P168 图5-7)
在这里插入图片描述
观察到状态3与6,状态4与8, 状态2与5,状态7与9,状态树几乎大了一倍呀,可是他们就第一条的follow项不同。能不能合并一下呢,这就出现了LALR(1) 的分析方法。



5 LALR(1)

5.1 解决LR(1) 耗费过多空间的问题

我们仍然考虑文法规则
A → ( A )   ∣   a A → (A) ~|~ a A(A)  a
我们将LR(1) 中的DFA图的一些类似的结点合并,得出LALR(1)的DFA图(课本P172 图5-9):
在这里插入图片描述
可以看到存储空间可以省下很多。



5.2 LALR(1)出错延迟的问题

大家分别用 LR(1)LALR(1) 分析一下 a)
在这里插入图片描述

  • LR(1): 从状态0开始,接收 a, 到状态3, 然后 发现 )并不是 [A → a., $] 的follow项 $, 产生出错处理。
  • LALR(1): 从状态0开始,接收a, 到状态3, 然后规约出A跳转到到状态1 (3 → 0 → 1), 发现不是[A’ → A., $] 的follow项 $ ,产生出错处理。
    在这里插入图片描述
    可见,LALR(1) 会比 LR(1) 在处理错误字符串的时候,花费多一些时间。尽管如此,由于LALR(1) 省下了大量的存储空间,很多工具还是使用了 LALR(1) 作为文法分析方法,比如 Yacc。



6 结语

自底向上作为一种重要的文法分析方法,还是很有必要花时间学习一下的。午安~~

猜你喜欢

转载自blog.csdn.net/weixin_43850253/article/details/110477220