编译原理:构造LR(0)分析表

前言

本文旨在用通俗的方式讲解如何手工构造LR(0)分析表,不涉及如何解决冲突问题

冲突问题解决见:SLR、LR(1) 分析

在阅读本文前你应该对 LR文法、移入/规约分析 有基本的了解。

1. 基本名词解释


1.1 项目

定义:在右部的某个地方加点的产生式。比如:

E -> A·bC
E -> Ab·C
... ...

这个概念的定义并不重要,因为你看了定义可能也不知道它的意义何在。那为什么要这么定义一个项目呢?为了符号化,看起来更直观

比起你一大段一大段的文字,有时还不如简单的一个符号直观

因为在构造分析表前我们需要构造对应的状态自动机,那么如果我们能一眼看出某个项目对应什么状态那效率不就更高。举个例子:

E -> ·AbC

你一眼可以看出:当前刚刚开始分析这个文法,因为 · 在产生式的开始处。除此之外,你还能看出当遇到一个非终结符 A 时,这个项目将会进入下一个状态(项目):

E -> A·bC

换句话说:一个项目对应着LR分析过程中的某个状态

根据点位置的不同,项目可以分为两大类:

  • 待约项目:点不在末尾(等待规约)
E -> ·AbC
E -> A·bC
E -> Ab·C
  • 规约项目:点在末尾
E -> AbC·

其中有两个特殊的项目:

  • 初始项目:特殊的待约项目,点在开始符号右部产生式的开始(初始态,表示文法刚开始分析)
现有文法:
	S -> E
	E -> AbC
那么
	S -> ·E
为初始项目,表示当前文法 S 开始分析;
而	
	E -> ·AbC
不能称为初始项目,只能称为待约项目。
因为 E 不是开始符号,此项目开始分析
时并不能表示 “文法 S 开始分析”
  • 接收项目:特殊的规约项目,点在开始符号右部产生式的末尾(结束态,表示文法分析完毕)
现有文法:
	S -> E
	E -> AbC
那么
	S -> E·
为接受项目,表示当前文法 S 分析完毕;
而	
	E -> AbC·
不能称为接受项目,只能称为规约项目。
因为 E 不是开始符号,此项目分析结束
后并不能表示 “文法 S 分析完毕”

1.2 项目集闭包

定义:某个项目与其所有等价项目的所构成的集合。显然:一个项目集闭包对应 LR自动机 的一个状态
其求解方法为:
当 · 的后面的字符为

  • 非终结符时,将后面的非终结符用对应的产生式进行展开,并且其点位于开始处
  • 终结符时,结束

【例】 现有文法:

S -> E
E -> Ab
A -> a

那么对于初始项目:

S -> ·E

由于 · 后的 E 是非终结符,那么用对应的产生式 “E -> Ab” 将其展开得:

E -> ·Ab

同理将 A 展开:

A -> ·b

由于 b 时终结符,结束。也就是说:

S -> ·E
E -> ·Ab
A -> ·a

为一个项目集闭包,他们之间都是等价项目(即对应同一状态)。


【附加】标识项目

该定义是为了后续表达的方便,我自己定义的

定义:用来唯一标识一个项目集闭包的项目。
因为在构造 LR 自动机的构建过程中很容易出现重复的情况。
那么用什么来标识呢?举个两个例子:
【例1】现有文法

S -> A
A -> a

它的初始项目集闭包为

(0) S -> ·A
(1) A -> ·a

那么用 (0) 来标识上述项目集闭包,因为 (1) 是由它推出来的。

【例2】现有文法

S -> A | B | c
A -> a
B -> b

初始项目集闭包为

(0) S -> ·A
(1) S -> ·B
(2) S -> ·c
(3) A -> ·a
(4) B -> ·b

那么它用 (0) (1) (2) 共同来标识,因为其他项目都是通过他们推导出来的,少一个就会不完整,多一个会有冗余
可以认为:如果两个项目集闭包的标识项目一样,则认为这两个项目集闭包是一样的


1.3 项目集规范族

定义:一个文法的所有项目集闭包所构成的集合。换句话说,项目集规范族是 一个LR自动机 的所有状态 的集合
求解它无非就是求解多个项目集闭包,这并不难,难点在于容易漏掉。

