1. 程式人生 > >編譯器實現之旅——第五章 實現語法分析器前的準備

編譯器實現之旅——第五章 實現語法分析器前的準備

在前面的旅程中,我們已經實現了詞法分析器。詞法分析器可將原始碼轉變為記號流,以供語法分析器使用。所以現在就讓我們啟程,朝著下一站——語法分析器出發吧。 ## 1. 什麼是語法 什麼是語法呢?提到詞法分析器,我們能夠立即聯想到一個個看得見摸得著的詞;而提到語法分析器,又能聯想到什麼呢? 詞法和語法的關係,就像撲克牌和打撲克牌的遊戲規則之間的關係一樣:詞法決定了撲克牌的印刷、張數、花色、點數等等,而語法則決定了我們應該如何打出這些撲克牌,以贏得遊戲勝利;這在程式語言中就是:詞法決定了程式碼中能夠出現哪些詞,而語法決定了這些詞的正確組織方式。由此可見:詞法是具象的,而語法是抽象的。 ## 2. 怎麼表示語法 正如我們通過一段文字描述某種遊戲規則一樣,我們也可以用一段文字來描述一個語法。請看: ``` 句子的語法: 1. 一個句子,由主語 + 謂語 + 賓語構成 2. 主語是:“程式設計師” 3. 謂語是:“沒有” 4. 賓語是:“頭髮” ``` 顯然,這就是一套語法了。但是我們發現,這種白話文式的語法表示顯然不是我們編譯器一貫的作風嘛。針對這個問題,巴科斯及後人提出了用於表示語法的“巴科斯正規化”和“拓展的巴科斯正規化”。拓展的巴科斯正規化的主要規則如下: ``` 1. 用“::=”符號表示“是”,或“由...構成”的意思。說的專業點,叫“推匯出” 2. 用“|”符號表示“或”的意思 3. 用“[ ... ]”表示中括號中的內容是可選的 4. 用“{ ... }”表示大括號中的內容可以出現0至無窮多次的重複 ``` 現在,就讓我們用“拓展的巴科斯正規化”來描述我們的語法吧: ``` 句子的語法: 1. 句子 ::= 主語 謂語 賓語 2. 主語 ::= “程式設計師” 3. 謂語 ::= “沒有” 4. 賓語 ::= “頭髮” ``` ## 3. 語法怎麼使用 我們已經有了語法,現在的問題是,語法有什麼用處?又該怎麼發揮這些用處呢? 首先,我們顯然可以做這樣一件事:判定一個記號流是否符合語法。請看: 假如我們有記號流:(“程式設計師”,“有很多”,“頭髮”),我們從語法的第一條開始,我們知道:“句子 ::= 主語 謂語 賓語”,這句話代表什麼呢?其代表著:我們需要先看到一個主語,再看到一個謂語,最後看到一個賓語,如果都看到了,我們就認為這段記號流是符合語法的,否則,只要有任何一個地方不符合要求,我們就認為這段記號流是不符合語法的。那麼問題來了,主語、謂語、賓語又分別是什麼?顯然,接下來的三條語法告訴了我們答案。我們首先檢查主語,根據語法規則,我們知道:主語必須是“程式設計師”,記號流呢?嗯,第一個記號確實是“程式設計師”,我們接著往下看;語法規則又告訴我們:謂語必須是“沒有”,而此時的記號是“有很多”,這就不對了,後面也不用看了,我們可以立即判定:(“程式設計師”,“有很多”,“頭髮”)這個記號流是不符合語法的。 接下來的故事就不用我多說了吧。經過一番小小的努力,我們終於發現:只有:(“程式設計師”,“沒有”,“頭髮”)這個記號流是符合語法的。 確實符合語法了,然後呢?正如前面的章節所說,此時,我們就可以將這段記號流立體化,變為一棵抽象語法樹了。這棵抽象語法樹長什麼樣呢。請看: ``` 句子 / | \ 程式設計師(主語) 沒有(謂語) 頭髮(賓語) ``` 一句話解釋:語法長什麼樣,語法樹就長什麼樣。 將上面的模型變為程式碼,我們就得到了抽象語法樹節點的定義。請看: ``` Cpp struct AST { // Attribute TOKEN_TYPE tokenType; string tokenStr;