编译原理复习

编译原理复习

Chapter 1:引言

  • 程序设计语言
    • 翻译程序:将一种语言描述的程序翻译成等价的另一种语言描述的程序
    • 解释程序:一边解释一边执行的翻译程序
  • 程序设计语言的翻译
  • 编译程序的总体结构
    • 词法分析器:又称为scanner
    • 语法分析器:又称为parser
    • 语义分析与中间代码生成:波兰表示:前缀表示
    • 代码优化器
      • 与机器无关的优化:常量合并、循环优化
      • 与机器有关的优化:寄存器的利用、体系结构、存储策略、任务划分
    • 目标代码生成器
    • 表格管理
    • 错误处理
  • 编译程序的组织
  • 编译程序的生成
    • T形图:上面为源语言和目标语言,下面为工具语言。
    • 自展
    • 移植:
      • C语言可以作为工具语言,实现任意源语言转换为任意目标语言
      • 如果某机器上有C语言编译器,则该机器语言可以作为工具,将C语言转换为机器语言

Chapter 2:高级语言及其文法

2.1 语言概述

  • 描述形式:文法:G=(V,T,P,S)

2.2 基本定义

  • 字母表 Σ \Sigma Σ,如{a,b,c,d}

  • 假设A,B为两个字母表

    加法: A + B = { ω ∣ ω ∈ A   o r   ω ∈ B } A+B=\{ \omega|\omega\in A \ or \ \omega \in B\} A+B={ωωA or ωB}

    乘积: A ∗ B = { a b ∣ a ∈ A , b ∈ B } A*B = \{ab|a \in A,b \in B \} AB={abaA,bB}

    n次幂: A 0 = { ε } A^0=\{\varepsilon \} A0={ε}

    A n = A n − 1 A A^n=A^{n-1}A An=An1A

    正闭包: A + = A ∪ A 2 ∪ A 3 . . . . . . A^+=A \cup A^2 \cup A^3...... A+=AA2A3......,例如: { 0 , 1 } + = { 0 , 1 , 00 , 01 , 10 , 11 , 000 , 001 , 010 , 011 , 100 , . . . . . . } \{0,1\}^+=\{0,1,00,01,10,11,000,001,010,011,100,......\} {0,1}+={0,1,00,01,10,11,000,001,010,011,100,......}

    克林闭包: A ∗ = A 0 ∪ A + A^*=A^0 \cup A^+ A=A0A+, 例如: { 0 , 1 } ∗ = { ε , 0 , 1 , 00 , 01 , 10 , 11 , 000 , 001 , 010 , 011 , 100 , . . . . . . } \{0,1\}^*=\{\varepsilon,0,1,00,01,10,11,000,001,010,011,100,......\} {0,1}={ε,0,1,00,01,10,11,000,001,010,011,100,......}

  • 句子: ∀ x ∈ A ∗ , x 称 为 一 个 句 子 , ε 叫 做 空 句 子 \forall x \in A^*,x称为一个句子,\varepsilon 叫做空句子 xA,xε

  • 前缀: 对 ∀ x , y , z ∈ A ∗ , 如 果 x = y z , 则 y 是 x 的 前 缀 , z 不 为 ε 时 称 作 真 前 缀 对\forall x,y,z \in A^*,如果x=yz,则y是x的前缀,z不为\varepsilon 时称作真前缀 x,y,zA,x=yz,yxzε

    后缀: 对 ∀ x , y , z ∈ A ∗ , 如 果 x = y z , 则 z 是 x 的 后 缀 , y 不 为 ε 时 称 作 真 后 缀 对\forall x,y,z \in A^*,如果x=yz,则z是x的后缀,y不为\varepsilon 时称作真后缀 x,y,zA,x=yz,zxyε

  • 语言: ∀ L ⊆ Σ ∗ \forall L \subseteq\Sigma ^* LΣ,L称为字母表 Σ \Sigma Σ上的语言

2.3 文法的定义

  • 文法的定义: G = ( V , T , P , S ) G=(V,T,P,S) G=(V,T,P,S)
  • 文法产生的语言 L ( G ) L(G) L(G),即可由开始符号S推导出来的由终结符组成字符串的集合
  • 句型:对于 S ⇒ a , 如 果 a ∈ ( V ∪ T ) ∗ S\Rightarrow a ,如果a \in (V \cup T)^* Sa,a(VT),则称a为句型
  • 句子:对于 S ⇒ a , 如 果 a ∈ T ∗ S\Rightarrow a,如果a \in T^* Sa,aT,则称a为句子

2.4 文法的分类(Chomsky体系)

