语法制导的翻译
繼詞法分析和文法分析之后,本文將介紹使用上下文無關(guān)文法來引導(dǎo)對(duì)語言的翻譯。
SDD
語法制導(dǎo)定義(Syntax-Directed Definition,SDD)是一個(gè)上下文無關(guān)文法和屬性及語義規(guī)則的結(jié)合。屬性和文法符號(hào)相關(guān)聯(lián),語義規(guī)則和產(chǎn)生式相關(guān)聯(lián),文法符號(hào)X的屬性a表示為X.a。
非終結(jié)符號(hào)可以有兩種屬性:
- 綜合屬性:如果語法分析樹上的結(jié)點(diǎn)N的某個(gè)屬性a只能通過N的子結(jié)點(diǎn)和N本身的屬性值來定義,那么屬性a是結(jié)點(diǎn)N的一個(gè)綜合屬性;
- 繼承屬性:如果語法分析樹上的結(jié)點(diǎn)N的某個(gè)屬性b只能通過N的父結(jié)點(diǎn)、N本身和N的兄弟結(jié)點(diǎn)的屬性值來定義,那么屬性a是結(jié)點(diǎn)N的一個(gè)繼承屬性。
終結(jié)符號(hào)可以有綜合屬性,但不能有繼承屬性,終結(jié)符號(hào)的屬性是由詞法分析器提供的詞法值。
綜合屬性
如果語法分析樹中的結(jié)點(diǎn)N的某個(gè)屬性a只能通過N的子結(jié)點(diǎn)或N本身的屬性值來定義,那么屬性a是結(jié)點(diǎn)N的一個(gè)綜合屬性。
對(duì)表達(dá)式文法G:
E → E+T | TT → T*F | FF → (E) | id
它對(duì)應(yīng)的SDD如下:
其中,屬性val是文法符號(hào)的數(shù)值,屬性lexval是由詞法分析器返回的數(shù)值。為id1*id2+id3構(gòu)建語法分析樹,其中,id1、id2、id3的lexval屬性分別為3、4、5:
在這棵語法分析樹中,所有結(jié)點(diǎn)的屬性都被顯示了出來,這樣的語法分析樹也稱為注釋語法分析樹。
繼承屬性
如果語法分析樹上的結(jié)點(diǎn)N的某個(gè)屬性b只能通過N的父結(jié)點(diǎn)、N本身和N的兄弟結(jié)點(diǎn)的屬性值來定義,那么屬性a是結(jié)點(diǎn)N的一個(gè)繼承屬性。
考慮上一小節(jié)中的表達(dá)式文法的非左遞歸形式G’:
E → TE'E'→ +TE' | εT → FT'T'→ *FT' | εF → (E) | id
它對(duì)應(yīng)的SDD如下:
其中,屬性val是文法符號(hào)的數(shù)值,屬性lexval是由詞法分析器返回的數(shù)值,屬性inh是一個(gè)繼承屬性,屬性syn是一個(gè)綜合屬性。為id1*id2+id3構(gòu)建語法分析樹,其中,id1、id2、id3的lexval屬性分別為3、4、5:
在這棵語法分析樹中,屬性inh“向下地傳遞參數(shù)”,屬性syn“向上地返回結(jié)果”,關(guān)于繼承屬性和綜合屬性的求值順序?qū)⒃谙乱恍」?jié)介紹。
SDD的求值順序
和終結(jié)符號(hào)的綜合屬性直接由詞法分析器給出不同,非終結(jié)符號(hào)的綜合屬性和繼承屬性都或多或少地依賴于其他屬性,因此確定一棵注釋語法分析樹中屬性的求值順序是十分重要的,依賴圖可以幫助我們完成這個(gè)工作。
依賴圖描述了某個(gè)語法分析樹中的屬性實(shí)例之間的信息流,從一個(gè)屬性實(shí)例到另一個(gè)屬性實(shí)例的邊表示計(jì)算第二個(gè)屬性實(shí)例需要用到第一個(gè)屬性實(shí)例的值。具體地講,一個(gè)依賴圖包括:
- 屬性結(jié)點(diǎn):對(duì)于語法分析樹中的每個(gè)結(jié)點(diǎn)N,N的每個(gè)屬性在依賴圖中都有一個(gè)結(jié)點(diǎn);
- 綜合屬性的邊:如果和產(chǎn)生式p關(guān)聯(lián)的語義規(guī)則通過X.a的值定義了綜合屬性Y.b的值,那么在依賴圖中有一條從X.a到Y(jié).b的邊;
- 繼承屬性的邊:如果和產(chǎn)生式p關(guān)聯(lián)的語義規(guī)則通過X.a的值定義了繼承屬性Y.b的值,那么在依賴圖中有一條從X.a到Y(jié).b的邊。
得到依賴圖后,如果這個(gè)依賴圖中沒有環(huán),那么這個(gè)依賴圖至少存在一個(gè)拓?fù)渑判颉<僭O(shè)依賴圖的結(jié)點(diǎn)集合為N,邊集合為E,計(jì)算此依賴圖的拓?fù)渑判虻牟襟E為:
- 構(gòu)建一個(gè)序列A,A的長(zhǎng)度為len(N),將i初始化為0;
- 從N中選出一個(gè)入度為0的結(jié)點(diǎn)n,從N中刪除n且從E中刪除所有以n為起點(diǎn)的邊,將A[i]設(shè)為n,同時(shí)將i加1;
- 如果N不為空,重復(fù)步驟2,否則排序結(jié)束,得到拓?fù)湫蛄蠥[0]、A[1]、…、A[len(N)-1]。
考慮圖1的注釋語法分析樹,它的依賴圖如下,結(jié)點(diǎn)前的標(biāo)號(hào)是拓?fù)渑判蚝蟮慕Y(jié)果:
考慮圖2的注釋語法分析樹,它的依賴圖如下,結(jié)點(diǎn)前的標(biāo)號(hào)是拓?fù)渑判蚝蟮慕Y(jié)果:
到這里,我們已經(jīng)知道了如何借助依賴圖確定一棵注釋語法分析樹中屬性的求值順序。但是,不是所有的注釋語法分析樹都能通過依賴圖找到一個(gè)拓?fù)渑判?#xff0c;如果依賴圖中存在環(huán),那么這個(gè)依賴圖沒有拓?fù)渑判颉H绻覀兡苄⌒牡囟xSDD中的語義規(guī)則使得其依賴圖中沒有環(huán),那么我們一定能夠確定此SDD中屬性的求值順序,下面介紹的S屬性的SDD和L屬性的SDD一定能確定屬性的求值順序。
S屬性的SDD
如果一個(gè)SDD的每個(gè)屬性都是綜合屬性,那么這個(gè)SDD是一個(gè)S屬性的SDD。
對(duì)于一個(gè)S屬性的SDD,可以按照語法分析樹結(jié)點(diǎn)的任何自底向上順序來計(jì)算它的各個(gè)屬性值。S屬性的SDD可以在自底向上的語法分析過程中實(shí)現(xiàn)。
L屬性的SDD
L屬性的SDD的思想是在一個(gè)產(chǎn)生式體所關(guān)聯(lián)的各個(gè)屬性之間,依賴圖的邊總是從左到右,而不能從右到左。也就是說,每個(gè)屬性必須滿足如下條件:
- 要么是一個(gè)綜合屬性;
- 要么是一個(gè)繼承屬性,但是它的語義規(guī)則有這些限制:假設(shè)有產(chǎn)生式A→X1X2…Xn和繼承屬性Xi.a,Xi.a的計(jì)算規(guī)則只能使用A的繼承屬性以及Xj(1<=j<=i)的綜合屬性或繼承屬性,并且Xi的全部屬性組成的依賴圖中不存在環(huán)。
L屬性的SDD可以在自頂向下的語法分析過程中實(shí)現(xiàn)。
SDT
語法制導(dǎo)的翻譯方案(Syntax-Directed Translation scheme,SDT)是SDD的一種補(bǔ)充,它是在其產(chǎn)生式體中嵌入程序片段的一個(gè)上下文無關(guān)文法,這些程序片段稱為語義動(dòng)作,它們可以出現(xiàn)在產(chǎn)生式體中的任何地方。通常情況下,語義動(dòng)作的兩邊要加上花括號(hào),如果花括號(hào)作為文法符號(hào)出現(xiàn),則要給它們加上引號(hào)。
實(shí)現(xiàn)SDT的通用方法
語義動(dòng)作可以放在產(chǎn)生式體中的任何位置,當(dāng)一個(gè)語義動(dòng)作左邊的所有符號(hào)都被處理完后該動(dòng)作立即執(zhí)行。更具體的,對(duì)產(chǎn)生式A→X{a}Y,有:
- 如果語法分析過程是自底向上的,那么當(dāng)X出現(xiàn)在語法分析棧的棧頂時(shí)立即執(zhí)行動(dòng)作a;
- 如果語法分析過程是自頂向下的,那么當(dāng)試圖展開Y(Y是非終結(jié)符號(hào))或在輸入中檢測(cè)到Y(jié)(Y是終結(jié)符號(hào))之前執(zhí)行動(dòng)作a。
通用的SDT實(shí)現(xiàn)方法如下:
- 忽略語義動(dòng)作,對(duì)輸入進(jìn)行語法分析,并產(chǎn)生一棵語法分析樹;
- 然后檢查每個(gè)內(nèi)部結(jié)點(diǎn)N,假設(shè)它的產(chǎn)生式是A→α。將α中的各個(gè)動(dòng)作當(dāng)作N的附加子結(jié)點(diǎn)加入,使得N的子結(jié)點(diǎn)從左到右和α中的符號(hào)及動(dòng)作完全一致;
- 對(duì)這棵語法分析樹進(jìn)行前序遍歷,并且當(dāng)訪問到一個(gè)以某個(gè)動(dòng)作為標(biāo)號(hào)的結(jié)點(diǎn)時(shí)立即執(zhí)行此動(dòng)作。
對(duì)于表1顯示的SDD,把它轉(zhuǎn)換成SDT:
注意到,此SDT和相應(yīng)的SDD相比,只有前兩個(gè)產(chǎn)生式的語義動(dòng)作和原來的語義規(guī)則不同。由于這兩個(gè)產(chǎn)生式的產(chǎn)生式頭已經(jīng)是開始符號(hào),因此相應(yīng)的語義動(dòng)作需要把實(shí)現(xiàn)結(jié)果輸出。由此SDT構(gòu)建的嵌入了動(dòng)作的語法分析樹如下:
對(duì)上面的語法分析樹進(jìn)行前序遍歷,每遇到一個(gè)動(dòng)作就立即執(zhí)行它,遍歷完成后就實(shí)現(xiàn)了此SDT。
在語法分析過程中實(shí)現(xiàn)SDT
可以在語法分析過程中實(shí)現(xiàn)的SDT包括后綴SDT和L屬性定義的SDT,后綴SDT通常在自底向上的語法分析過程中實(shí)現(xiàn),L屬性定義的SDT通常在自頂向下的語法分析過程中實(shí)現(xiàn)。注意,不是所有的SDT都能在語法分析過程中實(shí)現(xiàn)。
后綴SDT
如果一個(gè)SDD的基礎(chǔ)文法是LR文法并且此SDD是S屬性定義的,那么我們可以構(gòu)造這樣一個(gè)SDT,其中的每個(gè)動(dòng)作都放在產(chǎn)生式的最后,并且在按照這個(gè)產(chǎn)生式將產(chǎn)生式體歸約為產(chǎn)生式頭的時(shí)候執(zhí)行這個(gè)動(dòng)作。像這樣所有動(dòng)作都在產(chǎn)生式體最右端的SDT也稱為后綴翻譯方案(后綴SDT)。
將一個(gè)S屬性定義的且基礎(chǔ)文法為L(zhǎng)R文法的SDD轉(zhuǎn)換為一個(gè)SDT的規(guī)則如下:
- 將計(jì)算一個(gè)產(chǎn)生式頭的綜合屬性的動(dòng)作放置在這個(gè)產(chǎn)生式體的最右端。
注意到,由于表1中的SDD是S屬性定義的且其基礎(chǔ)文法G是LR文法,因此可以把它轉(zhuǎn)換成后綴SDT,表3中的SDT就是此SDD的一個(gè)轉(zhuǎn)換形式。下面我們進(jìn)一步改進(jìn)表3中的SDT,使其能夠在語法分析過程中實(shí)現(xiàn):
表4中的stack表示LR語法分析棧,top指向棧頂。為了幫助讀者理解這個(gè)SDT中的語義動(dòng)作,以產(chǎn)生式F→(E)為例,考慮將(E)歸約成F的過程,如圖6所示:
圖6顯示了把(E)歸約成F的LR語法分析過程。圖6(a)是即將把(E)歸約成F的語法分析棧快照,此時(shí)有stack[top]=")",stack[top-1]="E",stack[top-2]="("。執(zhí)行的歸約動(dòng)作是連續(xù)3次出棧操作(將”)”、”E”、”(“依次彈出棧頂,如圖6(b))和1次入棧操作(將”F”壓入棧頂,如圖6(c))。注意到,(c)中的top相當(dāng)于(a)中的top-2,也就是說,F實(shí)際上出現(xiàn)在(a)中的stack[top-2]處;另外,由于E.val將被賦值給F.val,并且E出現(xiàn)在(a)中的stack[top-1]處,因此在(a)中有stack[top-2].val=stack[top-1].val;將(a)變成(c)還需要執(zhí)行2次出棧操作(實(shí)際上是3次出棧操作和1次入棧操作),因此還需要把top指針向下移動(dòng)2位。
從SDT中消除左遞歸
在詳細(xì)介紹如何在語法分析過程中實(shí)現(xiàn)L屬性定義的SDT之前,我們先來介紹如何從SDT中消除左遞歸。在介紹語法分析的時(shí)候我們知道了帶有左遞歸的文法不能按照自頂向下的方式進(jìn)行語法分析,并且我們還知道如何從一個(gè)左遞歸的文法中消除左遞歸。當(dāng)文法是SDT的一部分時(shí),我們還需要考慮如何處理其中的語義動(dòng)作。
情況1
最簡(jiǎn)單的情況是,我們只需要關(guān)心一個(gè)SDT中動(dòng)作的執(zhí)行順序。在這種情況下,當(dāng)對(duì)文法進(jìn)行消除左遞歸的轉(zhuǎn)換時(shí),可以簡(jiǎn)單地將動(dòng)作當(dāng)成終結(jié)符號(hào)處理。之所以可以這樣做,是因?yàn)閷?duì)文法的轉(zhuǎn)換保持了由文法生成的符號(hào)串中終結(jié)符號(hào)的順序。舉個(gè)例子,對(duì)下面的SDT:
E → E+T { print('+'); }E → T
將動(dòng)作看作終結(jié)符號(hào),消除左遞歸后得到的SDT為:
E → TRR → +T { print('+'); } R | ε
情況2
另一種比較復(fù)雜的情況是,一個(gè)SDT的動(dòng)作包含了對(duì)屬性值的計(jì)算。舉個(gè)例子,對(duì)下面的SDT:
A → A1Y { A.a = g(A1.a, Y.y); }A → X { A.a = f(X.x); }
其中,f和g是兩個(gè)任意的函數(shù),對(duì)基礎(chǔ)文法消除左遞歸得到:
A → XRR → YR | ε
現(xiàn)在嘗試在文法中加入語義動(dòng)作。我們給文法符號(hào)R附加上兩個(gè)屬性,一個(gè)屬性i是繼承屬性,它用來保存函數(shù)f和g的結(jié)果,另一個(gè)屬性s是綜合屬性,它會(huì)在計(jì)算結(jié)束后返回最終的計(jì)算結(jié)果。得到的SDT為:
A → X { R.i = f(X.x); } R { A.a = R.s; }R → Y { R1.i = g(R.i, Y.y); } R1 { R.s = R1.s; }R → ε { R.s = R.i; }
我們用注釋語法分析樹來展示對(duì)SDT消除左遞歸前后的變化(這里省略了對(duì)R.s的計(jì)算):
L屬性定義的SDT
如果一個(gè)SDD的基礎(chǔ)文法能夠以自頂向下的方式進(jìn)行語法分析,并且此SDD是L屬性定義的,那么我們可以把這個(gè)SDD轉(zhuǎn)換成一個(gè)L屬性定義的SDT。
將一個(gè)L屬性定義的SDD轉(zhuǎn)換為一個(gè)SDT的規(guī)則如下:
- 將計(jì)算某個(gè)非終結(jié)符號(hào)A的繼承屬性的動(dòng)作插入到產(chǎn)生式體中緊靠在A的本次出現(xiàn)之前的位置上。如果A的多個(gè)繼承屬性以無環(huán)的方式相互依賴,就需要對(duì)這些屬性的求值動(dòng)作進(jìn)行排序,以便先計(jì)算需要的屬性;
- 將計(jì)算一個(gè)產(chǎn)生式頭的綜合屬性的動(dòng)作放置在這個(gè)產(chǎn)生式體的最右端。
在語法分析過程中實(shí)現(xiàn)L屬性定義的SDT的方法包括:
- 使用一個(gè)遞歸下降的語法分析器。它為每個(gè)非終結(jié)符號(hào)都建立一個(gè)函數(shù),對(duì)應(yīng)于非終結(jié)符號(hào)A的函數(shù)以參數(shù)的形式接受A的繼承屬性,并返回A的綜合屬性;
- 使用一個(gè)遞歸下降的語法分析器,以邊掃描邊生成的方式生成代碼;
- 與LL語法分析器結(jié)合實(shí)現(xiàn)一個(gè)SDT。屬性的值存放在語法分析棧中,各個(gè)規(guī)則從棧中的已知位置獲取需要的屬性值;
- 與LR語法分析器結(jié)合實(shí)現(xiàn)一個(gè)SDT。如果基礎(chǔ)文法是LL的,我們總是可以按照自底向上的方式來處理語法分析和翻譯過程。
在遞歸下降語法分析過程中進(jìn)行翻譯
一個(gè)遞歸下降的語法分析器對(duì)每個(gè)非終結(jié)符號(hào)A都有一個(gè)函數(shù)A,我們可以把這個(gè)語法分析器擴(kuò)展為一個(gè)翻譯器:
- 函數(shù)A的參數(shù)是非終結(jié)符號(hào)A的繼承屬性,返回值是非終結(jié)符號(hào)A的綜合屬性;
- 在函數(shù)A的函數(shù)體中進(jìn)行語法分析并處理屬性。
舉個(gè)例子,考慮表2中的SDD,非終結(jié)符號(hào)E對(duì)應(yīng)的函數(shù)E如下:
float E() {float Tval = T();float EQUOTEinh = Tval;float EQUOTEsyn = EQUOTE(EQUOTEinh);float Eval = EQUOTEsyn;return Eval;
}
在函數(shù)E中,首先,注意到函數(shù)E沒有任何參數(shù),這是因?yàn)榉墙K結(jié)符號(hào)E沒有任何繼承屬性;其次,在E的函數(shù)體中,調(diào)用了函數(shù)T和函數(shù)EQUOTE,前者對(duì)應(yīng)于非終結(jié)符號(hào)T(T也沒有任何繼承屬性),后者對(duì)應(yīng)于非終結(jié)符號(hào)E’(E’有一個(gè)繼承屬性);最后,定義了一系列變量來保存繼承屬性或綜合屬性(實(shí)際上是為了方便讀者理解才定義了如此多的變量),并返回了計(jì)算結(jié)果。
另外,非終結(jié)符號(hào)E’對(duì)于的函數(shù)EQUOTE如下:
float EQUOTE(float EQUOTEinh) {if (下一個(gè)輸入符號(hào)為'+') {讀取輸入return EQUOTE(EQUOTEinh + T());} else if (輸入已經(jīng)結(jié)束) {return EQUOTEinh;} else {...}
}
邊掃描邊生成代碼
對(duì)上面的例子,如果需要在調(diào)用函數(shù)EQUOTE的過程中打印出表達(dá)式,而不是返回最后的結(jié)果,那么可以一邊掃描一邊生成代碼。使用這個(gè)方法需要滿足的條件是:每個(gè)非終結(jié)符號(hào)存在一個(gè)綜合屬性作為主屬性,對(duì)主屬性的求值是將相關(guān)產(chǎn)生式體中的非終結(jié)符號(hào)的主屬性值連接起來得到的(連接時(shí)也可能包括非主屬性),各個(gè)非終結(jié)符號(hào)的主屬性值在連接運(yùn)算中出現(xiàn)的順序和這些非終結(jié)符號(hào)在產(chǎn)生式體中出現(xiàn)的順序相同。
修改函數(shù)EQUOTE,使其打印出表達(dá)式:
float EQUOTE(float EQUOTEinh) {if (下一個(gè)輸入符號(hào)為'+') {讀取輸入print(EQUOTEinh);print('+');float Tval = T();return EQUOTE(EQUOTEinh + Tval);} else if (輸入已經(jīng)結(jié)束) {return EQUOTEinh;} else {...}
}
L屬性的SDD與LL語法分析
對(duì)于一個(gè)基礎(chǔ)文法是LL文法的L屬性的SDD,如果我們已經(jīng)把它轉(zhuǎn)換成了一個(gè)L屬性定義的SDT,那么我們可以在LL語法分析過程中完成對(duì)它的翻譯。
為了在LL語法分析過程中實(shí)現(xiàn)L屬性定義的SDT,語法分析棧需要被擴(kuò)展,擴(kuò)展后的語法分析棧可以存放三種記錄:
- 代表終結(jié)符號(hào)和非終結(jié)符號(hào)的記錄,實(shí)際上是狀態(tài)記錄;
- 動(dòng)作記錄。非終結(jié)符號(hào)A的繼承屬性放在表示A的棧記錄中,對(duì)A的繼承屬性求值的代碼放在一個(gè)緊靠在A的棧記錄之上的動(dòng)作記錄中;
- 綜合記錄。非終結(jié)符號(hào)A的綜合屬性放在一個(gè)緊靠在A的棧記錄之下的綜合記錄中。
在語法分析過程中,某些屬性值可能會(huì)被拷貝到動(dòng)作記錄或綜合記錄中,這是因?yàn)橐粋€(gè)分析表驅(qū)動(dòng)的LL語法分析器模擬了一個(gè)最左推導(dǎo)過程。舉個(gè)例子,當(dāng)語法分析器按照一個(gè)產(chǎn)生式A→BC對(duì)棧頂?shù)姆墙K結(jié)符號(hào)A展開時(shí),棧頂?shù)腁被替換為BC,假設(shè)C有一個(gè)繼承屬性C.i依賴于A的某個(gè)屬性,但是此時(shí)已經(jīng)無法訪問到A,因此我們需要把A的屬性臨時(shí)拷貝到C的動(dòng)作記錄中,這樣一來,C在計(jì)算其屬性C.i時(shí)就能找到這個(gè)依賴屬性了。
下圖是用文法G’中的產(chǎn)生式E'→ +TE'對(duì)E’進(jìn)行展開的語法分析過程:
我們對(duì)這個(gè)語法分析過程進(jìn)行分析:
- 圖8(a)中展示了即將展開
E'的語法分析棧,此時(shí)棧頂是E'的棧記錄,緊跟其后的是E'的綜合記錄,假設(shè)E'.inh已經(jīng)被拷貝給了E1'的動(dòng)作記錄中的EQUOTEval; - 圖8(b)用產(chǎn)生式體
+TE1'替換了E'。一開始,+的棧記錄在棧頂,它將和輸入進(jìn)行匹配,在匹配完成后,它被彈出棧頂,T的棧記錄變?yōu)闂m?#xff1b; - 由于
T的棧記錄位于棧頂,因此會(huì)用某個(gè)產(chǎn)生式對(duì)符號(hào)T進(jìn)行展開,當(dāng)這個(gè)展開過程結(jié)束后,T的綜合記錄將位于棧頂,并且該記錄中的val屬性值已經(jīng)被賦值了; - 由于
E1'的繼承屬性依賴于T.val,因此在將T的綜合記錄彈出棧頂之前,還需要把T.val拷貝給E1'的動(dòng)作記錄中的Tval; - 此時(shí)
E1'的動(dòng)作記錄位于棧頂,它使用兩個(gè)拷貝的屬性值計(jì)算E1'.inh,計(jì)算的結(jié)果被放入E1'的棧記錄中; - 由于
E1'的棧記錄位于棧頂,因此會(huì)用某個(gè)產(chǎn)生式對(duì)符號(hào)E1'進(jìn)行展開,當(dāng)這個(gè)展開過程結(jié)束后,E1'的綜合記錄將位于棧頂,并且該記錄中的syn屬性值已經(jīng)被賦值了; E1'的綜合記錄將E1'.syn賦值給E'.syn,到這里,用產(chǎn)生式體+TE1'展開E'的過程結(jié)束。
L屬性的SDD與LR語法分析
我們可以使用自底向上的方法來完成任何可以用自頂向下方式完成的翻譯過程,也就是說,對(duì)一個(gè)基礎(chǔ)文法為L(zhǎng)L文法的L屬性的SDD,我們可以修改這個(gè)文法,使得可以在LR語法分析過程中實(shí)現(xiàn)這個(gè)新文法上的SDD。具體的方法包括:
- 將給定的基礎(chǔ)文法為L(zhǎng)L文法的L屬性的SDD轉(zhuǎn)換成SDT,這樣的SDT在每個(gè)非終結(jié)符號(hào)之前放置語義動(dòng)作計(jì)算它的繼承屬性,并且在產(chǎn)生式最右端放置一個(gè)語義動(dòng)作計(jì)算綜合屬性;
- 對(duì)每個(gè)內(nèi)嵌的語義動(dòng)作,向這個(gè)文法中引入一個(gè)標(biāo)記非終結(jié)符號(hào)來替換它。每個(gè)這樣的位置都有一個(gè)不同的標(biāo)記,并且對(duì)于任意一個(gè)標(biāo)記M都有一個(gè)產(chǎn)生式M→ε;
- 如果標(biāo)記非終結(jié)符號(hào)M在某個(gè)產(chǎn)生式A→α{a}β中替換了語義動(dòng)作a,對(duì)a進(jìn)行修改得到a’,并且將a’關(guān)聯(lián)到M→ε上。這個(gè)動(dòng)作a’將動(dòng)作a需要的A或α中符號(hào)的任何屬性作為M的繼承屬性進(jìn)行拷貝,并且按照a中的方法計(jì)算各個(gè)屬性,計(jì)算得到的屬性將作為M的綜合屬性。
對(duì)表2中的SDD,先將其轉(zhuǎn)換成SDT:
以這個(gè)SDT中的產(chǎn)生式E'→+TE1'為例,它的LR語法分析過程如下:
圖9(a)中首先對(duì)產(chǎn)生式E'→+TE1'進(jìn)行變換,引入了一個(gè)標(biāo)記非終結(jié)符號(hào)M對(duì)嵌入的動(dòng)作{E1'.inh=E'.inh+T.val}進(jìn)行替換,其中,E'.inh和T.val將作為M的兩個(gè)繼承屬性,E1'.inh將作為M的綜合屬性。
圖9(b)中是LR語法分析棧,我們假設(shè)接下來的輸入能夠匹配產(chǎn)生式體+TME1'并且最終被歸約成產(chǎn)生式頭E'。在這個(gè)語法分析棧中,我們不知道棧頂?shù)挠涗洿砹四囊粋€(gè)文法符號(hào)(或者狀態(tài)),但是我們一定能確定這個(gè)記錄中的某個(gè)字段保存了E’的inh屬性。
圖9(c)中顯示了即將把產(chǎn)生式體+TME1'歸約成產(chǎn)生式頭E'之前的語法分析棧。我們對(duì)從圖9(b)到圖9(c)的LR語法分析過程進(jìn)行詳細(xì)的分析:
- 語法分析棧的初始狀態(tài)如圖9(b)所示,此時(shí)棧頂?shù)挠涗洷4媪薊’的inh屬性;
- 由于檢測(cè)到的下一個(gè)輸入的文法符號(hào)是”+”,因此識(shí)別出的產(chǎn)生式肯定是
E'→+TME1'; - 假設(shè)接下來的輸入被成功歸約成T,此時(shí)棧頂是代表非終結(jié)符號(hào)T的記錄,該記錄中保存了T的val屬性。由于我們已經(jīng)確定了產(chǎn)生式是
E'→+TME1',因此我們可以確定下一步是把ε歸約成M; - 在把ε歸約成M時(shí),棧頂是代表標(biāo)記非終結(jié)符號(hào)M的記錄,并且會(huì)執(zhí)行一個(gè)動(dòng)作,這個(gè)動(dòng)作把E.inh(位于stack[top-3]的記錄中)和T.val(位于stack[top-1]的記錄中)分別拷貝給M的i和j屬性,同時(shí)使用屬性i和j計(jì)算它的綜合屬性s,屬性s實(shí)際上是下一個(gè)非終結(jié)符號(hào)E1’的繼承屬性;
- 假設(shè)接下來的輸入被成功歸約成E1’,此時(shí)棧頂是代表非終結(jié)符號(hào)E1’的記錄,計(jì)算E1’的syn屬性時(shí)用到的E1’的inh屬性是從代表M的記錄中拿到的,這種情況下,代表M的記錄相當(dāng)于圖9(b)中棧頂?shù)挠涗?#xff0c;唯一的區(qū)別是這里我們能夠知道保存E1’.inh的記錄是代表M的記錄;
- 由于句柄
+TME1'已經(jīng)位于棧頂,因此我們可以把+TME1'歸約成E',此時(shí)會(huì)執(zhí)行一個(gè)動(dòng)作,這個(gè)動(dòng)作把E1’.syn拷貝到stack[top-3]記錄中,并執(zhí)行3次出棧操作(實(shí)際上是4次出棧操作和1次入棧操作)。到這里,從圖9(b)到圖9(c)的LR語法分析過程結(jié)束。
歡迎關(guān)注微信公眾號(hào)fightingZh?(?>?<?)?
總結(jié)
- 上一篇: Codeforces Round #63
- 下一篇: Linux/macOS 安装 Kaldi