编译器:阅读某语言编写的程序,把该程序翻译成一个等价的,用另一种语言(目标语言编写的程序)
解释器:解释器是另一种比较常见的语言处理器,解释器不通过翻译生成目标程序。从用户角度看,解释器直接直接用用户的输入执行源程序中的操作。
Java语言处理器结合了编译和解释过程。
练习:
编译器和解释器的区别:看定义
编译器相对于解释器的优点是什么?解释器相对于编译器的优点是什么?
编译器是针对于某一个特定的处理器对源程序进行翻译的,所以它能够将一次性翻译成 目标代码,再在处理器上运行,这种形式的程序运行显然要比翻译一部分命令执行一部分命令要快,这也正是相对于解释器的优点所在。 对于解释器而言恰恰相反,它并不是将源程序一次性翻译成目标代码而是翻译一部分再执行 一部分,这种做法有两好处,首先它使得运行变得动态性,即对于相同的小部分代码它不用再解释,这使得编译的效益提高。其次是这种解释的机制使得相同的程序在不同的处理器上运行成为可能,通过解释可以在不同的处理器上执行。
编译器产生汇编语言的好处:汇编语言比较容易输出和调试,再由汇编器处理后生成可重新定位的机器语言。
把一种高级语言翻译成为另一种高级语言的编译器称为源到源的翻译器。编译器使用 C 语言做为目标语言有什么好处? :把C语言作为目标语言的好处是一方面C语言被广泛的应用,使得编译后的目标语言能 够被大多数人理解。另一方面c语言已经有比较高效完备的编译器,便于再程序编译成其它 语言的目标程序。
词法分析、语法分析、语义分析、中间代码生成器、机器无关代码优化器、代码生成器、机器相关代码优化器。
各个步骤:
词法分析:读入程序字符流,组织为有意义的词素序列
position = initial + rate * 60:
<=> <+> <*> <60>
语法分析:使用词法分析器的各个词法单元的第一个分量来创建树形的中间表示,例子看图
语义分析:使用语法树和符号表中的信息检查源程序是否和语言定义的语义一致(类型检查、自动类型转换)
中间代码生成:(三地址代码表示)
代码优化
代码生成:代码生成器以中间代码为输入,并把它映射到目标语言
符号表管理:
本章的重点是编译器的前端,主要是词法分析、语法分析、中间代码生成。
介绍一种用于描述程序设计语言语法的表示方法:上下文无关法,简称“文法”。文法用于组织编译器的前端。
2.2.1 文法定义
一个上下文无关文法由四个元素组成:
2.2.3 语法分析树
符号表(Symbol Table)是一种编译器用于保存有关源程序构造的各种信息的数据结构。
为每一个作用域都建立一个符号表来实现作用域。每个带有声明的程序块都会有自己的符号表,这个块中的每一个声明都在此符号表中有一个对应的条目
符号表条目是在分析阶段由词法分析器、语法分析器、语义分析器创建并使用的。
2.7.1 为每个作用域建立一个符号表
**最近嵌套原则:**一个标识符x在最近声明x的作用域中,也就是说,从x出现的块开始,从内向外检查各个块时找到的第一个对x的声明。
2.7.2 符号表的使用
The Use of Symbol Tables3 Lexical Analysis
使用符号表翻译带有语句块的语言:
本章讨论如何手动的创建一个词法分析器
介绍词法分析器之前,现介绍正则表达式。正则是一种很方便描述词素模式的方法。(首先转换为不确定有穷自动机,再转换为确定有穷自动机)
这里状态2、3、4是一组的,所以挑选一个代表,如果某个元素指向组内元素,直接指向代表就可以了
词法分析器与语法分析器的交互
交互是由语法分析器调用词法分析器实现的
词法分析器在编译器中负责读取源程序,因此还会完成一些识别词素之外的其他任务。
有时,词法分析器也可以分为两个级联的阶段
3.1.1 词法分析和语法分析
编译的分析阶段分为词法和语法分析两部分的原因:
加快源程序读入速度的方法。
双缓冲区方案,这种方案能够安全的处理向前看多个符号的情况。然后我们将考虑一种改进方法,,这种方法使用“哨兵标记”来节约用于检查缓冲区末端的时间。
3.2.1 缓冲区对
一种特殊的缓冲技术来减少用于处理单个输入字符的时间开销,一种重要的机制就是利用两个交替读入的缓冲区。
3.2.2 哨兵标记
上面的做法,每一次读入字符都需要进行两次测试,另一种优化方法:“哨兵标记”,在缓冲区末端添加“哨兵字符”—“eof”,把两个测试合二为一。这样的话大部分时间我们只需要执行一次测试,除非真的在词素末尾或者缓冲区末尾才会执行更多测试。
3.3.3 正则表达式
在上例中,我们可以首先给出字母和数位集合的名字,然后使用并、连接、闭包这些运算符来描述标识符。例如,上例中第5项差不多就是C语言中可以允许的标识符。这种处理方法非常有用。因此,人们常常使用一种称为正则表达式的表示方法来描述语言。
3.3.4 正则定义
3.3.5 正则表达式的扩展
3.3.6 练习
长度为n:
3.4.1 状态转化图
作为构造词法分析器的一个中间步骤,我们首先讲模式转换成具有特定风格的流图,称为“状态转化图”。本节用手工的方式将正则表达式表示的模式转化为状态转换图。
3.4.4 基于状态转换图的词法分析器的体系结构
我们考虑几种将状态转换图的代码集成到词法分析器的方法:
有穷自动机和状态转换图的区别:
确定的和不确定的有穷自动机能识别的语言的集合是相同的。事实上,这些语言的集合正好是能够用正则表达式描述的语言的集合。这个集合中的语言称为正则语言。
3.6.1 不确定的有穷自动机
不管是NFA还是DFA,都可以转换为一张转换图。途中的结点是状态,带有标号的边表示自动机的转换函数。从状态s到状态t存在一条标号为a的边当且仅当状态t是状态s在输入a上的后续状态之一。
3.6.2 转换表
可以将NFA表示为一张转换表
优点:很容易的确定和一个给定状态和一个输入符号相对应的转换。
缺点:如果输入字母表很大,确大多数输入字符上没有转换的时候,转换表需要占用大量空间
初始状态用一个开始箭头指向,接受状态是一个双圈
3.6.3 自动机中输入字符串的接受
NFA不接受开始的空串:
3.6.4 确定的有穷自动机
确定的有穷自动机(DFA)是不确定的有穷自动机(NFA)的一个特例
一个DFA的例子:
给定的输入字符,出边是唯一的
NFA对于一个输入符号可以旋转不同的转换,它还可以执行输入e上的转换,甚至可以选择是对e或事对真实的输入读好执行转换,因此对NFA的模拟不如对DFA的模拟直接。于是,我们需要将一个NFA转换为一个识别相同语言的DFA
这一节介绍如何把NFA转DFA。然后利用“子集构造法”给出一个直接模拟NFA的算法。
3.7.1 从NFA到DFA的转换
基于自动机的词法分析方法的处理能力部分源于如下事实:对于一个真实的语言,它的NFA和DFA的状态数量大致相同,状态数量呈指数关系的情形尚未实践中出现过。
子集构造法
3.7.2 NFA模拟
P115
3.7.3 NFA模拟的效率
P115 - p116
算法总效率:O(k(n + m))
3.7.4 从正则表达式构造NFA
将正则表达式转化为一个NFA的McManughton-Yamada-Thompson算法
基本原则、归纳原则
P117-119
3.7.5 字符串处理算法的效率
程序设计语言构造的语法可以使用上下文无关文法和BNF表示法来描述。
一些例子:
- a^n b^n 的文法:
S → aSb
S → ab
- a^m b^n (2n >= m >= n >=1)
S → aSb | ab
S → aaSb | aab
- a^n · b^(n+2)·c (n >= 1)
S → aAbbbc
A → aAb | ε
- a^n · b^(n+2)·c (n >= 0)
S → Abbc
A → aAb | ε
本节研究算术表达式的典型文法。
语法分析:接受一个终结符号串作为输入,找出从文法的开始符号推导出这个串的方法。如果不能从文法的开始符号的呆该终结符号串,则报告该终结符号串中包含的语法错误。
4.1.1 语法分析器的作用
4.1.2 代表性的文法(Representative Grammars)
(Context-Free Grammars)简称“文法”
正则表达式不能表示递归,上下文文法可以用if表达嵌套和递归
4.2.1 上下文无关文法的正式定义
p125
4.2.3 - 4.2.5 推导 - 二义性
自顶向下:从开始符号出发,每个重写步骤把一个非终结符号替换为它的某个产生式的体。
自底向上:和一种被称为“最右”推导的推到类型相关。每一步重写的都是最右边的非终结符号。
文法G:
最左推导、最右推导(最右推导又叫做规范推导)
自底向上和“最右”推导相关。
因为在语法分析树和最左推导/最右推导之间存在着一种一对一的关系。每一个语法分析树都和唯一的最左推导及最右推导相关联。
二义性文法:对同一个句子又多个最左推导和最右推导。
我们的任务:需要使用“消二义性规则来“抛弃”不想要的语法分析树”
文法是比正则表达式表达能力更强的表示方法。
A → Aα | β
左递归产生式替换成非左递归产生式:
A → βA'
A' → αA' | ε
这个替换不会改变可以从A推导得到的串的集合。
一般情况(可以处理立即做递归):
例如:对于没有环或者 ε 产生式的文法G:
考虑文法:
S → Aa | b
A → Ac | Sd | ε
转换:
A → A c | A a d | b d | ε
结果:
S → A a | b
A → b d A' | A '
A' → c A' | a d A' | ε
提取左公因子是一种文法转换方法,可以产生适用于预测分析技术或自顶向下分析技术的文法。(推迟决定)
A → αβ1 | αβ2
转换成:
A → αA'
A' → β1 | β2
自顶向下:从根节点开始,按照先根次序(深度优先),创建这棵语法分析树的各个节点,自顶向下语法分析也可以被看作寻找输入串的最左推导过程。
关键问题确定一个非终结节点,应该使用哪一个产生式。
对于有些文法,我们可以构造出向前看k个输入符号的预测分析器,这一类文法有事也称为LL(k)文法类。
FIRST和FOLLOW集,我们可以构造出“预测分析表”,它说明了如何在自顶向下语法分析过程中选择产生式。这些集合也可以用于自低向下的语法分析。
4.4.2:FIRST、FOLLOW计算。4.4.3:LL(1)文法。4.4.4:非递归的语法分析方法。4.4.5(不考):自顶向下语法分析过程中的错误恢复问题。
定义S_文法:
解决了回溯问题,但是不允许出现空串
- 每个产生式的右部都以终结符开始
- 同一非终结符的各个候选式的首终结符都不同
例:
总的递归下降程序:(每一个非终结符都要有一个程序)
PROGRAM程序:
DECLIST程序:
DECLISTN程序:
STLIST和STLISTN省略,TYPE对应过程:
产生式的可选集SELECT(A → β)
SELECT(A → aβ) = {a}
SELECT(A → ε) = FOLLOW(A)
定义q_文法:
解决了回溯问题,且右部可以出现空串
- 每个产生式的右部或为 ε ,或以终结符开始
- 具有相同左部的产生式有不可相交的可选集SELECT
FIRST(α) 被定义为可以从α推导得到的串的首符号的集合。(串首终结符集)
FIRST(α):
如果α 可以推出空串(零次或多次推导),那么 ε 也在FIRST(α)中
重新定义SELECT(A → α)可选集:
- 如果 ε ∉ FIRST(α),那么SELECT(A → α) = FIRST(α)
- 如果 ε ∈ FIRST(α),那么SELECT(A → α) = (FIRST(α) - { ε }) U FOLLOW(A)
FOLLOW(A) 被定义为可能在某些句型中紧跟在A右边的终结符号的集合。
什么时候才能使用A → ε?,A的FOLLOW集
如果A是某个句型的最右符号,则将结束符‘$’添加到FOLLOW(A)中
计算FIRST集合FOLLOW集:P141的例子:
重要
如果 A → Bβ , 那么 FIRST(A)包含FIRST(B),如果FIRST(B)包含ε,那么FIRST(A)还包含FOLLOW(B)
如果 E → ... , 那么FOLLOW(E)包含‘$’
如果 A → BE , 那么FOLLOW(A)包含FOLLOW(E),同时FOLLOW(B)应该包含FIRST(E), 如果FIRST(E)包含ε,那么FOLLOW(B)还包含FOLLOW(B)
注意:FIRST集可以包含 ε, FOLLOW集不能有 ε
A → Bcb
B → aC
C → abC | ε
概念:
移入 - 规约例子:
文法:
E → E + E
E → E * E
E → (E)
E → id
输入串:id + (id + id)
栈 | 剩余输入 | 动作 |
---|---|---|
$ | id+(id+id)$ | |
$id | +(id+id)$ | 移入 |
$E | +(id+id)$ | 归约 |
$E+ | (id+id)$ | 移入 |
$E+( | id+id)$ | 移入 |
$ E+(id | +id)$ | 移入 |
$ E+(E | +id)$ | 归约 |
$E+(E+ | id)$ | 移入 |
$E+(E+id | )$ | 移入 |
$E+(E+E | )$ | 归约 |
$E+(E | )$ | 归约 |
$E+(E) | $ | 移入 |
$E+E | $ | 归约 |
$E | $ | 归约 |
$ |
LR分析法
LR分析法的基本原理
例:S→ bBB
S → ·bBB 移进状态
S → b·BB 待约状态
S → bB·B 待约状态
S → bBB· 归约状态
可知:LR文法的分析表是比较关键的:不同文法,构造方法不同
LR(0):
k个符号,有k+1个项目,项目描述了句柄识别的状态
**增广文法:**保证只有一个接受状态
LR(0)自动机:
CFG不总是LR(0)文法
语法树浓缩了转换所需的所有信息,比分析树效率更高,也称为抽象语法树。给出文法和特定串,完成推导,画出分析树,语法树是我们需要掌握的内容
语法树可以屏蔽文法描述:
同属链接:
二义性文法:
消除二义性:
S → SS | (S) | ()
S → S A | (S) | ()
A → (S) | ()
悬挂else:【视频3-10】
BNF 和 EBNF,语言分类,局限:【视频3-11】
能被5整除的文法:
S → A0 | A5 | 5
A → AB | C
B → C | 0
C → 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
自顶向下分析主要学习递归下降分析和LL1分析,重点掌握first集合和follow集合的计算,LL1分析表的构建
递归下降:【视频4-1】
对算术文法进行递归下降:
EBNF左结合:(看视频)
右结合:
例题:
设有文法:G(S):
S → +SA | *SA | n
A → (S)| n
写出递归下降伪代码:
// 递归下降:
function A:
begin:
case token of:
(:
match('(');
S;
match(')');
n:
match('n')
else error;
end case;
end A
function S:
begin:
case token of:
+:
match('+');
S;
A;
*:
match('*')
S;
A;
n:
match('n')
else error;
end case;
end S
function match:expectedToken;
begin
if token = expectedToken then
getToken();
else error
end if;
end match;
LL(1)分析过程 【视频4-2】
构造分析表【视频4-3】
设有文法G(S):
S → aaAaB
A → bAa | Ba
B → cb | ε
请构造LL(1)分析表并分析aaaa是否是该文法可以识别的串。
a | b | c | $ | |
---|---|---|---|---|
S | S→aaAaB | |||
A | Ba | bAa | Ba | |
B | B→ε | B→cb | B→ε |
设有文法G(S):
S → AB
A → aAb | ε
B → ba | ε
请构造LL(1)分析表,并分析串abb是否是该文法可以接受的串?
a | b | $ | |
---|---|---|---|
S | S→AB | S→AB | S→AB |
A | A→aAb | A→ε | A→ε |
B | B→ba | B→ε |
step | Stack | Input | Action |
---|---|---|---|
1 | $S | abb$ | S→AB |
2 | $BA | abb$ | A →aAb |
3 | $BbAa | abb$ | m |
4 | $BbA | bb$ | A→ε |
5 | $Bb | bb$ | m |
6 | $B | b$ | B→ba |
7 | $ab | b$ | m |
8 | $a | $ | x |
LL(1)限制:
消除左递归(1):消除直接左递归 【视频4-4】
S → S*T | S/T | T | (T)
改为:
S → TS' | (T)S'
S' → *TS' | /TS' | ε
消除左递归(2):消除间接左递归【 视频4-5】
S → Qc | c
Q → Rb | b
R → Sa | a
改为:
S → Qc | c
Q → Rb | b
R → bcaR' | caR' | aR'
R' → bcaR' | ε
消除左因子: 【视频4-6】
S → (T) | a + S | a
T → T,S | S
改为:
S → (T) | aB
B → +S | ε
T → ST'
T' → ,ST' | ε
First集【视频4-7】
写出下面文法的First集:
S → (T)| aS'
S' → +S | ε
T → T' | S'
T' → ,ST' | ε
First(S) = { ( a }
First(S') = { + ε }
First(T') = { , ε }
First(T) = {+ , ε}
rules | Pass1 | Pass2 |
---|---|---|
S → (T) | F(S)= {(} | |
S →aS’ | F(S) = {(, a} | |
S’ → +S | F(S’) = {+} | |
S’ → ε | F(S’) = {+, ε} | |
T → T’S’ | F(T) <= F(T’)-{ε}, F(T)<=F(S’) | F(T) = {+, ε} |
T’ → ,ST’ | F(T’)={,} | |
T’ → ε | F(T’)={, , ε} | F(T)={+, , ,ε} |
Follow集【视频4-9、4-10】
根据First、Follow构造LL(1)分析表,p143
【视频5-1、5-2、5-3、5-4】
需要增加产生式:S’ → S
构造NFA
移进、待约、规约
LR:从左到右扫描,反向构造最右推导
SLR:在Follow集中:规约,不在:移入。
如果有交集:要使用LR(1)文法
LR(0) 会出现移入-规约冲突
↓
SLR 根据Follow集进行分类
↓
LR(1) 引入展望符,将SLR的归约范围进行细化
↓
LALR 合并同心,形式上与LR(1)相同,大小与LR(0)相同,分析能力SLR
SDD、SDT
--------------------------- The End ----------------------------