编译原理_SLR分析表&LR(1)分析表的构建


前言

在编译器设计领域,解析(Parsing)是一个核心过程,它负责将源代码转换成一系列可以被编译器理解的结构。SLR(简单LR分析)和LR(1)分析表是构建解析器时常用的两种技术。本文将用通俗易懂的方式,结合笔者自身理解,探讨这两种技术的原理、构建方法。
本文结合龙书第2版习题4.6.2和4.7.1进行讲解。


首先给出贯穿本文的语法:
S → S S + ∣ S S ∗ ∣ a S \rightarrow SS+ | SS* | a SSS+SSa

一、SLR分析表的构建

1. 基本概念

1.1 增广文法

如果某个文法 G 的开始符号是 S,那么它的增广文法 G’ 就是原来文法 G 中的产生式再加上新的产生式“S’ → S”。本例中的增广文法 G ′ G' G
0. S ′ → S 0. S' \rightarrow S 0.SS
1. S → S S + 1.S\rightarrow SS+ 1.SSS+
2. S → S S ∗ 2.S\rightarrow SS* 2.SSS
3. S → a 3.S\rightarrow a 3.Sa

1.2 项集的闭包

对于项集 I I I ,首先把它里面的所有项放到它的闭包 C ( I ) C(I) C(I) 中;接着遍历 I I I中的每一项。如果遍历到的某一项点号右边恰好是非终结符,我们就把这个非终结符对应的若干产生式,做成“LR(0) 项”(点号放在产生式体最左边),再全部添加到 C ( I ) C(I) C(I)中。反复遍历,直到没有新项被添加到 C ( I ) C(I) C(I)中为止。此时的 C ( I ) C(I) C(I) 就叫做“项集的闭包”。
求项集的闭包
I 0 I_0 I0即为初态 S ′ → ⋅ S S' \rightarrow \cdot S SS 的闭包。
对于本例,项集 I 0 I0 I0的构造过程如下:
首先将初态加入 S ′ → ⋅ S S' \rightarrow \cdot S SS
此时,点的右边为 S S S,是非终结符,将其所有产生式加入,此时闭包中包含:
S ′ → ⋅ S S' \rightarrow \cdot S SS
S → ⋅ S S + S\rightarrow\cdot SS+ SSS+
S → ⋅ S S ∗ S\rightarrow\cdot SS* SSS
S → ⋅ a S\rightarrow\cdot a Sa
再次观察新添加的项中是否含有 ⋅ \cdot 后是除刚加入的 S S S外的非终结符。没有,则完成。此时即为LR(0)自动机初态的项集 I 0 I_0 I0
之后如何更新LR(0)自动机的状态呢?需要使用第三个关键点 GOTO函数

1.3 GOTO函数

GOTO函数相当于状态转移函数 G O T O ( I i , X ) GOTO(I_i, X) GOTO(Ii,X)代表对项集 I i I_i Ii输入语法符号 X X X后的项集。
具体的计算方法如下:

  1. 新建空项集 I j I_{j} Ij
  2. 遍历项集 I i I_i Ii中的项,看点号右边是否为 X X X
  3. 如果是,则将其点号右移,并加入 I j I_{j} Ij中;否则,丢弃
  4. 遍历完成,求此时 I j I_{j} Ij的闭包,得到最终的 I j I_{j} Ij

例如 G O T O ( I 0 , S ) GOTO(I_0, S) GOTO(I0,S)
首先挑选出全部点后为 S S S的项:
S ′ → ⋅ S S' \rightarrow \cdot S SS
S → ⋅ S S + S\rightarrow\cdot SS+ SSS+
S → ⋅ S S ∗ S\rightarrow\cdot SS* SSS
将点右移一位:
S ′ → S ⋅ S' \rightarrow S\cdot SS
S → S ⋅ S + S\rightarrow S\cdot S+ SSS+
S → S ⋅ S ∗ S\rightarrow S\cdot S* SSS
然后,求上述项集的闭包:
(以下再次回顾求闭包的过程)
遍历所有项,发现点后有非终结符 S S S,将其产生式全部加入,此时项集中包含:
S ′ → S ⋅ S' \rightarrow S\cdot SS
S → S ⋅ S + S\rightarrow S\cdot S+ SSS+
S → S ⋅ S ∗ S\rightarrow S\cdot S* SSS
——————
S → ⋅ S S + S\rightarrow\cdot SS+ SSS+
S → ⋅ S S ∗ S\rightarrow\cdot SS* SSS
S → ⋅ a S\rightarrow\cdot a Sa
现在,项集中不再含有点后除 S S S外的非终结符了。此时,GOTO函数完成,将该状态命名为 I 2 I_2 I2

有了以上基础我们就可以进行SLR分析表的构建了。

2 SLR分析表的构建

2.1 找到全部的项集

首先,按照上述方法,找到这个语法对应的全部项集。
在确定初态后,不断调用GOTO函数,生成新的项集,直到GOTO函数不再能生成新的项集为止。(规范-LR(0) 项集族)
在这里插入图片描述

将所有的项集及其对应的生成关系画图表示如下(LR(0)自动机):
在这里插入图片描述

