41
第第第 LR 第第第 1965 第D.knuth 第第第第第 LR(K) 第第第 LR(K) 第第第第LR(K) 第第第第第第第第第第第第第第第第第第第第 第第第第第 第第 第第第第第第第第第第第第第第第第第第 一, 第第第第第第 第第第第第第第第 K 第第第第第 第第第第 第第第第 第第第第第第第第第第第第第第第第第第第第第 第第第第第第第第第第第第第第第第第第 第第第第第第第 。( 第第第第第第第第第第第)。 第第第第第 Xn+1, 第第第第 k 第第第 第第第第第 Xn+1 第第第 第第第 Xi…Xn 第第第第第第第第1 第第第第 第第第第第第第 U→XiXi+1…Xn 第第第第#X1X2…Xi-1UXn+1Xn+2…Xn+k…# #X1X2…Xi… Xn Xn+1Xn+2…Xn+kXn+k+1…# 第第

第六章 LR 分析法

  • Upload
    niles

  • View
    108

  • Download
    4

Embed Size (px)

DESCRIPTION

例: #X1X2…Xi… Xn Xn+1Xn+2…Xn+kXn+k+1…#. 栈顶. 第六章 LR 分析法. 1965 年, D.knuth 首先提出了 LR(K) 文法及 LR(K) 分析技术。 LR(K) 分析是指自左向右扫描和自底向上的语法分析,且 在分析的每一步,只须根据分析栈中当前已移进和归约出的 全部文法符号,并至多再向前查看 K 个输入符号,就能确定 相当于某一产生式右部符号的句柄是否已在分析栈的顶部形 成。从而也就可以确定所应采取的分析动作(是移进输入符号 还是按某产生式进行归约)。. - PowerPoint PPT Presentation

Citation preview

Page 1: 第六章    LR  分析法

第六章 LR 分析法 1965 年, D.knuth 首先提出了 LR(K) 文法及 LR(K) 分析技术。 LR(K) 分析是指自左向右扫描和自底向上的语法分析,且在分析的每一步,只须根据分析栈中当前已移进和归约出的全部文法符号,并至多再向前查看 K 个输入符号,就能确定相当于某一产生式右部符号的句柄是否已在分析栈的顶部形成。从而也就可以确定所应采取的分析动作(是移进输入符号还是按某产生式进行归约)。

当前扫描到 Xn+1, 向前查看 k 个符号,来确定是把Xn+1 移进栈,还是把 Xi…Xn 作为句柄进行归约。

1 ) 要归约时,则根据某产生式 U→XiXi+1…Xn 进行归约: #X1X2…Xi-1UXn+1Xn+2…Xn+k…#

例: #X1X2…Xi… Xn Xn+1Xn+2…Xn+kXn+k+1…#栈顶

Page 2: 第六章    LR  分析法

(续页)

LR(0) 表示在每一步分析时都不用向前输入符号LR(1) 表示在每一步分析时都向前看一个输入符号来决定当 前的动作。SLR(1) 表示简单的 LR(1) ,即只在动作不唯一的地方向前看一 个符号,在动作唯一时则不向前看输入符号。

2 ) 要移进时,即把 Xn+1 进栈,并读下一符号: #X1X2…Xi…XnXn+1 Xn+2…Xn+k…#

在栈中 当前扫描符栈顶

Page 3: 第六章    LR  分析法

6.1 LR 分析概论一 .LR 分析器的逻辑结构及工作过程 从逻辑上来说,一个 LR 分析器如图:

输入串#…ai…a1

Sp→

X1#

S1S0

┋┋

┋┋

XmSm 总 控 程 序 输出ACTION 表

GOTO 表

其中 S 栈为状态栈 X 栈为符号栈

Page 4: 第六章    LR  分析法

即一个 LR(k) 分析器主要由:总控程序,分析栈(状态栈和符号栈)输入队列和分析表组成。一般来说所有 LR 分析器的总控程序基本上是大同小异。只有分析表各不相同。一般主要讨论三种不同的分析表的构造方法。第一种称为规范 LR 分析表构造法。用此法构造的分析表功能最强而且也适合于很多文法,但实现代价比较高。

第二种称为简单 LR( 即 SLR) 分析表构造法。这是一种比较容易实现的方法。但 SLR 分析表的功能太弱,而且对某些文法可能根本就构造不出相应的 SLR 分析表。第三种称为向前 LR (即 LALR )分析表构造法。这种方法构造的分析表功能介于规范 LR 分析表 SLR 分析表之间。这种表适用于绝大多数程序语言的文法。而且也可以设法有效的实现。

