CH1-Introduction
什么是编译器:
编译器是一个将源语言编写的程序翻译成目标语言的程序。
编译器的结构
主要包括了两个阶段:
1. Analysis phrase 分析阶段(Lexical 词法、Syntax 语法、Semantic 语义分析)
2. Synthesis phrase 综合阶段 (中间代码生成、中间代码优化、目标机器语言代码生成)
CH3-Lexical analysis
1.要点概述:
如何描述和识别词法元素
- 正规式(Regular Expression)用于描述词法元素长什么样子(pattern),
- 正规文法(Regular Grammar)用于描述词法元素的构造过程,
- 有限状态自动机(Finite Automata)用于识别词法元素。

2.terms introduction 术语介绍
1. token & Lexemes
属性 | Token | Lexeme |
---|---|---|
本质 | 抽象的语法单位类型 | 具体的字符序列实例 |
作用 | 描述词法单元的分类 | 是输入中实际的内容 |
例子 | IDENTIFIER , NUMBER , KEYWORD | x , 123 , if |
表达方式 | 通过名称和模式定义(如正则表达式) | 直接从源代码中提取 |
2.alphabet & string & language
概念 | 定义 | 本质 | 例子 |
---|---|---|---|
Alphabet | 一个有限的字符集,表示语言中可用的符号。 | 基础单元:字符的集合 | {0, 1} 表示二进制字符集, {a, b, c} 表示字母集 |
String | 由 alphabet 中的字符按照某种顺序排列组成的有限长度的序列。 | 具体序列:字符按顺序排列 | 1010 是基于 {0, 1} 的字符串,abc 是字母字符串 |
Language | 一组字符串的集合,这些字符串是从某个 alphabet 中组合而成的,并满足某些特定规则。 | 集合:包含符合规则的字符串 | {ε, 0, 1, 01, 10} 是 {0, 1} 上的一个语言 |
-
Alphabet 是基础:
- Alphabet 是语言的“字母表”,定义了语言中可以使用的字符。所有字符串都由 alphabet 中的字符构成。
-
String 是 alphabet 的排列:
- 一个字符串是 alphabet 中字符的具体排列。每个字符串有有限的长度。
-
Language 是 string 的集合:
- 一个语言是由 alphabet 上所有可能字符串的子集构成(可能有限,也可能无限)。语言通常由语法规则或生成规则定义。
3.operation on language(语言操作)
- Union并集
- 笛卡尔积
- 幂
- Kleene closure闭包
- positive closure
4.derivations 推导
推导就是生成字符串的过程,它从一个起始符号(通常为文法的开始符号)出发,逐步使用文法规则,将符号替换为更具体的形式,直到生成最终的字符串。简单讲,将产生式变成具体的式子。
推导是人脑从理论往实际分析的过程,实际程序中是从实际往理论去分析的过程!!!
4.1最左推导和最右推导
(1) 最左推导(Leftmost Derivation)
- 定义:在每一步推导中,始终选择当前最左侧的非终结符进行替换。
- 特点:优先处理左侧的符号,按从左到右的顺序解析。
- 作用:最左推导是 LL 语法分析器(如递归下降解析器)的基础。
(2) 最右推导(Rightmost Derivation)
- 定义:在每一步推导中,始终选择当前最右侧的非终结符进行替换。
- 特点:优先处理右侧的符号,按从右到左的顺序解析。
- 作用:最右推导是 LR 语法分析器(如移进-规约解析器)的基础。
概念 | 最左推导 | 最右推导 |
---|---|---|
定义 | 优先替换最左侧的非终结符 | 优先替换最右侧的非终结符 |
解析器 | LL 解析器(自顶向下解析) | LR 解析器(自底向上解析) |
特点 | 从左到右依次替换,适合递归下降解析 | 从右到左依次替换,适合移进-规约解析 |
与递归关系 | 非终结符递归调用,映射为推导过程 | 非终结符递归调用,映射为推导过程 |
语法树关系 | 对应语法树的先序遍历 | 对应语法树的后序遍历 |
4.2 LL和LR简单介绍,我们会在CH4中详细介绍
特性 | LL 分析器 | LR 分析器 |
---|---|---|
分析方向 | 自顶向下 | 自底向上 |
构造推导 | 最左推导L | 最右推导R |
预测能力 | 简单(需预测表) | 更强(依赖状态栈) |
适用范围 | 文法简单,无左递归 | 文法复杂,包括左递归 |
实现难度 | 相对简单 | 相对复杂 |
通俗来说,LL 分析器更像“猜谜高手”,边读边猜;而 LR 分析器更像“稳重审稿人”,读完一段再回头判断。这两种方式各有优劣,具体选用哪种,取决于目标语言和实现需求。
5.Linear(Regular) Grammar 线性(正规)文法
在形式语言理论中,**语法(Grammar)**通常用一个四元组 G=(N,Σ,P,S) 来表示,它定义了一种语言的生成规则。其中:
5.1. 语法四元组的组成
-
N: 非终结符集合(Non-terminal symbols)
- 表示语法的中间符号,用于生成语言的结构。
- 通常用大写字母表示(如 A,B,S)。
- 必须至少包含一个非终结符,且至少包含一个起始符号 S。
-
Σ: 终结符集合(Terminal symbols)
- 表示语言中的具体符号(如字母、数字、标点符号等)。
- 这些符号是语言的实际组成部分,不能被进一步替换。
- 通常用小写字母或其他符号表示(如 a,b,c,0,1)。
- Σ和 N 必须互斥,即 N∩Σ=∅。
-
P: 产生式集合(Production rules)
- 定义了如何用非终结符和终结符生成语言。
- 每条产生式的形式为:α→β,其中:
- α∈(N∪Σ)+:表示由非终结符和终结符构成的串,至少包含一个非终结符。
- β∈(N∪Σ)∗:表示可以是空串或由非终结符和终结符构成的串。
- P 必须是有限的集合。
-
S: 起始符号(Start symbol)
- 一个特殊的非终结符 S∈N,表示生成语言的起始点。
- 所有语言的生成过程必须从 S 开始。
线性文法,也叫正规文法,也就是我们的产生式
右线性和左线性简单直接的理解就是,非终结符是否是在产生式的最左边,如果是则为左线性,反之在最右边为右线性。非终结符夹在中间的叫上下文无关文法!!


