4.2.4 lr 分析法

38
4.2.4 LR 4.2.4 LR 分分分 分分分 • 分分分分分分分分分分分分分分分分分分 分分分分 , 分分分分分 ; • 1965 分 D.Knuth 分分分分分分分分分分 LR(k) 分分分分 ; LR LR 分分 分分 : 分分分分分分分分分分分分分分 ; • 分分分分分 , 分分分分分分分分分分分分分分分分分分分分分 , 分分分分分分分 k 分分分分分分分分分分分分分分分分 ( 分分 分分分 分分分 分分 、、、 ); • LR 分分分分分分分分分 , 分分分分 , 分分分分分分分分 , 分分分分分分分分分分 分; 分分 CFG 分分分分分分分分 LR 分分分

Upload: hilda-zamora

Post on 30-Dec-2015

89 views

Category:

Documents


3 download

DESCRIPTION

4.2.4 LR 分析法. 迄今为止我们所学的分析方法对文法都有一定的要求 , 即有局限性 ; 1965 年 D.Knuth 提出了分析效率极高的 LR(k) 分析技术; LR 分析 : 自左至右扫描的自底向上的分析; 在分析的每一步,只须根据分析栈中的已移进的和已归约出的符号,并至多向前扫描 k 个字符就能确定应采取什么分析动作( 移进、归约、接受、报错 ); LR 分析对文法要求很少 , 效率极高 , 且能及时发现错误 , 是目前最广泛使用的方法 ; 一般用 CFG 描述的语言均可用 LR 分析法. LR 分析综述. 计算机理论研究已证明了如下结论 : - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 4.2.4   LR 分析法

4.2.4 LR4.2.4 LR 分析法分析法• 迄今为止我们所学的分析方法对文法都有一定的要求 ,

即有局限性 ;• 1965 年 D.Knuth 提出了分析效率极高的 LR(k) 分析技术 ;• LRLR 分析分析 : 自左至右扫描的自底向上的分析 ;• 在分析的每一步 , 只须根据分析栈中的已移进的和已归

约出的符号 , 并至多向前扫描 k 个字符就能确定应采取什么分析动作 ( 移进、归约、接受、报错 );

• LR 分析对文法要求很少 , 效率极高 , 且能及时发现错误 ,是目前最广泛使用的方法 ; 一般用 CFG 描述的语言均可用 LR 分析法

Page 2: 4.2.4   LR 分析法

LR 分析综述分析综述• 计算机理论研究已证明了如下结论 :• LR(k) 文法是无二义性文法 ;• LR(k) 文法与 LR(1) 文法等价 .• 由于常见的程序设计语言均能由 LR(1) 文法产生 , 因此我们只讨论 k=0,

1 两种情况 ;• 本节中 , 我们将先介绍 LR 分析器的逻辑结构及工作原理 , 再分别介绍

几种 LR 分析器 ( 即 LR(0),SLR(1),LR(1) 和 LALR(1)) 的构造 ;• LR(0) 简单 , 能力低 ; SLR(1) 能力强于 LR(0); LR(1) 能力强 , 但分析

表大 ;• LALR(1) 能力介于 SLR(1) 与 LR(1) 之间 , 表大小与 SLR(1) 相同 , 是最

常用的分析方法

Page 3: 4.2.4   LR 分析法

LR 分析器的逻辑结构及工作原理分析器的逻辑结构及工作原理• 分析器自左至右地扫描输入串的

各符号,并根据当前分析栈的内容及正扫描的符号按分析表的指示完成相应的动作。

• 分析栈记录了已分析的内容及当前的状态;

• 开始时,栈内放入 # 及开始状态S0 ,随着分析的深入,栈内容总是刻划了当前的状态,及分析的“历史历史”。

总控程序

分析表

分析栈

a1 a2 … ai …an#

Sm Xm

…S1 X1

S0 #

分析栈

的内容

Page 4: 4.2.4   LR 分析法

LRLR 分析表分析表• LRLR 分析表分析表是 L

