編譯原理學習筆記--語法分析(二)


自頂向上分析方法

1 思想

  簡單來說就是試圖從輸入符號串出發,將其直接作為葉子結點,然后向上構造出一棵分析樹。從樹根到葉子叫展開,而從葉子回樹根就叫歸約。所以這種方法的關鍵在於查找當前句型的可歸約串,然后規約到非終結符,形成一個新串,再重復查找句型,歸約的過程。

2 方法

2.1 優先分析法

  1. 簡單優先分析法
    規范歸約:按照文法符號之間的優先關系確定當前句型的可歸約串。(划重點 意義不大,因為它限制了作用范圍只能是簡單優先文法。
  2. 算符優先分析法
    算符文法:若產生式右邊任意位置都沒有連續兩個或以上的非終結符出現,則稱為算符文法。
    算符優先文法:若算符文法G不含有\(\varepsilon\)產生式,且它的任何兩個構成序對的終結符號之間最多有>、<、=三種優先關系中的一種成立,則稱G是一個算符優先文法。

其實上面這些定義並沒有什么卵用

2.2 LR分析方法

  • 句柄:最左直接短語。也是分析樹最左邊的只有父子兩代的子樹葉結點自左至右排列形成的符號串。
  • LR是一種規范規約,具體過程如下:
    1. 把輸入符號一個一個地移進棧里,直到棧頂的符號串形成一個可歸約串為止。
    2. 把棧頂的這個可歸約串歸約為產生式的左部符號,直到棧頂不再有可歸約串為止。
    3. 重復以上移進-歸約操作,直到歸約到文法的開始符號。

2.3 規范規約

  • 步驟:先找出當前句型的句柄,然后把句柄歸約為相應產生式的左部符號,得到一個新的句型,重復此過程,最終歸約到文法的開始符號。
  • 形式定義:假定\(\alpha\)是文法G的一個句子,如果右句型序列\(\alpha_n,\alpha_{n-1},...,\alpha_1,\alpha_0\)滿足以下兩個條件,則稱該序列是\(\alpha\)的一個規范歸約。
    1.\(\alpha_n = \alpha,\alpha_0 = S\)
    2.對任何\(i(0<i\leq n),\alpha_{n-1}\)是經過把\(\alpha_i\)的句柄替換為相應產生式的左部符號而得到的。
  • 規范歸約的可歸約串可以描述為:若\(\alpha \beta \omega\)是一個規范句型,\(\beta\)是它的句柄,\(\alpha\)是位於\(\beta\)之前的符號串,它是在\(\beta\)之前進行歸約的結果,可含有終結符和非終結符。\(\omega\)是位於\(\beta\)之后的符號串,只能有終結符號。將句柄\(\beta\)歸約到它的父結點A上去。
  • 下面通過一張表作為栗子看一下歸約過程:
    對下列文法,分析符號串\(abbcde\)
    \(S\rightarrow aAcBe, A\rightarrow b|Ab, B\rightarrow d\)

3 LR分析方法

其實這里開始才是重點

3.1 LR模型和工作過程



說白了就是goto和action都是二維數組,兩個角標做索引存儲信息。
\(goto[S_m, A]\)表示當前的\(S_m\)狀態相對於非終結符號A的后繼狀態。
\(action[S_m,a_i]\)和上一節講的分析表意思差不多
(沒看的自行面壁補)存儲的是當前狀態對輸入的終結符采取的分析動作,包括移進,歸約,接受,出錯。
突然亂入一個定義

  • 活前綴:對規范句型的一個前綴,如果它不包含句柄之后的任何符號,則稱該前綴為該句型的一個活前綴。比如上面那個栗子里的一個規范句型aAbcde的句柄Ab,活前綴有\(\varepsilon ,a,aA,aAb\)這什么沙雕定義
  • 描述一下LR分析程序的算法:貼圖警告

    下面再舉個栗子體現如何利用LR分析表來分析識別符號串的動作
    分析輸入符號串id+id*id,文法為:
    \(E\rightarrow E+T|T,T\rightarrow T*F|F,F\rightarrow (E)|id\)



    現在的問題在於如何構造LR分析表 這句話是不是似曾相識呢?看出來了嘛,構造表就是考點,2333 咳咳,規范點說就是為給定的文法構造一個識別它所有活前綴的DFA。不知道DFA的自行移步形式語言與自動機

3.2 LR(0)

  • 引入必要定義 貼圖,算了不警告了,估計習慣了



    忽略定義,算法標號4.10,4.11啥的
    下面敲黑板!構造文法G的LR(0)項目集規范族。

    這里不想舉栗子惹 (突然賣萌,因為后面寫DFA的時候潛移默化地融進這里的思想。瘋狂暗示這里看不懂也沒關系,理解掌握后面的套路就可(誤
    來,看一個DFA的栗子:文法 \(S\rightarrow aA|bB,A\rightarrow cA|d, B\rightarrow cB|d\)

    整明白沒?就是先拓展一個 \(S^{'}\rightarrow S\) 出來,然后給這個推導式箭頭右邊第一個符號前面加個點,表示現在分析的位置,然后點后面如果是非終結符,把非終結符的所有推導式加進來,在箭頭后面加個點。對於新加進來的推導式,繼續觀察點后面是不是非終結符,需不需要再加入新的推導式。如果沒有了,這就是一個項目集\(I_0\),然后對所有推導式點后面的終結符和非終結符。都牽出一條外箭頭到一個新的項目集\(I_i\)里,線上寫的是對應的那個符號,然后把對應的推導式的點往后移一個,作為新的推導式放入\(I_i\),之后再用\(I_0\)拓展的方法,完善\(I_i\)閉包。推導式推到點在末尾的歸約狀態后,該推導式不能引出新的項目集

3.3 SLR(1)

  • 引入原因:LR(0)出來的項目集如果同一個狀態既有移進項(點后面是終結符),又有歸約項(點后面沒符號了),此時就有移進-歸約沖突。若含有兩個不同的歸約項,則為歸約-歸約沖突。SLR(1)是為了解決以上沖突中的部分情況。
  • 解決沖突的思想:

    這個說白了,就是往后看一個,然后簡單判斷一下哪個推導式和這個下一個輸入不矛盾。如果有沖突的兩個推導式左邊的那個非終結符的follow還有交集,那這種情況就沒戲(官方點說,這叫不算SLR(1)文法,這個是證明文法要說的。總的來說,SLR(1)的DFA跟LR(0)看起來沒啥區別,主要是人工判斷一下沖突的follow集,考慮能不能解決沖突。當然不過這樣也解決不了,那就引出下一種,LR(1)。

3.4 LR(1)

  • 引入相關定義

  • 構造DFA
    這個構造DFA和之前LR(0)類似,都是找項目集規范族\(I\)作為不同的狀態。區別在於\(I\)里面的每個推導式后面還包括若干個終結符或$。這里找推導式后面的終結符(向前看符號)的關鍵就是上面推廣定義里提到的對\([A\rightarrow \alpha \cdot B \beta,a]\),由此引出的下面左側是B的推導式,終結符(向前看符號)的來源就是\(FIRST(\beta a)\)

    關於狀態轉移函數,和之前的一樣,只不過區別在於移點的時候,繼承移點前推導式的終結符不變。然后再按照上面的方法構造閉包。這個也叫go轉移函數。

    總的來說,構造LR(1)的項目集規范族標准算法步驟如下:

    下面當然還是栗子栗子~~
    拓廣文法$ G^{'}$,的產生式: \(S^{'}\rightarrow S, S\rightarrow CC,C\rightarrow cC|d\)
  • 構造LR(1)分析表(含action和goto表兩部分)


    比如上面那個栗子的分析表:


    再看個栗子~~
    文法:\(S^{'}\rightarrow S, S\rightarrow L=R|R,L\rightarrow *R|id,R\rightarrow L\)
    先自個兒寫去
    寫完了么?圖畫完了?表寫完了?
    真的完了?


3.5 LALR(1)

這個提出是因為點什么呢?主要是因為嫌SLR(1)能力太弱,LR(1)又太長,需要一個身材短小,功能強悍的LALR(1)幫我們解決日常問題。

  • 引入定義:

    1. 同心集:如果兩個LR(1)項目集去掉向前看符號后是相同的,就是同心集。(相當於屬於LR(0)中的同一個項目集)
    2. 項目集的核:除了\(I_0\)以外,項目集中圓點不在最左邊的項目組成的集合。\(I_0\)中的核有且只有\([S^{'}\rightarrow \cdot S,\)$]
  • 構造思想

    1. 合並LR(1)項目集規范族中的同心集,以減少分析表的狀態數。
    2. 用項目集的核代替項目集,減少項目集需要的存儲空間。
  • 構造方法

    值得注意的是,從LR(1)改成LALR(1),不會新產生移進-歸約沖突,但是可能會增加歸約-歸約沖突。 下面還用之前那個栗子來感受一下。先回顧一下文法. 我知道你懶得往上翻 \(S^{'}\rightarrow S, S\rightarrow CC,C\rightarrow cC|d\)
    得到的LALR(1)分析表

    ps:36就是以前表里第三行和第六行合並了一下。
    對於正確的輸入符號串,LR(1)和LALR(1)的分析步驟沒有差別,但是對於錯誤符號,LR(1)可以立即報告錯誤,而LALR(1)可能需要做更多的歸約動作才報告錯誤,但不會移進更多的輸入符號。自己拿上面栗子找輸入串試試吧,我是不會幫你們用實例再分析的,hhhh

4 總結一下LR

  • 首先,所有的LR文法都是非二義性文法。上面說的那一大堆具有的包含關系如下:
  • 如果遇到了一些有二義性的文法,可以利用以下方法來解決沖突
    1.利用優先級和結合規則解決表達式沖突
    看個栗子,文法:\(E^{'}\rightarrow, E\rightarrow E+E|E*E|(E)|id\)

    \(I_7,I_8\)具有移進-歸約沖突。查看E的follow集,發現SLR(1)解決不了。這時候分析不用優先率和結合規則情形下的動作:

    規定*優先且左結合,所以原表只去第一種情況的7,8行。
    2.利用最近最后匹配原則解決if語句沖突
    看個栗子,文法:\(S\rightarrow if\ E\ then\ S\ else\ S|if\ E\ then\ S|others\)可以先用i表示if E then,用e表示else,用a表示others。所以可以寫出\(S\rightarrow iS|iSeS|a\)

    顯然\(I_4\)具有移進-歸約沖突,但當一個iS后面出現e,認為這個e和前面離它最近的i匹配,所以\(I_4\)對e使用移進,對$使用歸約。得到的分析表:
  • LR分析的錯誤處理與恢復

    跟上一節講的策略也差不多,就是把分析表沒填的狀態分析一下錯誤類型,填上錯誤處理即可。看一下剛剛那個栗子:



    下面來看對於id+)的識別過程:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM