目录
- [Part One: Lexical Analysis(Scanning)](#Part One: Lexical Analysis(Scanning))
- [Part Two: Syntax Analysis(Parsing)](#Part Two: Syntax Analysis(Parsing))
-
- [1. Grammar](#1. Grammar)
-
- [1.1 **CFG**](#1.1 CFG)
- [1.2 Parse Tree](#1.2 Parse Tree)
- [1.3 Abstract Syntax Tree](#1.3 Abstract Syntax Tree)
- [1.4 Ambiguity](#1.4 Ambiguity)
- [2. Top-Down Parsing](#2. Top-Down Parsing)
-
- [2.1 LL(1)](#2.1 LL(1))
- [2.2 错误处理](#2.2 错误处理)
- [3. Bottom-Up Parsing](#3. Bottom-Up Parsing)
- [Part Three: Semantic Analysis](#Part Three: Semantic Analysis)
Part One: Lexical Analysis(Scanning)
-
Regular Expression
操作
-
Union
-
Concatenation
-
Kleene closure
-
Positive closure
-
-
NFA
有空串,无法确定当前状态
-
正则表达式转换成NFA
一步一步把正则表达式拆开
-
问题
-
如何确定token对应的记号类型?(冲突)
解决
-
为正则表达式构建FA
-
算法:Left-to right scan
-
规则
-
Maximal Munch(最大匹配规则)
-
Priority rule
-
"catch-all" rule:匹配任何字符并报错
-
-
-
如何多种可能的扫描方式?
解决:DFA
-
如何高效实现词法分析?
-
-
DFA
NFA变为DFA
-
需解决
-
消除空闭包
-
消除同一字符跳转到不同状态
-
-
方法
-
Subset Construction(子集构造)
-
NFA变为DFA
-
从空闭包开始,不断Ia,Iaa...
-
-
最小化算法:简化DFA状态
-
Part Two: Syntax Analysis(Parsing)
1. Grammar
1.1 CFG
-
R.E. vs. CFG
-
允许递归,表达能力更强
-
CFG没有*
-
-
包含关系
Regular Expression -> Context Free Grammar -> Context Sensitive Grammar -> Unrestricted Grammar(如图灵机Turing machine)
-
Sentence
-
Sentential Form(句型)
推导过程中产生的所有中间符号串(含非终结符;含最终结果)
-
Sentence(句子)
推导的最终结果(不含非终结符)
-
-
包含LL(k)
1.2 Parse Tree
Derivation(推导)
-
Leftmost Derivation
最左推导对应前序遍历(根左右)
-
Rightmost Derivation
最右推导的逆序对应后序遍历(左右根)
1.3 Abstract Syntax Tree
-
只保留必要信息
-
AST和三地址码是中间表示形式
1.4 Ambiguity
-
同一句表达式对应不同语法解析树
-
解决二义性
-
Disambiguating rule(歧义消除规则)
- 附加额外规则
-
Rewriting the grammar(重写文法)
-
消除左递归
-
提取左因子
-
-
2. Top-Down Parsing
自顶向下,最左推导
2.1 LL(1)
-
是预测分析文法
-
不含二义性,不含左递归和左因子
-
从左往右扫描输入,最左推导,1个lookahead
-
First集和Follow集
-
LL(1)文法判断
-
无左递归和左因子
- 注意这只是条件之一,无法判断一定是LL(1)
-
根据定义
-
任意First集无交集
-
First集包含 ε,则First集和Follow集无交集
-
-
-
非LL(1)变成LL(1)
-
消除左递归
-
提取左因子
-
-
Recursive-Descent Parsing(递归下降分析)
构造递归下降分析:
-
Step1:判断是否为LL(1)
-
Step2:构造递归下降解析器
-
Step3:构造语法分析树和抽象语法树
-
-
构建语法分析表
-
Step1:拆开所有"或",求First(α)
Follow不用求,除非First包含ε,则Follow(ε)=Follow(A)
-
Step2:对于First(α)的每个a,A->α填入M[A,a]
-
Step3:对于Follow(A)的每个a,将A->α填入M[A,a]
-
-
语法分析过程
-
Stack,Input,Action
-
产生式体的末尾先进栈
-
2.2 错误处理
错误恢复策略
-
Panic Mode
遇到错误跳过
-
Phrase-Level Recovery
局部修复错误
3. Bottom-Up Parsing
-
包含关系
LR(0) 属于 SLR(1) 属于 LALR(1) 属于 LR(1)
-
自底向上,最右推导
-
Handle
-
是产生式体
-
是最右推导链的句型的一部分
-
在栈顶寻找句柄
-
-
item
-
关于栈
-
Handle在栈顶附近
-
栈顶必须是状态符号
-
占地是任意符号(终/非终/状态)
-
-
LR(0) Parsing
-
起始的产生式体不是单一字符要增广(augment)
-
构造DFA
-
NFA状态数与文法大小成正比,构造简单
-
DFA状态数呈指数级增长
-
-
判断LR(0)
-
无SR冲突:SR冲突:lookahead没要求
-
无RR冲突:RR冲突:lookahead需要存在交集
-
-
缺点
-
无lookahead,仅依靠栈内符号
-
限制语言类别
-
-
-
Viable Prefix
一个可行前缀是一个最右句型的前缀,并且它没有越过该最右句型的最右句柄的右端(可包括句柄)
这些前缀的集合叫做可行前缀。
-
SLR(1) Parsing
-
解决SR和RR冲突
-
前提:I={X->α • bβ,Α->γ •,Β->δ•},若Follow(A)∩FOLLOW(B)=φ,且不包含b,下一输入为a
-
策略
-
若a=b,Shift
-
若a∈FOLLOW(A),Reduce Α->γ
-
若a∈FOLLOW(B),Reduce Β->δ
-
其他情况error
-
-
-
构建SLR(1)语法分析表
先构建DFA
-
判断SLR(1)
-
存在SR或RR冲突
-
对于每个Ai->α • aβ,Bi->γi •,有{ai}∩Follow(B1)∩Follow(B2)∩...=φ(避免缺点3),则是SLR(1)
-
-
SLR(1)缺点
-
Follow集过于宽泛,可能错误归约
-
状态合并,无法区分不同位置的相同非终结符
-
存在无法解决的冲突,如next=b,且next∈FOLLOW(A)
-
-
-
LR(1) Parsing
-
核心:添加lookahead
-
与SLR(1)区别
-
SLR(1)先构建LR(0)的DFA,再根据Follow引入lookahead决定归约
-
LR(1)直接构建带lookahead的DFA
-
-
计算lookahead
-
起始S->E的的lookahead是$
-
对于终结符T-> •t, a 的展开,lookahead=a
-
对于非终结符A-> • Bγ, a的展开,lookahead=First(γa)
- 若γ可推出ε,lookahead再加入a
-
-
构建LR(1)语法分析表
-
LALR(1)
-
即Lookahead LR(1),是LR(0)和LR(1)的折中
-
合并不同状态里的同心集,若合并后存在RR冲突,则是LALR(1)
-
同心集指逗号前产生式一样
-
例如I5:A->d, a和B->d, c,I10:A->d, c和B->d, a
合并后:A->d, a/c和B->d, a/c。lookahead一样,存在RR冲突,则是LALR(1)
-
-
-
Part Three: Semantic Analysis
-
编译器逻辑结构
-
Scanner
-
Parser
-
Semantic analyzer
-
IR genenration
-
IR optimizer
-
Code Generator
-
Target code optimizer
-
-
流程
源代码 --Scanner--> Tokens --Parser--> AST --Semantic analyzer--> Annotated Syntax Tree --IR generation--> 中间代码(三地址码) --Code generator--> 目标代码(机器码/汇编代码) --Target code optimizer--> Target machine code
-
辅助部件
-
Literal table
-
Symbol table
-
Error handler
-
-
输入AST,输出dependency graph
-
符号表
-
符号表是编译器的核心数据结构,支持语义分析和代码生成(synthesis)
-
作用域检查通常通过符号表来实现,这种符号表可以采用链式结构实现,也可以采用显式的栈结构
-