R 分析器的核心,它由分析动作 (ACTION) 表和状态转移 (GOTO) 表两个子表组成 ;

• ACTION[SACTION[Smm,a,aii]] 指明当栈顶为 Sm, 输入符为 ai 时应完成的分析动作 ;

• GOTO[SGOTO[Smm,X,Xii]]指明当分析栈移进一个输入符号或按某产生式进行归约后所要转移到的下一状态 .

a1 a2 … al

S1 ACTION(S1

,a1]ACTION(S1 ,a2]

… ACTION(S1 ,al]

S2 ACTION(S2

,a1]ACTION(S2 ,a2]

… ACTION(S2 ,al]

… … … … …

Sn ACTION(Sn ,a1]

ACTION(Sn ,a2]

… ACTION(Sn ,al

]X1 X2 … Xl

S1 GOTO(S1

,X1]GOTO(S1

,X2]… GOTO(S1 ,

Xl]

S2 GOTO(S2

,X1]GOTO(S2

,X2]… GOTO(S2 ,

Xl]

… … … … …

Sn GOTO(Sn ,X1]

GOTO(Sn ,X2]

… GOTO(Sn ,Xl]

ACTION 表

GOTO 表

Page 5: 4.2.4   LR 分析法

LR 分析器的工作过程分析器的工作过程1. 分析开始时 , 首先将初始状态 S0

及 # 压入栈 ;2. 设在分析的某一步 , 分析栈和余

留输入串处于格局 :

S0S1S2…Sm

# X1X2…Xm aiai+1ai+2…an#

用 Sm,ai 查 ACTION 表 , 并根据指示完成相应的动作 , 分析动作有移进 ,归约 , 报错 , 接受四种 :

(1) 移进 句柄尚未在栈顶形成 , 正期待继续移进输入符号以形成句柄 , 故将 ai 压入栈 :

S0S1S2…Sm

# X1X2…Xmai ai+1ai+2…an#

再用 Sm,ai 查 GOTO 表 , 设得 Sm+1, 将 Sm+1 压入栈 :

S0S1S2…SmSm+1

# X1X2…Xmai ai+1ai+2…an#

Page 6: 4.2.4   LR 分析法

LR 分析器的工作过程分析器的工作过程 (( 续续 ))(2) 归约 rj 其中 rj 表示按 P 的第 j 产

生式 AXm-r+1Xm-r+2Xm 进行归约 ;这表明栈顶部的符号串 Xm-r+1Xm-r+2Xm 是当前句型的句柄句柄 . 归约的方法是 , 将栈顶符号串 Xm-r+1Xm-r+2Xm(r 个符号 ) 从栈顶退出 , 再将A 压入栈 , 此时的格局为 S0S1S2…Sm-r # X1X2…Xm-rA aiai+1ai+2…an#

再以( Sm-r,A )查 GOTO 表 , 得 Sk, 将 Sk 压入栈 , 得到格局 : S0S1S2…Sm-rSk # X1X2…Xm-rA aiai+1ai+2…an#

注意 , 完成归约动作时 , 输入串指针并未移动 .

(3) 若 ACTION(Sm,ai)=“acc”, 则表明当前输入串已被成功地分析完毕 , 结束 ;

(4) 若 ACTION(Sm,ai)=“ERROR”,则表明当前输入串有语法错误 , 转入出错处理程序 ;

3. 重复步骤 2. 的工作 , 直到分析的某步 , 栈顶出现“ acc” 为止 . 此时的格局应为 :S0SZ # # Z其中 ,Z 为文法 的开始符号 , SZ 是使 ACTION(SZ,#)=“acc” 的唯一状态 .

Page 7: 4.2.4   LR 分析法

LR 分析实例分析实例• 设已给文法 G[L]: 1. LE,L 2. LE 3. Ea 4. Eb• 文法相应的分析表为

  a b , #

0 S S    

1       ac

2     S r2

3     r3 r3

4     r4 r4

5 S S    

6       r1

  a b , # L E

0 3 4     1 2

1            

2     5      