上下文有关文法CSG

  • 如果对于 ∀ a → b , 均 有 ∣ b ∣ > = ∣ a ∣ 成 立 \forall a \rightarrow b,均有|b|>=|a|成立 ab,b>=a,则称为1型文法(除去 S → ε S \rightarrow \varepsilon Sε

上下文无关文法CFG

  • 如果对于 ∀ a → b , 均 有 ∣ b ∣ > = ∣ a ∣ 成 立 \forall a \rightarrow b,均有|b|>=|a|成立 ab,b>=a,且a为非终结符,则成为2型文法
  • 其识别系统是不确定的下推自动机

正规文法RG

  • 如果为下述样式则称为3型文法:

    A → a B 或 A → a A \rightarrow aB或A \rightarrow a AaBAa(左线性)

    A → B a 或 A → a A \rightarrow Ba或A \rightarrow a ABaAa(右线性)

  • 其识别系统是有穷自动机

2.5 CFG 的语法树

  • 又称为分析树、推导树、派生树

  • 短语:一颗子树的所有叶子自左向右排列起来,形成一个相对于子树根的短语。

  • 直接短语:仅仅有父子两代的一颗子树的所有叶子

  • 句柄:一个句型的分析树中的最左那颗只有父子两代的子树的所有叶子的自左至右排列。

编译原理复习_第1张图片

  • 最左推导、最右推导

2.6 CFG 的二义性

  • 二义性:对同一句子存在两颗语法树.在理论上不可判定

Chapter 3:词法分析

3.1 词法分析器的功能

  • 功能:输入源程序,输出单词符号(token)

    • 根据词法规则识别、组合单词,进行词法检查
    • 将数字字符串转化为二进制数值
    • 删去空格以及注释
  • 单词的种类

    • 关键字:begin、end…

    • 标识符:用户定义的各种名字

    • 常数

    • 运算符:+,-

    • 分界符:, . ;

      一般对种类编码时,保留字和分界符最好每一个都单独编码。

3.2 单词的描述

  • 正则文法
  • 正则表达式:

正则表达式和正则文法等价:教材p77,可以看一下

如图为正则表达式转换为正则文法的例题:

编译原理复习_第2张图片

正则文法转换为正则表达式的例题:
编译原理复习_第3张图片

  • 确定的有穷自动机(DFA):

    五元组: M = ( Q , Σ , δ , q 0 , F ) M=(Q,\Sigma,\delta,q_0,F) M=(Q,Σ,δ,q0,F),依次为:有穷状态集合、字母表、状态转移映射、初始状态、终止状态

  • DFA所接受的语言:

    L ( M ) = { ω ∣ ω ∈ Σ ∗ , 且 δ ( q 0 , ω ) ∈ F } L(M)=\{\omega| \omega \in \Sigma^*,且\delta(q_0,\omega) \in F\} L(M)={ωωΣ,δ(q0,ω)F}.

  • 状态转换图

3.3 单词的识别

  • 有穷状态自动机和正则文法等价。

  • 单词识别:

编译原理复习_第4张图片

3.4 词法分析程序的自动生成

Chapter 4:自顶向下的语法分析

4.1 语法分析概述

  • 自 顶 向 下 : { 递 归 子 程 序 法 预 测 分 析 法 ( L L ( 1 ) ) 自顶向下: \begin {cases} 递归子程序法 \\ 预测分析法(LL(1)) \end {cases} {(LL(1))

    自 底 向 上 { 算 符 优 先 分 析 法 L R ( 0 ) , S L R ( 1 ) , L R ( 1 ) , L A L R ( 1 ) 自底向上 \begin {cases} 算符优先分析法 \\ LR(0),SLR(1),LR(1),LALR(1) \end {cases} {LR(0),SLR(1),LR(1),LALR(1)

4.2 自顶向下的语法分析面临的问题

  • 二义性问题

  • 回溯问题:对于产生式A,如果A有多个候选式存在公共前缀,则自顶向下的语法分析程序将无法根据当前输入符号准确选择用于推导的产生式。此时需要向前试探,如果不正确则需要回溯。

  • 左递归引起的无穷推导:

    • 假设A时文法G的某个语法变量,如果存在推导 A ⇒ A b A \Rightarrow Ab AAb,则称该推导是左递归的。
  • 对上下文无关文法的改造

    • 消除二义性

    • 消除左递归

      • A → A a ∣ b ⇒ { A → b A ′ A ′ → a A ′ ∣ ε A \rightarrow Aa|b \Rightarrow \begin {cases} A \rightarrow bA' \\ A' \rightarrow aA'|\varepsilon \end {cases} AAab{AbAAaAε
    • 提取左因子:
      如 果 有 产 生 式 : A → a b 1 ∣ a b 2 . . . 则 可 以 用 下 面 的 产 生 式 替 换 { A → a A ′ A ′ → b 1 ∣ b 2 . . . 如果有产生式:A \rightarrow ab_1|ab_2... \\ 则可以用下面的产生式替换\begin {cases} A \rightarrow aA' \\ A' \rightarrow b_1|b_2... \end {cases} Aab1ab2...{AaAAb1b2...

  • LL(1)文法

    • FIRST(a):从a推导出的串的首符号

      • 如果a是终结符,那么a 的FIRST集是自己本身;
      • 如果a是非终结符:
        • 如果a能推出空,则将{ ε \varepsilon ε }加入FIRST集;
        • a → b a \rightarrow b ab 这种形式的b加入FIRST集;
        • 如果 a → B . . . a \rightarrow B... aB...,B为终结符,则FIRST(a)需要将 FIRST(B)-{ ε \varepsilon ε}并入。
        • 如果 a → B C D . . . 且 B ⇒ ε a \rightarrow BCD... 且B\Rightarrow \varepsilon aBCD...Bε,则FIRST(a)需要将 FIRST©-{ ε \varepsilon ε}并入。
    • FOLLOW(A) F O L L O W ( A ) = { b ∣ S ⇒ ∗ a A b β , b ∈ T , a , b ∈ ( V ∪ T ) ∗ } FOLLOW(A)=\{ b|S \overset{*} {\Rightarrow } aAb\beta ,b \in T,a,b \in (V \cup T)^*\} FOLLOW(A)={bSaAbβ,bT,a,b(VT)}.

      • 如果a是非终结符,首先将FOLLOW集置为 ∅ \empty
      • FOLLOW(S)={#};
      • A → a B C A \rightarrow aBC AaBC,则 F O L L O W ( B ) = F O L L O W ( B ) ∪ F I R S T ( C ) − ε FOLLOW(B)=FOLLOW(B)\cup FIRST(C)-{\varepsilon} FOLLOW(B)=FOLLOW(B)FIRST(C)ε
      • A → a B A \rightarrow aB AaB A → a B β A \rightarrow aB\beta AaBβ,且 β ⇒ ε \beta \Rightarrow \varepsilon βε,则 F O L L O W ( B ) = F O L L O W ( B ) ∪ F O L L O W ( A ) FOLLOW(B)=FOLLOW(B)\cup FOLLOW(A) FOLLOW(B)=FOLLOW(B)FOLLOW(A)
    • 对于一个文法G,如果G的==任意两个具有相同左部的产生式 A → a ∣ b A \rightarrow a|b Aab==满足以下条件:

      • 如果a,b均不能推导出 ε \varepsilon ε ,则 F I R S T ( a ) ∩ F I R S T ( b ) = ∅ FIRST(a) \cap FIRST(b)=\empty FIRST(a)FIRST(b)=
      • a,b至多有一个能推导出 ε \varepsilon ε
      • 如果b能推导出 ε \varepsilon ε ,则 F I R S T ( a ) ∩ F O L L O W ( A ) = ∅ FIRST(a)\cap FOLLOW(A)=\empty FIRST(a)FOLLOW(A)=

      称G为LL(1)文法

4.3 预测分析法

  • 步骤:

    • 构造文法
    • 改造文法:消除二义性、消除左递归、提取左因子。
    • 求每个候选式的FIRST集和FOLLOW集。
    • 检查是不是LL(1)文法
    • 构造预测分析表
    • 实现预测分析器
  • 关键:构造预测分析表

    • 对于任意产生式S,将S的first集中对应的终结符a(S ,a)处填上该产生式。
    • 如果在first集中存在 ε \varepsilon ε,则将S的follow集中对应的终结符a(S ,a)处填上 ε \varepsilon ε产生式。
    • 如果在first集中存在 ε \varepsilon ε,且follow集中包含#,则将该 ε \varepsilon ε产生式填在(S,#)。
  • 在启动时,输入指针指向输入串的第一个字符,分析栈中存放栈底符号#和文法开始符号S

  • 根据栈顶符号A和读入的符号a,查看分析表,以进行相应的动作。

  • E → T E ′ E\rightarrow TE' ETE,执行动作时,将E弹出栈,然后先将E’入栈,再将T入栈
    编译原理复习_第5张图片
    预测分析表

4.4 递归下降分析法

  • 对应每个变量设置一个处理子程序
    编译原理复习_第6张图片

  • 优点:直观、简单、可读性好;便于扩充

  • 缺点:递归算法的实现效率低;处理能力相对有限;通用性差,难以自动生成

Chapter 5:自底向上的语法分析

5.1 自底向上的语法分析概述

  • 思想:从输入串出发,反复利用产生式进行规约,如果最后能得到文法的开始符号,则输入串是句子。一旦句柄在栈顶形成,就将其弹出归约。

  • 移进-归约分析:分析栈、输入缓冲区

编译原理复习_第7张图片

  • 四种动作:移进,归约,接受,出错

    有什么问题?

    • 移进-归约冲突:可以移进,也可以按照产生式归约
    • 归约-规约冲突:存在两个可用的产生式
  • 如何确定句柄的开始处与结束处?

    • 优先法:根据归约的先后次序,为句型中相邻的文法符号规定优先关系。句柄两端符号的优先级高于句柄外与之相邻的符号
    • 状态法:根据句柄的识别状态,进行状态分析。

5.2 算符优先分析法

  • 算符文法:如果文法中不存在: A → a B C β A \rightarrow aBC\beta AaBCβ的产生式,即不具有相邻非终结符的产生式,则称为算符文法。

  • 算符优先文法:假设G是一个不含$\varepsilon $-产生式的文法:

    • a ≡ b a \equiv b ab,当且仅当文法G中含有形如==$A \rightarrow …ab… 或者A \rightarrow …aBb… $==的产生式
    • a < b a a<b,当且仅当含有== A → . . . a B . . . 且 B ⇒ + b . . . 或 B ⇒ + C b . . . A\rightarrow ...aB...且B\overset{+}{\Rightarrow} b... 或B\overset{+}{\Rightarrow} Cb... A...aB...B+b...B+Cb...==的产生式
    • a > b a >b a>b,当且仅当含有== A → . . . B b . . . 且 B ⇒ + . . . a 或 B ⇒ + . . . a C A\rightarrow ...Bb...且B\overset{+}{\Rightarrow} ...a 或B\overset{+}{\Rightarrow} ...aC A...Bb...B+...aB+...aC==的产生式
    • a与b无关,当且仅当a与b在G的任何句型中都不相邻。

    如果对于不含$\varepsilon $-产生式的G的任意a,b,a和b至多有一种关系成立,则称G为算符优先文法。

    FIRSTOP(B):以B为左部的产生式的最开头的终结符

    LASTOP(B):以B为左部的产生式的最末尾的终结符

  • 原理:如果栈中的符号<输入符号,则移进,如果> ,则归约

  • 问题:有时未归约真正的句柄(S);不是严格的最左归约

  • 素短语:至少含有一个终结符,且不含更小的含终结符的短语。

  • 最左素短语:算符优先分析过程中识别出来的“句柄”(不是真正意义上的句柄),即在树状图中的素短语最左的那个

  • 优先函数:用两个优先选择函数f和g,做一个从终结符到整数的映射。

  • 优先函数的损失:id>id不存在,但是f(id)和g(id)可以比较

  • 优先函数的构造:对于终结符a或者#,建立 f a , g a f_a,g_a fa,ga,并构造有向图。

  • 算符优先文法简单小结:

    • 优点:简单效率高;能够处理部分二义性文法。
    • 缺点:限制大(优先关系唯一);占用内存大;不规范,存在查不到的语法错误;发现最左素短语的尾时,需要回头寻找对应的头。

5.3 LR分析法

  • L:从左到右扫描输入符号;R:最右推导对应的最左归约;k:超前读入k个符号

  • 总体结构:

编译原理复习_第8张图片

  • 规范句型:分析栈中内容+剩余输入符号,即最左归约形成的句型。

  • 活前缀:不含句柄右侧任意符号的规范句型的前缀(活前缀在分析栈中,可以说活前缀就是句柄的子集

  • LR(0)分析表的构造

    • LR(0)项目:右部某个位置标有圆点的产生式称为相应文法的LR(0)项目。

    • 扩广文法:添加一个S’

    • LR(0)项目集规范族:从 I 0 I_0 I0出发,构造一个识别所有规范句型活前缀的DFA, I i I_i Ii的集合

    • 示例如图:IMG_0439(20221114-163052)编译原理复习_第9张图片

    • 构造分析表

      • 首先根据DFA,将所有的状态转化操作填入Action表和GoTo表中
      • 将所有可以归约的状态,将Action表中的一整行对应填入 r i r_i ri
  • 上下文无关文法不总是LR(0)文法

  • 项目集 I I I的相容:

    • 如果I中至少含有两个归约项目,则称I有归约-归约冲突
    • 如果I中既含归约项目,又含移进项目,则称I有移进-归约冲突
    • 如果I以上两种冲突均没有,则称I是相容的。
  • SLR(1)分析表的构造

    • 对于项目集 I I I

      ​ 如果I中存在归约项目如: B → a b c . B \rightarrow abc. Babc. ,那么在下一个输入符号d来临时,假如d在B的FOLLOW集中,则进行归约操作,反之进行移入操作。

    • 局限性:仅使用FOLLOW集依然有可能产生冲突

    • 构造分析表

      • 首先根据DFA,将所有的状态转化操作填入Action表和GoTo表中
      • 将所有可以归约的状态,如归约式子为: B → a b c . B \rightarrow abc. Babc.,将Action表中的FOLLOW(B)集中的终结符下方,对应填入 r i r_i ri
  • LR(1)分析表的构造

    • 对于项目集 I I I

      ​ 如果I中存在项目如: B → . a b c B \rightarrow .abc B.abc ,需要同步填上B的后继符号。后继符号需要通过上下文关系得到

      示例如图:IMG_0438(20221114-163023)编译原理复习_第10张图片

    • 构造分析表

      • 首先根据DFA,将所有的状态转化操作填入Action表和GoTo表中
      • 将所有可以归约的状态,若存在归约式为 B → a b c . , a / b B \rightarrow abc.,a/b Babc.,a/b,将Action表中该状态的a,b列对应填入 r i r_i ri
  • LALR(1)分析表的构造

    • 在不带来归约归约冲突的情况下,合并部分状态,重构分析表。
    • 缺点:延迟问题语句的发现
  • LR分析的基本步骤

    • 编写扩广文法,求FOLLOW集
    • 求识别所有活前缀的DFA
    • 构造LR分析表

5.4 语法分析程序的自动生成工具Yacc

Chapter 6:语法制导翻译与属性文法

6.1 语法制导翻译概述

  • 静态语义分析或静态检查:检查各个语法结构的静态语义。
  • 语法制导翻译:将静态检查和中间代码生成结合到语法分析中进行的技术。
  • 语义属性:一个文法符号X所携带的语义信息
  • 语义处理方式:
    • 语法制导定义:对应每一个产生式编写一个语义子程序,当一个产生式获得匹配时,就调用相应的语义子程序来实现语义检查和翻译。(适宜在归约的时候完成)
    • 语法制导翻译模式:通过将属性与文法符号关联,并将语义规则插入到产生式的右部来描述语言结构的翻译方案。(适宜在推导的时候完成)

6.2 语法制导定义

  • 语法制导定义是附带有属性和语义规则的上下文无关文法
  • 综合属性:节点的属性值是通过分析树中该节点或其子节点的属性值计算出来的
  • 继承属性:节点的属性值是由该节点该节点的兄弟节点或父节点的属性值计算出来的
  • 固有属性:通过词法分析直接得到的属性

6.3 属性计算

  • S-属性定义:只含有综合属性的语法制导定义称为S属性定义。

    通常使用自底向上的分析方法。即在用哪个产生式进行归约后,就执行那个产生式的S-属性定义计算属性的值。

  • L-属性定义:当且仅当它的每个属性是综合属性,或者是满足如下条件的继承属性:设有产生式 A → X 1 X 2 . . . X n A\rightarrow X_1X_2...X_n AX1X2...Xn,其右部符号 X i X_i Xi的继承属性只依赖于下列属性:

    • A的继承属性
    • 产生式中 X i X_i Xi左边的符号 X 1 , X 2 . . . X_1,X_2... X1,X2...的综合属性或继承属性
    • X i X_i Xi本身的综合属性或继承属性,但是前提是 X i X_i Xi的属性不能再依赖图中形成回路
  • 语法树

6.4 翻译模式

  • 翻译模式:是语法制导定义的一种便于实现的书写形式。语义规则或语义动作用{}括起来,并可以被插入到产生式右部的任何合适的位置上。

  • 翻译模式的设计:根据语法制导定义:

    • 语法制导定义是L-属性定义

      • 既有综合属性又有继承属性:
        • 产生式右部符号的继承属性必须在这个符号以前的动作中计算出来
        • 一个动作不能引用这个动作右边符号的综合属性
        • 产生式左边的非终结符号的综合属性,一般在末尾进行计算。
    • 语法制导定义是S-属性定义

      • 将每个产生式的动作放在末尾。
  • S-属性定义的自底向上计算

    • 实现:只要将每个语义规则放到相应产生式的末尾即可得到一个和翻译模式,在对产生式进行归约时执行相应的语义动作。

      在分析栈中使用一个附加的域来存放综合属性值。

    • 采用自底向上分析。

  • L-属性定义的自顶向下翻译

    • 用翻译模式构造自顶向下的翻译

      1. 从翻译模式中消除左递归

        • 对于一个翻译模式,若采用自顶向下分析,必须消除左递归和提取左公因子。

        • 将语义动作看作终结符号,当其处在栈顶时被弹出执行

        • 为了便于讨论,只看S-属性定义的左递归消除
          设 有 如 下 左 递 归 翻 译 模 式 : { A → A 1 Y      { A . a = g ( A 1 . a , Y . y ) } A → X          { A . a = f ( X . x ) } 则 消 除 左 递 归 后 : { A → X R R → Y R ∣ ε 翻 译 模 式 变 为 : { A → X { R . i = f ( X . x ) } R { A . a = R . s } R → Y { R 1 . i = g ( R . i , Y . y ) } R 1 { R . s = R 1 . s } R → ε { R . s = R . i } 设有如下左递归翻译模式: \begin {cases} A \rightarrow A_1Y \ \ \ \ \{ A.a = g(A_1.a,Y.y)\} \\ A \rightarrow X \ \ \ \ \ \ \ \ \{A.a = f(X.x) \} \end {cases} \\ 则消除左递归后: \begin {cases} A \rightarrow XR \\ R \rightarrow YR|\varepsilon \end {cases} \\ 翻译模式变为: \begin {cases} A \rightarrow X\{R.i = f(X.x) \}R \{A.a = R.s \} \\ R \rightarrow Y\{R_1.i = g(R.i,Y.y) \}R_1 \{R.s = R_1.s \} \\ R \rightarrow \varepsilon \{ R.s = R.i\} \end {cases} \\ {AA1Y    {A.a=g(A1.a,Y.y)}AX        {A.a=f(X.x)}{AXRRYRεAX{R.i=f(X.x)}R{A.a=R.s}RY{R1.i=g(R.i,Y.y)}R1{R.s=R1.s}Rε{R.s=R.i}

      2. L-属性定义的递归下降翻译法

        • L-属性定义的递归下降翻译法的构造:
          • 每个非终结符A构造一个函数A,将非终结符A的各个继承属性当作函数A的形式参数,而将其综合属性当作函数A的返回值。(为了便于讨论假设每个非终结符只有一个综合属性)
          • 函数A的代码首先根据当前的输入确定用哪个产生式展开A
          • 与每个产生式对应的程序代码的工作过程为:按照从左到右的次序,依次对产生式右部的记号、非终结符和语义动作执行如下的动作:
    1. L-属性定义的LL(1)翻译法

      与递归子程序法的区别与联系:

      • 都是在扫描过程中进行产生式的推导,同时在适当的地方加入语义动作
        • 递归子程序法将语义动作融入分析程序;LL(1)分析法则由语义子程序完成相应的翻译
        • 递归子程序法隐式地使用语义栈;LL(1)分析法则用显示的语义栈(此处的语义栈和语法栈不同步)

      语法栈按照LL(1)分析法,将语义子程序视作终结符,当在栈顶时,执行相应的动作对语义栈进行修改。

      当最终语义栈中只有T.node时,代表语法树的根节点

  • L-属性定义的自底向上翻译

    • 在自底向上分析的框架中实现L属性定义的方法
      • 它能实现任何基于LL(1)文法的L属性定义
      • 也能实现大部分的基于LR(1)文法的L属性定义
        1. 根据[L属性的设计](# 6.4-翻译模式),首先将计算继承属性的动作嵌入在非终结符的前面,而将计算综合属性的动作放在产生式的末尾。
        2. 在每个嵌入动作处,引入一个标记性非终结符(M),每个标记性非终结符都有 M → ε M \rightarrow \varepsilon Mε的产生式。
        3. 如果标记性非终结符M取代了某个产生式 A → a { b } β A \rightarrow a\{ b \} \beta Aa{b}β中的动作b,则按如下将b修改为b’,并将b’放在产生式 M → ε M \rightarrow \varepsilon Mε的末尾。
          • 为M设置继承属性来复制动作b所需要的A或a中符号的继承属性
          • 以与动作b相同的方式计算属性,只不过要将这些属性置为M的综合属性。
            编译原理复习_第11张图片

在这里插入图片描述
+ 下面讨论如何在自底向上的分析中实现L-属性定义
+ 给定一个L-属性定义,假设它的每个非终结符A都有一个继承属性A.inh,而且每个文法符号X都有一个综合属性X.syn
+ 如果X是终结符,则其综合属性值就是词法分析器所返回的词法值
+ 假设分析栈仍是stack,语义栈仍为val,如果stack[i]代表文法符号X,那么stack[i].val将保存X.syn
+ 为了在自底向上的分析过程中计算继承属性,需要对每一个产生式引入标记性非终结符M
+ 归约M1时,它出现在产生式 A → M 1 X 1 M 2 X 2 . . . A \rightarrow M_1X_1M_2X_2... AM1X1M2X2...中,从而可以确定为计算继承属性X1.inh所需要的属性的位置。
+ 归约非标记符号时,如 A → M 1 X 1 M 2 X 2 . . . A \rightarrow M_1X_1M_2X_2... AM1X1M2X2...,只需要计算A.syn,而需要的其他属性已经在栈中了。

Chapter 7:语义分析与中间代码生成

7.1 中间代码的形式

  • 优点:形式简单;语义明确;独立于目标语言;便于实现、移植、优化。
  • 三地址码:每条指令最多包含3个地址:两个操作数一个结果
  • 四元式:op,arg1,arg2,result
  • 图表示(dag):每个节点对应一个运算符,叶节点对应变量或常量

7.2 声明语句的翻译

  • 作用:为变量或常量名指定类型

  • 类型表达式

    • 基本类型是类型表达式
    • 类型名是类型表达式
  • 类型等价;

    • 结构等价:T1和T2是相同的类型 ,或T1和T2是将同一类型构造符(record)应用于结构等价的类型上形成的, 或T1是表示T2的类型名
    • 名字等价:两个类型表达式名字等价当且仅当它们完全相同。
  • 声明语句的文法

编译原理复习_第12张图片

  • 过程内声明语句的翻译(自顶向下)

编译原理复习_第13张图片
编译原理复习_第14张图片

  • 嵌套过程中声明语句的翻译

编译原理复习_第15张图片
编译原理复习_第16张图片

7.3 赋值语句的翻译

编译原理复习_第17张图片
编译原理复习_第18张图片

  • 临时变量:大量临时变量的使用对优化有利;但是会增加符号表管理的负担,也会增加运行时临时数据占的空间。

  • 数组元素的寻址:bash+偏移地址

    x = a [ i 1 , i 2 , i 3 ]     o f f s e t = w i d t h ∗ [   [ i 1 ∗ n 2 + i 2 ] ∗ n 3 + i 3 ] x = a[i_1,i_2,i_3] \ \ \ offset=width*[ \ [i_1*n_2+i_2]*n_3+i_3 ] x=a[i1,i2,i3]   offset=width[ [i1n2+i2]n3+i3]

  • 带有数组引用的赋值语句的翻译

7.4 类型检查

  • 类型转换
    • 隐式转换:编译器自动完成
    • 显示转换:程序员给出

7.5 控制结构的翻译

  • 布尔表达式的翻译:

    • 基本文法 B → B 1   o r   B 2 ∣ B 1   a n d   B 2 ∣ n o t   B 1 ∣ ( B 1 ) ∣ E 1   r e l o p   E 2 ∣ t r u e ∣ f a l s e B \rightarrow B_1 \ or \ B_2|B_1 \ and \ B_2|not \ B_1|(B_1)|E_1 \ relop \ E_2|true|false BB1 or B2B1 and B2not B1(B1)E1 relop E2truefalse.
    • 处理方法:
      • 数值表示法:真:B.addr = 1 假:B.addr = 0
        编译原理复习_第19张图片
        编译原理复习_第20张图片

      • 真假出口表示法:真出口:B.true 假:B.false(表示跳转的位置)

  • 常见控制结构的翻译

    • 文法
      S → i f   B   t h e n   S 1 S → i f   B   t h e n   S 1   e l s e   S 2 S → w h i l e   B   d o   S 1 S → b e g i n   S l i s t   e n d S l i s t → S l i s t ; S ∣ S 其 中 B 是 布 尔 表 达 式 S \rightarrow if \ B \ then \ S_1 \\ S \rightarrow if \ B \ then \ S_1 \ else \ S_2 \\ S \rightarrow while \ B \ do \ S_1 \\ S \rightarrow begin \ Slist \ end \\ Slist \rightarrow Slist;S|S \\ 其中B是布尔表达式 Sif B then S1Sif B then S1 else S2Swhile B do S1Sbegin Slist endSlistSlist;SSB

    • 翻译:(例子)
      KaTeX parse error: Undefined control sequence: \ at position 152: …':')||S_1.code \̲ ̲\}

  • 布尔表达式控制流翻译

    • 属性(继承属性):
      • B.true
      • B.false
        编译原理复习_第21张图片

编译原理复习_第22张图片

  • 混合模式的布尔表达式翻译

    • 布尔表达式中通常含有算术子表达式

7.6 回填

  • 回填技术:
    • 先产生暂时没有填写目标标号的转移指令
    • 对于每一条这样的指令,建立一个链表。每个链表中的指令,都需要转移到相同的地方
    • 一旦确定目标标号,再“回填”
  • 布尔表达式的回填式翻译
    编译原理复习_第23张图片

在这里插入图片描述

  • 常见控制结构的回填式翻译
    编译原理复习_第24张图片

  • for循环语句的回填式翻译

  • repeat语句的回填式翻译

Chapter 9:运行时的存储组织

9.1 与存储组织有关的源语言概念与特征

  • 决策:

    • 静态策略:在编译时即可做出决定的策略
    • 动态策略:直到程序执行时才能做出决定的策略
  • 名字及其绑定

    • 名字表示编译时的名字,变量表示运行时该名字所代表的内存位置
    • 标识符是一个字符串,用于指示数据对象、过程、类或对象的入口
    • 所有的标识符都是名字,但是并不是所有的名字都是标识符,名字还可以是表达式
    • 同一标识符可以被声明多次,但每个声明都引入一个新的变量。
  • 声明的作用域

    • 静态作用域:如果只通过考察其程序就可以确定某个声明的作用域,则称该语言使用静态作用域;否则是动态作用域

    • 显式访问控制:public、private、protected

    • 动态作用域:像java中的例子: x . m ( )   且 : 1. 有 一 个 类 C 带 有 方 法 m ( ) ; 2. D 是 C 的 子 类 , D 也 有 一 个 方 法 m ( ) x.m() \ 且:1.有一个类C带有方法m();2.D是C的子类,D也有一个方法m() x.m() 1.Cm();2.DCDm().

      一般来说,编译时无法确定x是类C的对象还是子类D的对象,直到运行时才能确定

  • 过程及其活动

    • 过程:将“过程”、“函数”和“方法”统称为过程。
    • 活动:过程的每次执行。过程p的一个活动的生存期是从过程体执行的第一步到最后一步。
    • 两个活动a和b,要么不交迭,要么嵌套。
  • 参数传递方式

    • 传值
      • 计算实参,并把右值放入到形参的形式单元中。
      • 在被调用过程的目标代码中,访问形参时直接访问相应的形式单元即可。
      • 特点:对形参的操作不影响调用过程中实参的值。
    • 传地址
      • 将实参的地址传给被调用过程。
        • 如果实参是一个具有左值的名字或者表达式(如x,x = 2),则传递该左值本身。
        • 如果实参时a+b或2这样的没有左值的表达式,则调用过程中首先计算实参的值并将其存入一个新的存储单元中,然后将这个新单元的地址传递给被调用过程。
      • 特点:对形参的操作通过传递给该过程的地址转化成了对实参的间接操作。所以,对形参的赋值将改变实参的值。
    • 传值结果
      • 将传值和传地址结合起来。
        • 首先计算实参,并将实参的右值按传值方式传递给被调用过程(传值)。此外,如果实参具有左值,则在调用之前要将其左值同时传递给被调用过程(传地址)。
        • 当控制返回时,将形参的当前右值复制回实参的左值。
      • 特点:先传值,被调用过程结束之前,再将值传回到对应的实参左值中。
    • 传名
      • 只有在被调用过程中用到形参时才会动态地建立它与实参的联系。
        • 在调用过程中设置计算实参左值或右值的形实转换子程序。
        • 每当要向形参赋值或取该形参的值时,就调用相应的形实转换子程序,以获取相应的实参地址或值。
      • 特点:被调用过程中对形参的操作,都将是对实参的动态操作。
      • 与传地址不同,传地址会为表达式实参创建一个新的存储单元,但是传名不会。

9.2 存储组织

  • 运行时内存的划分
    − − − − − − 目 标 代 码 − − − − − − 静 态 数 据 − − − − − − 栈 − − − − − − 空 闲 空 间 − − − − − − 堆 − − − − − − ------\\ 目标代码\\ ------\\ 静态数据\\ ------\\ 栈\\ ------\\ 空闲空间\\ ------\\ 堆\\ ------\\
    目标代码:在编译时即可确认
    静态数据:部分数据大小在编译时也可确认
    栈:可动态增长
    堆:可动态增长

  • 活动记录

    • 活动记录:过程的每次活动(函数的每次调用)所需要的信息用一块连续的存储区来管理。

    • 采用栈式存储分配机制

    • 运行时,每当将进入一个过程就有一个相应的活动记录压入栈顶。

编译原理复习_第25张图片
编译原理复习_第26张图片

  • 临时变量:存放临时数据对象,如计算表达式时的临时变量。

    局部数据区:局部变量、内情变量、临时工作单元(如存放对表达式求值的结果)

    静态链:用它来访问存放在其他活动记录中的非局部数据。

    动态链:用来指向调用者的活动记录。

    形式单元:存放相应的实参的地址或值。

    旧SP:前一活动记录的地址

  • 局部数据的组织

    • 变量所需存储空间可以根据其类型而静态确定
    • 一个过程中声明的局部变量,按照出现的次序,在局部数据域中依次分配空间。
    • 数据对齐
  • 全局存储分配策略

    • 静态存储分配策略(FORTRAN):编译时能确定
    • 动态存储分配策略(PASCAL):编译时不能确定

9.3 静态存储分配

  • 必须满足条件:
    • 数据对象的长度和在内存中的位置在编译时是已知的;
    • 不允许过程的递归调用
    • 数据结构不能动态建立
  • 分配策略:
    • 顺序分配算法:按照程序段出现的先后分配
    • 分层分配算法:允许程序段之间的覆盖

9.4 栈式存储分配

  • 如果过程允许递归:必须保存每次调用相应的数据区内容(活动记录)

    引入一个运行栈,让过程的每次执行和过程的活动记录相对应。

  • 调用序列和返回序列

    • 过程调用和返回都是通过在目标代码中生成调用序列和返回序列来实现的

    • 调用序列:负责分配活动记录,并将信息填入活动记录中

    • 返回序列:则负责恢复机器状态
      编译原理复习_第27张图片

    • 调用:

      • 调用者计算实参
      • 调用者把返回地址和sp的旧值存入被调用者的活动记录中,然后将sp放到如图所示的位置
      • 被调用者保存寄存器值和其他机器状态信息
      • 被调用者初始化局部数据,并开始执行
    • 返回:

      • 被调用者将返回值放入临近调用者的活动记录的地方
      • 利用状态域中的信息,被调用者恢复sp和其他寄存器,并且按调用者代码中的返回地址返回
      • 尽管sp的值已经减小了,调用者仍然可将返回值复制到自己的活动记录中
  • C语言的过程调用和返回

  • 栈中的可变长数据

    • 在活动记录中对于这样的数组,存放数组指针
    • 运行时,这些指针指向分配在栈顶的存储空间
    • 局限:
      • 过程活动停止后,局部名字的值还必须维持
      • 被调用者的生存期比调用者更长时,无法正确描述
      • 动态变量

9.5 栈中非局部数据的访问(display表不要求)

  • 无嵌套过程的静态作用域的实现(C语言)

    • 假定:允许过程递归调用、允许过程含有可变数组。但是不允许过程定义的嵌套
    • 要访问的数据分成两种情况:
      • 非静态局部变量(包括形式参数):分配在活动记录栈顶的那个活动记录中:通过sp指针来访问
      • 外部变量和静态局部变量:分配在静态数据区:只需使用静态分配的地址
        编译原理复习_第28张图片
        注意观察颜色。每个颜色对应一个过程。
  • 包含嵌套过程的静态作用域的实现(Pascal语言)

    • 嵌套深度:主程序名的嵌套深度为1,每当进入一个额内层过程时深度加一。

    • 访问链:如果将过程p直接嵌套在过程q中,则p的活动记录的访问链指向q的最近一次活动的活动记录的访问链。这样,沿着访问链找到的活动记录,就是当前正在运行的过程可以访问数据和对应过程的活动记录。

      a    深 度 为 : n a ∣ ( 相 差 n p − n a 个 访 问 链 ) ∣ p    深 度 为 : n p 这 样 可 以 快 速 定 位 到 a 所 在 的 活 动 记 录 , 由 于 a 所 在 位 置 是 : 活 动 记 录 中 相 对 于 某 个 位 置 的 固 定 偏 移 。 故 a 可 以 访 问 到 。 a \ \ 深度为:n_a \\ | \\ (相差 n_p-n_a个访问链) \\ | \\ p \ \ 深度为:n_p \\ 这样可以快速定位到a所在的活动记录,由于a所在位置是:活动记录中相对于某个位置的固定偏移。故a可以访问到。 a  na(npna访)p  npaaa访

    • 如何建立访问链:

      • 假定 n p 调 用 n x n_p 调用n_x npnx
      • n p < n x n_p < n_x np<nx:x必须在p中定义,否则p是不可访问x的。x的访问链必须指向p的访问链。
      • n p ≥ n x n_p \geq n_x npnx:从p开始,追踪访问链 n p − n a + 1 n_p-n_a+1 npna+1次,这样到达的访问链是x应当指向的访问链。
    • 过程型参数的访问链:

      • 当过程被当作参数时,调用者在创建被调用过程的活动记录之后,调用者同时还需要传递这个新活动记录的正确的访问链。
  • 动态作用域的实现(Lisp语言)

    • 指的是:名字x的引用指向带有x声明的最近被调用的过程中x的这个声明。动态作用域相对于时间,静态作用域相对于空间。

编译原理复习_第29张图片

  • 一个新的活动记录将继承已经存在的非局部名字与存储位置的绑定。

  • 实现动态作用域的方法:

    • 深访问:用控制链搜索运行栈,寻找包含该非局部名字的第一个活动记录。

    • 浅访问:将每个名字的当前值保存在静态分配的内存空间中。当过程p开始一个新的活动时,p的局部名字n使用在静态数据区分配给n的内存单元。n的先前值必须保存在p的活动记录中,当p的活动结束时再恢复。

      即:直接到内存找,看有没有同名的,如果有直接拿来用,但是这个n的原来的值需要保存在活动记录中。

    • 如果需要将函数作为参数传递或者作为结果返回,则使用深访问方法更方便一些。

Chapter 11:代码生成

11.1 代码生成器设计中的问题

  • 代码生成器的输入包括:中间代码和符号表信息。
  • 目标代码的形式:绝对机器语言代码、可重定位的机器语言代码、汇编语言代码
  • 指令选择:选择一个合适的机器指令序列来实现给定的中间代码
  • 寄存器分配
  • 计算顺序选择

11.2 目标语言

编译原理复习_第30张图片

你可能感兴趣的:(学习方法)