編譯原理系列之四 自頂向下語法分析方法


自頂向下語法分析方法

  • 什么叫確定

    兩個確定:①確定對最左的非終結符進行替換(最左推導)②對於同一個非終結符,確定一個產生式進行推導(SELECT集,無回溯)。

  • 一個上下文無關文法是LL(1)文法的充分必要條件

    關於一個非終結符的各個產生式的可選集互不相交。

  • LL(1)文法的判定過程

    1. 檢查產生式中是否有含有左遞歸或左公因子:

      含有左遞歸或左公因子的文法一定不是LL(1)文法;

      不含有左遞歸或左公因子的文法也不能確定是否為LL(1)文法;

    2. 標記能推導出ε的非終結符:

      先找出能直接推出ε的非終結符,然后再查看其他產生式的右部,通過這些非終結符檢查還有沒有其他非終結符也可推出ε,直到沒有發現為止。

    3. 計算每個產生式的FIRST集:

      ①如果這個產生式右部第一個字符是終結符,那么這個終結符就屬於它的FIRST集。

      ②如果這個產生式右部第一個字符是非終結符,那么這個非終結符的FIRST集就屬於它的FIRST集。

      如果這個非終結符的FIRST集中含ε,那么后面的字符如果是終結符......

      ③如果這個產生式右部可以推出ε,那么ε也屬於它的FIRST集。

    4. 計算每個非終結符的FOLLOW集:

      首先向開始符號的FOLLOW集中添加#,然后對於所有非終結符,不斷的找含有它的產生式右部:

      ①該非終結符后面的字符若是終結符,那么這個終結符就屬於它的FOLLOW集;

      ②該非終結符后面的字符若是非終結符,那么這個非終結符的FIRST()集中的所有元素就屬於它的FOLLOW集;

      如果這個非終結符的FIRST()集中含ε,將ε刪去,同時將這個產生式左部FOLLOW集中的所有元素添加至它的FOLLOW集中;

      注意:不需要考慮后面的字符了,因為已經包含在FIRST()集中了。

    5. 計算每個產生式的SELECT集:

      ①如果這個產生式可以推出ε,那么它的SELECT集是{FIRST(該產生式右部)-ε}∪FOLLOW(該產生式左部的非終結符)

      ②如果這個產生式不能推出ε,那么它的SELECT集是{FIRST(該產生式右部)}

    6. 檢查相同左部產生式的SELECT集的交集:

      檢查相同左部產生式的SELECT集的交集,如果全為空集說明該文法是LL(1)文法,反之則不是。

  • 消除左公因式

    有顯式的左公因式和隱式的左公因式,對於隱式的左公因式要先化成顯式;

    例:

    顯式:

    A→aB|aC

    隱式:

    A→ad|Bc

    B→ae

    解決方法:類似合並同類項,將左公因式提出,不同的部分用或連接,並用一個新的非終結符指向它。

    注意:某些特殊的含左公因式的文法可能會出現循環替換的情況,此時無法解決左公因式問題。

  • 消除左遞歸

    有直接左遞歸和間接左遞歸和一般左遞歸,對於間接左遞歸要先化成直接;

    例子:

    Ⅰ直接(模板):

    P → P α | β
    可改寫為:
    P → βQ(因為P一定是β開頭)
    Q → αQ | ε(Q就是α+)
    其中Q為新增的非終結符

    Ⅱ間接:

    S → PQ | a
    P → QS | b

    Ⅲ一般:
    S → PQ | a
    P → QS | b
    Q → SP | c
    做以下變換:
    ①S → PQ | a
    P → SPS|cS|b

    ②S → SPSQ|cSQ|bQ|a

    ③按照直接左遞歸方法消除后:
    S → cSQR|bQR|aR
    R → PSQR | ε

    ④結果:
    S → cSQR|bQR|aR
    P → SPS|cS|b
    Q → SP | c
    R → PSQR | ε

  • 遞歸下降分析法:

    通過計算的SELECT集判斷編寫子程序:

    例子:

    2869373-69d9e5dcbd9a4639.png

    遞歸下降分析法

    ParseE'函數表示進入E'的產生式,通過switch函數分離相同左部的產生式,然后依次檢查產生式右部字符,如果是終結符,則通過MatchToken函數判斷符合,不符合則出錯;如果是非終結符,則繼續遞歸跳轉至它所對應的Parse函數。

    遞歸下降分析法對應的是最左推導過程
    優點:程序結構和層次清晰明了,易於手工實現;
    對於語義加工,這種方法十分靈活;
    缺點:遞歸調用可能帶來效率問題。

  • 表驅動LL(1)分析方法(預測分析法 )

    例子:

    2869373-8704c0a96798ef11.png

    預測分析法

    首先根據計算出的SELECT集繪制出預測分析表

    2869373-19d1600316fedf55.png

    預測分析表

    然后新建一個分析棧,向空棧中依次壓入#和文法的開始符號E,然后比較剩余輸入串的首字符和分析棧頂元素,如果不同,則先將分析棧頂元素出棧,然后將對應預測分析表中的產生式右部<u>從后向前</u>依次入棧;如果相同,則先將分析棧頂元素出棧,並將剩余輸入串的首字符刪去;然后重復以上過程直到棧為#,剩余輸入串也為#,則表示語法匹配成功。

    2869373-962d52d255fc47ea.png

    分析過程

  • LL(1)分析中的一種錯誤處理辦法

    發現錯誤的情況:
    (1) 棧頂的終結符與當前輸入符不匹配;
    (2) 非終結符A於棧頂,面臨的輸入符為a,但分析表M的M[A,a]為空 (FIRST(A)中沒有a);

    應急”恢復策略:
    對於錯誤(1) 跳過輸入串中的一些符號直至遇到和棧頂的終結符相同的字符為止。

    對於錯誤((2) 跳過輸入串中的一些符號直至遇到“同步符號”為止 。

    同步符號的選擇
    (1) 把FOLLOW(A)中的所有符號作為A的同步符號。跳過輸入串中的一些符號直至遇到這些“同步符號”,把A從棧中彈出,可使分析繼續。(跳過A)
    (2) 把FIRST(A)中的符號加到A的同步符號集,當FIRST(A)中的符號在輸入中出現時,可根據A恢復分析。 (不跳過A)


免責聲明!

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



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