编译原理 第四章 语法分析
~~ 上一篇文章点击跳转 ~~ : 编译原理第一章绪论
~~ 下一篇文章点击跳转 ~~ : 数据库系统概念第一章
构造预测分析程序、状态转换图、预测分析表
例1 给出 文法 G1:
- E → E + T | T
- T → T * F | F
- F → ( E ) | id
- 首先,改写文法,消除左递归(只有LL(1)文法需要消除左递归),得到 文法 G1’:
- E → T E ’
- E ’ → + T E ’ | ε
- T → F T ’
- T ’ → * F T ’ | ε
- F → ( E ) | id
- 构造状态转换图:
- 状态转换图的化简:
- 预测分析程序:
- E 的过程:
void procE(void) {
procT();
if (char == '+') {
forward pointer;
procE();
}
}
- T 的过程:
void procT(void) {
procF();
if (char == '*') {
forward pointer;
procT();
}
}
- F 的过程:
void procF(void) {
if (char == '(') {
forward pointer;
procE();
if (char == ')')
forward pointer;
else error();
};
else if (char == 'id')
forward pointer;
else error();
}
- 作预测分析表:
- 对于 E → T E ’:由于 FIRST(T E ’) = FIRST(T) = { (,id },故应把 E → T E ’ 放入表项 M [ E,( ] 和 M [ E,id ] 中
- 对于 E ’ → + T E ’:由于 FIRST(+ T E ’) = { + },故应把 E ’ → + T E ’ 放入表项 M [ E ’,+ ] 中
- 对于 E ’ → ε:由于FOLLOW(E ’) = {$,) },故应把 E ’ → ε 放入表项 M [ E ’,$] 和 M [ E ’,) ] 中
- 依次检查其余产生式,得到表 M
构造FIRST集合、FOLLOW集合、LL(1)文法
- FIRST集合
- FOLLOW集合
- LL(1)文法
规范归约、LR分析程序的工作过程、LR(0)项目
- 规范归约(最左归约)过程是最右推导(规范推导)的逆过程,由最右推导得到的右句型也称为规范句型
- 例2 具有如下产生式集合的 文法 G2 的 LR 分析表 如图所示,利用该分析表分析输入符号串 id + id * id
(1)E → E + T (2)E → T (3)T → T * F
(4)T → F (5)F → ( E ) (6)F → id
(Si 中的 S 表示“移进”,即把当前输入符号和状态 i 压入栈,i 成为新的栈顶;Rj 中的 R 表示“归约”,即用第 j 个产生式进行归约;ACC 表示“接受”;空白表示“出错”)
- 分析动作:
- 常用的 LR 分析表有 SLR(1) 分析表、LR(1) 分析表、LALR(1) 分析表
- 产生式 A → X Y Z 对应有 4 个 LR(0) 项目:
(1)A → • X Y Z
(2)A → X • Y Z
(3)A → X Y • Z
(4)A → X Y Z •- 归约项目:圆点在产生式最右端的 LR(0) 项目,如 A → X Y Z •
- 接受项目:对文法开始符号的归约项目,如 S ’ → S •
- 移进项目:圆点后第一个符号为终结符号的 LR(0) 项目,如 A → α • a β
- 待约项目:圆点后第一个符号为非终结符号的 LR(0) 项目,如 A → α • B β
构造SLR(1)分析表
例3 构造如下 文法 G3 的 LR(0) 项目集规范族 及 识别所有活前缀的 DFA,并构造其 SLR(1) 分析表
- S → a A | b B
- A → c A | d
- B → c B | d
- 构造文法 G3 的 拓广文法 G3’:
- S ’ → S
- S → a A | b B
- A → c A | d
- B → c B | d
- 首先,构造活前缀 ε 的 LR(0) 有效项目集,记为 I0:
I0 = closure( { S ’ → • S } ) = { S ’ → • S,S → • a A,S → • b B }
现在,从 I0 出发构造其他活前缀的 LR(0) 有效项目集
从 I0 出发的转移有:
I1 = go( I0,S ) = closure( { S ’ → S • } ) = { S ’ → S • }
I2 = go( I0,a ) = closure( { S → a • A } ) = { S → a • A,A → • c A,A → • d }
I3 = go( I0,b ) = closure( { S → b • B } ) = { S → b • B,B → • c B,B → • d }
由于 I0 是活前缀 ε 的 LR(0) 有效项目集,所以 I1、I2、I3 分别是活前缀 S、a、b 的 LR(0) 有效项目集;由于 I1 中唯一的元素为接受项目 S ’ → S •,故没有从 I1 出发的转移
从 I2 出发的转移有:
I4 = go( I2,A ) = closure( { S → a A • } ) = { S → a A • }
I5 = go( I2,c ) = closure( { A → c • A } ) = { A → c • A,A → • c A,A → • d }
I6 = go( I2,d ) = closure( { A → d • } ) = { A → d • }
I4、I5、I6 分别是活前缀 aA、ac、ad 的 LR(0) 有效项目集
从 I3 出发的 • • • • • •
至此,不再有新的有效项目集出现,上面构造的 LR(0) 有效项目集 I0、I1、I2、I3、• • • 、I11 组成的 集合 C = { I0、I1、• • • 、I11 } 就是文法 G3 的 LR(0) 项目集规范族 - 识别文法 G3 的所有活前缀的 DFA:
- 构造 SLR(1) 分析表,并判断该文法是否为 SLR(1) 文法:
考察 I0 = { S ’ → • S,S → • a A,S → • b B }
对项目 S ’ → • S,有 go( I0,S ) = I1,所以置 goto[ 0,S ] = 1
对项目 S → • a A,有 go( I0,a ) = I2,所以置 action[ 0,a ] = S2
对项目 S → • b B,有 go( I0,b ) = I3,所以置 action[ 0,b ] = S3
考察 I1 = { S ’ → S • }
项目 S ’ → S • 是接受项目,所以置 action[ 1,$] = ACC
考察 I2 = { S → a • A,A → • c A,A → • d }
对项目 S → a • A,有 go( I2,A ) = I4,所以置 goto[ 2,A ] = 4
对项目 A → • c A,有 go( I2,c ) = I5,所以置 goto[ 2,c ] = S5
对项目 A → • d,有 go( I2,d ) = I6,所以置 goto[ 2,d ] = S6
考察 I3 = { S → b • B,B → • c B,B → • d }
对项目 S → b • B,有 go( I3,B ) = I7,所以置 goto[ 3,B ] = 7
对项目 B → • c B,有 go( I3,c ) = I8,所以置 goto[ 3,c ] = S8
对项目 B → • d,有 go( I3,d ) = I9,所以置 goto[ 3,d ] = S9
考察 I4 = { S → a A • }(假设产生式 S → a A 的编号为 1)
项目 S → a A • 是归约项目,因为 FOLLOW( S ) = {$},所以置 action[ 4,$] = R1
考察 I5 • • • • • •
最终可得到 SLR(1) 分析表 如下
由于表中不存在任何冲突,所以 文法 G3 是 SLR(1) 文法
LR(0)文法、SLR(1)文法

