实验内容:https://github.com/JLChenode/PL0_compiler.git
还可以采用“自编译方式”产生编译程序。
方法是,先对语言的核心部分构造一个小小的编译程序(可用低级语言实现),再以他为工具构造一个能够编译更多语言成分的较大编译程序。
这种通过一系列的自展途径而形成编译程序的过程叫做自编译过程。
总结:第一章重点是对编译器的结构有个整体的了解,主要是上述编译五步,及符号表管理,错误管理
程序语言主要由语法和语义两方面定义
语言的语法是指这样一组规则
- 词法规则:是指单词符号的形成规则
标识符,基本字,常数,算符,界符
正规式和有穷自动机是描述词法结构和进行词法分析的有效工具- 语法规则,语法规则规定了如何从单词符号形成更大的结构(即语法单位),换言之,语法规则是语法单位的形成规则
下推自动机理论和上下文无关文法是我们讨论语法分析的理论基础
语义是指这样的一组规则,使用它可以定义一个程序的意义。
我们采用的方法为:基于属性文法的语法制导翻译方法。语言的语法定义是非常重要的。本节将介绍语法结构的形式描述问题
文法是描述语言的语法结构的形式规则(即语法规则)。
上下文无关文法是这样一种文法,它所定义的语法范畴(或语法单位)是完全独立于这种范畴可能出现的环境的,(即处理这种语言时,不必考虑上下文,不能用来描述实际自然语言)
包括四个组成部分:
从左至右逐个字符地对源程序进行扫描,产生一个个单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。
1.关键字(保留字或基本字):while, if
2.标识符:用来表示各种名字
3.字面常数:256,3 .14,true, ‘abc’
4. 运算符:如,+、-、*、/ 等等
5.分界符:如逗号,分号,冒号等
词法分析器输出的单词符号常常表示为二元式:
(单词种别,单词符号的属性值)
单词符号的识别
一个状态转换图可用于识别(或接受)一定的字符串。
PL0 语言
有限次使用上述三步骤而定义的表达式才是 ∑ \sum ∑ 上的正规式,仅由这些正规式表示的字集才是 ∑ \sum ∑上的正规集。
所有词法结构一般都可以用正规式描述。
若两个正规式所表示的正规集相同,则称这两个正规式等价。
不确定的有限自动机(简称NFA)
确定的有限自动机(简称DFA)
DFA可以表示为状态转换图
从状态图中看NFA 和DFA的区别:
NFA确定化成DFA 子集法
引进新的初态结点X和终态结点Y.
从X到S0中任意状态结点连一条 ϵ \epsilon ϵ箭弧, 从F中任意状态结点连一条 ϵ \epsilon ϵ箭弧到Y。
S0是初态节点集合,F是终态节点集合
设字母表只包含两个a和b,我们构造一张表:
首先,置第1行第1列为 ϵ \epsilon ϵ-closure({X})求出这一列的Ia,Ib;
然后,检查这两个Ia,Ib,看它们是否已在表中的第一列中出现,把未曾出现的填入后面的空行的第1列上,求出每行第2,3列上的集合…
重复上述过程,知道所有第2,3列子集全部出现在第一列为止。
现在把这张表看成一个状态转换矩阵,把其中的每个子集看成一个状态。
这张表唯一刻划了一个确定的有限自动机M,它的初态是 ϵ \epsilon ϵ -closure({X}) ,它的终态是含有原终态Y的子集。
不难看出,这个DFA M与M’等价。
这里实际上,还可以对,DFA优化,比如说,终态,3,4,5,6合并成一个状态
*** DFA 的最小化算法->下面***
正规文法和正规文法存在一个有限自动机FA与其等价
定理:
1.对每一个右线性正规文法G或左线性正规文法G,都存在一个有限自动机(FA) M,使得L(M)=L(G)。
2.对每一个FA M,都存在一个右线性正规文法GR和左线性正规文法GL,使得L(M)=L(GR)=L(GL)。
DFA的化简
把M的状态集划分为一些不相交的子集,使得任何两个不同子集的状态是可区别的,而同一子集的任何两个状态是等价的。最后,让每个子集选出一个代表,同时消去其他状态。
具体做法: 对M的状态集进行划分
分析方法:递归子程序法、预测分析法,(一般都要回溯)
从文法的开始符号出发,向下推导出句子;
试图用一切可能的办法,从文法开始符号出发,
自上而下地为输入串建立一课语法树;(寻找一个最左推导)
此分析过程本质上是一种试探过程
消除直接左递归:BNF表示法或者下面:
将 A → A α|β 替换为
A→βA’ 和
A’→ αA’|ε
间接左递归,把终结符代入法
防止回溯,则需要计算first集, follow集
脑海中思考一遍,,开始符号的follow集首先给他一个‘#'
某些非LL(1)文法改写为LL(1)文法
并非一切非LL(1)的文法都能改写成LL(1)的。
递归子程序法
预测分析程序法
使用一张分析表和一个栈来实现LL(1)分析
预测分析表M[A,a]是一个矩阵,行代表非终结符,列代表终结符或‘#’;矩阵元素代表对应的产生式或出错标志栈STACK中存放文法符号,栈底先放一个结束符‘#’
预测分析表构造算法:
1、计算文法的每个非终结符的FIRST集和 FOLLOW集
2、1) 对于每一产生式 A→ α \alpha α ,执行 2)
2) 对于FIRST (α)中的每一终结符a(或$),
将 A→ α 填入 M[A,a]
3) 若ε ∈ FIRST (α),则对b ∈ FOLLOW(A)将 A→ α 填入 M[A,b]
4)将所有无定义的 M[A,b] 标上出错标识。
例子,作业4 第6页 第四章这种题出的概率很大
所谓自下而上分析法就是从输入串开始,逐步进行“归约”,直至归约到文法的开始符号;或者说从语法树的末端开始,步步向上“归约”,直到根结。
“移进-归约”思想
规范归约是关于是一个最右推导的逆过程
最左归约 规范推导
算符优先分析
归约即计算表达式的值。归约顺序不同,则计算的顺序也不同,结果也不一样。
如果规定算符的优先次序,并按这种规定进行归约,则归约过程是唯一的。
起决定作用的是相邻的两个算符之间的优先关系。
所谓算符优先分析法就是定义算符之间的某种优先关系,借助于这种关系寻找“可归约串”和进行归约。
定义:
算符文法: 它的任一产生式的右部都不含两个相继(并列)的非终结符
算符文法,终结符对的优先关系计算
简单来说, 就是在一个句子的语法树中, 如果两个终结符在语法树的同一层,
则先找到的终结符优先级等于后找到的终结符的优先级
如果两个终结符在语法树的不同层, 层次高的终结符的优先级高于层次低的终结符的优先级
算符优先文法: 如果一个算符文法G中的任何终结符对(a,b)
至多只满足下述三关系之一:a=·b,a<·b, a·>b
从算符优先文法G构造优先关系表的算法。
通过检查G的每个产生式的每个候选式,可找出所有满足a=·b的终结符对。
可用下面两条规则来构造集合FIRSTVT§:
有了优先表,就开始,算符优先分析算法
主要是找最左素短语
实际例子,课件51页,见计算题总结文档,第3题
5.3 LR分析器
规范归约的关键问题是寻找句柄.
LR分析方法:把"历史"及"展望"综合抽象成状态;由栈顶的状态和现行的输入符号唯一确定每一步工作
• LR分析器的核心是一张分析表:
ACTION[s,a]:当状态s面临输入符号a时,应采取什么动作.
GOTO[s,X]:状态s面对文法符号X时,下一状态是什么
• 每一项ACTION[s,a]所规定的四种动作:
定义:对于一个文法,如果能够构造一张分析表,使得它的每个入口均是唯一确定的,则这个文法就称为LR文法。 如果能用一个每步顶多向前检查k个输入符号的LR分析器进行分析,则这个文法就称为LR(k)文法.
以下每种方法都要先构造DFA,用来识别活前缀,再画表
A . 称为"归约项目"
归约项目 S’ . 称为"接受项目"
A .a (aVT) 称为"移进项目"
A .B (BVN) 称为"待约项目".
LR0 文法G的每个产生式的右部添加一个圆点称为G的LR(0)项目
1 构建拓广文法,并编号 (2,3,4)可以合起来写
2 写出文法的所有LR0项目,3 写出文法的LR0项目集规范族
4 写出识别活前缀的DFA(使用项目集规范族)
5,构造分析表 P109
区别其他:在归约时,即对于A->α. 属于状态Ik,(产生式是文法的第j个产生式)
把规约表中,ACTION[k, a]全部置为 “ rj”
如果分析表中,不存在冲突,表示文法是一个LR0文法
或者直接判断项目集中,存在一个项目集中同时含义移进项目和归约项目,也得出冲突
6,根据分析表对句子进行分析
SLR(1)
步骤,1,2,3,4与LR0完全一致
5 在构造分析表时
区别:若项目A→·属于Ik,假定A为文法G的第j个产生式;
那么,对任何终结符a,aFOLLOW(A),置ACTION[k,a]为 “rj”;
如果分析表中,不存在冲突,表示文法是一个SLR(1)文法
6,根据分析表对句子进行分析
LR1 这里重新定义项目
每个项目的一般形式是[A→·, a1a2…ak] ,
这样的一个项目称为一个LR(k)项目。项目中的 a1a2…ak 称为它的向前搜索符串(或展望串)
步骤:
按上述算法构造的分析表,若不存在多重定义的入口(即,动作冲突)的情形,则称它是文法G的一张规范的LR(1)分析表
LALR
如果两个LR(1)项目的状态集,除了搜索符号之外,是相同的,则把这两个项目集合并
同心集的合并不会产生新的“移进-规约”冲突,但有可能产生新的“规约-规约”冲突
P117
第 6 章,第 8 章,第 9 章:不考
翻译模式看书,
10.1,10.2
优化原则:3条
基本优化技术:删除公共子表达式、复习传播、删除无用代码,
(设计循环的优化)代码外提,强度削弱,删除归纳变量
基本块:
划分基本块,构造程序流图
基本块的DAG构造
利用DAG进行基本块内优化
10.4 数据流分析
活跃变量:
一个变量在基本块入口处是活跃的,则一定有:或者它在基本块的LiveUSe集合中,或者它在基本块的出口处是活跃的且在基本块中没有重新定值
有LiveIn(B) = LiveUse(B) ∪ ( LiveOut(B) – Def(B) )
所以在计算每个基本块的活跃变量时,要倒着计算,因为最后一个基本快 的LiveOut为空
11.3.1计算待用信息:
算法:还是看例子学习更好P314
P316
10.5DAG图 结点重排算法 P323
使表达式的右部,从右往左计算,