編譯原理之語法分析-自下而上分析(三)

從不洗頭的程式猿發表於2020-05-17

    在上一篇部落格中我們已經講過如何構造LR(0)分析表,SLR構造分析表的前五個步驟是與LR(0)一樣的,因此這裡就不再對前五個步驟講解。

    前五個步驟一樣的原因:一個文法如果是SLR文法,則它一定是LR(0)文法,因此我們在判斷它是不是SLR文法之前要先判斷是不是LR(0)文法

    https://www.cnblogs.com/scm2019/p/12899757.html (前五個步驟可參考上一篇部落格)

    (一)SLR分析法

      SLR分析法作用:SLR基於LR(0),通過一個前看符號,來決定是否執行歸約動作或執行哪個歸約動作。

      SLR分析法優點:1、有可能減少需要歸約的情況   2、能夠去除某些移進-歸約衝突

      SLR分析法缺點:仍然有衝突出現的可能。

      

      SLR(1)解決衝突的辦法

      首先看一下比較標準的解釋:

      

      舉個例子理解上圖,假如這裡有一個專案集,如下圖。

     

 

 

       很明顯這是一個s-r衝突,所以它不屬於LR(0)文法,但是根據SLR(1)的解決方法,我們可以先求出產生式左側終結符的Follow集。

       假如Follow(R) = { +, - } ,Follow(T) = { !,* }

       這時根據R和T的Follow集和移進專案圓點後的非終結符有以下三種情況

       1、當輸入符號為 = 時,這時我們就把第一個產生式進行移進操作。

       2、當輸入符號屬於Follow(R)時(+ 或者 -),就將R->L·進行歸約。

       3、當輸入符號屬於Follow(T)時(! 或者 *),就將T->T·進行歸約。

       以上為成功消除衝突的情況

       假如Follow(R) = { =, * } ,Follow(T) = { +,* }

       1、移進專案圓點後的終結符(=)屬於Follow(R),因此報錯,即還是存在移進-歸約衝突。因為我們還是不知道當輸入符號為=時,要對S->L·=R進行移進,還是對R->L·進行歸約。

       2、此外,Follow(R) ∩ Follow(T) = { * },報錯,即還是存在歸約-歸約衝突。因為我們還是不知道當輸入符號為*時,要對R->L·進行歸約,還是對T->L·進行歸約。

       以上為消除衝突失敗的情況

       總結:如果想消除專案中的集衝突,必須滿足以下兩種情況(重要事情說三遍)

          總結:如果想消除專案中的集衝突,必須滿足以下兩種情況(重要事情說三遍)

       總結:如果想消除專案中的集衝突,必須滿足以下兩種情況(重要事情說三遍)

       1、移進專案圓點後的終結符(嚴格來說是圓點後的First集),不屬於歸約專案的Follow集。

       2、所有歸約專案的Follow集兩兩相交必須為空。

    (二)構造SLR分析表

      ,判斷文法是否屬於SLR文法,如果是給出SLR分析表

      1、列出文法的規範專案集

       

       2、構造識別活字首的NFA

        

 

      3、識別或字首的DFA

 

         

      

 

 

       4、根據DFA判斷LR(0)

        顯然,在I1中面對符號E我們無法判斷要進行歸約還是移進,及存在S-R衝突。

        同樣,在I2中面對終結符id,也存在著S-R衝突。

        因此該文法不是LR(0)文法

      5、算出Follow集,然後判斷能否使用SLR的方法消除衝突,如果可以則是SLR文法        

        在I1中, Follow(S)={#} 不包含終結符+,因此I1中的S-R衝突可以消解。

 

        在I2中,Follow(E)={ #, ),+ },不包含終結符( ,因此I2的S-R衝突可以消解。

 

        綜上所述,衝突能夠被消除,該文法屬於SLR(1)文法。

 

      6、畫出SLR(1)分析表

           文法編號為:      

        1 : S->E

        2 : E -> id

        3 : E -> id(E)

        4 : E -> E+ id

        

 

         構造SLR(1)方法技巧

         其實LR(0)和SLR(1)分析表雷同,SLR(1)分析表只是比LR(0)分析表少幾個歸約項。

         假如該文法是一個LR(0)文法,那麼跟上圖不一樣的是3、6、7所在的行應該填滿,即3所在行全填r2,6所在行全填r4,7所在行全填r3。其餘的和上圖一模一樣。

         但是為什麼SLR(1)比LR(0)分析表少幾個呢?

         其實我們找到r2對應的歸約產生式為E->id,而發現Follow(E)={ (,+,# },即Follow(E)中不存在id 和 ( ,因此就不需要將id 和 ( 所在列也填上r2,否則使我們發現錯誤不及時,造成效率低額問題。

         同理r3和r4對應歸約產生式為 E -> id(E) 和  E -> E+ id,同樣Follow(E)={ (,+,# }。因此id 和 ( 不需要填r3 和 r4。

      如果還沒明白我們再舉一個例子,有如下文法

      

 

       該文法LR(0)分析表為

      

 

      該文法SLR(1)分析表為

       

 

       r1對應的歸約產生式為S->BB·,r2和r3對應的歸約產生式為B->aB· 和 B->b·

       在狀態5所在的行,LR(0)分析表全部填寫了r1,但是Follow(S)= { # },並沒有a和b,所以在SLR(1)分析表中,只需要在狀態5的#所在列填寫r1。

       同理,我們先計算r2和r3產生式的Follow集,Follow(B)= { a,b,# },三個都有,因此在SLR(1)分析表中和LR(0)分析表中對應狀態行都應該填寫上。

       總結,LR(0)和SLR(0)分析表結構一樣,唯一不同的就是歸約專案所在的狀態行,LR(0)不需要判斷直接全部填寫Rn,而在SLR(1)分析表中需要先判斷Follow集,然後根據Follow集中已有的終結符去填SLR(1)表

         下邊分別給出構造LR(0)和SLR(1)分析表比較系統的描述,可根據上述例子去理解一下:

       

 

        

 

       以上均為個人學習總結,如有錯誤或異議歡迎提出(自下而上分析法未完待續......)。

 

相關文章