编译原理
第一章 引论
1.1 语言处理器
-
编译器:阅读某语言编写的程序,把该程序翻译成一个等价的,用另一种语言(目标语言编写的程序)
-
解释器:解释器是另一种比较常见的语言处理器,解释器不通过翻译生成目标程序。从用户角度看,解释器直接直接用用户的输入执行源程序中的操作。
-
Java语言处理器结合了编译和解释过程。
- 预处理器:一个程序可能分割成很多块,存放在不同文件中,把源代码聚合在一起的程序叫预处理器。例如:宏。
- 汇编器:汇编语言翻译成机器语言。
- 链接器/加载器:一个文件中的代码可能指向另一个文件中的位置,链接器解决外部内存地址的问题。加载器把所有可执行的目标文件加载到内存中执行。
练习:
-
编译器和解释器的区别:看定义
-
编译器相对于解释器的优点是什么?解释器相对于编译器的优点是什么?
编译器是针对于某一个特定的处理器对源程序进行翻译的,所以它能够将一次性翻译成 目标代码,再在处理器上运行,这种形式的程序运行显然要比翻译一部分命令执行一部分命令要快,这也正是相对于解释器的优点所在。 对于解释器而言恰恰相反,它并不是将源程序一次性翻译成目标代码而是翻译一部分再执行 一部分,这种做法有两好处,首先它使得运行变得动态性,即对于相同的小部分代码它不用再解释,这使得编译的效益提高。其次是这种解释的机制使得相同的程序在不同的处理器上运行成为可能,通过解释可以在不同的处理器上执行。
-
编译器产生汇编语言的好处:汇编语言比较容易输出和调试,再由汇编器处理后生成可重新定位的机器语言。
-
把一种高级语言翻译成为另一种高级语言的编译器称为源到源的翻译器。编译器使用 C 语言做为目标语言有什么好处? :把C语言作为目标语言的好处是一方面C语言被广泛的应用,使得编译后的目标语言能 够被大多数人理解。另一方面c语言已经有比较高效完备的编译器,便于再程序编译成其它 语言的目标程序。
1.2 一个编译器的结构
- 编译器:分析部分和综合部分。
- 编译器的各个步骤
词法分析、语法分析、语义分析、中间代码生成器、机器无关代码优化器、代码生成器、机器相关代码优化器。
-
一个赋值语句的翻译过程
各个步骤:
-
词法分析:读入程序字符流,组织为有意义的词素序列
position = initial + rate * 60: <id,1> <=> <id,2> <+> <id,3> <*> <60>
-
语法分析:使用词法分析器的各个词法单元的第一个分量来创建树形的中间表示,例子看图
-
语义分析:使用语法树和符号表中的信息检查源程序是否和语言定义的语义一致(类型检查、自动类型转换)
-
中间代码生成:(三地址代码表示)
-
代码优化
-
代码生成:代码生成器以中间代码为输入,并把它映射到目标语言
-
符号表管理:
- 编译器构造工具
第二章 一个简单的语法制导翻译器
本章的重点是编译器的前端,主要是词法分析、语法分析、中间代码生成。
2.1 前言
2.2 语法定义(不做要求)
介绍一种用于描述程序设计语言语法的表示方法:上下文无关法,简称“文法”。文法用于组织编译器的前端。
2.2.1 文法定义
一个上下文无关文法由四个元素组成:
- 一个终结符号集合(if或者括号这样的符号单元)
- 一个非终结符号集合(每个非终结符号是终结符号的集合,,stmt或者expr)
- 一个产生式集合(箭头左边)
- 指定一个非终结符号为开始符号
2.2.3 语法分析树
2.7 符号表
-
符号表(Symbol Table)是一种编译器用于保存有关源程序构造的各种信息的数据结构。
-
为每一个作用域都建立一个符号表来实现作用域。每个带有声明的程序块都会有自己的符号表,这个块中的每一个声明都在此符号表中有一个对应的条目
-
符号表条目是在分析阶段由词法分析器、语法分析器、语义分析器创建并使用的。
2.7.1 为每个作用域建立一个符号表
**最近嵌套原则:**一个标识符x在最近声明x的作用域中,也就是说,从x出现的块开始,从内向外检查各个块时找到的第一个对x的声明。
- 符号表链的一个例子(符号表其实就代表了一种环境(Env)):
Java实现的链接的符号表环境代码:P72
2.7.2 符号表的使用
The Use of Symbol Tables3 Lexical Analysis
使用符号表翻译带有语句块的语言:
第三章 词法分析(Lexical Analysis)
-
本章讨论如何手动的创建一个词法分析器
-
介绍词法分析器之前,现介绍正则表达式。正则是一种很方便描述词素模式的方法。(首先转换为不确定有穷自动机,再转换为确定有穷自动机)
-
- 正则表达式转NFA
-
- NFA转DFA
-
- DFA最小化
这里状态2、3、4是一组的,所以挑选一个代表,如果某个元素指向组内元素,直接指向代表就可以了
-
- 写代码
3.1 词法分析器的作用
-
词法分析器与语法分析器的交互
-
交互是由语法分析器调用词法分析器实现的
-
词法分析器在编译器中负责读取源程序,因此还会完成一些识别词素之外的其他任务。
- 任务一:过滤源程序中的注释和空白
- 任务二:将编译器生成的错误信息和源程序的位置联系起来
-
有时,词法分析器也可以分为两个级联的阶段
- 扫描阶段(处理空格等,不生成词素)
- 词法分析阶段(生成词法单元)
3.1.1 词法分析和语法分析
编译的分析阶段分为词法和语法分析两部分的原因:
- 最重要的考虑是简化编译器的设计
- 提高编译器的效率
- 增强编译器的可移植性
3.1.2 词法单元、模式、词素
3.2 输入缓冲
-
加快源程序读入速度的方法。
-
双缓冲区方案,这种方案能够安全的处理向前看多个符号的情况。然后我们将考虑一种改进方法,,这种方法使用“哨兵标记”来节约用于检查缓冲区末端的时间。
3.2.1 缓冲区对
一种特殊的缓冲技术来减少用于处理单个输入字符的时间开销,一种重要的机制就是利用两个交替读入的缓冲区。
3.2.2 哨兵标记
上面的做法,每一次读入字符都需要进行两次测试,另一种优化方法:“哨兵标记”,在缓冲区末端添加“哨兵字符”—“eof”,把两个测试合二为一。这样的话大部分时间我们只需要执行一次测试,除非真的在词素末尾或者缓冲区末尾才会执行更多测试。
3.3 词法单元的规约
-
这一节研究正则表达式的形式化表示方法。
-
在词法分析中,最重要的运算是并、连接、闭包运算。
3.3.3 正则表达式
在上例中,我们可以首先给出字母和数位集合的名字,然后使用并、连接、闭包这些运算符来描述标识符。例如,上例中第5项差不多就是C语言中可以允许的标识符。这种处理方法非常有用。因此,人们常常使用一种称为正则表达式的表示方法来描述语言。
3.3.4 正则定义
3.3.5 正则表达式的扩展
- 一个或多个实例(+ / *)
- 零个或多个实例(?)
- 字符类( [ ] )
3.3.6 练习
- 一个或多个a以及一个或者多个b,以a开始,以a结束
- String of a’s and b’s.
- String of a’s and b’s that the character third from the last is a.
- String of a’s and b’s that only contains three b.
- String of a’s and b’s that has a even number of a and b.
长度为n:
- n + 1 前缀
- n + 1 后缀
- n - 1 真前缀
- C(n+1,2) + 1 (need to count epsilon in) 子串
- Σ(i=0,n) C(n, i) 子序列
3.4 词法单元的识别
3.4.1 状态转化图
作为构造词法分析器的一个中间步骤,我们首先讲模式转换成具有特定风格的流图,称为“状态转化图”。本节用手工的方式将正则表达式表示的模式转化为状态转换图。
3.4.4 基于状态转换图的词法分析器的体系结构
我们考虑几种将状态转换图的代码集成到词法分析器的方法:
3.6 有穷自动机
-
有穷自动机和状态转换图的区别:
- 有穷自动机是识别器,他只能对一个输入的串回答‘是’还说‘否’
- 有穷自动机分为两类:
- 不确定的有穷自动机(NFA):(Nondeterministic Finite Automata)对其边上的标号没有任何限制。一个符号标记离开同一状态的多条边,并且空串也可以作为标号
- 对于每个状态及自动机输入字母表的每个符号。确定的有穷自动机(DFA)(Deterministic Finite Automata)有切只有一条离开该状态、以该符号为标号的边。
- 区别:不确定的自动机:相同的字符输入对应的输出路径可能不一样
确定的和不确定的有穷自动机能识别的语言的集合是相同的。事实上,这些语言的集合正好是能够用正则表达式描述的语言的集合。这个集合中的语言称为正则语言。
3.6.1 不确定的有穷自动机
-
一个不确定的有穷自动机(NFA)的组成:
不管是NFA还是DFA,都可以转换为一张转换图。途中的结点是状态,带有标号的边表示自动机的转换函数。从状态s到状态t存在一条标号为a的边当且仅当状态t是状态s在输入a上的后续状态之一。
一个不确定的有穷状态机(NFA)的例子:
3.6.2 转换表
可以将NFA表示为一张转换表
优点:很容易的确定和一个给定状态和一个输入符号相对应的转换。
缺点:如果输入字母表很大,确大多数输入字符上没有转换的时候,转换表需要占用大量空间
初始状态用一个开始箭头指向,接受状态是一个双圈
3.6.3 自动机中输入字符串的接受
NFA不接受开始的空串:
3.6.4 确定的有穷自动机
-
确定的有穷自动机(DFA)是不确定的有穷自动机(NFA)的一个特例
- 没有输入e之上的转换动作
- 对每个状态s和每个输入符号a,有且只有一条标号为a的边离开
-
一个DFA的例子:
给定的输入字符,出边是唯一的
3.7 从正则表达式到自动机
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 字符串处理算法的效率
- 如果字符串处理器被频繁使用,比如词法分析器,那么转化到DFA时付出的任何代价都是值得的。
- 然而在另一些字符串处理应用中,例如grep,用户指定一个正则表达式,并在一个或多个文件中描述这个表达式所描述的模式,那么跳过构造DFA的步骤,直接模拟NFA可能更加高效。
- 正则表达式构造NFA,关键步骤是构造语法分析树。转换得到一个NFA话费的全部时间是O(|r|)
- 模拟NFA处理字符串x花费的时间是O(|r| * |x|),这个时间远远超过构造NFA所用时间O(|r|)
- 如果处理多个字符串花的时间太多,如词法分析器,我们倾向于构造DFA。
- 然而向grep这样处理一个字符串的情况,我们只对一个字符串运行自动机,我们倾向于使用NFA。
3.9 基于DFA的模式匹配器的优化
3.9.6 ✨最小化一个DFA的状态数
- 对于同一个语言,可以存在多个识别此语言的DFA
- 任何正则表达式都有一个唯一的,状态数目最少的DFA
- p130最小化DFA算法
第四章 语法分析
程序设计语言构造的语法可以使用上下文无关文法和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 引论
-
本节研究算术表达式的典型文法。
-
语法分析:接受一个终结符号串作为输入,找出从文法的开始符号推导出这个串的方法。如果不能从文法的开始符号的呆该终结符号串,则报告该终结符号串中包含的语法错误。
4.1.1 语法分析器的作用
- 处理文法的语法分析器大体上可以分为三种类型:通用的、自顶向下的和自底向上的。
- 通用的方法效率很低,不能用于编译器产品。
- 自顶向下的方法从语法分析树的顶部(根结点)开始向底部(叶子结点)构造语法分析树,而自底向上的方法则从叶子结点开始,逐渐向根结点构造。这两种分析方法中,语法分析器的输入总是按照从左向右的方式被扫描,每次扫描一个符号
4.1.2 代表性的文法(Representative Grammars)
- 我们主要关注表达式,因为运算符的结合性和优先级,表达式的处理更具挑战性。
4.2 上下无关文法
(Context-Free Grammars)简称“文法”
🔥正则表达式不能表示递归,上下文文法可以用if表达嵌套和递归
4.2.1 上下文无关文法的正式定义
p125
4.2.3 - 4.2.5 推导 - 二义性
-
自顶向下:从开始符号出发,每个重写步骤把一个非终结符号替换为它的某个产生式的体。
-
自底向上:和一种被称为“最右”推导的推到类型相关。每一步重写的都是最右边的非终结符号。
-
文法G:
- 句型:S=>a,其中S是文法G的开始符号,称a是G的一个句型
- 句子:不包含非终结符号的句型
- 生成的语言:句子的集合
- 上下文无关语言:由文法生成的语言
-
最左推导、最右推导(最右推导又叫做规范推导)
-
自底向上和“最右”推导相关。
-
因为在语法分析树和最左推导/最右推导之间存在着一种一对一的关系。每一个语法分析树都和唯一的最左推导及最右推导相关联。
-
二义性文法:对同一个句子又多个最左推导和最右推导。
- 例如:这个文法就是二义性的:(它又两个最左推导)
-
我们的任务:需要使用“消二义性规则来“抛弃”不想要的语法分析树”
-
文法是比正则表达式表达能力更强的表示方法。
4.3 设计文法
- 我们先讨论词法分析器和语法分析器如何配合。然后考虑几个语法分析器的转换方法,技术:消除文法的二义性 、 消除左递归和提取左公因子–用于改写文法。以适用于自顶向下的语法分析。
4.3.1 词法分析和语法分析
- 正则表达式最适合描述诸如标识符、常量、关键字、空白等语言构造的结构。文法最适合描述嵌套结构,比如对称的括号对,匹配的begin-end,相互对应的if-then-else等。这些潜逃结构不能使用正则表达式描述。
4.3.2 消除二义性
4.3.3 ✨左递归的消除
-
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' | ε
4.3.4 提取左公因子
提取左公因子是一种文法转换方法,可以产生适用于预测分析技术或自顶向下分析技术的文法。(推迟决定)
A → αβ1 | αβ2
转换成:
A → αA'
A' → β1 | β2
4.4 自顶向下的语法分析
-
自顶向下:从根节点开始,按照先根次序(深度优先),创建这棵语法分析树的各个节点,自顶向下语法分析也可以被看作寻找输入串的最左推导过程。
-
关键问题确定一个非终结节点,应该使用哪一个产生式。
-
对于有些文法,我们可以构造出向前看k个输入符号的预测分析器,这一类文法有事也称为LL(k)文法类。
-
FIRST和FOLLOW集,我们可以构造出“预测分析表”,它说明了如何在自顶向下语法分析过程中选择产生式。这些集合也可以用于自低向下的语法分析。
-
4.4.2:FIRST、FOLLOW计算。4.4.3:LL(1)文法。4.4.4:非递归的语法分析方法。4.4.5(不考):自顶向下语法分析过程中的错误恢复问题。
4.4.1 递归下降的语法分析
定义S_文法:
解决了回溯问题,但是不允许出现空串
- 每个产生式的右部都以终结符开始
- 同一非终结符的各个候选式的首终结符都不同
例:
总的递归下降程序:(每一个非终结符都要有一个程序)
PROGRAM程序:
DECLIST程序:
DECLISTN程序:
STLIST和STLISTN省略,TYPE对应过程:
4.4.2 ✨FIRST和FOLLOW
-
产生式的可选集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 | ε
4.4.3 LL(1)文法
- 对于LL(1)的文法,我们可以构造出预测分析器,即不需要回溯的递归下降语法分析器。
- LL,第一个L:从左向右扫描,第二个L:产生最左推导
- 对于每个LL(1)的文法,分析表中的每个条目都唯一的指定了一个产生式,或者标明一个错误。
- 然而,对于某些文法,M中可能存在多个条目。甚至一些文法,不存在对应的LL(1)
4.4.4 非递归的预测分析
- 显式的维护一个栈结构,可以模拟一个最左推导的过程。
预测分析表:
4.5 自底向上的语法分析
概念:
- 从分析树的底部(叶结点)向顶部(根结点)方向构造分析树
- 可以堪称是将输入串w归约为文法开始符号S的过程
- 自顶向下的语法分析采用最左推导的方式
自底向上的语法分析采用最左归约的方式(反向构造最右推导) - 自底向上语法分析的通用框架:
移入 - 规约分析(Shift-Reduce Parsing)
最左归约是最右推导的逆过程
移入 - 规约例子:
文法:
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文法是最大的、可以构造出相应移入-归约语法分析器的文法类
- L:对输入进行从左到右的扫描
- R:反向构造出一个最右推导序列
- LR(k)分析
- 需要向前查看k个输入符号的LR分析
- k = 0和k = 1 这两种清楚具有时间意义,当省略时,表示k = 1
LR分析法的基本原理
- 自底向上分析的关键问题是什么?
- 如何正确的识别句柄
- 句柄是逐步形成的,用“状态”表示句柄识别的进展程度
例:S→ bBB
S → ·bBB 移进状态
S → b·BB 待约状态
S → bB·B 待约状态
S → bBB· 归约状态

