自下而上的语法分析

句柄

右句型 γ \gamma γ的句柄是一个产生式的右部 β \beta β,并且该句柄 β \beta β通过产生式 A → β A\rightarrow\beta Aβ归约后,得到的是最右推导中的前一个句型。
右句型:所有在最右推导中出现的句型都是右句型。
? S ⇒ r m S \Rightarrow _ { r m } Srm aABe ⇒ r m \Rightarrow _ { r m } rm aAde ⇒ r m \Rightarrow _ { r m } rm aAbcde ⇒ r m \Rightarrow _ { r m } rm abbcde
文法为
S → S \rightarrow S aABe
A → A b c ∣ b { A \rightarrow A b c | b } AAbcb
B → d B \rightarrow d Bd
abbcde中的第一个b通过 A → b A\rightarrow b Ab归约后得到aAbcde,是最右推导的前一个句型,所以第一个b是句柄。而第二个b通过 A → b A\rightarrow b Ab归约后得到aAAcde,不是最右推导的前一个句型,所以第二个b不是句柄。(栗子中加粗部分为句柄)

  • 句柄的右边仅含终结符。
  • 如果文法二义,那么句柄可能不唯一。

两个冲突

移进-归约冲突:既可以移进又可以归约时,无法决定。
归约-归约冲突:当不止一个产生式可以归约,无法决定对哪个产生式进行归约。

活前缀

活前缀:右句型的前缀,该前缀不超过最右句型句柄的右端。
在移进-归约分析中,出现在栈中的串都是活前缀。
? S ⇒ ∗ r m γ A w ⇒ r m γ β w \mathcal { S } \Rightarrow * _ { r m } \gamma A w \Rightarrow _ { r m } \gamma \beta w SrmγAwrmγβw
γ β \gamma \beta γβ的任意前缀(包括 ε \varepsilon ε γ β \gamma \beta γβ本身)都是活前缀,这里的 β \beta β是句柄。

LR分析表

L表示从左到右扫描输入串,R表示最右推导。分为LR(0)/SLR(1)、LR(1)、LALR三种。

构造SLR分析表

  • 拓广文法,即添加产生式 S ′ → S S ^ { \prime } \rightarrow S SS
  • 构建识别活前缀的DFA
  • 根据DFA构建SLR分析表

构建识别活前缀的DFA

LR(0)闭包函数closure(I)

  • I中的所有项都属于closure(I)
  • 如果 A → α ⋅ B β A \rightarrow \alpha \cdot B \beta AαBβ属于closure(I),并且 B → γ B \rightarrow \gamma Bγ是产生式,那么如果 B → ⋅ γ B \rightarrow \cdot \gamma Bγ还不在closure(I)中,则把它加入closure(I)中。
  • 重复上面两个步骤,直至closure(I)不再变化。

LR(0)状态转换函数goto(I, X)
I状态集中所有形如 [ A → α ⋅ X β ] [ A \rightarrow \alpha \cdot X \beta ] [AαXβ]的产生式对应的产生式 [ A → α X ⋅ β ] [ A \rightarrow \alpha X \cdot \beta ] [AαXβ]LR(0)闭包。X为终结符或非终结符。
? S → S \rightarrow S aABe ; A → A b c ∣ b { A \rightarrow A b c | b } AAbcb B → d B \rightarrow d Bd
对于 I 0 : S ′ → S I_0: S' \rightarrow S I0:SS S → ⋅ a A B e S \rightarrow \cdot aABe SaABe
I 1 = g o t o ( I 0 , a ) : I_1=goto(I_0, a): I1=goto(I0,a): S → a ⋅ A B e S \rightarrow a \cdot ABe SaABe A → ⋅ A b c A \rightarrow \cdot Abc AAbc A → ⋅ b A \rightarrow \cdot b Ab

