關於編譯原理基礎概念可參考http://www.cnblogs.com/bitzhuwei/archive/2012/10/22/SmileWei_Compiler.html
關於下列代碼的基礎數據結構參見http://www.cnblogs.com/bitzhuwei/archive/2012/03/09/compiler_basic_data_structure.html
一、 消除直接左遞歸
設P -> Pα1 | Pα2 | ... | Pαn | β1 | β2 | ... |βm
其中每個α不為ε(ε就是空,什么都沒有的意思,類似null),每個β不以P開頭。
則非終結符P可改寫為
P -> β1P’ | β2P’ | ... | βmP’
P’ -> α1P’ | α2P’ | ... | αnP’ | null
解釋:原來的P展開就是βxαi..αiαj..αj...αt..αt的形式,即某個β開頭,各種阿爾法跟隨的一個串。所以與改寫形式所表達的東西是一樣的。
二、 消除間接左遞歸
給定文法G,若G不含回路(P經過若干步推導又得到P)且不含以ε為右部的產生式。
則其消除左遞歸的算法如下:
- 對G的非終結符按任意順序排列,如A1, A2, A3, ... , An
- for (i = 1; i <= n; i++)
for (j = 1; j <= i - 1; j++)
{
把形如Ai -> Ajγ的產生式改寫成Ai -> δ1γ | δ2γ | ... | δkγ的形式,其中Aj -> δ1 | δ2 | ... | δk是關於Aj的全部規則
消除Ai規則中的直接左遞歸
} - 簡化由上一步得到的文法,即去掉多余的規則
三、 FIRST集
若文法G為二型文法且不含左遞歸,則G的非終結符的每個候選式α的終結首符集FIRST(α)為FIRST(α) = { a | α經過0或多步推導為a...的形式,其中a∈VT }
解讀:FIRST集的含義是:候選式經過推導,最后就是一個終結符的串,推導過程不同,會有多個不同的串(可能是無限個),這些串里的第一個字符組成的集合就是這個候選式的FIRST集。有了這個FIRST集,就可以知道這個候選式是否能匹配接下來要解析的單詞流了。
四、 FOLLOW集
設上下文無關文法(二型文法)G,開始符號為S,對於G中的任意非終結符A,其FOLLOW(A) = { a | S 經過0或多步推導會出現 ...Aa...的形式,其中a∈VT或#號 }
解讀:FOLLOW集的含義是:G的一切句型中,能夠緊跟着非終結符A之后的一切終結符或井號#。#是當出現 ...A 這樣的情況,即A為最后一個字符。
五、 構造FOLLOW集的算法
- 令#∈FOLLOW(S)
- 若文法G中有形如A –> αBβ的規則,且β≠ε,則將FIRST(β)中的一切非終結符加入FOLLOW(B)
- 若文法G中有形如A -> αB或A -> αBβ的規則,且ε∈FIRST(β),則將FOLLOW(A)中的全部元素加入FOLLOW(B)
- 反復使用前兩條規則,直到所有的FOLLOW集都沒有改變。
六、 構造LL(1)分析表的算法
輸入:文法G
輸出:G的LL(1)分析表M(Ax, ay),其中A為非終結符,a為終結符
算法:
- 求出G的FIRST集和FOLLOW集
- for (G的每個產生式 A -> γ1 | γ2 | ... | γm)
{
if (a ∈ FIRST(γi)) 置 M(A, a) 為 “A -> γi”
if (ε ∈ FIRST(γi))
for (每個 a ∈ FOLLOW(A))
置 M(A, a)為 “A -> γi”(實際上此處的γi都是ε)
}
置所有無定義的 M(A, a)為出錯。