编译原理--01 复习大纲(清华大学出版社第3版)

前言

目前以手中这本清华大学出版社出版的编译原理(第3版,张素琴等编著)作为复习总结,因为考试都是大题,一部分概念会被忽略。所有内容都需要通过举例和推导来帮助加深理解,优先为过几天的考试服务。该文实现了教材中那些特别复杂的推导符号,并且这几天会加紧持续更新。

第2章 文法和语言

符号和符号串

空符号串用\(\varepsilon\)表示,长度为0

若 \(\Sigma=\{0,1\}\) ,则 \(\Sigma^*=\{\varepsilon,0,1,00,11,000,001,...\}\),称 \(\Sigma^*\) 为集合 \(\Sigma\) 的闭包;\(\Sigma^+=\{0,1,00,11,000,001,...\}\),称\(\Sigma^+\)为集合\(\Sigma^+\)的正闭包

文法和语言的形式定义

规则产生式生成式,表示为 \(\alpha\rightarrow\beta\) 或 \(\alpha::=\beta\)

文法 \(G\) 定义为四元组 \((V_N, V_T, P, S)\)
\(V_N\)为非终结符集合
\(V_T\)为终结符集合
\(P\)为规则集
\(S\)为识别符或开始符

例如:
\(G[S]:S\rightarrow0S1\)
\(S\rightarrow01\)

直接推导用 \(\Rightarrow\),如 \(0S1\Rightarrow 00S11\)
长度为\(n(n\geq1)\)的推导用 \(\stackrel{+}{\Rightarrow}\),如\(S\stackrel{+}{\Rightarrow} 000S111\)
长度为\(n(n\geq0)\)的推导用 \(\stackrel{*}{\Rightarrow}\),如\(S\stackrel{*}{\Rightarrow} 000S111 \stackrel{*}{\Rightarrow} 000S111\)

句型:对\(S\stackrel{*}{\Rightarrow}x\),称x是文法G[S]的句型。x可以包含非终结符
句子:若上述x仅由终结符构成,则x是文法G[S]的句子
短语:若\(S\stackrel{*}{\Rightarrow}\alpha A\delta\) 且\(A\stackrel{+}{\Rightarrow}\beta\),则称 \(\beta\) 为句型 \(\alpha A\delta\) 相对于非终结符\(A\)的短语
直接短语:特别的,若\(A\Rightarrow \beta\),则称 \(\beta\) 为句型 \(\alpha A\delta\) 相对于规则\(A\rightarrow \beta\)的直接短语
句柄:规范句型的直接短语

文法描述的语言该文法一切句子的集合,如:
\(L(G[S])=\{0^n1^n|n\geq1\}\)

文法类型

文法类型 每个产生式\(\alpha\rightarrow\beta\)的特点
0型文法 \(\alpha\in(V_N\cup V_T)^*\)且至少含一个非终结符,且\(\beta\in(V_N\cup V_T)^*\)
1型上下文有关文法 在0型文法的基础上,还满足\(\mid\beta\mid\geq\mid\alpha\mid\),仅\(S\rightarrow\varepsilon\)除外
2型上下文无关文法 \(\alpha\)是一个非终结符,\(\beta\in(V_N\cup V_T)^*\)
3型正规文法 满足\(A\rightarrow aB\)或\(A\rightarrow B\)的形式,即\(\beta\)中只有1个非终结符,以及0或1个终结符

最左推导:在推导\(\alpha\Rightarrow\beta\)中,对\(\alpha\)中最左非终结符进行替换
最右推导:又称作规范推导,所推导得到的句型称为右句型,或规范句型

二义性:一个文法存在某个句子对应两棵不同的语法树

语法树

已知文法\(G[S]:\)
\(S\rightarrow aAS\)
\(A\rightarrow SbA\)
\(S\rightarrow a\)
\(A\rightarrow ba\)

文法G的句型aabbaa的一颗推导树为:

文法限制

有害规则:如\(U\rightarrow U\),只会引发二义性
多余规则:非终结符D不在任何规则的右部出现,即不可到达的

这一章的可能考点

  1. 已知文法求语言
  2. 已知语言求文法
  3. 列出句型的短语、直接短语、句柄
  4. 语法树、最左推导、规范推导

第3章 词法分析

正规式

正规式 含义
\(a\) 仅a
\(a\mid b\) 该字符可以为a或b
\(ab\) 字符a后面紧跟b
\(a^*\) n(n>=0)个连续的a
\((a\mid b)b\) ab或bb

正规文法与正规式的等价性

正规式转化为正规文法

对\(A\rightarrow x^*y\)型正规产生式,重写为:
\(A\rightarrow xB\)
\(A\rightarrow y\)
\(B\rightarrow xB\)
\(B\rightarrow y\)

对\(A\rightarrow x\mid y\)型正规产生式,重写为:
\(A\rightarrow x\)
\(A\rightarrow y\)

正规文法转化为正规式

