编译原理期末快考试了,所以在复习过程中做了一些笔记;主要参考的是编译原理第三版-张素琴-王原生 这本书,还有一些B站的视频,链接放在下面:
【编译原理不挂科】【速成学习】知识点重现&大题秒杀模板,考试不挂科,学会就得分,分分钟带你走近编译原理_哔哩哔哩_bilibili
【武汉大学】编译原理混子速成——面向期末试卷复习:全集_哔哩哔哩_bilibili
东北大学编译原理符号表部分讲解——张俐老师_哔哩哔哩_bilibili
参考的文件:NiuTrans/compiler-notes
参考MOOC资源:
编译原理-期末预习
- 编译原理概念,每一步骤的作用干啥了前端后端
- 词法分析部分:BNF,正则式等概念,自动机DFA NFA 确定化,简化,含有空串边ε的转化(简单的_子集构造法,复杂的_ε闭包算法) 自动机>正则式转化
- 语法分析部分:文法,语言等概念,由给定的句型或语言进行语法树绘制_找短语简单短语句柄,判断二义性_画俩语法树_优先级-结合性的二义性, 等价文法_直接左递归处理_公式_改写 ,FIRST集FOLLOW集 SELECT集 LL(1)分析表,LL(1) 简单优先没时间就别看了 LR(0) _可归前缀图_构造矩阵
- 符号表_数组-记录型_复杂的内部表示会画,几种中间代码-逆波兰式_三元式_四元式 优化前优化后的四元式能写出,中间代码优化-对应的VVL表_二元组写出,前两种优化_
flowchart TD
root((编译原理))
subgraph 词法分析
direction LR
A(词法分析器)
B(正则表达式)
C(有限自动机)
A --> B
A --> C
end
subgraph 语法分析
direction LR
D(语法分析器)
E(上下文无关文法)
F(语法树)
D --> E
D --> F
end
subgraph 语义分析
direction LR
G(语义分析器)
H(类型检查)
I(符号表)
G --> H
G --> I
end
subgraph 中间代码生成
direction LR
J(中间表示)
K(三地址码)
J --> K
end
subgraph 优化
direction LR
L(代码优化)
M(常量折叠)
N(循环优化)
L --> M
L --> N
end
subgraph 目标代码生成
direction LR
O(目标代码生成器)
P(汇编代码)
O --> P
end
subgraph 汇编与链接
direction LR
Q(汇编器)
R(链接器)
S(加载器)
Q --> R
R --> S
end
root --> 词法分析
root --> 语法分析
root --> 语义分析
root --> 中间代码生成
root --> 优化
root --> 目标代码生成
root --> 汇编与链接
第一章 引论
翻译程序:将一种计算机编程语言所编写的程序(源程序)翻译成与之等价的另外一种计算机语言的程序(目标程序),完成这个翻译工作的程序称为翻译程序。编译程序、解释程序、汇编程序均被认为是翻译程序。
编译程序:源程序语言是高级语言,目标程序语言是汇编语言或机器语言之类的低级语言,这样的翻译程序称为编译程序。
解释程序:在词法、语法、语义分析方面与编译程序的工作原理基本相同,但在执行过程中不产生目标程序,而是直接解释执行源程序或源程序的内部形式(中间代码),即边解释边执行。
汇编程序:源程序语言是汇编语言,输出目标程序语言是机器语言,这样的翻译程序称为汇编程序。
第二章 文法和语言
阐明语法的一个工具是文法,本章节介绍文法和语言的概念,重点讨论上下文无关文法及其句型分析中的有关问题。
定义:文法
: 非终结符集(语法实体-变量)
: 终结符集
: 规则( )的集合
: 识别符或开始符
设
乔姆斯基对文法类型分类:
型文法,短语文法,任何 型语言都是可递归可枚举的
型文法,上下文有关,每个产生式 , ,仅 除外。
型文法,上下文无关,每一个产生式 满足: , ,(形式: )
型文法,正规文法,每一个产生式形式为 ,其中 和 都 , ,
四种文法类型的定义是逐渐增加限制的,因此每一种正规文法都是上下文无关的,每一种上下文无关文法是上下文有关的,而每一种上下文有关文法都是0型文法。一种文法产生相对应的语言。
2.5上下文无关文法及其语法树
语法树(推导树-语法分析树)
| — | — |
|---|---|
| 最左推导 | 最右推导(规范推导)
|
一个文法存在某个句子对应两棵不同的语法树,这个语法是二义的。
上下文无关语言每一个文法都是二义的,才说此语言是二义的
不存在一个算法,在有限的步骤内确切判定任给的一个文法是否为二义的,为构造无二义性文法,寻找一组充分条件。
2.6 句型的分析
| — | — |
|---|---|
| 自上而下的分析(选择正确的右部去替换) | 自下而上的分析(选择正确的可归约串) |
是一个文法, 是文法的开始符号, 是文法 的一个句型,如果有 且 ,称 是句型 相对于 的短语。
如果有
,则称 是句型 $\alpha \beta \delta $ 相对于规则 的直接短语(简单短语)。
一个右句型的直接短语称为该句型的句柄。(句柄只适用于右句型)。
求一个句型的短语、简单短语、句柄
画出句型的语法树
短语:看有子节点的结点,一个节点的所有叶节点的组合;
直接短语:一个结点它的子节点就是叶节点,所有满足这个的短语。
句柄:最左直接短语。
:memo:
graph TD E --> E1 E --> + E --> T T --> F F --> d E1 --> T1 T1 --> T2 T1 --> * T1 --> F1 F1 --> V T2 --> F2 F2 --> V1
短语:d+V
V1,d,V V1,V,V1 直接短语:d,V,V1
句柄:d
:memo:
graph TD S --> a S --> A S --> c S --> B S --> e A --> A1 A --> b B --> d
短语:aA1bcde、A1b、d
直接短语:A1b、d
句柄:A1b
:memo:
graph TD S --> S1 S --> S2 S2 --> a S --> * S1 --> S3 S1 --> S4 S1 --> + S3 --> a1 S4 --> a2
短语:a,a1, a2, a1a2
, a a1 a2 直接短语:a ,a1, a2
句柄:a
2.7 有关文法的实际应用说明
应限制文法不得含有
[^1]:
[^2]: 非终结符不可到达的
第三章 词法分析
词法分析是编译的第一个阶段,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用于语法分析。执行词法分析的程序称为词法分析程序或扫描程序。本章讨论词法分析程序的设计原则、单词的描述技术、识别机制及词法分析程序的自动构造原理。
3.1 词法分析程序设计
从左至右逐个字符对源程序进行扫描,产生单词序列,用于语法分析
词法分析中扫描的单词符号一般有5类:(1)关键字;(2)标识符;(3)常数;(4)运算符;(5)界符。
作用:获得有意义的单词符号:$ \to$ 二元式(单词种别码,单词自身的值)
1 | 词法分析程序中如何识别有意义的单词? |
3.3 正规文法和正规式的等价性
3.4 有穷自动机
| DFA—确定的有穷自动机 | NFA—不确定的有穷自动机 |
|---|---|
| Deterministic Finite Automata | Nondeterministic Finite Automata |
1 | 有几个问题:NFA的确定化;DFA的化简。。。 |
DFA:
,有穷状态集
,有穷字母表(输入符号表)
,转换函数, ,
, $S\in K $, 唯一初态
, ,终态集(可接受/结束状态)
初态结点 :$\Rightarrow $ Ⓢ 或 标记
NFA:
==NFA确定化
子集法:DFA中的每一个状态对应NFA的一组状态
1 | 定义状态集合 *I* 的两个运算 : |
---
title: NFA N 的状态图
---
stateDiagram-v2
direction LR
[*] --> 0
0 --> 1: ε
0 --> 7: ε
1 --> 2: ε
1 --> 4: ε
2 --> 3: a
3 --> 6: ε
4 --> 5: b
5 --> 6: ε
6 --> 1: ε
6 --> 7: ε
7 --> 8: a
8 --> 9: b
9 --> 10: b
10 --> [*]
:memo:
1 | // 子集构造算法 |
1 | 使用子集构造算法对上图NFA N构造子集: |
--- title: DFA M状态转换图 --- stateDiagram-v2 direction LR [*] --> 0 0 --> 2:b 0 --> 1:a 1 --> 1:a 1 --> 3:b 2 --> 2:b 2 --> 1:a 3 --> 1:a 3 --> 4:b 4 --> 2:b 4 --> 1:a 4 --> [*]
==DFA的化简== 最小化
没有多余状态–状态中没有两个是相互等价的
FA的无用状态:从自动机开始状态出发,任何输入串也不能到达此状态,或者从这个状态没有通路到达终态。
FA中两个状态s和t等价的条件是:
- 一致性条件–状态s和t必须同时为可接受状态或不可接受状态
- 蔓延性条件–对于所有输入符号,状态s和状态t必须转换到等价的状态里。
分割法:把DFA状态分为一些不相交的子集,任何不同的两个子集的状态都是可区别的,同一子集中的任何两个状态都是等价的。
:memo: 将下图的DFA M最小化
1 |
|
--- title: DFA M --- stateDiagram-v2 direction LR [*] --> 1 1 --> 3:b 1 --> 6:a 2 --> 3:b 2 --> 7:a 3 --> 1:a 3 --> 5:b 4 --> 4:a 4 --> 6:b 5 --> 3:b 5 --> 7:a 6 --> 4:a 6 --> 1:b 7 --> 4:a 7 --> 2:b 5 --> [*] 6 --> [*] 7 --> [*]
--- title: DFA M' --- stateDiagram-v2 direction LR [*] --> 1 1 --> 3:b 1 --> 6:a 3 --> 1:a 3 --> 5:b 4 --> 4:a 4 --> 6:b 5 --> 3:b 5 --> 6:a 6 --> 1:b 6 --> 4:a 5 --> [*] 6 --> [*]
3.5 正规式和FA的等价性
一、对于
stateDiagram-v2 direction LR 1 --> 2: r1 2 --> 3: r2
转换为:
stateDiagram-v2 direction LR 1 --> 3:r1r2
stateDiagram-v2 direction LR 1 --> 2:r1 1 --> 2:r2
转换为:
stateDiagram-v2 direction LR 1 --> 2:r1|r2
stateDiagram-v2 Direction LR 1 --> 2:r1 2 --> 2:r2 2 --> 3:r3
转换为:
stateDiagram-v2 direction LR 1 --> 3:r1r2*r3
弧上的标记为所求的正规式
:memo:
--- title: (a) --- stateDiagram-v2 direction LR x --> 0:e 0 --> 0:a,b 0 --> 1:b 0 --> 3:a 1 --> 2:b 3 --> 4:a 2 --> 2:a,b 4 --> 4:a,b 4 --> y:e 2 --> y:e y -->[*] 2 -->[*] 4 -->[*]
--- title: (b) --- stateDiagram-v2 direction LR x --> 0:e 0 --> 0:a|b 0 --> 2:bb 0 --> 4:aa 4 --> 4:a|b 2 --> 2:a|b 4 --> y:e 2 --> y:e 2 -->[*] 4 -->[*] y -->[*]
--- title: (c) --- stateDiagram-v2 direction LR x --> 0:e o --> 0:a|b 0 --> y:aa(a|b)* 0 --> y:bb(a|b)* y --> [*]
--- title: (d) --- stateDiagram-v2 direction LR x --> y:(a|b)*(aa|bb)(a|b)* y --> [*]
二、从
(1) 为
、 和 构造 为
---
title: 空串
---
stateDiagram-v2
direction LR
[*] --> x
y --> [*]
--- title: e --- stateDiagram-v2 direction LR [*] --> x x --> y:e y --> [*]
--- title: (a) --- stateDiagram-v2 direction LR [*] --> x x --> y:a y --> [*]
(2) 若
---
title: r = s|t
---
stateDiagram-v2
direction LR
[*] --> x
x --> 1:e
state 1{
direction LR
[*] --> N(s)
N(s) --> [*]
}
x --> 2:e
state 2{
direction LR
[*] --> N(t)
N(t) --> [*]
}
1 --> y:e
2 --> y:e
y --> [*]


:memo: 为
--- title: --- stateDiagram-v2 direction LR [*] --> 0 0 --> 1:e 0 --> 7:e 1 --> 2:e 1 --> 4:e 2 --> 3:a 4 --> 5:b 3 --> 6:e 5 --> 6:e 6 --> 1:e 6 --> 7:e 7 --> [*]
3.6 正规文法和FA的等价性
正规文法
(1)
(2) 增加一个新状态
(3) 对
(4) 对
(5)
:memo: 与文法
--- title: G(S)->NFA M --- stateDiagram-v2 direction LR [*] --> S S --> A:a A --> A:b A --> B:a S --> B:b B --> S:a S --> Z:e B --> Z:e B --> A:b Z --> [*]
:black_medium_small_square: 与下面
--- title: NFA -- G --- stateDiagram-v2 direction LR [*] --> A A --> B:a B --> C:b C --> A:a A --> D:b C --> D:b D --> D:b D --> B:a C -->[*] D --> [*]
graph LR 语法分析 --> id1[自顶向下分析] 语法分析 --> id2[自底向上分析] id1 --> 递归子程序法 id1 --> LL0 id2[自底向上分析] --> 算符优先 id2 --> id3[LR分析] id3 --> LR0 id3 --> SLR0 id3 --> LR1 id3 --> LALR1
语法分析是编译程序的核心部分。语法分析的作用是识别由词法分析给出的单词符号串是否是给定文法的正确句子(程序)。语法分析常用的方法可分为自顶向下分析和自底向上分析两大类。虽然语法分析可以通过确定分析或者不确定分析来实现,但在实际的编译器构造中,几乎都是采用确定分析方式,不确定分析仅具有理论价值。
本章主要介绍自顶向下的确定分析。在第5、6章中,分别介绍两种确定的自底向上分析方法:算符优先分析1和LR分析。这些分析方法各有优缺点,但都是迄今编译程序构造的实用方法。
第四章 自顶向下语法分析
自顶向下分析方法也称面向目标的分析方法,也就是从文法的开始符号出发企图推导出与输入的单词符号串完全相匹配的句子,若输入串是给定文法的句子,则必能推出,反之必然出错。自顶向下的确定分析方法需对文法有一定的限制,但由于实现方法简单、直观,便于手工构造或自动生成语法分析器,因而仍是目前常用的方法之一。而自顶向下的不确定分析方法是带回溯的分析方法,这种方法实际上是一种穷举的试探方法,效率低,代价高,因而极少使用.
4.1 确定的自顶向下分析思想
(1) 设
(2) 设
后跟符号的集合–
(3) 一个产生式的选择符号集–
如果
(4) 一个上下文无关文法是
由此,
1 | LL(1)的含义是: |
4.2 LL(1)文法的判别
计算FIRST、FOLLOW、SELECT集
判断LL(1)文法的步骤:
第一步,求出能推出
第二步,计算FIRST集
-
若
,则 . -
若
,且有产生式 ,则 . -
若
, ,则 . -
若
,而有产生式 。当 时( ),则 都包含在 中。此点意思是,看X产生式,由左向右看,只有前面的产生了
才继续向后看,是这么个意思。 -
当 上一点中 所有
,则 $FIRST(X)=FIRST(Y_i) \cup FIRST(Y_2) \cup … \cup FIRST(Y_n) \cup {\epsilon } $
第三步,计算FOLLOW集
-
为文法的开始符号,把 { }加入 中 这里#为句子括号 - 若
是一个产生式,则把 的非空元素加入 中;如果 则把 也加入到 中。。
第四步,计算SELECT集
- 一般是,
- 若
则
第五步,由SELECT结果,可求相同左部产生式的SELECT集的交集是否为
:memo:
文法
为 (1) $S \overset * \Rightarrow \epsilon \quad A \overset * \Rightarrow \epsilon \quad B \overset * \Rightarrow \epsilon \quad C \overset * \nRightarrow \epsilon \quad D \overset * \nRightarrow \epsilon $
(2) 求FIRST集:
(3) 求各非终结符FOLLOW集:
$ FOLLOW(A) =(FIRST(B)-{\epsilon})\cup FOLLOW(S) \cup FIRST(D)={a,#,c} $
$ FOLLOW(B)=FOLLOW(S)={#} $
$ FOLLOW©= FOLLOW(S)={#} $
$ FOLLOW(D)=FOLLOW(B)\cup FOLLOW©={#}$
(4) 求SELECT集:
(5) 由以上计算结果,得相同做不产生式的SELECT交集为:
由LL(1)文法定义得 该文发不是LL(1)文法,因为S和C的相同左部 其产生式的SELECT集的交集不是
.
4.3 某些非LL(1)文法到LL(1)文法的等价交换
含有直接或间接左递归,含有左公共因子
一、提取左公共因子:
(1) 不一定每个文法的左公共因子都能在有限的步骤内替换成无左公共因子的文法
(2) 一个文法提取了左公共因子后,只解决了相同左部产生式右部FIRST集不相交的问题。当改写后的文法不含空产生式,且无左递归时,则改写后的文法是LL(1)文法,若还有空产生式时,则还需要LL(1)文法的判别方式进行判断才能确定是否为LL(1)文法。
二、消除左递归:
直接左递归 (1)
间接左递归 (2)
(1)
(2) 先通过产生式非终结符置换,将间接左递归变为直接左递归
:memo: 文法
$\Rightarrow B \to aBc ~~~ B\to Bbc ~~~ B\to d $
三、消除文法中一切左递归(要求不含回路、无
1 | 算法步骤: |
:memo: 如下文法:
若非终结符排序为:
,左部为S的产生式(1)无直接左递归,左部为Q的产生式(2)中右部不含S,所以把产生式(1)的右部代入产生式(3)得:
再将产生式(2)的右部代入产生式(4)得 : 对产生式(5)消除直接左递归得:
最终文法为:
当非终结符得排序不同时,最终结果的产生式形式不同,但他们是等价的。
4.4 不确定的自顶向下分析思想(带回溯)
不作详细解释
- 由于相同左部的产生式的右部FIRST集交集不为空而引起回溯
- 由于相同左部非终结符的右部存在能
的产生式,且该非终结符的FOLLOW集中含有其它产生式右部FIRST集的元素 - 由于文法含有左递归而引起回溯
4.5 LL(1)文法的实现
graph LR LL1分析的实现方法 --> 递归下降LL1分析程序 LL1分析的实现方法 --> 表驱动LL1分析程序 表驱动LL1分析程序 --> 1.预测分析程序 表驱动LL1分析程序 --> 2.先进后出栈 表驱动LL1分析程序 --> 3.预测分析表
:memo: ==现以表达式文法为例构造预测分析表==
表达式文法:
构造步骤如下:
第一步,判断文法是否为LL(1)文法:
由于文法中含有左递归,所以必须先消除左递归,文法变为:
(1) 可推出
| E | E’ | T | T’ | F |
|---|---|---|---|---|
| 否 | 是 | 否 | 是 | 否 |
(2) 各非终结符的FIRST集合如下:
(3) 各非终结符的 FOLLOW 集合为:
(4) 各产生式的 SELECT 集合为:
由上可知,有相同左部产生式的SELECT集合的交集为空,所以文法是LL(1)文法。
第二步,构造预测分析表:
对每个终结符或’#‘号用
表示。若 ,则把 $A\to \alpha $ 放入 单元格中,把所有无定义的 标上出错标记;为了简化,产生式的左部可以不写,表中空白处为出错。
|
|
|
|
( | ) | # | |
|---|---|---|---|---|---|---|
| E |
|
|
||||
| E’ |
|
|
|
|||
| T |
|
|
||||
| T’ |
|
|
|
|
||
| F |
|
|
下面用预测分析程序、栈和预测分析表对输入串
| 步骤 | 分析栈 | 剩余输入串 | 推导所用产生式或匹配 |
|---|---|---|---|
| 1 |
|
|
|
| 2 |
|
|
|
| 3 |
|
|
|
| 4 |
|
|
“
|
| 5 |
|
|
|
| 6 |
|
|
|
| 7 |
|
$ +i*i#$ | "+"匹配 |
| 8 |
|
|
|
| 9 |
|
|
|
| 10 |
|
|
"
|
| 11 |
|
|
|
| 12 |
|
$ *i#$ | "*"匹配 |
| 13 |
|
|
|
| 14 |
|
$ i# $ | "
|
| 15 |
|
$ #$ |
|
| 16 |
|
|
|
| 17 |
|
|
接受 |
4.6 LL(1)分析中的出错处理
报错、错误恢复
(1) 应急恢复 (2) 短语层恢复
graph LR 自底向上分析 --> 优先分析-第五章 自底向上分析 --> LR分析-第六章 优先分析 --> 简单优先分析5.2 优先分析 --> 算符优先分析5.3
最右推导–规范推导 自左向右规约–规范规约
自底向上分析的移进-规约过程 为自顶向下最右推导的逆过程
简单优先分析:文法中所有符号之间的优先关系,确定归约过程中句柄。【规范规约】-- 实用价值不大
算符(终结符)优先分析:算符之间的优先关系,有可归约串就归约,不是规范规约–有一些用处
第五章 自底向上优先分析
自底向上分析,也称移进-归约分析,粗略地说它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄或其他可归约串时(该句柄或可归约串对应某产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,这个行为称为一步归约。重复这一过程,直到归约至栈中只剩文法的开始符号时则为分析成功,也就确认了输入串是文法的句子。
**分析过程中如何确定句柄或其它可归约串,就可确定何时进行归约**
:memo: 对输入串
$S \Rightarrow aAcBe \Rightarrow aAcde\Rightarrow aAbcde \Rightarrow abbcde $
| 步骤 | 符号栈 | 输入符号串 | 动作 |
|---|---|---|---|
| 1 |
|
|
移进 |
| 2 |
|
|
移进 |
| 3 |
|
|
归约(
|
| 4 |
|
|
移进 |
| 5 |
|
|
归约(
|
| 6 |
|
|
移进 |
| 7 |
|
|
移进 |
| 8 |
|
|
归约(
|
| 9 |
|
|
移进 |
| 10 |
|
|
归约(
|
| 11 |
|
|
接受 |
上述分析过程也可看成自底向上构造语法树的过程,每步归约都是构造一棵子树,最后输入串结束时刚好构造出整个语法树。
5.2 简单优先分析
(1)
(2)
(3) $X \gtrdot Y $ 当且仅当 G 中存在产生式规则,
:memo:
文法的简单优先关系矩阵
用来表示句子括号,# 的优先性小于所有符号
1 | 简单优先文法的定义: |
1 | 优先分析算法:根据一直优先文法构造相应的优先关系矩阵,并将文法的产生式保存,设置符号栈S,步骤如下: |
5.3 算符优先分析法
若有
算符文法(OG文法): 有文法G,如果G中没有形如
- 在算符文法中任何句型都不包含两个相邻的非终结符
- 如果Ab(或bA)出现在算符文法的句型
中,其中 则 中任何含此b的短语必含有A.

算符优先文法(OPG文法): 一个不含 $\epsilon $ 产生式的算符文法 G ,任一终结符对(a,b)之间至多有
优先关系是有序的,允许
算符优先关系表的构造:
表示 V^*^ 中的符号串;
这两种集合求时,类似于递归式的求法,可不断深入找符合要求。
FIRSTVT集构造有以下两条规则:
(1) 若有产生式
则 (2) 若
且有产生式 则有
:memo: 

表达式文法的算符优先关系表
|
|
|
|
|
|
|
|
|
|---|---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
算符优先分析算法(算符优先分析的归约过程与规范归约不同)【略】
最左素短语 : (解决在算符优先分析过程中如何寻找句柄)
有文法G[S] ,其句型的素短语是一个短语,它至少包含一个终结符,并除自身外不包含其它素短语,最左边的素短语称最左素短语。
优先函数【略】
算符优先分析法的局限性;(仅适用于表达式的语法分析)
由于其去掉了单非终结符之间的归约,所以有可能错误的句子得到“正确的”归约。
第六章 LR分析
在第5章中已经讨论过,自底向上分析方法是一种移进-归约过程,当分析的栈顶符号串形成句柄或可归约串时就采取归约动作。
若是限定采用规范规约,那么自底向上分析法的关键问题是在分析过程中如何确定句柄。LR分析法正是给出一种能根据当前分析栈中的符号串(通常以状态表示)和向右顺序查看输入串的k(k≥0)个符号就可唯一地确定分析器的动作是移进还是归约和用哪个产生式归约,因而也就能唯一地确定句柄。
LR分析法的归约过程是规范推导的逆过程,所以LR分析过程是一种规范归约过程。
LR(k)分析方法比起自顶向下的LL(k)分析方法和自底向上的优先分析方法对文法的限制要少得多,也就是说,对于大多数用无二义性上下文无关文法描述的语言都可以用相应的LR分析器进行识别,而且这种方法还具有分析速度快,能准确、即时地指出出错位置的特点。
它的主要缺点是对于一个实用语言文法的分析器的构造工作量相当大,k愈大,构造愈复杂,实现比较困难。
本章主要介绍LR分析的基本思想和当k≤1时LR分析器的基本构造原理和方法。其中LR(0)分析器在分析过程中不需向右查看输入符号,因而它对文法的限制较大,对绝大多数高级语言的语法分析器是不能适用的;然而,它是构造其他LR类分析器的基础。当k=1时,已能满足当前绝大多数高级语言编译程序的需要。本章着重介绍LR(0)、SLR(1)、LALR(1)和LR(1)这4种分析器的构造方法,其中SLR(1)和LALR(1)分别是LR(0)和LR(1)的一种改进。
6.1 LR分析概述
一个LR分析器由3个部分组成:
(1) 总控程序,也可以称为驱动程序。对所有的LR分析器,总控程序都是相同的。
(2) 分析表或分析函数。分析表又可分为动作(ACTION)表和状态转换(GOTO)表两个部分,它们都可用二维数组表示。
(3)分析栈,包括文法符号栈和相应的状态栈。它们均是先进后出栈。
分析器的动作由栈顶状态和当前输入符号来决定(LR(0)分析器不需向前查看输入符号)。
规定了栈顶状态为 时遇到输入符号 应执行的4种动作:
(1)移进
当成立,则把 移入到状态栈,把a移入到文法符号栈。其中 表示状态号。
(2)归约
当文法中有的产生式,进行归约,而 的长度为 (即 )S_i,则从状态栈和文法符号栈中自栈顶向下去掉 个符号;并把 移入文法符号栈内,再 把满足 的状态移进状态栈,其中 为修改指针后的栈顶状态。
(3)接受acc
归约到文法符号栈中只剩文法的开始符号S,并且输入符号串已结束,即当前输入符是#,则为分析成功。
(4)报错。
当遇到状态栈顶为某一状态下出现不该遇到的文法符号时则报错,说明输入串不是该文法能接受的句子。LR分析器的关键部分是分析表的构造
6.2 LR(0)分析
:memo: 文法
对输入串
用自底向上归约进行分析时,(有时归约所用的产生式不同,但栈顶符号相同,原因在于已分析过的部分,即栈中前缀不同) ,在LR分析中视为状态栈的栈顶状态不同。
文法G[S]的LR(0)分析表
— — — ACTION — — —| GOTO — —
对输入串 abbcde# 的分析过程
步骤 状态栈 符号栈 输入串 ACTION GOTO (1) (2) (3) 3 (4) (5) 3 (6) (7) (8) 7 (9) (10) 1 (11)
1 | ## 可归前缀和子前缀 【略】 |
6.2.4 LR(0)项目集规范族的构造
1.LR(0)项目
在文法G中每个产生式的右部适当位置添加一个圆点.构成项目:memo: 产生式
有6个项目。
$A\to \epsilon $ 仅有一个项目
2.构造识别活前缀的NFA【略】
(1)移进项目,
,其中 ,即圆点后面为终结符的项目为移进项目,对应移进状态。分析时把 移进符号栈。 (2)待约项目,形如
其中 即圆点后面为非终结符的项目称待约项目,等待分析B所能推出的串归约成B,才能继续分析A的右部。 (3)归约项目,形如
其中 即圆点在最右端的项目,称归约项目,表明一个产生式的右部已分析完,句柄已形成,可以归约。 (4)接受项目,形如
其中 为拓广文法,S’为左部的产生式只有一个,因而它是归约项目的特殊情况,对应状态称为接受状态。规定 为初态。实际上接受项目中的α为文法的开始符号。 ==3.LR(0)项目集规范族的构造==
构成识别一个文法活前缀的DFA项目集(状态)的全体称为这个文法的LR(0)项目集规范族;然而,构造识别活前缀的DFA若按上面的2.的方法,工作量较大。 **可以用闭包函数
求一个状态的项目集,转向函数 $GO(I,x) $构造规范族 ** 定义和构造
的闭包 的步骤如下: (1)
的项目均在 中。 (2) 若
属于 则每一形如 的项目也属于 。 (3) 重复(2)直到不出现新的项目为止,即
不再扩大。 由此,可以很容易构造出初态的闭包,即
再按上述3步求其闭包。 使用闭包函数
和转向函数 构造文法 的 项目集规范族,步骤如下: (1) 置项目
为初态集的核,然后对核求闭包, ,得到初态的项目集。 (2) 对初态集或其他所构造的项目集,应用转换函数
求出新状态 的项目集。 (3) 重复(2)直到不出现新的项目集为止。
总结
以上介绍了构造识别文法活前缀DFA的三种方法:
第1种方法,是根据形式定义求出活前缀的正规表达式,然后由此正规表达式构造NFA,再确定化为DFA。
第2种方法,是求出文法的所有项目,按一定规则构造识别活前缀的NFA,再确定化为DFA。
第3种方法,是把拓广文法的第一个项目
作为初态集的核,通过求核的闭包和转换函数,求出 项目集规范族,再由转换函数建立状态之间的连接关系,得到识别活前缀的 DFA。
一个项目集可能包含 移进项目、归约项目、待约项目、接受项目 四种不同的项目。但不能有下列情况存在:
(1) 移进-归约同时存在,
[移进-归约冲突] (2) 归约-归约同时存在,
【归约-归约冲突】 一个文法的
项目集规范族不存在移进-归约冲突或归约-归约冲突,此文法为**LR(0)文法 **。
==4.LR(0)分析表的构造==
构造算法如下:【文字描述较难懂,多看例子:memo:】
假设已构造出
项目集规范族为: ,其中 为项目集的名字, 为状态名,令包含 项目的集合 的下标 为分析器的初始态,那么分析表的 表 和 表 的构造步骤如下: (1)若项目
属于 且转换函数 ,当 为终结符时则置 为 ,其动作含义为将终结符 移进符号栈,状态 进入状态栈(相当于在状态 时遇 转向状态 )。 (2)若项目
属于 ,则对任何终结符 和 号置 和 为 , 为在文法 中某产生式 的序号。 动作的含义是把当前文法符号栈顶的符号串 归约为 ,并将栈指针从栈顶向下移动 的长度,符号栈中弹出 个符号,非终结符 变为当前面临的符号。 (3)若
,则置 为" ",其中 为非终结符,表示当前状态为 " "时,遇文法符号 时状态应转向 ,因此 移入文法符号栈, 移入状态栈。 (4)若项目
属于 ,则置 为 ,表示接受 (5)凡不能用上述方法填入的分析表的元素,均应填上报错标志。为了表的清晰,表中用空白表示错误标志。
5.LR(0)分析器的工作过程
(1)若
, 为终结符,则把 移入符号栈, 移入状态栈。 (2)若
, 为终结符或 号,则用第 个产生式归约,并将两个栈的指针减去 ,其中 为第 个产生式右部的符号串长度,这时当前符号为第 个产生式左部的非终结符,假设为 ,归约后栈顶状态设为 ,则再进行 。 (3)若
, 应为 号,则为接受,表示分析成功。 (4)若
, 为非终结符,表明前一动作是用关于 的产生式归约的,当前的非终结符 应移入符号栈, 移入状态栈。对于终结符的 已和 重合。 (5)若
为空白,则出错处理。
:memo:
以文法
该文法的项目有:
---
title: 识别活前缀的DFA
---
flowchart LR
E1[I0: S' → .E
E → .aA
E → .bB]
E2[I1: S' → E.]
E3[I2: E → a.A
A → .cA
A → .d]
E4[I3: E → b.B
B → .cB
B → .d]
E5[I4: A → c.A
A → .cA
A → .d]
E6[I5: B → c.B
B → .cB
B → .d]
E7[I6: E → aA.]
E8[I7: E → bB.]
E9[I8: A → cA.]
E10[I9: B → cB.]
E11[I10: A → d.]
E12[I11: B → d.]
E1 -->|E| E2
E1 -->|a| E3
E1 -->|b| E4
E3 -->|c|E5
E3 -->|d|E11
E3 -->|A|E7
E4 -->|B|E8
E4 -->|c|E6
E4 -->|d|E12
E5 -->|c|E5
E5 -->|A|E9
E5 -->|d|E11
E6 --> |c|E6
E6 --> |B|E10
E6 --> |d|E12
构造 LR(0) 分析表
状态 — — ACTION — —| |— GOTO — 状态 11
对输入串 bccd# 的LR(0)分析过程
| 步骤 | 状态栈 | 符号栈 | 输入串 | ACTION | GOTO |
|---|---|---|---|---|---|
| (1) |
|
|
|
|
|
| (2) |
|
|
|
|
|
| (3) |
|
|
|
|
|
| (4) |
|
|
|
S~11~ | |
| (5) |
|
|
|
|
|
| (6) | $03559 $ |
|
|
|
|
| (7) | $0359 $ |
|
|
|
|
| (8) |
|
|
|
|
|
| (9) | $01 $ |
|
|
|
6.3 SLR(1)分析
简单的LR(1)分析法 【略】
基于允许LR(0)规范族中有冲突的项目集(状态),向前看一个符号来进行处理,以解决冲突—需要用到
集
6.4 LR(1)分析
解决SLR(1)方法在某些情况下存在的无效归约问题,【略】
6.5 LALR(1)分析
可以解决SLR(1)方法解决不了的问题【略】
可能导致存储容量急剧增加,应用受到限制
6.6 二义性文法在LR分析中的应用【略】
我们已经知道任何一个二义性文法绝不是LR类文法,也不是一个算符优先文法或LL(k)文法,任何一个二义性文法不存在与其相应的确定的语法分析器,但是对某些二义性文法,可以人为地给出优先性和结合性的规定,从而可以构造出比相应非二义性文法更优越的LR分析器。
例如,算术表达式的二义性文法为
相应的非二义性文法为:
第七章 语法制导的语义计算【略】
第八章 静态语义分析和中间代码生成
8.1 符号表
符号表是编译程序用到的最重要的数据结构之一,几乎在编译的每个阶段每一遍都要涉及符号表。符号表自创建后便开始被用于收集符号(标识符)的属性信息,不同阶段会有不同的信息。
符号表是标识符的动态语义词典,属于编译中语义分析的知识库。它的核心目的是组织标 识符的查询。其中,符号表面向的对象是标识符,变量、函数名都是标识符。
1 | 标识符的四种语义信息 |
符号表的结构设计
有下列过程函数: :memo:
1 | FUNCTIONexp (x:REAL;VAR y : INTEGER):REAL; |


1 | 由于标识符的种类不同,导致语义属性也不尽相同。上面提供一个符号表的体系结构,观察符号表是怎样组织的: |
下面分别具体介绍符号表各个部分的内容。
-
符号表总表(SYNBL)
结构包括四项内容:
| NAME | TYPE | CAT | ADDR |
|---|
• NAME(名字):标识符源码(或内部码)。
• TYPE(类型):指针,指向类型表相应项。
• CAT(种类):种类编码。
f (函数),c(常量),t(类型),d(域名),v(常规变量),vn(换名形参,即地址传递,只需拷 贝存储地址),vf(赋值形参,即值传递,需要将实参拷贝一份到形参存储位置)。
• ADDR(地址):指针,根据标识符的种类不同,分别指向函数表PFINFL,常数表CONSL, 长度表LENL,活动记录VALL …
- 类型表(TYPEL)
类型表结构包括两项内容:
| TVAL | TPOINT |
|---|
1 | • TVAL(类码):表示类型的编码。 |
- 数组表(AINFL)
数组表结构包括四项内容:
| LOW | UP | CTP | CLEN |
|---|
1 | • LOW(数组的下界):C语言自动设置为0。 |
- 结构表(RINFL)
一个结构体会包括若干项,称之为域,每个域占表中的一个记录,指示结构体的每一项都 是什么类型。结构表结构包括三项内容:
| ID | OFF | TP |
|---|
1 | • ID (结构的域名):每个域的名字。 |
- 函数表(PFINFL)–过程或函数语义信息
| LEVEL | OFF | FN | ENTRY | PARAM | … |
|---|
1 | • LEVEL(层次号):该过函静态层次嵌套号,用来表示函数的位置(并非递归的层次)。 |
- 其他表(…)

1 | **常量表(CONSL)**:存放相应常量的初值,仅有一个域。对于字符常量、字符串常量、实型常量、整型常量等这些不同类型的常量,分别列表。 |
:memo:

1 | # 分析过程 |
:memo:
:memo:



