附錄A 一個(gè)完成的編譯器前端程序_第1頁
附錄A 一個(gè)完成的編譯器前端程序_第2頁
附錄A 一個(gè)完成的編譯器前端程序_第3頁
附錄A 一個(gè)完成的編譯器前端程序_第4頁
附錄A 一個(gè)完成的編譯器前端程序_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

附錄A一個(gè)完成的編譯器前端程序這個(gè)附錄給出了一個(gè)完整的編譯器前端程序,它是基于2.5-2.8節(jié)中非正式描述的簡單編譯器編寫的。和第二章的主要不同之處在于這個(gè)前端象6.6節(jié)中描述的那樣為布爾表達(dá)式生成跳轉(zhuǎn)代碼。我們首先給出源語言的語法。描述這個(gè)語法所用的文法需要進(jìn)行調(diào)整,以適應(yīng)自頂向下的語法分析技術(shù)。這個(gè)翻譯器的Java代碼由五個(gè)包組成:main、lexer、symbol、parser和inter。包inter中包含的類處理用抽象語法表示的語言結(jié)構(gòu)。因?yàn)檎Z法分析器的代碼和其它各個(gè)包交互,它將在最后描述。每個(gè)包被存放在一個(gè)獨(dú)立的目錄中,每個(gè)類都有一個(gè)單獨(dú)的文件。作為語法分析器的輸入時(shí),源程序就是一個(gè)由詞法單元組成的流,因此面向?qū)ο筇匦院驼Z法分析器的代碼之間沒有什么關(guān)系。當(dāng)由語法分析器輸出時(shí),源程序就是一棵抽象語法樹,樹中的結(jié)構(gòu)或結(jié)點(diǎn)被實(shí)現(xiàn)為對象。這些對象負(fù)責(zé)處理下列所有的事情:構(gòu)造一個(gè)抽象語法樹結(jié)點(diǎn),類型檢查,生成三地址中間代碼(見包inter)。A.1源語言這個(gè)語言的一個(gè)程序由一個(gè)塊組成,該塊中包含了可選的聲明和語句。記號basic表示基本類型。(見原文)把賦值當(dāng)作一個(gè)語句,而不是表達(dá)式中的運(yùn)算,進(jìn)行處理可以簡化翻譯工作。面向?qū)ο笈c面向步驟面向?qū)ο笈c面向步驟在一個(gè)面向?qū)ο蠓椒ㄖ?,一個(gè)構(gòu)造的所有代碼都集中在這個(gè)構(gòu)造對應(yīng)的類中。但是在面向步驟的方法中就不一樣,這個(gè)方法中的代碼是按照步驟進(jìn)行組織的,因此一個(gè)類型檢查子過程中對每個(gè)構(gòu)造都有一個(gè)case分支,且一個(gè)代碼生成子過程對每個(gè)構(gòu)造也都有一個(gè)case分支,等等。這兩者的折中之處在于:使用面向?qū)ο蠓椒〞沟酶淖兓蛟黾右粋€(gè)構(gòu)造,比如“for”語句,變得較容易;而使用面向步驟的方法會使得改變或增加一個(gè)步驟,比如類型檢查,變得比較容易。使用對象來實(shí)現(xiàn)時(shí),增加一個(gè)新的構(gòu)造可以通過寫一個(gè)自包含的類來實(shí)現(xiàn),但是如果要改變一個(gè)步驟,比如插入類型強(qiáng)制轉(zhuǎn)換的代碼,就需要改變所有受影響的類。使用面向步驟的方式時(shí),增加一個(gè)新構(gòu)造可能會引起各個(gè)步驟中的多個(gè)過程的改變。(見原文)表達(dá)式的產(chǎn)生式處理了運(yùn)算符的結(jié)合率和優(yōu)先級。它們對每個(gè)優(yōu)先級級別都使用了一個(gè)非終結(jié)符號,而非終結(jié)符號factor被用來表示括號中的表達(dá)式、標(biāo)識符、數(shù)組引用和常量。(見原文)A.2Main程序的執(zhí)行從類Main的例程main中開始。例程main創(chuàng)建了一個(gè)詞法分析器和一個(gè)語法分析器,然后調(diào)用語法分析器中的例程program。(見原文)A.3詞法分析器包lexer是2.6.5節(jié)中的詞法分析器的代碼的擴(kuò)展。類Tag定義了各個(gè)詞法單元對應(yīng)的常量:(見原文)其中的三個(gè)常量,INDEX、MINUS和TEMP不是詞法單元;它們將在抽象語法樹中使用。類Token和Num和2.6.5節(jié)的相同,但是增加了例程toString:(見原文)類Word用于管理保留字,標(biāo)識符,和象&&這樣的復(fù)合詞法單元的詞素。它也可以被用來管理在中間代碼中運(yùn)算符的寫法;比如單目減號,源文本中的-2的中間形式是minus2。(見原文)類Real用于處理浮點(diǎn)數(shù):(見原文)如我們在2.6.5節(jié)中討論的,類Lexer的主例程,即函數(shù)scan,識別數(shù)字、標(biāo)識符和保留字。類Lexer中的第9到13行保留了選定的關(guān)鍵字。第14-16行保留了在其它地方定義的對象的詞素。對象Word.True和Word.False在類Word中定義。對應(yīng)于基本類型int、char、bool和float的對象在類Type中定義。類Type是Word的一個(gè)子類。類Type來自包symbols。(見原文)函數(shù)readch()(第18行)被用來把下一個(gè)輸入字符讀到變量peek中。名字readch被復(fù)用,或者說重載,(第19-24行)來幫助識別復(fù)合的詞法單元。比如,一看到了輸入字符<,調(diào)用readch(“=”)就會把下一個(gè)字符讀入peek,并檢查它是否=。(見原文)函數(shù)scan一開始首先略過所有的空白字符(第26-30行)。它首先試圖識別象<=這樣的復(fù)合詞法單元(第31-34行)和象365及3.14這樣的數(shù)字(第45-58行)。如果不成功,它就試圖讀入一個(gè)字符串(第59-70行)。(見原文)最后,peek中的任意字符都被作為詞法單元返回(第71到72行)。(見原文)A.4符號表和類型包symbols實(shí)現(xiàn)了符號表和類型。類Env實(shí)質(zhì)上和圖2.37中的代碼一樣。盡管類Lexer把字符串映射為字,類Env把字符串詞法單元映射為類Id的對象。類Id和其它的對應(yīng)于表達(dá)式和語句的類一起都在包Inter中定義。(見原文)我們把類Type定義為類Word的子類,因?yàn)橄骾nt這樣的基本類型名字就是保留字,將被詞法分析器從詞素映射為適當(dāng)?shù)膶ο?。對?yīng)于基本類型的對象是Type.Int、Type.Float、Type.Char和Type.Bool(第7-10行)。這些對象從超類中繼承了域tag,相應(yīng)的值被設(shè)置為Tag.BASIC,因此語法分析器以同樣的方式處理它們。(見原文)函數(shù)numeric(第11到14行)和max(第15到20行)可用于類型轉(zhuǎn)換。(見原文)在兩個(gè)“數(shù)字”類型之間允許進(jìn)行類型轉(zhuǎn)換,“數(shù)字”類型包括Type.Char、Type.Int和Type.Float。當(dāng)一個(gè)算術(shù)運(yùn)算符被應(yīng)用于兩個(gè)數(shù)字類型時(shí),結(jié)果類型是這兩個(gè)類型的“max”值。數(shù)組是這個(gè)源語言中唯一的構(gòu)造類型。在第7行中調(diào)用super設(shè)置域width的值。這個(gè)值在計(jì)算地址時(shí)是必不可少的。它同時(shí)也把lexeme和tok設(shè)置為缺省值,這些值沒有被使用。(見原文)A.5表達(dá)式的中間代碼包inter包含了Node的類層次結(jié)構(gòu)。Node有兩個(gè)子類:對應(yīng)于表達(dá)式結(jié)點(diǎn)的Expr和對應(yīng)于語句結(jié)點(diǎn)的Stmt。本節(jié)介紹Expr和它的子類。Expr的某些例程處理布爾表達(dá)式和跳轉(zhuǎn)代碼;這些例程將會和Expr的其它子類在A.6節(jié)中討論。抽象語法樹中的結(jié)點(diǎn)被實(shí)現(xiàn)為類Node的對象。為了報(bào)告錯(cuò)誤,域lexline(文件Node.java的第四行)保存了本結(jié)點(diǎn)對應(yīng)的構(gòu)造在源程序中的行號。第7到10行被用來生成三地址代碼。(見原文)表達(dá)式構(gòu)造被實(shí)現(xiàn)為Expr的子類。類Expr包含域op和type(文件Expr.java的第4-5行),分別表示了一個(gè)結(jié)點(diǎn)上的運(yùn)算符和類型。(見原文)例程gen(第7行)返回了一個(gè)“項(xiàng)”,該項(xiàng)可以成為一個(gè)三地址指令的右部。給定一個(gè)表達(dá)式E=E1+E2,例程gen返回一個(gè)項(xiàng)x1+x2,其中x1和x2分別是存放E1和E2值的地址。如果這個(gè)對象是一個(gè)地址,就可以返回this值;Expr的子類通常會重新實(shí)現(xiàn)gen。例程reduce(第8行)把一個(gè)表達(dá)式計(jì)算,或者說“歸約”,成為一個(gè)單一的地址;就是說,它返回一個(gè)常量,一個(gè)標(biāo)識符,或者一個(gè)臨時(shí)名字。給定一個(gè)表達(dá)式E,例程reduce返回一個(gè)存放E的值的臨時(shí)變量t。如果這個(gè)對象是一個(gè)地址,那么this仍然是正確的返回值。我們把對例程jumping和emitjumps(第9-18行)的討論推遲到A.6中進(jìn)行;它們?yōu)閎oolean表達(dá)式生成跳轉(zhuǎn)代碼。(見原文)因?yàn)橐粋€(gè)標(biāo)識符就是一個(gè)地址,類Id從類Expr中繼承了gen和reduce的缺省實(shí)現(xiàn)。(見原文)對應(yīng)于一個(gè)標(biāo)識符的類Id的結(jié)點(diǎn)是一個(gè)葉子結(jié)點(diǎn)。函數(shù)調(diào)用super(id,p)(文件Id.java的第5行)把id和p分別保存在繼承得到的域op和type中。域offset(第4行)保存了這個(gè)標(biāo)識符的相對地址。類Op提供了reduce的一個(gè)實(shí)現(xiàn)(文件Op.java的第5-10行)。這個(gè)類的子類包括:表示算術(shù)運(yùn)算符的子類Arith,表示單目運(yùn)算符的子類Unary,和表示數(shù)組訪問的子類Access。這些子類都繼承了這個(gè)實(shí)現(xiàn)。在每種情況下,reduce調(diào)用gen來生成一個(gè)項(xiàng),生成一個(gè)指令把這個(gè)項(xiàng)賦值給一個(gè)新的臨時(shí)名字,并返回這個(gè)臨時(shí)名字。(見原文)類Arith實(shí)現(xiàn)了雙目運(yùn)算符,比如+和*。構(gòu)造函數(shù)Arith首先調(diào)用super(tok,null)(第6行),其中tok是一個(gè)表示該運(yùn)算符的詞法單元,null是類型的占位符。相應(yīng)的類型在第7行使用函數(shù)Type.max來確定,這個(gè)函數(shù)檢查兩個(gè)運(yùn)算分量是否可以被類型強(qiáng)制為一個(gè)公共的數(shù)字類型;Type.max的代碼在A.4節(jié)中給出。如果它們能夠被類型強(qiáng)制,type就被設(shè)置為結(jié)果類型;否則就報(bào)告一個(gè)類型錯(cuò)誤(第8行)。這個(gè)簡單編譯器檢查類型,但是它并不插入類型轉(zhuǎn)換代碼。(見原文)例程gen把表達(dá)式的子表達(dá)式歸約為地址,并將表達(dá)式的運(yùn)算符作用于這些地址(文件Arith.java的第11行),從而構(gòu)造出了一個(gè)三地址指令的右部。比如,假設(shè)gen在a+b*c的根部被調(diào)用。其中對reduce的調(diào)用返回a作為子表達(dá)式a的地址,并返回t作為b*c的地址。同時(shí),reduce還生成指令t=b*c。例程gen返回了一個(gè)新的Arith結(jié)點(diǎn),其中的運(yùn)算符是*,而運(yùn)算分量是地址a和t。為了報(bào)告錯(cuò)誤,在構(gòu)造一個(gè)結(jié)點(diǎn)時(shí),類Node中的域lexline記錄了當(dāng)前的文本行號。我們把在中間代碼生成過程中構(gòu)造新的結(jié)點(diǎn)時(shí)跟蹤行號的任務(wù)留給讀者。值得注意的是,和所有其它表達(dá)式一樣,臨時(shí)名字也有類型。因此,構(gòu)造函數(shù)Temp被調(diào)用時(shí)有一個(gè)類型參數(shù)(文件Temp.java的第6行)。另一種可行的方法是讓這個(gè)構(gòu)造函數(shù)以一個(gè)表達(dá)式結(jié)點(diǎn)作為參數(shù),這樣它就可以復(fù)制這個(gè)表達(dá)式結(jié)點(diǎn)的類型和文本位置。(見原文)類Unary和類Arith對應(yīng),但是處理的是單目運(yùn)算符:(見原文)A.6布爾表達(dá)式的跳轉(zhuǎn)代碼布爾表達(dá)式B的跳轉(zhuǎn)代碼由例程jumping生成。這個(gè)例程的參數(shù)是兩個(gè)標(biāo)號t和f,它們分別被稱為表達(dá)式的true出口和false出口。如果B的值為真,代碼中就包含一個(gè)目標(biāo)為t的跳轉(zhuǎn)指令,如果B求值為假,就有一個(gè)目標(biāo)為f的指令。按照慣例,特殊標(biāo)號0表示控制流從B穿越,到達(dá)B的代碼之后的下一個(gè)指令。我們從類Constant開始。第4行上的構(gòu)造函數(shù)Constant的參數(shù)是一個(gè)詞法單元tok和一個(gè)類型p。它在抽象語法樹中構(gòu)造出一個(gè)標(biāo)號為tok、類型為p的葉子結(jié)點(diǎn)。為方便起見,構(gòu)造函數(shù)Constant被重載(第5行),重載后的構(gòu)造函數(shù)可以根據(jù)一個(gè)整數(shù)創(chuàng)建一個(gè)常量對象。(見原文)例程jumping(文件Constant.java的第9-12行)有兩個(gè)參數(shù):標(biāo)號t和f。如果這個(gè)常量是靜態(tài)對象True(第7行中定義)而t不是特殊標(biāo)號0,那么就會生成一個(gè)目標(biāo)為t的跳轉(zhuǎn)指令。否則,如果這是對象False(第8行中定義)且f非零,那么就會生成一個(gè)目標(biāo)為f的跳轉(zhuǎn)指令。類Logical為類Or、And和Not提供了一些公用功能。域x和y(第4行)對應(yīng)于一個(gè)邏輯運(yùn)算符的運(yùn)算分量(雖然類Not實(shí)現(xiàn)了一個(gè)單目運(yùn)算符,為方便起見,我們還是把它當(dāng)作Logical的子類)。構(gòu)造函數(shù)Logical(tok,a,b)(第5-10行)構(gòu)造出了一個(gè)語法樹的結(jié)點(diǎn),其運(yùn)算符為tok而運(yùn)算分量為a和b。在完成這些工作時(shí),它調(diào)用check來保證a和b都是布爾類型。例程gen將會在本節(jié)的最后討論。(見原文)在類Or中,例程jumping(第5-10行)生成了一個(gè)布爾表達(dá)式B=B1||B2的跳轉(zhuǎn)代碼。當(dāng)前假設(shè)B的true出口t和false出口f都不是特殊標(biāo)號0。因?yàn)槿绻鸅1為真,B必然為真,所以B1的true出口必然是t,而它的false出口對應(yīng)于B2的第一條指令。B2的true和false出口和B的相應(yīng)出口相同。(見原文)在一般情況下,B的true出口t可能是特殊標(biāo)號0。變量label(文件Or.java的第6行)保證了B1的true出口被正確地設(shè)置為B的代碼的結(jié)尾處。如果t為0,那么label被設(shè)置為一個(gè)新的標(biāo)號,并在B1和B2的代碼被生成后再生成這個(gè)新標(biāo)號。類And的代碼和Or的代碼類似。(見原文)雖然類Not實(shí)現(xiàn)的是一個(gè)單目運(yùn)算符,這個(gè)類和其它邏輯運(yùn)算符之間仍然具有相當(dāng)多的共同之處,因此我們把它作為Logical的一個(gè)子類。它的超類具有兩個(gè)運(yùn)算分量,因此在第4行對super的調(diào)用中b出現(xiàn)了兩次。在第5-6行的例程中,只有y(文件Logical.java的第4行上聲明)被用到。在第5行,例程jumping僅僅把true出口和false出口對調(diào),調(diào)用y.jumping。(見原文)類Rel實(shí)現(xiàn)了運(yùn)算符<、<=、==、!=、>=和>。函數(shù)check(第5-9行)檢查兩個(gè)運(yùn)算分量是否具有相同的類型,并且不是數(shù)組類型。為簡單起見,這里不允許類型強(qiáng)制轉(zhuǎn)換。(見原文)例程jumping(文件Rel.java的第10-15行)首先為子表達(dá)式x和y生成代碼(第11-12行)。然后它調(diào)用例程emitjumps,這個(gè)例程在A.5節(jié)的文件Expr.java中的第10-18行中定義。如果t和f都不是特殊標(biāo)號0,那么emitjumps執(zhí)行下列代碼:(見原文)如果t或f是特殊標(biāo)號0,那么最多只會生成一個(gè)指令(同樣是來自文件Expr.java):(見原文)在生成類Access的代碼時(shí)演示了例程emitjumps的另一種用法。源語言允許把布爾值賦給標(biāo)識符和數(shù)組元素,因此一個(gè)布爾表達(dá)式可能是一個(gè)數(shù)組訪問。類Access有一個(gè)例程gen,用來生成“正?!贝a,另一個(gè)例程jumping用來生成跳轉(zhuǎn)代碼。例程jumping(第11行)在把這個(gè)數(shù)組訪問歸約為一個(gè)臨時(shí)變量后調(diào)用emitjumps。這個(gè)類的構(gòu)造函數(shù)(第6-9行)被調(diào)用時(shí)的參數(shù)為一個(gè)平坦化的數(shù)組a、一個(gè)下標(biāo)i和該數(shù)組的元素類型p。在生成數(shù)組地址計(jì)算代碼的過程中完成了類型檢查。(見原文)跳轉(zhuǎn)代碼還可以被用來返回一個(gè)布爾值。本節(jié)中較早描述的類Logical有一個(gè)例程gen(第15-24行)。這個(gè)例程返回一個(gè)臨時(shí)變量temp。這個(gè)變量的值由這個(gè)表達(dá)式的跳轉(zhuǎn)代碼中的控制流決定。在這個(gè)布爾表達(dá)式的true出口,temp被賦予true值;在false出口,temp被賦予false值。這個(gè)臨時(shí)變量在第17行聲明。這個(gè)表達(dá)式的跳轉(zhuǎn)代碼在第18行生成,其中的true出口是下一條指令,而false出口是一個(gè)新標(biāo)號f。下一條指令把true值賦給temp(第19行),后面緊跟目標(biāo)為新標(biāo)號a的跳轉(zhuǎn)指令(第20行)。第21行上的代碼生成標(biāo)號f和一個(gè)把false賦給temp的指令。這個(gè)代碼片段的結(jié)尾是標(biāo)號a,該標(biāo)號在第22行生成。最后,gen返回temp(第23行)。A.7語句的中間代碼每個(gè)語句構(gòu)造被實(shí)現(xiàn)為Stmt的一個(gè)子類。一個(gè)構(gòu)造的組成部分對應(yīng)的域是相應(yīng)子類的對象;例如,如我們將看到的,類While有一個(gè)對應(yīng)于測試表達(dá)式的域和一個(gè)子語句域。下面的類Stmt的代碼中的第3-4行處理抽象語法樹的構(gòu)造。構(gòu)造函數(shù)Stmt()不做任何事情,因?yàn)橄嚓P(guān)處理工作是在子類中完成的。靜態(tài)對象Stmt.Null(第4行)表示一個(gè)空的語句序列。(見原文)第5-7行處理三地址代碼的生成。例程gen被調(diào)用時(shí)兩個(gè)參數(shù)分別是標(biāo)號a和b,其中b標(biāo)記這個(gè)語句的代碼的開始處,而a標(biāo)記這個(gè)語句的代碼之后的第一個(gè)指令。例程gen(第5行)是子類中的gen例程的占位符。子類While和Do把它們的標(biāo)號a存放在域after(第6行)中。當(dāng)任何內(nèi)層的break語句要跳出這個(gè)外層構(gòu)造時(shí)就可以使用這些標(biāo)號。對象Stmt.Enclosing在語法分析時(shí)被用于跟蹤外圍構(gòu)造。(對于包含continue語句的源語言,我們可以使用同樣的方法來跟蹤一個(gè)continue語句的外層構(gòu)造。)類If的構(gòu)造函數(shù)為語句if(E)S構(gòu)造一個(gè)結(jié)點(diǎn)。域expr和stmt分別保存了E和S對應(yīng)的結(jié)點(diǎn)。請注意,小寫字母組成的expr是一個(gè)類型為Expr的域的名字;類似地,stmt是類型為Stmt的域的名字。(見原文)一個(gè)If對象的代碼包含了expr的跳轉(zhuǎn)代碼,然后是stmt的代碼。如A.6節(jié)中所討論的,第11行的調(diào)用expr.jumping(0,f)指明如果expr的值為真,控制流必須穿越expr的代碼;否則控制流必須轉(zhuǎn)向標(biāo)號a。類Else處理?xiàng)l件語句的else部分。它的實(shí)現(xiàn)和類If的實(shí)現(xiàn)類似:(見原文)一個(gè)While對象的構(gòu)造過程被分為兩個(gè)部分:構(gòu)造函數(shù)While()創(chuàng)建了一個(gè)子結(jié)點(diǎn)為空的結(jié)點(diǎn)(第5行);初始化函數(shù)int(x,s)把子結(jié)點(diǎn)expr設(shè)置成為x,子結(jié)點(diǎn)stmt設(shè)置成為s(第6-9行)。函數(shù)gen(b,a)被用于生成三地址代碼(第10-16行)。它和類If中的相應(yīng)函數(shù)gen()在本質(zhì)上有著相通之處。不同之處在于標(biāo)號a被保存在域after中(第11行),且stmt的代碼之后緊跟著一個(gè)目標(biāo)為b的跳轉(zhuǎn)指令(第15行)。這個(gè)指令使得while循環(huán)進(jìn)入下一次迭代。(見原文)類Do和類While非常相似。(見原文)類Set實(shí)現(xiàn)了左部為標(biāo)識符且右部為一個(gè)表達(dá)式的賦值語句。在類Set中的大部分代碼的目的是構(gòu)造一個(gè)結(jié)點(diǎn)和進(jìn)行類型檢查(第5-13行)。函數(shù)gen生成一個(gè)三地址指令(第14-16行)。(見原文)類SetElem實(shí)現(xiàn)了對數(shù)組元素的賦值。(見原文)類Seq實(shí)現(xiàn)了一個(gè)語句序列。在第6-7行上對空語句的測試是為了避免使用標(biāo)號。請注意,空語句Stmt.Null不會產(chǎn)生任何代碼,因?yàn)轭怱tmt中的例程gen不做任何處理。(見原文)一個(gè)break語句把控制流轉(zhuǎn)出它的外圍循環(huán)或外圍switch語句。類Break使用域stmt來保存它的外圍語句構(gòu)造(語法分析器保證Stmt.Enclosing表示了其外圍構(gòu)造對應(yīng)的語法樹結(jié)點(diǎn))。一個(gè)Break對象的代碼是一個(gè)目標(biāo)為標(biāo)號stmt.after的跳轉(zhuǎn)指令。這個(gè)標(biāo)號標(biāo)記了緊跟在stmt的代碼之后的指令。(見原文)A.8語法分析器語法分析器讀入一個(gè)由詞法單元組成的流,并調(diào)用適當(dāng)?shù)脑贏.5-A.7節(jié)中討論的構(gòu)造函數(shù),構(gòu)建出一顆抽象語法樹。當(dāng)前符號表按照2.7節(jié)中圖2.38的翻譯方案進(jìn)行處理。包parser包含一個(gè)類Parser:(見原文)和2.5節(jié)中的簡單表達(dá)式的翻譯器類似,類Parser對每個(gè)非終結(jié)符號有一個(gè)過程。消除A.1節(jié)中源語言文法中的左遞歸后可以得到一個(gè)新的文法。這些過程就是基于這個(gè)新文法創(chuàng)建的。語法分析過程首先調(diào)用了過程program,這個(gè)過程又調(diào)用了block()(第16行)來對輸入流進(jìn)行語法分析,并構(gòu)建出抽象語法樹。第17-18行生成了中間代碼。(見原文)對符號表的處理明確顯示在過程block中。另一種很具有吸引力的方法是向類Env中添加例程push和pop,而當(dāng)前的符號表可以通過一個(gè)靜態(tài)變量Env.top來訪問。變量top(第5行中聲明)存放了最頂層的符號表;變量savedEnv(第21行)是一個(gè)指向前面的另一種很具有吸引力的方法是向類Env中添加例程push和pop,而當(dāng)前的符號表可以通過一個(gè)靜態(tài)變量Env.top來訪問。(見原文)程序中的聲明會被處理為符號表中有關(guān)標(biāo)識符的條目(見第36行)。雖然這里沒有顯示,聲明還可能生成在運(yùn)行時(shí)刻為標(biāo)識符保留存儲空間的指令。(見原文)過程stmt有一個(gè)switch語句。這個(gè)語句的各個(gè)case分支對應(yīng)于非終結(jié)符號Stmt的各個(gè)產(chǎn)生式。每個(gè)case分支都使用A.7節(jié)中討論的構(gòu)造函數(shù)來建立某個(gè)構(gòu)造對應(yīng)的結(jié)點(diǎn)。當(dāng)語法分析器碰到while語句和do語句的開始關(guān)鍵字的時(shí)候,就會創(chuàng)建這些語句的結(jié)點(diǎn)。這些結(jié)點(diǎn)在相應(yīng)語句被語法分析完畢之前就構(gòu)造出來。這可以使得任何內(nèi)層的break語句可以回指到它的外圍循環(huán)語句。當(dāng)出現(xiàn)嵌套的循環(huán)時(shí),我們通過使用類Stmt中的變量Stmt.Enclosing和saveStmt(在第52行聲明)來保存當(dāng)前的外圍循環(huán)的。(見原文)為方便起見,賦值語句的代碼出現(xiàn)在一個(gè)輔助過程assign中。(見原文)對算術(shù)運(yùn)算和布爾表達(dá)式的分析掃描很相似。在每種情況下都會創(chuàng)建一個(gè)正確的抽象語法樹結(jié)點(diǎn)。如A.5和A.6節(jié)所討論的,這兩者的代碼生成方法有所不同。(見原文)在語法分析器中的其余代碼處理表達(dá)式“因子”。輔助過程offset按照6.4.3節(jié)中討論的方法,處理數(shù)組地址計(jì)算代碼的生成。(見原文)A.9創(chuàng)建前端程序這個(gè)編譯器的各個(gè)包的代碼存放在五個(gè)目錄中:main、lexer、symbol、parser和inter。創(chuàng)建編譯器的命令行根據(jù)系統(tǒng)的不同而不同。下面是創(chuàng)建編譯器的UNIX系統(tǒng)指令:(見原文)上面的javac命令為每個(gè)類創(chuàng)建了.class文件。要練習(xí)使用我們的翻譯器,只需要輸入javamain.Main,后面跟上將要被翻譯的源程序,比如文件test中的內(nèi)容:(見原文)對于這個(gè)輸入,這個(gè)前端程序輸出:(見原文)嘗試一下。附錄B尋找線形獨(dú)立解算法B.1:找出的最大的線性獨(dú)立解集合,并將它們表示為矩陣B的各行。輸入:一個(gè)M×N的矩陣A。輸出:由的各個(gè)線性獨(dú)立解組成的矩陣B。方法:算法以偽代碼的方式在下面給出。請注意X[y]表示矩陣X的第y行,X[y:z]表示矩陣X的第y到z行,而X[y:z][u:v]表示矩陣X中的第y到z行及第u到v列的方塊?!跻韵聝H順序翻譯偽代碼中的文字部分第990頁/*ann-by-nindentitymatrix*//*一個(gè)n×n的單元矩陣*//*1.MakeM[r0:r’-1][c0:c’-1]intoadiagonalmatrixwithpositivediagonalentriesandM[r’:n][c0:m]=0.M[r’:n]aresolution.*//*使M[r0:r’-1][c0:c’-1]成為一個(gè)對角線元素為正的對角矩陣,并且滿足M[r’:n][c0:m]=0。M[r’:n]為解。*/while(thereexistsM[r][c]!=0suchthatr-r’andc-c’areboth>=0) while(存在M[r][c]≠0使得r-r’和c-c’都≥0)MovepivotM[r][c]toM[r’][c’]byrowandcolumninterchange 通過行列互換,把中心點(diǎn)M[r][c]移動到M[r’][c’]Interchangerowrwithrowr’inB 把B中的第r行和第r’行互換第991頁/*2.FindasolutionbesidesM[r’:n].Itmustbeanonnegativecombinationof*/找出M[r’:n]之外的一個(gè)解,這個(gè)解一定是M[r0:r’-1][c0:m]的一個(gè)非負(fù)組合Findkr0,…,kr’-1≥0suchthat 找出kr0,…,kr’-1≥0使得if(thereexists

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論