編譯原理 自頂向下的遞迴下降語法分析器 解析

2022-08-01 08:15:12 字數 3278 閱讀 2515

任務:給定乙個算術表示式的無關文法,實現乙個語法分析器

分析:根據乙個上下文無關語法生成乙個遞迴下降的語法分析器需要注意幾個方面(思路、步驟):

1.觀察給定語法,如果遇到左遞迴,則需要改寫語法來消除左遞迴

2.根據給定的語法,生成相應符號的first集和fllow集

3.依照first集和fllow集實現語法分析器的**

一、消除左遞迴:

原本為左遞迴的語法會使得語法分析器無限迴圈,無法與給定的輸入串進行匹配。我們可以使用左遞迴將其改寫為右遞迴,該方法是通用的(引入乙個新的非終結符進行過渡):

這時候我們將原來的左遞迴的語法改寫成用右遞迴表示的語法:

二、進行完左遞迴後,我們要求出新生成語法中相應符號的first集和fllow集:

該步的目的是讓語法分析器產生「**」的能力,可以根據當前輸入中給定的字元來當前語法要進行哪個對應的語法規則轉換,從而避免因選擇了錯誤的語法分支而需要進行回溯所帶來的效能影響。

接下來講講什麼是符號的first集和fllow集。

first集是對所有的符號來說的(包括終結符和非終結符),它所表示的含義是從該符號出發,能夠推導出的所有句子(只包含終結符)中,句子開頭可能的非終結符有哪些,這些非終結符組成該符號的first集。

而fllow集則是對所有的終結符(準確的說,是對所有first集中含ε的非終結符來說)來說的,因為其first集中存在ε(即從乙個非終結符出發可以不推出任何語句),所以該非終結符根據相應的語法規則推導得出的句子中,該句子的第乙個符號不一定存在於它的first集合中(此時該非終結符選擇了能推導出ε所在分支的語法,記作語法x),那麼要想讓他具備「**」的能力,我們就需要知道在使用了語法x(能推導出ε的語法規則)後,其後面所跟的非終結符可能有哪些,這些非終結符就是所求的fllow集。

first集求解:

先給出求解first集合的偽**,再講解一下求解first集合的步驟:

1.對於終結符的first集合,我們可以直接得到,就是該終結符本身

2.而對於非終結符的first集合,我們需要遍歷所有的推導,對於一條推導a -> b1 b2 ... bn來說,若b1的first集合不含ε,則first(a)= first(b1),否則first(a)還包含first(b2),以此類推,直到當前所出bi的first集合不含ε為止。這裡需要注意一點只有當bn的first集也含ε時,才將ε加入到a的first集合中

3.直到所有的first集不再變化時(不動點)

通過以上求解,我們可以得到我們所求的語法的first集合,如下表示(將終結符和非終結符區分開來表示):

fllow集的求解:

同樣的,先給出fllow集合的偽**,再對步驟進行講解:

對於bn-1來說,如果first(bn)含ε時,fllow(bn-1) = first(bn)∪fllow(a);如果first(bn)不含ε時,fllow(bn-1) = first(bn),以此類推

3.直到所有的fllow集不再變化時(不動點)

通過以上求解我們得到所有的非終結符的fllow集如下表示:

三、編寫遞迴下降語法分析器

對於每一條語法的推導都有乙個函式實現

有較為詳細的錯誤提示(如:第幾個字元出現錯誤,希望出現xx字元,結果出現了xx字元)

特別注意:什麼時候呼叫其他的解析函式,什麼時候報錯,什麼時候利用first集合和fllow集合進行**,以及左右括號匹配的地方

先給出偽**實現:

再給出我的**實現:

#include #include 

#include

char *str;

size_t ind;

void

expr();

void

eprime();

void

term();

void

tprime();

void

factor();

void parse(char *s);

void error(char *want, char

got)

void

expr()

void

eprime()

else

if (str[ind] == '

\0')

else

return;}

void

term()

void

tprime()

else

if (str[ind] == '

\0' || str[ind] == '

+' || str[ind] == '-'

) else

return;}

void

isnumber()

}void

isname()

}void

factor()

else

if ((str[ind] >= '

a' && str[ind] <= '

z') ||(str[ind] >= '

a' && str[ind] <= 'z'

))

/*factor -> ( expr )

*/if (str[ind] == '('

) ind++;

} else

}void parse(char *s)

return;}

intmain( )

遞迴下降語法分析器

如果您覺得這篇文章幫到了您,或者覺得這篇文章寫的不錯,希望可以點個贊。

多謝您的參與!

編譯原理 自頂向下語法分析

對於任何輸入串,從文法開始符號 根節點 出發,自上而下,從左到右地為輸入串建立語法分析樹。簡而言之,就是尋找輸入串的最左推導的過程。之前我們了解到,一旦有左遞迴存在便無法構建這樣的語法樹,所以用自頂向下語法分析必須先去除左遞迴。同樣,由於使用最左推導,也不能處理有複雜回溯的輸入串。自頂向下的語法分析...

編譯原理 自頂向下分析

從頂部的根節點到底部的葉節點分析方法叫做自頂向下分析。我們知道頂部的根節點可以表示成乙個文法的開始符號s,所以說,自頂向下分析可以看成是從文法的開始符號s推導出詞串w的過程。例如,我們以輸入id id id 為例分析自頂向下的分析方法。首先從開始符號e 也就是樹的根節點 開始推導。首先用第乙個產生式...

編譯原理之自頂向下分析

編譯原理之自頂向下分析 daywolf原創 自頂向下分析演算法通過最左推導中描敘出各個步驟來分析記號串輸入,一般用遞迴下降分析和ll 1 分析。其中ll 1 分析表示從左向右地處理輸入,它為輸入串描述乙個最左推導,只用乙個符號來 分析的方向。現在在一般的程式裡都是使用ll 1 分析方法,我們在這裡就...