1. 程式人生 > >【編譯原理龍書筆記】(二)一個簡單的語法制導翻譯器(仍未完成)

【編譯原理龍書筆記】(二)一個簡單的語法制導翻譯器(仍未完成)

這篇部落格是根據自己學習龍書,因為博主習慣了英語環境,在強行從英語轉化為中文的時候難免會有些不自然,請大家諒解。

感謝沉魚姐姐,很多答案都是參考了她的github,雖然無緣認識,但也算是一位領路人。

正文:

一個簡單的語法制導翻譯器

在本章中,要先給一個編譯器大致的idea,而並非一開始就專注於各種編譯器過程的細節。

2.1 引言

首先快速瀏覽一下編譯器的前端模型,也就是詞法分析,語法分析,中間程式碼生成的這三個過程。

源程式通過詞法分析器得到了一個個的詞法單元,之後這些詞法單元通過語法分析器被構造成了一棵抽象語法樹。之後傳給中間程式碼生成器,會產生樹或者三地址形式的中間形式表達。

2.2 語法定義

我們要介紹的是一種用於描述程式設計語言語法的表示方法——“上下文無關文法”。

2.2.1 文法定義

一個上下文無關文法(context free grammar)由以下四個元素組成:

  1. 一個終結符號集合。終結符號是該文法所定義的語言的基本符號的集合。
  2. 一個非終結符號集合。每個非終結符號表示一個終結符號串的集合。
  3. 一個產生式集合。產生式的結構包括產生式頭(左部)的非終結符號,一個箭頭,和一個產生式體(右部)的由終結符號和非終結符號組成的序列。產生式主要用來描述一種非終結符號的結構體是如何被定義的,即一種非終結符號的書寫方式。
  4. 指定一個非終結符號作為開始符號。

為了更好地理解上下文無關文法,我們在此舉一個例子。

終結符號為 + - 0 1 2 3 4 5 6 7 8 9
list list + digit
list list - digit
list digit
digit 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

上述例子說明了digit是一個非終結符號,這個非終結符號digit是由數字0123456789之中的某些單一數字組成的。而list這個非終結符號,是由另一個list 加上(注意這裡只是表示式的加減,不會做出結果)digit 形成。根據開始符號的定義,我們還能看到這段文法的開始符號就是 list 。

零個終結符號組成的串成為空串,記為 ϵ.

2.2.2 推導

我們可以根據文法推導所有可能生成的符號串,比如可以從開始符號出發,不斷將某個非終結符號替換為該非終結符號產生式的體(右部),這樣可以推導得到所有終結符號串能得到的集合。這一套集合被稱為語言。

在語法分析(parsing)的過程中,我們的目的是接受一個終結符號串作為輸入,找出從文法的開始符號推匯出這個串的方法。如果不能從文法的開始符號推導得到該輸入的終結符號串,則報告語法錯誤。

用上面例子中的一套定義的話,我們如果輸入 9 - 5 + 2,編譯器就會開始推導說這個表示式是一個list,所以沒有語法錯誤。這個即是推導的作用。

2.2.3 語法分析樹

在上述從文法的開始符號推導到給定的終結符號串的過程中,我們可以用樹狀結構來簡化(更好的描述)我們的推導過程。我們把具有以下性質的樹稱作是語法分析樹:

  1. 根節點的標號是文法的開始符號
  2. 每個葉子節點的編號為一個終結符號或者是空串ϵ
  3. 每個內部節點的標號都是一個非終結符號。
  4. 如果非終結符號A是某個內部節點的標號,並且它的子節點的標號從左到右分別為X1,X2,,Xn 則必然有一個產生式是 AX1,X2Xn

2.2.4 二義性

我們最終的目的總是要對編譯器進行優化的,語法分析的優化也是其中重要的一部分。然而有一些優化並不是正確的。例如在上面digit list 的例子中,可能我們會想把它變成如下的形式:

strstr+str|strstr|0|1|2|3|4|5|6|7|8|9

看上去是一段不錯的優化,然而很大的問題就是這裡的語法表示式中存在著ambiguity,也就是二義性。

二義性意味著對於同樣一段程式碼,我們可以用兩棵語法樹去表達他,並且得到不同的結果。比如說對於 9 - 5 + 2。

這裡寫圖片描述

上面的兩棵樹都符合我們對於語法結構的要求,但是卻得到了不同的結果,這種後果是毀滅性的。

2.2.5 運算子的結合性

出現上述問題的原因是因為運算子應該具有結合性。依照生活中的慣例,(9-5)+2 和 9-5+2 是等價的,其原因就是運算子“+”是左結合的。在絕大多數程式設計語言中,加減乘除四種算術運算子都是左結合的。

右結合的例子有賦值號 = ,比如說 a = b = c 等價於 a = (b = c) 而非 (a = b) = c。在這兩種關係所對應的語法分析樹中,左結合運算子往往往左下延伸,而右結合運算子是往右下延伸。

2.2.6 運算子的優先順序

優先順序這種東西嘛,是個人類就知道該怎麼做了。那麼我們要考慮的是在編譯器的學習中,如何根據優先順序以及結合律的表格,構建出一套文法的生成式子。

這裡我們就拿四則運算的表示式做一個例子好了。

首先記住,有多少個優先順序,我們最好就建立多少個對應這些優先順序的非終結符號。在四則運算中,我們用expr和term來對應加減和乘除的終結符號,並且用factor來表達基本單元。在正常計算中,表示式的基本單元應該為 factordigit|(expr)

剩下的應當為

termterm×factor|term/factor|factor exprexpr+term|exprterm|term

注意在上述的表示式中,term 由 factor 組成,expr 再由 term 組成,這樣就確保了優先順序不會出現問題。

2.3 語法制導翻譯

照例自我介紹: