编译原理复习
第1章 介绍
1.1 编译器
- 一个编译器就是一个程序,它可以阅读以某一种语言(源语言)编写的程序,并把该程序翻译成为一个等价的,以另一种语言(目标语言)编写的程序
-
如果目标程序是一个可执行的机器语言程序,那么它就可以被用户调用,处理输入并产生输出
解释器是另一种常见的语言处理器
- 即时编译器
-
其他应用
词法分析器中的技术可运用于文本编辑器,信息检索系统,模式识别
语法解析器中的技术可以运用于查询处理系统(SQL)
复杂前端程序
自然语言处理
1.2 语言处理系统
预处理器
编译器
汇编器
链接器
加载器
Editors
Debuggers
1.3 编译器的各个阶段
编译器:
分析(前端):源程序→中间表示
综合(后端):中间表示→目标程序
-
词法分析:读入源程序的字符流,返回tokens
token是源程序中具有某些意义的字符模式(如标识符,运算符,关键字,数字等)
将标识符信息放入符号表
正则表达式用于描述tokens
有限状态自动机可用于实现词法分析器
-
语法分析:创建给定程序的句法结构(语法树)
syntax analyzer,parser
语法树中,所有终结符都是叶子节点,在CFG所有内部节点都是非终结符
-
语义分析:检查源程序是否存在语义错误,并收集代码生成的类型信息
类型检查
-
语义规则
语法制导翻译
属性文法
中间代码生成器
代码优化器
代码生成器:以编译器前端生成的中间代码表示(IR)和相关的符号表信息作为输入,输出语义等价的目标程序
符号表管理
错误检测
编译器中的主要数据结构:
tokens
syntax tree
symbol table:一种供编译器用于保存有关源程序构造的各种信息的数据结构
literal table
intermediate code:保存为文本字符串数组、临时文本文件或结构链表
temporary files
中缀转为后缀
第3章 词法分析
3.1 词法分析器的作用
词法分析器的主要任务是读入源程序的输入字符,将他们组成词素,生成并输出一个词法单元序列
Lexical analyzer = scanning + lexical analysis
编译过程的分析部分划分为词法分析和语法分析的原因:
-
简化编译器设计
高内聚低耦合
并行
提高编译器效率
增强可移植性
三个相关术语:
-
token:词法单元
常见字符串的分类
词法单元名+可选的属性值
-
pattern:模式
描述了一个词法单元的词素可能具有的形式
一个词法单元字符串集的规则
正则表达式被广泛用于指定模式
-
lexeme:词素
源程序中的一个字符序列
和某个词法单元的模式匹配,并被词法分析器识别为该词法单元的一个实例
attribute(属性): 如果有多个词素可以和一个模式匹配,那么词法分析器必须向编译器的后续阶段提供有关被匹配词素的附加信息
与token相关的任意值
对于标识符,它的属性值是一个指向符号表中该标识符对应条目的指针
token type和 attribute唯一标识一个词素
3.2 输入缓冲
Character-at-a-time I/O (getc, ungetc)
Block / Buffered I/O
3.3 词法单元的规约
正则表达式是一种用来描述词素模式的重要表示方法
相关术语
字母表(alphabet):一个有限的符合集合
-
串(string):字母表中符号的一个有穷序列
串的长度:|s|
空串:
前缀,后缀,子串,真前缀,真后缀,真子串,子序列
-
language(语言):某个给定字母表上的一个任意可数的串的集合
空集 也是语言
{ }
-
串的运算
- 连接
语言的运算
正则表达式:
正则表达式是从字母表构造符号(字符串)序列的一组规则/技术
L(r):正则表达式生成的语言
正则定义:给正则表达式命名
3.4 词法单元的识别
状态转换图(transition diagrams):用于表示tokens
3.5 词法分析生成工具Lex
Lex,最近实现也称为Flex,支持用正则表达式来描述各个词法单元的模式,由此给出一个词法分析器的规约
3.6 有穷自动机
有穷自动机是识别器,它们只能对每个可能的输入串简单地回答“是”或“否”
DFA:简单,精度低
NFA:复杂,精度高
首先,为tokens定义正则表达式;然后将它们转换为DFA,以获得tokens的词法分析器
算法1:Regular Expression ➔ NFA ➔ DFA
算法2:Regular Expression ➔ DFA
3.6.1 NFA
组成:
表示:
转换图
转换表
举例:
NFA对于正则表达式的问题:
有效的输入可能不被接受
同一输入上的行为可能不同
3.6.2 DFA
3.6.3 从NFA到DFA的转换
子集构造法(subset construction):
将一个NFA N 转换为一个接受同样语言的DFA D
- s代表N的单个状态,T代表N的一个状态集:
-
举例:
- 问题:
- 问题:
* 步骤:
![3-NFAtoDFA2.png](https://upload-images.jianshu.io/upload_images/25989339-8a41a483b02b1cfb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 转换表Dtran
![3-NFAtoDFA-Dran.png](https://upload-images.jianshu.io/upload_images/25989339-714c1c52bd781aff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 结果:
![3-NFAtoDFA-result.png](https://upload-images.jianshu.io/upload_images/25989339-5ac1b5b4d70a5ed4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
3.7 从正则表达式到NFA的转换
Thompson’s Construction
Piecing Together NFAs
直接将正则表达式转到DFAs:
r → (r)# augmented regular expression
followpos(i)
firstpos(n)
lastpos(n)
举例:
最小化DFA:
-
将状态集分成两组:
G1:接受状态集
G2:非接受状态集
-
对每一个新组:
- 将G分成子组,如果对于所有输入符号a,状态S1和S2具有向同一组中的状态的转换,使状态S1和S2处于同一组中
第4章 语法分析
4.1 引论
错误处理:
检测错误
找到错误位置
清楚呈现
恢复以继续查找错误
不能影响正确程序的编译
错误恢复策略:
恐慌模式
短语层次恢复
4.2 上下文无关文法
语法分析创建给定源程序的句法结构,一般是语法分析树
语法分析器(解析器)检查给定的源程序是否满足上下文无关文法所隐含的规则
上下文无关文法:编程语言的固有递归结构由上下文无关的语法定义
终结符(T),非终结符(NT),产生式(PR),开始符号(S)
PR: NT → (T | NT)*
推导:
- 产生式被看作重写规则,每次重写步骤把左边的非终结符号替换为它的某个产生式的体
-
最左推导,最右推导
语法分析树:
推导的图形表示形式
内部节点是非终结符,叶子节点是终结符
二义性:一个文法可以为某个句子生成多颗语法生成树。大部分语法分析器都期望文法是无二义性的
-
由于二义操作符引起的二义语法可以通过优先级和结合性消除
消除二义性:
左递归的消除:
自顶向下语法分析不能处理左递归语法
消除立即左递归:
提取左公因子:
4.3 自顶向下语法分析
语法生成树从上到下构建(从根到叶)
通过产生式替换最左边的非终结符号
-
Top-down parser
递归下降:需要回溯,通用的语法技术,但没有得到广泛应用,效率不高
-
可预测:
不需要回溯
有效的
LL(1)语法
-
a LL(1) parser: an input buffer, a stack, a parsing table, and an output stream
FIRST和FOLLOW:
计算first和follow前要先消除左递归
LL(1)文法:
构造语法分析表算法:
语法分析表没有多重定义的条目的语法被称为LL(1) 语法
注:构建完语法分析表后,要加一句:因为表项没有(/有)冲突 ,所以该文法是(不是)LL(1)文法
不是LL(1)文法:
A left recursive grammar cannot be a LL(1) grammar
A grammar is not left factored, it cannot be a LL(1) grammar
An ambiguous grammar cannot be a LL(1) grammar
预测语法分析中的错误恢复:
恐慌模式
短语层次的恢复
4.4 自底向上语法分析
bottom-up parser从给定的输入创建一个叶子到根的语法生成树
最右推导
-
被称为shift-reduce parsing
将给定输入字符串归约到起始符号
一次归约是一个推导步骤的反向操作
handle(句柄):
字符串的句柄是匹配产生式右侧的子字符串
如果一个文法是无二义性的,那么该文法的每个右句型有且只有一个句柄
通过句柄剪枝可以得到一个反向的最右推导
移入规约语法分析器:
-
四个动作(移入,规约,接受,报错):
移入 / 规约冲突,规约 / 规约冲突
Operator-Precedence Parser:
LR Parsers:
covers wide range of grammers
SLR - simple LR parser
LR - most general LR parser
LALR - intermediate LR parser(look-head LR parser)
增广文法(augmented grammer):G’ is G with a new production rule S’→S where S’ is the new starting。
(当使用rule S’→S进行规约时,输入符号串被接受)
CLOSURE 和 GOTO:
- CLOSURE:
- GOTO:
构造SLR语法分析表:[图片上传失败...(image-5de6b3-1615209458805)]
LR(1)项(item):
A → , a where a is the look-head of the LR(1) item(a is a terminal or end-marker)
构造LR(1)语法分析表:
构造LALR语法分析表:
LALR(向前看-LR)
构造LR(1)项集族C = {}
对于LR(1)项集族中的每个核心,找出所有具有这个核心的项集,并将这些项集替换成它们的并集
我们在创建LALR语法分析器不会引人Shift/Reduce Conflict,可能引入Reduce/Reduce Conflict
语法分析器生成工具Yacc
又一个编译器的编译器。构造一个翻译器
第5章 语法制导翻译
Semantic Analysis:
Semantic Analyzer
Attribute Grammars
Syntax Tree Construction
Top-Down Translators
Recursive Evaluators
Bottom-Up Translators
Type Checking (next chapter)
Syntax-Directed Translation 语法制导翻译
5.1 语法制导定义
语法制导定义(SDD)是一个上下文无关文法和属性及规则的结合。属性和文法符号相关联,而规则和产生式相关联。
综合属性和继承属性:
综合属性:在分析树节点N上的非终结符号A的综合属性是由N上的产生式所关联的语义规则来定义的。这个产生式的头一定是A。节点N上的综合属性只能通过N的子结点或N本身的属性值来定义
继承属性:在分析树节点N上的非终结符号B的继承属性是由N的父结点上的产生式所关联的语义规则来定义的。这个产生式的体中必然包含符号B。节点N上的继承属性只能通过N的父结点、N本身和N的兄弟结点上的属性值来定义
一个只包含综合属性的SDD称为S属性的SDD
一个没有副作用的SDD有时也称为属性文法
一个显示了各个属性的值的语法分析树称为注释语法分析树(annotated parse tree)
注释语法分析树的叶子节点的属性由词法分析器确定
注释语法分析树内部节点的属性由语义规则决定
依赖图可以确定一颗给定的语法分析树中各个属性实例的求值顺序
两类SDD:
-
S属性:SDD的每个属性都是综合属性
可以按照语法分析树节点的任何自底向上顺序来计算它的各个属性值
对语法分析树进行后序遍历并对属性求值会很简单
-
L属性:在一个产生式体所关联的各个属性之间,依赖图的边总是从左到右,不能从右到左。更准确的是,每个属性:
要么是一个综合属性
要么是一个继承属性
第6章 类型检查
第7章 运行时刻环境
为在源程序中命名的对象分配和安排存储位置
确定目标程序访问变量时使用的机制
过程间的连接,参数传递机制,与操作系统、I/O设备及其他程序的接口
活动树:表示整个程序运行期间的所有过程的活动,每个节点代表一个活动
过程调用和返回通常由一个称为控制栈的运行时刻栈进行管理。每个活跃的活动都有一个位于这个控制栈中的活动记录(有时也称为帧)
堆管理
堆是存储空间的一部分,被用来存储那些生命周期不确定,或者将生存到被程序显式删除为止的数据。
存储管理器:分配和回收堆区空间的子系统
-
分配:
-
回收:
存储分配策略:静态存储,堆式存储,栈式存储
函数参数传递
第8章 中间代码生成
三地址代码:指令的一般形式具有三个地址:两个运算分量y和z,一个结果变量x
通过选择不同的运算符,既可以是高层的表示方式,也可以是低层的表示方式
包含标号和跳转指令,用来表示目标语言的控制流
一颗语法树的线性表示
RTL: Run-Time Library,支持程序运行执行的函数库
第9章 代码生成
做题注意事项
NFA,DFA的起始状态箭头,最终状态两个同心圆不要忘记
正则表达式转DFA从1开始计数,直到 # 结尾
总是忘记在新的项集(closure)中,以点(·)后面的非终结符号为开头的产生式也要加到项集中去
堆栈分析,发生冲突时:移入优先,小编号优先