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

Posted x-jun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译原理--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*\\Rightarrowx\\),称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\\)

需列出方程组求解(此处为正规式):
\\[\\begincases 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 \\endcases\\]

用正规式表示求解结果:
\\[\\begincases LC(S') = \\varepsilon \\LC(S) = \\varepsilon \\LC(A) = a \\LC(B) = aAc \\endcases\\]

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

\\[\\begincases 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 \\endcases\\]

对于递归型:
\\[\\begincases LC(E)=\\varepsilon \\LC(A) = LC(E)\\cdot a\\mid LC(A) \\cdot c \\endcases\\]

一直展开可以看到:
\\(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\\stackrela\\rightarrowI_j\\),则置\\(ACTION[k,a]=S_j\\)
  2. 若项目\\(A\\rightarrow \\alpha\\cdot\\)属于\\(I_k\\),则对任何终结符和#号都置\\(ACTION[k,*]=r_j\\)
  3. \\(I_k\\stackrelA\\rightarrowI_j\\),则置\\(GOTO[k,A]=j\\)
  4. 若项目\\(S'\\rightarrow S\\)属于\\(I_k\\),则置\\(ACTION[k,\\#]=acc\\),表示接受

SLR(1)分析

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

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

根据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章 语法指导的语义计算

未完待续...

以上是关于编译原理--01 复习大纲(清华大学出版社第3版)的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法复习大纲----学霸经验分享系列

直播预告|Python网络爬虫程序设计原理与案例

南航计算机科学与技术专业复试科目541离散数学和编译原理

GPS测量原理与应用_第四版_徐绍铨_武汉大学出版社_考试复习资料

电子科技大学人工智能期末复习笔记:一阶逻辑

数据挖掘2021期末复习考试重点大纲