第7章 LR分析法
DESCRIPTION
第7章 LR分析法. LR 分析法的归约过程是规范推导的逆过程,所以 LR 分析过程是一种规范归约过程。本章主要介绍 LR(K) 分析技术 L —— 从左到右扫描输入串 R —— 构造一个最右推导的逆过程 K —— 向右顺序查看输入串的 K 个( K≥0) 符号就可唯一地确定分析器的动作是移进还是归约和用哪个产生式归约. 7.1 LR 分析概述 一个 LR 分析器由3个部分组成: 总控程序,也可以称为驱动程序。对所有的 LR 分析器总控程序都是相同的。 - PowerPoint PPT PresentationTRANSCRIPT
第 7章 LR分析法
LR分析法的归约过程是规范推导的逆过程,所以 LR分析过程是一种规范归约过程。本章主要介绍 LR(K)分析技术
L——从左到右扫描输入串
R——构造一个最右推导的逆过程
K——向右顺序查看输入串的 K 个 (K≥0)符号就可唯一地确定分析器的动作是移进还是归约和用哪个产生式归约
7.1 LR分析概述
一个 LR分析器由 3个部分组成:
(1)总控程序,也可以称为驱动程序。对所有的 LR分析器总控程序都是相同的。
(2) 分析表或分析函数,不同的文法分析表将不同,同一个文法采用的 LR分析器不同时,分析表将也不同,分析表又可分为动作表 (ACTION)和状态转换 (GOTO)表两个部分,它们都可用二维数组表示。
(3) 分析栈,包括文法符号栈和相应的状态栈,它们均是先进后出栈。
分析器的动作就是由栈顶状态和当前输入符号所决定。LR分析器工作过程示意图如图所示。
总控程序 output
Input#
S1 Xm
…S1
…X1
S0 #
栈
状态 文法符号
ACTION GOTO
LR 分析表
产生式表
SP
其中 SP为栈指针, S[i]为状态栈, X[i]为文法符号栈。状态转换表用 GOTO[Si,X]= Sj 表示,规定当栈顶状态为 Si 遇到当前文法符号为 X 时应转向状态Sj 。 X 为终结符或非终结符。ACTION[Si,a]规定了栈顶状态为 Si时遇到输入符号a 应执行的动作。动作有 4种可能: ① 移进 ② 归约 ③ 接受 acc ④ 报错
① 移进: 把 Sj=GOTO[Si, a]移入到状态栈,把 a 移入到文法符号栈。其 中 i, j 表示状态号。 ② 归约: 当在栈顶形成句柄为β时,则用β归约为相应的非终结符 A, 即文法中有 A→β的产生式,若β的长度为 r(即 |β|=r),则从 状态栈和文法符号栈中自栈顶向下去掉 r 个符号,即栈指针SP 减去 r。并把 A 移入文法符号栈内, Sj=GOTO[Si, A]移进状态, 其中 Si为修改指针后的栈顶状态。 ③ 接受 acc: 当归约到文法符号栈中只剩文法的开始符号 S 时,并且输入符 号串已结束即当前输入符是‘ #’,则为分析成功。 ④ 报错: 当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则 报错,说明输入串不是该文法能接受的句子。
7.2 LR(0)分析 LR(0)分析表构造的思想和方法是构造其它 LR分析表的基础。我们回顾在第 6章中曾给出例 6.1 文法 G[S]为: (1) S→aAcBe (2) A→b (3) A→Ab (4) B→d
文法 G[S] :(1) S → aAcBe(2) A → b(3) A → Ab(4) B → d
a b b c d e
步骤 符号栈 输入符号串 动作 1 ) # abbcde# 移进 2 ) #a bbcde# 移进
A
3 ) #ab bcde# 归约 (A→b)
4 ) #aA bcde# 移进
A
5 ) #aAb cde# 归约 (A→Ab)
6 ) #aA cde# 移进 7 ) #aAc de# 移进
B 8 ) # aAcd e# 归约 (B→d)
9 ) #aAcB e# 移进
11 ) #S # 接受
S
10 ) #aAcBe # 归约
符号串 abbcde 是否是 G[S]的子
对输入串 abbcde# 的移进 - 规约分析过程
S aAcBe
aAcde
aAbcde
abbcde
对输入串 abbcde#用自底向上归约的方法进行分析,当归约到第 5步时栈中符号串为 #aAb,我们采用了产生式 (3)进行归约而不是用产生式 (2) 归约,而在第 3步归约时栈中符号串为 #ab时却用产生式 (2) 归约,虽然在第 2步和第 5步归约前栈顶符号都为 b,但归约所用产生式却不同,为什么?
其原因在于已分析过的部分在栈中的前缀不同,为了说明这个问题并引进一些概念和术语。
1 、文法的规范句型的‘活前缀’和‘可归前缀’的概念
字的前缀是指该字的任意首部。例:字 abc 的前缀有 , a,ab,abc
在 LR分析工作过程中的任何时候,栈里的符号(自栈底而向上) X1X2…Xn应该构成活前缀,把输入串的剩余部分配上就成为规范句型。
所谓活前缀:是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。
可归前缀:每次归约动作之前的活前缀称为可归前缀
问 abbcde是否是它的句子?最右推导过程
S aAcBe[1] aAcd[4]e[1] aAb[3]cd[4]e[1]ab[2]b[3]cd[4]e[1]归约前句型的前缀(可归前缀) 归约前可在栈里的规范句型(不含句柄之后 的任何符号的前缀称为活前缀)ab[2] ,a,abaAb[3] ,a,aA,aAbaAcd[4] ,a,aA,aAc,aAcdaAcBe[1] ,a,aA,aAc,aAcB,aAcBe
例:文法 G[S] :(1) S → aAcBe(2) A → b(3) A → Ab(4) B → d
活前缀与句柄之间的关系有三种情况:
( 1)活前缀已经含有句柄的全部符号 ,表明产生式 A→β的右部β已出现在栈顶
( 2)活前缀只含有句柄的部分符号 ,表明 A→β1β2 的右部子串 β1 已出现在栈顶,期待从输入串中看到 β2 推出的符号
( 3)活前缀不含句柄的任何符号 ,此时期望 A→β的右部所推出的符号串为了刻划这种分析过程中文法 G 中的每个产生式的右部已有多大部分被识别(已经出现在栈顶)我们在产生式某处加上圆点“ .”来指示位置,对上述三种情况标出圆点的产生式:
( 1 ) A . (2) A 1. 2 (3) A .
构造 LR(0) 项目集规范族
(1) LR(0) 项目在文法 G 中每个产生式的右部适当位置添加一个圆点构成项目。
例: A xyz 对应 4 个项目: A.xyz Ax.yz Axy.z Axyz. 如: S→aAd 对应的项目: S→.aAd S→a .Ad S→aA .d S→aAd .
对于 A→ε 的 LR(0) 项目只有 A→ .
注意:
( 1 )凡是圆点在最右端的项目,称为归约项目。
如: Axyz. 用 r 表示归约
( 2 )对文法开始符号的归约项目称为接受项目,用 acc 表示
如: S . S 是开始符
( 3 )形如 A . B β 的项目称为待归约项目,即表明所对
应状态正等待分析完非终结符 B 所能推出的串归约成 B ,
才能分析 A 的右部
( 4 )形如 A . a β 的项目称为移进项目,即终结符 a 移进
符号栈。移进用 S 表示
2) 构造识别活前缀的 NFA 如果把文法的所有产生式的项目都引出,每个项目都为 NFA的一个状态。
•将文法 G 拓展为 G’, 目的是为了使文法有且仅有一个接受项目。
例 :文法 G:
E→aA|bB A→cA|d B→cB|d拓展后变为: (1)S′→E
(2)E→aA(3)E→bB(4)A →cA
(5)A →d
(6)B →cB
(7) B →d
•构造该文法的项目有: 1 . S′→·E 10. A→d· 2 . S′→E· 11. E→·bB 3 . E→·aA 12. E→b·B 4 . E→a·A 13. E→bB· 5 . E→aA· 14. B→·cB 6 . A→·cA 15. B→c·B 7 . A→c·A 16. B→cB· 8 . A→cA· 17. B→·d 9 . A→·d 18. B→d·
识别活前缀的 NFA
1 . S′→·E 10. A→d·2 . S′→E· 11. E→·bB3 . E→·aA 12. E→b·B4 . E→a·A 13. E→bB·5 . E→aA· 14. B→·cB6 . A→·cA 15. B→c·B7 . A→c·A 16. B→cB·8 . A→cA· 17. B→·d9 . A→·d 18. B→d·
对于图中识别活前辍的 NFA我们可以利用第 4章讲过的子集法将其确定化。对确定化后的 DFA如果把每个子集中所含状态集对应的项目写在新的状态中,结果见 P132如 7.8 图所示。
(3) LR(0)项目集规范族的构造 对于构成识别一个文法活前缀的 DFA项目集 (状态 )的全体称为这个文法的 LR(0)项目集规范族,我们可以分析图 7.8 中每个状态中项目集的构成,不难发现如下规律:
若状态中包含形如 A→α·Bβ的项目,则形如 B→·γ的项目也在此状态内。例如: 0状态中项目集为 {S′→·E, E→·aA, E→·bB}。若文法 G 已拓广为 G′,而 S 为文法 G 的开始符号,拓广后增加产生式 S′→S,如果 I 是文法 G′的一个项目集,定义和构造 I 的闭包 CLOSURE(I)如下: a) I的项目均在 CLOSURE(I)中。 b) 若 A→α·Bβ属于 CLOSURE(I),则每一形如B→·γ 的项目也属于 CLOSURE(I)。 c) 重复 b)直到不出现新的项目为止。即CLOSURE(I)不 再扩大。 由此,我们可以很容易构造出初态的闭包,即 S′→·S 属于 I,再按上述三点求其闭包。
有了初态的项目集,其它状态的项目集如何求出?由于识别活前缀的 DFA的每个状态是一个项目集,项目集中的每个项目都不相同,每个项目圆点后的符号不一定相同,因而对每个项目圆点移动一个位置后,箭弧上的标记也不会完全相同,这样,对于不同的标记将转向不同的状态。例如初态 {S′→·E, E→·aA,E→·bB}对第一个项目圆点右移一个位置后变为 S′→E·箭弧标记应为 E,对第二个项目E→·aA,圆点右移一个位置后,项目变为 E→a·A,箭弧标记为 a,同样第三个项目为圆点右移一个位置后变为E→b·B,箭弧标记为 b,显然,初态可发出三个不同标记的箭弧,因而转向三个不同的状态,也就由初态派生出三个新的状态,对于每个新的状态我们又可以利用前面的方法,若圆点后为非终结符则可对其求闭包,得到该状态的项目集。圆点后面为终结符或在一个产生式的最后,则不会再增加新的项目。
由前面可知 LR(0)项目集规范族的项目类型分为四种:a) 移进项目 b) 归约项目 c) 待约项目 d) 接受项目一个项目集中可能包含以上四种不同的项目,但是一个项目集中不能有下列情况存在: 1) 移进和归约项目同时存在: 形如: A→α·aβ B→γ· 由于这时面临输入符号为 a 时不能确定移进 a 还是把 γ 归约为 B,因为 LR(0)分析是不向前看符号,所以对归约的项目不管当前符号是什么都应归约。对于在一个项目集中同时存在移进和归约项目时称该状态含有移进 -归约冲突。2) 归约和归约项目同时存在。 形如: A→β· B→γ· 因这时不管面临什么输入符号都不能确定归约为 A,还是归约为 B,对于在一个项目集中同时存在两个以上归约项目时称该状态含有归约 -归约冲突。对一个文法的 LR(0)项目集规范族不存在移进 -归约,或归约 -归约冲突时,称这个文法为 LR(0)文法。
构造上例文法的 LR(0)分析表见 P136表 7.3 所示
对输入串 bccd#的 LR(0)分析过程见 P136表 7.4所示
按上述方法建立识别活前缀的 DFA结果与 7.8 图相同
例 :G[S] 为 : S a A c B e
A b A Ab B d 1) 构造识别活前缀的 DFA 2) 构造它的 LR(0) 分析表。
3) 分别给出对输入符号串 abbcde 和 abbbce的 LR(0) 分析步骤。
G[S] 拓广为 : S’ S
S a A c B e A b A Ab B d
I0 : S’ • S
S • a A c B e
I1 : S’ S •I2 : S a • A c B e
A • b
A • Ab
I3 : S a A • c B e
A A • b
I4 : A b •
I5 : S a A c • B e
B • dI7 : S a A c B • e
I8 : B d • I9 : S a A c B e •
I6 : A A b •
S a
A
bb
c
B
ed
G[S] 的 LR(0) 分析表ACTI ON GOTO
a c e b d # S A B0 S2 11 acc2 S4 33 S5 S6
4 r2 r2 r2 r2 r2 r2
5 S8 76 r3 r3 r3 r3 r3 r3
7 S9
8 r4 r4 r4 r4 r4 r4
9 r1 r1 r1 r1 r1 r1
Step states. Syms. The rest of input action goto 1 0 # abbcde# s2 2 02 #a bbcde# s4 3 024 #ab bcde# r2 3 4 023 #aA bcde# s6 5 0236 #aAb cde# r3 3 6 023 #aA cde# s5 7 0235 #aAc de# s8 8 02358 #aAcd e# r4 7 9 02357 #aAcB e# s9 10 023579 #aAcBe # r1 1 11 01 #S # acc
对输入串 abbcde# 的分析过程
Step states. Syms. The rest of input action goto 1 0 # abbce# s2 2 02 #a bbce# s4 3 024 #ab bce# r2 3 4 023 #aA bce# s6 5 0236 #aAb ce# r3 3 6 023 #aA ce# s5 7 0235 #aAc e# 出错
说明 abbce# 不是文法 G[S] 的句子
对输入串 abbce# 的分析过程
SLR(1)分析
由于大多数适用的程序设计语言的文法不能满足 LR(0)文法的条件,即使是描述一个实数变量说明这样简单的文法也不一定是LR(0)文法。
将该文法缩写后并拓广为 G′如下: (0) S′→S (1) S→rD (2) D→D,i (3) D→i
现举实型变量说明文法为例: <实型变量说明>→ real<标识符表> <标识符表>→<标识符表>, i <标识符表>→ i
LR ( 0 )项目
1. S`→ . S 2. S`→S . 3. S→ . rD
4. S→r . D 5. S→rD . 6. D→ . D,i
7. D→D . ,i 8. D→D, . i 9. D→D,i .
10. D→ . i 11. D→i .
LR ( 0 )项目集规范族I0: S`→ . S I3: S→r D .
S→ . r D D→D ., iI1: S`→S . I4: D→i .
I2: S→r . D I5: D→D ., i
D→ . D , i I6: D→D , i .
D→ . i 其中 I3 中含有移进 / 归约冲突 文法不是 LR(0) 的,如何解决?
首先构造该文法 G′ 的识别活前缀的 DFA 如图
I0:S’ → . SS →.rD
rI2:S →r.DD →.D,iD →.i
I1:S’ →S.
I3:S →rD.D →D.,i
S D
,
I5:D →D,.i
I4:D →i.
I6:D →D,i.
i
i
实数说明文法的 LR(0) 分析表 状态 ACTION GOTO
r , i # S D
0123456
S2..
r1r3.
r2
.
.
.r1, S5
r3.
r2
.
.S4r1r3S6r2
.acc
.r1r3.
r2
1 ..3
因此本节将介绍对于 LR(0)规范族中有冲突的项目集(状态 )用向前查看一个符号的办法进行处理,以解决冲突。这种办法将能满足一些文法的需要,因为只对有冲突的状态才向前查看一个符号,以确定做那种动作,因而称这种分析方法为简单的 LR(1)分析法,用 SLR(1)表示。因此假定一个 LR(0)规范族中含有如下的项目集 ( 状态 )I I={X→α·bβ,A→γ·,B→δ·}也就是在该项目集中含有移进 -归约冲突和归约 -归约冲突。其中 α,β,γ,δ为文法符号串, b 为终结符。那么只要在所有含有 A 或 B 的句型中,直接跟在 A 或 B 后的可能终结符的集合即FOLLOW(A)和 FOLLOW(B)互不相交,且都不包含 b,也就是只要满足 FOLLOW(A)∩FOLLOW(B)= FOLLOW(A)∩{b}=
FOLLOW(B)∩{b}=
那么,当在状态 I 时面临某输入符号为 a 时,则动作可由下规定决策。 1) 若 a=b,则移进。 2) 若 a∈FOLLOW(A),则用产生式 A→γ进行归约。 3) 若 a∈FOLLOW(B),则用产生式 B→δ进行归约。 4) 此外,报错。
I={X→α·bβ,A→γ·,B→δ·}
I3 : S→rD·.. D→D· , i
在上例中的 I3如下:因为 I3 中存在移进个归约的冲突,采用SLR( 1 )的方法看看是否可以解决:
Follow(S) ∩ {,}={#} ∩ {,}=
所以遇到‘,’就移进,遇到#就归约。
实数说明文法的 SLR(1) 分析表
状态ACTION GOTO
r , i # S D
0123456
S2...
r3.
r2
.
.
.S5r3.
r2
.
.S4.
r3S6r2
.acc
.r1r3.
r2
1 ..3
如果对于一个文法的 LR(0)项目集规范族的某些项目集或 LR(0)分析表中所含有的动作冲突都能用上述方法解决,则称这个文法是 SLR(1)文法,所构造的分析表为SLR(1)分析表,使用 SLR(1)分析表的分析器称为SLR(1)分析器。
例如,我们可以构造算术表达式文法的 LR(0)项目集规范族,然后分析它是 LR(0)文法还是 SLR(1)文法,现将表达式文法拓广如下: (0) S′→E (1) E→E+T (2) E→T (3) T→T*F (4) T→F (5) F→(E) (6) F→i
I0 : S′→·E
E→·E+T E→·T T→·T*F T→·F F→·(E) F→·i I1 : S′→E·
E→E·+T I2 : E→T·
T→T·*F I3 : T→F·
I4 : F→(·E)
E→·E+T E→·T T→·T*F T→·F F→·(E) F→·i
I5 : F→i·
I6 : E→E+·T
T→·T*F T→·F F→·(E) F→·i I7 : T→T*·F
F→·(E) F→·i I8 : F→(E·)
E→E·+T I9 : E→E+T·
T→T·*F I10 : T→T*F·
I11 : F→(E)·
该文法的 LR(0) 项目集规范族为:
与此相应的识别该文法活前缀的有限自动机如 P139图 7.10所示。
不难看出在 I1, I2, I9中存在移进 -归约冲突,因而这个表达式文法不是 LR(0)文法,也就不能构造 LR(0)分析表,现在分别考查这三个项目集 ( 状态 )中的冲突是否能用SLR(1)方法解决。 在 I1中: S′→E· E →E·+T由于 FOLLOW(S′)={#},而 S′→E·是唯一的接受项目,所以当且仅当遇到句子的结束符 "#"号时才被接受。又因 {#}∩{+} = ,因此 I1中的冲突可解决。
在 I2中: E→T· T→T·*F 我们可计算非终结符 E 的 FOLLOW集为: FOLLOW(E)={+,),#} 所以 FOLLOW(E)∩{*}={+,),#}∩{*} = ,因此面临输入符为 '+', ')'或 '#'号时,则用产生式 E→T进行归约。当面临输入符为 '*'号时,则移进,其它情况则报错。 在 I9中: E→E+T· T→T·*F 与 I2中的情况类似因归约项目的左部非终结符 E 的后跟符集合 FOLLOW(E)={+,),#}与移进项目圆点后终结符不相交,所以冲突可以用 SLR(1)方法解决,与 I2不同的只是在面临输入符为 '+', ')'或 '#'号时用产生式 E→E+T归约。
由以上考查该文法在 I1, I2, I9三个项目集 ( 状态 )中存在的移进 -归约冲突都可以用 SLR(1)方法解决,因此该文法是 SLR(1)文法。 SLR(1)分析表的构造与 LR(0)分析表的构造类似,仅在含有冲突的项目集中分别进行处理。如果对所有归约项目都采取 SLR(1)的处理思想,即对所有非终结符都求出其 FOLLOW集合,这样凡是归约项目仅对面临输入符号包含在该归约项目左部非终结符的 FOLLOW集合中,才采取用该产生式归约的动作。这种处理的好处是对某些不该归约的动作可提前发现错误。对于这样构造的 SLR(1)分析表我们不访称它为改进的 SLR(1)分析表 ,我们可构造上例中相应的改进的SLR(1)分析表 P142表 7.8所示。
对输入串 i+i*i#进行分析见 P142表 7.9所示
LR(1) 分析
本节介绍比 SLR(1) 功能更强的 LR(1) 分析法。 例如下列文法 G′ 为: (0) S′→S (1) S→aAd (2) S→bAc (3) S→aec (4) S→bed (5)A →e
我们首先用 S′→·S作为初态集的项目,然后用闭包函数和转换函数构造识别文法 G′的识别活前缀的有限自动机 DFA见P143如图 7.11 所示,可以发现在项目集 I5和 I7中存在移进和归约冲突。 I5: S→ae·c I7: S→be·d A→e· A→e· 而归约项目左部非终结符的 FOLLOW(A)={c,d} 在 I5中, FOLLOW(A)∩{c}={c,d}∩{c}≠ 在 I7中, FOLLOW(A) ∩{d}={c,d}∩{d}≠ 因此 I5, I7中冲突不能用 SLR(1)方法解决。只能考虑用下面将要介绍的 LR(1)方法解决。
由于用 SLR(1)方法解决动作冲突时,对于归约项目A→α·,只要当前面临输入符为 a∈FOLLOW(A)时,就确定采用产生式 A→α进行归约,但是如果栈中的符号串为 βα,归约后变为 βA,再移进当前符 a,则栈里变为 βAa,而实际上 βAa未必为文法规范句型的活前缀。例如:在识别表达式文法的活前缀 DFA中, (见图 7.10) 在项目集 I2存在移进 -归约冲突,即 {E→T· T→T·*F}若栈顶状态为 2,栈中符号为 #T,当前输入符为‘ )’,而‘ )’属 FOLLOW(E)中,这时按 SLR(1)方法应用产生式 E→T进行归约,归约后栈顶符号为 #E,而再加当前符‘ )’后,栈中为 #E)不是表达式文法规范句型的活前缀。 因此这个归约是多余的。因此可以看出 SLR(1)方法虽然相对 LR(0)有所改进,但仍然存在着多余归约,也说明 SLR(1)方法向前查看一个符号的方法仍不够确切, LR(1)方法恰好是要解决 SLR(1)方法在某些情况下存在的无效归约问题。
若 [A→α·Bβ]∈项目集 I,则 [B→·γ](B→γ为一产生式 )也包含在 I 中,不妨考虑,把 FIRST(β)作为用产生式 B→γ归约的搜索符,称为向前搜索符,作为归约时查看的符号集合,用以代替 SLR(1)分析中的 FOLLOW集,把此搜索符号的集合也放在相应项目的后面,这种处理方法即为 LR(1)方法。
LR(1)项目集族的构造 构造 LR(1)项目集的闭包函数 CLOSURE(I)按如下方式构造 :
a) I 的任何项目都属于 CLOSURE(I) b) 若有项目 [A→α·Bβ,a ]属于 CLOSURE(I) , B→γ 是文法中的产生式, β∈V* , b∈FIRST(βa) , 则 [B→·γ,b]也属于 CLOSURE(I)中。 c) 重复 b)直到 CLOSURE(I)不再增大为止。
e
c
A dI0:S′→·S,# S→·aAd,# S→·bAc,# S→·aec,#S→·bed,#
I2 :S→a·Ad,# S→a·ec,# A→·e,d
a
I1 : S′→S·,#S
I3: S→b·Ac,#
S→b·ed,# A→·e,c
bI6:S→bA·c,# A
I7 : S→be·d,#
A→e·,cI11 :
S→bed·,#d
I10 :S→bAc·,#
I4 : S→aA·d,#
I5 : S→ae·c,#
A→e·,de
I8 : S→aAd·,#
I9 : S→aec·,#c
(0) S′→S(1) S→aAd(2) S→bAc
构造 LR(1) 分析表 :
例 : 已知拓展文法为 : (3) S→aec(4) S→bed
(5)A →e
LR(1) 项目集和转换函数如下 :
状态 action gotoa b c d e # S A
0 S2 S3 1 1 acc
2 S5 4 3 S7 6 4 S8
5 S9 r5
6 S10
7 r5 S11
8 r1
9 r3
10 r2
11 r4
如果一个文法的 LR(1)分析表中不含多重入口时 (或任何一个 LR(1)项目集中无移进—归约或归约—归约的冲突 ,则称该文法为 LR(1)文法 .
例: G(S):S BB BaB B b
I0:S’.S S .BB B .aB B .b
I1:S’ S.
I2:S B.B B.aB B.b
I3:Ba.B B.aB B.b
I4:BaB.
I5: Bb.
I6: SBB.
I0:S’ S,# S BB,#
B aB,a/bB b,a/b
I1:S’ S,#
I2:S BB,# B aB,#B b,#
I5:S BB,#
I6:S aB,# B aB,#B b,# I3:B aB,a/b
B aB,a/b B b,a/b
I4:B b,a/b
I7:B b,#
I9:B aB,#
I8:B aB,a/b
s
B B
a
b
b
b
B
B
aa
a
LR(1) 项目集和转换函数
b
例: G(S):S BB BaB B b
拓展文法 : (0)S’ S (1) S BB (2) BaB (3) B b
对于该例题 ,只要仔细分析该文法的 LR(1)每个项目集的项目 , 不难发现 ,即使不考察搜索符 ,它的任何项目都没有冲突 , 因此它实际上是一 LR(0)文法 ,可以构造一个 LR(0)项目集 . 可以得知它的LR(0)分析器只含 7个状态,而现在 LR(1)分析器却含有 10个状态,其中 I3和 I6, I4和 I7, I8和 I9分别为同心集。
LALR(1) 分析 :
合并同心集 I36 I47 I89
I3:B aB,a/b B aB,a/b B b,a/b
I6:S aB,# B aB,#B b,#
I4:B b,a/b
I7:B b,#
I8:B aB,a/b
I9:B aB,#
ACTION GOTO
a b # S B状态0 S3 S4 1 21 acc2 S6 S7 53 S3 S4 84 r3 r3 5 r1 6 S6 S7 97 r38 r2 r2 9 r2
上例的 LR(1) 分析表
0 S3,6 S4,7 1 2
1 acc2 S3,6 S4,7 5
3,6 S3,6 S4,7 8,9
4,7 r3 r3 r35 r18,9 r2 r2 r2
a b # S B
ACTION GOTO状态
合并同心集后 LALR(1) 分析表
同心集合并后仍不包含冲突,因此该文法满足 LALR(1) 要求。 合并同心集有几个问题需要说明。 (1) 同心集是指心相同的项目集合并在一起,因此同 心集合并后心仍相同,只是超前搜索符集合为各 同心集超前搜索符集合的和集。
(2) 对于合并同心集后的项目集的转换函数仍为同心集,因为相同的心之转换函数的心仍相同,即仍属同心集,所以合并同心集后转换函数为自动合并,如例中 I3和 I6为同心集,它们的转换函数分别为: I3 : GO(I3, a)= I3 GO(I3, b)= I4 GO(I3, B)= I8 I6 : GO(I6, a)= I6 GO(I6, b)= I7 GO(I6, B)= I9 然而, I3和 I6, I4和 I7, I8 和 I9分别都为同心集。
(3) 若文法是 LR(1)文法,合并同心集后若有冲突也只可能是归约 -归约冲突,而不可能产生移进 -归约冲突 对 (3) 的证明不防假设某 LR(1)文法的项目集 Ik 与 Ij为同心集, 其中: Ik 为: [A→α·, u1] [B→β·aγ,b] Ij 为: [A→α·,u2] [B→β·aγ,c] 其中 u1, u2分别为超前搜索符集合,因为假设文法是 LR(1)的,所以不可能有移进 -归约冲突,也就是在 Ik 中 {u1}∩{a}= ,在 Ij中有 {u2}∩{a}= ,显然合并同心集后({u1}∪{u2})∩{a}=
(4) 合并同心集后对某些错误发现的时间会产生推迟现象,但错误的出现位置仍是准确的。这意味着 LALR(1)分析表比 LR(1)分析表对同一输入串的分析可能会有多余归约。这个问题将在后面用例子来说明。
现在我们举例说明由于合并同心集可能对某些错误发现的时间产生推迟现象。 上例所给文法识别的句子集合是正规式, a*ba*b,也就是该文法可推出的句子必须含有两个 b 且以 b 为结尾。因而输入串若为 ab#,显然不是这个文法能推出的句子。但用LR(1)分析表分析和用 LALR(1)分析表分析时发现错误的时间不同,现将分析步骤分别写出如下。
对例输入串 ab# 用 LR(1) 分析的过程
1 0 # ab# S3
2 03 #a b# S4
3 034 #ab # 出错
步骤 状态栈 符号栈 输入串 ACTION GOTO
表 7.14 对输入串 ab# 用 LALR(1) 分析的过程
1 0 # ab# S3,6
2 0(3,6) #a b# S4,7
3 0(3,6)(4,7) #ab # r3 (8,9)
4 0(3,6)(8,9) #aB # r2 2
5 02 #B # 出错
步骤 状态栈 符号栈 输入串 ACTION GOTO
我们已经知道任何一个二义性文法绝不是 LR类文法,也不是一个算符优先文法或 LL(k)文法,任何一个二义性文法不存在与其相应的确定的语法分析器,但是对某些二义性文法,我们可以人为地给出优先性和结合性的规定,从而可能构造出比相应非二义性文法更优越的 LR分析器。
二义性文法在LR分析中的应用
例:表达式文法: E’ E E E+E E E*E
E (E)E i
如:规定在表达式文法中i 的优先性最高 ‘*’ · > ‘+’ ‘*’ 和 ‘+’ 都服从左结合
当前符号
状态
+ * ( ) i # EI0:E’EEE+EEE*EE(E)Ei
I2:E (E)EE+EEE*EE(E)Ei
I3:E i
I1:E’ EE E+EE E*E
I1: I4:E E+EEE+EEE*EE(E)Ei
I5:E E*EEE+EEE*EE(E)Ei
acc
算术表达式二义性文法的 LR(0) 项目集及状态转换矩阵 P149-P150
当前 符号
状态
+
*
(
)
i
#
E
I2: I2: I3: I6:
E (E) E E+E E E*E
I3: I4: I2: I3: I7:
E E+E E E+E E E*E
I5: I2: I3: I8: E E*E E E+E E E*E
I6: I4: I5: I9: E (E)
I7: I4: I5: I8: I4: I5: I9:
在 I1 , I7 , I8 中 存在移进 - 归约冲突
在 I1 , I7 , I8 中 存在移进 - 归约冲突
I1 : E’ E•
E E•+E E E•*E
I7 : E E+E•
E E•+E E E•*E
I8 : E E*E•
E E•+E E E•*E
在 I1 中 : 归约项目 E′→E· 实际上为接受项目。由于 FOLLOW(E′) = {#} 也就是只有遇到句子的结束标志 '#' 号才能接受,因而与移进项目的移进符号 '+' , '*' 不会冲突,所以可用SLR(1) 方法解决,即遇当前输入符为 '#' 时则接受,遇 '+' 或 '*' 号时则移进。在 I7 和 I8 中,由于归约项目 [E→E + E·] 和[E→E*E·] 的左部都为非终结符 E ,而FOLLOW(E)={#, +, * , )} ,而移进项目均有 '+' 和 '*' 也就存在FOLLOW(E)∩{ +, *}≠ 对于 I7 和 I8 规定:‘ *’优先于‘ +’ ,都服从左结合I7 :遇‘ *’ 移进 遇‘ +’ 归约I8 :遇‘ +’ ,‘ *’ 都归约
LR分析的特征是: ◇ 归约过程是规范的: ◇符号栈中的符号是规范句型的前缀,且不含句柄以后的任何符号 (活前缀 )。 ◇ 分析决策依据栈顶状态和现行输入符号是什么来决定的。 ◇为构造 LR分析表,可先构造识别活前缀和句柄的 DFA。 ◇ LR分析器的关键部分是分析表的构造 , 对一个文法能判断是否是 LR类文法,能否构造相应的 LR分析表,并能对给定的输入串进行分析以决定该输入串是否为所给文法的句子
小结
◇ 四种 LR 类型: LR(0) SLR(1) LALR(1) LR(1) 功能逐个增强 ◇ 四种 LR 类型的文法是真包含关系