2.2 分析表的构建

SLR 解析表是基于上面的 LR(0) 自动机制作的。
SLR 表有两个部分:

  • Action 表
  • Goto 表。

构造 SLR 解析表中的 Action 表需要三步:

  • 按照上面的方法生成“规范-LR(0) 项集族”。
  • 接下来,遍历项集族中的项集 。
  • 对于其中的一个项集 I i I_{i} Ii ,遍历其中的每一个 LR(0) 项:
    • 如果这个项的点号右边是终结符 ,并且 G O T O ( I i , a ) = I j GOTO(I_i, a)=I_j GOTO(Ii,a)=Ij,就把 Action[i, a] 设成 S j S_j Sj”。
    • 如果这个项的点号在整个产生式的最右边(形如 A → α ⋅ A\rightarrow \alpha \cdot Aα)并且不是增广文法的开始符号(比如上面的 S’),就找出 A A A 的 FOLLOW 集,遍历其中的终结符 ,把 Action[i, a] 设成“归约 i”——相当于我们已经处理完了 A 这个非终结符,可以开始处理下一个了。
    • 如果这个项是 S ′ → S ⋅ S'\rightarrow S\cdot SS (S 可以是任意开始符),就把 Action[i, eof] 设成“接受”。

构造 Goto 表的过程要简单一些:我们遍历所有非终结符。如果对于某个非终结符 A A A ,有 G O T O ( I i , A ) = I j GOTO(I_i, A)=I_j GOTO(Ii,A)=Ij ,那么我们就把 GOTO[i, A] 设成 j——这样告诉解析器,归约了 A A A 之后,要切换到状态 j 接受新的输入。这样 Goto 和 Action 的构造就完成了。

按上述步骤,构造出的SLR分析表如下:
在这里插入图片描述

二、LR(1)分析表的构建

LR(1)分析表的构建与SLR分析表最大的区别是引入向前看符号。顾名思义,向前看符号就是要在本步骤的基础上再看下一步会是怎样的。不理解没关系,直接来看怎么做。(很简单的套公式)

1 向前看符号

首先,对于增广文法的开始项 S ′ → S S'\rightarrow S SS,其向前看符号为#(句子的结束符),该项变为 S ′ → S , # S'\rightarrow S , \# SS,#
对于直接右移点的项,向前看符号不变,然后求对应的闭包。
对于这些新增加的项,口诀先看前,再看后。先看左侧是什么,然后再看其它项中点后有相同符号的情况。比如
已知 A → α ⋅ B β , a A\rightarrow \alpha \cdot B\beta , a Aα,a
B ′ → ? B'\rightarrow ? B?的向前看符号。
(这里的问号是指可以是任何左侧是B的项,因为它们会获得相同的向前看符号)
左侧为B,则看所有式子中点后紧接是B的项。
然后分两种情况:

  • β \beta β为空,直接抄,将原来项的向前看符号抄过来即可
  • β \beta β不空,若其为终结符,直接将其加入;若为非终结符,则加入 F I R S T ( β ) FIRST(\beta) FIRST(β)

比如本例中的 I 0 I_0 I0
首先,初态的向前看符号加入
S ′ → ⋅ S , # S' \rightarrow \cdot S,\# SS,#
求闭包,将S的产生式全部加入
S ′ → ⋅ S , # S' \rightarrow \cdot S,\# SS,#
S → ⋅ S S + S\rightarrow\cdot SS+ SSS+
S → ⋅ S S ∗ S\rightarrow\cdot SS* SSS
S → ⋅ a S\rightarrow\cdot a Sa
此时,先看前要求的是S,再看后根据点后紧接着S的全部项求向前看符号。这里除最后一项外均为点后紧接S。

  • 第一项 S ′ → ⋅ S , # S' \rightarrow \cdot S,\# SS,#,S后为空,符合情况1,直接抄,#
  • 第二项和第三项,S后不空,符合情况2,且其后紧接的S非终结符,故加入 F I R S T ( S ) FIRST(S) FIRST(S)

F I R S T ( S ) = a FIRST(S)={a} FIRST(S)=a,因此,加入向前看符号后,最终的 I 0 I_0 I0为:
S ′ → ⋅ S , # S' \rightarrow \cdot S,\# SS,#
S → ⋅ S S + , # / a S\rightarrow\cdot SS+,\#/a SSS+,#/a
S → ⋅ S S ∗ , # / a S\rightarrow\cdot SS*,\#/a SSS,#/a
S → ⋅ a , # / a S\rightarrow\cdot a,\#/a Sa,#/a

2 结果

然后不断调用GOTO函数,即可得到全部项集。
画出状态转换图如下:
在这里插入图片描述
生成LR(1)分析表如下:
在这里插入图片描述

总结

本文介绍了SLR和LR(1)分析表的构建方法,虽然方法简单,但仅停留于表面,适合做题目使用,并不适合彻底了解二者的机制。今后有时间会补充相关内容的完整知识。
LR(1)分析器部分强推第二个链接中B站的讲解视频!非常清晰!


特别鸣谢:
SLR分析表详解
LR(1)分析表视频讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值