3            

4            

5 3 4     6 2

6            

ACTIONACTION 表 GOTOGOTO 表

Page 8: 4.2.4   LR 分析法

分析表的合并• 从分析表的功能及实例中 , 我们可看出 ,GOTO 表中所有关于终结

符的状态转换都与相应的 ACTION 表相关,因此我们可将其合并到 ACTION 表中,只将关于 VN 符的状态转换保留在 GOTO 表中:

状态

ACTION GOTOa b , # L E

0 s3 s4     1 21       ac    2     s5 r2    3     r3 r3    4     r4 r4    5 s3 s4     6 26       r1    

在实际的分析表中,大多数表项内容为 ERROR ,若略去不填,则分析表是一稀疏表,可采用紧凑的格式存储。

Page 9: 4.2.4   LR 分析法

符号串符号串“ a,b,a” 的的 LRLR 分析过分析过程程

步骤 状态 栈中符号 余留符号 分析动作 下一状态

1 0 # a,b,a# s3 3

2 03 #a ,b,a# r3 GOTO[0,E]=2

3 02 #E ,b,a# s5 5

4 025 #E, b,a# s4 4

5 0254 #E,b ,a# r4 GOTO[5,E]=2

6 0252 #E,E ,a# s5 5

7 02525 #E,E, a# s3 3

8 025253 #E,E,a # r3 GOTO[5,E]=2

9 025252 #E,E,E # r2 GOTO[5,L]=6

10 025256 #E,E,L # r1 GOTO[5,L]=6

11 0256 #E,L # r1 GOTO[0,L]=1

12 01 #L #    

Page 10: 4.2.4   LR 分析法

LR 分析器的总控程序• parser( ){• 初始化;• while((item=ACTION[TopStat][InpSym]))!=Acc){• if(item==ERROR) error( );• if(item==sj){push(j);advance( );}• else reduce(i);/* item== ri*/• }• accept( );• }

Page 11: 4.2.4   LR 分析法

二、 LR ( 0 )分析表的构造• LR(0) 分析就是 k=0 时的 LR(k) 分析 . 即在分析的每一步 , 根据

当前的栈顶状态确定下一步动作 .• 为了给出构造 LR(0) 分析表的算法 , 我们首先引入一些重要的

概念和术语1. 规范句型的活前缀 (viable prefix)

从前面例子的分析过程可知 , 将栈内符号与未扫描的输入串拼接起来 , 可得一规范句型 . 即栈内符号串总是规范句型的前缀 ,且不含句柄右侧的符号 .原因原因 : 句柄一旦在栈顶形成 , 就不再移进新符号 , 而是要进行归约了 .我们把具有上述性质的符号串称为规范句型的活前缀规范句型的活前缀 .规范句型的活前缀规范句型的活前缀有两个要点 : (1) 它是规范句型的前缀 ; (2) 它不含句柄右侧符号

Page 12: 4.2.4   LR 分析法

规范句型的活前缀规范句型的活前缀 ( 续 )• 在前面的例子中的第五行 : #E,b ,a# 中 ,b 是

当前句型的句柄 , “”, “E” , ”E ,” , ”E , b” 都是 ~.

• LR 分析过程实际上就是一个逐步产生 ( 识别 )~的过程 .

• 在分析的每一步 , 栈内符号总是 ~, 且与此时栈顶符号相关 .

• 提示提示 : 若能构造一个识别所有活前缀的自动机识别所有活前缀的自动机 ,则构造分析表就不难了 .

Page 13: 4.2.4   LR 分析法

2. LR(0) 项目集• 由 ~ 不含句柄右侧符号这一性质可知 ,~ 与当前句柄的

关系只能是下述三种情况之一 :(1) 句柄全部在 ~ 中 ( 句柄是 ~ 的后缀 );(2) ~ 只含句柄的部分符号 (~ 的后缀是句柄的前缀 );(3) ~ 不含任何句柄符号 .• 对于 (1), 应进行归约 : A, 记为 A;• 对于 (2), 应移进 ( 句柄的后半部分 ),A12;• 对于 (3), 期望移进一产生式的右部 : A• 我们把右部添加了一个“”的产生式 , 称为 LR(0)LR(0) 项项

