文法分析演算法LR(1)基礎教程(上)

來源:互聯網
上載者:User
文章目錄
  • 基本概念
  • 定義文法——BNF/EBNF
  • LL與LR
  • LR(1)狀態機器構建
  • 下篇:LR(1)分析過程
討厭英文的同學,請點我碰碰運氣
不小心亂玩後悔了的話,請再碰碰運氣,說不定會恢複

基本概念

首先解釋一下基本概念

詞法分析和文法分析:編譯或者解釋一門語言,必經兩個步驟:詞法分析和文法分析,詞法分析就是把原始碼的字元流變成電腦可理解的詞彙:token,文法分析就是把token流變成一顆結構化的文法樹,以便後面的程式去翻譯或者分析。比如,假如電腦要想識別整數四則運算,詞法分析器那麼就要認識整數、加減乘除四種運算子號,以及左右括弧這些token,而要根據運算子的結合性,把四則運算構建成文法樹。

3 + 4 × 5

最後得到的文法樹可能長成下面的樣子

AdditiveExpression:

    AdditiveExpression

        MultipleExpression:

            Integer:3

        "+"

    MultipleExpression:

        Integer:4

        "×"

        Integer:5

上面這顆樹用程式設計語言中的資料結構表達出來,就是比較容易被電腦理解和處理的了。(設計模式中的Interpreter模式,就是省略前面的步驟直接使用這種結構的)

詞法分析可以簡單地用Regex來完成,而本文則專門講一種文法分析的經典演算法:LR(1)

定義文法——BNF/EBNF

上面我們提到了四則運算,不過我們沒有嚴格定義四則運算到底是什麼,比如,有沒有括弧、用×還是*來表示乘法,但是電腦語言需要一個非常嚴謹的定義方式,現在廣泛使用的就是BNF及其擴充EBNF。如我們以以下方式定義帶括弧四則運算:

<Expression> ::= <AdditiveExpression>

<AdditiveExpression> ::=  <AdditiveExpression> "+" <MultipleExpression> | <AdditiveExpression> "-" <MultipleExpression> | <MultipleExpression>

<MultipleExpression> ::= <MultipleExpression> "×" <PrimaryExpression> | <MultipleExpression> "/" <PrimaryExpression> | <PrimaryExpression>

<PrimaryExpression> ::= "(" <Expression> ")" | <Integer>

其中Expression、AdditiveExpression、MultipleExpression 、PrimaryExpression、Integer我們成為Symbol(文法符號,C++連結器報的錯誤裡面說找不到符號,就是這個詞),詞法分析產生的每一個token也同時是Symbol,如果一個Symbol可以由若干個Symbol組成,那麼稱為non-terminal symbol(非終結符),否則稱terminal symbol,最後產生文法樹以後,terminal symbol就是所有的葉子節點。

因為對BNF的一些批評,後來出現了EBNF,它是BNF的擴充版本。

Expression ::= AdditiveExpression

AdditiveExpression ::=  AdditiveExpression "+" MultipleExpression , AdditiveExpression "-" MultipleExpression , MultipleExpression

MultipleExpression ::= MultipleExpression "×" PrimaryExpression , MultipleExpression "÷" PrimaryExpression , PrimaryExpression

PrimaryExpression ::= "(" Expression ")" , Integer

在現在大多數語言規範中,都在使用BNF/EBNF或者與之非常接近的方法來描述文法。

 

LL與LR

常見的文法分析演算法有LL和LR。

LL的第一個L表示from Left to right,第二個L表示Left most推導。

LR的第一個L和LL的第一個L含義相同,第二個R表示Right most推導。

在通常的描述中,後面還有一個括弧裡面的數字如,LL(0)、LL(1)、LL(4)、LR(0)、LR(1)這樣,括弧裡面的數字表示用於決策所需的後續token數。

 

 

 

LR(1)狀態機器構建

LR(1)分析從外部看起來,每次接受一個字元,最後接收程式終結符時,內部剛好形成一顆文法樹。

在內部,每當我們接受一個Symbol之後,我們就需要做一個決定:是把新的Symbol跟原有的Symbol立即合并成更進階的Symbol,還是把新的Symbol暫放呢?

在LR(1)中,有兩個術語:reduce(歸約)和shift(移入)。規約就是把已經讀入的低級Symbol組合成進階Symbol,如Integer "x" Integer 可以reduce成MultipleExpression

仍然以上面的 3 + 4 × 5 為例,當我們接受3的時候,實際上3已經是一個完整的PrimaryExpression了,而進一步,一個PrimaryExpression也是MultipleExpression,我們可以一直將3歸約到一個完整的Expression

Expression:

    AdditiveExpression:

        MultipleExpression:

            PrimaryExpression:

                Integer:3

但是我們此時不應當直接reduce到Expression,這是一個錯誤的文法樹結構。

 

考慮以下兩種情況:

3 + 4 × 5,在讀入×時,

此時 3 + 4顯然不應該被reduce

3 × 4 + 5,在讀入+時