Page 5: 第六章    LR  分析法

二、 LR 分析器的分析过程如下:1. 首先将初始状态 S0 及句子的左界限 # 分别压入状态栈和符号栈中。

则用栈顶状态 Sm 和当前扫描符 ai 组成符号对( Sm, ai) 去查分析动作表,根据 ACTION[Sm, ai] 的指示完成相应的分析动作。表中每一表元素所规定的动作仅能是下列四种动作之一:

S0S1 S2 … Sm Sm+1 ai+1 ai+2 …an # # X1 X2 … Xm ai ↑ ↑

2. 设在分析中的某一步,分析栈及余留的输入串为如下格局: ↓ S0S1… Sm ai ai+1…an

#X1… Xm ↑ ↑

( 1 ) ACTION[Sm, ai]= Sm+1 (移进)表明句柄尚未在栈顶形成,此时正期待移进输入符号以便形成句柄。故将当前的输入符号和表元素 Sm+1 分别压入栈中,有

Page 6: 第六章    LR  分析法

( 2 ) ACTION[Sm, ai]= Rj ( 归约) 表明此时应按文法的第 j 个产生式 A→ Xm-k+1Xm-k+2 …Xm进行归约。即栈顶符号串 Xm-k+1Xm-k+2 …Xm 已成为当前句型的句柄。所谓按第 j 个产生式归约,就是将分析栈中从顶向下的 k 个符号退栈,然后将文法符号 A 压入符号栈中。此时分析的格局为: ↓ S0S1… Sm-k ai ai+1…an #

# X1… Xm-k A ↑ ↑然后以( Sm-k,A) 去查状态转移表,设 GOTO[Sm-k,A]= Sl , 则将此新状态压入状态栈中,则有如下格局: ↓ S0S1… Sm-k Sl ai ai+1…an # # X1… Xm-k A ↑ ↑

Page 7: 第六章    LR  分析法

( 3 ) ACTION[Sm, ai]=acc (接受) 表明当前的输入串已被成功地分析完毕,应停止分析器的工作。