构造LR(1)分析表
-
例4 构造如下 文法 G4 的 LR(1) 项目集规范族 及 识别所有活前缀的 DFA,并构造其 LR(1)分析表
- S → C C
- C → c C | d
- 构造文法 G4 的 拓广文法 G4’:
(0)S ’ → S
(1)S → C C
(2)C → c C
(3)C → d - 构造其 LR(1) 项目集规范族 及 识别所有活前缀的 DFA:
- 构造 LR(1)分析表:
-
例5 构造如下 文法 G5 的 LR(1) 项目集规范族 及 识别所有活前缀的 DFA,并构造其 LR(1)分析表
- S → L = R
- S → R
- L → * R
- L → id
- R → L
- 拓广文法得到 G5’:
(0)S ’ → S
(1)S → L = R
(2)S → R
(3)L → * R
(4)L → id
(5)R → L - 构造 文法 G5 的 LR(1) 项目集规范族 及 识别所有活前缀的 DFA:
- 构造 LR(1)分析表(由于表中不含有多重定义的表项,所以该文法是 LR(1) 文法):
LR(1)项目集特征
- 同心集:
如果 两个 LR(1) 项目集 去掉向前看符号之后是相同的,即它们具有相同的心(core),则称这两个项目集是同心集
LR(1) 项目集 的心就是一个 LR(0) 项目集 - 项目集的核:
除初态项目集外,一个项目集的核(kernel)是由该项目集中那些圆点不在最左边的项目组成的集合
LR(1) 初态项目集 的核中有且只有项目 [ S ’ → • S,$]
构造LALR(1)分析表
- 思想:
(1)合并 LR(1) 项目集规范族 中的 同心集(,以减少分析表的状态数)
(2)用 项目集的核 代替 项目集(,以减少项目集所需的存储空间)
注:同心集合并后的项目集中 只可能 出现 归约—归约冲突,绝不可能 出现 移进—归约冲突 - 步骤:
(1)构造 LR(1) 项目集规范族,如果 不存在冲突,说明该文法是 LR(1) 文法
(2)检查 LR(1) 项目集规范族中有没有 同心集,若没有,则该 LR(1) 分析表 就是 LALR(1) 分析表,若有,则 合并同心集
(3)检查合并同心集后的项目集规范族是否有 归约—归约冲突,若有,则 不存在 LALR(1) 分析表,若没有,则根据它可以 构造文法的 LALR(1) 分析表 - 例6 构造 文法 G4 的 LALR(1) 分析表
- 在 例4 中,已经构造出该文法的 LR(1) 项目集规范族,也给出了 识别所有活前缀的 DFA,并且构造出了该文法的 LR(1) 分析表,说明 文法 G4 是 LR(1) 文法
- 构造 LALR(1) 分析表:
(i) 其 LR(1) 项目集规范族 有三对 同心集 可以合并,即
(ii) 计算 转移函数 go
先看 go( I36,C ),存在 go( I3,C ) = I8、go( I6,C ) = I9,因 I8、I9 都是 I89 的一部分,故有 go( I36,C ) = I89
再看 go( I2,c ),由于原来的 LR(1) 项目集规范族 中存在 go( I2,c ) = I6,因 I6 都是 I36 的一部分,因此 go( I2,c ) = I36(action[ 2,c ] = S36)
(iii) 作出分析表,可以看出该表不存在冲突的表项,因此 文法 G4 是 LALR(1) 文法
~~ 上一篇文章点击跳转 ~~ : 编译原理第一章绪论
~~ 下一篇文章点击跳转 ~~ : 数据库系统概念第一章
查看更多文章