句柄
右句型
γ
\gamma
γ的句柄是一个产生式的右部
β
\beta
β,并且该句柄
β
\beta
β通过产生式
A
→
β
A\rightarrow\beta
A→β归约后,得到的是最右推导中的前一个句型。
右句型:所有在最右推导中出现的句型都是右句型。
?
S
⇒
r
m
S \Rightarrow _ { r m }
S⇒rm 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 }
A→Abc∣b
B
→
d
B \rightarrow d
B→d
abbcde中的第一个b通过
A
→
b
A\rightarrow b
A→b归约后得到aAbcde,是最右推导的前一个句型,所以第一个b是句柄。而第二个b通过
A
→
b
A\rightarrow b
A→b归约后得到aAAcde,不是最右推导的前一个句型,所以第二个b不是句柄。(栗子中加粗部分为句柄)
- 句柄的右边仅含终结符。
- 如果文法二义,那么句柄可能不唯一。
两个冲突
移进-归约冲突:既可以移进又可以归约时,无法决定。
归约-归约冲突:当不止一个产生式可以归约,无法决定对哪个产生式进行归约。
活前缀
活前缀:右句型的前缀,该前缀不超过最右句型句柄的右端。
在移进-归约分析中,出现在栈中的串都是活前缀。
?
S
⇒
∗
r
m
γ
A
w
⇒
r
m
γ
β
w
\mathcal { S } \Rightarrow * _ { r m } \gamma A w \Rightarrow _ { r m } \gamma \beta w
S⇒∗rmγAw⇒rmγβw
γ
β
\gamma \beta
γβ的任意前缀(包括
ε
\varepsilon
ε和
γ
β
\gamma \beta
γβ本身)都是活前缀,这里的
β
\beta
β是句柄。
LR分析表
L表示从左到右扫描输入串,R表示最右推导。分为LR(0)/SLR(1)、LR(1)、LALR三种。
构造SLR分析表
- 拓广文法,即添加产生式 S ′ → S S ^ { \prime } \rightarrow S S′→S
- 构建识别活前缀的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 }
A→Abc∣b ;
B
→
d
B \rightarrow d
B→d
对于
I
0
:
S
′
→
S
I_0: S' \rightarrow S
I0:S′→S;
S
→
⋅
a
A
B
e
S \rightarrow \cdot aABe
S→⋅aABe
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
S→a⋅ABe;
A
→
⋅
A
b
c
A \rightarrow \cdot Abc
A→⋅Abc;
A
→
⋅
b
A \rightarrow \cdot b
A→⋅b
识别文法G活前缀的DFA通过下面的方式构造:
- 令 C = { c l o s u r e ( S ′ → S ) } C = \{closure(S' \rightarrow S)\} C={closure(S′→S)}
- 对 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] [S′→S⋅]在 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([S′→S,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
B→⋅bB 和
B
→
⋅
b
B
,
b
/
a
B \rightarrow \cdot b B ,b / a
B→⋅bB,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]
[A→c⋅,d]
[
B
→
c
⋅
,
e
]
[B \rightarrow c \cdot, e]
[B→c⋅,e]和
[
A
→
c
⋅
,
e
]
[A \rightarrow c \cdot, e]
[A→c⋅,e]
[
B
→
c
⋅
,
d
]
[B \rightarrow c \cdot, d]
[B→c⋅,d],合并后为
[
A
→
c
⋅
,
e
/
d
]
[A \rightarrow c \cdot, e/d]
[A→c⋅,e/d]
[
B
→
c
⋅
,
d
/
e
]
[B \rightarrow c \cdot, d/e]
[B→c⋅,d/e],此时就产生了新的归约-归约冲突。