可知:LR文法的分析表是比较关键的:不同文法,构造方法不同
- LR(0)
- SLR
- LR(1)
- LALR
LR(0):
k个符号,有k+1个项目,项目描述了句柄识别的状态
**增广文法:**保证只有一个接受状态
LR(0)自动机:
CFG不总是LR(0)文法
4.6 语法树和分析树(4/1/2020 课)
语法树浓缩了转换所需的所有信息,比分析树效率更高,也称为抽象语法树。给出文法和特定串,完成推导,画出分析树,语法树是我们需要掌握的内容
语法树可以屏蔽文法描述:
同属链接:
二义性文法:
- 定义规则
- 重写文法
解决优先级:
解决结合性:


消除二义性:
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
4.7 自顶向下(4/8课程)
自顶向下分析主要学习递归下降分析和LL1分析,重点掌握first集合和follow集合的计算,LL1分析表的构建
- 自顶向下:最左推导来描述各个步骤。顶:开始符号。下:句子或错误。
- 从开始符号开始推导
- 两类:1.回溯分析程序。2.预测分析程序
- 预测分析:1.递归下降。2.LL(1)
递归下降:【视频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】
- LL(1)文法:分析表,每个子项只有一个产生式。
- 如果某个格子没有产生式:出错
- 如果某个格子含2个产生式:二义性
- LL(1)文法不能有二义性
构造分析表【视频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' | ε
4/15日课
- 自顶向下文法不能有左递归、左因子、二义性
- LL(1)是自顶向下文法,要求不能有左递归、左因子
- LL(1)分析法使用栈不仅可以存非终结符,…
消除左递归(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
4/22日课
【视频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<LALR<LR(1)
第五章 语法制导的翻译
SDD、SDT
- SDD是关于语言翻译的高层次规格说明,隐蔽了许多具体实现细节,使用户不必显式的说明翻译发生的顺序。
- SDT可以看作SDD的一种补充,是SDD的具体实现方案,显式指明语义规则的计算顺序,以便说明某些实现细节
- 终结符有综合属性,从词法分析的词法值得到。非终结符有综合属性和继承属性
- 只包含综合属性的SDD成为S属性的SDD
- 后缀翻译方案
--------------------------- The End ----------------------------