重要概念
(1)词法分析
-
词法分析是编译的第一个阶段,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用于语法分析。执行词法分析的程序称为词法分析程序或扫描程序。
-
语言和文法:文法就是语言结构的定义和描述,是有穷非空的产生式集合。 文法 G 定义为四元组的形式: G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S) 其中: V N V_N VN 是非空有穷集合,称为非终结符号集合; V T V_T VT 是非空有穷集合, 称为终结符号集合; P P P 是产生式的集合(非空); S S S 是开始符号(或识别符号)。 这里, V N ∩ V T = ∅ , S ∉ V N , V = V N ∪ V T VN∩VT=\empty,S \notin VN, V=V_N∪V_T VN∩VT=∅,S∈/VN,V=VN∪VT,称为文法 G 的字母表,它是出现文法产生式中的一切符号的集合。 文法 G 所描述的语言用 L ( G ) L(G) L(G) 表示,它由文法 G G G 所产生的全部句子组成,即 L ( G ) = { x ∣ S ⇒ ∗ x , 其 中 S 为 文 法 开 始 符 号 , 且 x ∈ V T + } L(G)=\{x\mid S\Rightarrow^*x,其中 S 为文法开始符号,且x \in V_T^+\} L(G)={x∣S⇒∗x,其中S为文法开始符号,且x∈VT+}. 简单的说,文法描述的语言是该文法一切句子的集合。
-
设 G = ( V N , V T , P , S ) G = (V_N, V_T, P, S) G=(VN,VT,P,S) 为一个文法,若 P P P 中的每一个产生式 α → β \alpha \rightarrow \beta α→β 均满足 ∣ β ∣ ≥ ∣ α ∣ |\beta| \ge |\alpha| ∣β∣≥∣α∣,仅仅 S → ε S \rightarrow \varepsilon S→ε 除外,则文法 G G G 是1型或上下文有关的
-
设 G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S),如果它的每个产生式 α → β \alpha \rightarrow \beta α→β 是这样一种结构: α \alpha α 是一个非终结符, β ∈ ( V N ∪ V T ) ∗ \beta \in (V_N \cup V_T)^* β∈(VN∪VT)∗,则 G G G 是一个2型的或上下文无关的
-
二义性文法:一个文法存在某个句子对应两棵不同的语法树。或者说一个文法存在某个句子,它有两个不同的最左(最右)推导。
-
最左(最右推导):在推导的任何一步 α ⇒ β \alpha \Rightarrow \beta α⇒β,其中 α , β \alpha,\beta α,β 是句型,都是对 α \alpha α 中的最左(最右)非终结符进行替换。
-
正则表达式定义
- 空串 ε \varepsilon ε 是正则表达式,表示仅含空串的集合,该语言只含空串
- 对于任意 c ∈ ∑ c \in \sum c∈∑, c c c 是正则表达式,表示仅包含 c c c 的集合,仅串 c c c
- 如果 M 和 N 是正则表达式,则以下也是正则表达式
(1)选择 M ∣ N = { M , N } M \mid N = \{M, N\} M∣N={M,N}
(2)连接 M N = { m n ∣ m ∈ M , n ∈ N } MN = \{mn \mid m \in M, n \in N\} MN={mn∣m∈M,n∈N}
(3)闭包 { ε , M , M M , M M M . . . } \{\varepsilon, M, MM, MMM...\} {ε,M,MM,MMM...}
(2)语法分析
-
定义:语法分析是编译过程的第二个阶段。语法分析的任务是在词法分析的基础上将单词序列分解成各类语法短语,如“程序”、“语句”、“表达式”等。这种语法短语也成为语法单位,可以表示成语法树。
-
自顶向下:自顶向下的分析方法也称面向目标的分析方法,也就是从文法的开始符号出发企图推导出与输入的单词符号相匹配的句子,若输入串是给定文法的句子,则必能推出,反之必然出错。
-
自底向上:所谓自下而上分析法就是从输入串开始,逐步进行“归约”,直至归约到文法的 开始符号;或者说从语法树的末端开始,步步向上“归约”,直到根节点。
-
LL(1) 文法:若文法的任何两个产生式 A → α ∣ β A\rightarrow \alpha \mid \beta A→α∣β 都满足下面两个条件:(1) F I R S T ( α ) ∩ F I R S T ( β ) = ∅ FIRST(\alpha) \cap FIRST(\beta) = \empty FIRST(α)∩FIRST(β)=∅; (2)若 β ⇒ ∗ ε \beta \Rightarrow ^*\varepsilon β⇒∗ε ,那么 F I R S T ( α ) ∩ F O L L O W ( A ) = ∅ FIRST(\alpha)\cap FOLLOW(A)=\empty FIRST(α)∩FOLLOW(A)=∅。 我们把满足这两个条件的文法叫做 LL(1) 文法,**其中的第一个 L 代表从左向右扫描输入,第二个 L 表示产生最左推导,1 代表在决定分析器的每步动作时向前看一个输入符号。**除了没有公共左因子外,LL(1)文法还有一 些明显的性质,它不是二义的,也不含左递归。
-
LR(0) 分析器:所谓 LR(0) 分析,是指从左至右扫描和自底向上的语法分析,且在分析的每一步,只须根据分析栈当前已移进和归约出的全部文法符号,并至多再向前查看 0 个输入符号,就能确定相对于某一产生式左部符号的句柄是否已在分析栈的顶部形成,从而也就可以确定当前所应采取的分析动作 (是移进还是按某一产生式进行归约等)。
-
语法树:句子的树结构表示法称为语法树(语法分析树或语法推导树)。 给定文法 G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S),对于 G G G 的任何句型都能构造与之关联的语法树。这棵树具有下列特征:
- (1)根节点的标记是开始符号 S S S。
- (2)每个节点的标记都是 V V V 中的一个符号。
- (3)若一棵子树的根节点为 A A A,且其所有直接子孙的标记从左向右的排列次序为 A 1 A 2 , . . . , A R A_1A_2,...,A_R A1A2,...,AR,那么 A → A 1 A 2 . . . A R A\rightarrow A_1A_2...A_R A→A1A2...AR 一定是 P P P 中的一条产生式。
- (4)若一标记为 A A A 的节点至少有一个除它以外的子孙,则 A ∈ V N A \in V_N A∈VN。
- (5)若树的所有叶节点上的标记从左到右排列为字符串 w w w,则 w w w 是文法 G G G 的句型;若 w w w 中仅含终结符号,则 w w w 为文法 G G G 所产生的句子。
-
递归下降分析:为每一个语法成分编写一个可递归调用的分析子程序进行自顶向下分析的语法分析方法。
(3)语义分析
- 定义:审查源程序有无语义错误,为代码生成阶段收集类型信息。其任务是检查程序结构(控制结构和数据结构)的一致性或完整性(控制流检查,唯一性检查,名字的上下文相关性检查,类型检查)。
- 语法制导翻译:本质是上下文相关分析所需的操作可以围绕着语法的结构进行组织。此方法将上下文相关分析集成到解析上下文无关语法的过程中,由语法分析程序的分析过程来主导语义分析以及翻译的过程,所以叫做语法制导翻译/转换,也可称为语法制导的语义计算。
(4)中间代码生成
- 进行了语法分析和语义分析阶段的工作后,将源程序变成一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。
(5)代码优化
- 对前一阶段产生的中间代码进行变换或进行改造,目的是使生成的目标代码更为高效,即省时间和省空间。
- 问题:代码优化的目的和意义?
- 代码优化是尽量生成“好”的代码的编译阶段。也就是要对程序代码进行 一种等价变换,在保证变换前后代码执行结果相同的前提下,尽量使目 标程序运行时所需要的时间短,同时所占用的存储空间少。
- 分类:前端优化、中间表示上的优化、窥孔优化、局部优化、循环优化、全局优化、
(6)目标代码生成
- 把中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码。任务是:给源程序的数据分配计算资源、给源程序的代码选择指令
(7)对编译器整体的理解
- 编译程序的主要构成成分有:词法分析程序、语法分析程序、语义分析程序、中间代码生成程序、代码优化程序、目标代码生成程序、表格管理程序、出错处理程序。
- 编译程序与解释程序区别:前者生成目标代码,而后者不生成;前者产生的目标代码的执行速度比解释程序的执行速度要快;后者人机交互好,适于初学者使用。
第一章 编译器简介
-
编译器的核心功能是把源代码翻译成目标代码
-
编译器设计两个原则:语义相同;以某种可察觉的方式改进输入程序.
-
编译器内部结构
- 前端:依赖于源语言,与目标机器无关;将输入的代码映射到 IR
- 后端:依赖于目标机器,与源语言无关;将 IR 映射到目标机的指令集和有限资源上,处理前端生成的 IR。
-
编译器的什么性质最重要:
- 编译的效率,即编译一个程序的速度和代价
- 编译生成代码的效率,即编译器生成的可执行程序的运行效率
- 编译生成代码的质量,即和源代码语义等价性
第二章 文法
- 终结符:是文法所定义的语言的基本符号,可称为单词、token
- 非终结符:是用来表示语法成分的符号,称为语法变量
- 设 G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S),如果它的每个产生式 α → β \alpha \rightarrow \beta α→β 是这样一种结构: α ∈ ( V N ∪ V T ) ∗ \alpha \in (V_N \cup V_T)^* α∈(VN∪VT)∗,且至少含有一个非终结符,而 β ∈ ( V N ∪ V T ) ∗ \beta \in (V_N \cup V_T)^* β∈(VN∪VT)∗,则 G G G 是一个0型文法
- 设 G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S),若 P P P 中的每一个产生式的形式都是 A → a B A \rightarrow aB A→aB 或 A → a A \rightarrow a A→a,其中 A A A 和 B B B 都是非终结符, α ∈ V T ∗ \alpha \in V_T^* α∈VT∗,则 G G G 是 3型文法或正规文法
第三章 词法分析
-
词法分析器实现方法:手工编码实现法;词法分析器生成器
(自己画的,不是到对不对)
根据正则表达式画DNF
-
C语言标识符正则表达式:
-
C语言无符号整数正则表达式
-
正则表达式与正规文法等价:对任意一个正规文法,存在一个定义同一个语言的正则表达式;对每个正则表达式,存在一个生成同一个语言的正规文法;两者可相互转化。
-
有限状态自动机:字母表,状态集、初始状态、终结状态集、转移函数
-
非确定的有限状态自动机(NFA):状态转移函数是不确定的,即对于任意字符有多于一个状态可以转移
-
确定的有限状态自动机(DFA):每次接受字符走到的状态是确定的,即对于任意字符最多有一个状态可以转移
-
NFA和DFA在表达力上是等价的;任何DFA是某个NFA的一个特例,任何NFA可以通过一个DFA模拟
-
RE转化为NFA:Thompson算法;NFA转为DFA:子集构造算法;DFA转化为词法分析器代码:Hopcroft最小化算法;
第四章 语法分析
需要掌握的:LL(1),LR(0),SLR,LR(1),LALR
- 上下文无关文法:是一个四元组 G = ( T , N , P , S ) G=(T,N,P,S) G=(T,N,P,S). T是终结符集合,N是非终结符集合,P是一组产生式规则,S是唯一的开始符号。
- BNF范式:
- 非终结符要放到一对尖括号中
- 所有的终结符要加上下划线
- 最左推导:每次总是选择最左侧的符号进行替换
- 最右推导:每次总是选择最右侧的符号进行替换
- 分析树:推导可以表达成树状结构,和推导所用的顺序无关(最左、最右、其他),树中的每个内部节点代表非终结符,每个叶子节点代表终结符,每一步推导代表如何从双亲节点生成它的直接孩子节点
- 二义性语法:如果在某个语法中,一个语句存在多个最右(或最左)推导,则该语法称为二义性语法
- 分析树的含义取决于树的后序遍历
- 二义性文法:给定文法G,如果存在句子s,它有两棵不同的分析树
- 二义性文法存在问题:同一个程序有不同的含义;程序运行的结果不是唯一的。
- 二义性文法解决方案:文法的重写
-
左递归文法:加法左结合;右递归文法:加法右结合。
-
递归下降分析:为每个非终结符构造一个分析函数, 用前看符号指导产生式规则的选择
-
无递归的情况:
-
有递归的情况:
-
思考题:
LL(1) 分析算法
计算 FIRST 集一系列
-
LL(1)分析表中的冲突:若表中每个项最多只有一个元素,叫LL(1)文法;否则叫做冲突,不是LL(1)类型的文法。
-
LL(1)分析算法优点:算法运行高效,有现成的工具可用;缺点: 能分析的文法类型受限, 往往需要文法的改写
-
NULLABLE集合
-
FOLLOW集:哪些终结符可以跟在非终结符后面
FOLLOW容易推错,还是按照标准算法来吧:
-
FIRST集:比如 FIRST(N) 的含义是从非终结符 N 开始推导得出的句子开头的所有可能终结符的集合
-
FIRST_S也可以直接看出来,不过看一下这个算法,别忘记最后的一行
-
任何有左递归的文法都不是 LL(1) 文法
-
消除直接左递归
-
消除间接左递归
-
提左公因子
构造LL(1)文法递归下降分析程序
LR(0) 分析算法
-
自底向上分析法:自叶开始逐级向上归约,直到构造出表示句子结构的整个推导树为止的一种语言形式分析算法
-
自底向上分析法思想:比LL(1)分析文法范围广、不需要改写
-
LR分析算法(移进-归约算法):算法运行高效,有现成的工具可用;比LL(1)分析文法范围广、不需要改写
-
LR(0)分析算法优点:容易实现;缺点:能分析的文法有限,LR(0)分析表可能包含冲突
-
LR(0)分析算法的自动机和LR(0)分析表
-
画出自动机的一道思考题
SLR 分析算法
- 与 LR(0) 分析算法基本步骤相同,仅区别于对规约的处理
- 注意,是规约:对于状态 i i i 上的项目 X → α ∙ X \rightarrow \alpha \bullet X→α∙,仅对 y ∈ F O L L O W ( X ) y \in FOLLOW(X) y∈FOLLOW(X) 添加 A C T I O N [ i , y ] ACTION[i,y] ACTION[i,y].
- 优点:有可能减少需要规约的情况;有可能去除需要移进-规约冲突
- 缺点:仍然有冲突出现的可能
示例:
LR(1) 分析算法
- LR(1)缺点:但对同心集的分裂使状态数目剧烈增长,导致存储容量的急剧增加
- 同心集:如果除前看符号外,两个LR(1)项目集是相同的,则称这两个项目集是同心的
再来一个例子:
LALR(1) 文法
- LALR: lookahead-LR
- 说明
- 若文法是LR(1)文法,合并同心集后,若有冲突也是归约-归约冲突,不可能是移进-归约冲突(LR(1)文法没有移进-归约冲突);若有冲突,则不能称为是LALR(1)文法
- 合并同心集后,会推迟错误的发现
- LALR(1) 分析算法
- 形式上与LR(1)相同
- 大小上与LR(0)/SLR相当
- 分析能力介于SLR和LR(1)二者之间:SLR < LALR(1) < LR(1)
- 合并后的展望符集合仍为FOLLOW集的子集
- 仍用上面的例子
3和6是同心集,4和7是同心集,8和9是同心集
二义性文法在LR分析中的应用
- 特点:
- 任何一个二义性文法不是LR类文法,也不是LL(k)文法
- 任何一个二义性文法不存在与其相应的确定的语法分析器
- 但对某些二义性文法,可以人为给出优先级和结合性的规定,构造出比相应的非二义性文法更优越的LR分析器
第五章 语法制导翻译
-
对输入进行语法制导翻译
-
属性文法:在文法G[S]基础上,为文法符号关联有特定意义的属性,并为产生式关联相应的语义动作或条件谓词,称之为属性文法,并称文法G[S]是这一属性文法的基础文法
-
综合属性:对关联于产生式A->α的语义动作 b:= f(c1,c2,…,ck),如果b是A的某个属性,则成b是A的一个综合属性
-
继承属性:对关联于产生式A->α的语义动作 b:= f(c1,c2,…,ck),如果b是产生式右部某个文法符号X的某个属性,则称b是文法符号X的一个继承属性
-
S-属性文法:只包含综合属性的属性文法称为S-属性文法
-
L-属性文法某个属性文法,如果对其中每一个产生式A->X1X2…Xn,其每个语义动作所涉及的属性或者是综合属性,或者是某个Xi的继承属性,而这个继承属性只能依赖于:Xi左边的符号的属性;A的继承属性
第六章 语义分析
- 基于S-属性文法的语义计算
- 基于L-翻译模式的自顶向下语义计算
第七章 中间表示
-
中间表示的结构:
- 图IR:语法分析树、抽象语法树、有向无环图、控制流图、依赖关系图、调用图
- 线性IR:堆栈机代码、三地址代码
-
堆栈机代码:
-
三地址码
-
递归下降代码生成算法:语句的代码生成
-
基本块的划分
第八章 代码优化
代码优化是对被优化的程序进行的一种语义保持的变换,语义保持就是程序的可观察行为不能改变
循环
数据流方程
- 到达分析
- 注意,只有赋值语句才能对gen和kill产生贡献,条件判断语句、return语句都不可以.
- 可以以基本块为基本单位计算gen/kill/in/out
- 活性分析
- 更加一般的例子
- 另一个例子