




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、編譯原理編譯原理課程實(shí)驗(yàn)指導(dǎo)書課程實(shí)驗(yàn)指導(dǎo)書(Compiler Principle) 1目錄目錄序言序言.1一、實(shí)驗(yàn)安排一、實(shí)驗(yàn)安排.2第一階段:編譯器的詞法分析第一階段:編譯器的詞法分析.2第二階段:編譯器的語(yǔ)法分析第二階段:編譯器的語(yǔ)法分析.2第三階段:編譯器的代碼生成第三階段:編譯器的代碼生成.3二、考核方式及評(píng)定標(biāo)準(zhǔn)二、考核方式及評(píng)定標(biāo)準(zhǔn).4三、參考資料與編譯器分析三、參考資料與編譯器分析.4第一部分第一部分 PL 語(yǔ)言及其編譯器語(yǔ)言及其編譯器.41. PL 語(yǔ)言介紹 .41.1 PL 語(yǔ)言的語(yǔ)法圖.52. PL 語(yǔ)言編譯器 .82.1 詞法分析.92.2 語(yǔ)法分析.92.3 語(yǔ)義分析
2、.112.4 代碼生成.112.5 代碼執(zhí)行.132.6 錯(cuò)誤診斷處理.152.7 符號(hào)表管理.172.8 其他.18第二部分第二部分 上機(jī)實(shí)驗(yàn)要求上機(jī)實(shí)驗(yàn)要求.19第三部分第三部分 PL 語(yǔ)言編譯器源程序與示例語(yǔ)言編譯器源程序與示例.211示例與結(jié)果表示示例與結(jié)果表示.211.1 PL 語(yǔ)言源程序.211.2 生成的代碼(片段).232. PL 語(yǔ)言編譯器源程序語(yǔ)言編譯器源程序.231序言序言本編譯原理實(shí)驗(yàn),其目的是讓大家動(dòng)手設(shè)計(jì)和實(shí)現(xiàn)一個(gè)規(guī)模適中的語(yǔ)言的編譯器,該編譯器不僅涉及編譯程序的各個(gè)階段,而且也強(qiáng)調(diào)了編譯的總體設(shè)計(jì)、各個(gè)階段的接口安排等等。通過(guò)上機(jī)實(shí)踐,來(lái)設(shè)計(jì)這個(gè)相對(duì)完整的編譯器,
3、一方面可以使同學(xué)們?cè)黾訉?duì)編譯程序的整體認(rèn)識(shí)和了解鞏固編譯原理課程所學(xué)知識(shí),另一方面,通過(guò)上機(jī)練習(xí),學(xué)生也可以學(xué)到很多程序調(diào)試技巧和設(shè)計(jì)大型程序一般的原則,如模塊接口的協(xié)調(diào),數(shù)據(jù)結(jié)構(gòu)的合理選擇等等。為了使學(xué)生能盡早動(dòng)手實(shí)踐,我們建議把實(shí)踐分成三部分,首先閱讀本教程第一部分,在這部分就 PL 語(yǔ)言的語(yǔ)法及其編譯程序的各個(gè)階段作了簡(jiǎn)單介紹,以便對(duì) PL 編譯程序有個(gè)初步的印象。其次要認(rèn)真閱讀理解第三部分所給出的 PL編譯器源程序及示例,使上一階段的初步印象得以加深、具體化。最后按照第二部分的實(shí)驗(yàn)要求擴(kuò)充 PL 語(yǔ)言的功能并加以實(shí)現(xiàn)。具體操作時(shí)分成三個(gè)階段:詞法分析、語(yǔ)法分析及代碼生成。最后再統(tǒng)一組裝
4、成一個(gè)完整的 PL 編譯器,并適當(dāng)進(jìn)行改進(jìn)、補(bǔ)充。2一、實(shí)驗(yàn)安排一、實(shí)驗(yàn)安排第一階段:編譯器的詞法分析第一階段:編譯器的詞法分析學(xué)時(shí):學(xué)時(shí):2(一) 、實(shí)驗(yàn)?zāi)康模和ㄟ^(guò)閱讀 PL 的語(yǔ)法圖,設(shè)計(jì)、編制并調(diào)試一個(gè) PL 詞法分析程序,加深對(duì)詞法分析原理的理解。(二) 、實(shí)驗(yàn)內(nèi)容:PL 的詞法分析器將要完成以下工作:(1)跳過(guò)分隔符(如空格,回車,制表符) ;(2)識(shí)別諸如 begin,end,if,while 等保留字;(3)識(shí)別非保留字的一般標(biāo)識(shí)符,此標(biāo)識(shí)符值(字符序列)賦給全局量 id,而全局量 sym 賦值為 SYM_IDENTIFIER。(4)識(shí)別數(shù)字序列,當(dāng)前值賦給全局量 NUM,sym
5、 則置為 SYM_NUMBER;(5)識(shí)別:=,=之類的特殊符號(hào),全局量 sym 則分別被賦值為SYM_BECOMES,SYM_LEQ,SYM_GEQ 等。相關(guān)過(guò)程(函數(shù))有 getsym(),getch(),其中 getch()為獲取單個(gè)字符的過(guò)程,除此之外,它還完成:(1)識(shí)別且跳過(guò)行結(jié)束符;(2)將輸入源文件復(fù)寫到輸出文件;(3)產(chǎn)生一份程序列表,輸出相應(yīng)行號(hào)或指令計(jì)數(shù)器的值。第二階段:編譯器的語(yǔ)法分析第二階段:編譯器的語(yǔ)法分析學(xué)時(shí):學(xué)時(shí):4(一) 、實(shí)驗(yàn)?zāi)康模赫莆?PL 語(yǔ)言編譯器的語(yǔ)法分析程序設(shè)計(jì)與 LL(1)文法應(yīng)用的實(shí)現(xiàn)方法。(二) 、實(shí)驗(yàn)內(nèi)容:采用遞歸下降的方法來(lái)設(shè)計(jì) PL/0
6、 編譯器,證明 PL/0 語(yǔ)言屬于 LL(1)文法。然后結(jié)合語(yǔ)法圖編寫(遞歸下降)語(yǔ)法分析程序的一般方法,具體方面有:(1)用合適的替換將語(yǔ)法約化成盡可能少的單個(gè)圖;(2)將每一個(gè)圖按下面的規(guī)則(3)-(7)翻譯成一個(gè)過(guò)程說(shuō)明;(3)順序圖對(duì)應(yīng)復(fù)合語(yǔ)句:(4)選擇:(5)循環(huán)(6)表示另一個(gè)圖 A 的圖:(7)表示終結(jié)符的單元圖:相關(guān)過(guò)程有:block(), constdeclaration(), vardeclaration(), statement(), condition(), expression(), term(), factor()等。并畫出它們之間依賴關(guān)系圖,并在此基礎(chǔ)上實(shí)現(xiàn)程序
7、的編制。3并適當(dāng)進(jìn)行語(yǔ)義分析的相關(guān)檢查:(1)是否存在標(biāo)識(shí)符先引用未聲明的情況;(2)是否存在己聲明的標(biāo)識(shí)符的錯(cuò)誤引用;(3)是否存在一般標(biāo)識(shí)符的多重聲明。第三階段:編譯器的代碼生成第三階段:編譯器的代碼生成學(xué)時(shí):學(xué)時(shí):2(一) 、實(shí)驗(yàn)?zāi)康模赫莆?PL 語(yǔ)言編譯器的中間代碼生成的程序分析與實(shí)現(xiàn)方法,并能對(duì)錯(cuò)誤進(jìn)行分析與處理。(二) 、實(shí)驗(yàn)內(nèi)容:為了使我們的編譯程序保持適當(dāng)簡(jiǎn)單的水平,不致陷入與本課程無(wú)關(guān)的實(shí)際機(jī)器的特有性質(zhì)的考慮中去,我們假想有臺(tái)適合 PL 程序運(yùn)行的計(jì)算機(jī),我們稱之為 PL 處理機(jī)。PL 處理機(jī)順序解釋生成的目標(biāo)代碼。PL 處理機(jī)的指令集根據(jù) PL 語(yǔ)言的要求而設(shè)計(jì),它包括以
8、下的指令:(1)LIT /* 將常數(shù)置于棧頂 */(2)LOD /* 將變量值置于棧頂 */(3)STO /* 將棧頂?shù)闹蒂x與某變量 */(4)CAL /* 用于過(guò)程調(diào)用的指令 */(5)INT /* 在數(shù)據(jù)棧中分配存貯空間 */(6)JMP, JPC /* 用于 if, while 語(yǔ)句的條件或無(wú)條件控制轉(zhuǎn)移指令 */(7)OPR /* 一組算術(shù)或邏輯運(yùn)算指令 */上述指令的格式由三部分組成:FLA其中,f, l, a 的含義見下表:FLaINT常 量LIT常 量LOD層次差數(shù)據(jù)地址STO層次差數(shù)據(jù)地址CAL層次差程序地址JMP程序地址JPC程序地址OPR運(yùn)算類別PL 的編譯程序?yàn)槊恳粭l P
9、L 源程序的可執(zhí)行語(yǔ)句生成后綴式目標(biāo)代碼。另一方面,發(fā)現(xiàn)錯(cuò)誤,并給出合適的診斷信息且繼續(xù)編譯下去從而發(fā)現(xiàn)更多的錯(cuò)誤,對(duì)于編譯程序而言是完全必要的。結(jié)合關(guān)鍵字規(guī)則、鎮(zhèn)定規(guī)則,采用策略:先用一些明顯的關(guān)鍵符號(hào)給它賦初值,然后隨著分析子目標(biāo)的層次深入,逐步補(bǔ)充別的合法符號(hào)。并編寫子程序去驗(yàn)證之。表 1 PL 處理機(jī)指令4二、考核方式及評(píng)定標(biāo)準(zhǔn)二、考核方式及評(píng)定標(biāo)準(zhǔn)上機(jī)實(shí)驗(yàn)要求對(duì) PL 語(yǔ)言及其編譯器進(jìn)行實(shí)現(xiàn)及擴(kuò)充、修改。每個(gè)擴(kuò)充或修改方式可得到不同的分?jǐn)?shù),滿分為 100 分。完成上機(jī)作業(yè)后,必須提交下列文檔:(1) 修改后的 PL 語(yǔ)言文本。包含詞法分析(正規(guī)式) ,語(yǔ)法分析(BNF) 。(2) 有
10、關(guān)修改后的 PL 編譯/解釋器的說(shuō)明。詳細(xì)說(shuō)明編譯器是如何編譯新的 PL 語(yǔ)言程序的。指出程序中最精彩的部分,以及為什么這樣做,如何控制和恢復(fù)語(yǔ)義錯(cuò)誤的。(3) 給出改動(dòng)后的編譯器源程序清單,并標(biāo)記出所修改的部分。比較你的編譯器和原來(lái)的編譯器之間的差別。(4) 說(shuō)明你的編譯器中可能存在的錯(cuò)誤。(5) 總結(jié)經(jīng)驗(yàn)與教訓(xùn),如果重做一遍,會(huì)有哪些新的改進(jìn)?對(duì)現(xiàn)存的 PL 編譯程序做如下修改或擴(kuò)充,其中(1) 、 (2) 、 (11)和(12)必須完成,剩余的均可任意選擇,但總分必須超過(guò) 40 分。(1) 注釋(5 分)(2) 布爾類型的數(shù)據(jù)(10 分)(3) 布爾表達(dá)式的短路計(jì)算(5 分)(4) 數(shù)組
11、(10 分)為了便于解釋執(zhí)行,可能要增加新的 PL 機(jī)器操作指令。(5) 參數(shù)(10 分) 語(yǔ)法同 Pascal(不用 var 聲明) 。(6) 函數(shù)(10 分)語(yǔ)法同 Pascal。 (7) else 子句和 repeat 語(yǔ)句(5 分)(8) for 語(yǔ)句,語(yǔ)法參照 Pascal 或 C 語(yǔ)言(5 分)(9) exit 語(yǔ)句和 break 語(yǔ)句(5 分)(10)記錄(結(jié)構(gòu)),語(yǔ)法同 Pascal 語(yǔ)言(10 分) 。(11)更有力的語(yǔ)法錯(cuò)誤恢復(fù)機(jī)制(20 分)(12)分離解釋和編譯器(5 分)三、參考資料與編譯器分析三、參考資料與編譯器分析第一部分第一部分 PL 語(yǔ)言及其編譯器語(yǔ)言及其編譯
12、器1. PL 語(yǔ)言介紹語(yǔ)言介紹PL 程序設(shè)計(jì)語(yǔ)言是一個(gè)較簡(jiǎn)單的語(yǔ)言,它以賦值語(yǔ)句為基礎(chǔ),構(gòu)造概念有順序、條件和重復(fù)(循環(huán))三種。PL 有子程序概念,包括過(guò)程定義(可以嵌套)與調(diào)用且有局部變量說(shuō)明。PL 中唯一的數(shù)據(jù)類型是整型,可以用來(lái)說(shuō)明該類型的常量和變量。當(dāng)然 PL 也具有通常的算術(shù)運(yùn)算和關(guān)系運(yùn)算。具體的 PL 語(yǔ)法圖如下。51.1 PLPL 語(yǔ)言的語(yǔ)法圖語(yǔ)言的語(yǔ)法圖程序程序程序體程序體語(yǔ)句序列語(yǔ)句序列程序體.constidentnumber=var;identprocedure,ident;程序體;語(yǔ)句語(yǔ)句;,6語(yǔ)句語(yǔ)句條件條件表達(dá)式表達(dá)式項(xiàng)項(xiàng)identcallbeginwhileendi
13、dent:=ifdothen語(yǔ)句語(yǔ)句表達(dá)式語(yǔ)句序列條件條件表達(dá)式odd表達(dá)式=表達(dá)式項(xiàng)項(xiàng)+-+7因子因子因子因子/*identnumber表達(dá)式()82. PL 語(yǔ)言編譯器語(yǔ)言編譯器本書所提供的 PL 語(yǔ)言編譯器的基本工作流程如圖 1-1 所示:語(yǔ)法分析詞法分析語(yǔ)義分析代碼生成代碼執(zhí)行源程序執(zhí)行結(jié)果符號(hào)表管理錯(cuò)誤診斷處理圖 1-1 PL 編譯器基本工作流程92.1 詞法分析詞法分析PL 的語(yǔ)言的詞法分析器將要完成以下工作:(1) 跳過(guò)分隔符(如空格,回車,制表符) ;(2) 識(shí)別諸如 begin,end,if,while 等保留字;(3) 識(shí)別非保留字的一般標(biāo)識(shí)符,此標(biāo)識(shí)符值(字符序列)賦給全
14、局量id,而全局量 sym 賦值為 SYM_IDENTIFIER。(4) 識(shí)別數(shù)字序列,當(dāng)前值賦給全局量 NUM,sym 則置為 SYM_NUMBER;(5) 識(shí)別:=,=之類的特殊符號(hào),全局量 sym 則分別被賦值為SYM_BECOMES,SYM_LEQ,SYM_GEQ 等。相關(guān)過(guò)程(函數(shù))有 getsym(),getch(),其中 getch()為獲取單個(gè)字符的過(guò)程,除此之外,它還完成:(1) 識(shí)別且跳過(guò)行結(jié)束符;(2) 將輸入源文件復(fù)寫到輸出文件;(3) 產(chǎn)生一份程序列表,輸出相應(yīng)行號(hào)或指令計(jì)數(shù)器的值。2.2 語(yǔ)法分析語(yǔ)法分析我們采用遞歸下降的方法來(lái)設(shè)計(jì) PL 編譯器。以下我們給出該語(yǔ)言
15、的 FIRST和 FOLLOW 集合。非終結(jié)符(非終結(jié)符(S S)FIRST(S)FIRST(S)FOLLOW(S)FOLLOW(S)程序體const var procedure ident call if begin while. ;語(yǔ)句ident call begin if while. ; end條件odd + - ( ident numberthen do表達(dá)式+ - ( ident number. ; ) R end then do項(xiàng)ident number (. ; ) R + - end then do因子ident number (. ; ) R + - * / end the
16、n do注:表中 R 代表六個(gè)關(guān)系運(yùn)算符。不難證明,PL 語(yǔ)言屬于 LL(1)文法。 (證明從略。 )以下是我們給出如何結(jié)合語(yǔ)法圖編寫(遞歸下降)語(yǔ)法分析程序的一般方法。假定圖 S 所對(duì)應(yīng)的程序段為 T(S) ,則:(1) 用合適的替換將語(yǔ)法約化成盡可能少的單個(gè)圖;(2) 將每一個(gè)圖按下面的規(guī)則(3)-(7)翻譯成一個(gè)過(guò)程說(shuō)明;(3) 順序圖對(duì)應(yīng)復(fù)合語(yǔ)句:對(duì)應(yīng):begin T(S1); T(S2); .; T(Sn) end(4)選擇:S1S2Sn10對(duì)應(yīng):case 語(yǔ)句或者條件語(yǔ)句:case ch of if ch in L1 then T(S1) else L1: T(S1); if ch
17、 in L2 then T(S2) else L2: T(S2); 或 . if ch in Ln then T(Sn) elseLn: T(Sn); error其中 LiFIRST(Si) ,ch 為當(dāng)前輸入符號(hào)。 (下同)(5) 循環(huán)對(duì)應(yīng):while ch in L do T(S)(6) 表示另一個(gè)圖 A 的圖:對(duì)應(yīng):過(guò)程調(diào)用 A。(7) 表示終結(jié)符的單元圖:對(duì)應(yīng):if ch = x then read(ch) else error相關(guān)過(guò)程有:block(), constdeclaration(), vardeclaration(), statement(), condition(), e
18、xpression(), term(), factor()等。它們之間依賴關(guān)系如圖 1-2:S1S2S3SAx112.3 語(yǔ)義分析語(yǔ)義分析PL 的語(yǔ)義分析主要進(jìn)行以下檢查:(1) 是否存在標(biāo)識(shí)符先引用未聲明的情況;(2) 是否存在己聲明的標(biāo)識(shí)符的錯(cuò)誤引用;(3) 是否存在一般標(biāo)識(shí)符的多重聲明。2.4 代碼生成代碼生成PL 編譯程序不僅完成通常的詞法分析、語(yǔ)法分析,而且還產(chǎn)生中間代碼和“目標(biāo)”代碼。最終我們要“運(yùn)行”該目標(biāo)碼。為了使我們的編譯程序保持適當(dāng)簡(jiǎn)單的水平,不致陷入與本課程無(wú)關(guān)的實(shí)際機(jī)器的特有性質(zhì)的考慮中去,我們假想有臺(tái)適合 PL 程序運(yùn)行的計(jì)算機(jī),我們稱之為 PL 處理機(jī)。PL 處理機(jī)
19、順序解釋生成的目標(biāo)代碼,我們稱之為解釋程序。注意:這里的假設(shè)與我們的編譯概念并不矛盾,在本課程中我們寫的只是一個(gè)示范性的編譯程序,它的后端無(wú)法完整地實(shí)現(xiàn),因而只能在一個(gè)解釋性的環(huán)境下予以模擬。從另一個(gè)角度上講,把解釋程序程序程序體語(yǔ)句條件表達(dá)式項(xiàng)因子圖 1-2 語(yǔ)法分析過(guò)程依賴關(guān)系12就看成是 PL 機(jī)硬件,把解釋執(zhí)行看成是 PL 的硬件執(zhí)行,那么我們所做的工作:由 PL 源語(yǔ)言程序到 PL 機(jī)器指令的變換,就是一個(gè)完整的編譯程序。PL 處理機(jī)有兩類存貯,目標(biāo)代碼放在一個(gè)固定的存貯數(shù)組 code 中,而所需數(shù)據(jù)組織成一個(gè)棧形式存放。PL 處理機(jī)的指令集根據(jù) PL 語(yǔ)言的要求而設(shè)計(jì),它包括以下的
20、指令:(1)LIT /* 將常數(shù)置于棧頂 */(2)LOD /* 將變量值置于棧頂 */(3)STO /* 將棧頂?shù)闹蒂x與某變量 */(4)CAL /* 用于過(guò)程調(diào)用的指令 */(5)INT /* 在數(shù)據(jù)棧中分配存貯空間 */(6)JMP, JPC /* 用于 if, while 語(yǔ)句的條件或無(wú)條件控制轉(zhuǎn)移指令 */(7)OPR /* 一組算術(shù)或邏輯運(yùn)算指令 */上述指令的格式由三部分組成:FLA其中,f, l, a 的含義見下表: F FL La aINT常 量LIT常 量LOD層次差數(shù)據(jù)地址STO層次差數(shù)據(jù)地址CAL層次差程序地址JMP程序地址JPC程序地址OPR運(yùn)算類別上表中,層次差為變
21、量名或過(guò)程名引用和聲明之間的靜態(tài)層次差別,程序地址為目標(biāo)數(shù)組 code 的下標(biāo),數(shù)據(jù)地址為變量在局部存貯中的相對(duì)地址。PL 的編譯程序?yàn)槊恳粭l PL 源程序的可執(zhí)行語(yǔ)句生成后綴式目標(biāo)代碼。這種代碼生成方式對(duì)于表達(dá)式、賦值語(yǔ)句、過(guò)程調(diào)用等的翻譯較簡(jiǎn)單。如賦值語(yǔ)句 X := Y op Z(op 為某個(gè)運(yùn)算符) ,將被翻譯成下面的目標(biāo)代碼序列:(設(shè)指令計(jì)數(shù)從第 100 號(hào)開始)No.No.f fL La a100LODLevel_diff_YAddr_Y101LODLevel_diff_ZAddr_Z102OPRop103STOLevel_diff_XAddr_X而對(duì) if 和 while 語(yǔ)句稍繁
22、瑣一點(diǎn),因?yàn)榇藭r(shí)要生成一些跳轉(zhuǎn)指令,而跳轉(zhuǎn)的目標(biāo)地址大都是未知的。為解決這一問(wèn)題,我們?cè)?PL 編譯程序中采用了回填表 2-1 PL 處理機(jī)指令13技術(shù),即產(chǎn)生跳轉(zhuǎn)目標(biāo)地址不明確的指令時(shí),先保留這些指令的地址(code 數(shù)組的下標(biāo)) ,等到目標(biāo)地址明確后再回過(guò)來(lái)將該跳轉(zhuǎn)指令的目標(biāo)地址補(bǔ)上,使其成為完整的指令。下表是 if、while 語(yǔ)句目標(biāo)代碼生成的模式。 (L1,L2 是代碼地址)ifif C C thenthen S SWhileWhile C C dodo S S條件 C 的目標(biāo)代碼JPC - L1語(yǔ)句 S 的目標(biāo)代碼L1: .L1: 條件 C 的目標(biāo)代碼JPC L2語(yǔ)句 S 的目標(biāo)代
23、碼JMP L1L2: .相關(guān)過(guò)程(函數(shù))有:gen(),其任務(wù)是把三個(gè)參數(shù) f、l、a 組裝成一條目標(biāo)指令并存放于 code 數(shù)組中,增加 CX 的值,CX 表示下一條即將生成的目標(biāo)指令的地址。2.5 代碼執(zhí)行代碼執(zhí)行為了簡(jiǎn)單起見,我們假設(shè)有一個(gè) PL 處理機(jī),它能夠解釋執(zhí)行 PL 編譯程序所生成的目標(biāo)代碼。這個(gè) PL 處理機(jī)有兩類存貯、一個(gè)指令寄存器和三個(gè)地址寄存器組成。程序(目標(biāo)代碼)存貯稱為 code,由編譯程序裝入,在目標(biāo)代碼執(zhí)行過(guò)程中保持不變,因此它可被看成是“只讀”存貯器。數(shù)據(jù)存貯 S 組織成為一個(gè)棧,所有的算術(shù)運(yùn)算均對(duì)棧頂元和次棧頂元進(jìn)行(一元運(yùn)算僅作用于棧頂元) ,并用結(jié)果值代
24、替原來(lái)的運(yùn)算對(duì)象。棧頂元的地址(下標(biāo))記在棧頂寄存器 T 中,指令寄存器 I 包含著當(dāng)前正在解釋執(zhí)行的指令,程序地址寄存器 P 指向下一條將取出的指令。PL 的每一個(gè)過(guò)程可能包含著局部變量,因?yàn)檫@些過(guò)程可以被遞歸地調(diào)用,故在實(shí)際調(diào)用前,無(wú)法為這些局部變量分配存貯地址。各個(gè)過(guò)程的數(shù)據(jù)區(qū)在存貯棧S 內(nèi)順序疊起來(lái),每個(gè)過(guò)程,除用戶定義的變量外,還應(yīng)當(dāng)有它自己的內(nèi)部信息,即調(diào)用它的程序段地址(返回地址)和它的調(diào)用者的數(shù)據(jù)區(qū)地址。在過(guò)程終止后,為了恢復(fù)原來(lái)程序的執(zhí)行,這兩個(gè)地址都是必須的。我們可將這兩個(gè)內(nèi)部值作為位于該過(guò)程數(shù)據(jù)區(qū)的內(nèi)部式隱式局部變量。我們把它們分別稱為返回地址(return addres
25、s)RA 和動(dòng)態(tài)鏈(dynamic link)DL。動(dòng)態(tài)鏈的頭,即最新分配的數(shù)據(jù)區(qū)的地址,保存在某地址寄存器 B 內(nèi)。因?yàn)閷?shí)際的存貯分配是運(yùn)行(解釋)時(shí)進(jìn)行的,編譯程序不能為其生成的代碼提供絕對(duì)地址,它只能確定變量在數(shù)據(jù)區(qū)內(nèi)的位置,因此它只能提供相對(duì)地址。為了正確地存取數(shù)據(jù),解釋程序需將某個(gè)修正量加到相應(yīng)的數(shù)據(jù)區(qū)的基地址上去。若變量是局部于當(dāng)前正在解釋的過(guò)程,則此基地址由寄存器 B 給出,否則,就需要順著數(shù)據(jù)區(qū)的鏈逐層上去找。然而遺憾的是,編譯程序只能知道存取路線的表的長(zhǎng)度,同時(shí)動(dòng)態(tài)鏈保存的則是過(guò)程活動(dòng)的動(dòng)態(tài)歷史,而這兩條存取路線并不總是一樣。例如,假定有過(guò)程 A,B,C,其中過(guò)程 C 的說(shuō)明
26、局部于過(guò)程 B,而過(guò)程 B 的表 2-2 if-while 語(yǔ)句目標(biāo)代碼生成模式14說(shuō)明局部于過(guò)程 A,程序運(yùn)行時(shí),過(guò)程 A 調(diào)用過(guò)程 B,過(guò)程 B 則調(diào)用過(guò)程 C,過(guò)程 C 又調(diào)用過(guò)程 B,如下圖所示:從靜態(tài)的角度我們可以說(shuō) A 是在第一層說(shuō)明的,B 是在第二層說(shuō)明的,C 則是在第三層說(shuō)明的。若在 B 中存取 A 中說(shuō)明的變量 a,由于編譯程序只知道 A,B間的靜態(tài)層差為 1,如果這時(shí)沿著動(dòng)態(tài)鏈下降一步,將導(dǎo)致對(duì) C 的局部變量的操作。為防止這種情況發(fā)生,有必要設(shè)置第二條鏈,它以編譯程序能明了的方式將各個(gè)數(shù)據(jù)區(qū)連接起來(lái)。我們稱之為靜態(tài)鏈(static link)SL。這樣,編譯程序所生成的代
27、碼地址是一對(duì)數(shù),指示著靜態(tài)層差和數(shù)據(jù)區(qū)的相對(duì)修正量。下面我們給出的是過(guò)程 A、B 和 C 運(yùn)行時(shí)刻的數(shù)據(jù)區(qū)圖示: DLDL RARA SLSLA A 的變量的變量B B 的變量的變量C C 的變量的變量B B 的變量的變量有了以上認(rèn)識(shí),我們就不難明白 PL 源程序的目標(biāo)代碼是如何被解釋執(zhí)行的。以語(yǔ)句 X := Y op Z 為例, (該語(yǔ)句的目標(biāo)代碼序列我們己在 2.4 節(jié)給出) ,PL處理機(jī)解釋該指令的“步驟”如下:AABBBCACB圖 2-1 過(guò)程說(shuō)明嵌套圖 過(guò)程調(diào)用圖 表示 A 調(diào)用B15stepstep 1,1, S+T Sbase(level_diff_Y) + addr_Y;/ 將
28、變量 Y 的值放在棧頂stepstep 2,2, S+T Sbase(level_diff_Z) + addr_Z;/ 將變量 Z 的值放在棧頂,此棧頂元為變量 Y 的值stepstep 3,3, T-;/ 棧頂指針指向次棧頂元,即存放結(jié)果的單元stepstep 4,4, ST ST op ST + 1;/ 變量 Y 和變量 Z 之間進(jìn)行“op”操作stepstep 5,5, Sbase(level_diff_X) + addr_X ST;/ 將棧頂?shù)闹荡娣诺阶兞?X 所在的單元stepstep 6,6, T-;/ 棧頂指針減一相關(guān)過(guò)程:base(),interpret()。其中 base()
29、的功能是根據(jù)層次差并從當(dāng)前數(shù)據(jù)區(qū)沿著靜態(tài)鏈查找,以便獲取變量實(shí)際所在的數(shù)據(jù)區(qū)其地址;interpret()則完成各種指令的執(zhí)行工作。2.6 錯(cuò)誤診斷處理錯(cuò)誤診斷處理一個(gè)編譯程序,在多數(shù)情況下,所接受的源程序正文都是有錯(cuò)誤的。發(fā)現(xiàn)錯(cuò)誤,并給出合適的診斷信息且繼續(xù)編譯下去從而發(fā)現(xiàn)更多的錯(cuò)誤,對(duì)于編譯程序而言是完全必要的。一個(gè)好的編譯器,其特征在于:任何輸入序列都不會(huì)引起編譯程序的崩潰。一切按語(yǔ)言定義為非法的結(jié)構(gòu),都能被發(fā)現(xiàn)和標(biāo)志出來(lái)。經(jīng)常出現(xiàn)的錯(cuò)誤,程序員的粗心或誤解造成的錯(cuò)誤能被正確地診斷出來(lái),而不致引起進(jìn)一步的株連錯(cuò)誤。根據(jù)這樣的要求,我們?yōu)?PL 編譯程序制定了以下兩條規(guī)則:(1) 關(guān)鍵字規(guī)
30、則;程序員在寫程序時(shí),可能會(huì)因?yàn)榇中亩┑粽Z(yǔ)句的分隔符“;” ,但他決不會(huì)漏掉算術(shù)運(yùn)算符“+” ,對(duì)于編譯程序而言,不論是分隔符號(hào)類的符號(hào)還是關(guān)鍵字符號(hào)類的符號(hào),它們都具有同等重要的地位。基于這樣的特點(diǎn),我們可以采用不易出錯(cuò)的部分來(lái)作為恢復(fù)正常步調(diào)的標(biāo)記。每當(dāng)遇到錯(cuò)誤時(shí),分析程序跳過(guò)后面的某些部分,直到出現(xiàn)所期望的符號(hào)為止。對(duì)于程序設(shè)計(jì)語(yǔ)言來(lái)說(shuō),這種符號(hào)(稱為同步符號(hào))的最好選擇就是關(guān)鍵字。PL 的每一種構(gòu)造語(yǔ)句以begin、if 或 while 開頭;每種說(shuō)明則以 var、const 或 procedure 開頭。每遇到錯(cuò)誤時(shí),編譯程序便可跳過(guò)一段程序,直到遇到這類符號(hào)為止,而繼續(xù)編譯。(2
31、) 鎮(zhèn)定規(guī)則;自頂向下分析的特點(diǎn)在于目標(biāo)對(duì)分成一些子目標(biāo),分程序則用別的分析程序來(lái)處理其子目標(biāo)。鎮(zhèn)定規(guī)則是說(shuō)一個(gè)分析程序發(fā)現(xiàn)16了錯(cuò)誤,它不應(yīng)該消極地停止前進(jìn),僅僅向調(diào)用它的程序報(bào)告發(fā)生的錯(cuò)誤;而應(yīng)該自己繼續(xù)向前掃描,找到似乎可以使正常的分析得以恢復(fù)的地方。這一規(guī)則在程序設(shè)計(jì)上的含義就是任一分析程序除了正常終止外,沒(méi)有其它出口。對(duì)于鎮(zhèn)定規(guī)則,一個(gè)可能的嚴(yán)格解釋為:一旦發(fā)現(xiàn)非法結(jié)構(gòu),即跳過(guò)后面的輸入正文,直到下一個(gè)可以正確地跟隨當(dāng)前正在分析的句子結(jié)構(gòu)的符號(hào)為止。這意味著每一分析程序需知道其當(dāng)前活動(dòng)結(jié)點(diǎn)的后繼符號(hào)集合。為了找到這個(gè)后繼符號(hào)集合,我們給對(duì)應(yīng)語(yǔ)法圖的每一個(gè)分析過(guò)程提供一個(gè)顯式參數(shù),se
32、t,它指明可能的后繼集合。不過(guò)在任何條件下,如果都跳到輸入正文中下一個(gè)這種后繼符號(hào)出現(xiàn)的地方,未免太短視了。程序中所含的錯(cuò)誤可能只不過(guò)是漏掉了一個(gè)符號(hào)(如“;” )而己,由此而忽略去源程序的符號(hào)集合中,再湊加一些關(guān)鍵字,它們用于標(biāo)記那些不容忽略的結(jié)構(gòu)的開始符,因此,作為參數(shù)傳遞給分析過(guò)程的那些符號(hào)就不僅是后繼符號(hào)了。對(duì)于這樣的符號(hào)集,我們采用這樣的計(jì)算策略:先用一些明顯的關(guān)鍵符號(hào)給它賦初值,然后隨著分析子目標(biāo)的層次深入,逐步補(bǔ)充別的合法符號(hào)。為了靈活起見,我們引入 test 子程序來(lái)實(shí)現(xiàn)所說(shuō)的驗(yàn)證工作。test 過(guò)程有三個(gè)參數(shù):(1) 可允許的下一個(gè)符號(hào)集合 S1,如果當(dāng)前符號(hào)不在此集合中,當(dāng)
33、即得到一個(gè)錯(cuò)誤號(hào);(2) 另加的停止符號(hào)集合 S2,有些符號(hào)的出現(xiàn),雖然無(wú)疑是錯(cuò)的,但它們絕對(duì)不應(yīng)被忽略而跳過(guò);(3) 整數(shù) n,表示有關(guān)錯(cuò)誤的診斷號(hào):voidvoid test(symsettest(symset s1,s1, symsetsymset s2,s2, intint n)n) symsetsymset s;s;ifif (!(! inset(sym,inset(sym, s1)s1) error(n);error(n);s s = = uniteset(s1,uniteset(s1, s2);s2);while(!while(! inset(sym,inset(sym, s)s
34、)getsym();getsym();destroyset(s);destroyset(s); 我們前面提出的方案,具有這樣的性質(zhì):試圖通過(guò)略過(guò)輸入正文中的一個(gè)或多個(gè)符號(hào)來(lái)恢復(fù)分析的正常步調(diào)。在錯(cuò)誤僅為漏掉一個(gè)符號(hào)所引起的情況下,它都是不適宜的策略。經(jīng)驗(yàn)表明,這類錯(cuò)誤基本上限于那種僅有語(yǔ)法作用,而不代表動(dòng)作的符號(hào)(如“;” ) 。把一些關(guān)鍵字加到后繼符號(hào)集合中去可使分析程序不再盲目地跳過(guò)后面的符號(hào),好象漏掉的已經(jīng)補(bǔ)上去一樣。下面程序段就是 PL 分析程序中復(fù)合語(yǔ)句分析的一小段。它的效果等于關(guān)鍵字前插入漏掉的分號(hào)。statbegsys 集合是“語(yǔ)句”這個(gè)結(jié)構(gòu)的首符號(hào)集。ifif (sym(sym
35、 = SYM_BEGIN)SYM_BEGIN) getsym();getsym();17set1set1 = = createset(SYM_SEMICOLON,createset(SYM_SEMICOLON, SYM_END,SYM_END, SYM_NULL);SYM_NULL);setset = = uniteset(set1,uniteset(set1, fsys);fsys);statement(set);statement(set);whilewhile (sym(sym = SYM_SEMICOLONSYM_SEMICOLON | inset(sym,inset(sym, sta
36、tbegsys)statbegsys) ifif (sym(sym = SYM_SEMICOLON)SYM_SEMICOLON) getsym();getsym(); elseelse error(10);error(10); statement(set);statement(set); / whilewhiledestroyset(set1);destroyset(set1);destroyset(set);destroyset(set);ifif (sym(sym = SYM_END)SYM_END) getsym();getsym(); elseelse error(17);error(
37、17); / ; oror endend expected.expected. 相關(guān)過(guò)程:test(), inset(), createset, uniteset(), error().2.7 符號(hào)表管理符號(hào)表管理為了組成一條指令,編譯程序必須知道其操作碼及其參數(shù)(數(shù)或地址) 。這些值是由編譯程序本身聯(lián)系到相應(yīng)標(biāo)識(shí)符上去的。這種聯(lián)系是在處理常數(shù)、變量和過(guò)程說(shuō)明完成的。為此,標(biāo)識(shí)符表應(yīng)包含每一標(biāo)識(shí)符所聯(lián)系的屬性;如果標(biāo)識(shí)符被說(shuō)明為常數(shù),其屬性值為常數(shù)值;如果標(biāo)識(shí)符被說(shuō)明成變量,其屬性就是由層次和修正量(偏移量)組成的地址;如果標(biāo)識(shí)符被說(shuō)明為過(guò)程,其屬性就是過(guò)程的入口地址及層次。常數(shù)的值由程序正文
38、提供,編譯的任務(wù)就是確定存放該值的地址。我們選擇順序分配變量和代碼的方法;每遇到一個(gè)變量說(shuō)明,就將數(shù)據(jù)單元的下標(biāo)加一(PL 機(jī)中,每個(gè)變量占一個(gè)存貯單元) 。開始編譯一個(gè)過(guò)程時(shí),要對(duì)數(shù)據(jù)單元的下標(biāo) dx 賦初值,表示新開辟一個(gè)數(shù)據(jù)區(qū)。dx 的初值為 3,因?yàn)槊總€(gè)數(shù)據(jù)區(qū)包含三個(gè)內(nèi)部變量 RA,DL 和 SL。相關(guān)過(guò)程:enter(),該函數(shù)用于向符號(hào)表添加新的符號(hào),并確定標(biāo)識(shí)符的有關(guān)屬性。182.8 其他其他本實(shí)驗(yàn)所提供的 PL 編譯程序包括詞法分析、語(yǔ)法分析、錯(cuò)誤診斷、代碼生成、解釋執(zhí)行等幾部分。關(guān)于這幾個(gè)程序,我們做如下說(shuō)明:(1) 每一個(gè)分程序(過(guò)程)被編譯結(jié)束后,將列出該部分 PL 程序
39、代碼。這個(gè)工作由過(guò)程 listcode()完成。注意,每個(gè)分程序(過(guò)程)的第一條指令未被列出。該指令是跳轉(zhuǎn)指令。其作用是繞過(guò)該分程序的說(shuō)明部分所產(chǎn)生的代碼(含過(guò)程說(shuō)明所產(chǎn)生的代碼) 。(2) 解釋程序作為 PL 編譯程序的一個(gè)過(guò)程,若被編譯的源代碼沒(méi)有錯(cuò)誤,則編譯結(jié)束時(shí)調(diào)用這個(gè)過(guò)程。(3) PL 語(yǔ)言沒(méi)有輸出語(yǔ)句。解釋程序按執(zhí)行次序,每遇到對(duì)變量的賦值就輸出其值。19第二部分第二部分 上機(jī)實(shí)驗(yàn)具體要求上機(jī)實(shí)驗(yàn)具體要求“編譯原理”的上機(jī)實(shí)驗(yàn)要求對(duì) PL 語(yǔ)言及其編譯器進(jìn)行實(shí)現(xiàn)、擴(kuò)充和修改。每個(gè)擴(kuò)充或修改方式可得到不同的分?jǐn)?shù),滿分為 100 分。完成上機(jī)作業(yè)后,必須提交下列文檔:(1) 修改后的
40、PL 語(yǔ)言文本。包含詞法分析(正規(guī)式) ,語(yǔ)法分析(BNF) 。(2) 有關(guān)修改后的 PL 編譯/解釋器的說(shuō)明。詳細(xì)說(shuō)明你的編譯器是如何編譯新的 PL 語(yǔ)言程序的。指出你的程序中最精彩的部分,以及你為什么這樣做,你是如何控制和恢復(fù)語(yǔ)義錯(cuò)誤的。(3) 給出你所改動(dòng)后的編譯器源程序清單,并標(biāo)記出你所修改的部分。比較你的編譯器和原來(lái)的編譯器之間的差別。(4) 說(shuō)明你的編譯器中可能存在的錯(cuò)誤。(5) 總結(jié)經(jīng)驗(yàn)與教訓(xùn),如果重做一遍,你會(huì)有哪些新的改進(jìn)?對(duì)現(xiàn)存的 PL 編譯程序可做如下修改或擴(kuò)充,其中(1) 、 (2) 、 (11)和(12)必須完成,剩余的均可任意選擇,但總分必須超過(guò) 40 分。(1)
41、注釋(5 分)注釋由(*和*)包含,不允許嵌套。(2) 布爾類型的數(shù)據(jù)(10 分)布爾類型的 BNF 為:var_optionvar_option | | varvar var_decl_listvar_decl_listvar_decl_listvar_decl_list var_declvar_decl | | var_decl_listvar_decl_list var_declvar_declvar_declvar_decl ident_listident_list : : data_typedata_typedata_typedata_type integerinteger | |
42、booleanboolean這種修改包括:這種修改包括:(i)區(qū)別整型與布爾型變量、常量和表達(dá)式。(ii)增加按嚴(yán)格計(jì)算的布爾類型運(yùn)算符 and、or 和 not。這些算符以及己有的運(yùn)算符的優(yōu)先級(jí)與 Pascal 語(yǔ)言相同。(iii)能夠使用布爾常量 true 和 false。(iv)把 PL 語(yǔ)言中的“條件”概念一般化為 Pascal 語(yǔ)言那樣。(v v)布爾表達(dá)式可以比較大小:false 0 0 dodobeginbeginifif oddodd b b thenthen z z :=:= z z + + a;a;a a :=:= 2 2 * * a;a; b b :=:= b b / /
43、 2;2;endendend;end;procedureprocedure divide;divide;varvar w;w;beginbeginr r :=:= x;x; q q :=:= 0;0; w w :=:= y;y;whilewhile w w y y dodobeginbeginq q :=:= 2 2 * * q;q; w w :=:= w w / / 2;2;ifif w w = r r thenthenbeginbeginr r :=:= r r - - w;w;q q :=:= q q + + 1;1;end;end;endendend;end;procedureproc
44、edure gcd;gcd;varvar f,f, g;g;beginbegin22f f :=:= x;x;g g :=:= y;y;whilewhile f f g g dodobeginbeginifif f f g g thenthen g g :=:= g g f;f;ifif g g 1212JPCJPC0 02929-ifif b b = 0odd(b)z := z + aa := 2 * ab := b / 2ifwhileb := y29 Pl0c.c#include #include pl0c.h#include string.h/* 解釋執(zhí)行時(shí)使用的棧 */#define
45、 stacksize 500 int main()bool nxtlevsymnum;init();/* 初始化 */fas=fopen(fas.tmp,w);fa1=fopen(fa1.tmp,w);printf(Input file? );fprintf(fa1,Input file? );scanf(%s,fname);/* 輸入文件名 */fin=fopen(fname,r);if(fin)fprintf(fa1,%sn,fname);printf(List object code?(Y/N);/* 是否輸出虛擬機(jī)代碼 */scanf(%s,fname);listswitch=(fna
46、me0=y|fname0=Y);printf(List symbol table?(Y/N); /* 是否輸出名字表 */scanf(%s,fname);tableswitch=(fname0=y|fname0=Y);err=0;cc=cx=ll=0;ch= ;kk=al-1;if(-1!=getsym()fa=fopen(fa.tmp,w);fa2=fopen(fa2.tmp,w);addset(nxtlev,declbegsys,statbegsys,symnum);nxtlevperiod=true;if(-1=block(0,0,nxtlev) /* 調(diào)用編譯程序 */fclose(f
47、a);fclose(fa1);fclose(fin);printf(n);return 0;30fclose(fa);fclose(fa1);if(sym!=period)error(9);if(err=0)interpret(); /* 調(diào)用解釋執(zhí)行程序 */elseprintf(Errors in pl/0 program);fclose(fin);elseprintf(Cant open file!n);fprintf(fa1,Cant open file!n);fclose(fa1);fclose(fas);printf(n);return 0;/* 在適當(dāng)?shù)奈恢蔑@示錯(cuò)誤 */void
48、 error(int n)char space81;memset(space,32,81);spacecc-1=0; /* 出錯(cuò)時(shí)當(dāng)前符號(hào)已經(jīng)讀完,所以 cc-1 */ printf(*%s!%dn,space,n);fprintf(fa1,*%s!%dn,space,n);err+;/* 詞法分析,獲取一個(gè)符號(hào) */int getsym()int i,j,k;while(ch= |ch=10|ch=9) /* 忽略空格、換行和 TAB */getchdo;if(ch=a&ch=z)31/* 名字或保留字以 a.z 開頭 */k=0;doif(k=a&ch=0&ch=9);ak=0;strcp
49、y(id,a);i=0;j=norw-1;do /* 搜索當(dāng)前符號(hào)是否為保留字 */k=(i+j)/2;if(strcmp(id,wordk)=0)i=k+1;while(ij)sym=wsymk; else sym=ident; /* 搜索失敗則,是名字或數(shù)字 */elseif(ch=0&ch=0&chnmax)error(30);else32if(ch=:)/* 檢測(cè)賦值符號(hào) */getchdo;if(ch=)sym=becomes;getchdo;elsesym=nul;/* 不能識(shí)別的符號(hào) */elseif(ch=)/* 檢測(cè)大于或大于等于符號(hào) */getchdo;if(ch=)sym
50、=geq;getchdo;elsesym=gtr;else33sym=ssymch;/* 當(dāng)符號(hào)不滿足上述條件時(shí),全部按照單字符符號(hào)處理 */getchdo;return 0;/* 生成虛擬機(jī)代碼 */int gen(enum fct x, /* f */int y, /* l */int z /* a */)if(cxcxmax)printf(Program too long);/* 程序過(guò)長(zhǎng) */return -1;codecx.f=x;codecx.l=y;codecx.a=z;cx+;return 0;/* 在某一部分(如一條語(yǔ)句,一個(gè)表達(dá)式)將要結(jié)束時(shí)時(shí)我們希望下一個(gè)符號(hào)屬于某集合(
51、該部分的后跟符號(hào)) ,test 負(fù)責(zé)這項(xiàng)監(jiān)測(cè),并且負(fù)責(zé)當(dāng)監(jiān)測(cè)不通過(guò)時(shí)的補(bǔ)救措施,程序在需要檢測(cè)時(shí)指定當(dāng)前需要的符號(hào)集合和補(bǔ)救用的集合(如之前未完成部分的后跟符號(hào)) ,以及檢測(cè)不通過(guò)時(shí)的錯(cuò)誤號(hào) */int test(bool* s1, /* 我們需要的符號(hào) */ bool* s2, /* 如果不是我們需要的,則需要一個(gè)補(bǔ)救用的集合 */ int n) /* 錯(cuò)誤號(hào) */if(!inset(sym,s1)error(n);/* 當(dāng)檢測(cè)不通過(guò)時(shí),不停獲取符號(hào),直到它屬于需要的集合或補(bǔ)救的集合 */while(!inset(sym,s1)&(!inset(sym,s2)getsymdo;34retur
52、n 0;/* 編譯程序主體 */int block(int lev, /* 當(dāng)前分程序所在層 */ int tx, /* 名字表當(dāng)前尾指針 */ bool* fsys /* 當(dāng)前模塊后跟符號(hào)集合 */ )int i;int dx; /* 名字分配到的相對(duì)地址 */int tx0; /* 保留初始 tx */int cx0; /* 保留初始 cx */bool nxtlevsymnum; /* 在下級(jí)函數(shù)的參數(shù)中,符號(hào)集合均為值參,但由于使用數(shù)租實(shí)現(xiàn),傳遞進(jìn)來(lái)的是指針,為防止下級(jí)函數(shù)改變上級(jí)函數(shù)的集合,開辟新的空間傳遞給下級(jí)函數(shù),之后所有的 nxtlev 都是這樣 */dx=3; tx0=tx;
53、/* 記錄本層名字的初始位置 */tabletx.adr=cx;gendo(jmp,0,0);if(levlevmax)error(32);doif(sym=constsym)/* 收到常量聲明符號(hào),開始處理常量聲明 */getsymdo;doconstdeclarationdo(&tx,lev,&dx); /* dx 的值會(huì)被constdeclaration 改變,使用指針 */while(sym=comma) getsymdo;constdeclarationdo(&tx,lev,&dx);if(sym=semicolon)getsymdo;else error(5);while(sym=
54、ident);35if(sym=varsym)/* 收到變量聲明符號(hào),開始處理變量聲明 */getsymdo;dovardeclarationdo(&tx,lev,&dx);while(sym=comma) getsymdo;vardeclarationdo(&tx,lev,&dx);if(sym=semicolon)getsymdo;else error(5);while(sym=ident);while(sym=procsym) /* 收到過(guò)程聲明符號(hào),開始處理過(guò)程聲明 */getsymdo;if(sym=ident)enter(procedur,&tx,lev,&dx); /* 記錄過(guò)程
55、名字 */getsymdo;else error(4);/* procedure 后應(yīng)為標(biāo)識(shí)符 */if(sym=semicolon)getsymdo;else error(5);/* 漏掉了分號(hào) */memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlevsemicolon=true;if(-1=block(lev+1,tx,nxtlev)return -1;/* 遞歸調(diào)用 */if(sym=semicolon)getsymdo;memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlevident=true;n
56、xtlevprocsym=true;testdo(nxtlev,fsys,6);36else error(5);/* 漏掉了分號(hào) */memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlevident=true;testdo(nxtlev,declbegsys,7);while(inset(sym,declbegsys); /* 直到?jīng)]有聲明符號(hào) */codetabletx0.adr.a=cx;/* 開始生成當(dāng)前過(guò)程代碼 */tabletx0.adr=cx;/* 當(dāng)前過(guò)程代碼地址 */tabletx0.size=dx;/* 聲明部分中每增加一條聲
57、明都會(huì)給 dx 增加 1,聲明部分已經(jīng)結(jié)束,dx 就是當(dāng)前過(guò)程數(shù)據(jù)的 size */cx0=cx;gendo(inte,0,dx);/* 生成分配內(nèi)存代碼 */if(tableswitch)/* 輸出名字表 */printf(TABLE:n);if(tx0+1tx)printf( NULLn);for(i=tx0+1;i=tx;i+)switch(tablei.kind)case constant:printf( %d const %s ,i,);printf(val=%dn,tablei.val);fprintf(fas, %d const %s ,i,tablei.n
58、ame);fprintf(fas,val=%dn,tablei.val);break;case variable:printf( %d var %s ,i,);printf(lev=%d addr=%dn,tablei.level,tablei.adr);fprintf(fas, %d var %s ,i,);fprintf(fas,lev=%d addr=%dn,tablei.level,tablei.adr);break;case procedur:printf( %d proc %s ,i,);printf(lev=%d
59、addr=%d size=%dn,tablei.level,tablei.adr,tablei.size);fprintf(fas, %d proc %s ,i,);fprintf(fas,lev=%d addr=%d size=%dn,tablei.level,tablei.adr,tablei.size);break;printf(n);37/* 語(yǔ)句后跟符號(hào)為分號(hào)或 end */memcpy(nxtlev,fsys,sizeof(bool)*symnum);/* 每個(gè)后跟符號(hào)集和都包含上層后跟符號(hào)集和,以便補(bǔ)救 */nxtlevsemicolon=true;nxtl
60、evendsym=true;statementdo(nxtlev,&tx,lev);gendo(opr,0,0); /* 每個(gè)過(guò)程出口都要使用的釋放數(shù)據(jù)段指令 */memset(nxtlev,0,sizeof(bool)*symnum);/*分程序沒(méi)有補(bǔ)救集合 */testdo(fsys,nxtlev,8); /* 檢測(cè)后跟符號(hào)正確性 */listcode(cx0);/* 輸出代碼 */return 0;/* 解釋程序 */void interpret()int p,b,t;/* 指令指針,指令基址,棧頂指針 */struct instruction i;/* 存放當(dāng)前指令 */int sst
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 加工機(jī)械銷售合同樣本
- 停車停車合同樣本
- 中標(biāo)雙乙方合同樣本
- 凝膠糖果采購(gòu)合同標(biāo)準(zhǔn)文本
- 加盟買料合同標(biāo)準(zhǔn)文本
- 前臺(tái)收銀合同樣本
- 農(nóng)民包地合同樣本
- 公司廚房勞務(wù)合同樣本
- 分眾傳媒財(cái)政補(bǔ)貼合同樣本
- 住校項(xiàng)目合同樣本
- 3.7 移動(dòng)終端應(yīng)用安全
- 臨水作業(yè)安全專項(xiàng)方案
- 第四專題 中國(guó)革命新道路的探索歷程課件
- 《遙感導(dǎo)論》全套課件
- 飛行器總體設(shè)計(jì)(二)
- 奧迪A7L汽車說(shuō)明書
- 棲居之橋的現(xiàn)象學(xué)沉思-海德格爾的棲居之思(續(xù))
- A3報(bào)告模板優(yōu)秀課件
- 注冊(cè)計(jì)量師(一級(jí))試題+答案
- 中醫(yī)英語(yǔ)課后翻譯習(xí)題答案(全)
- 4D廚房設(shè)備設(shè)施管理責(zé)任卡
評(píng)論
0/150
提交評(píng)論