仓库源文站点原文


title: 编译原理 date: 2021-01-07 14:18:25 categories: 学习笔记 tag:


引言

编译过程的五个阶段

  1. 词法分析
  2. 语法分析
  3. 词义分析与中间代码生成
  4. 优化
  5. 目标代码生成

编译过程的八个部分

  1. 词法分析程序
  2. 语法分析程序
  3. 语义分析程序
  4. 中间代码生成
  5. 代码优化程序
  6. 目标代码生成程序
  7. 错误检查和处理程序
  8. 信息表管理程序

文法和语言

基本概念

字母表

字母表 $\sum$ 是一个<font color=red>有穷的符号集合</font>,符号包括了字母、数字、标点符号……

字母表上的运算

串上的运算

文法的定义

对于文法 $G$,可以定义为 $G = (V_T, V_N, P, S)$

产生式的简写

对于含有相同左部的产生式,可以通过“或”运算简写 例如对于

$$\alpha \rightarrow \beta_1 , \alpha \rightarrow \beta_2 , \dots , \alpha \rightarrow \beta_n$$

可以简写为

$$\alpha \rightarrow \beta_1 | \beta_2 | \dots | \beta_n$$

符号约定

语言的定义

推导

从语言的规则<font color=red>推导</font>生成语言的过程

根据规则,可以将符号串推成另一个符号串的过程称为推导。用产生式的右部替换产生式的左部

如果是经过多次推导得到,则记为 $a_0 \Rightarrow^n a_n$

$a_0 \Rightarrow^+ a_n$ 表示经过至少一步推导得到 $a_0 \Rightarrow^* a_n$ 表示经过任意步数推导得到

规约

根据语言的规则<font color=red>识别</font>语言的过程

根据规则,可以将符号串还原成。将产生式的右部替换为产生式的左部

句型和句子

$$L(G) = {w | S \Rightarrow^ w, w \in V_T^ }$$

文法的分类

0型文法

对于 $\forall \alpha \rightarrow \beta \in P$,满足 $\alpha$ 中至少包含一个非终结符

1型文法(上下文有关文法)

对于 $\forall \alpha \rightarrow \beta \in P$,满足 $|\alpha| \leq |\beta|$

也可以理解为对于 $\forall \alpha A \beta \rightarrow \alpha \gamma \beta \in P$,其中 $\alpha$、$\beta$ 可以为 $\varepsilon$,满足 $\gamma \neq \varepsilon$(在 $A = S$ 的情况下,此等式可以成立)

2型文法(上下文无关文法)

对于 $\forall \alpha \rightarrow \beta \in P$,满足 $\alpha$ 是一个非终结符

3型文法(正规文法)

对于 $\forall \alpha \rightarrow \beta \in P$,满足 $\alpha$ 是一个非终结符,而 $\beta$ 只能是空串、一个终结符号或者一个终结符号和一个非终结符号

CFG 分析树

分析树是推导的图形化表示

CFG

二义性文法

如果一个文法可以为某个句子生成多棵分析树,则称这个文法是二义性的

词法分析

正则表达式

正则表达式(简称:RE)是一种描述正则语言的表示方法,正则表达式可以由较小的正则表达式按照特定规则递归地构建。每个正则表达式 $r$ 定义(表示)一个语言,记为 $L(r)$。这个语言也是根据 $r$ 的子表达式所表示的语言递归定义的

正则表达式的定义

正则定义

给一些RE命名,并在之后的RE中像使用字母表中的符号一样使用这些名字

有穷自动机

具有一系列离散的输入输出信息和有穷数目的内部状态的系统

有穷自动机的分类

确定的有穷自动机(DFA)

对于任意的一个输入,自动机都唯一地确定了下一个状态

定义 $M=(S, \sum, \delta, s_0, F)$ 为一个确定的有穷自动机

确定的有穷自动机可以通过状态图来表示

DFA状态图

初始结点通常用 $\Rightarrow$ 表示,终态结点通常为双圈表示

确定的有穷自动机还可以使用表格来表示其状态,例如上述的状态图可以表示为

状态 0 1
$S_0$ $S_1$ $S_0$ 0
$S_1$ $S_1$ $S_2$ 0
$S_2$ $S_1$ $S_0$ 1

通常,在表格对应行的右端通过01的标注表示这个状态为终态

对于 $\sum^*$ 中的任意符号串 $t$,如果在状态图中存在一条从初态到某一终态的路径,且这条路径上所有弧的标记符连接起来等于 $t$,则称 $t$ 可以被此 DFA 接受

不确定的有穷自动机(NFA)

收到一个符号,可能进入不同的状态

定义 $M=(S, \sum, \delta, s_0, F)$ 为一个确定的有穷自动机

NFA 也可以通过状态图来表示和表格进行表示,与 DFA 的状态图类似

DFA 和 NFA 的等价性

从正则表达式转为 NFA

参考下面两张图 RE转NFA-1

RE转NFA-2

NFA 转 DFA

步骤:

  1. 从起始状态开始,通过所有的路径,得到新的状态集的组合
  2. 将所有新的状态集组合重新通过路径,重复操作直到没有新的组合
  3. 将状态集作为 DFA 的结点,建成 DFA 状态机
  4. 如果新的状态包含原来的可接受状态(终止状态),则认为新的状态也是可接受状态

以下图为例 NFA示例 画出如下的表格

序号 状态 a b
1 0 0,1 1
2 0,1 0,1 1
3 1 0

将序号代替状态,重新绘图 NFA转DFA-1

DFA 最小化

以上方的图为例,易得状态 1 和状态 2 是等价状态,所以合并得到

序号 状态 a b
1 0 1 2
2 1 1

最终得到 DFA最小化-2

自顶向下语法分析方法

自顶向下的分析思想

从分析树的顶部(根节点)向底部(叶节点)方向构造分析树,可以看成是从文法开始符号S推导出词串w的过程

最左推导

在最左推导中,总是选择每个句型的最左非终结符进行替换,即优先满足表达式左侧 最左推导

最右推导

在最右推导中,总是选择每个句型的最右非终结符进行替换,即优先满足表达式右侧 最右推导

自顶向下的语法分析采用最左推导方式

预测分析

文法转换

消除左递归

直接左递归

当出现了类似如下的推导公式时

$$A \rightarrow A \alpha | \beta$$

时,此时可以出现左递归的情况,这会导致无法正确的进行最左推导,因为可以出现下面的情况:

$$A \Rightarrow A \alpha \Rightarrow A \alpha\alpha \Rightarrow A \alpha\alpha\alpha \Rightarrow A \alpha\alpha\alpha\alpha \dots$$

此时可以将上述的推导公式转换为

$$ \begin{cases} A \rightarrow \beta A' \ A' \rightarrow \alpha A' | \varepsilon \end{cases} $$

将左递归的公式转换为右递归即可

这样的操作的代价是

间接左递归

例如

$$ \begin{cases} S \rightarrow Aa | b \ A \rightarrow Sd | \varepsilon \end{cases} $$

此时可以产生这样的推导

$$S \rightarrow Aa \rightarrow Sda \rightarrow Aada \rightarrow Sdada \dots$$

这时,应该将改为先进行替换得到

$$A \rightarrow Aad | bd | \varepsilon$$

$$ \begin{cases} A \rightarrow bdA' | A' \ A' \rightarrow abA' | \varepsilon \end{cases} $$

多个候选式

当出现

$$S \rightarrow aAd | aBe$$

之类的结构,当读入的第一个字符为 $a$ 时,无法确定应该选择哪个产生式的时候,应该进行左公因子提取,即改编为

$$ \begin{cases} S \rightarrow aS' \ S' \rightarrow Ad|Be \end{cases} $$

LL(1) 文法

FIRST 集

$FIRST(A)$ 集表示非终结符 $A$ 能够推导出的所有等式的第一个终结符的集合

$$FIRST(\alpha) = { a | \alpha \Rightarrow a \beta, a \in V_T , \alpha, \beta \in V^* }$$

这条等式可以用下面三个原则来求算

对于一个产生式 $A \rightarrow B$而言

FOLLOW 集

$FOLLOW(A)$ 集表示非终结符 $A$ 后可以跟随哪些终结符

$$FOLLOW(A) = { a | S \Rightarrow^ \dots A a \dots,a \in V_T }$$ $$若 A \Rightarrow^ \dots A,则 # \in FOLLOW(A)$$

这两条等式可以用下面三个原则来求算

SELECT 集

表示使用某个产生式的选择符号

$$ \begin{cases} SELECT(A \rightarrow \alpha) = (FIRST(\alpha) - {\varepsilon}) \cup FOLLOW(A),\alpha \Rightarrow^ \varepsilon \ SELECT(A \rightarrow \alpha) = FIRST(\alpha),\alpha \not\Rightarrow^ \varepsilon \end{cases} $$

同时满足

$$SELECT(A \rightarrow \alpha) \cap SELECT(A \rightarrow \beta) = \varnothing$$

LL(1)

方法一

满足 LL(1) 的文法有下面三个条件:

若文法存在语句

$$A \rightarrow \alpha | \beta$$

$$ \begin{cases} FIRST(\alpha) \cap FOLLOW(A) = \varnothing, \beta \Rightarrow^ \varepsilon \ FIRST(\beta) \cap FOLLOW(A) = \varnothing, \alpha \Rightarrow^ \varepsilon \end{cases} $$

方法二

$$对于所有的 A \rightarrow \alpha | \beta$$

若满足

$$SELECT(A \rightarrow \alpha) \cap SELECT(A \rightarrow \beta) = \varnothing$$

则为 LL(1)

非递归的预测分析法(表驱动的预测分析)

首先需要根据 $SELECT$ 集来构建一个分析表。通过表的信息实现语法分析

LL(1) 文法分析示例

以下面的表达式文法为例

$$ \begin{cases} E \rightarrow E + T | T \ T \rightarrow T * F | F \ F \rightarrow i | (E) \end{cases} $$

消除左递归

首先,消除左递归,易得,前两个式子均为左递归 得到

$$ \begin{cases} E \rightarrow TE' \ E' \rightarrow +TE' | \varepsilon \ T \rightarrow FT' \ T' \rightarrow *FT' | \varepsilon \ F \rightarrow i | (E) \end{cases} $$

求出 FIRST

得到各个符号的 FIRST 集:

首先根据2、4、5式得到