文法产生式 正规式
规则1 \(A\rightarrow xB\) 和 \(B\rightarrow y\) \(A=xy\)
规则2 \(A\rightarrow xA\mid y\) \(A=x^*y\)
规则3 \(A\rightarrow x\) 和 \(A\rightarrow y\) \(A=x\mid y\)

有穷自动机

确定的有穷自动机(DFA)

确定的有穷自动机\(M\)是一个五元组:\(M=(K,\Sigma,f,S,Z)\)
\(K\)是一个有穷状态集
\(\Sigma\)是一个输入符号表
\(f\)是状态转换函数,例如\(f(k_i,a)=k_j (k_i,k_j\in K)\)
\(S\in K\),是唯一的一个初态
\(Z\subseteq K\),是一个终态集

DFA的确定性表现在转换函数 \(f:K\times\Sigma\rightarrow K\) 是一个单值函数

例如: DFA \(M=(\{S,U,V\}, \{a,b\}, f, S, \{V\})\)
\(f(S,a)=U\)
\(f(S,b)=V\)
\(f(U,b)=V\)
\(f(V,a)=U\)

现验证\(bab\)是否为\(M\)所接受,因为:
\(f(S, bab)=f(f(S,b), ab)=f(V,ab)=f(f(V,a),b)=f(U,b)=V\),而\(V\)属于终态,故\(bab\)可为\(M\)接受。

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

不确定的有穷自动机\(M\)是一个五元组:\(M=(K,\Sigma,f,S,Z)\)
\(K\)是一个有穷状态集
\(\Sigma\)是一个输入符号表
\(f\)是\(K\times\Sigma^*\rightarrow 2^K\)的多值映像,即允许函数值有多种结果
\(S\in K\),是非空初态集
\(Z\subseteq K\),是一个终态集

NFA可以使用空转移,但DFA不可以。例如:\(f(0, \varepsilon)=\{0,3\}\)

NFA转换为等价DFA

状态集合\(I\)的\(\varepsilon-\)闭包,表示为\(\varepsilon-closure(I)\),是状态集\(I\)中的任何状态\(S\)经过任意条\(\varepsilon\)弧能到达的状态的集合。显然,状态集合\(I\)的任何状态\(S\)都属于\(\varepsilon-closure(I)\)

状态集合\(I\)的\(a\)弧转换,表示为\(move(I,a)\),定义为状态集合J,其中J是所有那些可以从\(I\)中某一状态经过一条\(a\)弧而到达的状态全体

子集法

初始状态集 \(\varepsilon-closure(I)\) a b
\(\{0\}\) \(\{0,1,2,4,7\}\)
\(T_0=\{0,1,2,4,7\}\) \(\{3,8\}\) \(\{5\}\)
\(\{3,8\}\) \(\{1,2,3,4,6,7,8\}\)
\(\{5\}\) \(\{1,2,4,5,6,7\}\)
\(T_1=\{1,2,3,4,6,7,8\}\) \(\{3,8\}\) \(\{5,9\}\)
\(T_2=\{1,2,4,5,6,7\}\) \(\{3,8\}\) \(\{5\}\)
\(\{5,9\}\) \(\{1,2,4,5,6,7,9\}\)
\(T_3=\{1,2,4,5,6,7,9\}\) \(\{3,8\}\) \(\{5,10\}\)
\(\{5,10\}\) \(\{1,2,4,5,6,7,10\}\)
\(T_4=\{1,2,4,5,6,7,10\}\) \(\{3,8\}\) \(\{5\}\)
重命名状态集 a b
\(T_0\) \(T_1\) \(T_2\)
\(T_1\) \(T_1\) \(T_3\)
\(T_2\) \(T_1\) \(T_2\)
\(T_3\) \(T_1\) \(T_4\)
\(T_4\) \(T_1\) \(T_2\)

