一个很笨很笨的人的编译自救笔记。
程序设计语言用于书写计算机程序的语言。语言的基础是一组记号和一组规则。根据规则由记号构成的记号串的总体就是语言。在程序设计语言中,这些记号串就是程序。
程序设计语言由三个方面的因素,语法、语义和语用。
源程序:用汇编语言或高级语言编写的程序称为源程序。
目标程序:用目标语言所表示的程序。
翻译程序:将源程序转换为目标程序的程序称为翻译程序。
汇编程序:若源程序用汇编语言书写,经过翻译程序得到用机器语言表示的程序,这是的翻译程序就称之为汇编程序。
编译程序:若源程序是用高级语言书写,经加工得到目标程序,这种翻译过程称为编译。
任务:分析和识别单词。
单词:是语言的基本语法单位。
任务:根据语法规则(即语言的文法),分析并识别出各种语法成分,如表达式、各种说明、各种语句、过程、函数等,并进行语法正确性检查。
任务:对识别出的各种语法成分进行语义分析,并产生相应的中间代码。
中间代码:一种介于源语言和目标语言的中间语言形式。
目的:
中间代码的形式:常用四元式、三元式、逆波兰表示。
四元式(三地址指令)
任务:目的是为了得到高质量的目标程序。
由中间代码很容易生成目标程序。这部分工作与机器关系密切,所以要根据机器进行。在做这部分工作时(要注意充分利用累加器),也可以进行优化处理。
上述五个阶段都需要做两件事:建表查表,出错处理。
符号表管理:在整个编译过程中始终都要贯穿着建表(填表)和查表的工作。即要及时地把源程序中的信息和编译过程中所产生的信息登记在表格中,而在随后的编译过程中同时又要不断地查找这些表格中的信息。
前端:与源程序有关的编译部分称为全段。
后端:与目标机有关的部分称为后端。
字母表:符号的非空有限集;
符号:字母表中的元素;
符号串:符号的有穷序列;
空符号串:无任何符号的符号串。
符号串的闭包运算:
文法是对语言结构的定义与描述。即从形式上用于描述和规定语言结构的称为文法。
语法规则:我们通过建立一组规则,来描述句子的语法结构。
由规则推导句子:有了一组规则之后,可以按照一定的方式用它们去推导或产生句子。
推导方法:从一个要识别的符号开始推导,即用相应规则的右部来替代规则的左部,每次仅用一条规则去进行推导。
所谓文法是在形式上对句子结构的定义和描述,而未涉及语义问题。
语法树:我们用语法树来表述一个句子的语法结构。
当符号串已没有非终结符号时,推导就必须终止。因为终结符不可能出现在规则左部,所以将在规则左部出现的符号称为非终结符号。
规范推导=最右推导
递归规则:
递归文法:
递归文法的优点:可用有穷条规则,定义无穷语言。
左递归文法的缺点:不能用自顶向下的方法来进行语法分析。
短语是前面句型中某个非终结符所能推出的符号串。
短语、简单短语、句柄都是基于句型来说的,先画出语法树,一个结点的子节点就是他的短语,一个结点能推出的终结符就是他的简单短语,在整棵子树最左边的终结符就是句柄。一个句型可能有多个短语、简单短语,而句柄只有一个。
语法树:句子结构的图示表示法,它是有向图,由结点和有向边组成。
文法所能产生的句子,可用用不同的推导序列(使用产生式顺序不同)将其推导出来。语法树的生长规律不同,蛋最终生成的语法树形状完全相同。
子树:语法树中的某个结点(子树的根)连同它向下派生的部分所组成。
文法的二义性:若对一个文法的某一句子,存在两颗不同的语法树,则该文法是二义性文法,否则是无二义性文法。
文法的二义性意味着句型的句柄不唯一。
若一个文法的某规范句型的句柄不唯一,则该文法是二义性的,否则是无二义性的。
若文法是二义性的,则在编译时就会产生不确定性,遗憾的是在理论上已经证明:文法的二义性是不可判定的,即不可能构造出一个算法,通过有限步骤来判定任一文法是否有二义性。
多余规则:
扩充的BNF表示
语法图
形式语言:用文法和自动机所描述的没有语义的语言。
文法定义:
语言定义:
文法和语言分类:0型、1型、2型、3型
0型
0型文法称为短语结构文法。规则的左部和右部都可以是符号串,一个短语可以产生另一个短语。
可以用图灵机接受。
1型
1型称为上下文敏感或上下文有关。即只有在x、y这样的上下文中才能把U改写为u。
可以用一种线性界限自动机接受。
2型
2型文法称为上下文无关文法,即把U改写为u时,不必考虑上下文。
2型文法与BNF等价。
2型语言可以由下推自动机接受。
3型
3型文法称为正则文法。它是对2型文法进行进一步限制。
又称正则语言、正则集合,这种语言可以被有穷自动机接受。
0型文法可以产生L0,L1,L2,L3
但2型文法只能产生L2,L3不能产生L0,L1。
3型文法只能产生L3。
词法分析:
几种常用的单词内部形式:
左线性文法的状态图的画法:
功能:根据文法规则,从源程序单词符号串中识别出语法成分,并进行语法检查。
基本任务:识别符号串S是否为某语法成分。
自顶向下分析:
自底向上分析算法:
给定符号串S,若预测某一语法成分,则可根据该语法从成分的文法,设法S构造一个语法树。
自顶向下分析方法特点:
自顶向下分析的基本缺点是:不能处理具有左递归性的文法。
如果在匹配输入串的过程中,假定正好轮到要用非终结符U直接匹配输入串,即要用非终结符U直接匹配输入串,即用U的右部符号串去匹配,为了用U去匹配,又得用U去匹配,这样无限的循环下去将无法终止。
如果文法具有间接左递归,则也将发生上述问题,只不过环的圈子都得更大。
要实行自顶向下分析,必须要消除文法的左递归。
消除直接左递归
用扩充的BNF表示来改写文法。
改写文法消除左递归,又前后等价:
具有一个直接左递归的右部并位于最后,这表明该语法类U是由x或y或z气候随有0个v或多个v组成。
消除一般左递归
一般左递归也可以通过改写文法予以消除。
消除所有左递归的算法:
消除回溯的途径
为了在不采用超前扫描的前提下实现不带回溯的自顶向下分析,文法需要满足两个条件:
递归子程序法
对语法的每一个非终结符都编一个分析程序,当根据文法和当时的输入符号预测到要用某个非终结符去匹配输入串时,就调用该非终结符的分析程序。
符号表:在编译过程中,编译程序用来记录源程序中各种名字的特性信息,多以也称为名字特性表。
名字:程序名、过程名、函数名、用户定义类型名、变量名、常量名、枚举值名、标号名。
特性信息:上述名字的种类、类型、位数、参数个数、数值及目标地址等。
当过程和函数体编译完成后,应将与之相应的参数名和局部变量名以及后者的特性信息从符号表中删去。
目标程序运行时所需存储空间的组织与管理以及源程序中变量存储空间的分配。
静态存储分配:在编译阶段由编译程序实现对存储空间的管理和为源程序中的变量分配存储的方法。
动态存储分配:在目标程序运行阶段由目标程序实现对存储空间的组织与管理,和为源程序中的变量分配存储的方法。
分配策略:由于每个变量所需空间的大小在编译时已知,因此可以用简单的方法给变量分配目标地址。
分配策略:整个数据区为一个堆栈;当进入一个过程时,在栈顶为其分配一个数据区;退出时,撤销过程数据区。
一个典型的活动记录可以分为三部分:
参数区:
prev abp:存放调用模块记录基地址,函数执行完时,释放其数据区,数据区指针指向调用前的位置。
ret addr:返回地址,即调用语句的吓一跳执行指令地址;
ret value:函数返回值;
形参数据区:每一形参都要分配数据空间,形参单元中存放实参值或者实参地址。
display区:
栈式分配和堆式分配的比较
栈 | 堆 |
解决了函数递归调用等问题 | 解决了动态申请空间的问题 |
由编译器自动管理 | 由程序员控制空间的申请和释放操作 |
向内存地址减少的方向增长 | 向内存地址增加的方向增长 |
不会产生碎片 | 会产生碎片 |
计算机底层支持,分配效率高 | C函数库支持,分配效率低 |
一般编译程序都生成中间代码,然后再生成目标代码,主要优点是可移植(与具体目标程序无关)。
前缀表达(波兰表达)
后缀表达(逆波兰表达)
算法:设一个操作符栈,当读到操作数时,立即输出改操作数,当扫描到操作符时,与栈顶操作符比较优先级,若栈顶操作符优先级高于栈外,则输出该栈顶操作符,反之,则栈外操作符入栈。
转换算法:
波兰表达式的优点:
在该表示中,每条指令由n个域组成,通常第一个域表示操作符,其余为操作数。
常用的n元表示:三元式四元式;
三元式:
间接三元式:将执行顺序和三元式编号分离。
四元式:
抽线语法树:用树型图的方式表示中间代码,操作数出现在叶结点上,操作符出现在中间节点。
DAG图:有向无环图,语法树的一种规约表达形式。
静态单一复制形式的IR主要特征是每个变量只赋值一次。
SSA优点:
从编译角度,将错误分为两类:语法错误和语义错误。
语义错误:源程序在语法上不合乎文法。
语义错误主要包括:程序不符合语义规则或超越具体计算机系统的限制。
语义规则:
超越系统限制:
错误诊察:
错误报告:
发现错误后,在报告错误的同时还要对错误进行处理,以方便编译能进行下去。
一般原则:当诊察到错误以后,就暂停对对面符号的复习,跳过错误所在的语法成分然后继续向下分析。
错误局部化处理的实现(递归下降分析法):
词法分析,语法分析:解决单词和语言成分的识别及词法和语法结构的检查。语法结构课形式化地用一组产生式来描述。给定一组产生式,能够狠容易地将其分析器构造出来。
输入文法:未插入动过符号时的文法。由输入文法可以通过推导产生输入序列。
翻译文法:插入动作符号的文法。由翻译文法可以通过推导产生活动序列。
活动序列:由翻译文法推导出的符号串,由终结符和动作符号组成。
翻译文法是上下文无关文法,其终结符号集由输入符号和动作符号组成。由翻译文法所产生的终结符号串称为活动序列。
符号串翻译文法:若插入文法中动作符号对应的语义子程序是输出动作符号标记@后的字符串的文法。
语法知道翻译:按翻译文法进行的翻译。
给定一输入符号串,根据翻译文法获得翻译该符号串的动作序列,并执行该序列所规定的动作的过程。
在翻译文法的基础上,可以进一步定义属性文法,翻译文法中的符号,包括终结符、非终结符和动作符号均可带有属性,这样能更好的描述和实现编译过程。
这是属性翻译文法中较简单的一种,其输入文法要求是LL(1)文法,可用自顶向下分析构造分析器。在分析过程中可进行属性求值。
L-属性翻译文法事带有下列说明的翻译文法:
属性的求值规则:
继承属性:
综合属性(适合在滴定向下分析过程中求值):
一个L-ATG被定义为简单赋值形式的(SL-ATG),当且仅当满足如下条件:
按翻译要求,在文法中插入语义动作符号,在分析过程中调用相应语义处理程序,完成翻译任务。
对于每个非终结符号都编写一个翻译子程序。根据该非终结符号具有的属性数目,设置相应的参数。
继承属性:声明为复制形参。
综合属性:声明为变量形参。
左线性文法的状态图的画法:
识别算法:
正则表达式:
正则表达式中的运算符:
| 或; · 连接; * 或 {} 重复; ()括号;
a*表示由任意个a组成的串;
而{a,b}* = {e,a,b,aa,ab,ba,bb...}
运算符优先级:
先*,后·,最后|。
正则表达式相等等价于这两个正则表达式表示的语言相等。
正则表达式与3型文法等价
若是一个多值函数,且输入可允许为空字符串,则有穷自动机是不确定的,即在某个状态下,对于某个输入字符存在多个后继状态。
从同一状态出发,有同一字符标记的多条边,或者有以空字符标记的特殊边的自动机。
不确定的有穷自动机与确定的有穷自动机从功能上来说是等价的。
集合I的e-闭包:
令I是一个状态集的子集,定义e-closure(I)为:
令I是NFA M’的状态集的一个子集,a∈∑,定义:Ia = e-closure(J),其中J = (s,a)
一个有穷自动机是化简的等价于它没有多余状态并且他的状态中没有两个是互相等价的。
一个有穷自动机可以通过消除多余状态和合并等价状态进而转换成一个最小的与之等价的有穷自动机。
有穷自动机的多余状态:从该自动机的开始状态出发,任何输入串也不能到达那个状态。
等价状态:
分割法:把一个DFA的状态分割成一些不相关的子集,是的任何不同的两个子集状态都是可区别的,而同一个子集中的任何状态都是等价的。
用上下文无关文法只能描述语言的语法结构,而不能描述其语义。
栈式抽象机:由三个存储器、一个指令寄存器和多个地址寄存器组成。
存储器:数据存储器(存放AR的运行栈)、操作存储器(操作数栈)、指令存储器。
编译程序处理声明语句要完成的主要任务为:
基本块:
划分基本块:
方法:
(1)利用代数性质
编译时完成常量表达式的计算,整数类型与实型的转换;
下标变量引用时,其地址计算的一部分工作可在编译时预先做好;
用一种需要较少执行时间的运算代替另一种运算
(2)常数合并和传播
如x:=y这样的赋值语句称为复写语句。由于x和y值相同,所以当满足一定条件时,在该赋值语句下面出现的x可用y来代替。
(3)删除冗余代码
冗余代码就是毫无实际意义的代码,又称死代码(dead code)或无用代码(useless code)。
消除公共子表达式:
DAG图(有向无环图)用来表示基本块内各中间代码之间的关系。
方法:
数据流分析
后面懒得写了