一些教程采用先列出所有项目,在进行分类,我觉得这种方法比较麻烦,我采用另外一种方法:确定初始项目集闭包,依次推出其他所有项目集闭包


1.4 增广文法

给文法增加一个新的开始符号 S’ 替换掉源开始符号,并加入 S’ -> S

为了书写方便,我用 T 作为 S’

为什么要这么做?这是为了确保只有一个接收项目集闭包

如此可以提高分析效率

比如现有文法

S -> E | e

那么你会发现会有两个接收项目:

1. S ->2. S ->

这就导致了有两个接收状态,因此引入增广文法,将上述文法改写为

T -> S
S -> E | e

那么现在的接收项目集闭包只有一个

T ->

下面以一个例题来讲解如何构造 LR 分析表

文法如下:
(0)	S -> BB
(1)	B -> aB
(2)	B -> b

2. 构造LR自动机


2.1 增广文法

将上述文法改写为

(0) T -> S
(1)	S -> BB
(2)	B -> aB
(3)	B -> b

2.2 构造 初始项目集闭包

首先,根据 (0) 产生式 (开始符号的产生式) 得出第一个初始项目

T -> ·S

根据此构造它的项目集闭包:

T -> ·S
S -> ·BB
B -> ·aB
B -> ·b

2.3 根据初始项目集闭包构建其余所有项目

算法:

  • 找出每个项目集闭包的下一字符(也就是 · 之后的字符,无论是终结符还是非终结符)集合,看通过每一个字符后,该项目集闭包能到达哪一个项目集闭包,直到该项目集闭包中的所有下一字符都被遍历过;
  • 如果下一字符集合为空,则跳过
  • 重复上述直到所有项目集闭包都被遍历过。

为什么要记录 · 后的字符?先看一个定义:后继项目

B -> ·aB 的后继项目为 B -> a·B
直观地说,某个项目的后继项目就是
将该项目中的 · 后移一位得到的项目

那么我们记录一个项目集的所有下一字符,就能知道当此项目(状态)遇到某个字符时,它应该进入哪一个项目(状态)。

2.3.1 分析 A0

现在我们有初始项目集闭包,它对应初始状态,记作 A0(后续状态以此类推)

A0 
(0) T -> ·S
(1) S -> ·BB
(2) B -> ·aB
(3) B -> ·b

则它的下一字符集为

{S, B, a, b}

  • 通过字符 S
    遍历 A0 中的所有项目,可以发现只有 A0(0) 满足
    在这里插入图片描述
    对应的后继项目为

    T ->

    由于它为接收项目,没有等价项目,因此 A1 只有一个项目

    A1
    (0) T ->

    因此
    在这里插入图片描述

    红色的项目表示它是此项目集闭包的标识项目

  • 通过字符 B
    只有 A0(1) 满足,对应的后继项目为

    S -> B·B
    

    由于 · 之后是非终结符,因此有等价项目,所以下一状态 A2

    A2
    (0) S ->B
    (1) B -> ·aB
    (2) B -> ·b
    

    故有
    在这里插入图片描述

  • 通过字符 a
    只有 A0(2) 满足,它的后继项目为

    B -> a·B
    

    同理得出下一状态 A3

    A3
    (0) B ->B
    (1) B -> ·aB
    (2) B -> ·b
    

    故有
    在这里插入图片描述

虽然 A2 与 A3 的 (1) (2) 一样,但是标识项目不同,所以 A2 与 A3 是不一样的状态

  • 通过字符 b
    只有 A0(3) 满足,对应的后继项目为
    B ->
    因此下一状态 A4
    A4
    (0) B ->
    故有
    在这里插入图片描述

现在 A0 的下一字符集被遍历完了,所以 A0 分析结束,进入 A1

2.3.2 分析 A1

A1
(0) T ->

显然它的下一字符集为空,故跳过

2.3.3 分析 A2

A2
(0) S ->B
(1) B -> ·aB
(2) B -> ·b

其下一字符集为

{B, a, b}