6.Regular expression 正规式
形如:
7.Finite Automata 有限自动机
7.1DFA-Deterministic FA
DFA是一个五元组(quintuple),定义:每个状态的每个输入符号只有唯一的转移路径。前缀可以相同,但是后缀一定不同!!
- S是状态集合
是输入字符表(alphabet)
- move是转移函数
是初始状态,
属于S
- F是接收状态集合
7.2NFA-Nondeterministic FA
定义:每个状态的输入符号可以有多条转移路径,甚至可以在**没有输入符号(ε转移)**的情况下发生状态变化
与DFA的主要区别在于转移函数move:S×(Σ∪{ϵ})→S,包含{ϵ}转移
属性 | DFA | NFA |
---|---|---|
状态转移 | 每个状态对每个输入符号有唯一转移 | 每个状态对输入符号可以有多个转移或无转移 |
ε转移 | 不允许 | 允许 |
执行方式 | 单条路径(确定性) | 多条路径(非确定性) |
实现复杂度 | 转移表更大,但执行简单 | 转移表小,但执行更复杂 |
等价性 | DFA 和 NFA 的识别能力相同 | DFA 和 NFA 的识别能力相同 |
构造 | 更容易直接实现 | 形式表达更灵活,适合表示复杂模式 |
7.3transition table
问题探讨-ϵ能随便删除吗-不能
7.4NFA转DFA
一般顺序
插一句嘴:Regular expression->NFA的时候注意按照规范,
形如:(RE)*的正则表达式,应该按照下面方式作图
接下来我们正式进入NFA转DFA
首先需要知道的是, 举个例子
其实就是某个状态通过
能够转换到的所有状态
如,
然后是具体流程,用伪代码来表述就是
init ϵ-closure(S0)
put ϵ-closure(S0) into a State Set as Dstate
set ϵ-closure(S0) unmarked
while (there is T in Dstate is unmarked)
{
choose unmarked T;
for (each a in alphabet)
{
tempState = move(T) by a;//先通过a转换到的状态集
NewState = ϵ-closure(tempState);//后计算闭包
if(NewState not in Dstate)
put unmarked NewState input Dstate;
}
}
总而言之,就是画表,通过字符转移到新的状态然后计算ϵ-closure(),然后再通过新的状态转移,循环,知道没有新的未被转移过的状态为止。那么接收状态就是包含原先NFA中接收状态的状态集。
这是上面figure3.34的答案。请对照!!
7.5DFA最小化
为什么需要DFA最小化,一个直观的表述:可以理解为归类后再细分,比如用华为手机的用户归为一类(一个状态)但是其中有用Mate的,用Nova的,这时候我们再通过手机型号转化到另外两个状态。也就是说,两个状态的前缀是相同的,但是后缀不同 ,这时候其实可以将两个前缀相同的状态合并为一个状态。
DFA(确定性有限自动机)最小化是将一个DFA转化为具有相同语言的最简状态集合的过程。具体流程如下:
1. 消除不可达状态
- 初始状态可达:从初始状态出发,标记所有可以通过转移到达的状态。
- 移除不可达状态:未被标记的状态即为不可达状态,从DFA中删除。
2. 初始划分(区分终止状态与非终止状态)
- 将DFA的所有状态分成两类:
- 终止状态集合:接收输入串后能够接受的状态。
- 非终止状态集合:不能接受输入串的状态。
3. 迭代划分(区分行为不同的状态)
根据状态的行为(即转移到的目标状态所属的分组),进一步细分状态集合。
步骤
- 对每一个状态分组,对每种输入符号 a,计算转移后的目标状态属于哪个分组。
- 如果同一个分组中的某些状态在输入 a 后转移到不同的分组,则将此分组细分为多个分组。
- 重复此过程,直到分组不再细分为止(达到稳定)。
示例
4. 构造最小化DFA
- 每个最终的状态分组成为最小化DFA中的一个状态。
- 如果多个状态属于同一个分组,它们被合并为一个状态。
- 转移函数按照原始DFA的转移关系映射到新的状态:
- 确定新的初始状态和接收状态:
- 初始状态为包含原DFA初始状态的分组。
- 接收状态为包含原DFA接收状态的分组。
注意:
- 初始状态的分组保证唯一,所以新 DFA 只能有一个初始状态。
- 接收状态由原 DFA 的接收状态继承,非接收状态无法成为接收状态。
- 新的最小化 DFA 的接收语言和原 DFA 完全相同。
7.5.x 答题规范
8.根据RE构造NFA(P99)
9.FA转换成RE (P108)
- 基本操作:消除状态、合并边直至只保留一个初始态、一 个接受态和二者之间的一条边。
- 基本思想:消除状态、保留路径(语言)
10.Linear Grammar to NFA
10.1右线性文法转NFA
产生式开始符号即为初始状态
例题:
10.2左线性文法转NFA
注意引入作为初始状态
例题:
第一道例题,消除左递归后会更好做!!