$$ \begin{cases} FIRST(E') = {+, \varepsilon} \ FIRST(T') = {*, \varepsilon} \ FIRST(F) = {i, (} \end{cases} $$

然后根据 FIRST 集的求出剩下的 FIRST 集

$$ \begin{cases} FIRST(E) = {i, ( } \ FIRST(T) = {i, ( } \end{cases} $$

求出 FOLLOW 集

然后再求出 FOLLOW 集

首先

$$# \in FOLLOW(E)$$

然后根据第一个等式得到

$$FIRST(E') - \varepsilon \subseteq FOLLOW(T) \Rightarrow FOLLOW(T) = {+}$$ $$FOLLOW(E) \subseteq FOLLOW(E') \Rightarrow FOLLOW(E') = {#}$$

然后继续重复做,直到没有 FOLLOW 集发生更新为止,最终得到

$$ \begin{cases} FOLLOW(E) = { #, ) } \ FOLLOW(E') = { #, ) } \ FOLLOW(T) = { #, +, )} \ FOLLOW(T') = { #, +, )} \ FOLLOW(F) = { #, +, ), *} \end{cases} $$

求出 SELECT 集

再得到 SELECT 集

$$ \begin{cases} SELECT(E \rightarrow TE') = FIRST(TE') = FIRST(T) = {i, ( } \ SELECT(E' \rightarrow +TE') = FIRST(+TE') = FIRST(+) = {+} \ SELECT(E' \rightarrow \varepsilon) = (FIRST(\varepsilon) - {\varepsilon }) \cup FOLLOW(E') = { #, ) } \ SELECT(T \rightarrow FT') = FIRST(FT') = FIRST(F) = {i, (} \ SELECT(T' \rightarrow FT') = FIRST(FT') = FIRST() = {} \ SELECT(T' \rightarrow \varepsilon) = (FIRST(\varepsilon) - {\varepsilon }) \cup FOLLOW(T') = { #, +, )} \ SELECT(F \rightarrow i) = FIRST(i) = { i } \ SELECT(F \rightarrow (E)) = FIRST((E)) = FIRST(() = { ( } \ \end{cases} $$

此时,判断是否为 LL(1) 文法 $$ \begin{cases} SELECT(E' \rightarrow +TE') \cup SELECT(E' \rightarrow \varepsilon) = \varnothing \ SELECT(T' \rightarrow * FT') \cup SELECT(T' \rightarrow \varepsilon) = \varnothing \ SELECT(F \rightarrow i) \cup SELECT(F \rightarrow (E)) = \varnothing \end{cases} $$

所以是 LL(1)文法

求出分析表

根据 SELECT 得出下表

i + * ( ) #
E $\rightarrow TE'$ $\rightarrow TE'$
E' $\rightarrow +TE'$ $\rightarrow \varepsilon$ $\rightarrow \varepsilon$
T $\rightarrow FT'$ $\rightarrow FT'$
T' $\rightarrow \varepsilon$ $\rightarrow *FT'$ $\rightarrow \varepsilon$ $\rightarrow \varepsilon$
F $\rightarrow i$ $\rightarrow (E)$

分析输入串

采用三列分析法来分析,使用 # 表示尾部,假设输入的串为 i+i*i

剩余输入 输出
E# i+i*i#
TE'# i+i*i# 匹配了表中的 $\rightarrow TE'$
FT'E'# i+i*i# $\rightarrow FT'$
iT'E'# i+i*i# $\rightarrow i$
T'E'# +i*i#
E'# +i*i# $\rightarrow \varepsilon$
+TE'# +i*i# $\rightarrow +TE'$
TE'# i*i#
FT'E'# i*i# $\rightarrow FT'$
iT'E'# i*i# $\rightarrow i$
T'E' # *i#
*FT'E'# *i# $\rightarrow *FT'$
FT'E'# i#
iT'E'# i# $\rightarrow i$
T'E'# #
E'# # $\rightarrow \varepsilon$
# # $\rightarrow \varepsilon$

匹配成功

LL(1) 分析中的出错处理

自底向上优先分析

自底向下的分析思想

从分析树的底部(叶节点)向顶部(根节点)方向构造分析树,可以看成是将输入串w归约为文法开始符号S的过程,自底向上的语法分析采用最左归约方式(反向构造最右推导)

LR(0) 和 SLR(1) 分析法

<font color=red>由于 SLR(1) 的操作和 LR(0) 分析法相似,且兼容,所以这里直接写 SLR(1) 的操作过程,LR(0) 文法也可以直接用此方法,得到的结果完全相同</font>

以此文法为例

$$ \begin{cases} S \rightarrow L*L | L \ L \rightarrow LB | B \ B \rightarrow 0 | 1 \end{cases} $$

构造分析表

将所有的或运算式子转换为多个式子

得到

$$ \begin{cases} S \rightarrow L*L \ S \rightarrow L \ L \rightarrow LB \ L \rightarrow B \ B \rightarrow 0 \ B \rightarrow 1 \end{cases} $$

合并相同开始符号

LR分析法不适用于有多个开始符号的产生式,所以应当对上面的产生式进行处理得到

$$ \begin{cases} 0) S' \rightarrow S \ 1) S \rightarrow L*L \ 2) S \rightarrow L \ 3) L \rightarrow LB \ 4) L \rightarrow B \ 5) B \rightarrow 0 \ 6) B \rightarrow 1 \end{cases} $$

每行开头的为编号,后续通过编号来指代产生式

建立状态

对于一个状态,首先,判断.后面是否为非终结符号。如果是,那我们就得找所有由此非终结符推出的产生式,并将它们添加进入此状态里。循环做即可。

使用 . 表示当前匹配到的位置

首先建立初状态,将第一个表达式加入初状态 State 0

State 0

此状态中有 $S' \rightarrow .S$

检查 . 后是否为非终结符,得到 $S$,由于 $S$ 是非终结符,所以将 $S$ 的产生式加入此状态得到

$$ \begin{cases} S' \rightarrow .S \ S \rightarrow .L*L \ S \rightarrow .L \ \end{cases} $$

再检查新加入的,得到 $L$ 也需要加入此状态

$$ \begin{cases} S' \rightarrow .S \ S \rightarrow .L*L \ S \rightarrow .L \ L \rightarrow .LB \ L \rightarrow .B \ \end{cases} $$

最后发现 $B$ 也是需要加入此状态的得到 State 0 的最终结果

$$ \begin{cases} S' \rightarrow .S \ S \rightarrow .L*L \ S \rightarrow .L \ L \rightarrow .LB \ L \rightarrow .B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$

接下来的每个状态都是从 State 转换过来的,即将小数点向后移动,可以得到不同的状态 根据其状态内的产生式,可以得到 State 0 可以有 $S, L, B, 0, 1$ 这五个转移方式

State 1

设定 State 1 是从 State 0 通过 $S$ 转移过来的

所以可以得到,仅

$$S' \rightarrow S.$$

满足,所以 State 1 即只有此产生式,且不可以再转移

注意,State 1 是第一个产生式的最后的结果,所以此状态作为 Accept 状态,简称 acc

State 2

设定 State 2 是从 State 0 通过 $L$ 转移过来

所以可以直接得到的有

$$ \begin{cases} S \rightarrow L.*L \ S \rightarrow L. \ L \rightarrow L.B \ \end{cases} $$

对于第三个式子,其满足条件(.后为非终结符),所以需要把 $B$ 加入此状态

即得到

$$ \begin{cases} S \rightarrow L.*L \ S \rightarrow L. \ L \rightarrow L.B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$

其他 State

不断重复上述步骤,得到下面的图和结果 $$ State 0 = \begin{cases} S' \rightarrow .S \ S \rightarrow .LL \ S \rightarrow .L \ L \rightarrow .LB \ L \rightarrow .B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$ $$ State 1 = \begin{cases} S' \rightarrow S. \end{cases} $$ $$ State 2 = \begin{cases} S \rightarrow L.L \ S \rightarrow L. \ L \rightarrow L.B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$ $$ State 3 = \begin{cases} L \rightarrow B. \end{cases} $$ $$ State 4 = \begin{cases} B \rightarrow 0. \end{cases} $$ $$ State 5 = \begin{cases} B \rightarrow 1. \end{cases} $$ $$ State 6 = \begin{cases} S \rightarrow L.L \ L \rightarrow .LB \ L \rightarrow .B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$ $$ State 7 = \begin{cases} L \rightarrow LB. \end{cases} $$ $$ State 8 = \begin{cases} S \rightarrow LL. \ L \rightarrow L.B \ B \rightarrow .0 \ B \rightarrow .1 \end{cases} $$

graph LR
A[State 0] -- S --> B[State 1]
A[State 0] -- L --> C[State 2]
A[State 0] -- B --> D[State 3]
A[State 0] -- 0 --> E[State 4]
A[State 0] -- 1 --> F[State 5]
C[State 2] -- * --> G[State 6]
C[State 2] -- B --> H[State 7]
C[State 2] -- 0 --> E[State 4]
C[State 2] -- 1 --> F[State 5]
G[State 6] -- L --> I[State 8]
G[State 6] -- B --> D[State 3]
G[State 6] -- 0 --> E[State 4]
G[State 6] -- 1 --> F[State 5]
I[State 8] -- B --> H[State 7]
I[State 8] -- 0 --> E[State 4]
I[State 8] -- 1 --> F[State 5]
B[State 1] --> Accept([Accept])

Accept通常状态不需要画出

创建LR分析表

由此图和上面的集合可以画出表格

<table border="0" cellpadding="0" cellspacing="0" width="576" style="border-collapse: collapse;table-layout:fixed;width:432pt"> <colgroup> <col class="x22" width="72" span="8" style="mso-width-source:userset;width:54pt"> </colgroup> <tbody> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r0"> <td rowspan="2" height="38" class="x21" width="72" style="height:28.5pt;width:54pt;">状态</td> <td colspan="4" class="x21" width="288">ACTION</td> <td colspan="3" class="x21" width="216">GOTO</td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r1"> <td class="x22">0</td> <td class="x22">1</td> <td class="x22">*</td> <td class="x22">$</td> <td class="x22">S</td> <td class="x22">L</td> <td class="x22">B</td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r2"> <td height="19" class="x22" style="height:14.25pt;">0</td> <td class="x22">s4</td> <td class="x22">s5</td> <td class="x22"></td> <td class="x22"></td> <td class="x22">1</td> <td class="x22">2</td> <td class="x22">3</td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r3"> <td height="19" class="x22" style="height:14.25pt;">1</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> <td class="x22">acc</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r4"> <td height="19" class="x22" style="height:14.25pt;">2</td> <td class="x22">s4</td> <td class="x22">s5</td> <td class="x22">s6</td> <td class="x22">r2</td> <td class="x22"></td> <td class="x22"></td> <td class="x22">7</td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r5"> <td height="19" class="x22" style="height:14.25pt;">3</td> <td class="x22">r4</td> <td class="x22">r4</td> <td class="x22">r4</td> <td class="x22">r4</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r6"> <td height="19" class="x22" style="height:14.25pt;">4</td> <td class="x22">r5</td> <td class="x22">r5</td> <td class="x22">r5</td> <td class="x22">r5</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r7"> <td height="19" class="x22" style="height:14.25pt;">5</td> <td class="x22">r6</td> <td class="x22">r6</td> <td class="x22">r6</td> <td class="x22">r6</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r8"> <td height="19" class="x22" style="height:14.25pt;">6</td> <td class="x22">s4</td> <td class="x22">s5</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> <td class="x22">8</td> <td class="x22">3</td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r9"> <td height="19" class="x22" style="height:14.25pt;">7</td> <td class="x22">r3</td> <td class="x22">r3</td> <td class="x22">r3</td> <td class="x22">r3</td> <td class="x22"></td> <td class="x22"></td> <td class="x22"></td> </tr> <tr height="19" style="mso-height-source:userset;height:14.25pt" id="r10"> <td height="19" class="x22" style="height:14.25pt;">8</td> <td class="x22">s4</td> <td class="x22">s5</td> <td class="x22"></td> <td class="x22">r1</td> <td class="x22"></td> <td class="x22"></td> <td class="x22">7</td> </tr> <!--[if supportMisalignedColumns]--> <tr height="0" style="display:none"> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> <td width="72" style="width:54pt"></td> </tr> <!--[endif]--> </tbody> </table>

表的构建原则:

<!-- ## LR(1) 和 LALR(1) 提出了后继符号概念 定义每一个产生式应当描述为 $$A \rightarrow \alpha . \beta, a$$ 其中,前半部分为普通的产生式,后面紧跟一个<font color=red>展望符</font>。其表示 $A$ 后面必须紧跟的终结符,其通常是 FOLLOW(A) 的真子集。 - LR(1) 中的 1 表示的即为此展望符的长度 - 当 $\beta \neq \varepsilon$ 时,此展望符没有任何作用 - 当 $\beta = \varepsilon$ 时,当且仅当下一个符号属于 $a$ 时,才可以用此产生式进行规约 - 若存在 $B \rightarrow \gamma$ 则其展望符为 $FIRST(\beta a)$,当 $\beta \Rightarrow^* \varepsilon$ 时,此时展望符为 $a$ 此时,再进行类似 LL(0) 文法的分析操作,以下面的文法为例 $$ \begin{cases} S \rightarrow L=R | R \\ L \rightarrow *R | id \\ R \rightarrow L \end{cases} $$ 此处省略过程,直接得到答案 ### 处理后的产生式为 $$ \begin{cases} 0) S' \rightarrow S \\ 1) S \rightarrow L=R \\ 2) S \rightarrow R \\ 3) L \rightarrow *R \\ 4) L \rightarrow id \\ 5) R \rightarrow L \end{cases} $$ ### 每个状态的产生式为 $$ State 0 \begin{cases} S' \rightarrow .S, \$ \\ S \rightarrow .L=R, \$ \\ S \rightarrow .R, \$ \\ L \rightarrow .*R, =/\$ \\ L \rightarrow .id, =/\$ \\ R \rightarrow .L, =/\$ \end{cases} $$ $$ State 1 \begin{cases} S' \rightarrow S.,\$ \end{cases} $$ $$ State 2 \begin{cases} S \rightarrow L.=R,\$ \\ R \rightarrow L.,\$ \end{cases} $$ $$ State 3 \begin{cases} S \rightarrow R.,\$ \end{cases} $$ $$ State 4 \begin{cases} L \rightarrow *.R,=/\$ \\ R \rightarrow .L, =/\$ \\ L \rightarrow .*R, =/\$ \\ L \rightarrow .id, =/\$ \end{cases} $$ $$ State 4 \begin{cases} S \rightarrow L=.R,\$ \\ R \rightarrow .L, \$ \\ L \rightarrow .*R, \$ \\ L \rightarrow .id, \$ \end{cases} $$ -->

LR(1) 和 LALR(1)

语法制导翻译

即将上面的那些产生式带入实际的使用中

例如可以通过下面的文法来描述 C 语言的定义一个变量的过程

产生式 语义规则
$S \rightarrow TL$ L的类型为T
$T \rightarrow int$ T为int
$T \rightarrow double$ T为double
$L \rightarrow L, id$ 创建一个新的 $$L{右}$$,将其类型设置为 $$L{左}$$ 相同的类型。并创建一个变量,其类型为 $$L_{左}$$ 的类型,名字为 $$id$$
$L \rightarrow id$ 创建一个变量,其类型为 $$L_{左}$$ 的类型,名字为 $$id$$

改为用属性来描述,则可以得到如下表格

产生式 语义规则
$S \rightarrow TL$ L.type = T.type
$T \rightarrow int$ T.type = int
$T \rightarrow double$ T.type = double
$L \rightarrow L, id$ L(右).type = L.type<br>CreateVar(type = L.type, name = id.name)
$L \rightarrow id$ CreateVar(type = L.type, name = id.name)

SDD(语法制导定义)

综合属性和继承属性

对于一个产生式产生的语义规则中,如果产生式左部的属性是仅通过右部的属性得到的,则称此属性为<font color=red>综合属性</font>。而如果产生式右部的属性是通过右部的属性或者左部的属性得到的,则称为<font color=red>继承属性</font>

L-SDD

S-属性定义 与 L-属性定义

S-属性文法

仅仅使用综合属性的SDD称为S属性的SDD,或S-属性定义、S-SDD

L-属性文法

一个SDD是L-属性定义(L-SDD),当且仅当它的每个属性要么是一个综合属性,要么是满足如下条件的继承属性:假如存在一个产生式 $A \rightarrow X_1 X_2 X_3 \dots$,若存在一个 $X_i$,它的一个属性值与下面的有关

例如上面的图片即为 L-SDD

SDT(语法制导翻译方案)

语法制导翻译方案(SDT)是在产生式右部中嵌入了程序片段(称为语义动作)的CFG(上下文无关文法)

例如

$$ \begin{cases} D \rightarrow T {L.type = T.type } L \ T \rightarrow int {T.type = int } \ T \rightarrow double {T.type = double } \ L \rightarrow { L_1.type = L.type } L_1, id \end{cases} $$

嵌入规则如下

使用时,当在进行规约操作时,需要同时执行此程序片段

中间代码生成

中间代码举例

典型语句的翻译(四元式)

赋值语句

x = b * (c + d) + a
  1. (+, c, d, t1)
  2. (*, b, t1, t2)
  3. (+, t2, a, t3)
  4. (=, t3, , x)

布尔表达式

(a > b) && (c < d) || (e < f) && (!g)
  1. (j>, a, b, 3)
  2. (j, , ,5)
  3. (j<, c, d, true)
  4. (j, , , 5)
  5. (j<, e, f, 7)
  6. (j, , , false)
  7. (jnz, g, , true)
  8. (j, , , false)

条件语句

if (a > 0) x = x + 1 else x = 4 * (x - 1)
  1. (j>, a, 0, 3)
  2. (j, , , 6)
  3. (+, x, 1, t1)
  4. (=, t1, , x)
  5. (j, , , 9)
  6. (-, x, 1, t2)
  7. (*, 4, t2, t3)
  8. (=, t3, , x) 9.

运行存储分配

memory

静态存储分配

顺序分配法

为每个静态过程都逐段分配存储空间,每个过程的内存空间都相互独立且不相交

优点:处理上简单 缺点:对内存空间的使用不够经济合理

层次分配法

通过对过程间的调用关系进行分析,凡属无相互调用关系的并列过程,尽量使其局部数据<font color=red>共享</font>存储空间

栈式存储分配

将内存认为是一个栈空间

当一个过程被调用时,向栈中推入一个活动记录,当此过程结束时,该记录被弹出栈

活动树

用来描述程序运行期间控制进入和离开各个活动的情况的树称为活动树。在表示过程 p 的某个活动的结点上,其子结点对应于被 p的这次活动调用的各个过程的活动。按照这些活动被调用的顺序,自左向右地显示它们。一个子结点必须在其右兄弟结点的活动开始之前结束

调用序列和返回序列

暂略

非局部数据的访问

暂略

堆式存储分配

暂略

符号表

暂略

代码优化

流图

基本块

划分方法:

例如对于代码

i = m - 1;
j = n;
v = a[n];
while (1) {
    do i = i + 1; while(a[i] < v);
    do j = j - 1; while(a[j] > v);
    if (i >= j) break;
    x = a[i];
    a[i] = a[j];
    a[j] = x;
}
x = a[i];
a[i] = a[n];
a[n] = x;

可以划分出 6 个基本块

B1:

i = m - 1;
j = n;
v = a[n];

B2:

i = i + 1;
if a[i] < v goto B2

B3:

j = j - 1;
if a[j] > v goto B3

B4:

if i >= j goto B6

B5:

x = a[i];
a[i] = a[j];
a[j] = x;
goto B2

B6:

x = a[i];
a[i] = a[n];
a[n] = x;

流图

根据上面的基本块,再根据其转跳关系,可以绘制流图

graph LR
B1 --> B2
B2 --> B2
B2 --> B3
B3 --> B3
B3 --> B4
B4 --> B5
B4 --> B6
B5 --> B2

常用的代码优化方法

暂略

基本块的优化

将基本块通过 DAG(有向无环图) 表示

例如对于代码

a = b + c;
b = b - d;
c = c + d;
e = b + c;

可以绘制出如下的 DAG 图(通常在图上标注运算符号而不是字母,这里为了更容易理解标注了字母)

graph LR
b0((b)) --> a0((a))
c0((c)) --> a0((a))
b0((b)) --> b1((b))
d0((d)) --> b1((b))
c0((c)) --> c1((c))
d0((d)) --> c1((c))
c1((c)) --> e0((e))
b1((b)) --> e0((e))

若结果 e 是需要返回的值,即其他基本块需要使用的值,则通过 DAG 图可知,变量 a 是无用的,可以删除

得到新的图为

graph LR
b0((b)) --> b1((b))
d0((d)) --> b1((b))
c0((c)) --> c1((c))
d0((d)) --> c1((c))
c1((c)) --> e0((e))
b1((b)) --> e0((e))

所以可以得到优化后的代码为

b = b - d;
c = c + d;
e = b + c;

数据流分析

到达定值分析

暂略

代码优化技术