其中 Z 为文法开始符号Sα 为使 ACTION[Sα , #]=acc 的 唯一状态(接受状态)

( 4 ) ACTION[Sm, ai]=ERROR (空白)。 表明当前的输入串中含有错误,也应终止当前的分析工作。转出错处理。3. 重复上述第 2 步的工作,直到分析栈顶出现“接受状态”或“出错状态“为止。对接受状态,分析栈的格局为: ↓ S0 Sα # # Z ↑ ↑

Page 8: 第六章    LR  分析法

例:有文法 G[S]:1 : S→aAcBe 2 : A→b 3 : A→Ab 4 : B→d其 ACTION 表和 GOTO 表为:考察对输入串 abbcde# 的分析过程。

r1r1r1r1r1r1

r4r4r4r4r4r4

S9

r3r3r3r3r3r3

7S8

r2r2r2r2r2r2

S6S5

3S4

acc

1S2

BAS#dbeca

GOTOACTION

0

1

23

4

5

6

78

9

S a A c B e A b d

b

Page 9: 第六章    LR  分析法

对输入串 abbcde# 的分析过程为: ACTION GOTO步骤 状态栈 符号栈 输入流 分析动作 下一状态1 0 # abbcde# S2(0,a)2 02 #a bbcde# S4(2,b)3 024 #ab bcde# r2(4,b) GOTO[2,A]=3

4 023 #aA bcde# S6(3,b)

6 023 #aA cde# S5(3,c)5 0236 #aAb cde# r3(6,b) GOTO[2,A]=3

7 0235 #aAc de# S8(5,d)8 02358 #aAcd e# r4(8,d) GOTO[5,B]=7

9 02357 #aAcB e# S9(7,e)10 023579 #aAcBe # r1(9,#) GOTO[0,S]=1

11 01 #S # acc(1,#)

Page 10: 第六章    LR  分析法

6.2 LR(0) 分析表的构造为了给出构造 LR(0) 分析表的算法,引出一些术语:1 、规范句型的活前缀 前缀:一个符号串的前缀是指该串的任意首部(包括)。例如 abc 的前缀有: ,a,ab,abc abcd 的前缀有: ,a,ab,abc,abcd 由此可知,对于符号串 而言,其前缀的数量为 +1 。例:有文法 G[S]:S→aAcBe[1] A→b[2] 这里在每条产生式后加上了产生 A→Ab[3] 式的序号 [i] 当进行推导时把序号 B→d[4] 带上,以便说明问题。对输入串 abbcde 进行推导如下(最右推导): S aAcBe[1] aAcd[4]e[1] aAb[3]cd[4]e[1] ab[2]b[3]cd[4]e[1]由此可知, abbcde 是该文法的句子。由于 LR 方法是自底向上的分析,故应采用归约。

Page 11: 第六章    LR  分析法

最左归约为: ab[2]b[3]cd[4]e[1] 用 [2] 式归约

aAb[3]cd[4]e[1] [3] aAcd[4]e[1] [4] aAcBe[1] [1] S

其中表示归约符 从归约的过程可看出,每次归约时,归约前和归约后的被归约部分与剩余部分合起来仅构成文法的规范句型,而用哪个产生式归约仅取决于当前句型的前面部分; X1X2…Xn[p] 其中 Xi 为文法的符号, [p] 为第 p 个产生式序号。 如上例中每次归约前句型的前面部分为: ab[2] aAb[3] aAcd[4] acABe[1]

我们把规范句型的这种前端部分的串称为可归前缀。实际上,它们恰好是符号栈栈顶形成句柄时符号栈中的内容。

S→aAcBe[1]A→b[2]A→Ab[3]B→d[4]

Page 12: 第六章    LR  分析法

这是因为一旦句型的句柄在符号栈顶形成,将会立即被归约之故。所以我们将把规范句型具有上述性质(即不含句柄之后的任何符号)的前缀称之为可归前缀。 对各规范句型有前缀:ab[2]b[3]cd[4]e[1] ,a,abaAb[3]cd[4]e[1] ,a,aA,aAbaAcd[4]e[1] ,a,aA,aAc,aAcdaAcBe[1] ,a,aA,aAc,aAcB,aAcBe可以发现前缀 a,ab,aA,aAc 是多个规范句型的前缀,因此我们可进一步把形成可归前缀前和形成可归前缀时的所有规范句型的前缀都称为活前缀。

可归前缀:是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。

活前缀:可归前缀的任意首部。特指在分析过程中对于在栈顶形成句柄之前和恰好形成句柄时,每一步中符号栈中的那些符号组成的符号串。

Page 13: 第六章    LR  分析法

活前缀定义:

在前面例中对输入串 abbcde 的归约分析过程中,在规范归约过程中的任何时候只要已分析过的部分即在符号栈中的符号串均为规范句型的活前缀,它表明输入串的已被分析过的部分是该文法某规范句型的一个正确部分。由此可形式地定义活前缀如下: 定义 6.1 :若 S' * A 是 文法 G 中的一个规范推导, 如果符号串是的前缀,则称是 G 的一个活前缀。 其中 S' 为文法开始符号。

R R

Page 14: 第六章    LR  分析法

2 、 LR ( 0 )项目 由上述分析和定义可知,活前缀与句柄间的关系不外乎下述 三种情况:( 1 )活前缀中已含有句柄的全部符号(句柄的最后符号就是 活前缀的最后符号)。( 2 )活前缀中只含有句柄的前部分符号(句柄的最左子串 为活前缀的最右子串)。( 3 )活前缀中全然不包含句柄的任何符号。第一种情况表明:此时某一产生式 A→β 的右部 β 已出现在符号栈顶,因此此时相应的分析动作应当是用此产生式进行归约。第二种情况表明:形如 A→12 的产生式的 右部子串已在符号栈栈顶,如 1 ,正期待着从余留的输入串中看到能由 β推出的 符号串,即期待 2 进栈以便能进行归约。故此时分析动作是“移进”当前输入符号。第三种情况则意味着:期望从余留输入串中能看到由某产生式A→ 的右部,即所代表的符号串 ( 即句柄 ) 。所以此时分析的动作也是读输入符进符号栈。

Page 15: 第六章    LR  分析法

为了刻画在分析过程中,文法的一个产生式右部符号串有多大部分已被识别,我们可在该产生式的右部相应位置上加一个圆点“.” ,来指示位置,标明在“ .” 前的部分已被识别。如上述三种情况,可分别标注为: A→β.; A→1 .2 ; A→. 。 我们把右部某位置上标有圆点的产生式称为相应文法的一个LR(0)项目。特别地,对形如 A→ 的产生式,相应的 LR(0)项目 A→. ,显然不同的 LR(0)项目,反映了分析过程中符号栈顶的不同情况。例如:产生式 S→aAcBe 对应有六个项目。[0] S→.aAcBe[1] S→a.AcBe[2] S→aA.cBe[3] S→aAc.Be[4] S→aAcB.e[5] S→aAcBe.

Page 16: 第六章    LR  分析法

例如:产生式 S→aAcBe 对应有六个项目。[0] S→.aAcBe[1] S→a.AcBe[2] S→aA.cBe[3] S→aAc.Be[4] S→aAcB.e[5] S→aAcBe.

一个产生式可对应的项目的数量为它的右部符号串长度加 1 ,值得注意的是对空产生式,即 A→ε 仅有项目 A→. 每个项目的含义与圆点的位置有关。概括地说,圆点左边的子串表示在分析过程的某一时刻用该产生式归约时句柄中已识别过的部分,圆点右边的子串表示待识别的部分。 文法的全部 LR(0)项目将是构造识别它的所有活前缀的有穷自动机的基础。

Page 17: 第六章    LR  分析法

3 、 LR(0)项目的分类:例:考虑文法 G[S]=({S,A,B}, {a,b,c,d},P,S), 构造其分析表:其中 P: (1)S→ A (2)S→ B (3)A→ aAb (4)A→ c (5)B→ aBd (6)B→ d

解:求该文法的项目集规范族 C :为了方便起见,我们在上述文法中引起一个新的开始符号 S',且将 S' →S 作为第 0 个产生式添加到文法 G 中,从而得到了所谓G 的拓广文法 G' 。显然 L(G')=L(G) ,则对于文法 G' ,其 LR(0)项目为:

