链接:我的 github
想说一下:以下是我自己在编译原理考试前整理的,可能会有细节错误,但基本都对,背一背吧!
基本概念
背
- 编译过程:词法分析 、语法分析、语义分析、中间代码生成与代码优化、目标代码生成。还有表格处理和出错处理
- 每条指令的执行代价定义为指令访问主存次数加 1
- 编译程序工作过程中,第一阶段输入是源程序,最后阶段的输出为目标代码程序
- 编译程序是将源程序翻译成目标程序的程序
- 目标代码可以是汇编指令代码或可重定位指令代码或绝对机器指令代码
- 把语法范畴翻译成中间代码所依据的是语义规则
- 常用的中间代码形式不含语法树
- 常用的中间代码形式为:三元式、四元式、逆波兰式,不含语法树
- 代码生成阶段的主要任务:把中间代码变换成依赖具体机器的目标代码
- 通常把编译过程分为分析前端与综合后端两大阶段
- 词法、语法和语义分析 是对源程序的(分析)
- 中间代码生成、代码优化与目标代码的生成 是对源程序的(综合)
- 根据优化所涉及的程序范围,可将优化分成为局部优化,循环优化,全局优化三个级别
- 局部优化:局限于基本块范围的优化
- 代码优化的目的是节省时间和空间
- 代码优化:尽量生成“好”的代码的编译阶段。也就是要对程序代码进行一种等价变换,在保证变换前后代码执行结果相同的前提下,尽量使目标程序运行时所需要的时间短,同时所占用的存储空间少
- 源程序中的错误通常分为语法错误和语义错误两大类
- 解释程序和编译程序的区别在于:是否生成目标代码
- 将编译程序分成若干个“遍“是为了使程序的结构更加清晰
- 编译程序分成若干个“遍”是为了在编译过程中逐步完成多个任务并最终生成可执行代码。
- 通常来说,一个编译器会将编译过程分成多个遍,每个遍负责实现一些特定的功能。例如,第一遍可能会进行词法分析和语法分析,第二遍可能会进行语义分析和类型检查,第三遍可能会生成中间代码,第四遍可能会对中间代码进行优化,最后一遍则会将优化后的中间代码转换为目标机器代码。
- 这种分层处理的方式可以使编译器的实现更加清晰、模块化,也能够让编译器更容易地进行调试和优化。此外,通过在不同的遍中生成不同的中间表示,还能够方便地在编译器的不同阶段进行调试和分析。
- 与编译系统相比,解释系统:较简单,可移植性好,执行速度慢
- Java 语言它是一个综合性的,在字节码生成的时候它是编译型语言,那么字节码生成之后,字节码的运行它是解释性语言
- Java 语言是一种编译性语言。Java 源代码在编译时会被翻译成 Java 字节码,这是一种中间码,类似于汇编语言。
- Java 字节码并不是机器码,因此需要通过 JVM(Java 虚拟机)进行解释执行。也就是说,运行Java程序时,首先需要将字节码加载到 JVM 中,然后由 JVM 进行解释和执行。因此,可以说Java是一种混合型语言,既有编译型语言的特点,也有解释型语言的特点。
- 解释型语言通常开发速度快但是可读性差,相对比较慢
- 解释性语言是直接解释执行代码。在解释型语言中编写的代码不需要编译,而是通过一个解释器逐行解释执行。当一个语句被解释器解释时,它会立即执行,而不是通过编译生成机器代码。
- 编译型语言开发周期可能较长,代码可读性较高,性能较好,相对比较块
- 编译型语言需要通过编译器将源代码编译成机器代码,才能够执行。在编译过程中,编译器将源代码翻译成机器码,并将其保存在文件中。当程序执行时,操作系统运行时加载这个程序并直接从保存在文件中的机器码执行它。
- 语法分析的方法:自上而下、自下而上
- 自顶而下分析法:从文法的开始符号出发,利用产生式不断用其右边符号替换左边符号,最终推导出句子的过程。
- 自下而上分析法:从输入串开始,逐步进行“归约”,直至归约到文法的开始符号;或者说从语法树的末端开始,步步向上“归约”,直到根节点。
- 用自上而下的方法分析一个文法 G,若它的 预测分析表M 不含多重定义,则该文法是 LL(1) 文法
- LR 分析法是寻找句型的句柄,是最左规约
- 自下而上分析法是一种**“移进—归约”法**
- 自顶向下分析是最左推导
- 词法分析的主要任务是从左向右扫描每行源程序的符号,按照词法规则从构成源程序的字符串中识别出一个个具有独立意义的最小语法单位,并转换成统一的内部表示(token),送给语法分析程序。
- 词法分析所输出的结果一定是一个二元的形式:单词的类别归属 type 和属性值/字面值 value
- 扫描器/词法分析器:执行词法分析的程序
- 扫描器/语法分析器的任务是:从源程序中识别出一个个单词符号/输入源程序,对构成源程序的(字符串)进行扫描和分解。
- 扫描器/语法分析器的输入是单词符号,其输出是语法单元
- 语法分析是依据语言的语法规则进行。中间代码产生是依据语言的语义规则进行的
- 语法分析器的任务
- 分析单词如何构成语言和说明的
- 分析语句和说明是如何构成程序的
- 分析程序结构
- 语法分析器中,有害规则和多余规则是指与一个语法语言无关或与该语言中某些合法句子没有关联的规则
- 有害规则指的是那些在语法规则中没有实际用途或者在语法分析过程中不会产生有用的信息,但是它们可能导致歧义和不必要的分析,从而使分析器性能降低
- 多余规则定义了一些与语言语法相容但实际上是不必要的规则。这些规则不会影响语言的正确性或终止性,但它们可能增加了语法规则的复杂度和生成语言的难度
- 从功能上说,程序语言的语句大体可分为执行性语句和说明性语句两大类
- 单词符号一般可分为五种:关键字、标识符、常数、运算符、界符
- 超前搜索:在词法分析过程中,有时为了确定词性,需超前扫描若干个字符
- 词法分析中不能识别四元式
- 词法分析通常是作为语法分析的一个子函数嵌入调用的。词法分析作为单独的子程序来处理较好
- 语法分析时不一定要先消除文法中存在的左递归,但自顶向下分析需要先消除
- 语法分析的输出是词法分析的输入
- 文法是描述语言的语法结构的形式规则
- 产生式是定义语法范畴的一种书写规则
- LL(1) 和 LR文法 都是无二义性的
- 文法:语言结构的定义和描述,是有穷非空的产生式集合
- 文法 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 是产生式的集合(非空)
- S 是**开始符号/**识别符号
- V N ∩ V T = Φ , S ∈ V N 0 , V = V N ∪ V T V_N∩V_T=Φ,S∈V_{N_0},V=V_N∪V_T VN∩VT=Φ,S∈VN0,V=VN∪VT,称为文法 G 的字母表,它是出现文法产生式中的一切符号的集合。
- 文法 G 所描述的语言用 L(G) 表示,它由文法 G 所产生的全部句子组成,即:
- L ( G ) = { x ∣ S ⇒ ∗ x ,其中 S 为文法开始符号,且 x ∈ V T ∗ } L(G)=\{x| S⇒*x,其中 S 为文法开始符号,且x∈V^*_T\} L(G)={x∣S⇒∗x,其中S为文法开始符号,且x∈VT∗}
- 文法的属性一般包括综合属性和继承属性。终结符和非终结符都具有这两种属性
- 二义性文法:如果一个文法存在某个句子对应两棵不同的语法树,则称这个文法是二义性文法
- 如果文法 G 是无二义的,则它的任何句子最右推导和最左推导对应的语法树必定相同
- 一个上下文无关文法 G 包括四个部分:终结符号、非终结符号、开始符号、一组产生式
- 一个上下文无关文法 G 是一个四元式 ( V T , V N , S , P ) (V_T,V_N,S,P) (VT,VN,S,P),其中:
- V T V_T VT 是一个非空有限集,它的每个元素称为终结符号;
- V N V_N VN 是一个非空有限集,它的每个元素称为非终结符号, V T ∪ V N = Φ V_T∪V_N=Φ VT∪VN=Φ;
- S 是一个非终结符号,称为开始符号,开始符号 S 至少必须在某个产生式的左部出现一次
- P 是一个产生式集合(有限),每个产生式的形式是 P→α,其中 P ∈ V N , α ∈ ( V T ∪ V N ) ∗ P∈V_N,α∈(V_T∪V_N)^* P∈VN,α∈(VT∪VN)∗
- 一个文法产生无穷个句子,一般都是递归文法
- 一个文法描述的语言是唯一的,但描述一个语言的文法是不唯一的
- 文法 G 产生的句子的全体是该文法描述的语言
- 一个上下文无关文法的开始符只能是非终结符
- 对文法的产生式来说,其左部可以是非终结符的串,右部可以是终结符或非终结符的串
- 数据空间的使用和管理方法:静态存储分配、栈式动态存储分配、堆式动态存储分配
- 对于数据空间的存贮分配, FORTRAN 采用静态策略, PASCAL 采用动态策略
- 确定有限自动机 DFA 是非确定有限自动机 NFA 的一个特例。本质上 DFA 其实可以看作 NFA
- NFA 本质上存在一个ε集或者存在同一个状态引出的具有两条相同的弧的一个自动机
- NFA 和 DFA 都是个五元组:状态集合、输入符号集合、转移函数、开始状态、终止状态集合
- DFA 和 NFA 的成分区别:开始状态和转换函数
- 转换
- DFA 在任何状态下都只能进行唯一的状态转换
- NFA 则可以根据输入符号进行零个或多个状态转换
- 持续
- DFA 在每个输入符号后只进行一次状态转换
- NFA 在每个输入符号下可能进行多个状态转换,这意味着 NFA 在某些情况下可能停留在同一个状态中,或者在两个状态之间切换多次,而DFA则不能。
- 状态
- NFA 中,状态之间的转移可以具有空弧转移,即转移没有特定的输入符号。这意味着NFA 可以在某些情况下跳过一些符号
- DFA 则无法进行空弧转移
- 状态图
- DAF 状态图中每个状态只有一条出边
- NFA 中的状态可以有多条出边
- 状态数
- NFA 具有更多的自由度和灵活性,因此它通常具有比 DFA 更少的状态。由于 NFA 需要在运行时进行更多的计算,因此 NFA 的执行速度可能不如 DFA
- DNF 状态数一般来说较多,但运行速度较快
- 转换
- 关于 NFA
- 字母表必须是有穷集合
- 初始状态集合不能为空
- 终止状态集合可为空
- 状态集合须是有穷集合
- 1 型语言(短语文法)可由图灵机识别
- 1 型语言(上下文有关文法)可由线性限界自动机识别
- 2 型语言(上下文无关文法)可由下推自动机识别
- 3 型语言(正规文法)可由有限自动机识别
- 对应 Chomsky 四种文法的四种语言之间的关系是: L 1 ⊂ L 2 ⊂ L 3 ⊂ L 4 L1⊂L2⊂L3⊂L4 L1⊂L2⊂L3⊂L4
- LALR(1) 只能解决移归冲突,不能解决归归冲突
- LALR(1) 和 LR(1) 分析发现错误的时间可能不同,但发现错误的位置还是确切的
- LL(1) 分析表中的空白表示需要进行出错处理
- 对符号表的任何一个串 t 和某个 DFA 而言,若存在一条从初态到终态的道路,这条道路上
所有标记的符号按顺序连成的串刚好是 t,则称 t 可为该自动机所识别。 - 对任意一个右线性正规文法 G,都存在一个 NFA M,满足 L(G)=L(M)
- 对任意一个右线性正规文法 G,都存在一个 DFA M,满足 L(G)= L(M)
- 对任何正规式 e,都存在一个 NFA M,满足 L(M)=L(e)
- 对任何正规式 e,都存在一个 DFA M,满足 L(M)=L(e)
- 确定有限自动机 DFA 以及非确定有限自动机 NFA 都能正确地识别正规集
- 二义文法是上下文无关文法
- 文法的距离指的是该文法中两个产生式规则之间所需的步骤或长度
- 二义文法的距离是不确定
- 非二义文法的距离一定,长度由直接短语决定
- 两个正规式 M1、M2 等价: M1 和 M2 所识别的语言集相等
- 两个文法 G[S1]、G[S2]等价:两个文法产生的语言是一样的
- 对于正规文法 G 和有限自动机 M,若 L ( G ) = L ( M ) L(G)=L(M) L(G)=L(M),则称 G 和 M 是(等价)的
- 最右推导亦称为规范推导,而由此得到的句型称规范句型
- 最左推导:任何一步 α ⇒ β α ⇒ β α⇒β 都是对 α 中的最右非终结符替换
- 规范句型:由规范推导所得到的句型
- 规范归约中的可归约串是指句柄
- 短语/子树:令 G 是一个文法,S 是的开始符号,假定 α β δ αβδ αβδ 是文法 G 的一个句型,如果有 S → α A δ S → αAδ S→αAδ 且 A → β A → β A→β,则称 β 是句型 α β δ αβδ αβδ 相对非终结符 A 的短语
- 直接短语/简单短语/简单子树:高度为 2 的子树
- 句柄:一个句型的最左直接短语
- 素短语:至少含有一个终结符的短语,并且不再含任何更小的素短语(和直接短语没关系)
- 最左素短语:最左边的素短语
-
推导:若 S 1 ⇒ S 2 ⇒ … ⇒ S n S1 ⇒ S2 ⇒ … ⇒ Sn S1⇒S2⇒…⇒Sn,则称这个序列是从 S1 到 Sn 的一个推导
-
句型:设文法 G 的开始符号为 S,如果 S ⇒ ∗ ⇒ α S⇒*⇒α S⇒∗⇒α 则称 α 是 L(G) 的一个句型
-
句子:对于文法 G,仅含终结符号(可多个)的**句型,**句子一定是一个句型
-
右句型:由最右推导所得出来的这么一个右句型
-
一个右句型的直接短语称为该句型的句柄
-
语言:文法 G 所产生的句子的集合
-
语法:一组规则,用它可形成和产生一组合式的程序
-
语义:定义程序的意义的一组规则
- 算符优先分析法每次都是对最左素短语进行归约
- 算符优先分析中的可归约串是指最左素短语
-
一个名字的属性包括类型和作用域
-
符号表:类型、名称、所占单元大小、地址
-
参数传递方式:传地址、传值、传名
-
DISPLAY 表:嵌套层次显示表,记录该过程的各外层过程的最新活动记录的起始地址
-
基本块:指程序中一顺序执行的语句序列,其中只有一个入口和一个出口,入口就是其中的第一个语句,出口就是其中的最后一个语句。
-
语法制导翻译:在语法分析过程中,根据每个产生式所对应的语义子程序进行翻译的办法叫做语法制导翻译。
-
一张转换图只包含有限个状态,其中有一个被认为是初态;而且实际上至少要有一个终态
-
预测分析程序是使用一张分析表和一个符号栈进行联合控制的
-
待用信息:如果在一个基本块中,四元式 i 对 A 定值,四元式 j 要引用 A 值,而从 i 到 j 之间没有 A 的其它定值,则称 j 是四元式 i 的变量 A 的待用信息。
-
波兰法的表达式亦称前缀式
-
逆波兰法的表达式亦称后缀式
-
多余的规则主要以两种形式出现,有关的非终结符要么是不可到达的,要么是不可终止的
-
四元式之间的联系是通过临时变量实现的
-
使用间接三元式表示法的主要目的便于优化处理
-
在 LR 分析法中,分析栈中存放的状态是识别规范句型活前缀的 DFA 状态
- 在 LR 分析法中,分析栈(Parsing Stack)是用来记录语法分析过程中的状态转换和符号的数据结构。当输入符号串被作为字符串读入后,分析栈用于跟踪分析器的当前状态以及分析器需要比较的符号。
- 分析栈中的状态是 LR 自动机的状态,也就是识别规范句型的 DFA 状态
- 在 LR 自动机中,DFA 状态表示了一个状态集,这个状态集由语法符号的有序前缀(即活前缀)和堆栈中的状态组成。
-
活前缀是指已经识别的符号串中,还可以扩展成一个非终结符的右部的那一部分
-
活前缀是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号
-
规范句型中形成可归前缀之前,包括可归前缀在内的所有前缀都称为活前缀。这种前缀不包含句柄右边的任何符号。
项目
在文法G中每个产生式的右部适当位置添加一个圆点构成项目。
每个项目的含义是:欲用改产生式归约时,圆点前面的部分为已经识别了的句柄部分,圆点后面的部分为期望的后缀部分。
移进项目: 形如 A -> α • aβ // 对应移进状态,把a移进符号栈
待约项目: 形如 A -> α • Bβ // 对应待约状态,需要等待分析完非终结符B的串再继续分析A的右部
归约项目: 形如 A -> α • // 句柄已形成,可以归约
接受项目: 形如 S' -> S •
初始项目: 形如 S' -> • S
核心项目:S' -> • S
后继项目: 表示同属于一个产生式的项目,但是圆点的位置仅相差一个文法符号,则称后者为前者的后继项目
对于产生式 S -> aAcBe,它有6个项目:
S -> ·aAcBe
S -> a·AcBe
S -> aA·cBe
S -> aAc·Be
S -> aAcB·e
S -> aAcBe·
推导 & 规约
S -> AB
A -> a|t
B -> +CD
C -> a
D -> a
最右推导:S -> AB -> A+CD -> A+Ca -> A+aa -> a+aa
规范归约:a+aa -> A+aa -> A+Ca -> A+CD -> AB -> S
最左推导:S -> AB -> aB -> a+CD -> a+aD -> a+aa
最右推导 也称为 规范推导,用 规范推导 推导出的句型称为 规范句型
规范推导 的逆过程称为 最左归约,也称为 规范归约
产生式右部的最长公共前缀
A -> abcdefg
B -> abcdxyz
C -> abcefg
它们的最长公共前缀是 "abc"
对于 LL(1) 文法来说,每个非终结符的所有产生式的右部的最长公共前缀必须为空串或不存在,否则该文法不满足 LL(1) 文法的定义。
拓广文法
对于文法 G = (VN, VT, P, S), 增加如下产生式:S' -> S
其中 S' ∈ VN ∪ VT , 得到 G 的拓广文法,G' = (VN', VT, P', S')
其实就是增加了一条右部为开始符号的产生式,就变成了拓广文法。
已知文法G[E]如下:
(1) E -> E + T
(2) E -> T
(3) T -> ( E )
(4) T -> d
它的拓广文法G'[E']为:
(0) E' -> E
(1) E -> E + T
(2) E -> T
(3) T -> ( E )
(4) T -> d
波兰式 & 逆波兰式
1920年,波兰科学家扬·武卡谢维奇(Jan ukasiewicz)发明了一种不需要括号的计算表达式的表示法将操作符号写在操作数之前,也就是前缀表达式,即波兰式(Polish Notation, PN)
a+b*(c-d)+e/f
波兰式 :+a*b-cd/ef
逆波兰式:abcd-*+ef/+
逆波兰式也叫做后缀表达式,它不需要括号来明确优先级,而是通过后缀表达式中操作符的顺序来确定运算的优先级。
逆波兰式的计算方法比较简单,可以通过栈来实现,从左到右扫描表达式,如果扫描到操作数就将其压入栈中,如果扫描到操作符则弹出两个操作数,进行相应的计算,将计算结果压入栈中,如此一直处理直到整个表达式都被扫描完。最后栈顶的元素即为该表达式的计算结果。
转换算法:
1.通过利用运算符优先级和结合性进行求解,称为中缀表达式转换为逆波兰式:
中缀表达式 3+4*2/(1-5)^2 的逆波兰式表示为 342*15-2^/+
(┐A⋁B)⋀(C⋁D) -- A┐B⋁CD⋁⋀
a:=(b+c)*e+(b+c)/f -- abc+e*bc+f/+:=
a+b*(c-d)/e -- abcd-*e/+
2.通过利用文法规则进行求解,称为表达式文法的转换算法
冲突
没有这两种冲突的文法是 LR(0) 文法,不是所有的上下文无关文法都可以用 LR(0) 文法分析
移进-规约冲突
一个结束,一个没结束
规约-规约冲突
两个同时结束
推导
对于文法
E->E+E E->(E) E->E*E E->id
最右推导:
E=>E*E
=>E*E+E
=>E*E+id
=>E*id+id
=>id*id+id
# 例1
E -> T | E + T | E - T
T -> F | T * F | T / F
F -> (E) | i
--> i * i + i
# 最左推导:
E -> E + T
-> T + T
-> F + T
-> i + T
-> i + T * F
-> i + F * F
-> i + i * F
-> i + i * i
# 最右推导:
E -> E + T
-> E + T * F
-> E + T * i
-> E + F * i
-> E + i * i
-> T + i * i
-> F + i * i
-> i + i * i
# 例2
E -> T | E + T |E - T
T -> F | T * F | T / F
F -> (E) | i
--> i * (i + i)
# 最左推导:
E -> T
-> T * F
-> F * F
-> i * F
-> i * (E)
-> i * (E + T)
-> i * (T + T)
-> i * (F + T)
-> i * (i + T)
-> i * (i + F)
-> i * (i + i)
# 最右推导:
E -> T
-> T * F
-> T * (E)
-> T * (E + T)
-> T * (E + F)
-> T * (E + i)
-> T * (T + i)
-> T * (F + i)
-> T * (i + i)
-> F * (i + i)
-> i * (i + i)
左递归解决
直接左递归:
- 第一条:右部递归项 移到最右,全加 ``(红色箭头)(全都变 S` )
- 第二条:左部含递归项的,右部最右加 `(红色横线,+)
- 第三条:` 加条空(红色波浪)
间接左递归:
- 全部代入(绿色)
- 和消除直接左递归步骤应该差不多了
不太会表述。。。。
短语、句柄、直接短语、素短语、最左素短语
- 短语:以非终结符为根子树中所有从左到右的叶子
- 直接短语:只有父子关系的树中所有从左到右排列的叶子(树高为2)
- 句柄:最左边父子关系树中所有从左到右排列的叶子(句柄是唯一的)
- 句型的句柄是该句型中和一个产生式右部匹配的子串并把它规约成该产生式左部的非终结符代表了最右推导过程中的逆过程的一步
- 句柄的右边仅仅含有终结符或者空符
- 只有文法无二义性,每个右句型才有唯一句柄
注意
以下可能不太对
四种文法
正则文法、上下文无关文法、上下文相关文法、正规文法
Chomsky 根据对产生式要求的不同,将文法分为4类,通常称为 Chomsky 体系
图片判断
LR(0) 文法
没有任何冲突的:
SLR(1) 文法
只有移进-归约冲突的:
LALR(1) 文法
lookahead LR 方法,实际编译器经常采用这种方法
LALR 合并同心集,即合并产生式相同但是向前搜索符不同的项集族
- 不需要合并同心集的,如果有移进 — 规约,求要规约的非终结符的 Follow,如果包含移进项目的下一个终结符,则为 LALR,否则为 SLR
~~LALR(1)
S -> d·c ,#
A -> d· ,a/c~~
LALR(1):
S -> d·c ,#
A -> d· ,c
Follow(A) = {a,c}
SLR(1):
S -> d·c ,#
A -> d· ,c
- 需要合并同心集的,合并后不会产生任何冲突,如果产生冲突就是 LR(1)
合并同心项集后,虽然不产生冲突,但可能会推迟错误的发现
LALR 分析法可能会作多余的归约动作,但绝不会作错误的移进操作
LR(1) 文法
文法几乎都是 LR(1)的,现在知道的只有当合并同心集产生了归约-归约冲突时才只属于 LR(1) 文法,而不属于其他文法。
SLR(1) 分析例子
参考:简书
- 在步骤2,3,4中,因为在(从状态栈中看)当前状态的上一个状态,也就是0,它可以接受E,F,T(看上表状态0的GOTO),所以GOTO就从0走。
- 在步骤12中,因为7,9都不能接受T,只有6能接受T(依次看上表状态7、9、6 的GOTO),所以GOTO 使用 6
例题
题目是不是很熟悉(本校的话!)