* 编译原理第一章内容概述
* 编译原理第二章内容概述:上下文无关文法,最左推导,最右推导,语法分析树与文法的二义性
* 编译原理第三章内容概述:正规表达式与有限自动机(DFA与NFA)
* 编译原理第四章内容概述:LL(1)分析法
* 编译原理第五章内容概述:算符优先分析法
* 编译原理第六章内容概述:S-属性文法的自下而上计算
L-属性文法的自顶向下翻译
* 编译原理第七章内容概述:中间代码生成
第一章
各阶段的任务:
* 词法分析任务:输入源程序,对构成源程序的字符串进行扫描和分解,识别出一个个单词。
* 语法分析任务:在词法分析的基础上,根据语言的语法规则,把单词符号串分解成各类语法单位。
* 语义分析与中间代码产生任务:对语法分析所识别出的各类语法范畴,分析其含义并进行初步翻译。
* 优化阶段任务:对前段产生的中间代码进行加工变换,以期在最后阶段能产生出更为高效的目标代码。
* 目标代码生成任务:把中间代码变换成特定机器上的低级语言代码。
第二章
语法分析树与二义性
1.语法树的根结由开始符号所标记。
2.随着推导的展开,当某个非终结符被它的某个候选式所替换时,这个非终结符的相应结就产生了下一代新结点。每个新结点
其父亲结点间都有一条连线。
3.在一棵语法树生长过程中的任何时刻,所有那些没有后代的端末结自左至右排列起来就是一个句型。
例如对于文法 E→E+E|E*E|(E)|i, 关于(i*i+i)的推导形成语法树如图
语法树有不唯一性,如果一个文法存在某个句子对应两棵不同的语法树,则称这个文法是二义的。
也就是说,若一个文法存在某个句子,它有两个不同的最左(最右)推导,则这个文法是法是二义的。关于文法二义性的几个问题:
1.文法二义不等于语言二义
2.文法的二义性是不可判定的
3.文法的二义性证明:找出一个句子,它有两个不同的最左推导或最右推导
4.文法二义性的消除:给每个产生式定义优先级
消除文法二义性的方法:定义优先级和结合性,引入新的非终结符,建立新的产生式。
形式语言鸟瞰:
* 0型文法(短语文法)
* 1型文法(上下文有关文法)
* 2型文法(上下文无关文法)
* 3型文法(右/左线性文法)
第三章
状态转换图
构造一个识别标识符的状态转换图
构造一个识别整数的状态转换图
识别实型常数的状态转换图
正规表达式与有限自动机
我们可以把具有相同特征的字放在一起组成一个集合,即所谓的正规集,然后使用一种形式化的方法来表示正规集,即所谓的正
规式。
[注]:
正规式是描述单词结构的一种形式;
正规集是该类单词的全集。正规式与正规集的定义(递归的定义方法):
(1)ε和φ是∑上的正规式,它们所表示的正规集分别为{ε}和φ
(2)任何a∈∑,是∑上的一个正规式,他所表示的正规集为{ a }
(3)假定U和V都是∑上的正规式,他们所表示的正规集分别记为L(U)和L(V),那么
(a) (U|V)是正规式,所表示的正规集为L(U)∪L(V)
(b) (UV)是正规式,所表示的正规集为L(U) · L(V)(连接积)
(c) (U)*是正规式,所表示的正规集为 (L(U))*(闭包)
仅由有限次使用(1)(2)(3)所得到的表达式才是∑上的正规式,仅由这些正规式所表示的字集才是∑上的正规集。
若两个正规式U和V所表示的正规集相同,则认为二者等价,记为: U = V
1.状态转换矩阵表示法
设DFA M = ({0,1,2,3},{a, b}, f, 0, {3}),其中
f: f(0, a) = 1, f(0, b) = 2 f(1, a) = 3, f(1, b) = 2 f(2, a) = 1, f(2, b) = 3 f(3, a) = 3, f(3, b) = 3
2.用状态转换图来表示
设DFA M = ({0,1,2,3},{a, b}, f, 0, {3}),其中
f: f(0, a) = 1, f(0, b) = 2 f(1, a) = 3, f(1, b) = 2 f(2, a) = 1, f(2, b) = 3 f(3, a) = 3, f(3, b) = 3
非确定的有限自动机
定义:一个非确定有限自动机(NFA)M是一个五元式
M = (S, ∑, f, S0, F),其中
* S是一个有限的状态集合,它的每个元素我们称为一个状态
* ∑是一个有限的输入符号的字母表,它的每个元素我们称为一个输入字符
* f是从S×∑*→2S 的部分映射,其中,2S表示S的幂集合(所有S的子集组成的集合)(f是非单值的M是非确定)
* 状态集合S0是初始状态集合,它是S的子集
* 状态集合F是终止状态的集合,它是S的子集
NFA M表示方法:
1.用状态矩阵表示
设NFA M = ({0,1,2},{a, b}, f, {0}, {1,2}),其中
f: f(0, aa) = 1, f(0, bb) = 2 f(0, a) = 0, f(0, b) = 0 f(1, a) = 1, f(1, b) = 1 f(2, a) = 2, f(2, b) = 2
2.用状态转换图表示
设NFA M = ({0,1,2},{a, b}, f, {0}, {1,2}),其中
f: f(0, aa) = 1, f(0, bb) = 2 f(0, a) = 0, f(0, b) = 0 f(1, a) = 1, f(1, b) = 1 f(2, a) = 2, f(2, b) = 2
化简DFA的一般步骤:
(1) 检查状态转换函数是否为全函数。
所谓全函数,是指每个状态对每个输入符号都有转换,若不是全函数,可以引入一个“死状态”d,d对所有输入符号都转换到
d,如果状态s对输入符号a没有转换,那么加上从s到d的转换。
(2) 用化简算法进行化简
(3) 去掉死状态
DFA化简算法基本思想:把M的状态集分割为一些不相交的子集,使得任何不同的两个子集状态都是可区别的,而同一个子集中
的任何状态都是等价的,最后让每个子集选一个代表,同时消去其他等价状态。
化简算法
①对M的状态集S进行划分:把S的终态和非终态分开,分成终态集合非终态集,形成基本分划П,显然这两个子集是可区别的。
②假定到某个时候П含有m个子集,记П={I(1),I(2),… I(m)}。并且,属于不同子集的状态是可区别的。检查П中的每个I(i)看能否
一步划分:对于某个I(i),令I(i)={q1 ,q2 ,…,qk}
若存在一个输入字符a使得I(i)a不全包含在现行П的某个子集I(j)中,就将I(i)一分为二③一般地,若I(i)a落入现行П中N个不同子集,则应将I(i)划分为N个不相交的组,使得每个组J的Ja都落入П的同一子集,这样再形
成新的分划
④重复上述过程,直至分划中所含的子集数不再增长为止。至此,П中的每个子集已不可再分。也就是说,每个子集中的状态是
互相等价的,而不同子集中的状态则是互相可区别的。
⑤经过上述过程后,得到一个最后分划П.对于这个П中的每个子集,选取子集中的一个状态代表其它状态。
第四章
消除直接左递归方法:
设有产生式P→Pα1|Pα2|…|Pαm|β1|β2|…|βn其中每个βi不以P开头,每个αi不为ε
消除P的直接左递归性就是把这些规则改写成:P→β1P’|β2P’|…|βnP’
P’→α1P’| α2P’|…|αmP’| ε
用这个办法,我们容易把见诸于表面的所有直接左递归都消除掉,也就是说,把直接左递归都改成直接右递归。但这并不意味着已经消除整个文法的左递归性。例如文法:
S→Qc|c
Q →Rb|b
R →Sa|a
虽然不具有直接左递归,但SQR都是左递归的,例如有S=>Qc=>Rbc=>Sabc。
消除文法的左递归的步骤:
1)将间接左递归改造为直接左递归
将文法中所有如下形式的产生式:
Pi →Pjγ|β1|β2|…|βn
Pj→δ1|δ2|δ3|…|δk
改写成:
Pi →δ1γ|δ2γ|δ3γ|…|δkγ|β1|β2|…|βn2)消除直接左递归
P→Pα1|Pα2|...|Pαm|β1| β2|...| βn
消除P的左递归
P→ β1P'| β2P'|...| βnP'
P'→ α1 P'| α2 P'|...|αm P'| ε
3)化简改写后的文法,即去除那些从开始符号出发却永远无法到达的非终结符的产生规则。
最终得到无左递归的文法。
消除回溯
欲构造行之有效的自上而下分析器,必须消除回溯。为了消除回溯就必须保证:对文法的任何非终结符,当要它去匹配输入串
时,能够根据它所面临的输入符号准确地指派它的一个候选去执行任务,并且此候选的工作结果应是确信无疑的。即若该候选式
匹配成功,那么该匹配不是虚假匹配。若该候选式无法完成最终的匹配任务,则其他任何候选式肯定也无法完成。
消除回溯的条件
定义FIRST集
令文法G是不含左递归的文法,对G的非终结符的候选α,定义它的开始符号(终结首符)集合:
预测分析表的构造——FIRST(X)
1.若X终结符,则FIRST(X)={X}
2.若X为非终结符,且有X->a …的产生式,则把a加入到FIRST(X)中;
3.若X->Y…是一个产生式,且Y为非终结符,则把FIRST (Y)-ε加入到FIRST(X)中;
若X->Y1Y2Y3….YK,是产生式, Y1Y2Y3….Yi-1是非终结符,而且ε属于 FIRST (Yj)(1<=j<=i-1),则把FIRST (Yj)-ε加入到
FIRST(X)中;如果ε属于所有的FIRST (Yj),则ε加入到FIRST(X)中
预测分析表的构造——FOLLOW(X)
1.对于文法的开始符,置#于FOLLOW(S)中
2.若A->αBβ, 则把FIRST (β)-ε加入到FOLLOW(B)中,
3.若A->αB 是一个产生式,或 A->αBβ是一个产生式,而β-> ε,则把FOLLOW(A)加入到FOLLOW(B)中
LL(1)分析条件
定义FOLLOW集
对文法G的任何非终结符A,定义它的后继符号集合:
【注】
1)特别地,如果S*=>…A,则#∈FOLLOW(A)
2)FOLLOW(A)集合是所有句型中出现在紧接A之后的终结符号或#所组成的集合
3)当非终结符A面临输入符号a,且a不属于A的任意候选式的FIRST集但A的某个候选式的FIRST集包含ε时,只有当a
∈FOLLOW(A),才可能允许A自动匹配
3.不带回溯的自上而下分析的文法条件(LL(1)文法)
(1)文法不含左递归
(2)对于文法中每一个非终结符A的各个产生式的候选式的FIRST集两两不相交。即,若 A→α1|α2|…|αn 则
FIRST(αi)∩FIRST(αj)=Φ (i≠j)
(3)对于文法中的每个非终结符A,若它的某个候选首符集包含ε,则FIRST(A)∩FOLLOW(A)=Φ
如果一个文法G满足以上条件,则称该文法G为LL(1)文法(第1个L代表从左到右扫描输入串,第2个L代表最左推导,1表示分
析时每一步只看1个符号)
4.不带回溯的自上而下分析的方法
对于LL(1)文法,假设要用非终结符A进行匹配,面临输入符号为a,A的所有产生式为 A→α1|α2|…|αn(1)若a ∈ FIRST(αi) ,则指派αi去匹配
(2)若a不属于任何一个候选首符集,则:
①若ε属于某个 FIRST(αi)且a∈FOLLOW(A),则让A与ε自动匹配;
②否则,a的出现是一种语法错误
第五章
规范归约
定义:令G是一个文法,S是文法的开始符号,假定αβ△是文法G的一个句型
其中α,β,△∈(VN∪VT)*,A∈VN ,如果有
则称β是句型αβ△相对于非终结符A的短语。
特别是,如果有A=>β则称β是句型αβ△相对于规则A—>β的直接短语,一个句型的最左直接短语称为该句型的句柄。
注:因为句型是由开始符号推出来的,而短语是由非终结符号推出来的。所以,短语是句型的一部份或全部符号串。
考虑文法
G : E —> T | E+T
T—> F | T*F
F—> (E) | i
求证i1*i2+i3是G的一个句型,并找出该句型的全部短语、直接短语和句柄。
分析:证明i1*i2+i3是G的一个句型
E => E+T=> T+T => T*F+T => F*F+T
=> i1*F+T => i1*i2+T=> i1*i2+F
=> i1*i2+i3找i1*i2+i3的所有短语
(1)假设i1*i2+i3是一个短语
因为 E =>*E 且 E =>+i1*i2+i3
所以i1*i2+i3是句型i1*i2+i3关于E的一个短语(2)假设i1*i2是一个短语
因为 E =>*T+i3 且 T =>+i1*i2
所以i1*i2是句型i1*i2+i3关于T的一个短语(3)假设i1是一个短语
因为 E =>*F*i2+i3 且 F =>+ i1
所以i1是句型i1*i2+i3关于F的一个短语(4)假设i2是一个短语
因为 E=>*i1*F+i3 且 F=>+i2
所以i2是句型i1*i2+i3关于F的一个短语
(5)假设i3是一个短语
因为 E=>*i1*i2+F且 F=>+i3
所以i3是句型i1*i2+i3关于F的一个短语(6)假设i2+i3是一个短语
因为 E=>*i1*E不成立, 且 E=>+i2+i3成立
所以i2+i3不是句型i1*i2+i3的一个短语注:缺一不可
所以短语有:i1*i2+i3, i1*i2, i1,i2,i3
找直接短语:
根据定义,如果有A=>β,则称β是句型αβ△相对于规则A—>β的直接短语
因为有:F —> i
所以i1、i2、i3是直接短语
找句柄:
根据定义,一个句型的最左直接短语称为该句型的句柄。
i1是句型i1*i2+i3的句柄
规范归约定义:假定α是文法G的一个句子,我们称序列 αn, αn-1,… ,α0 是α的一个规范归约,如果此序列满足:
(1) αn=α
(2) α0为文法的开始符号,即α0=S
(3) 对任何i,0 < i <=n, αi-1是从αi经把句柄替换成为相应产生式左部符号而得到的。
容易看到,规范归约是关于α的一个最右推导的逆过程。因此,规范归约也称最左归约。
例:设文法G(S):
(1) S —> aAcBe
(2) A —> b
(3) A —> Ab
(4) B —> d
求句子abbcde的规范归约abbcdeE=> aAbcde E=>aAcde E=> aAcBeE=> S
把上例倒过来写,则得到:
S => aAcBe=> aAcde => aAbcde =>abbcde
注:
1.在形式语言中,最右推导常被称为规范推导。由规范推导所得的句型称为规范句型。如果文法G是无二义的,那么,规范推
导的逆过程必是规范规约(最左归约)。
2.由于规范句型由最右推导推出的句型,故该句型的句柄右边只含有终结符号
算符优先分析
1.算符优先分析法思路:定义算符之间优先关系,借助这种关系来寻找“可归约串”和进行归约
2.定义两个终结符‘a’与‘b’的优先关系
a =.b 表示a的优先性等于b
a >.b 表示a的优先性大于b
a <.b 表示a的优先性小于b
注意:=. >. <. 不同于数学上的 = < >
a =.b 不一定对应着 b =. a
a >.b 不一定对应着 b <. a
a <.b 不一定对应着 b >. a
算符优先文法及优先表构造
1.一个文法,如果它的任一产生式的右部都不含两个相继(并列)的非终结符,即不含如下形式的产生式右部:
…QR…则我们称该文法为算符文法,也称OG文法 。
2.定义终结符之间的优先关系
假定G是一个不含ε产生式的算符文法。对于任何一对终结符a、b,我们说:
1) a =. b 当且仅当文法G中含有形如P→…ab…或P→…aQb…的产生式;
2)a <. b 当且仅当G中含有形如P→…aR…的产生式, 而R=>b…或R =>Qb…;
3 ) a>.b 当且仅当G中含有形如P→…Rb…的产生式,而 R=> …a或R=> …aQ。
3.如果一个算符文法G中的任何终结符对(a,b)至多只满足下述三关系之一:
a=.b
a>.b
a<.b
则称G是一个算符优先文法(OPG文法)。
构造算符优先关系表
(1)通过检查产生式的每一个候选式可以找出满足a=.b
(即P→…ab…或P→…aQb…的产生式)
(2)为了满足<.和>.,需对G中每个非终结符P构造两个集合FIRSTVT(P)和LASTVT(P):
(3)构造集合FIRSTVT(P)的算法
按其定义,可用下面两条规则来构造集合FIRSTVT(P):① 若有产生式P→a…或P→Qa…,
则aFIRSTVT(P);② 若aFIRSTVT(Q),且有产生式P→Q…,
则aFIRSTVT(P)。
(4)同理构造构造集合LASTVT(P)的算法
按其定义,可用下面两条规则来构造集合LASTVT(P):① 若有产生式P→… a或P→… aQ ,
则a LASTVT(P);② 若a LASTVT(Q),且有产生式P→… Q ,
则a LASTVT(P)。
(5)有了这两个集合之后,就可以通过检查每个产生式的候选式确定满足关系<.和>.的所有终结符对。
(1)假定有个产生式的一个候选形为…aP…
那么,对任何b ∈FIRSTVT(P),有a <. b。
(2)假定有个产生式的一个候选形为…Pb…
那么,对任何a∈LASTVT(P),有a >. b。
第六章
属性文法
属性文法:是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的“值”(称为属性)。
* 属性代表与文法符号相关的信息,和变量一样,可以进行计算和传递。
* 属性的加工过程即是语义的处理过程。
属性分为两种:继承属性和综合属性。
继承属性:用于“自上而下”传递信息。在语法树中,一个结点的继承属性由此结点的父结点和/或兄弟结点的某些属性确定。
例:继承属性的例子,说明语句的类型信息统计,说明语句的文法。
综合属性:用于“自下而上”传递信息。在语法树中,一个结点的综合属性的值,由其子结点的属性值确定。
S—属性文法:仅仅使用综合属性的属性文法
例:综合属性的例子,考虑如下属性文法,它用作台式计算器程序,对每个 非终结符E、T和F都有一个综合属性——val,是一个
整数值。每个产生式左边的综合属性val都是由右边的计算出来的。
语义规则:属性计算的过程即是语义处理的过程。对于文法的每一个产生式配备一组属性的计算规则,则称为语义规则。
基于属性的文法处理过程
输入串—>语法树—>依赖图—>语义规则计算次序—>计算结果
这种由源程序的语法结构所驱动的处理办法就是语法制导翻译法。
语义规则的计算可能产生代码、在符号表中存放信息、给出错误信息或执行任何其它动作。对输入串的翻译也就是根据语义规则
进行计算得出结果。
依赖图
1. 在一颗语法树中的结点的继承属性和综合属性之间的相互依赖关系可以用称作依赖图的一个有向图来描述。
在为一棵语法树构造依赖图以前,我们为每一个包含过程调用的语义规则引入一个虚综合属性b,这样把每一个语义规则都写成
b:= f(c1,c2, …ck) 。
依赖图中为每一个属性设置一个结点,如果属性b依赖属性c,则从属性c的结点有一条有向边连到属性b的结点。
依赖图的构造方法
for分析树中每一个结点n{
for 结点的文法符号的每一个属性a{
为a在依赖图中建立一个结点;
}
}for分析树中每一个结点n{
for结点n所用产生式对应的每一个语义规则 b:=f(c1,c2,…ck){
for i :=1 to k
从ci结点到b结点构造一条有向边}
}
例:产生式 A—>XY的语义规则
(1)A.a:=f(X.x, Y.y)
(2)X.i:=g(A.a,Y.y)
分析:1)若有其语义规则:A.a:=f(X.x,Y.y)。这条语义规则确定了依赖于属性X.x和Y.y的综合属性A.a。
如果在语法树中应用这个产生式,那么在依赖图中会有三个节点A.a X.x Y.y。
由于A.a依赖于X.x,所以有一条有向边从X.x到A.a。由于A.a也依赖于Y.y,所以有一条有向边从Y.y连接到A.a。
2)若产生式A—>XY还有对应的语义规则:X.i:=g(A,a,Y,y)
那么图中还应有两条有向边,一条从A.a连接到 X.i,另一条从Y.y连接到X.i,因为X.i依赖于A.a和Y.y。
注意:
如果一属性文法不存在属性之间的循环依赖关系,那么该文法为良定义的。为了设计编译程序,我们只处理良定义的属性文法。
S-属性文法的自上而下计算
S—属性文法,它只含有综合属性。
* 综合属性可以在分析符号串的同时由自上而下的分析器来构造
* 分析器可以保存与栈中文法符号有关的综合属性值
* 每当进行归约时,新的属性值就由栈中正在归约的产生式右边符号的属性值来计算
* 可以通过扩充分析器中的栈来存放这些综合属性值
* S-属性文法的翻译器通常可借助于LR分析器实现例:
输入 3 * 5 + 4n
L-属性文法的自顶向下翻译
定义:如果每个产生式A —>X1 X2 … Xn 的每条语义规则计算的属性是A的综合属性;或者是Xj 的继承属性, 1 j n, 但它仅
依赖:
* 该产生式中Xj左边符号X1, X2, …, Xj-1的属性;
* A的继承属性
S属性文法包含于L属性文法。
第七章
中间代码的形成
中间代码:是源程序的一种内部表示,复杂性介于源语言和目标机语言之间。
中间代码的作用:
1)使编译程序的逻辑结构更加简单明确
2)利于进行与目标机无关的优化
3)利于在不同目标机上实现同一种语言
翻译方法:语法制导翻译
中间代码的形式:
1)逆波兰表示
2)图表示法(DAG和抽象语法树)
3)三地址代码(四元式、三元式、简介三元式)
例: 对于语句a:=b*-c+b*-c 的三种表示方法
四元式
三元式
间接三元式
注:四元式出现的顺序和表达式计值顺序一样,但四元式之间的联系与三元式不同(通过指示器),它是通过临时变量,所以改
动四元式很容易,这就为优化提供了方便,因为不牵扯到改变指示器的问题。
逆波兰表示法
波兰表示是一种既不须考虑优先关系、又不用括号的一种表示表达式的方法(前缀式)。
现在我们要介绍的刚好是另一种波兰表示形式,称为后缀式,即运算符在后。
例: a+b → ab+
a*(b+c) → abc+*
-a+b*c → a@bc*+图表示法
抽象语法树。
无循环有向图(DAG)
* DAG与抽象语法树基本上一样,对表达式中的每个子表达式,DAG中都有一个结点。一个内部结点表示一个操作符,它的孩子
表示操作数。
* 两者所不同的是,在一个DAG中代表公共子表达式的结点具有多个父结点,而在一棵抽象语法树中公共子表达式被表示为重复
的子树。如下例:
a:=b*-c+b*-c的图表示法