1) S' →.S 2) S' → S. 3) S→.A 4) S→A . 5) S→.B 6) S→B. 7) A→.aAb 8) A→a .Ab 9) A→aA .b 10) A→aAb . 11) A→.c 12) A→c . 13) B→.aBd 14) B→a .Bd 15) B→aB .d 16) B→aBd . 17)B→.d 18) B→d .

G' : (0)S'→S (1)S→ A (2)S→ B (3)A→ aAb (4)A→ c (5)B→ aBd (6)B→ d

Page 18: 第六章    LR  分析法

如前所述,由于不同的项目反映了分析过程中栈顶的不同情况,因此,我们可根据它们不同的作用,将一个文法的全部 LR(0)项目进行分类:•对于形如 A→. 的项目,因为它表明右部符号串已出现在栈顶,此时相应的分析动作应当是按此产生式进行归约,故我们将此种项目称为归约项目。上例中的项目 2) ,4),6),10),12),16),18)均是归约项目。其中项目 2)显然仅用于分析过程中的最后一次归约,它表明整个分析过程已成功地完成,所以我们特别地将它称为接受项目。对于拓广文法而言,接受项目是唯一的。由此可看出到我们为什么要首先将文法拓广的原因。• 对于形如 A→. Xβ 的项目(其中 可以是 ),根据前面的讨论可知,当 X 为终结符时,相应的分析动作应将当前的输入符号移入栈中,故我们将此种项目称为移进项目。上例中的项目 7) ,9), 11),13) ,15) ,17) 就都是移进项目; 当 X 为非终结符时,由于我们期待着从余留的输入符中进行归约后而得到 X ,因此将此类项目称为待约项目。上例中的 1), 3), 5), 8), 14) 就都是待约项目。

Page 19: 第六章    LR  分析法

在 LR 方法实际过程中,并不是去直接分析符号栈中的符号是否已形成句柄,但它给我们一个启示,我们可以把终结符和非终结符都可看成一个有限自动机的输入符号,每把一个符号进栈时看成已识别过该符号,而状态进行转换(到下一状态),当识别到可归前缀时相当于栈顶已形成了句柄,则认为到达了识别句柄的终态。4 、构造识别活前缀的 DFA 在作出文法的全部 LR(0)项目之后,现在将用它们来构造识别全部活前缀的 DFA 。这种 DFA 中的中每一个状态由若干个 LR(0)项目所组成的集合(称为项目集)来表示。 下面以上例中的文法为例来说明构造 DFA 的方法。

Page 20: 第六章    LR  分析法