识别文法G活前缀的DFA通过下面的方式构造:

  • C = { c l o s u r e ( S ′ → S ) } C = \{closure(S' \rightarrow S)\} C={closure(SS)}
  • C C C中的每一个项目集应用转换函数goto(I, X)得到新的项目集 I n I_n In,并把 I n I_n In加入到 C C C中。
  • 重复第二步,直到 C C C不再增大为止。

根据DFA构建SLR分析表

状态i从 I i I_i Ii构造,它的action函数如下确定:

  • 如果 [ A → α ⋅ a β ] [ A \rightarrow \alpha \cdot a \beta ] [Aαaβ] I i I_i Ii中,并且goto( I i I_i Ii,a )= I j I_j Ij,那么置action[i, a]为 s j s_j sj
  • 如果 [ A → α ⋅ ] [ A \rightarrow \alpha \cdot ] [Aα] I i I_i Ii中,那么对FOLLOW(A)中的所有终结符a,置action[i, a]为 r j r_j rj j j j是产生式 A → α ⋅ A \rightarrow \alpha \cdot Aα的编号。
  • 如果 [ S ′ → S ⋅ ] \left[ \mathcal { S } ^ { \prime } \rightarrow \mathcal { S } \cdot \right] [SS] I i I_i Ii中,那么置action[i, ?]为接受acc。

如果出现动作冲突,那么该文法就不是SLR(1)的。

构造状态i的goto函数:
对所有的非终结符A,如果goto( I i I_i Ii,A)= I j I_j Ij, 那么goto[i, A]= j j j

不能由上面两步定义的条目都为error。

SLR(1)文法的问题

每个SLR(1)文法都不是二义的,但是,有许多非二义的文法不是SLR(1),文法描述能力弱。SLR(1)是在构造完DFA的LR(0)项目集之后才应用预测符号的,即对需要归约的产生式,当其遇到产生式左部非终结符的FOLLOW集中的终结符时才进行归约,而在LR(0)的构造中没有考虑预测。

构造规范的LR分析表

基本步骤同SLR一样,只在第二步和第三步时有所不同,只说不同的地方。

构建识别活前缀的DFA

使用LR(1)文法,1表示项目 [ A → α ⋅ β , a ] [A \rightarrow \alpha \cdot \beta , a] [Aαβ,a]中搜索符的长度。
LR(1)闭包函数closure(I)

  • I中的所有项都属于closure(I)
  • [ A → α ⋅ B β , a ] [A\rightarrow \alpha \cdot B \beta, a] [AαBβ,a]属于closure(I), B → γ B\rightarrow \gamma Bγ是产生式,则对于每个终结符b ∈ F I R S T ( β a ) \in FIRST(\beta a) FIRST(βa),项 [ B → ⋅ γ , b ] [B\rightarrow \cdot \gamma ,b] [Bγ,b]也加入到closure(I)中。
  • 重复上面两个步骤,直至closure(I)不再变化。

搜索符b的集合是FOLLOW(B)的一个子集。

LR(1)状态转换函数goto(I, X)
I状态集中所有形如 [ A → α ⋅ X β , b ] [ A \rightarrow \alpha \cdot X \beta,b ] [AαXβ,b]的产生式对应的产生式 [ A → α X ⋅ β , b ] [ A \rightarrow \alpha X \cdot \beta,b ] [AαXβ,b]LR(1)闭包。X为终结符或非终结符。注意这里的搜索符集b是直接由前面对应的项目抄过来的。

识别文法G活前缀的DFA通过下面的方式构造:

  • C = { c l o s u r e ( [ S ′ → S , d o l l e r ] ) } C = \{closure([S' \rightarrow S, doller ])\} C={closure([SS,doller])} 注:这里的doller指$,latex解析不正确这样代替一下…
  • C C C中的每一个项目集应用转换函数goto(I, X)得到新的项目集 I n I_n In,并把 I n I_n In加入到 C C C中。
  • 重复第二步,直到 C C C不再增大为止。

根据DFA构建SLR分析表

基本同SLR,不同点在于:在action函数中,如果有归约,SLR是根据左部非终结符的FOLLOW集决定进行归约;LR(1)是根据搜索符决定进行归约。

LR(1)文法的问题

LR(1)文法描述能力较强,但是由于状态数目多,分析表较大。

构造LALR分析表

LALR是在SLR(1)和LR(1)之间进行了文法描述能力与分析表紧凑程度之间做的折中。

LALR的做法

合并识别LR(1)文法的活前缀的DFA中的同心项目集。

同心项目集

略去搜索符后相同的项目集。
? B → ⋅ b B B \rightarrow \cdot b B BbB B → ⋅ b B , b / a B \rightarrow \cdot b B ,b / a BbB,b/a

合并同心集引起的冲突

同心集的合并不会引起新的移进-归约冲突。
?如果同心集中有移进-归约冲突 [ A → α ⋅ , a / b ] \left[ A \rightarrow \alpha \cdot, a / b \right] [Aα,a/b] [ B → β ⋅ a γ , c / d ] [ B \rightarrow \beta \cdot a \gamma , c / d ] [Bβaγ,c/d],当面对输入符号a时不知道该移进还是归约。合并前的项目集应该有 [ A → α ⋅ , x ] \left[ A \rightarrow \alpha \cdot, x \right] [Aα,x] [ B → β ⋅ a γ , y ] [ B \rightarrow \beta \cdot a \gamma , y ] [Bβaγ,y],肯定有个x为a,所以一定存在移进-归约冲突,说明合并之前就存在移进-归约冲突了。

同心集的合并有可能产生新的归约-归约冲突。
?合并前项目集 [ A → c ⋅ , d ] [A \rightarrow c \cdot, d] [Ac,d] [ B → c ⋅ , e ] [B \rightarrow c \cdot, e] [Bc,e] [ A → c ⋅ , e ] [A \rightarrow c \cdot, e] [Ac,e] [ B → c ⋅ , d ] [B \rightarrow c \cdot, d] [Bc,d],合并后为
[ A → c ⋅ , e / d ] [A \rightarrow c \cdot, e/d] [Ac,e/d] [ B → c ⋅ , d / e ] [B \rightarrow c \cdot, d/e] [Bc,d/e],此时就产生了新的归约-归约冲突。

你可能感兴趣的:(编译原理)