编译原理第五章——自下而上分析——LR(1)超详细分析!

文章深入解析了自下而上分析的核心思想、算符优先分析的构造方法、LR(0)和SLR分析过程,重点介绍了LR(1)项目集的确定和规范LR分析表的构造,以及LALR分析表的合并策略,强调了这些技术在编译原理中的重要性和考试中的高分价值。
摘要由CSDN通过智能技术生成

本文中内容整理西安交通大学软件学院吴晓军老师的ppt中,仅供学习使用,请勿转载或他用
参考教材:《程序设计语言 编译原理》(第3版) 陈火旺等 国防工业出版社

编译原理第五章——自下而上分析


目录

  • 一、复习:语法分析的两种方式
  • 二、自下而上分析概述
    • 1.🐯核心思想:移进-规约
    • 2.🐶规范规约
    • 3.🦁规范规约的两个问题
  • 三、算符优先分析
    • 1.🐸概念辨析-算符文法、算符优先文法
    • 2.🐙概念辨析-算符优先分析法、直观算符优先分析法
    • 3.🐬概念辨析-算符文法、算符优先文法
    • 4.🐳 利用优先关系表用直观算符优先分析法分析算数表达式(不考可以跳过)
    • 5.🐂构造FIRSTVT§和LASTVT§两个集合(必考!!)
    • 6.🐝利用FIRSTVT§和LASTVT§两个集合构造优先关系表(必考!!)
    • 7.🐇素短语
    • 8.🐏优先函数(感觉不会考,至少掌握下概念吧)
  • 四、LR分析法
    • 1.🐡LR分析法概述
    • 2.🍁LR分析过程
    • 3.🐚LR(0) (基础必须掌握!!)
    • 4.🎵SLR (复习一手FOLLOW集~!!)
    • 5.🐼规范规约:LR(k) (最容易考的!!)
    • 6.🐱LALR
  • 五、本章总结

前面部分的链接: 自下而上分析概述算符优先分析LR(0)和SLR详细分析(内含FOLLOW优化构造方法)

5.🐼规范规约:LR(k)

概念
  • 构造问题:有些无二义文法会产生“移进/归约”冲突或“归约/归约”冲突。
  • 产生原因:SLR分析法未包含足够多的“展望”信息。
  • 解决办法:让每个状态含有更多的“展望”信息。
  • 方法:重新定义项目,使得每个项目都附带有k个终结符,即每个项目的形式为:[A->a ⋅ · β, a 1 a 2 ⋯ a k a_1 a_2 \cdots a_k a1a2ak]。
  • 定义:如上形式的项目称为一个LR(k)项目。
  • 说明:
    • 向前搜索符串仅对归约项目[A->a·, a 1 a 2 ⋯ a k a_1 a_2 \cdots a_k a1a2ak]有意义;
    • 归约项目[A->a·, a 1 a 2 ⋯ a k a_1 a_2 \cdots a_k a1a2ak]意味着:当它所属的状态呈现在栈顶且后续的k个输入符号为 a 1 a 2 ⋯ a k a_1 a_2 \cdots a_k a1a2ak时,才可以把栈顶上的a归约为A;
  • 我们只研究k≤1的情形,因为当k = 1的时候已经很很很复杂了,并且已经可以满足绝绝绝大部分需要了~
确定LR(1)项目