目目

Page 14: 4.2.4   LR 分析法

LR(0) 项目集 ( 续 )• 同一产生式 ( 设右部有 n 个符号 ) 对应了若干 (n+1) 个 LR(0) 项目 ,

每个项目反映了栈顶所处的不同状态 .• 若 A∈P, 则对应了唯一的 LR(0)LR(0) 项目 A→ ;• 所有的 LR(0)LR(0) 项目是构造识别活前缀的自动机的基础。• 例 SA | B AaAb | c BaBb | c 为识别方便 , 我们引入

新开始符 S’ 及产生式 S’ S, 得到拓广的方法 G’.⒈s’ S ⒉ S’S ⒊S A ⒋S A ⒌A aAb ⒍ A aAb ⒎ A aAb ⒏ A aAb ⒐A c ⒑A c ⒒S B ⒓S B ⒔ B aBb ⒕ B aBb ⒖ B aBb ⒗ B aBb ⒘B d ⒙ B d

• 上面的 LR(0) 项目可用一对整数 (m,n) 表示 , 其中 ,m 表示第 m 产生式 ,n 表示圆点在该产生式的位置 ;

• 如项目 1. 可用 (0,0) 表示 , 项目 14. 可用 (5,1) 表示等等 .

Page 15: 4.2.4   LR 分析法

LR(0) 项目集 ( 续 )• 由于不同的项目反映了分析过程的不同情况 , 因此 , 我

们可根据其不同作用将其分类 .• 对于形如 A→ 的项目 , 此时应进行归约 , 因此称为归

约项目 ; 如前例中的 2,4,8,10,12,16,18( 蓝色项目 ) 等 ;• 对于形如 AX, ( XVT, 可以是空串 ) 的项目 , 则

有待于移进一个 VT 符号 X 到栈中 , 因此称为移进项目 ,如前例中的 5,7,9,13,15,17( 红色项目 ) 等 ;

• 对于形如 AX, ( XVN, 可以是空串 ) 我们期待移进若干符号之后并将其归约为 X, 因此称为待约项目 ,如前例中的 1,3,6,14( 绿色项目 ) 等 .

Page 16: 4.2.4   LR 分析法

3. 识别所有规范句型全部活前缀的 DFA• 该 DFA的每个状态由若干 LR(0)项目组成的集合表示 ;• 首先 ,初态 I0含有项目 S’→●S,期待将要扫描一个符

号串恰好匹配 S. 由于 S是 VN符 ,不可能从输入串中读出 ,因此应把可能构成 S的产生式相应的的项目 S→●A及 S→●B列入项目集中 ;

• 同理 ,由 A,B为 VN符 ,应将 A→●aAb, A→●c, B→●aBb, B→●d放入项目集中 .因此 ,I0由如下项目组成 : S’→●S, S→●A, S→●B, A→●aAb, A→●c, B→●aBb, B→●d

• 其中 , S’→●S称为基本项目 .从 S’→●S出发构造整个项目集的过程为求基本项目的闭包过程 ,即整个项目集称为基本项目集的闭包 CLOSURE({S’→●S})CLOSURE({S’→●S}).

Page 17: 4.2.4   LR 分析法

求项目集闭包的算法求项目集闭包的算法• 设 I 为一项目集 , 构造 I 的项目集闭包 CLOSU

RE(I) 的算法如下 :1. I CLOSURE( I );2. 若 A→XCLOSURE(I), XVN,

则任何 X- 产生式 X→P, XCLOSURE(I);3. 重复上述过程 , 直到 CLOSURE(I) 不再增大 .

Page 18: 4.2.4   LR 分析法

构造 DFA 的方法• 有了初态 I0, 为求下一状态的方法是 :• 设 I 为一状态 ,X∈V, 且 A → X I, 则当分析器识别出 X 后 , 将