首先我们用 I0 表示这个 DFA 的初态,它预示着整个分析过程的开始,并且期待着将给定的输入串逐步归约为文法的开始符号 S' 。或者反过来说,我们所期待的是,从使用产生式 S'→S 开始,能够逐渐推导出所给定的输入串。因此,我们应将项目 S'→.S 列入项目集 I0之中。换言之,也就是我们正期待将要扫描的输入串正好就是能由S'推导出的任何终结符串。然而,我们不能从输入串中直接读出非终结符号 S ,因此我们也应将项目 S→.A 和 S→.B加入 I0 中。由于A和 B 同样是非终结符,所以也应将 A→.aAb 和 A→.c 和 B→.aBb, B→.d加入 I0 中。由于最后加入 I0 的项目在圆点之后已都是终结符了,故 I0 已经“封闭”,宣告项目集 I0 构造结束。这样,表示初态的项目集 I0 将由如下项目组成:I0 : S'→.S, S→.A, S→.B, A→.aAb, A→.c, B→.aBd, B→.d 我们将 LR(0)项目 S'→.S 称为项目集 I0 的基本项目,上述从S'→.S 出发构造项目集 I0 的过程,可用一个对其基本项目集 {S' →.S} 的闭包运算,即 closure({S'→.S}) 来表示。

Page 21: 第六章    LR  分析法

一般地,设 I 为项目集, I 的闭包 closure(I) 的定义为:Closure(I)=I {A→∪ .A→ G K→ ∈ ∧ .A closure(I)∈ ∧ V*∈ ∧ V*}∈

故构造 closure(I) 的算法为:1 ) I 中的每一个项目都属于 closure(I);2 )若形如 K→.A 的项目属于 I ,且 A→ 是文法的一个产生式,则关于产生式 A 的任何形如 A→. 的项目也应加到 closure(I) 中(若它们不在 closure(I) 中);3 )重复上述过程,直至不再有新的项目加入到 closure(I) 中为止。 有了初态项目集 I0 之后,我们来说明如何确定从 I0 可能转移到的下一状态。设 A 为一文法符号 (A V)∈ ,若 I0 中有圆点位于 A 左边的项目 K→ .A( 其中可能为 ) ,则当分析器从输入串识别出 ( 即移进或归约出 ) 文法符号 A 后,分析器将进入它的下一状态。设此状态为 Ii ,显然 Ii 中必含有全部形如 K→A . 的项目,我们将这样的项目称为 K→ .A 的后继项目。对于每一个文法符号 A ,如果存在这样的后继项目,则可能不只一个,设其组成集合 J ,则 J 中的每个项目都是项目集 Ii 的基本项目,因此,按照与上面构造项目集I0 相类试的讨论,我们有: Ii =closure(J)

Page 22: 第六章    LR  分析法

为了指明 Ii 是“ I0关于文法符号 A 的后继状态”这一事实,我们可定义一个状态转移函数: GO(I,A)=closure(J) 其中, I 是当前状态, A 为文法符号, J 是 I 中所有形如 K→.A的项目之后继项目 K→A. 所组成的集合,而 closure(J) 就是项目集 I (即状态 I)关于符号 A 的后继项目集(即后继状态)。即: GO(I,A)=closure({ 所有形如 [K→A .] 的项目 [K→ .A]∈I}) 对于上例,我们有:

I1 =GO(I0,S)=closure({S'→S.}) I1 : S'→S.;

I2 =GO(I0,A)=closure({S→A.}) I2 :S→A.;

I3 =GO(I0,B)=closure({S→B.}) I3 : S→B.;

I4 =GO(I0,a)=closure({A→a.Ab,B→a.Bd})

Page 23: 第六章    LR  分析法

I4 : A→a.Ab B→a.Bd A→.aAb B→.aBd A→.c B→.d I5=GO(I0,c)=closure({A→c.}) I5 : A→c. I6=GO(I0,d)=closure({B→d.}) I6 :B→d. 此时,我们求出了 I0 的全部后继项目集 I1, I2,I3,I4,I5,I6, 而 I1, I2,I3,I5,I6均无后继项目集,仅 I4 有后继项目集: I7 =GO(I4,A)=closure({A→aA.b})={A→aA.b} I9 =GO(I4,B)=closure({B→aB.d})={B→aB.d}此外,还有: GO(I4,a)=closure({A→a.Ab, B→a.Bd})= I4

GO(I4,c)=closure({A→c.})= I5

GO(I4,d)=closure({B→d.})= I6 这些项目集均不产生新的项目集。另外还有 :

Page 24: 第六章    LR  分析法

I8 =GO(I7,b)=closure({A→aAb.})={A→aAb.} I10 =GO(I9,b)=closure({B→aBd.})={B→aBd.}此时 I8 , I10 也已无后继项目集,故我们已求出文法 G[S'] 的全部项目集 I0~ I10 。 通常我们将这些项目集的全体称为文法 G[S'] 的 LR(0)项目集规范族,并记为 C=(I0, I1,…, I10) 于是,我们所要构成的识别文法 G[S'] 的全部活前缀的 DFA 为 M=(C,V,GO, I0,Z) 其中 C—M 的状态集,即文法 G[S'] 的 LR(0)项目集规范族 I0~ I10 V— M 的字母表,即 V={S',S,A,B,a,b,c,d}; GO—M 的映射函数,即上面定义的状态转移函数 GO; I0—M 的唯一初态; Z—M 的终态集, ZC 为规范族中所有含有归约项目的 那些项目集。

Page 25: 第六章    LR  分析法

DFA:I0 : S'→.S

S→.A S→.B A→.aAb A→.c B→.aBd B→.d

I1 :S'→S.

I2 :S→A.

I3 :S→B.

I4 :A→a.Ab

A→a.Bd A→.aAb A→.c B→.aBd B→.d

I8 :A → aAb.

I7 :A → aA.b

I9 :B → aB.d

I10 :B → aBd.

I5 :A→c.

I6 :B→d.

A

B

d

b

c

d

d

a

c

S

A

B

a

Page 26: 第六章    LR  分析法

DFA即:

I0

I1

I2

I3

I4

I5

I6

I7

I9

I8

I10

SABa

c

d

c

d

A

B

b

d

Page 27: 第六章    LR  分析法

4 、 LR(0) 分析表的构造 对于一个文法 G 的拓广文法 G' ,当识别它的全部活前缀的 DFA作出之后,我们可以据此构造相应的 LR(0) 分析表了。 然而,要注意的是,用前述方法所构造的每一个 LR(0)项目集实质上表征了在分析过程中可能出现的一种分析状态;再根据前面对 LR(0)项目的分类,项目集中的每一个项目又与某一种分析动作相关联,因此,就要求每一个项目集中的的诸项目应当是相容的。所谓相容,是指在一个项目集中不出现下列的情况:( 1 )移进项目和归约项目并存,即存在移进—归约冲突;( 2 )多个归约项目并存,即存在归约—归约冲突。 如果一个文法 G满足上述条件,也就是它的每个 LR(0)项目集中都不含有冲突的项目,则称 G 为 LR(0) 文法。 显然,只有当一个文法是 LR(0) 文法时,才能对它构造不含冲突动作的 LR(0) 分析表来。

Page 28: 第六章    LR  分析法

为了方便起见,我们用整数 0 , 1 , 2 ,… 表示状态 I0 , I1, I2, …; 分析表的内容由两部分组成,一部分为动作 (ACTION) 表,它表示当前状态下所面临的输入符号应做的动作是移进、归约、接受或出错。另一部分为状态转移( GOTO) 表,它表示在当前状态下面临文法符号时应转向的下一个状态,相当于识别活前缀的有限自动机 DFA 的状态转换矩阵。分析表的行标为状态号,动作表的列标为只包含终结符和“ #”;状态转移表的列标为非终结符,而将其有关终结符的各列并入到 ACTION 表的各列中去,也就是把当前状态下面临终结符应作的动作和状态转移用同一数组元素表示,以便节省存储空间。 构造 LR(0) 分析表的算法为: (1) 对于每一项目集 Ii 中形如 A→.X 的项目,且有 GO(Ii,X)=Ij, 若 X 为一终结符号 a 时,则置 ACTION[i,a]=Sj; 若 X 为一非终结符号时,则仅置 GOTO[i,X]=j (2)若 Ii 中有归约项目 A→. , 设 A→ 为文法第 j 个产生式,则对 文法的任何终结符和“ #” (均记为 a )置 ACTION[i,a]=Rj

Page 29: 第六章    LR  分析法

(3)若接受项目 S'→S .属于 Ii , 则置 ACTION[i,#]=acc 。(4) 在分析表中 ,凡不能按上述规则填入信息的元素 ,均置为“出错”。 如上例可构造分析表为:

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

Page 30: 第六章    LR  分析法

5 、 LR(0) 分析器的工作过程 对于一个文法构造了它的 LR(0) 分析表就可以在 LR 分析器的总控程序控制下对输入串进行分析,即根据输入串当前符号 a 和分析栈栈顶状态 i 查找分析表应采取的动作,对状态栈和符号栈进行相应的操作即移进、归约、接受或报错。具体为:1 )若 ACTION[i,a]=Sj, a V∈ T, 则把 a 移进符号栈, j 移进状态栈。2 )若 ACTION[i,a]=Rj , a V∈ T 或 # ,则用第 j 个产生式归约。并将两个栈的指针减去 K( 其中 K 为第 j 个产生式右部的串长度),并把产生式的左部符号 A 压入符号栈,同时用符号对( Si-k,A) 去查GOTO 表(其中 Si-k 为状态栈当前栈顶元素,若 GOTO[Si-k,A]=j,则 j 压入状态栈,使得两个栈内的元素一样多。3 )若 ACTION[i,a]=Acc, (此时 a 应为“ #” 号),则表明分析成功,结束分析。4 )若 ACTION[i,a]= 空白,转出错处理。

Page 31: 第六章    LR  分析法

6.3 SLR(1) 分析 因大多数程序设计语言的文法不能满足 LR(0) 文法的条件,即使是描述一个变量这样简单的文法也不是 LR(0) 文法。因此下面将介绍对 LR(0) 规范族中有冲突的项目集(状态)用向前查 看一个(输入)符号的办法进行处理,以解决冲突。这种分析方法因为只对有冲突的状态才向前查看一个符号,以确定做什么动作,故称这种分析方法为简单的 LR(1) 分析法,用 SLR(1) 表示。 假定有一个 LR(0) 规范族中含有如下项目集 ( 状态 )I: I={X→.b,A→., B→.} 其中 ,,, 为符号串, b∈VT, 显然 I 中含有移进—归约和归约—归约冲突。那么只要在所有含有 A 或 B 的句型中,直接跟在 A 或 B 后面的可能终结符集合FOLLOW(A) 和 FOLLOW(B)互不相交,且都不包含 b, 即只要满足:

FOLLOW(A)∩FOLLOW(B)=φFOLLOW(A)∩{b}=φFOLLOW(B)∩{b}=φ

即: FOLLOW(A)∩FOLLOW(B)∩{b} =φ

Page 32: 第六章    LR  分析法

那么,当在状态 I面临某输入符号为 a 时,则动作可由下述规定决策:1 )若 a = b, 则移进。2 )若 a ∈ FOLLOW(A), 则用产生式 A→ 归约。3 )若 a ∈ FOLLOW(B), 则用产生式 B→ 归约。 一般地,对于 LR(0) 规范族的一个项目集 I 可能含有多个移进项目和多个归约项目,我们可假设项目集 I 中有 m 个移进项目: A1→1. b11, A2→ 2. b22, …, Am→ m. bmm; 同时含有 n 个归约项目: B1→1. , B2→ 2. ,…, Bn→ n. , 只要集合{b1, b2,…bm} 和 FOLLOW(B1),FOLLOW(B2),…,FOLLOW(Bn)两两交集都为空,则我们仍可用上述归则来解决冲突: 1 )若 a {∈ b1, b2,…,bm}, 则移进。 2 )若 a FOLLOW(B∈ i),i=1,…,n, 则用 Bi→ i 进行归约。 3 )此外,则报错。

Page 33: 第六章    LR  分析法

所以,我们只须把构造 LR(0) 分析表算法中的规则 (2) ,即:(2)若 Ii 中有归约项目 A→. , 设 A→ 为文法第 j 个产生式,则对 文法的任何终结符和“ #” (均记为 a )置 ACTION[i,a]=Rj 。修改为:

即:(1) 对于每一项目集 Ii 中形如 A.X 的项目,且有 GO(Ii,X)=Ij, 若 X 为一终结符号 a 时,则置 ACTION[I,a]=S; 若 X 为一非终结符号时,则仅置 GOTO[i,X]=j;(2')若归约项目 A→.属于 Ii, 设 A→ 为文法第 j 个行产生式,则对任何属于 FOLLOW(A) 的输入符号 a,置 ACTION[i,a]=Rj;(3)若接受项目 S' → S.属于 Ii , 则置 ACTION[i,#]=acc 。(4) 在分析表 ,凡不能按上述规则填入信息的元素 ,均置为“出错”。

( 2' )若归约项目 A→.属于 Ii, 设 A→ 为文法第 j 个行产生式,则对任何属于 FOLLOW(A) 的输入符号 a,置 ACTION[i,a]=Rj 。其余的规则不变,就得到了构造 SLR(1) 分析表的算法。

Page 34: 第六章    LR  分析法

例如: 有算术表达式文法 G[E], 构造其 LR(0)项目规范簇和 SLR(1) 分析表。G[E]: E→E+TT T→T*FF F→(E) i解: 1 、拓广文法为 G'[S'] :(0) S'→E(1) E→E+T(2) E→T(3) T→T*F(4) T→F(5) F→(E)(6) F→i

Page 35: 第六章    LR  分析法

2 、再求识别 G' 的全部活前缀的 DFA (即 LR(0) 的项目集规范):I0: S'→.E GO(I0,E)=I1 E→.E+T GO(I0,E)=I1 E→.T GO(I0,T)=I2 T→.T*F GO(I0,T)=I2 T→.F GO(I0,F)=I3 F→.(E) GO(I0,( )=I4I1: S'→E. E→E.+T GO(I1,+)=I6

I2: E→T. T→T.*F GO(I2,*)=I7

I3: T→F.

Page 36: 第六章    LR  分析法

I4: F→(.E) GO(I4,E)=I8 E→.E+T GO(I4,E)=I8 E→.T GO(I4,T)=I2 T→.T*F GO(I4,F)=I2 T→.F GO(I4,F)=I3 F→.(E) GO(I4,( )=I4 F→.i GO(I4,i)=I5

I5: F→i.I6: E→E+.T GO(I6,T)=I9 T→.T*F GO(I6,T)=I9 T→.F GO(I6,F)=I3 F→.(E) GO(I6,( )=I4 F→.i GO(I6,i)=I5

I7: T→T*.F GO(I7,F)=I10 F→.(E) GO(I7,( )=I4 F→.i GO(I7,i )=I5I8: F→(E.) GO(I8,) )=I11 E→E.+T GO(I8,+)=I6

I9: E→E+T. T→T.*F GO(I9,)=I7

I10: T→T*F.

I11: F→(E).

Page 37: 第六章    LR  分析法

DFAI0: S'→.E E→.E+T E→.T T→.T*F T→.F F→.(E) F→.i

I2: E→T. T→T*.F

I5: F→i.

I1: S'→E. E→E.+T

I3: T→F.

I4: F→(.E) E→.E+T E→.T T→.T*F T→.F F→.(E) F→.i

I7: T→T*.F F→.(E) F.i

I6: E→E+.T T→.T*F T→.F F→.(E) F→.i

I8: F→(E.) E→E.+T

I11: F→(E).

I9: E→E+T . T→T.*F

I10: T→T*F.

i

*

E

F

(

T

i

i

i

T

+

)

*F

(

+

F

(

E

(F

Page 38: 第六章    LR  分析法

3 、解决冲突 可以看到,项目 I1 , I2 , I9 中都同时包含有移进项目和归约项目。存在移进—归约冲突,因而该文法不属于 LR(0) 文法,故不能构造 LR(0) 分析表。 FOLLOW(S')= {#} FOLLOW(E)= {+ , ) , #} FOLLOW(T)= {+ , * , ) , #} FOLLOW(F)= {+ , * , ) ,#} 现在分别考虑上述三个冲突项目中的冲突是否能用 SLR(1) 方法解决。 在 I1 中,由于 FOLLOW(S')={#). 而 S'→E. 是唯一的接受项目,所以当且仅当遇到句子的结束符“ #” 号时才被接受 ,又因 {#}∩{*}=φ ,故 I1 中的冲突可解决。 对于 I2,因 FOLLOW(E)={+,),#}∩{*}=φ,因此当面临输入符号为“ +” ,“ )”或“ #” 号时,则用产生式 E→T 归约。当面临输入符为“ *” 时,则移进;其它情况则报错。 对于 I9,与 I2类似,当面临输入符号为“ +” ,“)”或“ #”时,则用产生式 E→E+T 归约;当面临输入符号为“ *” 时,则移进,其余情况报错。

Page 39: 第六章    LR  分析法

4 、构造 SLR(1) 分析表对于上述三个冲突项目等均可用 SLR(1) 方法解决冲突。因此该文法是 SLR(1) 文法。我们可造成其相应的 SLR(1) 分析表为:

i + * ( ) # E T F0 S5 S4 1 2 31 S6 acc2 R2 S7 R2 R23 R4 R4 R4 R44 S5 S4 8 2 35 R6 R6 R6 R66 S5 S4 9 37 S5 S4 108 S6 S11 9 R1 S7 R1 R1

10 R3 R3 R3 R311 R5 R5 R5 R5

Page 40: 第六章    LR  分析法

下面给定输入串 i+i*i # ,使用上述 SLR(1) 分析进行分析:步 状态栈 符号栈 输入串 ACTION GOTO

1 0 # i+i*i # S52 05 #i +i*i # R6 3

3 03 #F +i*i # R4 24 02 #T +i*i # R2 1

5 01 #E +i*i # S66 016 #E+ i*i # S57 0165 #E+i *i # R6 38 0163 #E+T *i # R4 99 0169 #E+T *i # S710 01697 #E+T* i # S511 016975 #E+T*i # R6 1012 0169710 #E+T*F # R3 913 0169 #E+T # R1 114 01 #E # acc

Page 41: 第六章    LR  分析法

LR 分析器 :

LR 分析器是一个确定的下推自动机。作为 LR 分析器的核心的分析表由两个子表组成:分析动作表 ACTION 和状态转移表GOTO.一个 LR 分析器如图:

输入串#…ai…a1

Sp→

X1#

S1S0

┋┋

┋┋

XmSm 总 控 程 序 输出ACTION 表

GOTO 表

其中 S 栈为状态栈 X 栈为符号栈