下面只针对有必要的地方给出解释,其余直接看图自行分析

  • 通过 B
    在这里插入图片描述
  • 通过 a
    只有 A1(1) 满足,其后继项目为
    B -> a·B
    
    你会发现这与 A3 的标识项目一样,因此不产生新状态,此时进入的下一状态为 A3
  • 通过 b
    与 “通过 a” 同理,因此有
    在这里插入图片描述

2.3.4 分析 A3

A3
(0) B ->B
(1) B -> ·aB
(2) B -> ·b

其中需要注意的是 通过字符 a 到达的是它自己 (A3)
在这里插入图片描述

剩下的状态的下一字符集都为空,故上图为最终结果


3. 构建 LR(0) 分析表

如何使用 LR(0) 分析表进行分析不在本文讨论范围,本文只讨论如何构建。
在此之前,你应知道如何使用 LR(0) 分析表进行语法分析。

LR(0)分析表分为两部分:

  • action 表:针对终结符
  • goto 表:针对非终结符

其中在 action 表中,有以下元素

  • sn:表示 移入当前终结符,并进入n状态

比如 s1 表示移入当前终结符,进入 1 状态

  • rn:表示 用 文法的 (n) 式来进行当前项目的规约

比如 r0 表示用 (0) 来进行规约

  • acc:表示 接收成功 (accept)
  • :表示 分析失败

在 goto 表中,它表示当某状态遇到非终结符时应进入哪一状态,因此它的元素只记录状态编号,空的地方表示分析失败

那么现在根据之前的分析开始构建分析表

  • 文法
    (0) T -> S
    (1)	S -> BB
    (2)	B -> aB
    (3)	B -> b
    
  • 自动机
    在这里插入图片描述

3.1 构建 goto 表

goto表只与 自动机中的输入字符中的非终结符 (也就是箭头上的非终结符) 有关

{ B, S }

据自动机分析可知:

A0 --S--> A1
表示 A0 遇见 S 进入 A1
其余类推...

A0 --B--> A2
A2 --B--> A5
A3 --B--> A6

并且共有7个状态,因此有

在表中,状态我只用数字表示(“A0” 用 “0” 表示)

stateBS
021
1
25
36
4
5
6

3.2 构建 action 表

action 表只与 自动机中的输入字符中的终结符 (也就是箭头上的终结符)有关

{ a, b, $ }

  • 加入 ‘$’ 字符
  • $ 表示结束字符,也就是说遇见 ‘$’ 说明当前输入中没有字符了

前面我们知道项目分两大类为:待约项目、规约项目,在 action 中满足:

  • 对于待约项目使用移入动作
    在之前的自动机中,待约项目有

    A0、A2、A3

    其中有

    A0 --a--> A3
    

    所以对应的 action 表为

    stateab$
    0s3

    表示 A0 遇见字符 a 时,将字符 a 移入栈内,并进入 A3 状态;遇见其他字符说明分析失败。
    其余以此类推… …

  • 对于规约项目使用规约动作
    一般规约项目,对于任何终结符都是采用对应的产生式进行规约,对于 接受项目,只有遇到 $ 时表示 acc,其余都表示分析失败
    在之前的自动机中,待约项目有

    A1、A4、A5、A6

    其中你会发现待约项目没有出边(没有出去的箭头,只有指向它自己的箭头)

    当然有时有的项目中既有待约项目也有规约项目,这就属于冲突,不在本文讨论范围

    • 对于 A1
    A1
    (0) T ->

    由于它是接受项目,因此

    stateab$
    1acc

    表示 A1 遇见 $ 时分析成功,遇见其他字符都分析失败。

    • 对于 A4
    A4
    (0) B ->

    在之前的文法中

    (0) T -> S
    (1)	S -> BB
    (2)	B -> aB
    (3)	B -> b
    

    A4 (B->b) 是 文法 (3) 式, 由于它是待约项目,因此

    stateab$
    4r3r3r3

    表示 A4 遇见 a、b、$ 时用 文法的 (3) 产生式来进行规约。
    其余以此类推… …

最后的 action 表为

stateab$
0s3s4
1acc
2s3s4
3s3s4
4r3r3r3
5r1r1r1
6r2r2r2

最后

如果你发现本文出现任何错误,欢迎指正。

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值