编译原理
2020-06-02 11:34:34 22 举报
AI智能生成
编译原理部分知识框架
作者其他创作
大纲/内容
第一章 编译概述
翻译 与 解释
把源程序翻译成另一种表现形式的翻译器称为编译器(编译程序)
直接执行源程序给出运行结果的翻译器称为解释器(解释程序)
编译的阶段和任务
一、分析阶段:根据源语言的定义,分析源程序的结构
词法分析
扫描,线性分析
可以检测出非法字符错误
依据:构词规则(词法、模式)
语法分析
层次结构的分析
能发现记好流不符合语法规则的错误
依据:语法规则
语义分析
对语句的意义进行检查分析
试图检测出具有正确的语法结构,但对所涉及的操作无意义的结构
依据:语义规则
重要任务:类型检查
二、综合阶段:根据分析结果构造出所要求的目标程序
中间代码生成
可能发现目标程序区超出了允许范围的错
代码优化
目标代码生成
三、符号表的管理
四、错误诊断和处理
相关的其他概念
前端和后端
前端:主要由与源语言有关而与目标机器无关的那些部分组成
后端:由编译程序中与目标机器有关的部分组成
便于移植,便于编译程序的构造
遍
是指对源程序或其中间形式从头到尾扫描一遍,并作相关的加工处理,生产新的中间形式或目标程序
分遍的好处
减少对主存容量的要求
功能独立、单纯、相互联系简单,结构清晰
实现更充分的优化工作,获得高质量目标程序
将前端和后端分开,为移植创造条件
缺点:增加不少重复性的工作
伙伴工具
预处理器
宏处理
文件包含
语言扩充
汇编程序
连接装配程序
第二章 形式语言与自动机基础
略
第三章 词法分析
子主题
第四章 语法分析
语法分析简介
语法错误的处理
错误处理目标
清楚而准确地报告发现的错误
迅速地从错误中恢复过来
不应该明显地影响编译程序对正确程序的处理效率
恢复策略
紧急恢复
短语级恢复
出错产生式
全局纠正
自顶向下分析方法
递归下降分析
试图为输入符号串建立一个最左推导序列的过程
实现
每一个非终结符号对应一个递归过程
每个过程作为一个布尔过程,一旦发现匹配则用该产生式展开分析树,并返回true,否则树不变,返回false
递归调用预测分析
确定的,不带回溯的递归下降分析方法
对文法的要求
1. 不含左递归
2. FIRST(ai) ∩FIRST(aj) = φ (i≠j)
预测分析程序的构造
转换图
改写文法
重写文法
消除左递归
提取左公因子
为每一个非终结符号A构造自动机
转换图的工作过程
化简转换图
用代入的方法进行化简
把E‘ 的转换图代入E 的转换图
程序实现
非递归预测分析
使用一张分析表和一个栈联合控制
预测分析控制程序是核心
分析表是关键
模型及工作过程
预测分析表的构造
改写文法
FIRST集合及其构造
FOLLOW集合及其构造
对开始符号S
置 $ 于FOLLOW(S)
A->αBβ
把FIRST(β)中的所有非空元素加入到FOLLOW(B)中
A->αA ||A->αBβ且β-> 空
FOLLOW(A)中的所有元素加入到FOLLOW(B)
预测分析表的构造
LL(1)文法
文法的预测分析表M不含多重定义的表项
判断是否是LL(1)文法
FIRST(α)∩FIRST(β) = φ
若β推导出空,FIRST(α)∩FOLLOW(A) = φ
错误处理示例
带有同步化信息的分析表
对于A∈VN, b∈FOLLOW(A), 若M[A,b]为空,则加入“synch”
分析表使用
若M[A,b]为空,则跳过a
若M[A,b]为synch,则弹出A
自底向上分析方法
关键:找到“可归约串”
常用方法
优先分析方法
简单优先分析法
算符优先分析法
LR分析方法
移进-归约
分析过程中的动作冲突
移进-归约冲突
归约-归约冲突
规范归约
右句型序列αn,αn-1,...α1,α0是 α的一个规范归约
LR分析方法
LR分析程序的模型及工作过程
基本思想
历史信息
记住已经移进归约出的整个符号串
预测信息
根据所用的产生式预测未来可能遇到的输入符号
模型
分析表
goto[Sm,X]:状态Sm经X转移的后继状态
action[Sm,ai]:状态Sm面临输入符号ai时应采取的分析动作
移进
X是栈顶,a是ip所指向的符号
X是终结符号 or $
X==a
弹出X;ip前移
else error
X是非终结符
M[X,a] = X->Y1Y2....Yk
弹出X,反序压栈,Y1在栈顶
else error
把a和S‘分别压入符号栈和状态栈
推进ip,使他指向下一个输入符号
归约
从栈顶弹出|β|个符号
把A和goto[S',A]分别压入符号栈和状态栈
输出产生式A->β
接受
出错
SLR(1)分析表的构造
中心思想
为给定的文法构造一个识别它所有活前缀的DFA
根据该DFA构造文法的分析表
相关概念定义
拓广文法
LR(0)有效项目
LR(0)有效项目集和LR(0)项目集规范族
闭包(closure)
转移函数go
冲突解决
移进-归约冲突
看归约项目左部符号的FOLLOW集是否包含移进项目的移进项
归约-归约冲突
看两者的FOLLOW集是否相交
构造分析表
A->α·aβ∈Ii,且go(Ii,a) = Ij
action[i,a] = Sj
A->α·∈Ii
对所有a∈FOLLOW(A),置action[i,a] = R A->α
S'->S· ∈Ii
action[i,$] = acc
go(Ii,A) = Ij
goto[i,A] = j
others are errors
如果构造出来的分析表不含有冲突,则该分析表称为SLR(1)分析表
每一个SLR(1)文法都是无二义的文法
反之不一定
如果执行算法过程中始终没有向前看任何输入符号,则改造的SLR分析表称为LR(0)分析表
要么所有元素都是移进-待约项目
要么只含有唯一的归约项目
LR(1)分析表的构造
LR(k)项目
LR(1)有效项目
算法
1.构造拓广文法的LR(1)项目集规范族C={I0,I1,...In}
对于状态Ii
若[A->α · aβ,b]∈Ii, go(Ii,a) = Ij
action[i,a] = Sj
[A ->α· ,a]∈Ii 且 A ≠ S'
action[i,a] = Rj
[S'->S· ,$]∈Ii
action[i,$] = ACC
对于非终结符A
go(Ii,A) = Ij
goto[i,A] = j
其他
error
LALR(1)分析表的构造
同心集
两个LR(1)项目集去掉搜索符号后相同
项目集的核
除初态项目集外,由该项目集中那些原点不在最左边的项目组成,LR(1)初态项目集中的核有且只有项目【S'->·S,$】
构造基本思想
合并同心集,减少状态数
用核代替项目集,减少存储空间
算法
构造拓广文法的项目集规范族
如果不存在冲突
合并同心集得到新的项目集规范族
如果没有归约归约冲突
是LALR(1)
构造action,goto表
否则不是LALR(1)
有冲突
不是LR(1)
文法分类
LR分析方法对二义文法的应用
利用优先级和结合规则
谁优先先处理谁
什么结合规则就先处理什么部分
最近最后匹配原则
LR分析的错误处理与恢复
软件工具YACC
第五章 语法制导翻译技术
语法制导定义
根据翻译目标来确定每个产生式的语义
更具产生式的含义,分析每个符号的语义
把语义以属性的形式附加到相应的文法符号中
根据产生式的语义给出符号属性的求职规则,从而形成语法制导定义
5.1 语法制导定义及翻译方案
5.1.1 语法制导定义
对上下文无关文法的推广
属性集
综合属性
从其子结点的属性值计算出来的
继承属性
从其兄弟结点或父结点的属性值计算出来的
表示上下文之间的依赖关系
5.1.2 依赖图
子主题
5.1.3 计算次序
5.1.4 S属性定义及L属性定义
S属性
L属性
属性计算顺序
深度优先遍历
进入结点前计算它的继承属性
从结点返回时,计算它的综合属性
5.1.5 翻译方案
属性与文法符号对应
语义动作在花括号中,并插入到产生式右部某个合适的位置
翻译方案的设计
对于S属性定义
为每个语义规则建立一个包含赋值的动作
把这个动作放在产生式的右边末尾
对于L属性定义
右部文法符号的继承属性必须在这个符号以前被计算出来
一个动作不能应用这个动作右边的文法符号的综合属性
左边非终结符的综合属性只有在他所引用的所有属性都计算出来之后才能计算
子主题
5.2 S属性定义的自底向上翻译
5.2.1 为表达式构造语法树的语法制导定义
语法树
内部结点表示运算符号
子节点表示运算分量
构造函数
makenode(op,left,right)
makeleaf(id,entry)
makeleaf(num,val)
有向非循环图dag
5.2.2 S属性定义的自底向上翻译
修改分析栈
目的:使之能够保存综合属性
做法:在分析栈中增加一个域,存放综合属性值
修改分析程序
对于终结符
综合属性有词法分析产生
对每个产生式A->XYZ
属性值的计算与归约联系
归约前,执行与产生式相关的代码段
归约:右部符号的相应状态及其属性出栈,左部符号的相应状态及其属性入栈
5.3 L属性定义的自顶向下翻译
预测分析方法对文法的要求
不含左递归
A->α|β FIRST(α)∩FIRST(β) = φ
5.3.1 消除翻译方案中 的左递归
5.3.1 消除翻译方案中 的左递归
M -> 空 { M.s = M.i}
E-> T{ M.i = T.val}M{E.val = M.s}
M-> +T{M1.i = M.i + T.val } M1{ M.s = M1.s}
5.3.2 预测翻译程序的设计
算法5.2 构造语法制导的预测翻译程序
(1)为每个非终结符A建立一个函数
继承属性为形参,综合属性为返回值
(2)A的函数的代码由多个分支组成
(3)与每个产生式相关的程序代码
对带有综合属性x的记号X
对非终结符号B
对每一个语义动作
5.4 L属性定义的自底向上翻译
5.4.1 移走翻译方案中嵌入的语义规则
自底向上的处理继承属性
等价交换:使所有嵌入的动作都出现在产生式的右端末尾
在基础文法中引入新的产生式,形如 M->空
M:标记非终结符号,代替嵌入在产生式中的动作
把被M 代替的动作放在产生式 M->空 的末尾
5.4.2 直接使用分析栈中的继承属性
5.4.3 变换继承属性的计算规则
要想从栈中取得继承属性,当且仅当文法允许属性值再栈中存放的位置可以预测
如果不确定,则引入标记非终结符,等价变换
用标记非终结符号模拟非复制规则的语义规则
算法5.3 L属性定义的自底向上分析和翻译
对每个产生式A->X1X2X3...Xn
引入n个新的标记非终结符M1,M2,...,Mn
用A->M1X1M2X1....MnXn代替原式
属性Xj.i 总是在Mj处计算,且发送在开始做归约的动作之前
在自底向上分析过程中,各个属性都可以被计算出来
① 用Mj -> 空 进行归约
② 用A->M1X1M2X2...MnXn归约
5.4.4 改写语法制导定义为S属性定义
5.5 通用的语法制导翻译方法
为每一个非终结符号A 建立一个函数,该函数keyi是递归的
1.设计函数头
分析树结点和继承属性作为形参,综合属性作为返回值
为每一个属性都声明一个相应的局部变量
2. 函数体结构
如果有多个候选式,则首先根据当前节点处使用产生式来确定应执行的分支
3. 设计分支代码
根据相关语义规则来设计向相应的分支程序代码
根据属性间的依赖关系确定访问子节点的顺序
子节点可以是内部结点或叶子结点
叶子结点,对应记号X有综合属性x,则将它的值保存在属性X.x声明的变量中
内部结点,对应于非终结符号B
如果有继承属性B.i,则先根据语义规则生成计算B.i值的代码
产生一个函数调用语句c = B(n,b1,b2....),其中n为B对应的分析树结点,c对应于B 的综合属性变量
第六章 语义分析
6.1 语义分析概述
问题
变量的作用域问题
同一作用域内同名变量的重复声明问题
表达式、赋值语句中的类型一致性问题
解决方法:
利用语法制导翻译技术实现语义分析
设计专门的语义动作补充上下文无关文法的分析程序
6.1.1 语义分析程序的任务
将变量的定义与引用联系起来,对源程序的含义进行检查
1. 收集并保存上下文有关的信息
2. 类型检查
动态检查
目标程序运行时检查
静态检查
读入源程序但不执行源程序的情况下进行的检查
检验结构的类型是否与其上下文所期望的一致,检查操作的合法性和数据类型的相容性
唯一性检查
控制流检查
6.1.2 语义分析程序的位置
位置
以语法树为基础,检查语法成分在语义上是否满足上下文对它的要求
输出的是带有语义信息的语法树
有助于生成正确的目的代码
重载运算符
类型强制
6.1.3 错误处理
6.2 符号表
作用
检查语义(即上下文有关)的正确性
辅助正确地生成代码
6.2.1 符号表的建立和访问时机
1. 多遍编译程序
词法分析阶段建立
在符号表中的位置作为记号的属性
适用于非块结构语言的编译
2. 合并遍的编译程序
语法分析程序是核心模块
当声明语句被识别,,标识符和它的属性一起写入符号表中
6.2.2 符号表内容
名字
必须记录的属性
必须常驻内存
问题:标识符长度可变
长度有限制
存取速度快
空间利用率低
长度没有限制
串描述符:包含位置指针和长度两个子域
存取速度慢
节省存储空间
类型
有数据类型是必须存
无类型的语言可以删除
用于
类型检查
生成代码
空间分配
存储地址
对于静态存储分配的语言(如FORTRAN)
目标地址按顺序连续分配
对于块结构的语言(如Pascal,C)
采用二元地址<blkn, offset>
blkn:块的嵌套深度,用于确定分配给声明变量的块的数据区的基址
offset:变量的目的地址偏移量,指示该变量的存储单元在数据区中相对于基址的位置
数组维数/参数个数
数组引用时,维数要一致
过程调用时,实参和形参一致
交叉引用表
链域/指针
为了便于产生按字母顺序排列的交叉引用表
6.2.3 符号表操作
插入、检索
显式声明的强类型语言
声明语言
检索
插入
引用名字
检索
隐式声明的语言
每次出现都按首次出现处理
检索
已经声明,则类型检查
首次出现,插入操作
定位与重定位
对于块结构的语言
块开始,定位
块结束,重定位
符号表的逻辑结构
6.2.4 符号表组织
1. 非块结构语言
不含子模块
所有变量属于整个程序
符号表组织
无序线性表
按声明/出现顺序
插入前都要检索
有序线性表
按字母顺序排序
线性查找
折半查找
散列/哈希表
查找时间与表中记录数无关
散列/哈希函数H
除法
平方取中法
折叠法
长度相关法
解决冲突的方法
开放地址法
分离链表法
2. 块结构语言
栈式符号表
栈式散列符号表
6.3 类型检查
不同的观点
强调最大程度的限制
强类型语言
强调数据类型的灵活性
隐式类型,无须类型检查
检查
静态类型检查
编译程序完成的检查
动态类型检查
目标程序运行时完成的检查
健全的类型体制
不需要动态检查类型错误
强类型语言
能够保证程序不会有运行时的类型错误
显式类型和静态类型检查
可尽早检出程序错误,减少可能出现的执行错误
6.3.1 类型表达式
考虑因素
语法结构
数据类型
基本类型
类型构造器
类型体制
把类型指派给语法结构的规则
由类型检查程序实现
暗示的概念
每个表达式有一个类型
类型有结构
递归定义
1. 基本类型是类型表达式
int char, 错误类型type_error,回避类型 void
2. 类型名是类型表达式, 因为类型表达式可以命名
3. 类型构造器作用域类型表达式的结构仍是类型表达式
①数组
array(I,T)
元素类型为T和下标为I的数组
②笛卡尔积
T1×T2
③记录
record
记录各域类型的笛卡尔积上
域类型:域名+域
record((i×integer)×(c×char))
④指针
pointer(T)
⑤函数
D->R
定义域类型D到值域类型R的映射
4. 类型表达式中可以包含变量(类型变量),变量的值也是类型表达式
6.3.2 类型等价
关键
精确地定义什么情况下两个类型表达式等价
1. 结构等价
相同的结构
要么是同样的基本类型
要么是同样的构造器
类型表达式结构等价当且仅当完全相同
类型表达式的内部表示
为了提高测试效率,可以对类型表示式进行编码
做法
对基本类型规定确定位数、确定位置的二进制编码
对类型构造器规定确定位数的二进制编码
结构等价测试
比较二进制序列
不同,不等价
相同,用算法6.1作进一步测试
编码方式实例
2.名字等价
名字等价把每个类型名看成是一个可区别的类型
两个类型表达式名字等价,当且仅当它们完全相同
类型表达式的图形表示:类型图
内部结点
叶子结点
6.4 一个简单的类型检查程序
6.4.1 语言说明
6.4.2 符号表的建立
编译程序的任务
分离出每一个被声明的实体
尽可能多的将该实体的信息填入符号表
声明语句的形式
类型关键字的位置
前
后
标识符表
单一实体
多个同类型的实体
不同种类的实体
1. 过程中声明语句的处理
考虑最内层过程
引入
全局变量offset,记录相对地址
T.width:记录实体的域宽
T.type:记录实体的类型
2. 过程定义的处理
数据结构及过程
maketable(previous)
enter(table, name , type, offset)
addtheader(table, num, pwth, type, width)
addwidth(table, width)
enterproc( table, name, newtable)
3. 记录声明的处理
6.4.3 表达式的类型检查
E.type
无错:integer/real/boolean
有错:type_error
6.4.4 语句的类型检查
S.type
无错:void
有错:type_error
6.4.5 类型转换
强制转换(隐式))
信息不丢失的情况下
显式转换
6.5 类型检查有关的其他主题
6.5.1 函数和运算符的重载
6.5.2 多态函数
第七章 运行环境
7.1程序运行时的存储组织
过程与活动的概念
进程与程序
活动与过程
7.1.1 程序进行空间的划分
代码区
静态数据区
控制栈
堆
7.1.2 活动记录与控制栈
记录内容
返回值
实参区域
控制栈
访问链
机器状态链
局部数据区
临时数据区
根据确定每个域所需空间大小的时间早晚安排其座位
早:中间
晚:两头
用于通信:前
自己用的:后
7.1.3 名字的作用域及名字绑定
7.2 存储分配策略
7.2.1 静态数据存储
7.2.1 栈式数据存储
7.2.3 堆式数据存储
7.3 非局部名字的访问
7.3.1 程序块
7.3.2 静态作用域规则下非局部名字的访问
1. 非嵌套
静态变量
2. 嵌套
过程及名字的嵌套关系
最近嵌套规则
访问链
被调用过程活动记录的访问链指向其直接外层过程的最新活动的活动记录
访问链的使用
q嵌套在p中
q不嵌套在p中
display表
为了提高访问非局部名字的速度
7.3.3 动态作用域规则下非局部名字的访问
子主题
7.4 参数传递机制
7.4.1 传值调用
C, JAVA
7.4.2 引用调用
FORTRAN, Pascal: var, C++: &
第八章 中间代码生成
第九章 目标代码生成
基本块的划分方法
确定入口语句
三地址代码的第一天语句
goto语句转移到的目标语句
紧跟在goto语句后面的语句
确定基本块
从一个入口语句(含该语句)到下一个入口语句(不含)之间的语句序列
从一个入口语句(含该语句)到停止语句(含该语句)之间的语句序列
第十章 代码优化
基本块优化
常数合并及常数传播
删除公共表达式
复制传播
删除死代码
删除死块
削弱计算强度
改变计算次序
dag构造优化限制
基本块中设计数组元素赋值或引用的语句的相对顺序不能改变
所有其他语句相对于过程调用语句或指针赋值语句的顺序不能改变
循环优化
循环展开
循环次数、初值、终值、步长可以在编译时确定
用空间换时间
代码外提/频度削减
将循环无关代码提到循环结构的外面
削弱计算强度
乘法变加法
删除归纳变量
基本归纳变量
0 条评论
下一页