语法分析:自上而下分析
概述
本節(jié)將介紹編譯程序構(gòu)造中的一些典型的語法分析方法。語法分析器的功能,自上而下分析面臨的問題,LL(1)分析法
語法分析器的功能
語法分析是編譯過程的核心部分。它的任務(wù)是在詞法分析識(shí)別出單詞符號(hào)串的基礎(chǔ)上,分析并判定程序的語法結(jié)構(gòu)是否符合語法規(guī)則。
語言的語法結(jié)構(gòu)是用上下文無關(guān)文法描述的。因此,語法分析器的工作本質(zhì)上就是按文法的產(chǎn)生式,識(shí)別輸入符號(hào)串是否為一個(gè)句子。這里所說的輸入串是指由單詞符號(hào)(文法的終結(jié)符)組成的有限序列。對(duì)一個(gè)文法,當(dāng)給你一串(終結(jié))符號(hào)時(shí),怎樣知道它是不是該文法的一個(gè)句子(“程序”)呢?這就要判斷,看是否能從文法的開始符號(hào)出發(fā)推導(dǎo)出這個(gè)輸入串,或者,從概念上講,就是要建立一棵與輸入串相匹配的語法分析樹。
按照語法分析樹的建立方法,我們可以粗略地把語法分析辦法分成兩類,一類是自上而下分析法,另一類是自下而上分析法。本章主要介紹自上而下分析法,下一章我們將介紹自下而上分析法。
自上而下分析面臨的問題
現(xiàn)在來討論自上而下的語法分析方法。顧名思義,自上而下就是從文法的開始符號(hào)出發(fā),向下推導(dǎo),推出句子。我們首先將簡單地介紹自上而下分析的一般方法。這種方法是帶“回溯”的。下一節(jié),將著重討論一種廣為使用的不帶回溯的遞歸子程序(遞歸下降)分析方法。
自上而下分析的主旨是,對(duì)任何輸入串,試圖用一切可能的辦法,從文法開始符號(hào)(根結(jié))出發(fā),自上而下地為輸入串建立一棵語法樹。或者說,為輸入串尋找一個(gè)最左推導(dǎo)。這種分析過程本質(zhì)上是一種試探過程,是反復(fù)使用不同產(chǎn)生式謀求匹配輸入串的過程。
實(shí)現(xiàn)這種自上而下的帶回溯試探法的一個(gè)簡單途徑是讓每個(gè)非終結(jié)符對(duì)應(yīng)一個(gè)遞歸子程序。每個(gè)這種子程序可作為一個(gè)布爾過程。一旦發(fā)現(xiàn)它的某個(gè)候選與輸入串相匹配,就用這個(gè)候選去擴(kuò)展語法樹,并返回“真”值;否則,保持原來的語法樹和IP值不變,并返回“假”值。
上述這種自上而下分析法存在許多困難和缺點(diǎn)。
首先是文法的左遞歸性問題。一個(gè)文法是含有左遞歸的,如果存在非終結(jié)符P
PPa
含有左遞歸的文法將使上述的自上而下的分析過程陷入無限循環(huán)。即,當(dāng)試圖用P去匹配輸入串時(shí),我們會(huì)發(fā)現(xiàn),在沒有識(shí)別任何輸入符號(hào)的情況下,又得重新要求P去進(jìn)行新的匹配。因此,使用自上而下分析法必須消除文法的左遞歸性。
其次,由于回溯,就碰到一大堆麻煩事情。如果我們走了一大段錯(cuò)路,最后必須回頭,那么,就應(yīng)把已經(jīng)做的一大堆語義工作(指中間代碼產(chǎn)生工作和各種表格的簿記工作)推倒重來。這些事情既麻煩又費(fèi)時(shí)間,所以,最好應(yīng)設(shè)法消除回溯。
第三,在上述的自上而下分析過程中,當(dāng)一個(gè)非終結(jié)符用某一候選匹配成功時(shí),這種成功可能僅是暫時(shí)的。例如,就文法(4.1)而言,考慮輸入串x**y。若對(duì)A首先使用第二個(gè)候選式,A將成功地把它的唯一子結(jié)匹配于輸入串的第二個(gè)符號(hào)。但S的第三個(gè)子結(jié)y與第三個(gè)輸入符號(hào)不匹配。因而,導(dǎo)致了無法識(shí)別輸入串x**y是一個(gè)句子的事實(shí)。然而,若A首先使用它的第一個(gè)候選**,則整個(gè)輸入串即可獲得成功分析。這意味著,A首先使用第二個(gè)候選所得的成功匹配是虛假的。由于這種虛假現(xiàn)象,我們需要更復(fù)雜的回溯技術(shù)。一般說,要消除虛假匹配是很困難的。但若從最長的候選開始匹配,虛假匹配的現(xiàn)象就會(huì)減少一些。
第四,當(dāng)最終報(bào)告分析不成功時(shí),我們難于知道輸入串中出錯(cuò)的確切位置。
最后,由于帶回溯的自上而下分析實(shí)際上采用了一種窮盡一切可能的試探法,因此,效率很低,代價(jià)極高。嚴(yán)重的低效使得這種分析法只有理論意義,而在實(shí)踐上價(jià)值不大。
后面,我們將集中討論不帶回溯的自上而下分析法。
LL(1)分析法
左遞歸消除
直接消除見諸于產(chǎn)生式中的左遞歸是比較容易的。假定關(guān)于非終結(jié)符P的規(guī)則為
其中b不以P開頭。那么,我們可以把P的規(guī)則改寫為如下的非直接左遞歸形式:
P¢→aP¢|e ,e為空字)
這種形式和原來的形式是等價(jià)的,也就是說,從P推出的符號(hào)串是相同的。
如何消除一個(gè)文法的一切左遞歸呢?雖然困難不少,但仍有可能。如果一個(gè)文法不含回路(形如PP的推導(dǎo)),也不含以e為右部的產(chǎn)生式,那么,執(zhí)行下述算法將保證消除左遞歸(但改寫后的文法可能含有以e為右部的產(chǎn)生式)。
消除左遞歸算法:
BEGIN
FOR j:=1 TO i-1 DO
把形如Pi→Pjg的規(guī)則改寫成
Pi→d1g|d2g|…|dkg。其中Pj→d1|d2|…|dk是關(guān)于Pj的所有規(guī)則;
消除關(guān)于Pi規(guī)則的直接左遞歸性
END
消除回溯、提左因子
欲構(gòu)造行之有效的自上而下分析器,必須消除回溯。為了消除回溯就必須保證:對(duì)文法的任何非終結(jié)符,當(dāng)要它去匹配輸入串時(shí),能夠根據(jù)它所面臨的輸入符號(hào)準(zhǔn)確地指派它的一個(gè)候選去執(zhí)行任務(wù),并且此候選的工作結(jié)果應(yīng)是確信無疑的。也就是說,若此候選獲得成功匹配,那么,這種匹配決不會(huì)是虛假的;若此候選無法完成匹配任務(wù),則任何其它候選也肯定無法完成。換句話說,假定現(xiàn)在輪到非終結(jié)符A去執(zhí)行匹配(或稱識(shí)別)任務(wù),A共有n個(gè)候選a1,a2,…,an,即A→a1 | a2 | … |an。A所面臨的第一個(gè)輸入符號(hào)為a,如果A能夠根據(jù)不同的輸入符號(hào)指派相應(yīng)的候選ai作為全權(quán)代表去執(zhí)行任務(wù),那就肯定無需回溯了。在這里A已不再是讓某個(gè)候選去試探性地執(zhí)行任務(wù),而是根據(jù)所面臨的輸入符號(hào)a準(zhǔn)確地指派唯一的一個(gè)候選。其次,被指派候選的工作成敗完全代表了A。
前面已經(jīng)說過,欲實(shí)行自上而下分析,文法不得含有左遞歸。令G是一個(gè)不含左遞歸的文法,對(duì)G的所有非終結(jié)符的每個(gè)候選a定義它的終結(jié)首符集FIRST(a)為:
FIRST(a)={a | aa…, a?V T}特別是,若ae,則規(guī)定e?FIRST(a)。換句話說,FIRST(a)是a的所有可能推導(dǎo)的開頭終結(jié)符或可能的e。如果非終結(jié)符A的所有候選首符集兩兩不相交,即A的任何兩個(gè)不同候選a i和a j
那么,當(dāng)要求A匹配輸入串時(shí),A就能根據(jù)它所面臨的第一個(gè)輸入符號(hào)a,準(zhǔn)確地指派某一個(gè)候選前去執(zhí)行任務(wù)。這個(gè)候選就是那個(gè)終結(jié)首符集含a的a。
應(yīng)該指出,許多文法都存在那樣的非終結(jié)符,它的所有候選的終結(jié)首符集并非兩兩不相交的。例如,通常關(guān)于條件句的產(chǎn)生式 語句?if 條件 then 語句 else 語句| if 條件 then 語句
就是這樣一種情形。
如何把一個(gè)文法改造成任何非終結(jié)符的所有候選首符集兩兩不相交呢?其辦法是,提取公共左因子。例如,假定關(guān)于A的規(guī)則是
那么,可以把這些規(guī)則改寫成
A¢→b 1 | b 2 | … | b n
經(jīng)過反復(fù)提取左因子,就能夠把每個(gè)非終結(jié)符(包括新引進(jìn)者)的所有候選首符集變成為兩兩不相交。我們?yōu)榇烁冻龅拇鷥r(jià)是,大量引進(jìn)新的非終結(jié)符和e-產(chǎn)生式。
LL(1)分析條件
當(dāng)一個(gè)文法不含左遞歸,并且滿足每個(gè)非終結(jié)的所有候選首符集兩兩不相交的條件,我們可以找出滿足構(gòu)造不帶回溯的自上而下分析的文法條件:
則FIRST(a i)∩FIRST(a j)=f(i1j)
FIRST(A)∩FOLLOW(A)=f
如果一個(gè)文法G滿足以上條件,則稱該文法G為LL(1)文法。
這里,LL(1)中的第一個(gè)L表示從左到右掃描輸入串,第二個(gè)L表示最左推導(dǎo),1表示分析時(shí)每一步只需向前查看一個(gè)符號(hào)。
對(duì)于一個(gè)LL(1)文法,可以對(duì)其輸入串進(jìn)行有效的無回溯的自上而下分析。假設(shè)要用非終結(jié)符A進(jìn)行匹配,面臨的輸入符號(hào)為a,A的所有產(chǎn)生式為
1. 若a?FIRST(a i),則指派a i去執(zhí)行匹配任務(wù);
2. 若a不屬于任何一個(gè)候選首符集,則:
(1) 若e屬于某個(gè)FIRST(ai )且a?FOLLOW(A),則讓A與e自動(dòng)匹配。
(2) 否則,a的出現(xiàn)是一種語法錯(cuò)誤。
根據(jù)LL(1)文法的條件,每一步這樣的工作都是確信無疑的。
遞歸下降分析程序構(gòu)造
當(dāng)一個(gè)文法滿足LL(1)條件時(shí),我們就可以為它構(gòu)造一個(gè)不帶回溯的自上而下分析程序,這個(gè)分析程序是由一組遞歸過程組成的,每個(gè)過程對(duì)應(yīng)文法的一個(gè)非終結(jié)符。這樣的一個(gè)分析程序稱為遞歸下降分析器。如果用某種高級(jí)語言寫出所有遞歸過程,那就可以用這個(gè)語言的編譯系統(tǒng)來產(chǎn)生整個(gè)的分析程序。例如,考慮文法,它的每個(gè)非終結(jié)符所對(duì)應(yīng)的遞歸過程列于如下圖1。其中,ADVANCE是指把輸入串指示器IP調(diào)至指向下一個(gè)輸入符號(hào);SYM是指IP當(dāng)前所指的那個(gè)輸入符號(hào);ERROR為出錯(cuò)診察處理程序。
對(duì)于圖1的遞歸子程序,我們假定在開始工作前,輸入串指示器IP指向第一個(gè)輸入符號(hào)。當(dāng)每個(gè)子程序工作完畢之后,IP總是指向下一個(gè)未處理的符號(hào)。請(qǐng)注意遞歸子程序E¢,我們知道,關(guān)于E¢的規(guī)則是
即E¢只有兩個(gè)候選。第一個(gè)候選的開頭終結(jié)符為+,第二個(gè)候選為e。這就是說,當(dāng)E¢面臨輸入符號(hào)+時(shí)就令第一個(gè)候選進(jìn)入工作,當(dāng)面臨任何其它輸入符號(hào)時(shí),E¢就自動(dòng)認(rèn)為獲得了匹配(這時(shí),更精確的做法是判斷該輸入符號(hào)是否屬于FOLLOW(E¢))。遞歸過程E¢就是根據(jù)這一原則設(shè)計(jì)的。同理,關(guān)于T¢的過程也是如此。 PROCEDURE E; PROCEDURE T;BEGIN BEGINT;E¢ F;T¢END; ENDPROCEDURE E¢; PROCEDURE T¢;IF SYM=‘+’ THEN IF SYM=‘*’ THENBEGIN BEGINADVANCE; ADVANCE;T;E¢ F;T¢END END;PROCEDURE F;IF SYM=‘i’ THEN ADVANCEELSEIF SYM=‘(’ THENBEGINADVANCE;E;IF SYM=‘)’ THEN ADVANCEELSE ERRORENDELSE ERROR; 圖1 遞歸子程序
在前面的上下文無關(guān)文法產(chǎn)生式(或稱巴科斯范式)中我們只用到了兩個(gè)元符號(hào)“→”和“|”。下面我們擴(kuò)充幾個(gè)元語言符號(hào):
引入上述元符號(hào)的文法亦稱擴(kuò)充的巴科斯范式。
例如,通常的“實(shí)數(shù)”可定義為:
用這種定義系統(tǒng)來描述語法的好處是,直觀易懂,便于表示左遞歸消去和因子提取。對(duì)于構(gòu)造自上而下分析器來說,采用這種定義系統(tǒng)描述文法顯然是非常可取的。
預(yù)測分析程序
預(yù)測分析程序工作過程
預(yù)測分析表是一個(gè)M[A,a]形式的矩陣。其中,A為非終結(jié)符,a是終結(jié)符或‘#’(注意,‘#’不是文法的終結(jié)符,我們總把它當(dāng)成輸入串的結(jié)束符。雖然它不是文法的一部分,但假定它的存在將有助于簡化分析算法的描述)。矩陣元素M[A,a]中存放著一條關(guān)于A的產(chǎn)生式,指出當(dāng)A面臨輸入符號(hào)a時(shí)所應(yīng)采用的候選。M[A,a]中也可能存放一個(gè)“出錯(cuò)標(biāo)志”,指出A根本不該面臨輸入符號(hào)a。
棧STACK用于存放文法符號(hào)。分析開始時(shí),棧底先放一個(gè)‘#’,然后,放進(jìn)文法開始符號(hào)。同時(shí),假定輸入串之后也總有一個(gè)‘#’,標(biāo)志輸入串結(jié)束。
預(yù)測分析程序的總控程序在任何時(shí)候都是按STACK棧頂符號(hào)X和當(dāng)前的輸入符號(hào)a行事的,如圖4.4。對(duì)于任何(X,a),總控程序每次都執(zhí)行下述三種可能的動(dòng)作之一:
1. 若X=a=‘#’,則宣布分析成功,停止分析過程。
2. 若X=a 1‘#’,則把X從STACK棧頂逐出,讓a指向下一個(gè)輸入符號(hào)。
3. 若X是一個(gè)非終結(jié)符,則查看分析表M。若M[A,a]中存放著關(guān)于X的一個(gè)產(chǎn)生式,那么,首先把X逐出STACK棧頂,然后,把產(chǎn)生式的右部符號(hào)串按反序一一推進(jìn)STACK棧(若右部符號(hào)為e,則意味不推什么東西進(jìn)棧)。在把產(chǎn)生式的右部符號(hào)推進(jìn)棧的同時(shí)應(yīng)做這個(gè)產(chǎn)生式相應(yīng)的語義動(dòng)作(目前暫且不管)。若M[A,a]中存放著“出錯(cuò)標(biāo)志”,則調(diào)用出錯(cuò)診察程序ERROR。
預(yù)測分析程序的總控程序略微形式一點(diǎn)的描述是:
BEGIN
首先把‘#’然后把文法開始符號(hào)推進(jìn)STACK棧;
把第一個(gè)輸入符號(hào)讀進(jìn)a;
FLAG:=TRUE;
WHILE FLAG DO
BEGIN
把STACK棧頂符號(hào)上托出去并放在X中;
IF X?VT THEN
IF X= a THEN 把下一輸入符號(hào)讀進(jìn)a
ELSE ERROR
ELSE IF X=‘#’ THEN
IF X=a THEN FLAG:=FALSE ELSE ERROR
ELSE IF M[A,a]={X→X1X2…Xk}THEN
把Xk,Xk-1,…,X1一一推進(jìn)STACK棧
/* 若X1X2…Xk=e,不推什么進(jìn)棧 */
ELSE ERROR
END OF WHILE;
STOP /分析成功,過程完畢/
END
預(yù)測分析表的構(gòu)造
下面,我們介紹對(duì)于任給的文法G,如何構(gòu)造它的預(yù)測分析表M[A,a]。為了構(gòu)造預(yù)測分析表M,我們需要先構(gòu)造與文法G有關(guān)的集合FIRST和FOLLOW。
首先,我們來討論如何對(duì)每一個(gè)文法符號(hào)X?VT∪VN構(gòu)造FIRST(X)。其辦法是,連續(xù)使用下面的規(guī)則,直至每個(gè)集合FIRST不再增大為止:
1. 若X?VT,則FIRST(X)={X}。
2. 若X?VN,且有產(chǎn)生式X→a…,則把a(bǔ)加入到FIRST(X)中;若X→e也是一條產(chǎn)生式,則把e也加到FIRST(X)中。
3. 若X→Y…是一個(gè)產(chǎn)生式且Y?VN,則把FIRST(Y)中的所有非e-元素都加到FIRST(X)中;若X→Y1Y2…Yk是一個(gè)產(chǎn)生式,Y1,…,Yi-1都是非終結(jié)符,而且,對(duì)于任何j,1£j£i-1,FIRST(Yj)都含有e(即Y1…Yi-1e), 則把FIRST(Yi)中的所有非e-元素都加到FIRST(X)中;特別是,若所有的FIRST(Yj)均含有e,j=1,2,…,k,則把e加到FIRST(X)中。
現(xiàn)在,我們能夠?qū)ξ姆℅的任何符號(hào)串a(chǎn)=X1X2…Xn構(gòu)造集合FIRST(a)。首先,置FIRST(a)=FIRST(X1){e};若對(duì)任何1£j£i-1,e?FIRST(Xj),則把FIRST(Xi){e}加至FIRST(a)中;特別是,若所有的FIRST(Xj)均含有e,1£j£n,則把e也加至FIRST(a)中。顯然,若a=e則FIRST(a)={e}。
對(duì)于文法G的每個(gè)非終結(jié)符A構(gòu)造FOLLOW(A)的辦法是,連續(xù)使用下面的規(guī)則,直至每個(gè)FOLLOW不再增大為止:
在對(duì)文法G的每個(gè)非終結(jié)符A及其任意候選a都構(gòu)造出FIRST(a)和FOLLOW(A)之后,我們現(xiàn)在可以用它們來構(gòu)造G的分析表M[A,a]。構(gòu)造分析表算法的思想背景是很簡單的。例如,假定A→a是一個(gè)產(chǎn)生式,a ?FIRST(a)。那么,當(dāng)A呈現(xiàn)于STACK棧之頂且a是當(dāng)前的輸入符號(hào)時(shí),a應(yīng)被當(dāng)作是A唯一合適的全權(quán)代表。因此,M[A,a]中應(yīng)放進(jìn)產(chǎn)生式A→a。當(dāng)a=e或ae時(shí),如果當(dāng)前面臨的輸入符號(hào)a(可能是終結(jié)符或‘#’)屬于FOLLOW(A),那么,A→a就認(rèn)為已自動(dòng)得到匹配,因而,應(yīng)把A?a放在M[A,a]中。根據(jù)這個(gè)思想背景,構(gòu)造分析表M的算法是:
上述算法可應(yīng)用于任何文法G以構(gòu)造它的分析表M。但對(duì)于某些文法,有些M[A,a]可能持有若干個(gè)產(chǎn)生式,或者說有些M[A,a]可能是多重定義的。如果G是左遞歸或二義的,那么,M至少含有一個(gè)多重定義入口。因此,消除左遞歸和提取左因子將有助于獲得無多重定義的分析表M。
可以證明,一個(gè)文法G的預(yù)測分析表M不含多重定義入口,當(dāng)且僅當(dāng)該文法為LL(1)的
小結(jié)
通過這一章的學(xué)習(xí),我們了解了自上而下分析法的基本思想,學(xué)習(xí)了遞歸下降分析法的基本方法:如消除左遞歸、消除回溯、構(gòu)造遞歸下降子程序。學(xué)習(xí)了預(yù)測分析方法,掌握了預(yù)測分析表的構(gòu)造方法、LL(1)文法的定義。在下一章中,我們將討論自下而上語法分析方法。
典型題解
例題1按照喬姆斯基(Chomsky)對(duì)文法的分類,指出下述文法的所屬類型,并給出所描述的語言。
(a) S → Be
B → eC | Af
A → Ae | e
C → Cf
D → fDA
(b) A → ε| aB
B → Ab | a
(c) S → abcA
S → Aabc
A →ε
Aa → Sa
cA → cS
例題2給出文法G(S)
S → aSb | P
P → bPc | bQc
Q → Qa | a
1) 它是Chomsky哪一型文法?
2) 它生成的語言是什么?
解題思路:
注意到S推出的串的形式是aiPbi(i≥0),而P推出的串的形式是bjQcj(j≥1), Q推出的串的形式是ak(k≥1)。
解答:
1) 該文法是Chomsky2型文法,即上下文無關(guān)文法。
2) 它生成的語言是L={aibjakcjbi | i≥0,j≥1,k≥1}
例題3寫一個(gè)文法G,使得L(G)={ anbman|n,m30}。
解題思路:
寫出語言的文法是檢查考生形式抽象的能力。解答這類問題,首先應(yīng)當(dāng)仔細(xì)研究語言的結(jié)構(gòu)特點(diǎn),通常這些語言具有形式上的對(duì)稱性和字符數(shù)目上的相關(guān)性等特點(diǎn),這些特性可以用文法的遞歸定義來實(shí)現(xiàn)。
解答:所求文法是:
G(S):
S ? aSa | B
B ? bB| e
例題4 將文法G(S)改寫成等價(jià)的正規(guī)文法。
G(S):
S → dAB
A → aA | a
B → Bb |ε
解題思路:
對(duì)于這類題目,首先求出文法描述的正規(guī)語言,寫出相應(yīng)的正規(guī)式,在此基礎(chǔ)上構(gòu)造相應(yīng)的DFA,最后把DFA的狀態(tài)轉(zhuǎn)換成文法的非終結(jié)符,就能夠?qū)懗龅葍r(jià)的正規(guī)文法了。
解答:該文法描述的語言是daibj(i>0,j≥0),對(duì)應(yīng)的DFA是:
相應(yīng)的正規(guī)文法是:
G(S):
S → dA
A → aB
B → aB | bC |ε
C → bC |ε
注意: 把DFA的轉(zhuǎn)換成正規(guī)文法時(shí),終態(tài)對(duì)應(yīng)的非終結(jié)符應(yīng)當(dāng)有ε候選式(如B,C)。
例題5 按指定類型,給出語言的文法
(a) L={aibj|j>i≥1} 的上下文無關(guān)文法
(b) 字母表Σ={a,b}上的同時(shí)只有奇數(shù)個(gè)a和奇數(shù)個(gè)b的所有串的集合的正規(guī)文法
(c) 有相同個(gè)數(shù)的a和b組成的句子的無二義文法
解題思路:
給出語言的文法可以從多個(gè)角度進(jìn)行思考,如分解語言的結(jié)構(gòu),利用有限自動(dòng)機(jī),找出語言的遞歸或遞推特性等。
語言(a) L={aibj|j>i≥1},實(shí)際上可以看成aibibj-i的形式,而aibi可以由A → aAb | ab規(guī)則來描述,bj-i可以由B → bB | b規(guī)則來描述。
語言(b)的描述可以借助有限自動(dòng)機(jī)的思想,非終結(jié)符A、B、C、S分別表示下面四種狀態(tài):
識(shí)別了偶數(shù)個(gè)a和偶數(shù)個(gè)b的狀態(tài)
識(shí)別了奇數(shù)個(gè)a和偶數(shù)個(gè)b的狀態(tài)
識(shí)別了偶數(shù)個(gè)a和奇數(shù)個(gè)b的狀態(tài)
識(shí)別了奇數(shù)個(gè)a和奇數(shù)個(gè)b的狀態(tài)
文法規(guī)則只需要描述這些非終結(jié)符之間的推導(dǎo)關(guān)系,即狀態(tài)之間的轉(zhuǎn)換關(guān)系。
語言(c)的描述可以采用遞歸的思想,寫出相應(yīng)的無二義文法。
解答:
(a) 所求的文法是G(S):
S → AB
A → aAb | ab
B → bB | b
(b) 所求的文法是G(S):
S → aC | bB
A → bC | aB |ε
B → aA | bS
C → aS | bA
(c) 所求的文法是G(S):
S→aBS|bAS|aB|bA
B→aBB|b
A→bAA|a
例題6 寫一個(gè)上下文無關(guān)文法,使其語言是能被5整除且不以0開頭的無符號(hào)整數(shù)的集合。(如{5,10,15,…})
解題思路:
能被5整除的數(shù)從形式上看,是以0,5結(jié)尾的數(shù)字串。題目要求的不以0開頭,注意0不是該語言的句子。
解答:所求文法為:
G(S):
S → M F | 5
F → 5 | 0
N → 1 |2 | 3 | 4 | 5 | 6 | 7 | 8| 9
D → N | 0
M → M D | N
其中, S代表能被5整除且不以0開頭的無符號(hào)整數(shù);
F代表可以出現(xiàn)在個(gè)位上的數(shù)字;
D代表所有數(shù)字;
N代表所有非零數(shù)字;
M代表所有不以零開頭的數(shù)字串;
例題7
寫一個(gè)文法使其語言為L(G)={ anbm| 2n>m≥n≥1}
解題思路:
b的個(gè)數(shù)大于或等于a的個(gè)數(shù),但又比a的個(gè)數(shù)的2倍要少。這是一種類型的問題,一般在兩個(gè)或多個(gè)字符的數(shù)量上做文章,對(duì)于這類問題,有一種固定的問題求解方法。以本題為例,b的個(gè)數(shù)在a的個(gè)數(shù)的一倍和兩倍之間,那么就存在兩個(gè)邊界:一倍和兩倍,我們就分別為它們寫出兩個(gè)產(chǎn)生式:
1.S —> aSb
2.S —> aSbb
此時(shí)可以看到,用產(chǎn)生式1擴(kuò)展時(shí)所產(chǎn)生的a和b的個(gè)數(shù)相等,而用產(chǎn)生式2擴(kuò)展時(shí)所產(chǎn)生的b的個(gè)數(shù)是a的個(gè)數(shù)的兩倍,如果同時(shí)使用兩個(gè)產(chǎn)生式進(jìn)行擴(kuò)展,那b的個(gè)數(shù)將在a的個(gè)數(shù)的一倍和兩倍之間,滿足了這個(gè)前提之后,再用另一個(gè)產(chǎn)生式(S —> ab)來保證邊界條件(2n>m、m≥n和n≥1)就可以了。如果邊界條件是2n≥m>n≥1,則可以用產(chǎn)生式S —> abb來滿足。
解答: S —> aSb | aSbb | ab
例題8試簡述二義性概念。
解答:如果一個(gè)文法存在某個(gè)句子對(duì)應(yīng)兩顆不同的語法樹,則說這個(gè)文法是二義的。如果一個(gè)語言是二義的,當(dāng)且僅當(dāng)它不存在無二義性的文法。文法的二義性與語言的二義性是兩個(gè)不同的概念。例如,對(duì)于某種語言L來說,可能存在兩個(gè)文法G和G’,有L(G)=L(G’)=L,但文法G是二義的,而G’是無二義的,這時(shí),語言L并不是二義的。
例題9文法G的產(chǎn)生式集為{S → S+S |S*S | i | (S)},對(duì)于輸入串i+i*i:
1) 給出一個(gè)推導(dǎo);
2) 畫出一棵語法樹;
3) 文法G是否是二義性的,請(qǐng)證明你的結(jié)論?
解題思路:
這類題目,重點(diǎn)考察推導(dǎo)、語法樹和二義性等基本概念。要證明一個(gè)文法是二義性的,只要找出該文法的一個(gè)句子,說明該句子有兩種不同的最左推導(dǎo)或最右推導(dǎo),或者有兩棵不同的語法樹。
解答:
1) STS+STi+ST i+S*S T i+i*S T i+i*i
2) i+i*i 的語法樹
3) 文法G是二義性的。考慮句子i+i*i,除了語法樹外,還有另一棵語法樹,所以文法G是二義性的。
例題10已知文法G=({S},{a},{S → SaS, S →ε},S)
1) 該文法是否是二義性文法,為什么?
2) 該文法是否是OPG(算符優(yōu)先文法)文法,為什么?
3) 該文法是否是LL(1)文法,為什么?
4) 該文法是否是SLR(1)文法,為什么?
解題思路:
本題和核心是判斷文法的二義性,同時(shí)必須掌握OPG(算符優(yōu)先文法)文法、 LL(1)文法和SLR(1)文法和二義性文法的關(guān)系,并根據(jù)它們之間的關(guān)系判斷文法的性質(zhì)。
解答:考慮該文法的句子aa,我們有下面兩個(gè)不同的最左推導(dǎo):
STSaSTSaSaSTaSaSTaaSTaa
STSaSTaSTaSaSTaaSTaa
所以該文法是二義性的。因?yàn)镺PG(算符優(yōu)先文法)文法, LL(1)文法和SLR(1)文法一定不是二義性文法,所以,該文法不是OPG(算符優(yōu)先文法)文法、 LL(1)文法和SLR(1)文法。
例題11 生成L={ albmclanbn| l30,m31,n32}這種語言的文法是什么?它是Chomsky哪一型文法?
解答:所求文法是G(S)
S → AC
A → aAc | B
B → bB | b
C → aCb | ab
它是Chomsky 2型文法,即上下文無關(guān)文法。
例題12文法G(S):
S → aSPQ | abQ
QP → PQ
bP → bb
bQ → bc
cQ → cc
它是Chomsky哪一型文法?它生成的語言是什么?
解答:從規(guī)則形式上可以看出,文法G是Chomsky 1型文法,即上下文有關(guān)文法。它生成的語言是L={ anbncn | n31}
例題13給出下列術(shù)語的嚴(yán)格定義:
1) 上下文無關(guān)文法 2) LL(1)文法
解答:
上下文無關(guān)文法的定義:
形式上說,一個(gè)上下文無關(guān)文法G是一個(gè)四元式(VT,VN,S,P),其中
VT是一個(gè)非空有限集,它的每個(gè)元素稱為終結(jié)符號(hào);
VN是一個(gè)非空有限集,它的每個(gè)元素稱為非終結(jié)符號(hào),VT∩VN=f;
S是一個(gè)非終結(jié)符號(hào),稱為開始符號(hào);
P是一個(gè)產(chǎn)生式集合(有限), 每個(gè)產(chǎn)生式的形式是P?a, 其中,P?VN, a?(VT∪VN)*。開始符號(hào)S至少必須在某個(gè)產(chǎn)生式的左部出現(xiàn)一次。
LL(1)文法的定義:
如果一個(gè)文法G滿足下面的條件,則稱該文法G為LL(1)文法:
1. 文法不含左遞歸,
2. 對(duì)于文法中每一個(gè)非終結(jié)符A的各個(gè)產(chǎn)生式的候選首符集兩兩不相交。即,若A→a1|a2|…|an
則 FIRST(ai)∩FIRST(aj)=f (i1j)
3. 對(duì)文法中的每個(gè)非終結(jié)符A,若它存在某個(gè)候選首符集包含e,則
FIRST(A)∩FOLLOW(A)=f
例題14給出文法G1(S)
S → aSb | P
P → bPc | bQc
Q → Qa | a
消除文法的左遞歸、提取左公共因子后是不是LL(1)文法?請(qǐng)證實(shí)。
本章練習(xí)
考慮下面文法G1:
S→a | ù | (T)
T→T, S | S
(1) 消去G1的左遞歸。然后,對(duì)每個(gè)非終結(jié)符,寫出不帶回溯的遞歸子程序。
(2) 經(jīng)改寫后的文法是否是LL(1)的?給出它的預(yù)測分析表。
對(duì)下面的文法G
E→TE¢
E¢→+E | e
T→FT¢
T¢→T | e
F→PF¢
F¢→*F¢ | e
P→ (E) | a | b | ù
(1) 計(jì)算這個(gè)文法的每個(gè)非終結(jié)符的FIRST和FOLLOW。
(2) 證明這個(gè)文法是LL(1)的。
(3) 構(gòu)造它的預(yù)測分析表。
(4) 構(gòu)造它的遞歸下降分析程序。
下面文法中,哪些是LL(1)的,說明理由。
(1) S→Abc
A→a | e
B→b | e
(2) S→Ab
A→a | B | e
(3) S→ABBA
A→a | e
B→b | e
(4) S→aSe | B
B→bBe | C
C→cCe | d
對(duì)下面文法
Expr→-Expr
Expr→ (Expr)|Var ExprTail
ExprTail→-Expr | e
Var→id VarTail
VarTail→ (Expr) | e
(1) 構(gòu)造LL(1)分析表。
(2) 給出對(duì)句子id–id((id))的分析過程。
把下面文法改寫為LL(1)的:
Declist→Declist; Decl | Decl
Decl→IdList:Type
IdList→Idlist, id | id
Type→ScalarType | array (ScalarTypeList) of Type
ScalarType→id | Bound..Bound
Bound→Sign IntLiteral | id
Sign→+ | - | e
ScalarTypeList→ScalarTypeList, ScalarType | ScalarType
參考文獻(xiàn)
[1]Alfred V.Aho, Ravi Sethi, Jeffrey D. Ullman. Compilers: Principles, Techniques, and Tools. Acldison-Wesley Publishing Company, 1986.
[2]A.V.Aho, J.D. Ullman. Principles of Compiler Descign, Addison=Wesley, 1977.
[3]Arthur B.Pyster. Compiler Design and Constuction. Van Nostrand Reihold Company, 1980.
[4]陳火旺,錢家驊,孫永強(qiáng). 程序設(shè)計(jì)語言編譯原理. 北京:國防工業(yè)出版社,1984.
[5]王兵山,吳兵,形式語言. 國防科技大學(xué)出版社,1988.
[6]霍普克羅夫特,厄爾曼. 形式語言及其與自動(dòng)機(jī)的關(guān)系. 科學(xué)出版社,1979.
總結(jié)
以上是生活随笔為你收集整理的语法分析:自上而下分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 销毁session的三种方式
- 下一篇: 知识图谱关键技术总览