编译原理第四章语法分析by SixInNight


~~ 上一篇文章点击跳转 ~~ 编译原理第一章绪论
~~ 下一篇文章点击跳转 ~~ 数据库系统概念第一章

构造预测分析程序、状态转换图、预测分析表


例1 给出 文法 G1

  • E → E + T | T
  • T → T * F | F
  • F → ( E ) | id
  1. 首先,改写文法,消除左递归(只有LL(1)文法需要消除左递归),得到 文法 G1
  • E → T E
  • E → + T E | ε
  • T → F T
  • T * F T | ε
  • F → ( E ) | id
  1. 构造状态转换图
  2. 状态转换图的化简
  3. 预测分析程序
  • 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();
}
  1. 预测分析表
  • 对于 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 具有如下产生式集合的 文法 G2LR 分析表 如图所示,利用该分析表分析输入符号串 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 表示“接受”;空白表示“出错”)
  1. 分析动作

  2. 常用的 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 构造如下 文法 G3LR(0) 项目集规范族识别所有活前缀的 DFA,并构造其 SLR(1) 分析表

  • S → a A | b B
  • A → c A | d
  • B → c B | d
  1. 构造文法 G3拓广文法 G3
  • S → S
  • S → a A | b B
  • A → c A | d
  • B → c B | d
  1. 首先,构造活前缀 ε 的 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) 项目集规范族
  2. 识别文法 G3 的所有活前缀的 DFA
  3. 构造 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 构造如下 文法 G4LR(1) 项目集规范族识别所有活前缀的 DFA,并构造其 LR(1)分析表

    • S → C C
    • C → c C | d
    1. 构造文法 G4拓广文法 G4
      (0)S → S
      (1)S → C C
      (2)C → c C
      (3)C → d
    2. 构造其 LR(1) 项目集规范族识别所有活前缀的 DFA
    3. 构造 LR(1)分析表
  • 例5 构造如下 文法 G5LR(1) 项目集规范族识别所有活前缀的 DFA,并构造其 LR(1)分析表

    • S → L = R
    • S → R
    • L → * R
    • L → id
    • R → L
    1. 拓广文法得到 G5
      (0)S → S
      (1)S → L = R
      (2)S → R
      (3)L → * R
      (4)L → id
      (5)R → L
    2. 构造 文法 G5LR(1) 项目集规范族识别所有活前缀的 DFA
    3. 构造 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 构造 文法 G4LALR(1) 分析表
  1. 例4 中,已经构造出该文法的 LR(1) 项目集规范族,也给出了 识别所有活前缀的 DFA,并且构造出了该文法的 LR(1) 分析表,说明 文法 G4 是 LR(1) 文法
  2. 构造 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) 文法


~~ 上一篇文章点击跳转 ~~编译原理第一章绪论
~~ 下一篇文章点击跳转 ~~数据库系统概念第一章

查看更多文章

计算机组成原理课程设计—硬连线、流水

Win10系统虚拟机(VMware14Pro)及Ubuntu16.04安装教程

数据库实验——表/视图的创建

Python练习题(一)

Flask笔记(一)

JS学习第一天

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值