进入下一状态 Ii 且必含有项目 A →X ( 称为项目 A → X 的后继 ), 对于整个项目集而言 , 关于 X 的后继可能有多个 , 将其合并在一起构成集合 J ( 即下一状态 Ii 的基本项目 ), 再通过求闭包得出Ii全部项目 : Ii =CLOSURE( J ). 为指明 Ii 是 I 的后继 , 记 GO(I,X)= Ii;

• 例如 , 对于上例 , 我们有 :I1=GO(I0,S)=CLOSURE({S’ →S·})={S’ →S·}; I2=GO(I0,A)=CLOSUR

E({S→A·})={S→A·}; I3=GO(I0,B)=CLOSURE({S→B·})={S→B·}; I4=GO(I0,a)=CLOSURE({A→a·Ab,B→a·Bb})={A→a·Ab,B→a·Bb, A→· aAb, B→ · aBb, A→·c,B→·d};

… ( 参见 P157)

Page 19: 4.2.4   LR 分析法

构造 DFA 的方法 ( 续 )• 重复上述构造新状态的过程 , 我们可得到全部

的状态 , 其集合称为方法 G 的 LR(0) 项目集规范族 , 记为 C( 上例中 ,C={I0,I1,…,I10});

• 至此 , 我们所要构造的识别方法 G[S] 全部活前缀的 DFA 为 : M=(C,V,GO,I0,C); 其中 ,

• 状态集及终态集为项目集规范族 C;• 字母表为 V=VN∪VT∪{S’}• 初态为 I0;• 转换函数为 GO;

Page 20: 4.2.4   LR 分析法

I0:S’→·SS→·AS→·B

A→·aAbA→·c

B→·aBbB→·d

I1: S’→S·S

I2: S→A·A

I3: S→B·B

I4:A→a·AbA→·aAb

A→·cB→a·BbB→·aBb

B→·d

a

I5: A→c·

cc

I6: A→d·

d

d

a

I7: A→aA·b

I9: B→aB·b

I8: A→aAb·

I10: B→aBb·

B

A

b

b

图 4-16 识别 G[S]G[S] 全部活前缀的 DFADFA

Page 21: 4.2.4   LR 分析法

LR(0) 分析表的构造分析表的构造• 有了识别文法 G 的全部活前缀的 DFA ,就可构造相

应的 LR(0) 分析表 .• 应指出 , 每个项目集代表了分析过程中的一个状态 ,

且其每个项目与分析动作相关 . 因此要求每个项目集的诸项目是相容的 , 即在同一项目集中 , 不应出现 :

1. 移进项目与归约项目并存 ;2. 多个归约项目并存 .

• 若文法 G 满足上述条件 , 即不含上述冲突项目 , 则称G 为 LR(0) 文法 .

• 显然 , 只有当一文法是 LR(0) 文法时 , 才能构造出无冲突动作的分析表来 .

Page 22: 4.2.4   LR 分析法

构造 LR(0) 分析表分析表的算法我们用序号i表示状态Ii,则填写 LR(0)分析表的方法如下:(1)对于每个项目集 Ii中形如 A→X 项目,若 GO(Ii,X)= Ij,则 ,若 X∈VT,则置 ACTION[i,X]=sj,否则 (X∈VN),置 GOTO[i,X]=j;

