PL0編譯器源程序分析_第1頁(yè)
PL0編譯器源程序分析_第2頁(yè)
PL0編譯器源程序分析_第3頁(yè)
PL0編譯器源程序分析_第4頁(yè)
PL0編譯器源程序分析_第5頁(yè)
已閱讀5頁(yè),還剩25頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

PL/0編譯器源程序分析PL/0語(yǔ)言是Pascal語(yǔ)言的一個(gè)子集,我們這里分析的PL/0的編譯程序包括了對(duì)PL/0語(yǔ)言源程序進(jìn)行分析處理、編譯生成類(lèi)PCODE代碼,并在虛擬機(jī)上解釋運(yùn)行生成的類(lèi)PCODE代碼的功能。PL/0語(yǔ)言編譯程序采用以語(yǔ)法分析為核心、一遍掃描的編譯方法。詞法分析和代碼生成作為獨(dú)立的子程序供語(yǔ)法分析程序調(diào)用。語(yǔ)法分析的同時(shí),提供了出錯(cuò)報(bào)告和出錯(cuò)恢復(fù)的功能。在源程序沒(méi)有錯(cuò)誤編譯通過(guò)的情況下,調(diào)用類(lèi)PCODE解釋程序解釋執(zhí)行生成的類(lèi)PCODE代碼。詞法分析子程序分析:詞法分析子程序名為getsym,功能是從源程序中讀出一個(gè)單詞符號(hào)(token),把它的信息放入全局變量sym、id和num中,語(yǔ)法分析器需要單詞時(shí),直接從這三個(gè)變量中獲得。(注意!語(yǔ)法分析器每次用完這三個(gè)變量的值就立即調(diào)用getsym子程序獲取新的單詞供下一次使用。而不是在需要新單詞時(shí)才調(diào)用getsym過(guò)程。)getsym過(guò)程通過(guò)反復(fù)調(diào)用getch子過(guò)程從源程序過(guò)獲取字符,并把它們拼成單詞。getch過(guò)程中使用了行緩沖區(qū)技術(shù)以提高程序運(yùn)行效率。詞法分析器的分析過(guò)程:調(diào)用getsym時(shí),它通過(guò)getch過(guò)程從源程序中獲得一個(gè)字符。如果這個(gè)字符是字母,則繼續(xù)獲取字符或數(shù)字,最終可以拼成一個(gè)單詞,查保留字表,如果查到為保留字,則把sym變量賦成相應(yīng)的保留字類(lèi)型值;如果沒(méi)有查到,則這個(gè)單詞應(yīng)是一個(gè)用戶(hù)自定義的標(biāo)識(shí)符(可能是變量名、常量名或是過(guò)程的名字),把sym置為ident,把這個(gè)單詞存入id變量。查保留字表時(shí)使用了二分法查找以提高效率。如果getch獲得的字符是數(shù)字,則繼續(xù)用getch獲取數(shù)字,并把它們拼成一個(gè)整數(shù),然后把sym置為number,并把拼成的數(shù)值放入num變量。如果識(shí)別出其它合法的符號(hào)(比如:賦值號(hào)、大于號(hào)、小于等于號(hào)等),則把sym則成相應(yīng)的類(lèi)型。如果遇到不合法的字符,把sym置成nul。語(yǔ)法分析子程序分析:語(yǔ)法分析子程序采用了自頂向下的遞歸子程序法,語(yǔ)法分析同時(shí)也根據(jù)程序的語(yǔ)意生成相應(yīng)的代碼,并提供了出錯(cuò)處理的機(jī)制。語(yǔ)法分析主要由分程序分析過(guò)程(block)、常量定義分析過(guò)程(constdeclaration)、變量定義分析過(guò)程(vardeclaration)、語(yǔ)句分析過(guò)程(statement)、表達(dá)式處理過(guò)程(expression)、項(xiàng)處理過(guò)程(term)、因子處理過(guò)程(factor)和條件處理過(guò)程(condition)構(gòu)成。這些過(guò)程在結(jié)構(gòu)上構(gòu)成一個(gè)嵌套的層次結(jié)構(gòu)。除此之外,還有出錯(cuò)報(bào)告過(guò)程(error)、代碼生成過(guò)程(gen)、測(cè)試單詞合法性及出錯(cuò)恢復(fù)過(guò)程(test)、登錄名字表過(guò)程(enter)、查詢(xún)名字表函數(shù)(position)以及列出類(lèi)PCODE代碼過(guò)程(listcode)作過(guò)語(yǔ)法分析的輔助過(guò)程。由PL/0的語(yǔ)法圖可知:一個(gè)完整的PL/0程序是由分程序和句號(hào)構(gòu)成的。因此,本編譯程序在運(yùn)行的時(shí)候,通過(guò)主程序中調(diào)用分程序處理過(guò)程block來(lái)分析分程序部分(分程序分析過(guò)程中還可能會(huì)遞歸調(diào)用block過(guò)程),然后,判斷最后讀入的符號(hào)是否為句號(hào)。如果是句號(hào)且分程序分析中未出錯(cuò),則是一個(gè)合法的PL/0程序,可以運(yùn)行生成的代碼,否則就說(shuō)明源PL/0程序是不合法的,輸出出錯(cuò)提示即可。下面按各語(yǔ)法單元分析PL/0編譯程序的運(yùn)行機(jī)制。分程序處理過(guò)程:語(yǔ)法分析開(kāi)始后,首先調(diào)用分程序處理過(guò)程(block)處理分程序。過(guò)程入口參數(shù)置為:0層、符號(hào)表位置0、出錯(cuò)恢復(fù)單詞集合為句號(hào)、聲明符或語(yǔ)句開(kāi)始符。進(jìn)入block過(guò)程后,首先把局部數(shù)據(jù)段分配指針設(shè)為3,準(zhǔn)備分配3個(gè)單元供運(yùn)行期存放靜態(tài)鏈SL、動(dòng)態(tài)鏈DL和返回地址RA。然后用tx0記錄下當(dāng)前符號(hào)表位置并產(chǎn)生一條jmp指令,準(zhǔn)備跳轉(zhuǎn)到主程序的開(kāi)始位置,由于當(dāng)前還沒(méi)有知到主程序究竟在何處開(kāi)始,所以jmp的目標(biāo)暫時(shí)填為0,稍后再改。同時(shí)在符號(hào)表的當(dāng)前位置記錄下這個(gè)jmp指令在代碼段中的位置。在判斷了嵌套層數(shù)沒(méi)有超過(guò)規(guī)定的層數(shù)后,開(kāi)始分析源程序。首先判斷是否遇到了常量聲明,如果遇到則開(kāi)始常量定義,把常量存入符號(hào)表。接下去用同樣的方法分析變量聲明,變量定義過(guò)程中會(huì)用dx變量記錄下局部數(shù)據(jù)段分配的空間個(gè)數(shù)。然后如果遇到procedure保留字則進(jìn)行過(guò)程聲明和定義,聲明的方法是把過(guò)程的名字和所在的層次記入符號(hào)表,過(guò)程定義的方法就是通過(guò)遞歸調(diào)用block過(guò)程,因?yàn)槊總€(gè)過(guò)程都是一個(gè)分程序。由于這是分程序中的分程序,因此調(diào)用block時(shí)需把當(dāng)前的層次號(hào)lev加一傳遞給block過(guò)程。分程序聲明部分完成后,即將進(jìn)入語(yǔ)句的處理,這時(shí)的代碼分配指針cx的值正好指向語(yǔ)句的開(kāi)始位置,這個(gè)位置正是前面的jmp指令需要跳轉(zhuǎn)到的位置。于是通過(guò)前面記錄下來(lái)的地址值,把這個(gè)jmp指令的跳轉(zhuǎn)位置改成當(dāng)前cx的位置。并在符號(hào)表中記錄下當(dāng)前的代碼段分配地址和局部數(shù)據(jù)段要分配的大?。╠x的值)。生成一條int指令,分配dx個(gè)空間,作為這個(gè)分程序段的第一條指令。下面就調(diào)用語(yǔ)句處理過(guò)程statement分析語(yǔ)句。分析完成后,生成操作數(shù)為0的opr指令,用于從分程序返回(對(duì)于0層的主程序來(lái)說(shuō),就是程序運(yùn)行完成,退出)。常量定義過(guò)程:通過(guò)循環(huán),反復(fù)獲得標(biāo)識(shí)符和對(duì)應(yīng)的值,存入符號(hào)表。符號(hào)表中記錄下標(biāo)識(shí)符的名字和它對(duì)應(yīng)的值。變量定義過(guò)程:與常量定義類(lèi)似,通過(guò)循環(huán),反復(fù)獲得標(biāo)識(shí)符,存入符號(hào)表。符號(hào)表中記錄下標(biāo)識(shí)符的名字、它所在的層及它在所在層中的偏移地址。語(yǔ)句處理過(guò)程:語(yǔ)句處理過(guò)程是一個(gè)嵌套子程序,通過(guò)調(diào)用表達(dá)式處理、項(xiàng)處理、因子處理等過(guò)程及遞歸調(diào)用自己來(lái)實(shí)現(xiàn)對(duì)語(yǔ)句的分析。語(yǔ)句處理過(guò)程可以識(shí)別的語(yǔ)句包括賦值語(yǔ)句、read語(yǔ)句、write語(yǔ)句、call語(yǔ)句、if語(yǔ)句、while語(yǔ)句。當(dāng)遇到begin/end語(yǔ)句時(shí),就遞歸調(diào)用自己來(lái)分析。分析的同時(shí)生成相應(yīng)的類(lèi)PCODE指令。賦值語(yǔ)句的處理:首先獲取賦值號(hào)左邊的標(biāo)識(shí)符,從符號(hào)表中找到它的信息,并確認(rèn)這個(gè)標(biāo)識(shí)符確為變量名。然后通過(guò)調(diào)用表達(dá)式處理過(guò)程算得賦值號(hào)右部的表達(dá)式的值并生成相應(yīng)的指令保證這個(gè)值放在運(yùn)行期的數(shù)據(jù)棧頂。最后通過(guò)前面查到的左部變量的位置信息,生成相應(yīng)的sto指令,把棧頂值存入指定的變量的空間,實(shí)現(xiàn)了賦值操作。read語(yǔ)句的處理:確定read語(yǔ)句語(yǔ)法合理的前提下(否則報(bào)錯(cuò)),生成相應(yīng)的指令:第一條是16號(hào)操作的opr指令,實(shí)現(xiàn)從標(biāo)準(zhǔn)輸入設(shè)備上讀一個(gè)整數(shù)值,放在數(shù)據(jù)棧頂。第二條是sto指令,把棧頂?shù)闹荡嫒雛ead語(yǔ)句括號(hào)中的變量所在的單元。write語(yǔ)句的處理:與read語(yǔ)句相似。在語(yǔ)法正確的前提下,生成指令:通過(guò)循環(huán)調(diào)用表達(dá)式處理過(guò)程分析write語(yǔ)句括號(hào)中的每一個(gè)表達(dá)式,生成相應(yīng)指令保證把表達(dá)式的值算出并放到數(shù)據(jù)棧頂并生成14號(hào)操作的opr指令,輸出表達(dá)式的值。最后生成15號(hào)操作的opr指令輸出一個(gè)換行。call語(yǔ)句的處理:從符號(hào)表中找到call語(yǔ)句右部的標(biāo)識(shí)符,獲得其所在層次和偏移地址。然后生成相應(yīng)的cal指令。至于調(diào)用子過(guò)程所需的保護(hù)現(xiàn)場(chǎng)等工作是由類(lèi)PCODE解釋程序在解釋執(zhí)行cal指令時(shí)自動(dòng)完成的。if語(yǔ)句的處理:按if語(yǔ)句的語(yǔ)法,首先調(diào)用邏輯表達(dá)式處理過(guò)程處理if語(yǔ)句的條件,把相應(yīng)的真假值放到數(shù)據(jù)棧頂。接下去記錄下代碼段分配位置(即下面生成的jpc指令的位置),然后生成條件轉(zhuǎn)移jpc指令(遇0或遇假轉(zhuǎn)移),轉(zhuǎn)移地址未知暫時(shí)填0。然后調(diào)用語(yǔ)句處理過(guò)程處理then語(yǔ)句后面的語(yǔ)句或語(yǔ)句塊。then后的語(yǔ)句處理完后,當(dāng)前代碼段分配指針的位置就應(yīng)該是上面的jpc指令的轉(zhuǎn)移位置。通過(guò)前面記錄下的jpc指令的位置,把它的跳轉(zhuǎn)位置改成當(dāng)前的代碼段指針位置。begin/end語(yǔ)句的處理:通過(guò)循環(huán)遍歷begin/end語(yǔ)句塊中的每一個(gè)語(yǔ)句,通過(guò)遞歸調(diào)用語(yǔ)句分析過(guò)程分析并生成相應(yīng)代碼。while語(yǔ)句的處理:首先用cx1變量記下當(dāng)前代碼段分配位置,作為循環(huán)的開(kāi)始位置。然后處理while語(yǔ)句中的條件表達(dá)式生成相應(yīng)代碼把結(jié)果放在數(shù)據(jù)棧頂,再用cx2變量記下當(dāng)前位置,生成條件轉(zhuǎn)移指令,轉(zhuǎn)移位置未知,填0。通過(guò)遞歸調(diào)用語(yǔ)句分析過(guò)程分析do語(yǔ)句后的語(yǔ)句或語(yǔ)句塊并生成相應(yīng)代碼。最后生成一條無(wú)條件跳轉(zhuǎn)指令jmp,跳轉(zhuǎn)到cx1所指位置,并把cx2所指的條件跳轉(zhuǎn)指令的跳轉(zhuǎn)位置改成當(dāng)前代碼段分配位置。表達(dá)式、項(xiàng)、因子處理:根據(jù)PL/0語(yǔ)法可知,表達(dá)式應(yīng)該是由正負(fù)號(hào)或無(wú)符號(hào)開(kāi)頭、由若干個(gè)項(xiàng)以加減號(hào)連接而成。而項(xiàng)是由若干個(gè)因子以乘除號(hào)連接而成,因子則可能是一個(gè)標(biāo)識(shí)符或一個(gè)數(shù)字,或是一個(gè)以括號(hào)括起來(lái)的子表達(dá)式。根據(jù)這樣的結(jié)構(gòu),構(gòu)造出相應(yīng)的過(guò)程,遞歸調(diào)用就完成了表達(dá)式的處理。把項(xiàng)和因子獨(dú)立開(kāi)處理解決了加減號(hào)與乘除號(hào)的優(yōu)先級(jí)問(wèn)題。在這幾個(gè)過(guò)程的反復(fù)調(diào)用中,始終傳遞fsys變量的值,保證可以在出錯(cuò)的情況下跳過(guò)出錯(cuò)的符號(hào),使分析過(guò)程得以進(jìn)行下去。邏輯表達(dá)式的處理:首先判斷是否為一元邏輯表達(dá)式:判奇偶。如果是,則通過(guò)調(diào)用表達(dá)式處理過(guò)程分析計(jì)算表達(dá)式的值,然后生成判奇指令。如果不是,則肯定是二元邏輯運(yùn)算符,通過(guò)調(diào)用表達(dá)式處理過(guò)程依次分析運(yùn)算符左右兩部分的值,放在棧頂?shù)膬蓚€(gè)空間中,然后依不同的邏輯運(yùn)算符,生成相應(yīng)的邏輯判斷指令,放入代碼段。判斷單詞合法性與出錯(cuò)恢復(fù)過(guò)程分析:本過(guò)程有三個(gè)參數(shù),s1、s2為兩個(gè)符號(hào)集合,n為出錯(cuò)代碼。本過(guò)程的功能是:測(cè)試當(dāng)前符號(hào)(即sym變量中的值)是否在s1集合中,如果不在,就通過(guò)調(diào)用出錯(cuò)報(bào)告過(guò)程輸出出錯(cuò)代碼n,并放棄當(dāng)前符號(hào),通過(guò)詞法分析過(guò)程獲取一下單詞,直到這個(gè)單詞出現(xiàn)在s1或s2集合中為止。這個(gè)過(guò)程在實(shí)際使用中很靈活,主要有兩個(gè)用法:在進(jìn)入某個(gè)語(yǔ)法單位時(shí),調(diào)用本過(guò)程,檢查當(dāng)前符號(hào)是否屬于該語(yǔ)法單位的開(kāi)始符號(hào)集合。若不屬于,則濾去開(kāi)始符號(hào)和后繼符號(hào)集合外的所有符號(hào)。在語(yǔ)法單位分析結(jié)束時(shí),調(diào)用本過(guò)程,檢查當(dāng)前符號(hào)是否屬于調(diào)用該語(yǔ)法單位時(shí)應(yīng)有的后繼符號(hào)集合。若不屬于,則濾去后繼符號(hào)和開(kāi)始符號(hào)集合外的所有符號(hào)。通過(guò)這樣的機(jī)制,可以在源程序出現(xiàn)錯(cuò)誤時(shí),及時(shí)跳過(guò)出錯(cuò)的部分,保證語(yǔ)法分析可以繼續(xù)下去。語(yǔ)法分析過(guò)程中調(diào)用的其它子過(guò)程相對(duì)比較簡(jiǎn)單,請(qǐng)參考源程序的注釋。類(lèi)PCODE代碼解釋執(zhí)行過(guò)程分析這個(gè)過(guò)程模擬了一臺(tái)可以運(yùn)行類(lèi)PCODE指令的棧式計(jì)算機(jī)。它擁有一個(gè)棧式數(shù)據(jù)段用于存放運(yùn)行期數(shù)據(jù)、擁有一個(gè)代碼段用于存放類(lèi)PCODE程序代碼。同時(shí)還擁用數(shù)據(jù)段分配指針、指令指針、指令寄存器、局部段基址指針等寄存器。解釋執(zhí)行類(lèi)PCODE代碼時(shí),數(shù)據(jù)段存儲(chǔ)分配方式如下:對(duì)于源程序的每一個(gè)過(guò)程(包括主程序),在被調(diào)用時(shí),首先在數(shù)據(jù)段中開(kāi)辟三個(gè)空間,存放靜態(tài)鏈SL、動(dòng)態(tài)鏈DL和返回地址RA。靜態(tài)鏈記錄了定義該過(guò)程的直接外過(guò)程(或主程序)運(yùn)行時(shí)最新數(shù)據(jù)段的基地址。動(dòng)態(tài)鏈記錄調(diào)用該過(guò)程前正在運(yùn)行的過(guò)程的數(shù)據(jù)段基址。返回地址記錄了調(diào)用該過(guò)程時(shí)程序運(yùn)行的斷點(diǎn)位置。對(duì)于主程序來(lái)說(shuō),SL、DL和RA的值均置為0。靜態(tài)鏈的功能是在一個(gè)子過(guò)程要引用它的直接或間接父過(guò)程(這里的父過(guò)程是按定義過(guò)程時(shí)的嵌套情況來(lái)定的,而不是按執(zhí)行時(shí)的調(diào)用順序定的)的變量時(shí),可以通過(guò)靜態(tài)鏈,跳過(guò)個(gè)數(shù)為層差的數(shù)據(jù)段,找到包含要引用的變量所在的數(shù)據(jù)段基址,然后通過(guò)偏移地址訪(fǎng)問(wèn)它。在過(guò)程返回時(shí),解釋程序通過(guò)返回地址恢復(fù)指令指針的值到調(diào)用前的地址,通過(guò)當(dāng)前段基址恢復(fù)數(shù)據(jù)段分配指針,通過(guò)動(dòng)態(tài)鏈恢復(fù)局部段基址指針。實(shí)現(xiàn)子過(guò)程的返回。對(duì)于主程序來(lái)說(shuō),解釋程序會(huì)遇到返回地址為0的情況,這時(shí)就認(rèn)為程序運(yùn)行結(jié)束。解釋程序過(guò)程中的base函數(shù)的功能,就是用于沿著靜態(tài)鏈,向前查找相差指定層數(shù)的局部數(shù)據(jù)段基址。這在使用sto、lod等訪(fǎng)問(wèn)局部變量的指令中會(huì)經(jīng)常用到。類(lèi)PCODE代碼解釋執(zhí)行的部分通過(guò)循環(huán)和簡(jiǎn)單的case判斷不同的指令,做出相應(yīng)的動(dòng)作。當(dāng)遇到主程序中的返回指令時(shí),指令指針會(huì)指到0位置,把這樣一個(gè)條件作為終至循環(huán)的條件,保證程序運(yùn)行可以正常的結(jié)束。以下源程序是以清華大學(xué)出版社《編譯原理》中的源代碼為基礎(chǔ)作了少量改動(dòng)而成。程序在TurboPascal7.0上編譯運(yùn)行通過(guò)。************************************************************************************programpl0(fa,fa1,fa2);(*PL/0編譯程序與代碼生成解釋運(yùn)行程序*)(*PL/0compilerwithcodegeneration*)label99;(*聲明出錯(cuò)跳轉(zhuǎn)標(biāo)記*)(*在TurboPascal7.0中已不允許跨過(guò)程的GOTO轉(zhuǎn)移,因此后面的GOTO語(yǔ)句均被我去除了,因此這里的label也沒(méi)有意義了*)const(*常量定義*)norw=13;(*ofreservedwords*)(*保留字的個(gè)數(shù)*)txmax=100;(*lengthofidentifiertable*)(*標(biāo)識(shí)符表的長(zhǎng)度(容量)*)nmax=14;(*maxnumberofdigitsinnumbers*)(*數(shù)字允許的最長(zhǎng)位數(shù)*)al=10;(*lengthofidentifiers*)(*標(biāo)識(shí)符最長(zhǎng)長(zhǎng)度*)amax=2047;(*maximumaddress*)(*尋址空間*)levmax=3;(*maxdepthofblocknesting*)(*最大允許的塊嵌套層數(shù)*)cxmax=200;(*sizeofcodearray*)(*類(lèi)PCODE目標(biāo)代碼數(shù)組長(zhǎng)度(可容納代碼行數(shù))*)type(*類(lèi)型定義*)symbol=(nul,ident,number,plus,minus,times,slash,oddsym,eql,neq,lss,leq,gtr,geq,lparen,rparen,comma,semicolon,period,becomes,beginsym,endsym,ifsym,thensym,whilesym,writesym,readsym,dosym,callsym,constsym,varsym,procsym);(*symobl類(lèi)型標(biāo)識(shí)了不同類(lèi)型的詞匯*)alfa=packedarray[1..al]ofchar;(*alfa類(lèi)型用于標(biāo)識(shí)符*)object1=(constant,variable,procedur);(*object1為三種標(biāo)識(shí)符的類(lèi)型*)(*原程序在此使用object作為類(lèi)型名稱(chēng),在支持面向?qū)ο蟮腡urboPascal7.0中編譯不能通過(guò)*)(*wirthusedtheword"procedure"there,whickwon'twork!*)(*上面一行是課本上的程序清單中的注釋?zhuān)f(shuō)本程序的原作者Wirth在這里用了procedure這個(gè)詞作為標(biāo)識(shí)符類(lèi)型,是不可以的。事實(shí)上Wirth原本在這里用的詞是prozedure,是可以的。*)symset=setofsymbol;(*symset是symbol類(lèi)型的一個(gè)集合類(lèi)型,可用于存放一組symbol*)fct=(lit,opr,lod,sto,cal,int,jmp,jpc);(*fct類(lèi)型分別標(biāo)識(shí)類(lèi)PCODE的各條指令*)instruction=packedrecordf:fct;(*functioncode*)l:0..levmax;(*level*)a:0..amax;(*displacementaddr*)end;(*類(lèi)PCODE指令類(lèi)型,包含三個(gè)字段:指令f、層差l和另一個(gè)操作數(shù)a*)(*lit0,aloadconstantaopr0,aexecuteopralodl,aloadvariablel,astol,astorevariablel,acall,acallprocedureaatlevellint0,aincrementt-registerbyajmp0,ajumptoajpc0,ajumpconditionaltoa*)var(*全局變量定義*)fa:text;(*文本文件fa用于列出源程序*)fa1,fa2:text;(*文本文件fa1用于列出類(lèi)PCODE代碼、fa2用于記錄解釋執(zhí)行類(lèi)PCODE代碼的過(guò)程*)listswitch:boolean;(*truesetlistobjectcode*)(*如果本變量置true,程序編譯后將為列出類(lèi)PCODE代碼,否則不列出類(lèi)PCODE代碼*)ch:char;(*lastcharread*)(*主要用于詞法分析器,存放最近一次從文件中讀出的字符*)sym:symbol;(*lastsymbolread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識(shí)別出來(lái)的token的類(lèi)型*)id:alfa;(*lastidentifierread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識(shí)別出來(lái)的標(biāo)識(shí)符的名字*)num:integer;(*lastnumberread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識(shí)別出來(lái)的數(shù)字的值*)cc:integer;(*charactercount*)(*行緩沖區(qū)指針*)ll:integer;(*linelength*)(*行緩沖區(qū)長(zhǎng)度*)kk:integer;(*引入此變量是出于程序性能考慮,見(jiàn)getsym過(guò)程注釋*)cx:integer;(*codeallocationindex*)(*代碼分配指針,代碼生成模塊總在cx所指位置生成新的代碼*)line:array[1..81]ofchar;(*行緩沖區(qū),用于從文件讀出一行,供詞法分析獲取單詞時(shí)之用*)a:alfa;(*詞法分析器中用于臨時(shí)存放正在分析的詞*)code:array[0..cxmax]ofinstruction;(*生成的類(lèi)PCODE代碼表,存放編譯得到的類(lèi)PCODE代碼*)word:array[1..norw]ofalfa;(*保留字表*)wsym:array[1..norw]ofsymbol;(*保留字表中每一個(gè)保留字對(duì)應(yīng)的symbol類(lèi)型*)ssym:array[''..'^']ofsymbol;(*一些符號(hào)對(duì)應(yīng)的symbol類(lèi)型表*)(*wirthuses"array[char]"here*)mnemonic:array[fct]ofpackedarray[1..5]ofchar;(*類(lèi)PCODE指令助記符表*)declbegsys,statbegsys,facbegsys:symset;(*聲明開(kāi)始、表達(dá)式開(kāi)始和項(xiàng)開(kāi)始符號(hào)集合*)table:array[0..txmax]ofrecord(*符號(hào)表*)name:alfa;(*符號(hào)的名字*)casekind:object1of(*符號(hào)的類(lèi)型*)constant:(*如果是常量名*)(val:integer);(*val中放常量的值*)variable,procedur:(*如果是變量名或過(guò)程名*)(level,adr,size:integer)(*存放層差、偏移地址和大小*)(*"size"lackinginorginal.Ithinkitbelonshere*)end;fin,fout:text;(*fin文本文件用于指向輸入的源程序文件,fout程序中沒(méi)有用到*)fname:string;(*存放PL/0源程序文件的文件名*)(*我修改的代碼:原程序在此處使用alfa類(lèi)型,無(wú)法在TurboPascal7.0中通過(guò),readln函數(shù)的參數(shù)不能為alfa型*)err:integer;(*出錯(cuò)總次數(shù)*)(*出錯(cuò)處理過(guò)程error*)(*參數(shù):n:出錯(cuò)代碼*)procedureerror(n:integer);beginwriteln('****','':cc-1,'!',n:2);(*在屏幕cc-1位置顯示!與出錯(cuò)代碼提示,由于cc是行緩沖區(qū)指針,所以!所指位置即為出錯(cuò)位置*)writeln(fa1,'****','':cc-1,'!',n:2);(*在文件cc-1位置輸出!與出錯(cuò)代碼提示*)err:=err+1(*出錯(cuò)總次數(shù)加一*)end(*error*);(*詞法分析過(guò)程getsym*)proceduregetsym;vari,j,k:integer;(*讀取原程序中下一個(gè)字符過(guò)程getch*)proceduregetch;beginifcc=llthen(*如果行緩沖區(qū)指針指向行緩沖區(qū)最后一個(gè)字符就從文件讀一行到行緩沖區(qū)*)beginifeof(fin)then(*如果到達(dá)文件末尾*)beginwrite('Programincomplete');(*出錯(cuò),退出程序*)close(fa);close(fa1);close(fin);halt(0);{goto99}(*我修改的代碼,由于TurboPascal7.0中不允許跨過(guò)程的goto,就只能用上面的方法退出程序了。*)end;ll:=0;(*行緩沖區(qū)長(zhǎng)度置0*)cc:=0;(*行緩沖區(qū)指針置行首*)write(cx:4,'');(*輸出cx值,寬度為4*)write(fa1,cx:4,'');(*輸出cx值,寬度為4到文件*)whilenoteoln(fin)do(*當(dāng)未到行末時(shí)*)beginll:=ll+1;(*行緩沖區(qū)長(zhǎng)度加一*)read(fin,ch);(*從文件讀入一個(gè)字符到ch*)write(ch);(*在屏幕輸出ch*)write(fa1,ch);(*把ch輸出到文件*)line[ll]:=ch;(*把讀到的字符存入行緩沖區(qū)相應(yīng)的位置*)end;(*可見(jiàn),PL/0源程序要求每行的長(zhǎng)度都小于81個(gè)字符*)writeln;ll:=ll+1;(*行緩沖區(qū)長(zhǎng)度加一,用于容納即將讀入的回車(chē)符CR*)read(fin,line[ll]);(*把#13(CR)讀入行緩沖區(qū)尾部*)read(fin,ch);(*我添加的代碼。由于PC上文本文件換行是以#13#10(CR+LF)表示的,所以要把多余的LF從文件讀出,這里放在ch變量中是由于ch變量的值在下面即將被改變,把這個(gè)多余值放在ch中沒(méi)有問(wèn)題*)writeln(fa1);end;cc:=cc+1;(*行緩沖區(qū)指針加一,指向即將讀到的字符*)ch:=line[cc](*讀出字符,放入全局變量ch*)end(*getch*);begin(*getsym*)while(ch='')or(ch=#13)do(*我修改的代碼:這句原來(lái)是用于讀一個(gè)有效的字符(跳過(guò)讀出的字符中多余的空格),但實(shí)際上還要跳過(guò)多余的回車(chē)*)getch;ifchin['a'..'z']then(*如果讀出的字符是一個(gè)字母,說(shuō)明是保留字或標(biāo)識(shí)符*)begink:=0;(*標(biāo)識(shí)符緩沖區(qū)指針置0*)repeat(*這個(gè)循環(huán)用于依次讀出源文件中的字符構(gòu)成標(biāo)識(shí)符*)ifk<althen(*如果標(biāo)識(shí)符長(zhǎng)度沒(méi)有超過(guò)最大標(biāo)識(shí)符長(zhǎng)度(如果超過(guò),就取前面一部分,把多余的拋棄)*)begink:=k+1;a[k]:=ch;end;getch(*讀下一個(gè)字符*)untilnot(chin['a'..'z','0'..'9']);(*直到讀出的不是字母或數(shù)字,由此可知PL/0的標(biāo)識(shí)符構(gòu)成規(guī)則是:以字母開(kāi)頭,后面跟若干個(gè)字母或數(shù)字*)ifk>=kkthen(*如果當(dāng)前獲得的標(biāo)識(shí)符長(zhǎng)度大于等于kk*)kk:=k(*令kk為當(dāng)前標(biāo)識(shí)符長(zhǎng)度*)elserepeat(*這個(gè)循環(huán)用于把標(biāo)識(shí)符緩沖后部沒(méi)有填入相應(yīng)字母或空格的空間用空格補(bǔ)足*)a[kk]:='';kk:=kk-1untilkk=k;(*在第一次運(yùn)行這個(gè)過(guò)程時(shí),kk的值為al,即最大標(biāo)識(shí)符長(zhǎng)度,如果讀到的標(biāo)識(shí)符長(zhǎng)度小于kk,就把a(bǔ)數(shù)組的后部沒(méi)有字母的空間用空格補(bǔ)足。這時(shí),kk的值就成為a數(shù)組前部非空格字符的個(gè)數(shù)。以后再運(yùn)行g(shù)etsym時(shí),如果讀到的標(biāo)識(shí)符長(zhǎng)度大于等于kk,就把kk的值變成當(dāng)前標(biāo)識(shí)符的長(zhǎng)度。這時(shí)就不必在后面填空格了,因?yàn)樗暮竺婵隙ㄈ强崭?。反之如果最近讀到的標(biāo)識(shí)符長(zhǎng)度小于kk,那就需要從kk位置向前,把超過(guò)當(dāng)前標(biāo)識(shí)長(zhǎng)度的空間填滿(mǎn)空格。以上的這樣一個(gè)邏輯,完全是出于程序性能的上考慮。其實(shí)完全可以簡(jiǎn)單的把a(bǔ)數(shù)組中a[k]元素以后的空間不管三七二十一全填空格。*)(*下面開(kāi)始二分法查找看讀出的標(biāo)識(shí)符是不是保留字之一*)id:=a;(*最后讀出標(biāo)識(shí)符等于a*)i:=1;(*i指向第一個(gè)保留字*)j:=norw;(*j指向最后一個(gè)保留字*)repeatk:=(i+j)div2;(*k指向中間一個(gè)保留字*)ifid<=word[k]then(*如果當(dāng)前的標(biāo)識(shí)符小于k所指的保留字*)j:=k-1;(*移動(dòng)j指針*)ifid>=word[k]then(*如果當(dāng)前的標(biāo)識(shí)符大于k所指的保留字*)i:=k+1(*移動(dòng)i指針*)untili>j;(*循環(huán)直到找完保留字表*)ifi-1>jthen(*如果i-1>j表明在保留字表中找到相應(yīng)的項(xiàng),id中存的是保留字*)sym:=wsym[k](*找到保留字,把sym置為相應(yīng)的保留字值*)elsesym:=ident(*未找到保留字,把sym置為ident類(lèi)型,表示是標(biāo)識(shí)符*)end(*至此讀出字符為字母即對(duì)保留字或標(biāo)識(shí)符的處理結(jié)束*)else(*如果讀出字符不是字母*)ifchin['0'..'9']then(*如果讀出字符是數(shù)字*)begin(*number*)(*開(kāi)始對(duì)數(shù)字進(jìn)行處理*)k:=0;(*數(shù)字位數(shù)*)num:=0;(*數(shù)字置為0*)sym:=number;(*置sym為number,表示這一次讀到的是數(shù)字*)repeat(*這個(gè)循環(huán)依次從源文件中讀出字符,組成數(shù)字*)num:=10*num+(ord(ch)-ord('0'));(*num*10加上最近讀出的字符ASCII減'0'的ASCII得到相應(yīng)的數(shù)值*)k:=k+1;(*數(shù)字位數(shù)加一*)getchuntilnot(chin['0'..'9']);(*直到讀出的字符不是數(shù)字為止*)ifk>nmaxthen(*如果組成的數(shù)字位數(shù)大于最大允許的數(shù)字位數(shù)*)error(30)(*發(fā)出30號(hào)錯(cuò)*)end(*至此對(duì)數(shù)字的識(shí)別處理結(jié)束*)elseifch=':'then(*如果讀出的不字母也不是數(shù)字而是冒號(hào)*)begingetch;(*再讀一個(gè)字符*)ifch='='then(*如果讀到的是等號(hào),正好可以與冒號(hào)構(gòu)成賦值號(hào)*)beginsym:=becomes;(*sym的類(lèi)型設(shè)為賦值號(hào)becomes*)getch(*再讀出下一個(gè)字*)endelsesym:=nul;(*如果不是讀到等號(hào),那單獨(dú)的一個(gè)冒號(hào)就什么也不是*)end(*以上完成對(duì)賦值號(hào)的處理*)else(*如果讀到不是字母也不是數(shù)字也不是冒號(hào)*)ifch='<'then(*如果讀到小于號(hào)*)begingetch;(*再讀一個(gè)字符*)ifch='='then(*如果讀到等號(hào)*)beginsym:=leq;(*購(gòu)成一個(gè)小于等于號(hào)*)getch(*讀一個(gè)字符*)endelse(*如果小于號(hào)后不是跟的等號(hào)*)sym:=lss(*那就是一個(gè)單獨(dú)的小于號(hào)*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號(hào)也不是小于號(hào)*)ifch='>'then(*如果讀到大于號(hào),處理過(guò)程類(lèi)似于處理小于號(hào)*)begingetch;(*再讀一個(gè)字符*)ifch='='then(*如果讀到等號(hào)*)beginsym:=geq;(*購(gòu)成一個(gè)大于等于號(hào)*)getch(*讀一個(gè)字符*)endelse(*如果大于號(hào)后不是跟的等號(hào)*)sym:=gtr(*那就是一個(gè)單獨(dú)的大于號(hào)*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號(hào)也不是小于號(hào)也不是大于號(hào)*)begin(*那就說(shuō)明它不是標(biāo)識(shí)符/保留字,也不是復(fù)雜的雙字節(jié)操作符,應(yīng)該是一個(gè)普通的符號(hào)*)sym:=ssym[ch];(*直接成符號(hào)表中查到它的類(lèi)型,賦給sym*)getch(*讀下一個(gè)字符*)end(*整個(gè)if語(yǔ)句判斷結(jié)束*)end(*getsym*);(*詞法分析過(guò)程getsym總結(jié):從源文件中讀出若干有效字符,組成一個(gè)token串,識(shí)別它的類(lèi)型為保留字/標(biāo)識(shí)符/數(shù)字或是其它符號(hào)。如果是保留字,把sym置成相應(yīng)的保留字類(lèi)型,如果是標(biāo)識(shí)符,把sym置成ident表示是標(biāo)識(shí)符,于此同時(shí),id變量中存放的即為保留字字符串或標(biāo)識(shí)符名字。如果是數(shù)字,把sym置為number,同時(shí)num變量中存放該數(shù)字的值。如果是其它的操作符,則直接把sym置成相應(yīng)類(lèi)型。經(jīng)過(guò)本過(guò)程后ch變量中存放的是下一個(gè)即將被識(shí)別的字符*)(*目標(biāo)代碼生成過(guò)程gen*)(*參數(shù):x:要生成的一行代碼的助記符*)(*y,z:代碼的兩個(gè)操作數(shù)*)(*本過(guò)程用于把生成的目標(biāo)代碼寫(xiě)入目標(biāo)代碼數(shù)組,供后面的解釋器解釋執(zhí)行*)proceduregen(x:fct;y,z:integer);beginifcx>cxmaxthen(*如果cx>cxmax表示當(dāng)前生成的代碼行號(hào)大于允許的最大代碼行數(shù)*)beginwrite('programtoolong');(*輸出"程序太長(zhǎng)",退出*)close(fa);close(fa1);close(fin);halt(0){goto99}(*我修改的代碼,由于TurboPascal7.0中不允許跨過(guò)程的goto,就只能用上面的方法退出程序了。*)end;withcode[cx]do(*把代碼寫(xiě)入目標(biāo)代碼數(shù)組的當(dāng)前cx所指位置*)beginf:=x;l:=y;a:=z;end;cx:=cx+1(*移動(dòng)cx指針指向下一個(gè)空位*)end(*gen*);(*測(cè)試當(dāng)前單詞是否合法過(guò)程test*)(*參數(shù):s1:當(dāng)語(yǔ)法分析進(jìn)入或退出某一語(yǔ)法單元時(shí)當(dāng)前單詞符合應(yīng)屬于的集合*)(*s2:在某一出錯(cuò)狀態(tài)下,可恢復(fù)語(yǔ)法分析正常工作的補(bǔ)充單詞集合*)(*n:出錯(cuò)信息編號(hào),當(dāng)當(dāng)前符號(hào)不屬于合法的s1集合時(shí)發(fā)出的出錯(cuò)信息*)proceduretest(s1,s2:symset;n:integer);beginifnot(symins1)then(*如果當(dāng)前符號(hào)不在s1中*)beginerror(n);(*發(fā)出n號(hào)錯(cuò)誤*)s1:=s1+s2;(*把s2集合補(bǔ)充進(jìn)s1集合*)whilenot(symins1)do(*通過(guò)循環(huán)找到下一個(gè)合法的符號(hào),以恢復(fù)語(yǔ)法分析工作*)getsymendend(*test*);(*語(yǔ)法分析過(guò)程block*)(*參數(shù):lev:這一次語(yǔ)法分析所在的層次*)(*tx:符號(hào)表指針*)(*fsys:用于出錯(cuò)恢復(fù)的單詞集合*)procedureblock(lev,tx:integer;fsys:symset);vardx:integer;(*dataallocationindex*)(*數(shù)據(jù)段內(nèi)存分配指針,指向下一個(gè)被分配空間在數(shù)據(jù)段中的偏移位置*)tx0:integer;(*initialtableindex*)(*記錄本層開(kāi)始時(shí)符號(hào)表位置*)cx0:integer;(*initialcodeindex*)(*記錄本層開(kāi)始時(shí)代碼段分配位置*)(*登陸符號(hào)表過(guò)程enter*)(*參數(shù):k:欲登陸到符號(hào)表的符號(hào)類(lèi)型*)procedureenter(k:object1);begin(*enterobjectintotable*)tx:=tx+1;(*符號(hào)表指針指向一個(gè)新的空位*)withtable[tx]do(*開(kāi)始登錄*)beginname:=id;(*name是符號(hào)的名字,對(duì)于標(biāo)識(shí)符,這里就是標(biāo)識(shí)符的名字*)kind:=k;(*符號(hào)類(lèi)型,可能是常量、變量或過(guò)程名*)casekof(*根據(jù)不同的類(lèi)型進(jìn)行不同的操作*)constant:(*如果是常量名*)beginifnum>amaxthen(*在常量的數(shù)值大于允許的最大值的情況下*)beginerror(31);(*拋出31號(hào)錯(cuò)誤*)num:=0;(*實(shí)際登陸的數(shù)字以0代替*)end;val:=num(*如是合法的數(shù)值,就登陸到符號(hào)表*)end;variable:(*如果是變量名*)beginlevel:=lev;(*記下它所屬的層次號(hào)*)adr:=dx;(*記下它在當(dāng)前層中的偏移量*)dx:=dx+1;(*偏移量自增一,為下一次做好準(zhǔn)備*)end;procedur:(*如果要登陸的是過(guò)程名*)level:=lev(*記錄下這個(gè)過(guò)程所在層次*)endendend(*enter*);[樓主]|Posted:2006-03-2100:00angel級(jí)別:管理員精華:23發(fā)帖:489威望:534點(diǎn)金錢(qián):5330RMB貢獻(xiàn)值:0點(diǎn)好評(píng)度:0點(diǎn)注冊(cè)時(shí)間:2006-02-26最后登錄:2006-08-24(*登錄符號(hào)過(guò)程沒(méi)有考慮到重復(fù)的定義的問(wèn)題。如果出現(xiàn)重復(fù)定義,則以最后一次的定義為準(zhǔn)。*)(*在符號(hào)表中查找指定符號(hào)所在位置的函數(shù)position*)(*參數(shù):id:要找的符號(hào)*)(*返回值:要找的符號(hào)在符號(hào)表中的位置,如果找不到就返回0*)functionposition(id:alfa):integer;vari:integer;begin(*findidentifierintable*)table[0].name:=id;(*先把id放入符號(hào)表0號(hào)位置*)i:=tx;(*從符號(hào)表中當(dāng)前位置也即最后一個(gè)符號(hào)開(kāi)始找*)<>iddo(*如果當(dāng)前的符號(hào)與要找的不一致*)i:=i-1;(*找前面一個(gè)*)position:=i(*返回找到的位置號(hào),如果沒(méi)找到則一定正好為0*)end(*position*);(*常量聲明處理過(guò)程constdeclaration*)procedureconstdeclaration;beginifsym=identthen(*常量聲明過(guò)程開(kāi)始遇到的第一個(gè)符號(hào)必然應(yīng)為標(biāo)識(shí)符*)begingetsym;(*獲取下一個(gè)token*)ifsymin[eql,becomes]then(*如果是等號(hào)或賦值號(hào)*)beginifsym=becomesthen(*如果是賦值號(hào)(常量生明中應(yīng)該是等號(hào))*)error(1);(*拋出1號(hào)錯(cuò)誤*)(*這里其實(shí)自動(dòng)進(jìn)行了錯(cuò)誤糾正使編譯繼續(xù)進(jìn)行,把賦值號(hào)當(dāng)作等號(hào)處理*)getsym;(*獲取下一個(gè)token,等號(hào)或賦值號(hào)后應(yīng)接上數(shù)字*)ifsym=numberthen(*如果的確是數(shù)字*)beginenter(constant);(*把這個(gè)常量登陸到符號(hào)表*)getsym(*獲取下一個(gè)token,為后面作準(zhǔn)備*)endelseerror(2)(*如果等號(hào)后接的不是數(shù)字,拋出2號(hào)錯(cuò)誤*)endelseerror(3)(*如果常量標(biāo)識(shí)符后接的不是等號(hào)或賦值號(hào),拋出3號(hào)錯(cuò)誤*)endelseerror(4)(*如果常量聲明過(guò)程遇到的第一個(gè)符號(hào)不為標(biāo)識(shí)符,拋出4號(hào)錯(cuò)誤*)end(*constdeclaration*);(*變量聲明過(guò)程vardeclaration*)procedurevardeclaration;beginifsym=identthen(*變量聲明過(guò)程開(kāi)始遇到的第一個(gè)符號(hào)必然應(yīng)為標(biāo)識(shí)符*)beginenter(variable);(*將標(biāo)識(shí)符登陸到符號(hào)表中*)getsym(*獲取下一個(gè)token,為后面作準(zhǔn)備*)endelseerror(4)(*如果變量聲明過(guò)程遇到的第一個(gè)符號(hào)不是標(biāo)識(shí)符,拋出4號(hào)錯(cuò)誤*)end(*vardeclaration*);(*列出當(dāng)前一層類(lèi)PCODE目標(biāo)代碼過(guò)程listcode*)procedurelistcode;vari:integer;begin(*listcodegeneratedforthisblock*)iflistswitchthen(*如果用戶(hù)選擇是要列出代碼的情況下才列出代碼*)beginfori:=cx0tocx-1do(*從當(dāng)前層代碼開(kāi)始位置到當(dāng)前代碼位置-1處,即為本分程序塊*)withcodedobeginwriteln(i:4,mnemonic[f]:5,l:3,a:5);(*顯示出第i行代碼的助記符和L與A操作數(shù)*)(*我修改的代碼:原程序此處在輸出i時(shí),沒(méi)有指定占4個(gè)字符寬度,不美觀(guān)也與下面一句不配套。*)writeln(fa,i:4,mnemonic[f]:5,l:3,a:5)(*同時(shí)把屏顯打印到文件*)end;endend(*listcode*);(*語(yǔ)句處理過(guò)程statement*)(*參數(shù)說(shuō)明:fsys:如果出錯(cuò)可用來(lái)恢復(fù)語(yǔ)法分析的符號(hào)集合*)procedurestatement(fsys:symset);vari,cx1,cx2:integer;(*表達(dá)式處理過(guò)程expression*)(*參數(shù)說(shuō)明:fsys:如果出錯(cuò)可用來(lái)恢復(fù)語(yǔ)法分析的符號(hào)集合*)procedureexpression(fsys:symset);varaddop:symbol;(*項(xiàng)處理過(guò)程term*)(*參數(shù)說(shuō)明:fsys:如果出錯(cuò)可用來(lái)恢復(fù)語(yǔ)法分析的符號(hào)集合*)procedureterm(fsys:symset);varmulop:symbol;(*因子處理過(guò)程factor*)(*參數(shù)說(shuō)明:fsys:如果出錯(cuò)可用來(lái)恢復(fù)語(yǔ)法分析的符號(hào)集合*)procedurefactor(fsys:symset);vari:integer;begintest(facbegsys,fsys,24);(*開(kāi)始因子處理前,先檢查當(dāng)前token是否在facbegsys集合中。*)(*如果不是合法的token,拋24號(hào)錯(cuò)誤,并通過(guò)fsys集恢復(fù)使語(yǔ)法處理可以繼續(xù)進(jìn)行*)whilesyminfacbegsysdo(*循環(huán)處理因子*)beginifsym=identthen(*如果遇到的是標(biāo)識(shí)符*)begini:=position(id);(*查符號(hào)表,找到當(dāng)前標(biāo)識(shí)符在符號(hào)表中的位置*)ifi=0then(*如果查符號(hào)表返回為0,表示沒(méi)有找到標(biāo)識(shí)符*)error(11)(*拋出11號(hào)錯(cuò)誤*)elsewithtabledo(*如果在符號(hào)表中找到了當(dāng)前標(biāo)識(shí)符的位置,開(kāi)始生成相應(yīng)代碼*)casekindofconstant:gen(lit,0,val);(*如果這個(gè)標(biāo)識(shí)符對(duì)應(yīng)的是常量,值為val,生成lit指令,把val放到棧頂*)variable:gen(lod,lev-level,adr);(*如果標(biāo)識(shí)符是變量名,生成lod指令,*)(*把位于距離當(dāng)前層level的層的偏移地址為adr的變量放到棧頂*)procedur:error(21)(*如果在因子處理中遇到的標(biāo)識(shí)符是過(guò)程名,出錯(cuò)了,拋21號(hào)錯(cuò)*)end;getsym(*獲取下一token,繼續(xù)循環(huán)處理*)endelseifsym=numberthen(*如果因子處理時(shí)遇到數(shù)字*)beginifnum>amaxthen(*如果數(shù)字的大小超過(guò)允許最大值amax*)beginerror(31);(*拋出31號(hào)錯(cuò)*)num:=0(*把數(shù)字按0值處理*)end;gen(lit,0,num);(*生成lit指令,把這個(gè)數(shù)值字面常量放到棧頂*)getsym(*獲取下一token*)endelseifsym=lparenthen(*如果遇到的是左括號(hào)*)begingetsym;(*獲取一個(gè)token*)expression([rparen]+fsys);(*遞歸調(diào)用expression子程序分析一個(gè)子表達(dá)式*)ifsym=rparenthen(*子表達(dá)式分析完后,應(yīng)遇到右括號(hào)*)getsym(*如果的確遇到右括號(hào),讀取下一個(gè)token*)elseerror(22)(*否則拋出22號(hào)錯(cuò)誤*)end;test(fsys,facbegsys,23)(*一個(gè)因子處理完畢,遇到的token應(yīng)在fsys集合中*)(*如果不是,拋23號(hào)錯(cuò),并找到下一個(gè)因子的開(kāi)始,使語(yǔ)法分析可以繼續(xù)運(yùn)行下去*)endend(*factor*);begin(*term*)factor([times,slash]+fsys);(*每一個(gè)項(xiàng)都應(yīng)該由因子開(kāi)始,因此調(diào)用factor子程序分析因子*)whilesymin[times,slash]do(*一個(gè)因子后應(yīng)當(dāng)遇到乘號(hào)或除號(hào)*)beginmulop:=sym;(*保存當(dāng)前運(yùn)算符*)getsym;(*獲取下一個(gè)token*)factor(fsys+[times,slash]);(*運(yùn)算符后應(yīng)是一個(gè)因子,故調(diào)factor子程序分析因子*)ifmulop=timesthen(*如果剛才遇到乘號(hào)*)gen(opr,0,4)(*生成乘法指令*)elsegen(opr,0,5)(*不是乘號(hào)一定是除號(hào),生成除法指令*)endend(*term*);begin(*expression*)ifsymin[plus,minus]then(*一個(gè)表達(dá)式可能會(huì)由加號(hào)或減號(hào)開(kāi)始,表示正負(fù)號(hào)*)beginaddop:=sym;(*把當(dāng)前的正號(hào)或負(fù)號(hào)保存起來(lái),以便下面生成相應(yīng)代碼*)getsym;(*獲取一個(gè)token*)term(fsys+[plus,minus]);(*正負(fù)號(hào)后面應(yīng)該是一個(gè)項(xiàng),調(diào)term子程序分析*)ifaddop=minusthen(*如果保存下來(lái)的符號(hào)是負(fù)號(hào)*)gen(opr,0,1)(*生成一條1號(hào)操作指令:取反運(yùn)算*)(*如果不是負(fù)號(hào)就是正號(hào),不需生成相應(yīng)的指令*)endelse(*如果不是由正負(fù)號(hào)開(kāi)頭,就應(yīng)是一個(gè)項(xiàng)開(kāi)頭*)term(fsys+[plus,minus]);(*調(diào)用term子程序分析項(xiàng)*)whilesymin[plus,minus]do(*項(xiàng)后應(yīng)是加運(yùn)算或減運(yùn)算*)beginaddop:=sym;(*把運(yùn)算符保存下來(lái)*)getsym;(*獲取下一個(gè)token,加減運(yùn)算符后應(yīng)跟的是一個(gè)項(xiàng)*)term(fsys+[plus,minus]);(*調(diào)term子程序分析項(xiàng)*)ifaddop=plusthen(*如果項(xiàng)與項(xiàng)之間的運(yùn)算符是加號(hào)*)gen(opr,0,2)(*生成2號(hào)操作指令:加法*)else(*否則是減法*)gen(opr,0,3)(*生成3號(hào)操作指令:減法*)endend(*expression*);(*條件處理過(guò)程condition*)(*參數(shù)說(shuō)明:fsys:如果出錯(cuò)可用來(lái)恢復(fù)語(yǔ)法分析的符號(hào)集合*)procedurecondition(fsys:symset);varrelop:symbol;(*用于臨時(shí)記錄token(這里一定是一個(gè)二元邏輯運(yùn)算符)的內(nèi)容*)beginifsym=oddsymthen(*如果是odd運(yùn)算符(一元)*)begingetsym;(*獲取下一個(gè)token*)expression(fsys);(*對(duì)odd的表達(dá)式進(jìn)行處理計(jì)算*)gen(opr,0,6);(*生成6號(hào)操作指令:奇偶判斷運(yùn)算*)endelse(*如果不是odd運(yùn)算符(那就一定是二元邏輯運(yùn)算符)*)beginexpression([eql,neq,lss,leq,gtr,geq]+fsys);(*對(duì)表達(dá)式左部進(jìn)行處理計(jì)算*)ifnot(symin[eql,neq,lss,leq,gtr,geq])then(*如果token不是邏輯運(yùn)算符中的一個(gè)*)error(20)(*拋出20號(hào)錯(cuò)誤*)elsebeginrelop:=sym;(*記錄下當(dāng)前的邏輯運(yùn)算符*)getsym;(*獲取下一個(gè)token*)expression(fsys);(*對(duì)表達(dá)式右部進(jìn)行處理計(jì)算*)caserelopof(*如果剛才的運(yùn)算符是下面的一種*)eql:gen(opr,0,8);(*等號(hào):產(chǎn)生8號(hào)判等指令*)neq:gen(opr,0,9);(*不等號(hào):產(chǎn)生9號(hào)判不等指令*)lss:gen(opr,0,10);(*小于號(hào):產(chǎn)生10號(hào)判小指令*)geq:gen(opr,0,11);(*大于等號(hào)號(hào):產(chǎn)生11號(hào)判不小于指令*)gtr:gen(opr,0,12);(*大于號(hào):產(chǎn)生12號(hào)判大于指令*)leq:gen(opr,0,13);(*小于等于號(hào):產(chǎn)生13號(hào)判不大于指令*)endendendend(*condition*);begin(*statement*)ifsym=identthen(*所謂"語(yǔ)句"可能是賦值語(yǔ)句,以標(biāo)識(shí)符開(kāi)頭*)begini:=position(id);(*在符號(hào)表中查到該標(biāo)識(shí)符所在位置*)ifi=0then(*如果沒(méi)找到*)error(11)(*拋出11號(hào)錯(cuò)誤*)elseiftable.kind<>variablethen(*如果在符號(hào)表中找到該標(biāo)識(shí)符,但該標(biāo)識(shí)符不是變量名*)beginerror(12);(*拋出12號(hào)錯(cuò)誤*)i:=0(*i置0作為錯(cuò)誤標(biāo)志*)end;getsym;(*獲得下一個(gè)token,正常應(yīng)為賦值號(hào)*)ifsym=becomesthen(*如果的確為賦值號(hào)*)getsym(*獲取下一個(gè)token,正常應(yīng)為一個(gè)表達(dá)式*)elseerror(13);(*如果賦值語(yǔ)句的左部標(biāo)識(shí)符號(hào)后所接不是賦值號(hào),拋出13號(hào)錯(cuò)誤*)expression(fsys);(*處理表達(dá)式*)ifi<>0then(*如果不曾出錯(cuò),i將不為0,i所指為當(dāng)前語(yǔ)名左部標(biāo)識(shí)符在符號(hào)表中的位置*)withtabledogen(sto,lev-level,adr)(*產(chǎn)生一行把表達(dá)式值寫(xiě)往指定內(nèi)存的sto目標(biāo)代碼*)endelseifsym=readsymthen(*如果不是賦值語(yǔ)句,而是遇到了read語(yǔ)句*)begingetsym;(*獲得下一token,正常情況下應(yīng)為左括號(hào)*)ifsym<>lparenthen(*如果read語(yǔ)句后跟的不是左括號(hào)*)error(34)(*拋出34號(hào)錯(cuò)誤*)elserepeat(*循環(huán)得到read語(yǔ)句括號(hào)中的參數(shù)表,依次產(chǎn)生相應(yīng)的“從鍵盤(pán)讀入”目標(biāo)代碼*)getsym;(*獲得一個(gè)token,正常應(yīng)是一個(gè)變量名*)ifsym=identthen(*如果確為一個(gè)標(biāo)識(shí)符*)(*這里略有問(wèn)題,還應(yīng)判斷一下這個(gè)標(biāo)識(shí)符是不是變量名,如果是常量名或過(guò)程名應(yīng)出錯(cuò)*)i:=position(id)(*查符號(hào)表,找到它所在位置給i,找不到時(shí)i會(huì)為0*)elsei:=0;(*不是標(biāo)識(shí)符則有問(wèn)題,i置0作為出錯(cuò)標(biāo)志*)ifi=0then(*如果有錯(cuò)誤*)error(35)(*拋出35號(hào)錯(cuò)誤*)else(*否則生成相應(yīng)的目標(biāo)代碼*)withtabledobegingen(opr,0,16);(*生成16號(hào)操作指令:從鍵盤(pán)讀入數(shù)字*)gen(sto,lev-level,adr)(*生成sto指令,把讀入的值存入指定變量所在的空間*)end;getsym(*獲取下一個(gè)token,如果是逗號(hào),則read語(yǔ)還沒(méi)完,否則應(yīng)當(dāng)是右括號(hào)*)untilsym<>comma;(*不斷生成代碼直到read語(yǔ)句的參數(shù)表中的變量遍歷完為止,這里遇到不是逗號(hào),應(yīng)為右括號(hào)*)ifsym<>rparenthen(*如果不是我們預(yù)想中的右括號(hào)*)beginerror(33);(*拋出33號(hào)錯(cuò)誤*)whilenot(syminfsys)do(*依靠fsys集,找到下一個(gè)合法的token,恢復(fù)語(yǔ)法分析*)getsymendelsegetsym(*如果read語(yǔ)句正常結(jié)束,得到下一個(gè)token,一般為分號(hào)或end*)endelseifsym=writesymthen(*如果遇到了write語(yǔ)句*)begingetsym;(*獲取下一token,應(yīng)為左括號(hào)*)ifsym=lparenthen(*如確為左括號(hào)*)beginrepeat(*依次獲取括號(hào)中的每一個(gè)值,進(jìn)行輸出*)getsym;(*獲得一個(gè)token,這里應(yīng)是一個(gè)標(biāo)識(shí)符*)expression([rparen,comma]+fsys);(*調(diào)用expression過(guò)程分析表達(dá)式,用于出錯(cuò)恢復(fù)的集合中加上右括號(hào)和逗號(hào)*)gen(opr,0,14)(*生成14號(hào)指令:向屏幕輸出*)untilsym<>comma;(*循環(huán)直到遇到的不再是逗號(hào),這時(shí)應(yīng)是右括號(hào)*)ifsym<>rparenthen(*如果不是右括號(hào)*)error(33)(*拋出33號(hào)錯(cuò)誤*)elsegetsym(*正常情況下要獲取下一個(gè)token,為后面準(zhǔn)備好*)end;gen(opr,0,15)(*生成一個(gè)15號(hào)操作的目標(biāo)代碼,功能是輸出一個(gè)換行*)(*由此可知PL/0中的write語(yǔ)句與Pascal中的writeln語(yǔ)句類(lèi)似,是帶有輸出換行的*)endelseifsym=callsymthen(*如果是call語(yǔ)句*)begingetsym;(*獲取token,應(yīng)是過(guò)程名型標(biāo)識(shí)符*)ifsym<>identthen(*如果call后跟的不是標(biāo)識(shí)符*)error(14)(*拋出14號(hào)錯(cuò)誤*)elsebegini:=position(id);(*從符號(hào)表中找出相應(yīng)的標(biāo)識(shí)符*)ifi=0then(*如果沒(méi)找到*)error(11)(*拋出11號(hào)錯(cuò)誤*)elsewithtabledo(*如果找到標(biāo)識(shí)符位于符號(hào)表第i位置*)ifkind=procedurthen(*如果這個(gè)標(biāo)識(shí)符為一個(gè)過(guò)程名*)gen(cal,lev-level,adr)(*生成cal目標(biāo)代碼,呼叫這個(gè)過(guò)程*)elseerror(15);(*如果call后跟的不是過(guò)程名,拋出15號(hào)錯(cuò)誤*)getsym(*獲取下一token,為后面作準(zhǔn)備*)endendelseifsym=ifsymthen(*如果是if語(yǔ)句*)begingetsym;(*獲取一token應(yīng)是一個(gè)邏輯表達(dá)式*)condition([thensym,dosym]+fsys);(*對(duì)邏輯表達(dá)式進(jìn)行分析計(jì)算,出錯(cuò)恢復(fù)集中加入then和do語(yǔ)句*)ifsym=thensymthen(*表達(dá)式后應(yīng)遇到then語(yǔ)句*)getsym(*獲取then后的token,應(yīng)是一語(yǔ)句*)elseerror(16);(*如果if后沒(méi)有then,拋出16號(hào)錯(cuò)誤*)cx1:=cx;(*記下當(dāng)前代碼分配指針位置*)gen(jpc,0,0);(*生成條件跳轉(zhuǎn)指令,跳轉(zhuǎn)位置暫時(shí)填0,分析完語(yǔ)句后再填寫(xiě)*)statement(fsys);(*分析then后的語(yǔ)句*)code[cx1].a:=cx(*上一行指令(cx1所指的)的跳轉(zhuǎn)位置應(yīng)為當(dāng)前cx所指位置*)endelseifsym=beginsymthen(*如果遇到begin*)begingetsym;(*獲取下一個(gè)token*)statement([semicolon,endsym]+fsys);(*對(duì)begin與end之間的語(yǔ)句進(jìn)行分析處理*)whilesymin[semicolon]+statbegsysdo(*如果分析完一句后遇到分號(hào)或語(yǔ)句開(kāi)始符循環(huán)分析下一句語(yǔ)句*)beginifsym=semicolonthen(*如果語(yǔ)句是分號(hào)(可能是空語(yǔ)句)*)getsym(*獲取下一token繼續(xù)分析*)elseerror(10);(*如果語(yǔ)句與語(yǔ)句間沒(méi)有分號(hào),出10號(hào)錯(cuò)*)statement([semicolon,endsym]+fsys)(*分析一個(gè)語(yǔ)句*)end;ifsym=endsymthen(*如果語(yǔ)句全分析完了,應(yīng)該遇到end*)getsym(*的確是end,讀下一token*)elseerror(17)(*如果不是end,拋出17號(hào)錯(cuò)*)endelseifsym=whilesymthen(*如果遇到while語(yǔ)句*)begincx1:=cx;(*記下當(dāng)前代碼分配位置,這是while循環(huán)的開(kāi)始位置*)getsym;(*獲取下一token,應(yīng)為一邏輯表達(dá)式*)condition([dosym]+fsys);(*對(duì)這個(gè)邏輯表達(dá)式進(jìn)行分析計(jì)算*)cx2:=cx;(*記下當(dāng)前代碼分配位置,這是while的do中的語(yǔ)句的開(kāi)始位置*)gen(jpc,0,0);(*生成條件跳轉(zhuǎn)指令,跳轉(zhuǎn)位置暫時(shí)填0*)ifsym=dosymthen(*邏輯表達(dá)式后應(yīng)為do語(yǔ)句*)getsym(*獲取下一token*)elseerror(18);(*if后缺少then,拋出18號(hào)錯(cuò)誤*)statement(fsys);(*分析do后的語(yǔ)句塊*)gen(jmp,0,cx1);(*循環(huán)跳轉(zhuǎn)到cx1位置,即再次進(jìn)行邏輯判斷*)code[cx2].a:=cx(*把剛才填0的跳轉(zhuǎn)位置改成當(dāng)前位置,完成while語(yǔ)句的處理*)end;test(fsys,[],19)(*至此一個(gè)語(yǔ)句處理完成,一定會(huì)遇到fsys集中的符號(hào),如果沒(méi)有遇到,就拋19號(hào)錯(cuò)*)end(*statement*);begin(*block*)dx:=3;(*地址指示器給出每層局部量當(dāng)前已分配到的相對(duì)位置。置初始值為3的原因是:每一層最開(kāi)始的位置有三個(gè)空間用于存放靜態(tài)鏈SL、動(dòng)態(tài)鏈DL和返回地址RA*)tx0:=tx;(*初始符號(hào)表指針指向當(dāng)前層的符號(hào)在符號(hào)表中的開(kāi)始位置*)table[tx].adr:=cx;(*符號(hào)表當(dāng)前位置記下當(dāng)前層代碼的開(kāi)始位置*)gen(jmp,0,0);(*產(chǎn)生一行跳轉(zhuǎn)指令,跳轉(zhuǎn)位置暫時(shí)未知填0*)iflev>levmaxthen(*如果當(dāng)前過(guò)程嵌套層數(shù)大于最大允許的套層數(shù)*)error(32);(*發(fā)出32號(hào)錯(cuò)誤*)repeat(*開(kāi)始循環(huán)處理源程序中所有的聲明部分*)ifsym=constsymthen(*如果當(dāng)前token是const保留字,開(kāi)始進(jìn)行常量聲明*)begingetsym;(*獲取下一個(gè)token,正常應(yīng)為用作常量名的標(biāo)識(shí)符*)repeat(*反復(fù)進(jìn)行常量聲明*)constdeclaration;(*聲明以當(dāng)前token為標(biāo)識(shí)符的常量*)whilesym=commado(*如果遇到了逗號(hào)則反復(fù)聲明下一個(gè)常量*)begingetsym;(*獲取下一個(gè)token,這里正好應(yīng)該是標(biāo)識(shí)符*)constdeclaration(*聲明以當(dāng)前token為標(biāo)識(shí)符的常量*)end;ifsym=semicolonthen(*如果常量聲明結(jié)束,應(yīng)遇到分號(hào)*)getsym(*獲取下一個(gè)token,為下一輪循環(huán)做好準(zhǔn)備*)elseerror(5)(*如果常量聲明語(yǔ)句結(jié)束后沒(méi)有遇到分號(hào)則發(fā)出5號(hào)錯(cuò)誤*)untilsym<>ident(*如果遇到非標(biāo)識(shí)符,則常量聲明結(jié)束*)end;(*此處的常量聲明的語(yǔ)法與課本上的EBNF范式有不同之處:它可以接受像下面的聲明方法,而根據(jù)課本上的EBNF范式不可得出下面的語(yǔ)法:consta=3,b=3;c=6;d=7,e=8;即它可以接受分號(hào)或逗號(hào)隔開(kāi)的常量聲明,而根據(jù)EBNF范式只可接受用逗號(hào)隔開(kāi)的聲明*)ifsym=varsymthen(*如果當(dāng)前token是var保留字,開(kāi)始進(jìn)行變量聲明,與常量聲明類(lèi)似*)begingetsym;(*獲取下一個(gè)token,此處正常應(yīng)為用作變量名的一個(gè)標(biāo)識(shí)符*)repeat(*反復(fù)進(jìn)行變量聲明*)vardeclaration;(*以當(dāng)前token為標(biāo)識(shí)符聲明一個(gè)變量*)whilesym=commado(*如果遇到了逗號(hào)則反復(fù)聲明下一個(gè)變量*)begingetsym;(*獲取下一個(gè)token,這里正好應(yīng)該是標(biāo)識(shí)符*)vardeclaration;(*聲明以當(dāng)前token為標(biāo)識(shí)符的變量*)end;ifsym=semicolonthen(*如果變量聲明結(jié)束,應(yīng)遇到分號(hào)*)getsym(*獲取下一個(gè)token,為下一輪循環(huán)做好準(zhǔn)備*)elseerror(5)(*如果變量聲明語(yǔ)句結(jié)束后沒(méi)有遇到分號(hào)則發(fā)出5號(hào)錯(cuò)誤*)untilsym<>ident;(*如果遇到非標(biāo)識(shí)符,則變量聲明結(jié)束*)(*這里也存在與上面的常量聲明一樣的毛?。号cPL/0的語(yǔ)法規(guī)范有沖突。*)end;whilesym=procsymdo(*循環(huán)聲明各子過(guò)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 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ì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論