LR(0)
第一步:改写为扩广文法,一般只是新加一个起始非终结符,并且将所有的产生式拆开即可,然后在所有文法的前面加从0开始的编号,以便构建LR(0)分析表的时候有用。
第二步:列出所有项目。这个比较简单,将所有的产生式插空加点即可。
第三步:写出所有项目集规范族。即写出所有项目的转移,并且标号。
第四步:画出状态转移。即根据第三步构造的项目写出DFA。
第五步:作出LR(0)分析表。
一般题目都只会问到LR(0)的分析表,有些第三小问会问对一些指定的输入串进行分析,这时候也比较简单,下面先给个例子。
例子:
文法如下,构建该文法的LR(0)分析表。
G[S]:S–>BB
B–>aB|b
答:
1、改写为扩广文法为:
G’[S’]:
(0) S’–>S
(1) S–>BB
(2) B–>aB
(3) B–>b
2、所有项目为(键盘上找不到原点这个符号,所以写到纸上了):
3、项目集规范族为:
如何得到的呢?对于每一个状态,如果圆点后面是非终结符,那么就得把这个终结符所有的产生式都写上去,以此类推,然后我们要对每一个圆点后面可能会读取什么东西都要进行转移,等第四步就懂了。
4、状态转移DFA为:
我们可以看到,圆点后面就是我们需要读取的一个东西,然后我们读取这个东西之后圆点后移,然后转移到其他状态去。
如果圆点到产生式右边的最后了,那么就说明不能读取了,因此我们称这个项目为归约项目
如果圆点到产生式右边的中间,还没有到最后,如果后面是非终结符,我们称这个项目为待约项目。
如果圆点到产生式右边的中间,还没有到最后,如果后面是终结符,我们称这个项目为移进项目。
5、LR(0)的分析表为:
对于每一个状态,如果一个状态a吸收了一个终结符转移到另一个状态b的话,那么就在状态a那一行的action那里写上
s
b
s_b
sb即可,对于结尾的状态,我们在#那一栏写acc,表示接受,这里的话就是状态1表示的项目,如果一个状态时归约项目,那么就去我们改写的扩广文法那里找它是第几个产生式,然后前面加r即可。如果一个状态a吸收了一个非终结符转移到另一个状态b的话,就在状态a那一行的goto那里写上b即可。
SLR(1)
先来介绍一下移进-归约冲突和归约-归约冲突:
有时候我们在一个项目集里面得到两种归约项目,然后我们不知道归约哪一个,所以这就会导致计算机不知道归约哪一个,所以这是一种冲突,例如在一个项目集里面有:A–>B和A–>CB(键盘没有圆号,故用*代替之)。
而移进-归约冲突是形如这样的项目:A–>B*、A–>B*c
这时候计算机不知道是归约A–>B*还是移进A–>B*c,所以这也是一种冲突。
SLR(1)的方法其实是和LR(0)一致的,但是LR(0)可能会产生移进-归约冲突,归约-归约冲突等冲突,所以我们看能不能用SLR(1)方法改写LR(0),使之变得不冲突。
改写方法:
如果出现了移进归约冲突:
假设造成的冲突是A–>B*和A–>B*c,那么很明显,我们需要分析一下B后面输入串中的下一个是包含什么东西,如果输入的下一个串是c,那么我们肯定是移进A–>B*c,如果输入的下一个串是其他的怎么办呢?
因此有以下的方法:我们利用Follow(A)来判断,如果输入串的下一个属于Follow(A),并且不为c,那么我们就归约A–>B*,如果输入串的下一个不属于Follow(A),并且为c的话,我们就移进A–>B*c。
这样的方法必须要满足Follow(B)里面不能包括c,不然同样地,我们还是不知道是移进还是归约。因此我们可以推广开来。如下:
这就是SLR(1)的方法。就是LR(0)消除冲突的方法。他的解题步骤还是与LR(0)大体相似
但是SLR(1)的分析表与LR(0)的分析表有些不相同:
在SLR(1)分析表的action这一栏中,对于归约这个地方,我们要根据Follow集来写。看以下的例子就懂了。
例子:
我标出来的地方就是有移进归约冲突,故不能用LR(0)的方法来做,我们看能不能用SLR(1)来消除冲突。很容易得到Follow(S)={c,#},这里灵活运用以下,因为S–>bA*S,*后面是非终结符,所以可以看First(S)来看,又很容易得到First(S)={b},故发现这两个集合没有交集,所以用SLR(1)之后不会出现冲突,我们可以得到以下的SLR(1)分析表:
分析一下这里的状态3一行的转移:状态3吸收一个b,它属于First(S),所以我们移进到状态
s
2
s_2
s2,而c和#都是属于Follow(S),因此就归约了。
还要注意:SLR(1)分析表,对于归约的状态,我们只能在Follow集包含的终结符下写r了,就算是这个状态没有冲突,我们也不能一股脑一行全写r,例如状态5和状态7。