(2)若归约项目 A→ 属于 Ii,且 A→ 是P中第 j产生式,则对于 aVT{#},置 ACTION[i,a]=rj

(3)若接受项目 S’ →S·属于 Ii,则置 ACTION[i,#]=“acc”;

(4)在表中其它未填入信息的栏目中均填“ ERR”.在存储 ACTION表时,可用正负值分别表示归约和移进.

Page 23: 4.2.4   LR 分析法

  ACTION GOTO

a b c d # S A B0 s4   s5 s6   1 2 31         acc      

2 r1 r1 r1 r1 r1      3 r2 r2 r2 r2 r2      4 s4   s5 s6     7 95 r4 r4 r4 r4 r4      6 r6 r6 r6 r6 r6      7   s8            8 r3 r3 r3 r3 r3      9   s10            

10 r5 r5 r5 r5 r5      

表 4-13 G[S] 的 LR(0)LR(0) 分析表

Page 24: 4.2.4   LR 分析法

三、 SLR(1)SLR(1) 分析表的构造• 须指出 , 常见程序设计语言都不是 LR(0) 的 , 所以 LR

(0) 分析表缺乏实用性 . 例如 , 典型的分程序结构 :B’ →B B →bD;Se D →D;d|d S →s;S |s

• 识别上述文法活前缀的 DFA 及相应的 LR(0) 分析表见教材中 P160. 在状态 I8 中 , 出现了“移进 - 归约”冲突 .另外 , 甚至常见的文法 G[E] 也不是 LR(0) 文法 .

• 实际上 , 常见的程序设计语言相应的分析表冲突项目是很少的 , 对分析表做一定的修改 , 便可消除“移进 - 归约”或“归约 - 归约”冲突 .

Page 25: 4.2.4   LR 分析法

SLR(1)SLR(1) 分析表的构造(续)• 考虑 LR(0) 分析表的造表算法规则规则 (2)(2), 对于归约项目 A→·, 不

管下一输入符号是谁 , 均进行归约 . 这显然是一个武断的决定 .• 若 Ii 中同时含有 B→·b 及 C→· 两类项目时 , 上述填表方

法必然得到冲突的分析表 .• 一般地 ,Ii={A1→·a11,…,Am→·amm,B1→·,…,Bn→·}• 如果能根据下一输入符号 a 对上述冲突加以区分 , 则冲突可解决 .• 当集合 FOLLOWFOLLOW(Bk)(1≦k≦n) 与 {a1,a2,…,am} 两两互不相交时 ,

则可按下述方法解决冲突 : aVT{#},

IF a{a1,a2,…,am} THEN ACTION[i,a]=sj ELSE IF aFOLLOW(Bj) THEN ACTION[i,a]=rBj

ELSE ACTION[i,a]=“ERR”

Page 26: 4.2.4   LR 分析法

SLR(1)SLR(1) 分析表的构造(续)• 上述方法就是 SLR(1) 规则 (Simple LR(1)). 按照 SLR

(1) 规则 , 只须将 LR(0) 分析表的填表规则 (2) 修改为( 注 : 其它规则不变 !):(2‘) 若归约项目 A→ 属于 Ii, 且 A→ 是 P中第 j产生式 ,则对于 aFOLLOW(A), 置 ACTION[i,a]=rj

• 对于给定的文法 G, 若其相应的 SLR(1) 分析表无冲突项 , 则称 G 是 SLR(1) 文法 .

• 例 分程序文法 G[B] 中 ,I8={Ss;S, Ss} 含有冲突 ,但 FOLLOW(S)={e} ≠{;}, 故冲突可解决 .

• G[B] 的 SLR(1) 分析表见 P161 表 4-15. 这里略去 .

Page 27: 4.2.4   LR 分析法

四、 LR(1) 分析表的构造• SLR(1) 分析表简单实用 , 但并不能完全解决问题 ;• 例如 , 文法 S’→S S→CbBA A→Aab|ab B→C|Db C→a D

→a 相应的 DFA 见书中 P162图 4-18.其中项目集• I10={S→CbBA·,A→A·ab} 中存在“移进 - 归约”冲突 , 由 FOLL

OW(S)={#} ≠{a}, 冲突是可解决的 . 但项目集 I8={C→a·,D→a·} 中 , FOLLOW(C)={a,b}, FOLLOW(D)={b}, SLR(1) 方法不能解决此冲突 .

• 从 SLR(1)解决问题的方法看 , 对于归约项目 A→, 只要是 FOLLOW(A) 中的符号均可按此产生式进行归约 ,这也有一定的片面性 ,因为没有考虑所在的“环境” .

• 当在栈顶形成时 ( 设此时栈内容为 #, 输入符为 a),若强行将归约为 A( 栈内容 : #A), 但 #Aa又不是任何规范句型的前缀时 ,这个归约就是无效的 .

Page 28: 4.2.4   LR 分析法

LR(1) 分析表的构造(续)• 例如 , 对于上述文法的规范句

型 Cbabab, 当分析达到格局I0I2I4I8 bab # C b a 时 , 由于输入符 b∈FOLLOW(C), 我们若用 C来归约 ,得到I0I2I4I6 bab # C b C 但 CbC不是任何规范前缀 !因此在移进 b 之前 , 分析将报错 .

• 因此 , 将 #a 归约成 #Aa… 的前提条件不仅仅是要求a∈FOLLOW(A),还必须要求Aa 是某规范句型的前缀 .

• 为了确保上述条件 , 应在原来的每个 LR(0) 项目 [A]中放置一向前搜索符号 a: [A,a],称为 LR(1) 项目 .

• 为使分析的每一步都能得到规范句型的活前缀 ,还应要求每个 LR(1) 项目对相应的活前缀是有效的 .

Page 29: 4.2.4   LR 分析法

对活前缀有效的 LR(1) 项目定义 LR(1) 项目 [A,a] 对活前缀 = 有效 ,iff 存

在规范推导 S Ay y yVT* 且满足条件 :

(1) 当 y≠ 时 ,a∈FIRST(y);(2) 当 y= 时 ,a=#.

• 例如 , 对于上例中文法 , 有

S CbBACbBabCbDbab A y

可知 [B→D·b,a] 对前缀 = =CbD 有效

SCbDbabCba bab

A = y

可知 [D→a·,b] 对前缀 = =Cba 有效

Page 30: 4.2.4   LR 分析法

求 LR(1) 项目集闭包算法• 类似于 LR(0) 分析 , 识别文法全部活前缀的 DFA 的状

态是由 LR(1) 项目集表示的 . 每个项目集由若干对相应的活前缀有效的 LR(1) 项目组成 .

• 对每个 LR(1) 项目集 I, 相应的闭包 CLOSURE(I) 的定义为

(1) ICLOSURE(I);(2) 设项目 [A B , a] CLOSURE(I), 并设它对活前缀

= 有效 , 则对所有形如 B 的产生式 , 及每个 bFIRST(a), 项目 [B , b] 也对有效 ( 证明见后 ), 即将[B , b] =>CLOSURE(I);

(3) 重复 (2), 直到 CLOSURE(I) 不再增大 .

Page 31: 4.2.4   LR 分析法

新增项目对活前缀仍有效的证明

• 事实上 , 当 [A B,a] 对 = 有效时 , 由定义 , 有 : SAyBy yVT

*

且 aFIRST(y) {#}

即 SAyBa VT*{#}

无论是否为 , 可知 a 总能推出终结符号串 : ab’

由 aVT*{#}, FIRST(a)=FIRST(a), 从而有规范推

导 : S B b’ b’ 即 [B , b] 对活前缀 = 有效 .

Page 32: 4.2.4   LR 分析法

GO 函数及 DFA 的构造方法• 为求 GO(I,X), 其中 I 为 LR(1) 项目集 ,X∈V, 类似于 LR

(0) 方法 , 有 : GO(I,X)=CLOSURE(J)其中 , J={[ AX , a ] | [ AX , a ] I}

• 注意 , 每个 LR(1) 项目与其后继项目有相同的搜索符号• 采用与 LR(0) 类似的方法 , 可构造出文法 G 的 LR(1) 项

目集族 C 及状态转换图 (DFA).

Page 33: 4.2.4   LR 分析法

I0:

S’ → ·S, #

S→·CbBA, #

C → ·a, b

I1: S’ →S ·, #

S

I3: C→a ·, b

a

I2: S →C·bBA, #

C

I4:S→Cb·BA, #

B→·C, a

B →·Db, a

C → ·a, a

D →·a, b

b

I5:S→CbB·A, #

A→·Aab, #/a

A →·ab, #/a

I6: B→C ·, #

B

C

I8: C→a ·, aD →a ·, b

a

I7: B→D ·b, a

I9: B→Db ·, a

D

b

I11: A→a·b, #/a

I12: A→ab·, #/a

a

b

I10: S→CbBA·, #A →A·ab #/a

A

I13: A→Aa·b, #/a

I14: A→Aab·, #/a

a

b

图 4-19 文法 G[S] 的 LR(1) 项目集及DFA

Page 34: 4.2.4   LR 分析法

LR(1) 分析表的构造• 利用 LR(1) 项目集族 C 和 GO 函数构造 LR(1) 分析表的方法 :(1) 对于每个项目集 Ii 中的项目 [AX , a], 若 GO(Ii,X)=Ij则 ,I

F X∈VT THEN ACTION(i,X)=sj ELSE (X∈VN) GOTO(i,X)=j;(2) 若归约项目 [A , a] ∈Ii, A 是第 j 产生式 , 则 ACTION

(i,a)=rj;(3) 若 [S’ →S· , #] ∈Ii , 则 ACTION(i , #)=“acc”;(4) 其它 : ERROR. • 对于一文法 G 而言 , 若按上述方法所构造的分析表不含多重

定义的元素 , 则称此分析表为 LR(1) 分析表 . 凡具有 LR(1)分析表的文法称为 LR(1) 文法

Page 35: 4.2.4   LR 分析法

状态

ACTION GOTO

a b c S A B C D

0 s3     1     2  

1     acc          

2   s4            

3   r6            

4 s8         5 6 7

5 s11       10      

6 r4              

7   s9            

8 r6 r7            

9 r5              

10 s13   r1          

11   s12            

12 r3   r3          

13   s14            

14 r2   r2          

分析表实例

分析表实例

Page 36: 4.2.4   LR 分析法

五、 LALR(1) 分析简介• 从前面的介绍可知 , 每个 LR(1) 项目均由两部分组成 ,

一是 LR(0) 项目 , 称为 LR(1) 项目的核核 ; 一是向前搜索符号集 .

• 对于移进项目 , 搜索符集无作用 ; 对于归约项目 , 它指明了在扫描到哪些符号时进行归约 .

• 上述原理解决了 SLR(1) 分析中所不能解决的一些冲突 ,使 LR(1) 分析比 SLR(1) 分析的能力有明显的提高 .

• LR(1) 也有缺点 : 分析表状态数过大 , 使分析的效率降低 .• 为解决二者之间的能力与效率之矛盾 , 目前最流行的 LA

LR(1) 分析是最佳方案 .

Page 37: 4.2.4   LR 分析法

LALR(1) 分析简介 ( 续 )• LALR(1) 分析 (Lookahead-LR) 是由 F.DeRemer 提出

的 . 其基本思想就是将 LR(1) 项目集中的同心集合并 ,将其压缩为较小的 DFA, 若压缩过程并未带来新的冲突 ,则分析表可大大地简化 ( 状态数与 SLR(1),LR(0) 的 DFA 相同 ).

• 书中给出了文法 G[E] 的例子 . 合并前该文法有 22 个状态 , 合并后只有 12 个 .

• LALR(1) 方法的分析能力介于 LR(1) 与 SLR(1) 之间 .是目前最流行的分析技术 .

• 目前已有许多自动构造 LALR(1) 分析表的工具 , 最典型的当属 YACC. 其相关内容将在 < 编译原理课程设计> 中给大家作详细介绍 .

Page 38: 4.2.4   LR 分析法

关于各类文法的一些结论1. 任何 LR(K),LL(K) 及简单优先文法类都

是无二义性文法 ;2. 任何二义性的文法不可能性是 LR(K) 文

法 , 但借助其它方法 , 可对某些二义性文法建立无冲突的 LR(K) 分析表 ;

3. 每个 SLR(K) 文法必是 LR(K) 的 , 存在 LR(1) 文法 , 对任何 K, 它不是 SLR(K) 的 .

4. 各文法类之间的关系见 P175 图 4-24