开学了,我又来啦
教材:《编译原理与实践》
概述
词法分析->语法分析->语义分析->生成与优化
源程序->编译器->(静态分析)->目标语言->目标机器->(动态计算)->计算结果
精度无限时,计算结果理论上是一致的,实际上有差别。
编译与解释的区别
编译器流程,例:
a[index] = 4 + 2
1.扫描程序或词法分析程序(The Scanner)
输入:字符串序列“a[index] = 4 + 2”
输出:记号序列 a , [ , index , ],=, 4, + , 2
注意:每个记号有1个或多个字符组成;空格和换行符,自动跳过。
2.语法分析程序(The Parser)
输入:记号序列
输出:分析树->语法树(抽象语法树)
3.语义分析程序(The Semantic Analyzer )
输入:抽象语法树
输出:符合语义的抽象语法树
4.优化(The Optimizer )
5.中间代码生成(三元式)
7.目标代码生成与优化
编译器使用的数据结构
词法分析
词法分析器的主要作用是将输入的源程序(字符流)转换或切分成记号流(编译器内部定义的数据结构)。
实现方法:手动or通过生成器(申明式规范->Lex, fLex, jLex0>词法分析器)自动实现
流程:正则表达式-(汤姆逊算法/自底向上)>NFA-(子集构造,列表)>DFA-(子集合并分裂)>minDFA->词法分析器
扩展:
- 能否实现自动纠错?
- 输入缓冲太长怎么办?(双缓冲)
- 如何判断是否到末尾?(加EOF)
记号(词法单元)
记号-示例-描述
const-const-const
if-if-if
relation-<,<=,=,<>,>=,>
id-pi,count,d2-字母打头的字母数字串
num-3.14,0,6.2e23-数字常数
literal-“core dumped”-""之间的字符
这是一个简单的实现(tiny c),c比之更复杂
数据结构
typedef enum{//枚举列出所有的记号类型
/* book-keeping tokens */
ENDFILE, ERROR,
/* reserved words */
IF, THEN, ELSE, END, REPEAT, UNTIL, READ, WRITE,
/* multicharacter tokens */
ID, NUM,
/* special symbols */
ASSIGN, EQ, LT, PLUS, MINUS, TIMES, OVER, LPAREN, RPAREN, SEMI
} TokenType;
Typedef struct {
TokenType tokenVal; /* 记号类型 */
char *stringVal; /* 记号字符串 */
int numVal;
} TokenRecord;
正则表达式
正则表达式-菜鸟教程
正则表达式r由它所匹配的字符串集合来定义,该集合为由正则表达式生成的语言,L(r)。
-
语言组成:字符集与归纳规则
-
字符集: 符号的非空有限集合 ∑ \sum ∑
-
归纳定义:
空串是ε正则表达式,对应语言L(ε)={ε}
如果字符a(a∈ ∑ \sum ∑ ),那么a是正则表达式,对应语言L(a)={a}
如果r和s都是正则表达式,则以下也是正则表达式:
选择:r|s 语言:L(r|s) = L®∪L(s)
连接:rs 语言: L(rs) = L® L(s)
Kleene闭包:r* 语言:L(r*) = L(ε)∪L ( r)∪L(rr) ∪ …… -
运算的优先级: 闭包 > 连接 > 选择
-
正则表达式的等价性:如果两个正则表达式r和s表示同样的语言,则称r和s等价,记作:r = s
-
r|s = s|r
r(s|t) = rs|rt
r** = r* 幂等性
选择有交换律、结合律
连接有结合律,对选择可分配,但没有交换律
闭包具有幂等性 -
正则表达式的扩展
-
常用表达式:
digit=(0|1|2|…|9)= [0-9]
keyWords = if | while |for|…
letter = A|B|C|……|Z| a|b|c|……|z|_ = [A-Za-Z_]
id = letter(letter|digit)* 以字母或下划线开头的标识符
digits = digit(digit)* = d i g i t + digit^+ digit+ 至少一位数字
optionalFraction = .digits | ε 小数
optionalExponent = E(+ | - | ε) digits | ε 浮点数
number = digits optionalFraction optionalExponent=digits (. digits)? (E(+|-)? digits)?
例1:
∑
=
{
a
,
b
,
c
}
\sum =\{a,b,c\}
∑={a,b,c} ,则~a=b|c
例2:并不是所有字符串都可以用正则表达式表示,比如:由一个b及在其前后有相同数目的a组成的串S的集合(上下文相关)
有穷自动机
确定有穷自动机(DFA;Definite of Deterministic finite automation)
非确定有穷自动机(NFA;Non-deterministic finite automation)
有状态,用圆圈表示,状态之间可以用箭头表示转换,一个初态,多个终态。
DFA
-
用函数描述:
M = ( ∑ \sum ∑ , S, T, s0, A)
∑ \sum ∑输入字母表(终极符集合)
S 有穷状态集(非终极符集合)
T :S× ∑ \sum ∑→S 状态转换函数 是单值函数
s0∈S 唯一的初始状态
A ⊂ \subset ⊂S 终止状态集
-
用状态表(状态转移矩阵)描述(考试建议)
行表示状态,列表示输入字符,矩阵元素表示T(s,a)的值
例子:
代码实现:
{ starting in state 1 }
if the next character is a letter then
advance the input;
{ now in state 2 }
while the next character is a letter or a digit do
advance the input; { stay in state 2 }
end while;
{ go to state 3 without advancing the input}
accept;
else
{ error or other cases }
end if
state := 1;
ch := next input character;
while not Accept[state] and not error(state) do
newstate := T[state,ch];
if Advance[state,ch] then
ch := next input character;
state := newstate;
end while;
if Accept[state] then accept;
NFA
M = (
∑
\sum
∑ , S, T, s0, A)
∑
\sum
∑输入字母表(终极符集合)
S 有穷状态集(非终极符集合)
T :S×(
∑
\sum
∑U{ε})->℘(S) (S的幂集) 状态转换函数 多值函数 包含空边
S0∈S 初始状态集
A
⊂
\subset
⊂S 终止状态集
正则表达式->NFA
汤姆逊算法(自底向上)(适合考试?)
是一种递归构造的算法,是对正则表达式做归纳。
自顶而下的构造方法
NFA->DFA(子集构造)
符号合并:对任意字符a,如果有S1
→
a
\overset{a}{\rightarrow}
→aS2, S1
→
a
\overset{a}{\rightarrow}
→aS3,则把对应
的状态S2和S3合并{S2, S3}。
ε合并:如果有S1→S2,则把状态S2合并至状态S1中
操作:
首先找初态(ε合并),初态可能是一个集合
然后对初态列出经过符号后的状态,每次出现新状态时,加入队列(状态这一列表示尚未处理的状态,DFA这一列表示所有出现的状态)
当没有新增状态时,算法结束。包含原终止状态的状态集是新的终止状态
此时可将状态集合用新字母表示(也可以画出DFA图,但没有必要,因为还有下一步)
共有三种表示方法:函数、状态转移矩阵、图
抽象化:
定义1:状态集合I的ε-闭包(ε-closure)
状态集ε-closure(I)称为I的ε-闭包
令I是一个状态集的子集,定义ε-closure(I) 为:
① 若s∈I,则:s∈ε-closure(I);
② 若s∈I,则:从s出发,经过任意条ε弧,能够到达的任何状态都属于ε-closure(I)
定义2:状态子集。令I是NFA M’状态集的一个子集,a∈Σ,定义:
Ia= ε-closure(J) ,其中J = ∪T(I,a)。 (所有I经过a能去的状态及它们经过ε能去的状态)
①J是从状态子集I中的每个状态出发,经过标记为a的弧而达到的
状态集合。(与符号合并相关)
② Ia是状态子集,其元素为J中的状态,加上从J中每一个状态出发
通过ε弧到达的状态。(与ε合并相关)
算法:队列(BFS)/ DFS
表驱动的算法
算法结束后,可以重命名状态集,然后画出图
DFA极小化 DFA->min DFA
对于任一个DFA,存在一个唯一的状态最少的等价的DFA
一个有穷自动机可以通过消除多余状态(死状态)和合并等价状态(划分不等价状态),转换成一个最小的与之等价的有穷自动机。
如何算法找出死状态?bfs所有的从初态开始的连通图 ,没有被遍历到的是死状态。
等价状态:
状态s和t的等价条件是:
① 一致性条件:状态s和t必须同时为可接受状态或不接受状态
② 蔓延性条件:对于所有输入符号,状态s和t必须转换到等价的状态里
对于所有输入符号c,Ic(s) = Ic(t),若状态s、t对于c具有相同的后继,则称s,t是等价的。
任何有后继的状态和任何无后继的状态一定不等价
有穷自动机的状态s和t不等价,则称这两个状态是可区别的
方法:
- 分割法(自顶而下):把一个DFA(不含多余状态)的状态分
割成一些不相关的子集,使得任何不同的两个子集状态都是可区别的,而同一个子集中的任何状态都是等价的 - 状态集划分(自底向上):将所有DFA的终态与其它状态划分成两个子集G1与G2;分别从两个子集G1与G2(一般化:上次递归划分后的子集)中寻找等价状态进行化简
同时划分出好几个集合是可以的,不影响。
思考:会不会出现划分完需要合并的情况?划分的次序会和顺序有关。