1、第7章 LR分析法,LR(k)-从左向右扫描的右分析方法其中:k代表向后看k个符号来决定归约的句柄。 LR(0) :向后看0个符号来决定的句柄 LR(1) :向后看1个符号来决定归约的句柄 SLR(1) : 简单LR(1) (LR(0) + LR(1) ) LALR(1) :向前(压缩的)LR(1),7.1 LR分析概述,一个LR分析器由3个部分组成:(1)一个总控(驱动)程序。对所有的LR分析器总控程序 都是相同的。(2)一张分析表(或称分析函数),它由两部分组成: 动作表(ACTION)和状态转换表(GOTO)。(3)分析栈,包括:文法符号栈、状态栈。,LR分析器工作过程示意图:,7.1
2、LR分析概述,其核心是分析表: ACTION+GOTO其中:,Sj:表示将状态 j 移入状态栈,将符号a移入文法符号栈 rj:表示按j号产生式 (j) A (=XkXn 为当前句柄) 归约,则SP:=SP-(n-k+1),即从状态栈和符号栈顶去 掉n-k+1个符号,并把A移入符号栈(此时 SP.S=i) GOTOi,A=l 的 l 移入状态栈 S: Shift r: reduce,7.2 LR(0)分析,一、可归前缀和活前缀BBBBBBBBBB例:设文法: GS :S aAcBe 1 A b 2 A Ab 3 B d 4则对句子 abbcde 有如下规范推导: S aAcBe1 aAcd4e1
3、 aAb3cd4e1 ab2b3cd4e1,说明:这里称含有句柄的规范句型的前部串为可归前缀。本文法的可归前缀有: ab2 aAb3 aAcd4 aAcBe1,用可归前缀分析句子的过程:可归前缀 ab2 GS:SaAcBe 1 aAb3 Ab 2 aAcd4 AAb 3 aAcBe1 B d 4,分析栈 输入流 动作,# abbcde # 移入,#a bbcde # 移入,#ab bcde # 规约Ab2,#aA bcde # 移入,#aAb cde # 规约AAb3,#aA cde # 移入,#aAc de # 移入,#aAcd de # 规约B d4,#aAcB e # 移入,#aAcBe
4、 # 规约SaAcBe1,#S # 接受,问题是如何来求可归前缀串,7.2 LR(0)分析,活前缀: 形成可归前缀之前(包括可归前缀)的前缀例如: 可归前缀 活前缀 ab2 , a , ab aAb3 , a , aA , aAb aAcd4 , a , aA , aAc , aAcd aAcBe1 , a , aA , aAc , aAcB , aAcBe注意:活前缀的特点是,可能是多个可归前缀公用一个活 前缀,二、识别活前缀有限自动机例:设拓广文法: GS: SS 0 S aAcBe 1 A b 2 A Ab 3 B d 4基于句子abbcde的可归前缀及各自的有限自动机有:,7.2 LR
5、(0)分析,/同时又是句子识别态,/句柄识别态,NFA:,DFA:,状态栈 分析栈 输入流 动作,X # abbcde # 移入,X52 #ab bcde # 规约Ab2,X563 #aAb cde # 规约AAb3,X5 #a bbcde # 移入,X56 #aA bcde # 移入,X56 #aA cde # 移入,X567 #aAc de # 移入,X5674 #aAcd de # 规约B d4,X5678 #aAcB e # 移入,X56781 #aAcBe # 规约SaAcBe1,X0* #S # 接受,GS: SS 0 S aAcBe 1 A b 2 A Ab 3 B d 4,7.
6、2 LR(0)分析,三、活前缀及其可归前缀的一般计算方法定义7.2 设文法G,对于AVN,有,*R,LC(A)= | S=A , V*, VT*,其中:LC(A)是关于A恰不含句柄的活前缀集合。例如设拓广文法: GS: SS 0 S aAcBe 1 A b 2 | Ab 3 B d 4例由规范推导: S S aAcBe1得: LC(S)= LC(B)=aAc 由规范推导: S S aAb3cd4e1得:LC(A)=a即: 右边加上A的右部串(句柄)则构成一可归前缀, aAcd4e1, ab2b3cd4e1,7.2 LR(0)分析-活前缀及其可归前缀的一般计算方法,推论:若文法G中有产生式 BA
7、 则有 LC(B) () LC(A),因为 S=B=A,*R,R,所以 LC(B) LC(A),仅(恰)不含句柄的活前缀集合方程组的建立以文法 GS: SS S aAcBe A b | Ab B d为例,方程组如下: LC(S)= /令 LC(S)=LC(S) / SS LC(A)=LC(S) a LC(A) / S aAcBe, A Ab LC(B)= LC(S) aAc / S aAcBe,7.2 LR(0)分析-活前缀及其可归前缀的一般计算方法,为求解方便上述方程组简写为正则式方程组形式: S= S=S A=Sa + A / +:| B= S aAc用代入法求解得: S= S= A=a
8、+ A / A=a* =a B=aAc即求得: LC(S)= LC(S)= LC(A)= a LC(B)= aAc,7.2 LR(0)分析-活前缀及其可归前缀的一般计算方法,含有句柄的活前缀(可归前缀)记为: LR(0)CONTEXT(A ) 简写为: LR(0)C(A )则有:LR(0)C(A )=LC(A)例如对文法 GS: SS S aAcBe A b | Ab B d则其包含句柄的活前缀为:LR(0)C(SS)=LC(S) S = S=SLR(0)C(SaAcBe)= LC(S) aAcBe= aAcBe=aAcBeLR(0)C(Ab )= LC(A) b=abLR(0)C(AAb )
9、= LC(A) Ab =aAbLR(0)C(Bd )= LC(B) d=aAcd注意:上述可归前缀是有穷的,7.2 LR(0)分析-活前缀及其可归前缀的一般计算方法,例:设有文法GS:(1) SE (2) EaA (3) EbB (4) AcA (5) Ad (5) B cB (6) Bd求LC方程组为: LC(S)= /令 LC(E)=LC(S) / SE LC(A)=LC(E) aLC(A) c / EaA, AcA LC(B)=LC(E) bLC(B) c / EbB, BcB,7.2 LR(0)分析-活前缀及其可归前缀的一般计算方法,GS:(0) SE (2) EbB (4) Ad (
10、6) Bd (1) EAa (3) AcA (5) B cB化简为: 解方程得: S= S= E= E= A=a+Ac A=ac* B=b+Bc B=bc*,则其包含句柄的活前缀为:LR(0)C(SE)=LC(S) E = E=ELR(0)C(EaA)=LC(E) aA= aA=aALR(0)C(EbB)=LC(E) bB= bB=bBLR(0)C(AcA)=LC(A) cA = ac*cALR(0)C(Ad )= LC(A) d= ac*dLR(0)C(BcB)=LC(B) cB = bc*cBLR(0)C(Ad )= LC(B) d= bc*d注意:上述可归前缀是无穷的,7.2 LR(0)
11、分析-活前缀及其可归前缀的一般计算方法,归纳:1. 设S是开始符,若有 S 1| 2 | . | n 则 1,2,. ,n是可归前缀2. 设A是可归前缀, AVN ,A 为一产生 式,则 也是可归前缀。,例如对文法 GS: SS S aAcBe A b | Ab B d,已知可归前缀:S,由S求得的可归前缀: aAcBe,由aAcBe求得的可归前缀: aAcd 由aAcd求得的可归前缀:ab 、aAb,识别活前缀的FA略,7.2 LR(0)分析,四、LR(0)项目集规范族的构造 1. LR(0)项目项目:在产生式的右部适当位置添加一个圆点“”例如,产生式 SaAcBe 对应有6个项目 0 S
12、aAcBe /待归约句柄aAcBe,输入符为a 1 S a AcBe /a进栈,待分析A串 2 S aA cBe /A串归约为A,输入符为c 3 S aAc Be /c进栈,待分析B串 4 S aAcB e /B串归约为B,输入符为e 5 S aAcBe /栈顶形成句柄aAcBe,则可把 栈顶句柄aAcBe归约为S。,说明:1. 一个产生式对应的项目数为它的右部串长度加1 2. 空产生式,如A 仅由项目A 3. 圆点“”抽象的代表识别活前缀FA的一个状态,7.2 LR(0)分析-LR(0)项目集规范族的构造,2. 构造识别活前缀的NFAa) 列出文法所有产生式的项目,每个项目即为NFA的 个状
13、态以文法G 为例: G:SE EaA | bB AcA | d BcB | d 该文法的项目有: 1. S E 7. Ac A 13. EbB 2. SE 8. AcA 14. B cB 3. E aA 9. A d 15. Bc B 4. Ea A 10. Ad 16. BcB 5. EaA 11. E bB 17. B d 6. A cA 12. Eb B 18. Bd ,即识别该文法的活前缀的NFA有18个状态,7.2 LR(0)分析-LR(0)项目集规范族的构造,b) 状态之间的转换关系确定方法如下:若 i 项目为:X X1X2Xi-1 XiXn j 项目为:X X1X2Xi-1Xi
14、Xi+1 Xn则有:,或,若 XiVN ,设 Xi=A , 且有 i. X A k. A 则有:,7.2 LR(0)分析-LR(0)项目集规范族的构造,1. S E 7. Ac A 13. EbB 2. SE 8. AcA 14. B cB 3. E aA 9. A d 15. Bc B 4. Ea A 10. Ad 16. BcB 5. EaA 11. E bB 17. B d 6. A cA 12. Eb B 18. Bd ,识别活前缀的NFA:,7.2 LR(0)分析-LR(0)项目集规范族的构造,识别活前缀的DFA:,其中:每一个Ii称为项目集,7.2 LR(0)分析-LR(0)项目集
15、规范族的构造,项目分为四种:移进项目:形如 A a, aVT,移入指把a移入符号 栈。移进项目对应的状态为移进状态。待约项目:形如 A B , BVN,待约指等待把B串 归约为B后,再继续分析A的右部串。 归约项目:形如 A ,归约指可以把栈顶上的句柄 归约为左部的非终结符A。 归约项目对应的状态为归约状态。接受项目:形如 SS, 接受项目对应的状态为接受状态。,7.2 LR(0)分析,3. LR(0)项目集规范族的构造LR(0)项目集规范族:识别一个文法活前缀的DFA的项目 集的全体。例如,文法G的LR(0)项目集规范族为:,I0: SE EaA EBbI1: SEI2: EaA AcA A
16、d,I3: EbB BcB Bd I4: AcA AcA Ad I5: BcB BcB Bd,I6: EaA I7: EbBI8: AcAI9: BcBI10: Ad I11: Bd,7.2 LR(0)分析- LR(0)项目集规范族的构造,LR(0)项目集规范族的构造所需的两个函数: 闭包函数CLOSUER 设I为项目集,则定义 CLOSUER(I)=IB | B G, A BCLOSUER (I) , 转向函数GO设I为项目集, XVNVT ,则定义 GO(I, X)=CLOSUER (J) 其中: J= A X | A X I ,7.2 LR(0)分析- LR(0)项目集规范族的构造,使用
17、CLOSUER和GO函数构造 LR(0)项目集规范族,步骤如下:a) 置项目S S为初始项目集(初态集)的核,且求 CLOSUER(S S) 得到初始项目集b) 对初始项目集或其他所构造的项目集应用转换函数 GO(I, X)=CLOSUER (J) 求出新状态J的项目集.c) 重复b)直至不出现新的项目集为止。,G:SE EaA | bB AcA | d BcB | d,EaA,EbB,BcB,Bd,AcA,Ad,AcA,Ad,BcB,Bd,7.2 LR(0)分析- LR(0)项目集规范族的构造,LR(0)文法定义: 一个文法的LR(0)项目集规范族中的项集不存在 “移进归约”,或“归约归约”
18、冲突时,称该 文法为LR(0)文法。其中: a) “移进归约”冲突 在一个项目集中同时存在移进项目和归约项目,如: A a B b) “归约归约”冲突 在一个项目集中同时存在两个以上归约项目,如: A B ,7.2 LR(0)分析,4. LR(0)分析表的构造假设已构造出LR(0)项目集规范族为: C = I0,I1 ,In 其中:Ik为项目集的名字,k为状态名,令含有项目“SS”的项目集Ik为初始项目集,k为初始状态,则LR(0)分析表的构造算法如下: a) 若项目A aIk且GO(Ik , a)=Ij,aVT,则置 ACTIONk,a=Sj 其中,S代表“移入”,表示将符号a和状态j分别移
19、入符号栈顶和状态栈顶。 b)若项目A Ik,则对任何aVT#,置 ACTIONk,a=rj。 其中,r代表“归约”,表示用编号为j的产生式进行归约。,c) 若项目SS Ik,则置 ACTIONk,#=acc 其中,acc代表“接受”,表示整个句子已输入并归约 结束。 d) 若GO(Ik,A) = Ij,AVN,则置 GOTOk,A=j e) 凡不能用步骤a)d)填入的分析表中元素,均应填写 “报错标志”,为了表的清晰在表中用空白表示。,7.2 LR(0)分析- LR(0)分析表的构造,G:(0) SE (1) EaA (2) EbB (3) AcA (4) Ad (5) BcB (6) Bd,
20、acc,G 的LR(0)分析表,acc,7.2 LR(0)分析,(5) LR(0)分析器的工作过程1) 若ACTIONS,a=Sj ,aVT,则把a移入符号栈, j移入状态栈。2) 若ACTIONS,a=rj,aVT#,则用j号产生式 进行归约,并将两个栈指针减去k(k为j号产生式 右部串的长度)。再执行4)后返回。3) 若ACTIONS,#=acc , 则接受。4) 若GOTOS,A=J, AVN , 则将J移入状态栈。5) 若ACTIONS,a=空白,则转向出错处理。,步骤 状态栈 符号栈 输入串 ACTION GOTO,1 0 # bccd# S3,2 03 #b ccd# S5,3 0
21、35 #bc cd# S5,4 0355 #bcc d# S11,5 0355(11) #bccd # r6 9,步骤 状态栈 符号栈 输入串 ACTION GOTO,6 03559 #bccB # r5 9,7 0359 #bcB # r5 7,8 037 #bB # r2 1,9 01 #E # acc,7.3 SLR(1)分析,SLR(1)方法: 是LR(0)的一种改进,为了解决LR(0)项目 集中的项目“冲突”问题,在含有冲突项目 的状态下向前看一个符号的简单LR(1)方法 例如对实型变量说明文法: real ,i i 将该文法缩写后并拓广为G如下: (0) SS # (1) SrD
22、(2) DD , i (3) D i 则语句的形式为:r i , i , , i,7.3 SLR(1)分析,LR(0)项目集规范族及识别活前缀的DFA如图:,不难发现I3含有“移进归约”冲突项目:,SrD 移进项目DD , i 归约项目,说明该文法不是LR(0)文法。注意: 如果此时向后看一个符号, 后面的符号不是“,”而 是S的右继符“#”, 很显然则应按“(1) SrD”归约。,LR(0)矩阵:,7.3 SLR(1)分析,假定一个LR(0)项目集规范族中含有如下项目集: I=X b , A , B 若满足: FOLLOW(A) FOLLOW(B) b= 则当在状态I时面临输入符为a时,动作
23、可由如下规定决策 1) 若a=b,则移进 2) 若aFOLLOW(A) ,则用 A 归约 3) 若aFOLLOW(B) ,则用 B 归约 4) 此外,报错,7.3 SLR(1)分析,推广到一般情况:假定有如下项目集: I=A11 a11 , A22 a22 , , An m amm ,B11 , B22 , , Bnn 若满足:1,2, mFOLLOW(B1)FOLLOW(Bn) = 则当在状态I时面临输入符为a时,动作可由下规定决策: 1) 若a1,2, m ,则移进 2) 若aFOLLOW(Bi) ,则用 Bii归约 (i=1,2,n) 3) 此外,报错,说明:如果对于一个文法的LR(0)
24、项目集规范族的某些项 目集或 LR(0) 分析表中所含动作冲突都能用上述 方法解决,则称该文法是SLR(1)文法,所构造的 分析表为SLR(1)分析表。,7.3 SLR(1)分析,例: 设有拓广表达式文法: (1) S E (2) E E+T (3) E T (4) T T*F (5) T F (6) F (E) (7) F i,该文法的LR(0)项目集规范族及识别活前缀的DFA如图:,7.3 SLR(1)分析,其中在:I1: SE I2: ET I9: EE+T EE+T TT*F TT*F中存在“移进-归约”冲突,该文法是非LR(0)文法.但在I1中: FOLLOW(S)=# #+=因此该
25、冲突可以解决。在I2中: FOLLOW(E)=+ , ) , # + , ) , # *=因此该冲突可以解决。同理在I9中:FOLLOW(E)=+ , ) , # + , ) , # *=因此该冲突可以解决。,7.3 SLR(1)分析,(改进的) SLR(1)分析表 ( ACTION + GOTO) 的构造构造SLR(1)分析表,首先求LR(0) 项目集规范族 C = I0,I1,In 和GO函数,然后按下列步骤填写ACTION和GOTO表项:a) 若项目AaIk且GO(Ik,a)=Ij,aVT,则置 ACTIONk,a=Sjb) 若项目AIk,则对任何终结符aFOLLOW(A) 则置 ACT
26、IONk,a=rjc) 若GO(Ik,A) = Ij,AVN,则置GOTOk,A=j 。d) 若项目SSIk,则置ACTIONk,#=acc。e) 否则为空(代表err),说明:所谓“改进”是指对所有归约项目(包括没有冲突的 项目集)归约前都要向后看一符号,所看符号是 FOLLOW集中的符号则归约,否则为“报错”。,例:,FOLLOW(S)=# FOLLOW(E)=+ , ) , #,状态,步骤 状态栈 符号栈 输入串 ACTION GOTO,1 0 # i+i*i# S5,2 05 #i +i*i # r6 3,3 03 #F +i*i # r4 2,4 02 #T +i*i # r2 1,