第五章 自底向上的语法分析
5.1 引言
- 工作方式:移进-归约
2.2.3 短语、直接短语、句柄(第二章)
- 直接短语一定是一个产生式的右部,但是产生式的右部不一定是当前句型的直接短语
- 高人,民生,活水则都不是当前句型的直接短语,但是是其他句型的产生式右部
设G[Z]是一个文法,假定αβδ是文法G的一个句型。
1)短语:若存在Z ⇒+ αAδ且A ⇒+ β,则称β是句型αβδ相对于非终结符A的短语。(即子树的边缘)
2)直接短语:若存在Z ⇒+ αAδ且A⇒β,则称β是句型αβδ相对于产生式规则A→β的直接短语。(高度为2的子树的边缘)
3)句柄:一个句型的最左直接短语称为该句型的句柄。
- 基本思想:将输入符号串中的符号从左向右逐个的移进栈,每当栈顶形成某一个可归约子串时,就把该可归约子串归约成某一个非终结符号。即先把该可归约子串从栈顶逐出,再把归约的非终结符号压进栈。
5.2 自底向上的语法分析面临的问题
- 如何寻找可归约子串?
- 可归约子串被归约到哪一个非终结符号?
LR分析法概述
LR分析法基本原理
LR分析器(自动机)的总体结构
LR分析表的结构
LR分析器的工作过程
LR分析算法
LR(0)分析
LR(0)项目
K个符号对应K+1个项目
增广文法
(文法开始符号仅出现在一个产生式左部,一个接受状态)
文法中的项目
. 后是非终结符则存在等价项目
LR(0)分析表构造算法(略)
LR(0)分析过程中的冲突(移进规约冲突)
不知道使用移入动作还是规约动作
移进规约冲突和规约规约冲突
SLR分析
新增的LOLLOW集,说明就算规约了,E的FOLLW集也没有,故不能规约*
借助FOLLOW集判定什么时候不能进行规约
例子
根据状态2冲突的B和T,对比他们的FOLLOW集,写出对应的ACTION
SLR分析中的冲突
移入规约冲突
所以需要引入功能更加强大的分析法
LR(1)分析
LR(1)分析法的提出
等价LR(1)项目
例子
对比多了四个状态
项目集同心的概念
8 10
4 11
5 12
7 13都是同心的项目集
例子
LALR分析法
基本思想
合并同心项目集
例子
合并同心项目集产生规约规约冲突
(因为展望符只在规约的时候冲突,移入不起作用,所以不会产生移入规约冲突)
推迟错误的发现
LALR(1)的特点
二义性文法的LR分析
例子1
例子2
二义性文法的使用
LR分析中的错误处理
恐慌层次错误恢复
短语层次错误恢复
例子(不会推迟错误的发现)
核心是假设获得了相应的输入或者删除相应的输入,并且转移到相应状态,并且输出相应信息
参考
LR分析技术
LR分析技术概述
- LR(k)分析技术
1)L代表从左向右分析,R代表最右推导,k代表向前查看k个字符。
2)LR分析法实际上是最右推导的逆过程——最右归约。
3)LR(k)分析技术利用已经移进栈中的和归约后进入栈中的一切文法符号,并向前查看最多k个符号,从而确定句柄是否已经在栈顶形成,一旦句柄出现在栈顶,立即进行归约。 - LR(k)技术的分类:LR(0)分析法、SLR(1)分析法、LR(1)分析法、LALR(1)分析法。
- LR分析器组成:一个输入串、一个分析栈、一张LR分析表、LR分析器总控程序。
LR(0)分析法
- 活前缀:将每一条规则编上序号①、②、③…,附加在规则的右边,并在推导的过程中将序号带入句型中。句型中形如βω℗的前部称为活前缀。
- LR(0)项目:文法G中每一条产生式规则的右部添加一个圆点,称为LR(0)项目。
eg:A→cAd可以产生四个LR(0)项目:A→·cAd,A→c·Ad,A→cA·d,A→cAd·。
1)若是空规则A→ε,则只对应一个LR(0)项目A→·。
2)含义 :
i)A→·cAd表示期望下一个符号是终结符c。
ii)A→c·Ad表示目前输入串中已经有部分符号已正确分析成功,期望剩下的输入串能与圆点右部的部分分析成功。
iii)A→cAd·表示输入串已全部正确解析成功,可以归约到非终结符A。
- 初始项目:S→·α,S是文法的开始符号,称该项目为初始项目。
- 移进项目:A→α·aβ,其中a∈VT,称该项目为移进项目。
- 待约项目:A→α·Bβ,其中B∈VN,称该项目为待约项目。
- 归约项目和接收项目:A→α·,其中A∈VN,若A不是文法G的开始符号,则称为归约项目,否则称为接收项目。
- 后继项目:设有两个项目A→α·aβ和A→αa·β,两者同属于一条规则,只是圆点位置相差一个终结符,则称A→αa·β是A→α·aβ的后继项目。
- 文法G的拓广
1)原因:为了保证文法G只有一个接收项目,一旦达到接收项目就完成整个语法分析。当一个文法G的开始符号不是只出现在一条规则的左边,则这个文法G需要拓广。
2)定义:设文法G的开始符号是S,现引入一个新的开始符号S‘,并加入一条新规则:S’→S。产生新文法G’[S’],称G’是文法G的拓广。 - LR(0)项目集:由LR(0)项目构成的集合。
- LR(0)项目集闭包
已知项目集I,求I的闭包CLOSURE(I)的算法:
1)项目集I中所有的项目加入到CLOSURE(I)集合中。
2)若待约项目A→α·Bβ属于CLOSURE(I),则对于每一个关于B的产生式规则B→γ,将项目B→·γ加入到CLOSURE(I)中。
3)反复执行2),直到CLOSURE(I)中不再有新的项目加入为止。 - LR(0)的GO函数:项目集I经过符号X的状态转换函数GO(I,X)=CLOSURE(I的后继项目集)。
- LR(0)项目集规范族:把识别文法G的所有活前缀的LR(0)项目集闭包组成的DFA称为项目集的规范族。
eg:设有文法G[S]:
S→cAd①
A→a②
A→Aa③
求该文法G的LR(0)项目集规范族。
- LR(0)分析表组成:ACTION子表和GOTO子表。
------ | ACTION | ACTION | ACTION | GOTO | GOTO |
---|---|---|---|---|---|
状态 | a1 | … | # | S | A |
0 | s2 | 1 | 2 | ||
1 | acc | … | … | ||
… | … | … | … | ||
n | r3 | r3 | r3 |
其中,ai是终结符号,S和A是非终结符。
1)LR(0)分析表的第一列是状态列,登记DFA中的状态编号。
2)ACTION子表中的列全是终结符(包含#),填写动作,动作分为四种:
i)移进动作:用s表示。如状态0所在行与ACTION子表中的a1列交叉处填写的是s2,表示a1进栈,当前状态从状态0变成状态2。
ii)归约动作:用r表示。如状态n所在行与ACTION子表中的a1列交叉处填写的是r3,表示用第三条规则进行归约。
iii)接受动作:用acc表示。如状态1所在行与ACTION子表中的#列交叉处填写的是acc,表示成功接收,已正确识别出句子。
iv)报错:ACTION子表中的空白处,表示出错。
3)GOTO子表的列全是非终结符,表示状态转移。如状态0所在行与GOTO子表中第A列交叉处填写的是2,表示当前状态0下若识别出非终结符A,状态将变迁到状态2。
- LR(0)分析表的构造步骤:
1)若移进项目A→α·aβ属于Ik且GO(Ik,a)=Ij,其中a是终结符,则令ACTION[k][a]=sj。表示将a移进栈,且状态变迁到j。
2)若归约项目A→β·,并设A→β的规则编号为p,则将ACTION子表中状态k所在的行与每一个终结符(包括#)所在列的交叉处填写上rp。表示无论下一个字符是什么,只要当前状态是k,就立即使用第p条规则A→β进行归约。
3)若接收项目S‘→S·属于Ik,则令ACTION[k][#]=acc。就是将ACTION子表中状态k所在的行与符号#所在列的交叉处填写acc,表示成功接收。
4)若GO(Ik,A)=Ip,其中A是非终结符,则令GOTO[k][A]=p,表示状态k下若识别出非终结符A,状态将变为p。
5)分析表中不能用以上规则填写的交叉处,全部填写报错标记。 - LR(0)文法:若一个文法G的LR(0)项目集规范族中所有的LR(0)项目集均不含有冲突项目,则该文法是LR(0)文法。
- LR(0)项目集中的冲突
1)移进-归约冲突
面临一个终结符,同时出现了移进和归约两种动作。
2)归约-归约冲突
一个项目集中出现两个归约动作 - LR分析器总控程序的工作步骤
1)将(状态0,#)入栈.
2)将下一个输入符号读入变量a中。
3)由栈顶当前状态和变量a,查找ACTION子表:
i)对sj型动作,二元组(状态j,a)入栈,且下一个输入符号读入变量a中;
ii)对rj型动作,用第j条规则进行归约;
若是rj型动作,用第j条规则进行归约时,设j条规则是A→β,且规则右部β的长度为n,则首先从栈中弹出n个符号,设此时栈顶的状态为s‘,然后由当前栈顶状态s‘及第j条规则A→β的左部符号A查GOTO子表,即GOTO[状态s’][A],得到新状态w,最后将二元组(状态w,A)入栈。
iii)acc则成功,结束;
iv)报错,调用出错处理子程序。
4)跳到3)。
SLR(1)分析技术
- 含义:简化了的LR(1)分析技术。
- SLR(1)的移进-归约方式:
1)若下一个符号x是b,则使用A→α·bβ项目将b移进。
2)若下一个符号x∈FOLLOW(B),则使用B→γ·项目进行归约。
3)若下一个符号x∈FOLLOW©,则使用C→ξ·项目进行归约。 - 与LR(0)的区别:ACTION子表中r型动作填写规则不同。
对于项目集Ik={B→γ·℗}:
1)LR(0)分析表:状态k所在的行全部填写上rp。
2)SLR(1)分析表:只有当终结符号x∈FOLLOW(B)时,才在状态所在的行与符号x所在的列的交叉处填写上rp,其余部分和LR(0)相同。 - SLR(1)型文法:若文法G的SLR(1)分析表中没有多重定义项,则该文法是SLR(1)文法。
LR(1)分析技术
- SLR(1)的缺点:对每一个项目,仅依靠FOLLOW集,没有精确指明面临哪些符号时才能归约。
- LR(1)项目
1)定义:形如[A→α·β,x]称为LR(1)项目。A→α·β是LR(0)项目,x是终结符。终结符x称为该LR(1)项目的搜索符。
2)含义:表示当A→α·β到达归约项目A→αβ·时,只有面临的下一个符号是x时才能进行归约。
3)若一个项目集中有LR(1)项目:[A→α·β,a],[A→α·β,b],[A→α·β,c],则可合并写成[A→α·β,a/b/c]。
4)LR(1)的开始项目:[S’→·S,#] - LR(1)项目对活前缀有效:若存在推导S ⇒* δAη ⇒ δαβη,其中ω=δα,x是η的第一个符号,或者η为ε时,x为#,则称LR(1)项目[A→α·β,x]对活前缀ω有效。
- LR(1)项目闭包集
设有LR(1)项目集I,计算CLOSURE(I)的步骤:
1)将I中的任何项目全加入到CLOSURE(I)中。
2)若项目[A→α·Bβ,x]∈CLOSURE(I),则对任何规则B→ξ,将项目[B→·ξ,First(βx)]加入到CLOSURE(I)中。
3)反复做2),直到CLOSURE(I)中不再加入新的项目为止。 - LR(1)的GO函数:状态集I与符号X的状态变迁函数GO(I,X)=CLOSURE(J)。其中J是I经过X的后继项目集,即J={[A→αX·β,a]|[A→α·Xβ,a]∈I}。
- LR(1)分析表的构造步骤:
1)若移进项目[A→α·aβ,x]∈Ik且GO(Ik,a)=Ij,其中a是终结符,则令ACTION[k][a]=sj。表示将a移进栈,当前状态变迁到状态j。
2)若归约项目[A→β·,a]∈Ik,并设A→β的规则编号为p,则将ACTION[k][a]rp。
3)若接收项目[S‘→S·,#]∈Ik,则令ACTION[k][#]=accj。表示成功接收。
4)若GO(Ik,A)=Ip,其中A是非终结符,则令GOTO[k][A]=p。表示状态k下若识别出非终结符A,则将状态变为p。
5)分析表中不能用以上规则进行填写的交叉处,全部填写报错标记。 - LR(1)文法:若LR(1)分析表中没有多重定义项,则该文法是LR(1)的文法。
LALR(1)分析技术
- 概述
1)LALR(1)分析表的结构和大小与SLR(1)相同,比LR(1)的分析表小。
2)LALR(1)的分析能力比SLR(1)强,比LR(1)稍弱。 - 基本思想
将LR(1)项目集规范族中的所有同心项状态集合并。合并时,对应项目的搜索符也合并。原先各自接收有向边,都改为由合并后的项目集接收。原先各自发出的有向边都改为由合并后的项目集发出。
1)合并后的项目集可能出现归-约归冲突。 - LALR(1)分析表的构造步骤:
1)构造文法G的LR(1)项目集规范族。设项目集族为{I0,I1,…,In}。
2)合并所有同心项集。设新的项目集族为{ I0’,I1’,…,Ij’ }。
3)若移进项目[A→α·aβ,x]∈Ik’且GO(Ik’,a)=Ij’,其中a是终结符,则令ACTION[k][a]=sj。表示将a移进栈,且状态变迁到状态j。
4)若归约项目[A→β·,a]∈Ik’,并设A→β的规则编号是p,则令ACTION[k][a]=rp。
5)若接收项目[S‘→S·,#]∈Ik’,则令ACTION[k][#]=acc,表示成功接收。
6)构造合并后的GOTO表。
设Ik’是由It1,It2,…,Itp合并得到的,由于It1,It2,…,Itp是同心项目集,所以GO(It1,X),GO(It2,X) ,…,GO(Itn,X)也是同心项目集且它们合并后的项目集为Im’。
若GO(Ik’,X)=Im’,其中X是非终结符,则令GOTO[k][X]=m,表示状态k下若识别处非终结符X,状态将变为m。
7)分析表中不能用以上规则填写的交叉处,全部填写上报错标记。 - LALR(1)文法:若LALR(1)分析表中没有多重定义项,则该文法是LALR(1)文法。