文章目录
1 Introduction
20分出勤
10分大作业:交纸质版,报告性质的大作业
70分考试
汇编程序是翻译程序
汇编语言程序是汇编程序的源程序
编译程序将高级语言程序翻译成低级语言的程序
解释程序将源程序中的语句按动态顺序,逐句逐段翻译成可执行代码,并立即执行
1.1 编译的过程
- 词法分析:对组成源程序的字符串进行扫描和识别,识别出一个具有独立意义的单词,将字符串变换成单词符号流
标识符:用户自己定义的变量名之类的 - 语法分析:在输出单词流的基础上,根据语言的语法规则分析单词流是否正确的组成各类语法单位
语法错误:但一个语句中就能发现 - 语义分析、中间代码生成:明确你是想要干什么,根据语义规则逐一翻译成等价的中间语言代码,通常称为语法制导翻译
语义错误:多条语句综合来看,发现的问题,比如说有变量名c没有定义 - 优化阶段:等价变换,使程序占用空间少,运行时间短
- 目标代码生成阶段:
C++是一门通用的语言: 不同的指令系统,都有这个语言的编译系统,意味着有多个不一样的编译器
编译的前端:词法分析、语法分析、语义分析中间代码生成
后端:目标代码生成
1.2 编译程序的实现
C
o
m
p
i
l
e
r
S
L
−
>
A
+
C
o
m
p
i
l
e
r
A
S
−
>
A
=
C
o
m
p
i
l
e
r
A
L
−
>
A
Compiler^{L->A}_{S}+Compiler^{S->A}_{A}=Compiler^{L->A}_{A}
CompilerSL−>A+CompilerAS−>A=CompilerAL−>A
其中A表示硬件指令系统,L和S表示一种高级语言
- 自展:若 S ⊆ L S \subseteq L S⊆L表示S是L的扩展
- 移植:
C S S − > B + C A S − > A = C A S − > B C_{S}^{S->B}+C_{A}^{S->A}=C_{A}^{S->B} CSS−>B+CAS−>A=CAS−>B
上面这是交叉编译器
C S S − > B + C A S − > B = C B S − > B C^{S->B}_{S}+C_{A}^{S->B}=C_{B}^{S->B} CSS−>B+CAS−>B=CBS−>B
2 文法与语言
- 词法:正规文法
- 语法:上下文无关文法
- 语义:上下文有关文法
2.1 定义
-
字母表:符号的非空有穷集合,用 Σ \Sigma Σ表示,通常程序语言的字母表是ASCII字符集
ASCII码一般只用前128个字符 -
字符串:有字母表 Σ \Sigma Σ 的字符所组成的有穷序列,称为字母表上的符号串/字符串/字,用 Σ ∗ \Sigma * Σ∗表示所有字符串的集合
-
空串:不包含任何字符的字符串,用 ϵ \epsilon ϵ 表示
-
字符串的长度:字符串中字符的个数
-
字符串的联结:x=ab, y=cd, xy=abcd
-
前缀与后缀:若z=xy,则x是z的前缀,y是z的后缀
-
子串:非空字符串x删去他的前缀和后缀所得的字符串是x的子串
-
语言:给定字母表 Σ \Sigma Σ,则 Σ \Sigma Σ上任意字符串集合,称为 Σ \Sigma Σ上的一个语言,记为 L L L
ϵ {\epsilon} ϵ是语言,但是{}不是语言 -
L ∪ M = { x ∣ x ∈ L , o r x ∈ M } L \cup M=\{ x|x \in L, or x \in M \} L∪M={x∣x∈L,orx∈M}
若 Σ = { a , b } \Sigma =\{ a, b\} Σ={a,b},则 Σ 2 = { a b , b a , a a , b b } \Sigma ^2 =\{ ab, ba, aa, bb\} Σ2={ab,ba,aa,bb},显然 Σ 3 \Sigma ^3 Σ3是长度为3的字符串的集合, Σ ∗ = Σ 0 ∪ Σ 1 ∪ Σ 2 ∪ Σ 3 ∪ . . . . . \Sigma ^* =\Sigma ^0 \cup \Sigma ^1 \cup \Sigma ^2 \cup \Sigma ^3 \cup..... Σ∗=Σ0∪Σ1∪Σ2∪Σ3∪..... -
文法:文法由四部分组成:终结符号集Vt、非终结符号集Vn、文法开始符、产生式的非空有限集合
其中终结符号集与非终结符号集之间不能有交集 -
<字母> L=a|b|c…
-
<数字> D=0|1|2…
-
<字母或数字> T->L|D
-
<字母数字串> S->T|ST(这是左递归,即产生式的左部与右部的最左面相同)
-
<标识符> 字母开始的字母数字串 I->L|LS
是IS的一个子集,所以I才能做文法的开始符 -
开始符:一定是非终结符号,一定不是 ϵ \epsilon ϵ
-
<产生式>左部一定是非终结符号,是终结符号集与非终结符号集的正闭包,即至少有一个非终结符号
-
终结符号:语言不可再分的基本符号,一般用小写字母表示
-
非终结符号:也称语法变量,一半用大写字母表示
-
文法符号串:终结符与非终结符的任意组合,一般用希腊字母表示
为什么C++中运算符重载不能改变符号的优先级和结合性??因为这些文法中都规定好了
a=b=c=0是右结合的 -
直接推出:如果 A − > δ A-> \delta A−>δ ,则称 α A β \alpha A \beta αAβ 可直接推导出 α δ β \alpha \delta \beta αδβ
左到右是推导,右到左是规约
- 上下文无关文法:文法的所有产生式都是 P − > α P-> \alpha P−>α,且P是非终结符, α \alpha α是文法符号串,且分法开始符至少在产生式的左部出现一次。
- 从文法的开始符,经过至少零步推导的多步推导,得到的文法符号串,称文法符号串为句型
- 全部都是又终结符号组成的句型是一个句子
所以说S一个文法的句型,但不可能是句子。句子是句型的一个特例。 - 语言:文法G所产生的句子的全体,称为文法G所产生的语言L(G)
- 文法的等价:如果文法产生的语言相同,则成为文法是等价的。
- 最左推导:每次推导,都是对句型的最左边的非终结符进行推导
我们将将最右推导称为规范推导
规范规约:从左往右将第一个规约起来,所以规范规约和最右推导互为逆过程 - 短语:就像“玩电脑”,如果 α β δ \alpha \beta \delta αβδ是一个句型,如果 S = > α A δ S=>\alpha A\delta S=>αAδ且 A = > β A=>\beta A=>β
- 直接短语:只经过一步推导得到的短语
- 句柄:一个句型的最左直接短语
- 分析树:一棵树可以表示一个句型的推导
- 子树:每个子树对应的符号串是这个句型的一个短语
- 二义性:一个句子有两种不同的规范推导或两颗不同的分析树
对于一个二义文法,如果能找到一个非二义文法,那么二义文法的二义性可以消除
3 词法分析
单词符号分为5类: 基本字、标识符、常数、运算符、界符
单词符号的编码:(单词类别,单词符号的属性值——符号表的入口地址)
输入预处理:剔除注释、多余的空白符、制表符换行符
扫描缓冲区:长度为2N,两个指针,只要单词长度小于N
正规集:正规表达式简称正规式,它表示的集合称为正规集,用L®表示
如果两个正规集相同,那么这两个正规式等价
若
Σ
=
a
,
b
\Sigma={a,b}
Σ=a,b,则R=a(a|b)*是正规式,正规集表示所有以a开头的字符串集。其中
∗
*
∗表示闭包
用文法来识别单词,太浪费时间。用正规式进行词法分析就比较省时间。
3.1 有限自动机
- 有限自动机FAM:一般的状态转换图,用五元组表示(有穷字母表,有限状态集,唯一的初态,终态集,单值映射)
确定性有限自动机DFA是非确定性有限自动机NFA的特例
如果初态是一个集合,那么一定是NFA——存在空字 ϵ \epsilon ϵ一定是NFA - 状态转换矩阵:表示状态转换图,m*n,m表示有限状态数量,n表示字母表数量
- FAM识别的语言:一个字符串能够从初始状态到终止状态,称为FAM所识别。为FAM所识别的字符串集称为FAM所识别的语言,记为L(M)
- 有限状态机的等价:如果L(M)=L(M’),称为M和M’等价
对于任意给定的NFA,一定存在一个DFA与之等价
**怎么找DFA??**用子集法
**终态怎么找?**原来NFA的终态去哪里了,DFA的终态就是啥 - 状态的等价:从si出发能识别的字符,和从sj出发能识别的字符,如果二者相同,称为是等价的,否则就是可区分的
- 简化的DFA:如果DFA没有无关状态,也没有彼此等价的两个不用状态
**如何简化呢?**生成DFA之后,先将所有的状态分成两类终态集A和非终态集B,然后读入一个元素a,看如果A中的状态中A1读a变到A3,而A2状态读到a变到B1,那么说明A1和A3是两个不同的,应该拆开。然后不断循环,直达大家都没法拆开,还在一起的状态都是等价的。
当然有可能初态和终态是同一个集合,那么意思就是最开始就没法分成两类,那么大家状态都是等价的
作业:3.10:a、d
3.2 一个例子
先是一个NFA
然后按照子集法进行“洗牌”,得到构造的表
根据这个表,重新命名得到DFA
然后再做DFA的简化,先分成终结集和非终结集,然后再分
大作业题目:中文文本中单词自动查错方法的探讨。
内容:
- 问题
- 主要难点
- 解决方法:出一个自然语言的算法,
- 可能需要什么工具:比如中文分词系统?比如词库?
提交形式:纸质论文报告,16周提交。
4 语法分析
语法分析有两种方法:自上而下或自下而上,来判断一个输入串是否是一个合法的句子。
自上而下:推到,一个非终结符,可能对应好几个产生式的右部。其实就是看能否从根结点出发,向下生长出一棵语法分析树,其叶结点组成的句子恰为输入串。
自下而上:规约,从叶结点出发,向上归结出以文法开始符为根结点的语法分析树
任何一个句子都可能存在最左推导或者最右推导,但任何一个句型不一定存在。但句柄有且仅有一个
规范推导:最右推导
从文法的开始符S出发,经过0步推导或者多步推导得到的任何的符号串是句型。
4.1 试探分析法
从根节点(开始符S)开始,生长分析树来进行匹配。如果
空字既不是终结符,也不是非终结符
每一个非终结符号对应一个函数,名字就是非终结符号,函数体就是他产生式的右部
**为什么叫递归下降分析器?**因为这些函数可以递归调用自己
缺点:一但文法改了,程序也就要改
4.2 递归下降分析法
要消除左递归
4.3 非递归的预测分析法
推导的方法,利用分析表,对应LL(1)文法
4.4 算符优先分析方法
- 算符文法:对于上下文无关文法G,如果它不含有 ϵ \epsilon ϵ产生式,并且产生式右部不含相连的非终结符
- 算符优先文法OPG:对于一个不含 ϵ \epsilon ϵ规则的算符文法G,如果任意两个终结符号a和b,优先级更高、更低、相同三个关系只有一个关系成立,则称G是算符优先文法
- FIRSTVT(A)={a|A->a…或A->Ba…,其中a是终结符,B是非终结符}其实是产生式右部的第一个终结符
- LASTVT(A)={a|A->…a或A->…aB,其中a是终结符,B是非终结符}其实是产生式右部的最后一个终结符
4.5 LR分析法
- 活前缀DFA:对于规范句型的一个前缀,如果它不含句柄以外的任何符号,则称这个前缀为活前缀
- 规范句型:最右推导每次得到的句型
在LR分析工作中的任一时刻,栈内的文法符号应该是某一规范句型活前缀
4.5.1 LR(0)文法
4.5.2 SLR(1)文法
如果同时存在右部相同的两个表达式:
A
−
>
α
A->\alpha
A−>α,
B
−
>
α
B->\alpha
B−>α,如果FOLLOW(A)和FOLLOW(B)不相交
若
a
∈
F
O
L
L
O
W
(
A
)
a \in FOLLOW(A)
a∈FOLLOW(A),则用
A
−
>
α
A->\alpha
A−>α归约
若
a
∈
F
O
L
L
O
W
(
B
)
a \in FOLLOW(B)
a∈FOLLOW(B),则用
B
−
>
α
B->\alpha
B−>α归约
相当于SLR(1)消除了LR(0)一些不该出现的情况
4.5.3 LR(1)文法不考
就怕SLR(1)方法中,两个FOLLOW集合有交集,这就有“归约-归约”冲突,因为我们只是简单的求一个FOLLOW集合,并不一定是规范归约的FOLLOW集合
所以用
[
A
−
>
α
.
,
a
]
[A->\alpha. ,a]
[A−>α.,a]只有当输入符号是a时才能用
A
−
>
α
A->\alpha
A−>α归约,而不是对FOLLOW(A)的所有符号
4.5.4 LALR(1)文法不考
LALR(1)分析表是LR(1)分析表的简化
同心的LR(1)项目集: 两个LR(1)项目集中,除了搜索符不同之外,核心部分都相同
LALR(1)分析法:将LR(1)项目集规范族中所有同心的项目集合并为一个,以减少项目集个数,所以说合并之后只可能有“归约-归约”冲突,新产生“移进-归约”冲突
那么多的分析表,有什么共同点?? 所有分析表关于移进的部分都一样,不同的是关于归约的部分
5 语义分析
5.1 赋值语句
要考虑简单变量、数组、a=a*(b+c)这些情况
对于102010的三维数组d1=10,d2=20,d3=10,A[y,z,k],low1=low2=low3=1,一个元素所占空间W=4,则x=A[y,z,k]的三地址语句序列为
- t1=y*20
- t1=t1+z
- t2=t1*10
- t2=t2+k
- t3=A-211
- t4=t2*4
- t5=t3[t4]
- x=t5
如果是A[y,z,k]=x则7、8变成
t3[t4]=x
5.2 布尔表达式
5.2.1 数值表示法
对布尔表达式 af 生成三地址代码,其中
a
<
b
a<b
a<b
c
=
d
c=d
c=d
e
>
f
e>f
e>f分别是一个布尔量,对每个布尔量都有四条三地址代码
5.2.2 解释法(短路法)
每个布尔量有两个goto表达式,在语义分析的时候通过三地址代码的顺序来表示优先级
可是不知道跳转的位置怎么办? 先空着,最后按照优先级一点一点合并,比如说true的两部分连成一条线,false的两部分连成一条线
对于a<b or c<d and not e<f
如果这个布尔表达式是用于if条件语句:
S->if(E) L=L+1; else L=L-1
那么后面增加:
106 t1=L+1
107 L=t1
108 goto 0
109 t2=L-1
110 L=t2
nextlist=108//这句话表示,108语句跳转的目标地址应该是nextlist,即下一个语句
6 优化
6.1 程序流图
到了中间结点一级,程序的数据流不好判断,需要构造程序流图。程序流图的每一个节点是 顺序执行 程序段,即基本块
不执行的语句在这一部分就被删掉了
6.2 无环有向图DAG
描述基本块内部,可以合并已知量(能常数就常数),删除多余代码(如果没有被引用)
6.3 循环优化
- 循环的定义:是程序流图中有唯一入口节点的强连通子图
- 必经结点集:首结点和他本身是每个结点的必经结点
怎么求必经结点集? 设D表示必经结点集,如果节点2的前驱节点是1和9,那么
D ( 2 ) = { 2 } ∪ D ( 1 ) ∩ D ( 9 ) D(2)= \{2\}\cup D(1) \cap D(9) D(2)={2}∪D(1)∩D(9)
变化一次就要再循环,直到大家都不变
回边是什么? 如4是5的必经节点集中的点,则5->4是回边
步骤总结一下
- 找基本块
- 找边,形成程序流图
- 局部优化:基本块划分的时候就能优化,删除掉不可能执行到的代码
- 循环优化:循环优化之前先要判断出是否是循环,循环优化的细节不要求
7 代码生成
代码生成的输入有两部分:一个是前端生成的中间代码,及三地址代码,一个是符号表
- 活跃信息:出了基本块之后变量是否被引用
- 待用信息:在基本块内某一变量下一个引用点
[例8.1]四行代码
t=a-b
u=a+c
v=a-t
w=v+u
转换出二地址代码
mov a, R0
sub R0, b
mov a, R1
add R1,c
mov R1, u
mov a, R1
sub R1,R0
add R1,u
mov R1,w
8 复习课
- 自展和移植
- 给定文法,描述这个文法对应的语言
- 给定语言,描述这个语言对应的文法
- 给定文法和句型,确定该句型的短语、直接短语、句柄和短素语
- 正规式->最简DFA
- 正规式->NFA
- NFA->DFA
- 化简
- 预测分析表、算符优先分析表、LR(0)、SLR(0)
- 综合语句的三地址代码翻译
- 赋值语句
- 数组元素
- 条件语句
- 循环语句
- 程序流图的描述(基本块的划分、有向边)
- 局部优化DAG方法
- 循环体查找(必经结点集、回边)
- 待用信息的确定和目标代码的生成