编译原理复习.docx
- 文档编号:3757493
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:17
- 大小:315.17KB
编译原理复习.docx
《编译原理复习.docx》由会员分享,可在线阅读,更多相关《编译原理复习.docx(17页珍藏版)》请在冰豆网上搜索。
编译原理复习
《编译原理》第一章:
绪论
1.翻译器:
把一种语言变换到另外一种语言的软件。
这两种语言分别称为源语言和目标语言。
2.编译器:
一种翻译器,它的目标语言比源语言低级。
3.典型的编译器可以划分成6个逻辑阶段:
词法分析,语法分析,语义分析,中间代码生成,代码优化,代码生成。
4.按照对目标机器的依赖性,把编译过程分成前端(依赖于源语言,独立于目标机器)和后端(依赖于目标机器,独立于源语言,只与中间语言有关(从中间代码生成目标代码))。
5.前端包括:
词法分析,语法分析,语义分析,中间代码生成。
6.后端包括:
代码优化,代码生成。
7.解释器:
不同于编译器的另一类语言处理器,直接执行源程序所指定的运算。
解释器的执行效率比编译器低,因为解释器无法解开循环。
8.遍:
编译的几个阶段常用一遍(pass)扫描实现,一遍扫描包括读一个输入文件和写一个输出文件。
《编译原理》第二章:
词法分析
一些概念:
1.词法单元:
又称单词,是编程语言中合法的字符串。
2.词法记号:
满足某种规则的词法单元,采用同一种记法——词法记号。
该规则称为模式。
3.模式:
描述词法单元与词法记号对应关系的规则。
是描述源程序中某个记号的词法单元集合的规则。
4.字母表:
符号的有限集合,例:
={0,1}
串:
符号的有穷序列,例:
0110,
语言:
字母表上的一个串集{,0,00,000,…},{},
正规式(运算符的优先级:
*>连接运算>|)
NFA是这样一个数学模型,包括
1)状态集合S
2)输入字母表
3)转换函数move:
S({})P(S)
4)唯一的初态sS
5)终态集合FS
DFA是这样一个数学模型,包括
1)状态集合S
2)输入字母表
3)转换函数move:
SS
4)唯一的初态sS
5)终态集合FS
第三章语法分析
3.1上下文无关文法
上下文无关文法是四元组(VT,VN,S,P)
1)VT:
终结符集合(非空有限集合,记号名是其同义词)
2)VN:
非终结符集合(非空有限集合)
3)S:
开始符号
4)P:
产生式集合,产生式形式:
A
上下文无关文法没有强制的顺序关系。
这点和正规式不同。
推导:
把产生式看成重写规则,把符号串中的非终结符用其产生式右部的串来代替。
最左推导
ElmElm(E)lm(E+E)lm(id+E)lm(id+id)
最右推导(规范推导)
ErmErm(E)rm(E+E)
rm(E+id)rm(id+id)
文法的二义性,是指对于符合文法规则的同一个句子,存在两种可能的分析树
根据运算符的优先级引入非终结符可消除二义性。
3.2自上而下分析
消除左递归
对产生式组
A→Aa1|Aa2|…|Aan|b1|b2|…|bm
•用如下产生式组替换
A→b1B|b2B|…|bmB
B→a1B|a2B|…|anB|ε
其中:
B为新变量,相当于A’
提取左因子
将形如
A→β1|β2|…|βm|1|2|…|p
的规则改写为
A→A'|1|2|…|p和A'→β1|β2|…|βm
求FIRST集
FIRST集合计算方法
•若Xa..,则将终结符a加入FIRST(X)中
•若X,则将加入FIRST(X)中
•若XY…,且Y属于非终结符,则将FIRST(Y)\{}加入到FIRST(X)中
•若XY1Y2..YK,且Y1,Y2,..Yi-1(2≤i≤k)都是非终结符,且Y1,Y2,..Yi-1的FIRST集合中均包含,则将FIRST(Yj)的所有非元素加入到FIRST(X)中,(j=1,2,..i).特别地,若Y1~YK均有产生式,则将加到FIRST(X)中。
求follow集
FOLLOW集合计算方法
•对文法开始符号S,置$于FOLLOW(S)中。
•若有AB,则将FIRST()\{}加入FOLLOW(B)中。
(此处可以为空)
•若AB或AB,且*(即属于FIRST()),则将FOLLOW(A)加入FOLLOW(B)中(此处可以为空)。
LL
(1)文法
任何两个产生式A|都满足下列条件:
FIRST()FIRST()=
若*,那么FIRST()FOLLOW(A)=
递归下降的预测分析:
为每一个非终结符写一个分析过程,这些过程可能是递归的
预测分析方法中的预测分析表的构建
(1)对文法的每个产生式A,执行
(2)和(3)。
(2)对FIRST()的每个终结符a,把A加入M[A,a](即加入表中A行a列)。
(3)如果在FIRST()中,对FOLLOW(A)的每个终结符b(包括$),把A加入M[A,b]。
(4)M的其它没有定义的条目都是error。
如果预测分析表中存在多重定义的条目,则该文法存在二义性!
且该文法一定不是LL
(1)文法。
预测分析的错误恢复
同步:
词法分析器当前提供的记号流能构成的语法结构,正是语法分析器所期望的。
把FIRST(A)的终结符加入A的同步记号集合。
如果非终结符可以产生空串,若出错时栈顶是这样的非终结符,则可以使用产生空串的产生式。
如果终结符在栈顶而不能匹配,弹出此终结符。
出错的一般处理方法:
•1查表,当前表项空白,指向记号流的指针后移;
•2查表,当前表项中含有同步记号synch,将当前栈中的非终结符弹出栈;
•3栈顶终结符和当前指针指向的终结符不匹配,将栈顶终结符弹出栈。
•1栈顶不动;2,3指针不动
LL
(1)文法即深度优先的构造一个分析树
3.3自下而上分析
归约和句柄
归约,对应着最右推导的逆过程
句柄:
和某产生式右部匹配的子串
句柄性质:
1.句柄的右边仅含终结符。
2.如果文法二义,那么句柄可能不唯一。
句柄的精确定义
右句型的句柄是一个产生式的右部,并且该句柄在用A替换中的句柄之后,得到的是最右推导中的前一个句型
令=ω,则可以通过产生式A->归约为句型Aω
3.4LR分析器
活前缀:
右句型的前缀,该前缀不超过最右句柄的右端
S*rmAwrmw
1.活前缀已含有句柄的全部符号,表明产生式A→β的右部β已出现在栈顶。
2.活前缀只含句柄的一部分符号如β1表明A→β1β2的右部子串β1已出现在栈顶,当前期待从输入串中看到β2推出的符号。
3.活前缀不含有句柄的任何符号,此时期望产生式A→β的右部所推出的符号串。
从文法构造识别活前缀的DFA
1.拓广文法
2.构造LR(0)项目集规范族
3.从DFA构造SLR分析表
一、状态i从Ii构造,它的action函数如下确定:
1.如果[A·a]在Ii中,并且goto(Ii,a)=Ij,那么置action[i,a]为sj。
2.如果[A·]在Ii中,那么对FOLLOW(A)中的所有a,置action[i,a]为rj,j是产生式A的编号。
3.如果[SS·]在Ii中,那么置action[i,$]为接受acc。
如果出现动作冲突,那么该文法就不是SLR
(1)的。
二、使用下面规则构造状态i的goto函数:
对所有的非终结符A,如果goto(Ii,A)=Ij,那么goto[i,A]=j。
三、不能由上面两步定义的条目都置为error。
四、分析器的初始状态是包含[S·S]的项目集对应的状态。
闭包函数closure(I)
1、I的每个项目均加入closure(I)
2、如果Aα·Bβ在closure(I)中,且Bγ是产生式,那么如果项目B·γ还不在closure(I)中的话,那么把它加入。
有效项目:
如果S*rmAwrm12w,那么项目A1·2对活前缀1是有效的。
一个项目可能对好几个活前缀都是有效的。
一个活前缀可能有多个有效项目
LR
(1)项目:
重新定义项目,让它带上搜索符,成为如下形式[A·,a]
LR
(1)项目[A·,a]对活前缀有效:
如果存在着推导S*rmAwrmw,其中:
–=;
–a是w的第一个符号,或者w是且a是$。
对于项目[A·,a],
–当不为空时,采取移进操作(同LR(0)项目);
–当为空时,是根据搜索符a来决定归约,而不是根据A的后继符FOLLOW(A)来规约(不同于LR(0)项目),通常搜索符a的集合是FOLLOW(A)的子集。
怎么加前向搜索符?
计算闭包CLOSURE(I)
(a)I中的任何项目都属于CLOSURE(I)
(b)若有项目[A·B,a]在CLOSURE(I)中,而B是文法中的产生式,b是FIRST(a)中的元素,则[B·,b]也属于CLOSURE(I)
构造规范的LR分析表
•基于LR
(1)项目来构造识别G活前缀的DFA。
•从Ii构造分析器的状态i,状态i的action函数如下确定
–如果[A·a,b]在Ii中,且goto(Ii,a)=Ij,那么置action[i,a]为sj。
–如果[A·,a]在Ii中,且AS,那么置action[i,a]为rj.
–如果[SS·,$]在Ii中,那么置action[i,$]=acc。
如果用上面规则构造出现了冲突,那么文法就不是LR
(1)的。
•状态i的goto函数如下确定:
如果goto(Ii,A)=Ij,那么goto[i,A]=j
•用上面规则未能定义的所有条目都置为error。
•分析器的初始状态是包含[S·S,$]的项目集对应的状态。
LALR
LALR的做法:
合并识别LR
(1)文法的活前缀的DFA中的同心项目集
•同心集的合并不会引起新的移进归约冲突
•同心集的合并有可能产生新的归约归约冲突
构造LALR
(1)分析表
•构造LR
(1)项目集规范族C={I0,I1,…,In}。
•寻找LR
(1)项目集规范族中同心的项目集,用它们的并集代替它们。
按构造规范的LR
(1)分析表的方式进行构造
LR分析方法的特点
1.栈中的文法符号总是形成一个活前缀。
2.分析表的转移函数本质上是识别活前缀的DFA。
3.栈顶的状态符号包含了确定句柄所需要的一切信息。
4.是已知的最一般的无回溯的移进归约方法。
5.能分析的文法类是预测分析法能分析的文法类的真超集。
6.能及时发现语法错误。
缺点:
手工构造分析表的工作量太大。
LR分析的紧急错误分析
紧急方式错误恢复
•退栈,直至出现状态s,它有预先确定的A的转移
•抛弃若干个输入符号,直至找到a,它是A的合法后继
•再把A和状态goto[s,A]压进栈,恢复正常分析
二义文法决不是LR文法!
第四章语法制导的翻译
本章要点
语义规则和产生式相联系的两种方式
–语法制导定义
对应每一个产生式编制一个语义子程序,当一个产生式获得匹配时,调用相应的语义子程序实现语义检查与翻译。
–语法制导的翻译方案
在产生式的右部的适当位置,插入相应的语义动作,按照分析的进程,执行遇到的语义动作。
属性文法(属性翻译文法)
•在上下文无关文法的基础上,为每个文法符号(终结符或非终结符)配备若干相关的“值”(称为属性)。
•属性代表与文法符号相关信息,如类型、值、代码序列、符号表内容等。
•属性可以进行计算和传递。
语义规则:
对于文法的每个产生式都配备了一组属性的计算规则。
•语法制导的定义:
带属性和规则的上下文无关文法。
•在语法制导定义中的文法被称为基础文法。
◎设计简单问题的语法制导定义和翻译方案(重点,难点)
◎L属性的自上而下计算(边分析边计算)
◎L属性的自下而上计算(边分析边计算)
语法制导的定义
每个文法符号有一组属性
每个文法产生式A->α有一组形为b:
=f(c1,c2,…,ck)的语义规则,其中f是函数,b和c1,c2,…,ck是该产生式文法符号的属性
综合属性:
如果b是A的属性,c1,c2,…,ck是产生式右部文法符号的属性或是A的其他属性(属性值有分析树中他的子节点属性值来计算)
继承属性:
如果b是产生式右部某个文法符号X的属性(属性值由节点的兄弟节点及父节点的属性值来计算)用继承属性来表示程序设计语言结构中的上下文依赖关系很方便。
注释分析树:
每个节点的属性值都标注出来的分析树
分析树各结点属性的计算可以自下而上地完成
•终结符只有综合属性,并且这些综合属性通常由词法分析器提供
•非终结符号既有综合属性也可有继承属性,文法的开始符号没有继承属性,除非另外加以说明。
•文法符号的综合属性集和继承属性集的交集应为空。
•对出现在产生式右边的继承属性和出现在产生式左边的综合属性都必须提供一个计算规则。
属性计算规则中只能使用相应产生式中的文法符号的属性。
•出现在产生式左边的继承属性和出现在产生式右边的综合属性不由所给的产生式的属性计算规则进行计算,它们由其它产生式的属性规则计算或者由属性计算器的参数提供。
语义规则b:
=f(c1,c2,…,ck)中,函数f通常是表达式(T.val=F.val)。
也有一些规则写成过程调用或程序段(打印值,输出中间代码等),称为产生副作用的操作(如:
print(E.val))可看成是产生式左部非终结符的虚拟综合属性。
属性文法是指语义规则函数无副作用的语法制导定义。
S属性定义:
仅仅使用综合属性的语法制导定义
属性依赖图
•在一棵分析树中的结点的继承属性和综合属性之间的相互依赖关系可以由称作依赖图的一个有向图来描述。
•为每一个包含过程调用的语义规则引入一个虚拟综合属性b,这样把每一个语义规则都写成b:
=f(c1,c2,…,ck)的形式。
•依赖图中为每一个属性设置一个结点,如果属性b依赖于属性c,则从属性c的结点有一条有向边连到属性b的结点。
属性计算计算次序:
依赖图的任一拓扑排序都是一个合理的属性计算顺序
如果一属性文法不存在属性之间的循环依赖关系,那么称该文法为良定义的。
属性文法说明的翻译是很精确的
–基础文法用于建立输入符号串的语法分析树;
–根据语义规则建立依赖图
–从依赖图的拓扑排序中,可以得到计算语义规则的顺序。
语义规则的计算方法
•分析树方法:
前面介绍的方法(有环则失败)
•基于规则的方法:
静态确定语义规则的计算次序。
•忽略规则的方法:
事先确定属性的计算策略(如边分析边计算),那么语义规则的设计必须符合所选分析方法的限制。
基于(语义)规则的方法
•语法分析和属性计算分开,先构造分析树,然后按预先定义的策略遍历分析树来计算属性
•语义规则的定义和计算顺序(翻译模式)在编译器构造之前确定。
•分析树遍历策略的确定(构造编译器时)要考虑语义规则的定义及计算顺序,因此是基于规则的方法。
忽略(语义)规则的方法
•在进行语法分析的同时进行翻译,即边分析边计算属性,计算次序由分析方法确定而与语义规则无关。
•缺点:
这样确定计算次序将限制能实现的语法制导定义的种类。
•优点:
不构造依赖图,不对依赖图进行拓扑排序,提高了时空效率。
S属性定义的自下而上计算
1.语法树是分析树的浓缩表示:
算符和关键字是作为内部结点。
2.语法制导翻译可以基于分析树,也可以基于语法树
a)mknode(op,left,right):
建立一个运算符号结点,标号是op,两个域left和right分别指向左子树和右子树。
b)mkleaf(id,entry):
建立一个标识符结点,标号为id,一个域entry指向标识符在符号表中的入口。
c)mkleaf(num,val):
建立一个数结点,标号为num,一个域val用于存放数的值。
3.S-属性文法:
只含有综合属性
4.综合属性可以在分析输入符号串的同时由自下而上的分析器来计算。
分析器可以保存与栈中文法符号有关的综合属性值,每当进行归约时,新的属性值就由栈中正在归约的产生式右边符号的属性值来计算。
5.S属性的自下而上计算(一般方法):
将LR分析器增加一个域来保存综合属性值。
6.边分析边计算,分析完毕后计算也完毕了。
不用单独的为语义分析进行一遍分析。
L属性定义的自上而下计算
1.属性计算与分析方法之间的关系:
属性的计算次序受分析方法所限定的分析树结点建立次序的限制。
分析树的结点是自左向右生成。
所以,仅当属性信息是自左向右流动时,才有可能在分析的同时完成属性计算。
2.L属性定义:
如果每个产生式AX1X2…Xn的每条语义规则计算的属性是A的综合属性或者是Xj的继承属性,1jn,但它仅依赖:
a)该产生式中Xj左边符号X1,X2,…,Xj-1的属性;
b)A的继承属性。
3.S属性定义属于L属性定义。
4.对于L属性定义,与S属性的一个最本质区别在于:
S属性定义中,只要将产生式作为一个整体看待即可,语义规则可以视为是附着在整个产生式上,L属性定义则不一样,它跟属性所属的符号在产生式中的位置有关系
5.翻译方案:
给出了使用语义规则进行计算的次序,这样就可把某些实现细节表示出来。
在翻译方案中,和文法符号相关语义动作,用花括号{}括起来,插入到产生式右部的合适位置上。
这是一种动作和分析交错的方法,以表示动作的执行时机。
6.建立翻译模式
•当只需要综合属性时:
为每一个语义规则建立一个包含赋值的动作,并把这个动作放在相应的产生式右边的末尾。
•如果既有综合属性又有继承属性,在建立翻译模式时就必须保证:
1.产生式右边的符号的继承属性必须在先于这个符号的动作中计算出来。
2.一个动作不能引用这个动作右边的符号的综合属性。
3.产生式左边非终结符的综合属性只有在它所引用的所有属性都计算出来以后才能计算。
计算这种属性的动作通常可放在产生式右端的末尾。
L属性的自下而上计算
1.删除翻译方案中嵌入的动作
在文法中加入产生的标记非终结符,让每个嵌入动作由不同标记非终结符M代表,并把该动作放在产生式M的右端。
a)ETR
b)R+TMR1|TNR1|
c)Tnum{print(num.val)}
d)M{print(‘+’)}
e)N{print(‘’)}
2.分析栈上的继承属性
属性T.type在栈中的位置相对于栈顶是事先知道的。
因此,可以用栈中的属性值T.type代替L.in。
通过标记非终结符M复写属性的值,预知属性值在栈中所放位置
3.模拟继承属性的计算(一般情况)
增加标记非终结符,把f(A.s)的计算移到对标记非终结符归约时进行。
SaANCN.i:
=A.s;C.i:
=N.s
NN.s:
=f(N.i)
CcC.s:
=g(C.i)
第六章运行时存储空间的组织和管理
在程序语言中,程序使用的存储单元都是由标识符来表示的。
它们对应的内存地址都是由编译程序在编译时或由其生成的目标程序运行时进行分配。
1过程的活动
过程的一次执行称为过程的一次活动
2活动记录
过程的活动需要可执行代码和存放所需信息的存储空间,后者称为活动记录
3过程定义、过程调用、形式参数、实在参数、活动的生存期
•过程P一次活动的生存期,指的是从执行该过程体第一步操作到最后一步操作之间的操作时间,包括执行P时调用其它过程花费的时间。
–过程可以是递归的
–一个过程可对应多个活动
4即使一个名字在程序中只声明一次,该名字在程序运行时也可能表示不同的数据对象。
5名字到存储单元的绑定
(1)环境把名字映射到左值(存储单元),而状态把左值映射到右值(值)。
(2)赋值改变状态,但不改变环境。
(3)如果环境将名字x映射到存储单元s,我们就说x被绑定到s。
6活动记录
被编译程序每次运行时,编译器从操作系统(OS)获得一块存储区(内存)。
其内容包括:
(1)编译后的目标代码(可执行程序.exe)
(2)数据对象(各种静态变量和动态变量)
(3)用于管理过程活动的控制栈(活动记录)
一般的活动记录的布局:
返回值参数控制链访问链保存的机器状态局部数据临时数据
7局部数据的安排
(1)字节是可编址内存的最小单位。
(2)一个过程所声明的局部变量,按这些变量声明时出现的次序,在活动记录的局部数据区中依次分配空间。
(3)局部数据的地址可以用相对于某个位置(本过程对应的活动记录的起始位置)的偏移来表示。
(4)数据对象的存储安排深受目标机器寻址方式的影响,存在对齐问题。
例如,要求整数(int,long)的相对地址可以被4整除。
由于对齐而引起的无用空间称为衬垫空白区。
8程序块
本身含有局部变量声明的语句,可以嵌套服从最接近的嵌套作用域规则
性质:
(1)并列程序块不会同时活跃
(2)并列程序块的变量可以重叠分配
(3)可以把程序块看成是不带参数和返回值的函数
9静态分配
名字在程序被编译时绑定到存储单元,不需要运行时的任何支持。
绑定的生存期是程序的整个运行时间。
控制再次进入该过程时,局部变量的值和控制上一次离开时的一样。
每个活动记录的大小是固定的。
过程调用时保存信息的地址在编译时也是已知的
静态分配给语言带来限制
–递归过程不被允许
–数据对象的长度和它在内存中位置,必须是在编译时可以知道的
–数据结构不能动态建立
静态分配的应用:
Fortran语言被设计成允许静态存储分配
C语言程序的外部变量和程序中出现的常量都可以静态分配。
10栈式分配
◆栈式分配主要用于管理过程的活动记录。
◆局部变量的生存期是过程活动的时间。
◆控制进入该过程时,局部变量绑定到存储单元,过程活动结束后,局部变量的空间释放。
活动树:
用树来描绘控制进入和离开活动的方式
活动树的特点
–每个结点代表某过程的一个活动
–根结点代表主程序的活动
–结点a是结点b的父结点,当且仅当控制流从a的活动进入b的活动
–结点a处于结点b的左边,当且仅当a的生存期先于b的生存期
运行栈:
把控制栈中的信息拓广到包括过程活动所需的所有局部信息
运行栈的管理:
过程调用和过程返回都需要执行一些代码来管理活动记录栈,保存或恢复机器状态等过程调用时执行的分配活动记录,把信息填入它的域中的代码。
过程返回序列过程返回时执行的恢复机器状态,释放活动记录,使调用过程能够继续执行的代码
栈式分配的动态释放空间引起悬空引用:
引用某个已被释放的存储单元
11堆式分配
内存分配与释放按照任意次序进行
堆中可能包含交错的正在使用的和已经释放的区域
12无过程嵌套的静态作用域
过程体中的非局部引用可以直接使用静态确定的地址
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编译 原理 复习