第五章 自下而上语法分析
自下而上分析法(Bottom-up)
- 基本思想:从输入串开始,逐步进行“归约”,直到文法的开始符号。即从树末端开始,构造语法树。核心问题:确定可归约串。
- 所谓归约,是指根据文法的产生式规则,如果一个串中出现了产生式的右部,那就把那个产生式的右部替换成该产生式的左部符号。
- 两类自下而上分析方法
- 算符优先分析法:按照算符的优先关系和结合性质进行语法分析。适合分析表达式。
- 最左素短语作为可归约串
- LR分析法:规范归约–确保每次归约都是用当前句子的句柄进行归约。
- 规范归约的过程从开始符号这样倒过来写完,就是规范推导的过程(而且因为句柄都是最左边的,所以倒过来后的规范推导是最右推导)
- 句柄作为可归约串
- 算符优先分析法:按照算符的优先关系和结合性质进行语法分析。适合分析表达式。
“移进–归约”思想进行自下而上分析:把输入串一个一个移进栈中,当栈顶形成某个产生式的右部时,就把那部分替换为产生式的左部符号。到栈里只剩一个文法开始符号且串已经分析完毕,则返回“输入串是合法符号”
- 短语:需要归约的目标。定义是设S是文法G开始符号,假设αßδ是文法G的一个句型(α、ß、δ均为任意长度串),如果有A=*=>ß,即ß可以归约为A,并且归约为A后,αAδ仍然是文法G的一个句型,则ß称为句型αßδ对非终结符A的短语
- 句柄:特别的,如果上面短语定义中的ß可以一步直接归约成A,而不是A=*=>ß这样多步,则此时ß为句型αßδ对规则A->ß的直接短语(对应短语,短语是需要归约的目标,直接短语是马上就要归约的所有目标,因为一步就能归约),而一个句型的最左直接短语称为该句型的句柄(马上就要归约的那一个目标)。
- 快捷找短语、直接短语、句柄:把问的句型的语法树画出来,以非终结符为根的两代以上的子树的所有末端结点从左到右排列就是相对于该非终结符的一个短语,如果子树只有两代,则该短语就是直接短语
- 通过不断画语法树找句柄来对句子进行归约,归约后继续画语法树找句柄…(因为句柄是马上要归约的短语)
- 演示目的,实际不可能画出语法树,因为语法分析的最终目的就是画出语法树,怎么可能一开始就把语法树画出来,那还用算什么
- 规范归约是最右推导的逆过程,规范归约是最左归约,规范推导是最右推导
- 凡是由规范推导(最右推导)推出的句型成为规范句型
- 规范句型句柄后都是终结符
- 规范归约中得到的每一个串都是规范句型,因为规范归约是规范推导的逆过程
符号栈的使用:
例
步骤 符号栈 输入串 动作
0 # i1*i2+i3# 预备
1 #i1 *i2+i3# 进
2 #F *i2+i3# 归,用F->i
... ... ... ...
13 #E # 归,用E->E+T
算符优先分析法
为什么有算符优先分析法?首先以四则运算为例,是有固定顺序的。但是对于二义文法中的四则运算,却有不同的计算顺序。这是由不同的归约顺序造成的。不同的归约顺序导致了不同的语法树,也就导致了不同的语义解释,这也就导致了最后计算出来的值不一样,因为你先算加减再算乘除和先算乘除再算加减最后结果肯定不一样啊。
但是如果规定了算符的优先次序,并要求要按这种规定进行归约,则归约过程是唯一的,结果也是一致的。
算符优先分析就是找算符(终结符)之间的优先关系,根据优先关系找可归约串。
我们说过自下而上的语法分析核心是确定可归约串,规范归约是根据句柄来确定,算符优先分析则是根据算符之间的优先关系来确定可归约串(而且这个优先关系可以由产生式本身反映出来)。
优先关系分为低于、高于、等于:表示在相邻出现的情况下(如终结符a、b,…ab…和…aQb…都叫相邻出现,Q为一个非终结符),谁先归约,谁后归约。注意,优先级相等是ab一起归约的意思。
适合算符优先分析的文法–算符文法:任何产生式的右部不可能出现两个非终结符并列靠在一起,也就是任何两个非终结符中间一定有一个或者多个终结符靠在一起
- 即不能出现…PQR…,比如四则运算语法,要表示1+2不可能直接写成12,中间一定要有‘+’这个终结符
- P、Q、R:非终结符
- a、b:终结符
- …非终结符和终结符组成的任意串,包含空字 ℇ
具体什么时候是优先级相等、低、高呢
- 文法G中含有像P->…ab…或者P->…aQb…这样的产生式(Q是一个非终结符)则a优先级等于b
- 文法G中含有像P->…aR…的产生式,并且R=+=>b…或R=+=>Qb…(R一步或多步推出b开头的串或一个非终结符接b开头的串)则a优先级低于b(a ⋖ b)
- 文法G中含有像P->…Rb…的产生式,而R=+=>…a或R=+=>…aQ(R一步或多步推出a结尾的串或a接一个非终结符结尾的串)则a优先级高于b(a ⋗ b)
- 如果文法G中任意终结符的对(a,b)要么没有,要么只有以上三种优先级关系中的一种,则G是算符优先文法(算符文法基础上加入了”优先级最多一种”的要求,也就是优先关系表中无冲突)
用自己的话总结FIRSTVT、LASTVT集求法、优先关系表的构造
- 算符优先关系表
- 行名:在左边的终结符、列名:在右边的终结符。
- 优先级相等的格子很好找,直接扫描一遍所有产生式,找所有像P->…ab…或者P->…aQb…这样的产生式就知道(a,b)那个格子应该填相等。
- 优先级高低则需要再次引入集合的概念。如果我能够把P->…aR…中R推出所有串中排在第一个位置的终结符或者排在第二个位置上的终结符(此时第一个位置要求是非终结符),把这些所有的终结符构成一个集合,取名为FIRSTVT集,这样找P->…aR…时只要看FIRSTVT(R)中的元素,在a的右边,那么此时就直接在优先关系表中对应的格子里面填入优先级a小于FIRSTVT(R)中元素即可( ⋖ )
- 同理,定义一个P->…Rb…中R推出所有串中最后出现的或倒数第二个出现的终结符(此时倒数第一个要求是非终结符),把这些所有的终结符构成一个集合,取名为LASTVT集,这样P->…Rb…时看LASTVT(R)中的元素,在b的左边,那么此时就直接在优先关系表中对应的格子里面填入优先级LASTVT(R)中元素大于b( ⋗ )
- 所以说就是不断遍历产生式,对于计算FIRSTVT(P):
- 若有产生式P->a…或P->Qa…则 a ∈ FIRSTVT(P)
- 若a ∈ FIRSTVT(Q),且有产生式P->Q…,则a ∈ FIRSTVT(P) 这是因为如果a ∈ FIRSTVT(Q),说明Q能推出a开头的串,所以直接Q就换成了a…,那