首先,什么叫做左遞歸呢? 一個左遞歸的語法通常有這樣的形式 : A-> Aa .而自頂向下的語法分析是無法處理左遞歸語法的。為什么呢?無論是遞歸分析還是預測分析或者是LL文法分析,在碰到左遞歸這種語法時都會陷入死循環當中。如果我們用遞歸分析,那么在分析A這個非終結符號的時候就會調用functionA,functionA將A分解成A,a,然后在我們再次碰到A的時候又會調用functionA,這樣便形成了無限遞歸。如果我們用非遞歸的LL文法分析,那么在我們將把A->Aa無限次地壓入到棧中,即每次彈出A都會壓入Aa。所以我們必須采取手段消除左遞歸,下面給出標准方法。
其中β1...βn 不是從A開始
其實原理在於通過轉換將A的語法不從非終結符號(A本身)開始,而是從終結符號β1...βn 開始。雖然A的原語法是從A本身開始的,但是第一個符號一定是β1...βn中的一個,而不可能是任何一個α。所以我們通過一個中間變量A'來表示剩下的α,然而不要忘記由於A' ->αA' 這條規則,A' -> ε 必須也存在於語法規則中,否則末尾將無法匹配完成。
但是,上述方法只適用於立即左遞歸,還有一種更隱蔽的非立即左遞歸,如 S -> Aa | b , A -> Sc | d ,我們如果用自頂向下的分析方法會陷入 S -> Aa -> Sca 這樣的死循環中。當然,也有相應的解決辦法。
將所有非終端符號以某個固定的順序排列
-
從 i = 1 到 n {
-
從 j = 1 到 i – 1 {
-
- 設
的生成規則為
- 設
-
- 將所有規則
換成
- 將所有規則
-
- 移除
規則中的直接左遞歸
- 移除
-
- }
-
從 j = 1 到 i – 1 {
- }
-
也許看上面的規則過於抽象,我們用S -> Aa | b , A -> Sc | d 來實踐一下上述的方法。我們以S,A的順序排列。則只需執行一次主程序體,且Ai 為A,Aj為S。則:
- A -> Aac | bc | d, 然后再運用前面的規則消除直接左遞歸可得:A -> bcA' | dA' , A' -> acA' | ε
- 請注意,以上的解決方案是基於右遞歸的文法,並不是完全適用於所有的情況。我們得到的文法可能含有 ε表達式,並且可能會改變語法的結合律。解決方案就是保留左遞歸的語法,不用自頂向下的方式分析。