構造AST在compiler中是否是一個必不可少的步驟? 由於attribute grammar和one-pass compiler或者說語法制導翻譯(syntax-directed translation,SDT)聯繫很緊密,所以attribute grammar和這個問題有存在間接的的關係。
答案是否,構造AST並不是一個必要的步驟(可以思考構造AST的目的是什麼),甚至在計算機資源極其有限的幾十年前,one-pass compiler還是一個比較普遍的做法。
什麼是Attribute Grammars
Attribute Grammars were invented by Donald Knuth as a way to unify all of the stages of compiling into one. They give a formal way to pass semantic information (types, values, etc.) around a parse tree.
注:再次跪拜Don Knuth大神
首先attribute grammars被提出來用於將多個編譯步驟整合成一個,例如你要編譯數學表達式,此時就沒有必要生成AST併爲其生成binary了。“attribute grammar + 語法制導”就直接將該表達式的值evaluate出來了。針對應用場景使用合適的方法纔是王道,否則就可能像見識少的我一樣,把特例當做通用解法使用。
Attribute Grammar爲何能夠做到將不同的編譯階段整合成一個呢?Attribute Grammar就是在Grammar上擴充了Attriute的文法,核心就在於這些Attribute。Attribute可以是類型,可以中間代碼,也可以是表達式的值。我們就可以根據這些attributed的文法,將類型檢查,中間代碼生成以及表達式求值融合到parser中。甚至於一些constant folding,available expressioin也可以融合進去。
Attribute Grammar的核心在於這些Attribute,而關於Attribute如何定義以及如何evaluate則視問題不同而不同。
下面我摘PLP中的一個AG的示例,簡單的複述一下整個過程。
上面的文法用於生成一個數學運算式,我們可以在其上附着一些attribute,如下所示:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
使用開頭的可以稱爲Semantic Functions(與PLP稍有不同),針對上述AG,這些semantic functions只是簡單的數學運算,它們的參數都來源於當前文法內部。
至此我們就可以知道Attriubte Grammar的作用是將***文法和一些有意義的sematic function粘和起來***。這些semantic function都是人爲賦予的,我們同樣可以在代碼生成階段,將sematic function替換爲一段段machine code。
Evaluate Attributes
PLP將evaluate attribute稱作Parse Tree的annotation(或decoration),一個簡單的示例如下圖所示:
注:圖片摘自PLP 4.3
從上圖中我們可以看到整個evaluate的過程就是後序遞歸下降,最終我們得到結果8,這個Parse Tree不是必須的,並且運算符號的優先級已經在parse的時候隱含的處理了。
Synthesized Attributes
對於數學運算式對應的Attribute Grammar,我們可以稱之爲Synthesized Attributes。
Syntathesized Attributes: their values are calculated (synthesized) only in productions in which their symbol appears on the left-hand side. This means that the attribute flow - the pattern in which information moves from node to node - is entirely bottom-up.
Synthesized Attributes又稱之爲S-attribute,龍書翻譯成綜合屬性和S屬性,注意終結符的屬性值來源於scanner。
Inherited Attributes
與Synthesized Attributes對應的另外一種屬性是Inherited Attributes,又稱之爲L屬性(L意思是Left-To-Right)。
In general, we can imagine attributes whose values are calculated when their symbol is on the right-hand side of the current production. Such attributes are said to be inherited.
Inherited Attributes能夠將context information從上至下,從左至右進行傳遞,一個比較典型的Inherited Attributes應用是Symbol Table和Type check。
下面給出一個既含有S屬性又包含L屬性的例子,這個例子主要用於automatic type evaluation,表示symbol table。
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
Attribute:
其中屬性是S屬性,而屬性是L屬性。對於下面的兩行代碼,對應的decorated tree如下所示。
int a, b, c;
string s;
從上圖中我們可以清楚的看到,L屬性沿着和傳遞,S屬性沿着的方式傳遞。
在parse的時候這裏有一個準則,
When we get to a node during parsing,
- we must have all of the information we need to evaluate its inherited attributes.
- Before we leave the node we must have all of the information we need to evaluate its synthesized attributes.
讀書的重點在於思考和理解,否則無疑是向腦子裏傾倒垃圾。