此時 3 × 4 顯然應該被reduce

 

為了正確處理以上的問題,我們要構建一個狀態機器來指導LR(1)運算,何時reduce,何時shift。

由BNF我們可以得到的文法規則,以四則運算為例,最終我們想要的是一個Expression,即我們的文法樹最終根節點是Expression 。

我們認為輸入的token序列最終會形成一個Expression,那麼考慮一個問題,狀態機器的最初狀態0,能夠接受哪些Symbol的shift呢?

第一個考慮到,狀態機器可以接受Expression這個Symbol,這將導致我們進入最終狀態。

此外因為有文法規則Expression ::= AdditiveExpression,狀態0還能接受AdditiveExpression

又因為有文法規則AdditiveExpression ::=  MultipleExpression "+" MultipleExpression , MultipleExpression "-" MultipleExpression , MultipleExpression

狀態0還能接受MultipleExpression

……

 

從這個分析過程不難看出,狀態0可以接受所有Expression,以及所有能產生Expression的規則的第一個Symbol,並按此規則遞迴。

 

接下來我們要關心遷移問題了,毫無疑問接受Expression這個Symbol以後,直接進入最終狀態,然而對於其它情況,如接受了一個MultipleExpression ,則會產生更多可能性,我們把AdditiveExpression ::=  MultipleExpression "+" MultipleExpression , MultipleExpression "-" MultipleExpression , MultipleExpression看成3條規則,把所有的規則全都拆分出來.

每一條規則都將會形成若干狀態遷移規則:

Expression ::= AdditiveExpression

● AdditiveExpression

AdditiveExpression ●

AdditiveExpression ::=  AdditiveExpression "+" MultipleExpression , AdditiveExpression "-" MultipleExpression , MultipleExpression

● AdditiveExpression "+" MultipleExpression

AdditiveExpression ● "+" MultipleExpression

AdditiveExpression "+" ● MultipleExpression

AdditiveExpression "+" MultipleExpression ●

● AdditiveExpression "-" MultipleExpression

AdditiveExpression ● "-" MultipleExpression

AdditiveExpression "-" ● MultipleExpression

AdditiveExpression "-" MultipleExpression ●

● MultipleExpression

MultipleExpression ●

MultipleExpression ::= MultipleExpression "×" PrimaryExpression , MultipleExpression "÷" PrimaryExpression , PrimaryExpression

● MultipleExpression "×" PrimaryExpression

MultipleExpression ● "×" PrimaryExpression

MultipleExpression "×" ● PrimaryExpression

MultipleExpression "×" PrimaryExpression ●

● MultipleExpression "÷" PrimaryExpression

MultipleExpression ● "÷" PrimaryExpression

MultipleExpression "÷" ● PrimaryExpression

MultipleExpression "÷" PrimaryExpression ●

● PrimaryExpression

PrimaryExpression ●

PrimaryExpression ::= "(" Expression ")" , Integer

● "(" Expression ")"

"(" ● Expression ")"

"(" Expression ● ")"

"(" Expression ")" ●

● Integer

Integer ●

顯而易見,我們可以總結出下面兩條規律:

  1. 所有以●結尾的狀態,操作就是規約,其它狀態操作就是移入。
  2. 假如目前狀態● 後是一個非終結符X,那麼所有能規約到X的規則中以●開頭的狀態遷移規則也適用於目前狀態。

我們現在可以認為● Expression是初始狀態,Expression ● 是結束狀態,根據規則2,可以得到以下狀態遷移適用目前狀態

● AdditiveExpression

● AdditiveExpression "+" MultipleExpression

● AdditiveExpression "-" MultipleExpression

● MultipleExpression

● MultipleExpression "×" PrimaryExpression

● MultipleExpression "÷" PrimaryExpression

● PrimaryExpression

● "(" Expression ")"

● Integer

同樣開頭的遷移規則必須歸併,實際上,第一次我們可以得到JSON表示的以下狀態結構(我們用JS程式員最喜歡的$表示reduce)

{

    AdditiveExpression:{

        "+": {MultipleExpression:{$:"AdditiveExpression"}},

        "-": {MultipleExpression:{$:"AdditiveExpression"}},

        $: "Expression"

    },

    MultipleExpression:{

        "×": {PrimaryExpression:{$:"MultipleExpression"}},

        "÷": {PrimaryExpression:{$:"MultipleExpression"}},

        $: "AdditiveExpression"

    },

    PrimaryExpression:{$:"MultipleExpression"},

    "(":{Expression:{")":{$:"PrimaryExpression"}}},

    Integer:{$:"PrimaryExpression"}

}

注意,這個狀態遷移關係僅僅是分析了第一個狀態後的結果,要想得到完整地狀態機器,還要對所有後續狀態遞迴地應用規則2。這將可能產生無限迴圈,為了避免這種情況,我們應該對每個狀態做hash,然後把迴圈的情況直接指向之前的結果。

做完這些之後,我們就得到了一個完整的LR(1)狀態機器。

 

下篇:LR(1)分析過程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.