确定LR(1)项目的方法:

  • ① 对S和S’,只向前搜索#;
  • ② 其他产生式,对每一个 V t V_t Vt(含#)均向前搜索。

栗子!
(0) S’ -> S
(1) S -> aB
(2) B -> BB
(3) B -> b

由(0)和(1):
S’ -> [·S,#]
S’ -> [S·,#]
S -> [·BB,#]
S -> [B·B,#]
S -> [BB·,#]

由(2)和(3):
B -> [·aB,a]
B -> [·aB,b]
B -> [·aB,#]
B -> [a·B,a]
B -> [a·B,b]
B -> [a·B,#]
B -> [aB·,a]
B -> [aB·,b]
B -> [aB·,#]
B -> [·b,a]
B -> [·b,b]
B -> [·b,#]
B -> [b·,a]
B -> [b·,b]
B -> [b·,#]

是不是写起来很累,对啦,就没打算让你写,这就是让你康康就行的!

LR(1)分析表的构造步骤

跟LR(0)分析表的构造一样~

标准步骤:(同样了解即可)

  • 确定LR(1)项目
  • 根据与LR(1)项目构造NFA
  • 利用函数CLOSURE和GO求DFA
  • 根据DFA构造规范LR分析表

简化步骤:(也是考试的步骤)

  • LR(1)项目集规范族的构造
  • LR(1)分析表的构造
LR(1)分析的项目集闭包

假定I是文法G’的任一项目集,定义和构造1的闭包CLOSURE(I)如下:

  • I的任何项目都属于CLOSURE(I)。
  • 若项目[A -> α·Bβ,a]属于CLOSURE(I),B -> ξ \xi ξ是一个产生式,那么,对于FIRST(βa)中的每个终结符b,如果[B -> ξ \xi ξ,b]原来不在CLOSURE(I)中,则把它加进去。
  • 重复执行步骤2,直至CLOSURE(I)不再增大为止。
项目集的转换函数GO

令I是一个项目集,X是一个文法符号,函数GO(I,X)定义为:
G O ( I , X ) = C L O S U R E ( J ) GO(I, X)=CLOSURE(J) GO(I,X)=CLOSURE(J)
其中
J = 任何形如 [ A → α X ⋅ β , α ] 的项目 ∣ [ A → α ٠ X β , α ] ∈ I J = {任何形如[A→αX·β,α]的项目| [ A→α٠Xβ, α]∈I} J=任何形如[AαXβ,α]的项目[Aα٠,α]I

直接上例子(重中之重!!)

构造如下拓广文法的规范LR分析表

S -> BB
B -> aB
B -> b

1.为文法编号
(O) S’ -> S
(1) S -> BB
(2) B -> aB
(3) B -> b

2.利用CLOSURE方法构造LR(1)项目集规范族

    1. 先写$ I_0$ = { [S’ -> · S , #] }
    2. 发现 · 后面有非终结符号,写出S的有关项,则$ I_0$ = { [S’ -> · S , #] , [S -> · B B , #]},因为S和S’的项目的搜索符号只有#,自然不必多说。
    3. 发现 · 后面有非终结符号,写出B的有关项,则$ I_0$ = { [S’ -> · S , #] , [S -> · B B , #] , [B -> · a B , ?] , [B -> · b , ?] }
    4. 那么?处到底填什么呢,我们观察[S -> · B B , #]这个项目,当 · 向后移位到第一个B后面时,搜索的就是第二个B了,那么我们填入a/b,则$ I_0$ = { [S’ -> · S , #] , [S -> · B B , #] , [B -> · a B , a/b] , [B -> · b , a/b] }
    5. 至此$ I_0$总算是写完了。
  • 根据$ I_0$可以得到:
    1. $ I_1$ = GO( $ I_0$ , S ) = { [S’ -> S · , #] }
  • 根据$ I_0$可以得到:
    1. $ I_2$ = GO( $ I_0$ , B ) = { [S -> B · B , #] }
    2. 发现 · 后面有非终结符号,写出B的有关项,则$ I_2$ = { [S -> B · B , #] , [B -> · a B , ?] , [B -> · b , ?] }
    3. 那么?处到底填什么呢,我们观察[S -> B · B , #]这个项目,当 · 向后移位到第二个B后面时,搜索的就是#了,那么我们填入#,则$ I_2$ = { [S -> B · B , #] , [B -> · a B , #] , [B -> · b , #] }
  • 根据$ I_0$可以得到:
    1. $ I_3$ = GO( $ I_0$ , a ) = { [B -> a · B , a/b] }
    2. 填入B的有关项,$ I_3$ = { [B -> a · B , a/b] , [B -> · a B , ?] , [B -> · b , ?] }
    3. 我们观察[B -> a · B , a/b]这个项目,当 · 向后移位到B后面时,搜索的是a/b了,所以$ I_3$ = { [B -> a · B , a/b] , [B -> · a B , a/b] , [B -> · b , a/b] }
  • 根据$ I_0$可以得到:
    1. $ I_4$ = GO( $ I_0$ , b ) = { [B -> b · , a/b] }
  • $ I_1$无法进行下一步了,跳~
  • 根据$ I_2$可以得到:
    1. $ I_5$ = GO( $ I_2$ , B ) = { [S -> B B · , #] }
  • 根据$ I_2$可以得到:
    1. $ I_6$ = GO( $ I_2$ , a ) = { [B -> a · B , #] }
    2. 填入B的有关项,$ I_6$ = { [B -> a · B , #] , [B -> · a B , ?] , [B -> · b , ?] }
    3. ? 填入#,$ I_6$ = { [B -> a · B , #] , [B -> · a B , #] , [B -> · b , #] }
  • 根据$ I_2$可以得到:
    1. $ I_7$ = GO( $ I_2$ , b ) = { [B -> b · , #] }
  • 根据$ I_3 可以得到: 可以得到: 可以得到: I_8$ = GO( $ I_3$ , B ) = { [B -> a B · , a/b] }
  • 根据$ I_3$可以得到:GO( $ I_3$ , a ) = $ I_3$
  • 根据$ I_3$可以得到:GO( $ I_3$ , b ) = $ I_4$
  • I 4 I_4 I4无法进行下一步了,跳~
  • I 5 I_5 I5无法进行下一步了,跳~
  • 根据$ I_6 可以得到: 可以得到: 可以得到: I_9$ = GO( $ I_6$ , B ) = { [B -> a B · , #] }
  • 根据$ I_6$可以得到:GO( $ I_6$ , a ) = $ I_6$
  • 根据$ I_6$可以得到:GO( $ I_6$ , b ) = $ I_7$
  • I 7 I_7 I7无法进行下一步了,跳~
  • I 8 I_8 I8无法进行下一步了,跳~
  • I 9 I_9 I9无法进行下一步了,跳~
  • 终于结束辣~~

3.利用LR(1)项目集规范族构造LR(1)分析表

掏出一张空表:

ab#SB
0
1
2
3
4
5
6
7
8
9
  1. 先找acc避免遗忘,[S’ -> S · , #] 在 I 1 I_1 I1:
ab#SB
0
1acc
  1. 按顺序看 I 0 I_0 I0
    • 第一个项目[S’ -> · S , #],点后面是非终结符S,因为$ I_1$ = GO( $ I_0$ , S ),所以输入E前往状态 I 1 I_1 I1,所以GOTO表的第0行第S列写1;
    • 第二个项目[S -> · B B , #],点后面是非终结符B,因为$ I_2$ = GO( $ I_0$ , S ),所以输入E前往状态 I 2 I_2 I2,所以GOTO表的第0行第B列写2;
    • 第三个项目[B -> · a B , a/b],点后面是a,因为$ I_3$ = GO( $ I_0$ , a ),所以输入a以后前往 I 3 I_3 I3,则ACTION表第0行第a列写s3。
    • 第四个项目[B -> · b , a/b],点后面是b,因为$ I_4$ = GO( $ I_0$ , b ),所以输入b后前往 I 4 I_4 I4,则ACTION表第0行第b列写s4。
ab#SB
0s3s412
1acc
  1. 同理往后看 I 1 I_1 I1
    • 哈哈这一行只有一个项目~填过了,过!
  2. 同理往后看 I 2 I_2 I2
    • GOTO表B的位置填5
    • ACTION表a的位置填s6
    • ACTION表b的位置填s7
  3. 同理往后看 I 3 I_3 I3
    • GOTO表B的位置填8
    • ACTION表c的位置填s3
    • ACTION表d的位置填s4
  4. 同理往后看 I 4 I_4 I4
    • 对项目[B -> b · , a/b]可以规约,根据(3)B -> b ,我们在ACTION表的a/b填上r3。
  5. 同理往后看 I 5 I_5 I5
    • 对项目[S -> B B · , #]可以规约,根据(1) S -> BB ,我们在ACTION表的#填上r1。
  6. 同理往后看 I 6 I_6 I6
    • GOTO表B的位置填9
    • ACTION表c的位置填s6
    • ACTION表d的位置填s7
  7. 同理往后看 I 7 I_7 I7
    • 对项目[B -> b · , #]可以规约,根据(3)B -> b ,我们在ACTION表的#填上r3。
  8. 同理往后看 I 8 I_8 I8
  • 对项目[B -> a B · , a/b]可以规约,根据(2) B -> aB ,我们在ACTION表的a/b填上r2。
  1. 同理往后看 I 9 I_9 I9
  • 对项目[B -> a B · , #]可以规约,根据(2) B -> aB ,我们在ACTION表的#填上r2。
ab#SB
0s3s412
1acc
2s6s75
3s3s48
4r3r3
5r1
6s6s79
7r3
8r2r2
9r2

终于,结束辣哈哈哈!!!

6.🐱LALR

概念
  • 问题:对于一般的语言,规范LR分析表要用几千个状态,无法实际应用。
  • 分析:由例6可以看到,有些状态集除了搜索符不同外是两两相同的。
  • 解决办法:合并同心集,构造LALR分析表。

我们称两个LR(1)项目集具有相同的心,如果除去搜索符之后,这两个集合是相同的。

将所有同心的LR(1)项目集合并后,得到一个心就是一个LR(O)项目集。

合并项目集时不用修改转换函数,即GO(1,X);但是,动作ACTION应进行修改,使得能够反映各被合并的集合的既定动作。

合并同心集不会产生新的移进-归约冲突,但有可能产生新的“归约-归约”冲突。

对于同一个文法,LALR分析表和SLR分析表永远具有相同数目的状态,但却能处理一些SLR所不能对付的事情。

构造方法
  • 构造文法G的LR(1)项目集族 C = { I 0 , I 1 , ⋯   , I n } C=\{I_0,I_1,\cdots,I_n\} C={I0,I1,,In}
  • 把所有的同心集合并在一起,记 C ′ = { J 0 , J 1 , ⋯   , J m } C'=\{J_0,J_1,\cdots,J_m\} C={J0,J1,,Jm}为合并后的新族。那个含有项目 [ S ′ → ⋅ S , # ] [S'\to \cdot S,\#] [SS,#] J k J_k Jk为分析表的初态
  • C ′ C' C构造 A C T I O N ACTION ACTION
    • 若项目 [ A → α ⋅ a β , b ] [A\to \alpha \cdot a \beta ,b] [Aαaβ,b]属于 J k J_k Jk G O ( J k , a ) = J i GO(J_k,a)=J_i GO(Jk,a)=Ji a a a为终结符,则置 A C T I O N [ k , a ] ACTION[k,a] ACTION[k,a]为“ s j sj sj
    • 若项目 [ A → α ⋅ , a ] [A\to \alpha \cdot ,a] [Aα,a]属于 J k J_k Jk,则置 A C T I O N [ k , a ] ACTION[k,a] ACTION[k,a]为“用产生式 A → α A\to \alpha Aα归约”,简记为" r j rj rj";其中,假定 A → α A\to \alpha Aα为文法 G ′ G' G的第 j j j个产生式
    • 若项目 [ S ′ → S ⋅ , # ] [S'\to S\cdot ,\#] [SS,#]属于 J k J_k Jk,则置 A C T I O N [ k , # ] ACTION[k,\#] ACTION[k,#]为"接受",简记为“ a c c acc acc
  • 构造 G O T O GOTO GOTO
  • 分析表中凡不能用步骤3、4填入信息的空白格均填上“出错标志”
好好好我知道你们都不看,上栗子!

对于刚刚构造的LR(1)项目集规范族,进行同心集合并。

$ I_{36}$ = { [B -> a · B , a/b/#] , [B -> · a B , a/b/#] , [B -> · b , a/b/#] }
$ I_{47}$ = GO( $ I_0$ , a/b/# ) = { [B -> b · , a/b/#] }
$ I_{89}$ = GO( $ I_3$ , a/b/# ) = { [B -> a B · , a/b/#] }

重新写构造表(规则完全一致)。

请添加图片描述

图片来源于西安交通大学软件工程专业编译原理 吴晓军 2024春
 

五、本章总结

  • 简单来说,这一章是重点中的重点,内容也相当多(看我这1000多行就知道了),相对也比较难理解,当然考的分也很多,就单纯的大题,算符优先分析约10分,LR分析约17分,比前面几章加起来的分都多,所以一定要好好学习~
  • 写题时要注意:看小分写,一般3分对应3个;步骤写全,别偷懒。

我,我的任务完成辣!~~啊哈哈哈哈哈!!(穿山甲的狂笑~)


  • 37
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值