【龍書筆記】編譯器前端之語法分析涉及的基本概念

前一篇龍書筆記主要介紹了編譯器內部實現的幾個主要步驟,本篇筆記主要說明編譯器前端涉及到的重要基礎概念。
編譯器前端主要包括詞法分析、語法分析、語義分析及中間碼生成4個階段,一個典型的編譯器前端處理模型如下圖所示:

下面出現的術語或基礎概念均是語法分析階段會涉及到的。

1. syntax & semantics
我們會在很多地方聽到到“語法”和“語義”這兩個概念,那麼,從編譯器角度來看,它們到底是指什麼呢?龍書第2.1節給出瞭如下定義:
The syntax of a programming language describes the proper form of its programs; while the semantics of the language defines what its programs mean, that is, what each program does when it executes.

2. context-free grammar
上下文無關文法(簡稱文法,grammar)是一種約定的標記法(notation),用來描述由編程語言構造的層次語法結構。考慮下面的語法形式:
if(expression) statement else statement
這種語法構造規則可以被表達成下面的形式:
stmt -> if (expr) stmt else stmt
這種規則在術語上稱爲產生式(production);在產生式中,關鍵詞(如if/else)和括號元素被稱爲終結符號(terminal);而expr和stmt這樣的變量用來表示由terminals構成的序列,它們被稱爲非終結符號(nonterminal)。
context-free grammar有4個要素,如下所列:
1) 包含一組終結符號(又稱爲"tokens"),它們通常是由編程語言預留的一些基礎符號(如關鍵字或括號或常數)
2) 包含一組非終結符號(又稱爲"syntactic variables"),每個nonterminal表示一個終結符號串的集合
3) 包含一組產生式,其中每個產生式由3部分組成:一個被稱爲產生式的head或left side的非終結符號;一個箭頭(表示"can have the form");由終結符號或非終結符號構成的序列(被稱爲產生式的body或right side)
4) 指定一個非終結符號作爲start符號
在描述文法時,通常會列出該文法的產生式且首先列出start符號對應的產生式。
若某個非終結符號是某個產生式的頭部,則該產生式是該非終結符號的產生式。
根據文法推導符號串時,通常從開始符號出發,不斷將某個非終結符號替換爲其對應的某個產生式。可以從開始符號推導得到的所有非終結符號串的集合稱爲該文法定義的語言(language)
語法分析的任務是:以終結符號串爲輸入,找出從文法的開始符號推導出這個串的方法。如果找不到對應的方法,則應該拋出語法錯誤。

3. parse tree
語法分析樹以圖形方式展示瞭如何從文法的start符號來推導相應語言中的符號串。
給定一個上下文無關文法,其對應的語法分析樹具有以下性質:
1) 樹的root節點被標記爲start符號
2) 每個葉子節點被標記爲終結符號或空串
3) 樹的內部節點被標記爲非終結符號
4) 如果非終結符號A是某內部節點的標號,且它的子節點的標號從左至右分別爲X1, X2, ..., Xn,則必然存在產生式A->X1X2...Xn,其中X1至Xn既可以是終結符號也可以是非終結符號
假設某個語言的文法規則由如下產生式組成:

則表達式9-5+2的推導過程可由下面的語法分析樹來展示:

一顆語法分析樹的葉子節點從左至右構成了樹的結果,也就是從這顆語法分析樹的根節點上的非終結符號推導得到的符號串。
一個文法的語言的另一個定義是指能夠由某顆語法分析樹生成的符號串的集合
爲一個給定的終結符號串構建一顆語法分析樹的過程稱爲對該符號串進行語法分析

4. 二義性(ambiguity)
對於同一個給定的終結符號串,文法可能會生成不只一顆語法分析樹。這樣的文法稱爲具有二義性(ambiguous)。
例如下面給出的文法規則就具有二義性:
string -> string + string | string - string | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 
因爲根據該文法的產生式,可以對錶達式9-5+2生成兩顆意義不同的語法分析樹,依次表示(9-5)+2和9-(5+2):

我們需
要設計出沒有二義性的文法,或者在使用二義性文法時通過附加的規則來消歧。

5. 運算符的結合性(associativity of operators)
當一個操作數左右兩邊都有運算符時,如9-5-2或9+5+2,必須有規則約定哪個運算符被應用與該操作數。
運算符"+"是左結合的,因爲當操作數兩側都有"+"時,它屬於其左邊的運算符。在大多數程序語言中,加/減/乘/除這4個算術運算符都是左結合的。
但熟悉C語言的話,我們知道有些運算符是右結合的,如賦值運算符"=",也即a=b=c的處理順序與a=(b=c)相同。
下圖分別是9-5-2和a=b=c兩個運算操作的語法分析樹,可以看到前者的分析樹向左樹延伸,而後者的分析樹向右樹延伸,明顯可以看到運算符結合性對語法分析樹的影響。

6. 運算符的優先級
考慮表達式9+5*2。拋開數學常識,在計算機看來,它有兩種可能的解釋:(9+5)*2或9+(5*2)。而運算符結合性規則只適用於同一類運算符多次出現的情況,故此處無法通過結合性規則消歧。因此,當多種運算符同時出現時,需要有規則約定它們的優先級。
與數學中的規定相同,大多數程序語言中,"*"的優先級高於"+",這樣就消除了9+5*2的潛在歧義。
備註:筆記寫到這裏,結合C語言中“先看優先級,優先級相同時再看結合性”的語法約定,我才徹底明白在程序語言中規定運算符的結合性和優先級是編譯器在語法分析時的需求。-_-

7. 部分習題練習
Exercise 2.2.1:  Consider the context-free grammar 
S -> SS+ | SS* | a 
a) Show how the string aa+a* can be generated by this grammar. 
b) Construct a parse tree for this string. 
c) What language does this grammar generate?  Justify your answer. 
解答如下:
a) aa+a*可由下面的鏈式推導生成
S -> SS* -> (SS+)S* -> ((a)S+)S* -> (aa+)S* -> aa+a*
b) 根據a)給出的鏈式推導規則,很容易得出生成aa+a*的語法分析樹如下

c) 該文法生成的語言是由數字(digit)、加號(+)和乘號(*)組成的後綴表達式

【參考資料】
1. <Compilers Principles, Techniques, & Tools>即龍書第2.1-2.2節

============================ EOF ============================


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章