编译原理 笔记 2
自底向上语法分析
自底向上的思想是从要分析的终结符串开始,每一步规约都是将一个子串用一个产生式替换,直到达到开始符号或报错
基本改进方法
与自顶向下相似,直接分析的版本不确定性太高,因此需要进行限制
选择可规约串
对于一个句型,可规约串 定义为该句型的短语
对于文法G = ( V N , V T , P , S ) G = (V_{N}, V_{T}, P, S) G = ( V N , V T , P , S ) ,设α , β , δ ∈ ( V N ∪ V T ) ∗ , w ∈ V T ∗ \alpha, \beta, \delta\in(V_{N}\cup V_{T})^{*}, w\in V_{T}^{*} α , β , δ ∈ ( V N ∪ V T ) ∗ , w ∈ V T ∗ :
若S ⇒ ∗ α A δ S\mathop{\Rightarrow}\limits^{*}\alpha A\delta S ⇒ ∗ α A δ 且A ⇒ + β A\mathop{\Rightarrow}\limits^{+}\beta A ⇒ + β ,则称β \beta β 是句型α β δ \alpha\beta\delta α β δ 相对非终结符A A A 的短语
若S ⇒ ∗ α A δ S\mathop{\Rightarrow}\limits^{*}\alpha A\delta S ⇒ ∗ α A δ 且A ⇒ β A\Rightarrow\beta A ⇒ β ,则称β \beta β 是句型α β δ \alpha\beta\delta α β δ 相对非终结符A A A 的直接短语
若S ⇒ r m ∗ α A w S\mathop{\Rightarrow}\limits_{rm}^{*}\alpha Aw S r m ⇒ ∗ α A w 且A ⇒ β A\Rightarrow\beta A ⇒ β ,则称β \beta β 是右句型α β w \alpha\beta w α βw 相对非终结符A A A 的句柄
短语可以理解为可以被规约的子串,直接短语就是可以通过一步就能规约到的子串,而句柄是所有直接短语中最靠左的一个 (因为在句柄的定义中要求了最右推导,因此右边的直接短语已经被推导完了)
例如对于文法:
S → A B A → a A A → ε B → b B → b B \begin{align*}
S &\to AB \\
A &\to aA \\
A &\to \varepsilon \\
B &\to b \\
B &\to bB
\end{align*}
S A A B B → A B → a A → ε → b → b B
则:
句子a a a b aaab aaab 的短语包括ε , a , a a , a a a , a a a b , b \varepsilon, a, aa, aaa, aaab, b ε , a , aa , aaa , aaab , b ,直接短语包括ε , b \varepsilon, b ε , b ,句柄为ε \varepsilon ε
句型a a A b aaAb aa A b 的短语包括a A , a a A , a a A b , b aA, aaA, aaAb, b a A , aa A , aa A b , b ,直接短语包括a A , b aA, b a A , b ,句柄为a A aA a A
如果文法是二义的,则右句型的最右推导可能有多个,则句柄也可能会有多个
通常采用移进-规约分析
与自顶向下分析比较
功能较强:规约的时候可以获取完整的串信息,而推导只能获得局部信息
利于出错处理
有成熟的自动化技术
移进-规约分析
有一个分析引擎和下推栈,分析引擎能完成的动作包括:
Reduce:按照确定的方式对栈顶短语进行规约
Shift:从输入序列移进一个单词到栈顶
Error:发现错误,进行处理或恢复
Accept:分析成功
上图中的步骤逆向过来就是一种最右推导,称之为规范推导
移进-规约冲突
到达一个既可移进又可规约的状态,例如文法:
S → if E then S ∣ if E then S else S \begin{align*}
S &\to \text{if } E \text{ then } S \,|\, \text{if } E \text{ then } S \text { else } S
\end{align*}
S → if E then S ∣ if E then S else S
则对于串if E then if E then S else S \text{if } E \text{ then if } E \text{ then } S \text { else } S if E then if E then S else S 即可移进又可规约
规约-规约冲突
到达了一个状态,可能有多种短语可以用于规约,例如产生式:
A → a A ∣ a a A A \to aA \,|\, aaA
A → a A ∣ aa A
则对于a a a b aaab aaab 时,对于栈中的a a A aaA aa A ,无法确定该用哪一个短语
表驱动方法
解决上面两个冲突的方法,即通过一张分析表来决定状态机的下一步行为,常见的表有LR分析中的LR分析表、算符优先分析中的算法优先分析表
LR分析
代表从左(L)到右扫描输入序列,产生最右(R)推导,是一种表驱动的移进-规约分析
LR分析表构造
主要是构造两张表ACTION表和GOTO表:
ACTION表:根据栈顶状态和当前输入符号来确定下一步动作
GOTO表:根据栈顶状态和规约用的非终结符决定下一个状态
每一个状态为一个set<pair>
,每一个pair
代表了在当前状态下,消耗的栈顶符号与去掉这个符号后下一步允许什么串进入,本质上是一个大型状态转移图
这张表具体的构造方法按照分析方法不同分别讨论
LR分析算法
1 2 3 4 5 6 7 8 9 10 11 12 13 if (ACTION[i, a] == sj) { PUSH j; READ w; } else if (ACTION[i, a] == rj) { POP len(beta); PUSH GOTO[k, A]; } else if (ACTION[i, a] == acc) { return ; } else exit (1 );
带符号栈的LR分析算法
除了状态栈之外,还有一个符号栈,存储的读入但是没有规约的符号,算法修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 if (ACTION[i, a] == sj) { PUSH (j, a); READ w; } else if (ACTION[i, a] == rj) { POP len(beta); PUSH (GOTO[k, A], A); } else if (ACTION[i, a] == acc) { return ; } else exit (1 );
LR(0)分析
首先引入一些概念
对于文法G = ( V N , V T , P , S ) G = (V_{N}, V_{T}, P, S) G = ( V N , V T , P , S ) ,增加产生式S ′ → S S'\to S S ′ → S ,其中S ′ ∉ V N ∪ V T S'\notin V_{N}\cup V_{T} S ′ ∈ / V N ∪ V T ,则得到增广文法G ′ = ( V N , V T , P , S ′ ) G' = (V_{N}, V_{T}, P, S') G ′ = ( V N , V T , P , S ′ )
对于文法G = ( V N , V T , P , S ) G = (V_{N}, V_{T}, P, S) G = ( V N , V T , P , S ) ,以及串α , β ∈ ( V N ∪ V T ) ∗ , w ∈ V T ∗ \alpha, \beta\in(V_{N}\cup V_{T})^{*}, w\in V_{T}^{*} α , β ∈ ( V N ∪ V T ) ∗ , w ∈ V T ∗ ,其中S ⇒ ∗ α β w S \Rightarrow^{*} \alpha\beta w S ⇒ ∗ α βw ,且β \beta β 为句柄,则α β \alpha\beta α β 任何前缀γ \gamma γ 称为文法G G G 的活前缀
对于增广文法,原开始符号S S S 是G ′ G' G ′ 的活前缀
LR(0) FSM
LR(0)分析的主要工具,是一个以V N ∪ V T V_{N}\cup V_{T} V N ∪ V T 为字母表的DFA,可以通过增广后的文法直接构造
可以证明,G G G 的LR(0)FSM对应的语言是G ′ G' G ′ 所有活前缀的集合
FSM的状态
LR(0) FSM的状态是一个特殊的LR(0)项集,一个LR(0)项是指在右端某一位置有圆点的产生式,圆点代表着已分析过的串与该产生式匹配的位置
项有如下分类:
移进项:A → α . a β A\to \alpha .a\beta A → α . a β
待约项:A → α . B β A\to \alpha .B\beta A → α . Bβ
规约项:A → α . A\to \alpha . A → α .
接收项:S ′ → S . S'\to S . S ′ → S .
我们将LR(0) FSM的每一项定义为一个项集的闭包(CLOSURE)
1 2 3 4 5 6 7 8 9 def CLOSURE (I ): J = I while (1 ): for (j, p) in zip (J, P): if j.hasform("A -> α.Bβ" ) and p.hasform("B -> γ" ): J.append("B -> .γ" ) if J.hasNotChanged(): break return J
则其状态转移函数定义为:
G O ( I , X ) = CLOSURE ( J ) J = { A → α X . β ∣ A → α . X β ∈ I } \begin{align*}
GO(I, X) &= \text{CLOSURE}(J) \\
J &= \{A\to \alpha X.\beta \,|\, A\to \alpha.X\beta \in I \}
\end{align*}
GO ( I , X ) J = CLOSURE ( J ) = { A → α X . β ∣ A → α . Xβ ∈ I }
则构造状态集合的方法就是从{ CLOSURE ( { S ′ → . S } ) } \{\text{CLOSURE}(\{S'\to .S\})\} { CLOSURE ({ S ′ → . S })} 开始,利用上述的状态转移函数逐步扩展状态集合
增广文法的每个活前缀都有唯一与之相对应的状态
LR(0)分析表的构造
令状态集合为C = { I 0 , … , I n } C = \{I_{0} ,\dots,I_{n}\} C = { I 0 , … , I n } ,则构造ACTION与GOTO表的方法为:
G O T O ( k , A ) = j ⇔ G O ( I k , A ) = I j \mathrm{GOTO}(k, A) = j \Leftrightarrow \mathrm{GO}(I_{k}, A) = I_{j} GOTO ( k , A ) = j ⇔ GO ( I k , A ) = I j
A C T I O N ( k , a ) = s j ⇔ A → α . a β ∈ I k & G O ( I k , a ) = I j \mathrm{ACTION}(k, a) = sj \Leftrightarrow A\to\alpha.a\beta \in I_{k} \,\&\, \mathrm{GO}(I_{k}, a) = I_{j} ACTION ( k , a ) = s j ⇔ A → α . a β ∈ I k & GO ( I k , a ) = I j
A C T I O N ( k , a ) = r j ⇔ A → α . ∈ I k \mathrm{ACTION}(k, a) = rj \Leftrightarrow A\to\alpha.\in I_{k} ACTION ( k , a ) = r j ⇔ A → α . ∈ I k
A C T I O N ( k , # ) = a c c ⇔ S ′ → S . ∈ I k \mathrm{ACTION}(k, \#) = acc \Leftrightarrow S'\to S. \in I_{k} ACTION ( k , # ) = a cc ⇔ S ′ → S . ∈ I k
LR(0)文法
按照上述定义构造的分析表,如果各表项均没有多重定义,则称为LR(0)文法,等价于要求每个状态满足:
不同时含有移进和规约项(不能有移进-规约冲突)
不含有两个以上规约项(不能有规约-规约冲突)
一个非 LR(0)文法的例子为:
E → E + T ∣ T T → T ∗ F ∣ F F → ( E ) ∣ v ∣ d \begin{align*}
E &\to E + T \,|\, T \\
T &\to T * F \,|\, F \\
F &\to (E) \,|\, v \,|\, d
\end{align*}
E T F → E + T ∣ T → T ∗ F ∣ F → ( E ) ∣ v ∣ d
短语T T T 即存在移进-规约冲突
SLR(1)分析
由于LR(0)限制非常严格(要求看到栈顶状态就能决定动作),因此做一些条件的放松,常见的是允许向前查看一个符号成为LR(1),首先来研究其简化版本SLR(1)
SLR(1)通过判断下一个输入符号是否属于要规约的非终结符的Follow集合,来解决移进-规约冲突和规约-规约冲突
如果一个状态中规约项的非终结符的Follow集两两无交,则可以解决规约-规约冲突
如果一个状态中所有规约项中要规约的非终结符的Follow集与所有要移进的符号集互不相交,则可以解决移进-规约冲突
SLR(1)分析表
其分析表的构造相对于LR(0)来说只用做简单的修改,也即在填充A C T I O N ( k , a ) = r j \mathrm{ACTION}(k, a) = rj ACTION ( k , a ) = r j 时,需要保证a ∈ F o l l o w ( A ) a\in \mathrm{Follow}(A) a ∈ Follow ( A )
按照上述定义构造的分析表,如果各表项均没有多重定义,则称为SLR(1)文法,等价于要求每个状态满足:
对于任意两项A → α . A\to \alpha. A → α . 与B → β . B\to \beta. B → β . ,有F o l l o w ( A ) ∩ F o l l o w ( B ) = ∅ \mathrm{Follow}(A) \cap \mathrm{Follow}(B) = \emptyset Follow ( A ) ∩ Follow ( B ) = ∅
对于任意两项A → α . a β A\to \alpha.a\beta A → α . a β 与B → γ . B\to \gamma. B → γ . ,均有a ∉ F o l l o w ( B ) a\notin \mathrm{Follow}(B) a ∈ / Follow ( B )
一个非 SLR(1)文法的例子为:
E → ( L , E ) ∣ F L → L , E ∣ E F → ( F ) ∣ d \begin{align*}
E &\to (L,E) \,|\, F \\
L &\to L,E \,|\, E \\
F &\to (F) \,|\, d
\end{align*}
E L F → ( L , E ) ∣ F → L , E ∣ E → ( F ) ∣ d
短语( F ) (F) ( F ) 会被错误的规约为( E ) (E) ( E )
LR(1)分析
在SLR(1)的基础上进一步改进,将传统的项增加一个终结符,表示产生式右端完整匹配 后所允许留在符号串中的下一个终结符,即项会形如:
A → α . β , a A\to \alpha.\beta\,,\,a
A → α . β , a
其中a ∈ V T ∪ { # } a\in V_{T}\cup\{\#\} a ∈ V T ∪ { # }
对于形如A → α . , a A\to \alpha.\,,\,a A → α . , a 的项,只有当下一个输入为a a a 的时候才能进行规约
LR(1)FSM
LR(1)FSM的状态是LR(1)项集的闭包,闭包算法为:
1 2 3 4 5 6 7 8 9 def CLOSURE (I ): J = I while (1 ): for (j, p) in zip (J, P): if j.hasform("[A -> α.Bβ, a]" ) and p.hasform("B -> γ" ): J.append("[B -> .γ, b]" ) for b in First(βa) if J.hasNotChanged(): break return J
初态定义为I 0 = C L O S U R E ( { [ S ′ → . S , # ] } ) I_{0} = \mathrm{CLOSURE}(\{[S'\to .S, \#]\}) I 0 = CLOSURE ({[ S ′ → . S , # ]})
状态转移函数定义为:
G O ( I , X ) = CLOSURE ( J ) J = { [ A → α X . β , a ] ∣ [ A → α . X β , a ] ∈ I } \begin{align*}
GO(I, X) &= \text{CLOSURE}(J) \\
J &= \{[A\to \alpha X.\beta\,,\, a] \,|\, [A\to \alpha.X\beta \,,\, a] \in I \}
\end{align*}
GO ( I , X ) J = CLOSURE ( J ) = {[ A → α X . β , a ] ∣ [ A → α . Xβ , a ] ∈ I }
从初态开始利用上述转移函数扩展,可以得到项集规范族
在上述非SLR(1)的例子中,E → F . E\to F. E → F . 所期待的下一个输入符号没有) ) ) ,因此( F ) (F) ( F ) 中的F F F 不会被规约为E E E ,而是会选择移进
LR(1) 分析表构造
G O T O ( k , A ) = j ⇔ G O ( I k , A ) = I j \mathrm{GOTO}(k, A) = j \Leftrightarrow \mathrm{GO}(I_{k}, A) = I_{j} GOTO ( k , A ) = j ⇔ GO ( I k , A ) = I j
A C T I O N ( k , a ) = s j ⇔ [ A → α . a β , b ] ∈ I k & G O ( I k , a ) = I j \mathrm{ACTION}(k, a) = sj \Leftrightarrow [A\to\alpha.a\beta\,,\,b] \in I_{k} \,\&\, \mathrm{GO}(I_{k}, a) = I_{j} ACTION ( k , a ) = s j ⇔ [ A → α . a β , b ] ∈ I k & GO ( I k , a ) = I j
A C T I O N ( k , b ) = r j ⇔ [ A → α . , b ] ∈ I k \mathrm{ACTION}(k, b) = rj \Leftrightarrow [A\to\alpha.\,,\, b]\in I_{k} ACTION ( k , b ) = r j ⇔ [ A → α . , b ] ∈ I k
A C T I O N ( k , # ) = a c c ⇔ [ S ′ → S . , # ] ∈ I k \mathrm{ACTION}(k, \#) = acc \Leftrightarrow [S'\to S.\,,\, \#] \in I_{k} ACTION ( k , # ) = a cc ⇔ [ S ′ → S . , # ] ∈ I k
LR(1)文法
按上述算法构造的分析表若每项都无重复定义,则称为LR(1)文法,其FSM每一个状态都满足:
对于任意两个项目[ A → α . a β , b ] [A\to \alpha.a\beta\,,\, b] [ A → α . a β , b ] 与[ B → γ . , c ] [B\to \gamma., c] [ B → γ . , c ] ,一定有a ≠ c a\neq c a = c
对于任意两个项目[ A → α . , a ] [A\to \alpha.\,,\, a] [ A → α . , a ] 与[ B → β . , b ] [B\to \beta.\,,\, b] [ B → β . , b ] ,一定有a ≠ b a\neq b a = b
LR(k)文法
可以扩展到LR(k)文法,但是状态机的复杂度过高,无实际意义,并且可以证明L R ( N + ) \mathrm{LR}(\mathbb{N}^{+}) LR ( N + ) 都是等价的
LALR(1)分析
是一个LR(1)与SLR(1)的折中版本
将LR(1)的项的第一部分称为芯 ,如果LR(1)FSM的两个状态的芯构成的集合完全相同,那么我们称其为同芯的状态
将LR(1)FSM中的同芯状态合并,如果得到的新状态没有规约-规约冲突,则得到的新文法是LALR(1)文法
LALR(1)FSM
构造方法为brute-force方法:
构造增广文法的LR(1)FSM状态集
合并同芯状态
LALR(1)FSM状态的后继状态是其合并前所有同芯状态的后继状态的并
LALR(1)FSM状态数与LR(0)状态数相同,但是其能力强于SLR(1)