DFA的最小化

  1. 将\(P\)划分为终态集非终态集,得\(P'=\{N,T\}\)
  2. 递归地分割\(P'\)中的子集,使得被分割的子集中的所有状态都能够根据不同的输入符号转换到被分割的目标子集中的所有状态
  3. 直到不可再被分割后,将\(P'\)中的每个子集合并为一个状态。含原初态的状态为初态,而含原终态的状态为终态。


\(P=\{1,2,3,4,5,6,7\}\)被划分为\(P_0=\{\{1,2,3,4\},\{5,6,7\}\}\)

在非终态集中,在1和2构成集合时,通过a可以到达终态集,通过b可以到达3。而3,4通过b到达的是终态集,显然有区别,故划分为\(P_1=\{\{1,2\},\{3,4\},\{5,6,7\}\}\)

在6和7构成集合时,通过a可以到达4,通过b可以到达集合{1,2},而5通过a到达的是7,显然有区别,故划分为\(P_2=\{\{1,2\},\{3,4\},\{5\},\{6,7\}\}\)

由于3通过a到达集合{1,2},而4通过a到达4,有明显区分,故划分为\(P_3=\{\{1,2\},\{3\},\{4\},\{5\},\{6,7\}\}\)

最后不能再划分了,因此令1代表{1,2},消去2,令6代表{6,7},消去7.最终得到的为最小化的DFA \(M'\)

正规式和有穷自动机的等价性

正规文法和有穷自动机的等价性

这一章的可能考点

  1. 根据文法构造DFA,或给定NFA转DFA
  2. DFA的最小化
  3. 正规文法、正规式、有穷自动机之间的转换

第4章 自顶向下的语法分析方法

确定的自顶向下分析思想

开始符号集首符号集:设\(G=(V_T,V_N,P,S)\)是上下文无关文法。
\(FIRST(\alpha)=\{a \mid\alpha \stackrel{*}{\Rightarrow} a\beta, a\in V_T, \alpha,\beta\in V^*\}\)

若\(\alpha \stackrel{*}{\Rightarrow} \varepsilon\),则规定\(\varepsilon \in FIRST(\alpha)\),称\(\varepsilon \in FIRST(\alpha)\)为\(\alpha\)的开始符号集首符号集

简单来说,就是查看该句型推导出的所有句子的首字母集合。

例如文法\(G[S]:\)
\(S\rightarrow Ap\)
\(S\rightarrow Bq\)
\(A\rightarrow aA\)
\(A\rightarrow \varepsilon\)
\(B\rightarrow Bb\)
\(B\rightarrow b\)

那么\(FIRST(S)=\{a,b,p\}\),\(FIRST(A)=\{a,\varepsilon\}\),\(FIRST(B)=\{b\}\)

FOLLOW集:设\(G=(V_T,V_N,P,S)\)是上下文无关文法,\(A\in V_N\),S是开始符号
\(FOLLOW(A)=\{a\mid S\stackrel{*}{\Rightarrow}\mu A\beta且a\in V_T,a\in FIRST(\beta), \mu\in {V_T}^*, \beta\in V^+\)
若\(S \stackrel{*}{\Rightarrow}\mu A\beta\),且\(\beta \stackrel{*}{\Rightarrow}\varepsilon\),则 \(\# \in FOLLOW(A)\)

简单来说,就是查看该句型在被推导前后面跟随的所有可能的第一个字母的集合。需要将出现在推导式左边的非终结符往

例如之前的文法\(G[S]\),\(FOLLOW(S)=\{\#\}\),\(FOLLOW(A)=\{p\}\),\(FOLLOW(B)=\{b,q\}\)

选择符号集:一个产生式的选择符号集SELECT。给定上下文无关文法的产生式\(A\rightarrow \alpha, A\in V_N, \alpha\in V^*\),若\(a\stackrel{*}{\nRightarrow}\varepsilon\),则\(SELECT(A\rightarrow\alpha)=FIRST(\alpha)\)

而如果\(a\stackrel{*}{\Rightarrow}\varepsilon\),则\(SELECT(A\rightarrow\alpha)=(FIRST(\alpha)-\{\epsilon\})\cup FOLLOW(A)\)。

求出选择符号集,是为了找到哪些符号应该使用该推导。那么,如果\(A\rightarrow \alpha\)不能推出空串,显然 从该推导得到的所有句子的首字母构成的集合 来反向看出 哪些字母应该使用该推导。而如果\(A\rightarrow \alpha\)能推出空串,则还要考虑该非终结符的后跟字符。

一个上下文无关文法是LL(1)文法的充要条件,是对每个非终结符A的两个不同产生式,\(A\rightarrow\alpha, A\rightarrow\beta\),满足
\[SELECT(A\rightarrow\alpha)\cap SELECT(A\rightarrow\beta)=\emptyset\]

例如文法\(G[S]:\)
\(S\rightarrow aA\)
\(S\rightarrow d\)
\(A\rightarrow bAS\)
\(A\rightarrow \varepsilon\)

有:
\(SELECT(S\rightarrow aA)=\{a\}\)
\(SELECT(S\rightarrow d)=\{d\}\)
\(SELECT(A\rightarrow bAS)=\{b\}\)
\(SELECT(A\rightarrow \varepsilon)=\{a,d,\#\}\)

所以:
\(SELECT(S\rightarrow aA)\cap SELECT(S\rightarrow d)=\{a\}\cap\{d\}=\emptyset\)
\(SELECT(A\rightarrow bAS)\cap SELECT(A\rightarrow \varepsilon)=\{b\}\cap\{a,d,\#\}=\emptyset\)

LL(1)文法的判别

LL(1)的含义:第1个L 表明 从左向右扫描输入串,第2个L 表明 分析过程中将使用最左推导,1表明只需向右看一个符号就知道该选择哪个产生式推导。

  1. 找出能推出\(\varepsilon\)的非终结符
  2. 计算FIRST集
  3. 计算FOLLOR集
  4. 计算SELECT集
  5. 进行判断

某些非LL(1)文法到LL(1)文法的等价变换

LL(1)文法的充分条件为不含左公共因子

提取左公共因子

例如\(A\rightarrow \alpha\beta\mid \alpha\gamma\),可以写成:
\(A\rightarrow \alpha B\)
\(B\rightarrow \beta\)
\(B\rightarrow \gamma\)

一般情况如\(A\rightarrow \alpha_1\alpha_2...\alpha_n(\beta_1\mid\beta_2\mid...\mid\beta_n)\),可以写成:
\(A\rightarrow\alpha_1 A_1\)
\(A_1\rightarrow\alpha_2 A_2\)
\(...\)
\(A_{n-1}\rightarrow\alpha_n B\)

\(B\rightarrow\beta_1\)
\(B\rightarrow\beta_2\)
\(...\)
\(B\rightarrow\beta_n\)

此外,还需要检查文法是否含有隐式的左公共因子,如:
\((1)A\rightarrow ad\)
\((2)A\rightarrow Bc\)
\((3)B\rightarrow aA\)

将(3)带入(2),可暴露出左公共因子:
\((1)A\rightarrow ad\)
\((2)A\rightarrow aAc\)
\((3)B\rightarrow aA\)

可以看到此时(3)为多余规则,可以删去,最后整理得到:
\((1)A\rightarrow aB\)
\((2)B\rightarrow Ac\)
\((2)B\rightarrow d\)

改写后的文法不含空产生式,且无左递归时,则改写后的文法是LL(1)文法

若还有空产生式,则还需要用LL(1)文法的判别方式进行判断

消除左递归

对于包含直接左递归的文法,如\(G[S]:A\rightarrow Ab,A\rightarrow c, A\rightarrow d\)

可以直接改写成右递归:\(A\rightarrow cA', A\rightarrow dA', A'\rightarrow bA',A'\rightarrow b\)

一般情况下,如\(A\rightarrow A(\beta_1\mid\beta_2\mid...\mid\beta_n), A\rightarrow \alpha_1\mid\alpha_2\mid...\mid\alpha_n\),分为了包含左递归和不含左递归的两部分,可以变形为:
\(A\rightarrow \alpha_1 A'\)
\(A\rightarrow \alpha_2 A'\)
\(...\)
\(A\rightarrow \alpha_n A'\)

\(A'\rightarrow \beta_1 A'\)
\(A'\rightarrow \beta_2 A'\)
\(...\)
\(A'\rightarrow \beta_n A'\)

对于包含间接左递归的文法,如\(G[S]:\)
\((1)A\rightarrow aB\)
\((2)A\rightarrow Bb\)
\((3)B\rightarrow Ac\)
\((4)B\rightarrow d\)

可以考虑把(1)和(2)带入(3):
\((1)B\rightarrow aBc\)
\((2)B\rightarrow Bbc\)
\((3)B\rightarrow d\)

不会引起左递归的式子为(1)和(3),故可以写成:
\((1)B\rightarrow aBcB'\)
\((2)B\rightarrow dB'\)
\((3)B'\rightarrow bcB'\)
\((4)B'\rightarrow \varepsilon\)

消除一切左递归

LL(1)文法的实现

程序实现

  1. 求出文法G[S]各个产生式的SELECT集合
  2. 然后根据集合内的符号来选择所属产生式

写程序时,用getsym来读入下一个符号,如果有不合法的符号,应当有错误处理。

如文法\(L(G[S]): A\rightarrow aBd \mid b, B\rightarrow\varepsilon\mid c\)

\(SELECT(A\rightarrow aBd)=\{a\}\)
\(SELECT(A\rightarrow b)=\{b\}\)
\(SELECT(B\rightarrow \varepsilon)=\{d\}\)
\(SELECT(B\rightarrow c)=\{c\}\)

void PraseA()
{
    if (sym == 'a')
    {
        getsym();
        PraseB();
        if (sym == 'd')
            getsym();
    }
    else if (sym == 'b')
    {
        getsym();
    }
    else
    {
        error();
    }
}

void PraseB()
{
    if (sym == 'c')
    {
        getsym();
    }
    else if (sym == 'd')
    {
    }
    else
    {
        error();
    }
}

预测分析表

求出所有规则的SELECT集后,根据集合填入预测分析表。如上面的文法:

a b c d #
A \(\rightarrow aBd\) \(\rightarrow b\)
B \(\rightarrow c\) \(\rightarrow \varepsilon\)

对\(acd\)分析过程如下:

步骤 分析栈 剩余输入串 推导所用产生式或匹配
1 #A acd# A→aBd
2 #dBa acd# a匹配
3 #dB cd# B→c
4 #dc cd# c匹配
5 #d d# d匹配
6 # # 接受

这一章的可能考点

  1. 判别文法是否为LL(1)
  2. 已知LL(1)文法,构造预测分析表
  3. 给定LL(1)文法和输入串,写出分析过程表

第5章 自底向上的移进-归约分析

自底向上的移进-规约分析要求对输入符号串自左向右扫描,按句柄进行归约。
移进:将输入串的下一个字符移入符号栈
归约:符号栈中的顶部几个符号如果能匹配某条推导式的右边,则用该推导式的左边替换

自底向上的移进-归约法是每次对最左边的内容进行归约,它的逆过程为自顶向下的规范(最右)推导。

设文法 \(G[S]\) 为
\(S\rightarrow aAcBe\)
\(A\rightarrow b\)
\(A\rightarrow Ab\)
\(B\rightarrow d\)

对输入串\(abbcde\)使用自顶向下的最右推导:
\[S\Rightarrow aAcBe \Rightarrow aAcde \Rightarrow aAbcde \Rightarrow abbcde\]

对应的,我们可以得到它的逆过程,即规约过程。

步骤 符号栈 输入符号串 动作
(1) # abbcde# 移进
(2) #a bbcde# 移进
(3) #ab bcde# 归约\((A\rightarrow b)\)
(4) #aA bcde# 移进
(5) #aAb cde# 归约\((A\rightarrow Ab)\)
(6) #aA cde# 移进
(7) #aAc de# 移进
(8) #aAcd e# 归约\((B\rightarrow d)\)
(9) #aAcB e# 移进
(10) #aAcBe # 归约\((S\rightarrow aAcBe)\)
(11) #S # 接受(acc)

第6章 LR分析

LR(K)分析使用自底向上分析法,从左到右扫描符号,只需要根据分析栈中的符号栈和向右顺序查看输入串的K(K>=0)个符号来确定分析器接下来是移进还是规约,因而也能唯一地确定句柄。

LR分析器


总控程序负责LR分析过程

分析栈分为状态栈和文法符号栈。它们均是后进先出。

分析表分为动作(ACTION)表和状态转换(GOTO)表两个部分。

SP为栈指针,指向状态栈和文法符号栈,即状态栈和符号栈元素数目始终保持一致。

状态转换表内容按关系\(GOTO[S_i, X]=S_j\)确定,即当栈顶状态为\(S_i\)遇到栈顶符号\(X\)时应当转向状态\(S_j\)

动作表按\(ACTION[S_i,a]\)确定了栈顶状态为\(S_i\)时遇到输入符号\(a\)应执行的动作。




动作按优先程度排列:

规约:如果栈顶形成了句柄\(\beta\)(它的长度为\(r\)),且有原来的推导\(A\rightarrow\beta\)来进行规约,则从状态栈和文法符号栈中自顶向下去掉\(r\)个符号,即对SP减去\(r\)。接着把A移入文法符号栈内,再根据此时修改SP后的栈顶状态,把满足\(S_j=GOTO[S_i, A]\)的状态移进状态栈。

移进:如果栈顶没有形成句柄,且\(S_j=GOTO[S_i,a]\)成立,则把\(S_j\)移入到状态栈,把\(a\)移入到文法符号栈。其中\(i\)和\(j\)表示状态号。

接受acc:当归约到文法符号栈中只剩下文法的开始符号S,并且输入符号串只剩下#(表示已经结束),则为分析成功。

报错:如果状态栈顶的当前状态遇到了不该出现的文法符号时则报错,说明输入串不是该文法能接受的句子。

LR(0)分析

已知文法\(G[S]:\)
\((1)S\rightarrow aAcBe\)
\((2)A\rightarrow b\)
\((3)A\rightarrow Ab\)
\((4)B\rightarrow d\)

对输入串\(abbcde\#\)用自底向上归约的方法来分析,由于到第5步栈中的符号串为\(\#aAb\),此时状态栈的栈顶状态\(0236\)决定了应该用(3)式而不是(2)式来归约。

分析表和分析过程如下

可归前缀和活前缀

为了更清楚地表示最右推导与最左归约的关系,可以在推导过程中加入一些附加信息。对上面的文法用\([i]\)编号:

\(S\rightarrow aAcBe[1]\)
\(A\rightarrow b[2]\)
\(A\rightarrow Ab[3]\)
\(B\rightarrow d[4]\)

对输入串abbcde进行最右推导,把序号也带入:
\(S\Rightarrow aAcBe[1]\Rightarrow aAcd[4]e[1]\Rightarrow aAb[3]cd[4]e[1]\Rightarrow ab[2]b[3]cd[4]e[1]\)

对应的逆过程——最左归约(规范归约,即从左到右归约)为:
\(ab[2]b[3]cd[4]e[1]\) 用产生式(2)归约
\(\Leftarrow aAb[3]cd[4]e[1]\) 用产生式(3)归约
\(\Leftarrow aAcd[4]e[1]\) 用产生式(4)归约
\(\Leftarrow aAcBe[1]\) 用产生式(1)归约
\(S\)

这里用\(\Leftarrow\)表示归约。

每次归约前,句型的前部依次为:
\(ab[2]\),它的前缀为\(\varepsilon, a, ab\)
\(aAb[3]\),它的前缀为\(\varepsilon, a, aA, aAb\)
\(aAcd[4]\),它的前缀为\(\varepsilon, a, aA, aAc, aAcd\)
\(aAcBe[1]\),它的前缀为\(\varepsilon, a, aA, aAc, aAcB, aAcBe\)

这些规范句型的前部我们称之为可归前缀。而a, aA, aAc等这些出现在一个或多个可归前缀的部分,可称之为活前缀,它的长度不能超过当前句型句柄的末端。

拓广文法是指对原文法G增加产生式\(S'\rightarrow S\),其中\(S\)为原文法G的开始符号。这样确保新的开始符号\(S'\)只会在推导式的左边出现(而\(S\)不一定)

使用拓广文法可以将上面的文法表示成:
\(S'\rightarrow S[0]\)
\(S\rightarrow aAcBe[1]\)
\(A\rightarrow b[2]\)
\(A\rightarrow Ab[3]\)
\(B\rightarrow d[4]\)

对句子\(abbcde\)列出可归前缀:
\(S[0]\)
\(ab[2]\)
\(aAb[3]\)
\(aAcd[4]\)
\(aAcBe[1]\)

构造识别其活前缀及可归前缀的有限自动机如下图所示:

带*的终态既是句柄识别态,也是句子识别态(仅有唯一一个)。

用一个额外的开始状态X,并用\(\varepsilon\)弧连接每个可归前缀的有限状态机,并进行确定化:

活前缀及可归前缀的一般计算方法

设\(G=(V_N, V_T, P, S)\)是一个上下文无关文法,对于\(A\in V_N\),有
\[LC(A)=\{\alpha\mid S'\mathop{\Rightarrow}\limits_{R}^{*}aA\omega,\alpha\in V^*, \omega\in V^T_*\}\]
其中S‘是G的拓广文法G‘的开始符号

\(LC(A)\)表明了在规范推导(最右推导)中在非终结符A左边出现的符号串集合。

推论:若文法G中有产生式\(B\rightarrow \gamma A\delta\),则有
\[LC(A) \supseteq LC(B) \cdot\{\gamma\}\]

因为对任一形为\(\alpha B\omega\)的句型,必然有规范推导:
\[S'\mathop{\Rightarrow}\limits_{R}^{*}\alpha B\omega\mathop{\Rightarrow}\limits_{R}^{*}\alpha\gamma A\delta\omega\]

因此对任一\(\alpha \in LC(B)\),必有\(\alpha \gamma \in LC(A)\),即\(LC(B) \cdot\{\gamma\} \subseteq LC(A)\)

对于文法\(G[S]\):
\(S'\rightarrow S\)
\(S\rightarrow aAcBe\)
\(A\rightarrow b\)
\(A\rightarrow Ab\)
\(B\rightarrow d\)

需列出方程组求解(此处为正规式):
\[\begin{cases}
LC(S') = \varepsilon \LC(S) = LC(S') \cdot\varepsilon=\varepsilon \LC(A) = LC(S) \cdot a \mid LC(A) \cdot\varepsilon = a \LC(B) = LC(S) \cdot aAc = aAc
\end{cases}\]

用正规式表示求解结果:
\[\begin{cases}
LC(S') = \varepsilon \LC(S) = \varepsilon \LC(A) = a \LC(B) = aAc
\end{cases}\]

规定\(LR(0)C(A\rightarrow\beta)=LC(A)\cdot\beta\),这样包含句柄的活前缀有:

\[\begin{cases}
LR(0)C(S'\rightarrow S) = S \LR(0)C(S\rightarrow aAcBe) = aAcBe \LR(0)C(A\rightarrow b) = ab \LR(0)C(A\rightarrow Ab) = aAc \LR(0)C(B\rightarrow d) = aAcd
\end{cases}\]

对于递归型:
\[\begin{cases}
LC(E)=\varepsilon \LC(A) = LC(E)\cdot a\mid LC(A) \cdot c
\end{cases}\]

一直展开可以看到:
\(LC(A)\)
\(= a\mid LC(A)\cdot c\)
\(=a\mid (a\mid LC(A)\cdot c)\cdot c\)
\(=a\mid ac\mid LC(A)\cdot cc\)
\(=a\mid ac\mid acc\mid LC(A)\cdot ccc\)
\(=...\)

故\(LC(A)=ac^*\)

LR(0)项目规范族的构造

1. LR(0)项目

在文法G‘中为每个产生式的右部的适当位置添加一个圆点构成项目
例如\(A\rightarrow Ab\)有3个项目:
\([0] A\rightarrow \cdot Ab\)
\([1] A\rightarrow A\cdot b\)
\([2] A\rightarrow Ab\cdot\)

而空产生式\(A\rightarrow\varepsilon\)只有一个项目\(A\rightarrow\cdot\)。

\(\cdot\)左边的符号表示已经被扫描过的部分,右边如果还有符号,则它的第一个符号则是下一个将会被扫描的符号。

2. 构造识别活前缀的NFA

列出所有项目后,这些项目标上编号用于构造NFA,确定\(S'\rightarrow \cdot S\)为初态,有2种情况:

  1. 即将从\(S'\rightarrow \cdot S\)过渡到\(S\rightarrow \cdot aA\)。这两个项目用\(\rightarrow\)连接,并在上面标记\(\varepsilon\)
  2. 即将从\(S\rightarrow\cdot aA\)过渡到\(S\rightarrow a\cdot A\)。这两个项目用\(\rightarrow\)连接,并在上面标记\(a\)

根据圆点所在位置和圆点后的符号状况,可以分为4类:

  1. 移进项目,形如\(A\rightarrow a\cdot aB\),圆点后面是终结符
  2. 待约项目,形如\(A\rightarrow aa\cdot B\),圆点后面是非终结符,需要移进完非终结符内的所有符号才能归约符号B
  3. 归约项目,形如\(A\rightarrow aaB\cdot\),圆点后面没有符号,此时可以进行归约
  4. 接受项目,形如\(S'\rightarrow A\),S‘是所有产生式唯一的左部

3. LR(0)项目集规范族的构造

从NFA构造成DFA的关键点仅在于,使用闭包函数,将当前项目(它前面不是由\(\varepsilon\)推出)以及后面用\(\varepsilon\)弧连接的所有项目,构成一个新的项目集。

但一个项目集中不能同时存在:

  1. 移进项目和归约项目,形如\(A\rightarrow a\cdot a\beta\)和\(B\rightarrow \gamma\cdot\)
  2. 归约项目和归约项目,形如和\(A\rightarrow \beta\cdot\)和\(B\rightarrow \gamma\cdot\)

4. LR(0)分析表的构造

现有LR(0)项目集规范族:
\[C=\{I_0, I_1, ..., I_n\}\]

\(I_k\)为项目集的名,k为状态名,令包含\(S'\rightarrow \cdot S\)项目的集合\(I_k\)的下标k作为分析器的初始状态。分析表的ACTION表和GOTO表构造步骤如下:

  1. 若项目\(A\rightarrow \alpha\cdot a\beta\)属于\(I_k\),且\(I_k\stackrel{a}{\rightarrow}I_j\),则置\(ACTION[k,a]=S_j\)
  2. 若项目\(A\rightarrow \alpha\cdot\)属于\(I_k\),则对任何终结符和#号都置\(ACTION[k,*]=r_j\)
  3. 若\(I_k\stackrel{A}{\rightarrow}I_j\),则置\(GOTO[k,A]=j\)
  4. 若项目\(S'\rightarrow S\)属于\(I_k\),则置\(ACTION[k,\#]=acc\),表示接受

SLR(1)分析

假定一个LR(0)规范族中含有如下的项目集(举例):
\[I_5=\begin{cases}
X\rightarrow\alpha\cdot b\beta \A\rightarrow \gamma \cdot \B\rightarrow \delta \cdot
\end{cases}\]

也就是该项目集中出现了移进—归约冲突归约—归约冲突

根据LR(0)分析,此时无法确定是移进,还是归约,即产生了冲突。但我们可以向前查看一个符号(即查看当前剩余输入串最前面一个符号),来确定接下来的动作,这就是SLR(1)分析

对于上面的例子,要求三个项目中 \(·\) 后面的符号各不相同,即要求它们的FOLLOR集互不相交:
\(FOLLOW(A)\cap\{b\}=\emptyset\)
\(FOLLOW(B)\cap\{b\}=\emptyset\)
\(FOLLOW(A)\cap FOLLOW(B)=\emptyset\)

即说明在状态5时,如果面临某输入符号为\(a\),则可以按以下规定决策:

  1. 若\(a=b\),则移进
  2. 若\(a\in FOLLOW(A)\),则用产生式\(A\rightarrow \gamma\)归约
  3. 若\(b\in FOLLOW(B)\),则用产生式\(B\rightarrow \delta\)归约
  4. 此外,则报错

接下来是分析表的修改,比如说产生式2为\(A\rightarrow \gamma\),产生式3为\(B\rightarrow \delta\),\(FOLLOW(A)=c\),\(FOLLOW(B)=d\),原本LR(0)时候分析表对应:

状态 a b c d #
5 \(r_2,r_3\) \(r_2,r_3,S_6\) \(r_2,r_3\) \(r_2,r_3\) \(r_2,r_3\)

可见冲突非常严重,经过修改后,变成了:

状态 a b c d #
5 \(S_6\) \(r_2\) \(r_3\)

此时的分析表满足SLR(1)分析。

第7章 语法指导的语义计算

未完待续...

原文地址:https://www.cnblogs.com/X-Jun/p/11029594.html

时间: 2024-08-29 07:54:25

编译原理--01 复习大纲(清华大学出版社第3版)的相关文章

现代交换原理课程复习大纲

一.交换原理 电路交换.报文交换.分组交换(数据报与虚电路).ATM交换等基本原理及对比 B1P9    B1P133        B1P170 电信网络的交换技术 电路交换 程控交换机 虚电路交换 ATM IP NGN软交换 和IMS 面向连接和无连接方式 来自于互联网 这依赖于信息发送方是否需要与接收方联系并通过联系来维持一个对话连接(面向连接的),还是没有任何预先联系就发送消息(无连接的). 1.面向连接的服务:就是通信双方在通信时,要事先建立一条通信线路,其过程有建立连接.使用连接和释

编译原理三大经典书籍(龙书 虎书 鲸书)

1.龙书(Dragon book)  英文名:Compilers: Principles,Techniques,and Tools  作者:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman  中文名:编译原理技术和工具   第一版龙书   第二版龙书 龙书”.龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有反映一些新的编译技术.新编的<编译原理>抛弃诸如算符优先分析等过时技术,增加面向对象编译.类型检查等新

实况:《编译原理》期末考试一天复习

大学的编译原理课程是讲解如何设计编译器的.我要做的,是在一学期没怎么听课的条件下,用我对算法的理解能力,和互联网以及身边同学的帮助,来在不足一天的时间中复习完考试所需的所有知识. 我手上有两份可用资料: 复习课上记录的,老师对考试知识点的回顾. 同学总结的,会考的算法的列表. 而现在的时间是晚上6:33,我刚刚看完大约四分之一,明天下午1:10就考试了.由于时间紧迫,我必须对自己的任务有个计划,不能浪费一点时间.这个计划会花去我大概半个小时的时间,但它是一定要做的,否则我一定复习不完. 考试涉及

Compiler_编译原理复习小注

本文出自:点击打开链接 词法分析: 1.之前一直没有理解关于正则表达式扩展的单目运算符.关于正则定义式的扩展: 在一个问题就是关于回退: 之前一直没有考虑回退符号的问题.回退一个符号,到达终态.实验那么做虽然没有问题,但是这个地方一直理解错了.  Compiler_编译原理复习小注

编译原理:引论

阅读的教材为:编译原理(张素琴等编著,清华大学出版社) 编译过程就是将高级语言翻译为低级语言 编译过程分为前段(front end)和后端(back end) 阅读过程中遇到的疑问是:解释程序和编译程序的区别? 1.编译程序直接生成目标代码,解释程序在运行时才将中间代码编译成目标代码.故执行效率低 2.编译过程生成的目标代码,逆翻译难度非常大,基本不可能.解释程序只是将程序生成为中间代码,易读,相当开放源码.故无保密性 3.编译过程从中间代码到目标代码需要考虑到不同系统,相反解释程序只是翻译到中

编译原理

1.编译原理是什么? (1)就是对程序语言进行翻译: (2)介绍编译程序构造的原理和基本方法: (3)从本质上讲是一个算法问题: 2.学习编译原理有什么好处? (1)接触一些新的算法,如,贪心算法,图论算法等: (2)学习一些新的思想,如,有限状态机,递归下降等: (3)多了一项技能: 3.不学有什么损失? (1)将来出社会可能比较难混: (2)不学好这门课程期末可能不及格: (3)有学不一定有用,不学肯定没用: 4.如何学习编译原理? (1)上课跟上老师的步伐,课前预习,课后复习: (2)多从

编译原理简单介绍

编译原理学习导论 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,可是编译原理却一直作为大学本科的必修课程,同时也成为了研究生入学考试的必考内容.编译原理及技术从本质上来讲就是一个算法问题而已,当然由于这个问题十分复杂,其解决算法也相对复杂.我们学的数据结构与算法分析也是讲算法的,不过讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比较专注解决一种的算法了.在20世纪50年代,编译器的编写一直被认为是十分困难的事情,第

程序员的三大浪漫之一——编译原理

编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法.内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成. 编译原理是计算机专业设置的一门重要的专业课程.虽然只有少数人从事编译方面的工作,但是这门课在理论.技术.方法上都对学生提供了系统而有效的训练,有利于提高软件人员的素质和能力. 我认为,既然编译过程是将一种语言翻译为另一种语言的计算机程序的过程,顾名思义,编译原理就是为我们解释计算机是如何将一种语言翻译成另一种语言这

关于计算机编译原理

从我个人理解,计算机编译原理,顾名思义,就是关于计算机编程翻译的相关原理,即对计算机编程的,更为深入.更为详细的去解读计算机语言.上网看了许多个人解读编译原理,发现最令我信服的,就是把编译原理类比成人体解剖:只有认真解剖.研究人体各部位,才能在手术中.医治中做到更好的处理.因此,唯有理解编译原理,才能更好地进行计算机的编程等一系列的操作. 博客上有人说到,学习编译原理能够更加容易理解算法之间的关系.能培育自己的观点.提高学习语言的效率.但在我个人看来,学好编译原理,能让我们有一个更强的临时变换的