pl0語法分析詞法分析語義分析_第1頁
pl0語法分析詞法分析語義分析_第2頁
pl0語法分析詞法分析語義分析_第3頁
pl0語法分析詞法分析語義分析_第4頁
pl0語法分析詞法分析語義分析_第5頁
已閱讀5頁,還剩56頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

PL/0語言是Pascal語言的一個子集,我們這里分析的PL/0的編譯程序包括了對PL/0PL/0語言編譯程序接受以語法分析為核心、一遍掃描的編譯方法。詞法分析和代碼生成作為獨(dú)立的子程序供語法分析程序調(diào)用。語法分析的同時,供應(yīng)了出錯報告和出錯恢復(fù)的功能。在源程序沒有錯誤編譯通過的狀況下,調(diào)用類PCODE解釋程序解釋執(zhí)行生成的類詞法分析子程序名為getsym,功能是從源程序中讀出一個單詞符號〔token〕,把它的信息放入全局變量sym.id和num中,語法分析器需要單詞時,直接從這三個變量中(留意!語法分析器每次用完這三個變量的值就馬上調(diào)用getsym子程序獵取新的單詞供下一次使用。而不是在需要新單詞時才調(diào)用getsymiS程?!砱etsym過程通過反復(fù)調(diào)用getch子過程從源程序過獵取字符,并把它們拼成單詞。getch過程中使用了行緩沖區(qū)技術(shù)以提詞法分析器的分析過程:調(diào)用getsym時,它通過getch過程從源程序中獲得一個字符。假如這個字符是字母,那么連續(xù)獵取字符或數(shù)字,最終可以拼成一個單詞,查保存字表,假如查到為保存字,那么把sym變量賦成相應(yīng)的保存字類型值;假如沒有查到,那么這個單詞應(yīng)是一個用戶自定義的標(biāo)識符(可能是變量名、常量名或是過程的名字),把sym置為ident,把這個單詞存入id變量。查保存字表時使用了二分法查找以提高效率。假如getch獲得的字符是數(shù)字,那么連續(xù)用getch獵取數(shù)字,并把它們拼成一個整數(shù),然后把sym置為number,并把拼成的數(shù)值放入num變量。假如識別出其它合法的符號(比方:賦值號、大于號、小于等于號等),那么把sym那么成相應(yīng)的類型。假如遇到不合法的字符,把sym置成nul。語法分析子程序接受了自頂向下的遞歸子程序法,語法分析同時也依據(jù)程序的語意生成相應(yīng)的代碼,并供應(yīng)了出錯處理的機(jī)制。語法分析主要山分程序分析過程(block)>常量定義分析過程(constdeclaration)>變量定義分析過程(vardeclaration)>語句分析過程(statement)>表達(dá)式處理過程(expression)>項處理過程(term)>因子處理過程(factor)和條件處理過程(condition)構(gòu)成。這些過程在結(jié)構(gòu)上構(gòu)成一個嵌套的層次結(jié)構(gòu)。除此之外,還有出錯報告過程(error)、代碼生成過程(gen)>測試單詞合法性及出錯恢復(fù)過程(test)、登錄名字表過程本編譯程序在運(yùn)行的時候,通過主程序中調(diào)用分程序處理過程block來分析分程序局部(分程序分析過程中還可能會遞歸調(diào)用block過程),然后,推斷最終讀入的符號是否為句號。假如是句號且分程序分析中未出錯,那么是一個合法的PL/0程序,可以運(yùn)行生成的代碼,否那么就說明源PL/0程序是不合法的,輸出出錯提示即下面按各語法單元分析PL/0編譯程序的運(yùn)行機(jī)制。語法分析開頭后,首先調(diào)用分程序處理過程(block)處理分程序。過程入口參數(shù)置為:0層、符號表位置0、出錯恢復(fù)單詞集合為句號、聲明符或語句開頭符。進(jìn)入block過程后,首先把局部數(shù)據(jù)段安排指針設(shè)為3,預(yù)備安排3個單元供運(yùn)行期存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。然后用tx0生一條jmp指令,預(yù)備跳轉(zhuǎn)到主程序的開頭位置,由于當(dāng)前還沒有知到主程序到底在何處開頭,所以jmp的LI標(biāo)臨時填為0,稍后再改。同時在符號表的當(dāng)前位置記錄下這個jmp指令在代碼段中的位置。在推斷了嵌套層數(shù)沒有超過規(guī)定的層數(shù)后,開始分析源程序。首先推斷是否遇到了常量聲明,假如遇到那么開頭常量定義,把常量存入符號表。接下去用同樣的方法分析變量聲明,變量定義過程中會用dx變量記錄下局部數(shù)據(jù)段安排的空間個數(shù)。然后假如遇到procedure保存字那么進(jìn)行過程聲明和定義,聲明的方法是把過程的名字和所在的層次記入符號表,過程定義的方法就是通過遞歸調(diào)用block過程,由于每個過程都是一個分程序。由于這是分程序中的分程序,因此調(diào)用block時需把當(dāng)前的層次號lev加一傳遞給block過程。分程序聲明局部完成后,即將進(jìn)入語句的處理,這時的代碼安排指針ex的值正好指向語句的開頭位置,這個位置正是前面的jmp指令需要跳轉(zhuǎn)到的位置。于是通過前面記錄下來的地址值,把這個jmp指令的跳轉(zhuǎn)位置改成當(dāng)前ex的位置。并在符號表中記錄下當(dāng)前的代碼段安排地址和局部數(shù)據(jù)段要安排的大小〔dx的值〕。生成一條int指令,安排dx個空間,作為這個分程序段的第一條指令。下面就調(diào)用語句處理過程statement分析語句。分析完成后,生成操作數(shù)為0的opr指令,用于從分程序返回(對于0層的主程序來說,就是程序運(yùn)行完成,退出)。通過循環(huán),反復(fù)獲得標(biāo)識符和對應(yīng)的值,存入符號表標(biāo)識符的名字、它所在的層及它在所在層中的偏移地址。語句處理過程是一個嵌套子程序,通過調(diào)用表達(dá)式處理、項處理、因子處理等過程及遞歸調(diào)用自己來實現(xiàn)對語句的分析。語句處理過程可以識別的語句包括賦值語句、read語句、write語句、call語句、過語句、while語句。當(dāng)遇到begin/end語首先獵取賦值號左邊的標(biāo)識符,從符號表中找到它的信息,并確認(rèn)這個標(biāo)識符確為變量名。然后通過調(diào)用表達(dá)式處理過程算得賦值號右部的表達(dá)式的值并生成相應(yīng)的指令保證這個值放在運(yùn)行期的數(shù)據(jù)棧頂。最終通過前面查到的左部變量的位置信息,生成相應(yīng)的sto指令,把棧頂值存入指定的變量的空間,實現(xiàn)了賦值操作。確定read語句語法合理的前提下(否那么報錯),生成相應(yīng)的指令:第一條是的opr指令,實現(xiàn)從標(biāo)準(zhǔn)輸入設(shè)備上讀一個整數(shù)值,放在數(shù)據(jù)棧頂。其次條是sto指令,把棧頂?shù)闹荡嫒雛ead語句括號中的變量所在的單元。與read語句相像。在語法正確的前提下,生成指令:通過循環(huán)調(diào)用表達(dá)式處理過程分析write語句括號中的每一個表達(dá)式,生成相應(yīng)指令保證把表達(dá)式的值算出并放到數(shù)據(jù)棧頂并生成14號操作的opr指令,輸出表達(dá)式的值。最終生成15號操從符號表中找到call語句右部的標(biāo)識符,獲得其所在層次和偏移地址。然后生成相應(yīng)的cal指令。至于調(diào)用子過程所需的愛護(hù)現(xiàn)場等工作是111類PCODE解釋程按if語句的語法,首先調(diào)用規(guī)律表達(dá)式處理過程處理過語句的條件,把相應(yīng)的真假值放到數(shù)據(jù)棧頂。接下去記錄下代碼段安排位置(即下面生成的jpc指令的位置),然后生成條件轉(zhuǎn)移jpc指令〔遇0或遇假轉(zhuǎn)移〕,轉(zhuǎn)移地址未知臨時填0o然后調(diào)用語句處理過程處理then語句后面的語句或語句塊。then后的語句處理完后,當(dāng)前代碼段安排指針的位置就應(yīng)當(dāng)是上面的jpc指令的轉(zhuǎn)移位置。通過前面記錄下的jpc指令的位置,把它的跳轉(zhuǎn)位置改成當(dāng)前的代碼段指針位置。通過循環(huán)遍歷begin/end語句塊中的每一個語句,通過遞歸調(diào)用語句分析過程首先用cxl變量登記當(dāng)前代碼段安排位置,作為循環(huán)的開頭位置。然后處理while語句中的條件表達(dá)式生成相應(yīng)代碼把結(jié)果放在數(shù)據(jù)棧頂,再用cx2變量登記當(dāng)前位置,生成條件轉(zhuǎn)移指令,轉(zhuǎn)移位置未知,填0。通過遞歸調(diào)用語句分析過程分析do語句后的語句或語句塊并生成相應(yīng)代碼。最終生成一條無條件跳轉(zhuǎn)指令jmp,跳轉(zhuǎn)到cxl所指位置,并把cx2所指的條件跳轉(zhuǎn)指令的跳轉(zhuǎn)位置改成當(dāng)前代碼段安排位置。依據(jù)PL/0語法可知,表達(dá)式應(yīng)當(dāng)是山正負(fù)號或無符號開頭、由假設(shè)干個項以加減號連接而成。而項是山假設(shè)干個因子以乘除號連接而成,因子那么可能是一個標(biāo)識符或一個數(shù)字,或是一個以括號括起來的子表達(dá)式。依據(jù)這樣的結(jié)構(gòu),構(gòu)造出相應(yīng)的過程,遞歸調(diào)用就完成了表達(dá)式的處理。把項和因子獨(dú)立開處理解決了加減號與乘除號的優(yōu)先級問題。在這兒個過程的反復(fù)調(diào)用中,始終傳遞fsys變量的值,保證可以在出錯的狀況下跳過出錯的符號,使分析過程得以進(jìn)行下去。首先推斷是否為一元規(guī)律表達(dá)式:判奇偶。過程分析計算表達(dá)式的值,然后生成判奇指令。假如不是,那么確定是二元規(guī)律運(yùn)算處理過程依次分析運(yùn)算符左右兩局部的值,放在棧頂?shù)膬蓚€空間中,然后依不同的規(guī)律運(yùn)算符,生成相應(yīng)的規(guī)律推斷指令,放入代碼段。推斷單詞合法性與出錯恢復(fù)過程分析:測試當(dāng)前符號(即sym變量中的值)是否在si集合中,假如不在,就通過調(diào)用出錯報告過程輸出出錯代碼n,并放棄當(dāng)前符號,通過詞法分析過程獵取一下單詞,直到這個單詞消滅在si或s2集合中為止。這個過程在實際使用中很機(jī)敏,主要有兩個用法:在進(jìn)入某個語法單位時,調(diào)用本過程,檢查當(dāng)前符號是否屬于該語法單位的開始符號集合。假設(shè)不屬于,那么濾去開頭符號和后繼符號集合外的全部符號。在語法單位分析結(jié)束時,調(diào)用本過程,檢查當(dāng)前符號是否屬于調(diào)用該語法單位時應(yīng)有的后繼符號集合。假設(shè)不屬于,那么濾去后繼符號和開頭符號集合外的全部通過這樣的機(jī)制,可以在源程序消滅錯誤時,準(zhǔn)時跳過出錯的局部,保證語法分析可以連續(xù)下去。語法分析過程中調(diào)用的其它子過程相比照擬簡潔,請參考源程序的注釋。數(shù)據(jù)段用于存放運(yùn)行期數(shù)據(jù)、擁有一個代碼段用于存放類PCODE程序代碼。同時還擁用數(shù)據(jù)段安排指針、指令指針、指令存放器、局部段基址指針等存放器。對于源程序的每一個過程(包括主程序),在被調(diào)用時,首先在數(shù)據(jù)段中開辟三個空間,存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。靜態(tài)鏈記錄了定義該過程的直接外過程(或主程序)運(yùn)行時最新數(shù)據(jù)段的基地址。動態(tài)鏈記錄調(diào)用該過程前正在運(yùn)行的過程的數(shù)據(jù)段基址。返回地址記錄了調(diào)用該過程時程序運(yùn)行的斷點(diǎn)要引用它的直接或間接父過程(這里的父過程是按定義過程時的嵌套悄況來定的,而不是按執(zhí)行時的調(diào)用挨次定的)的變量時,可以通過靜態(tài)鏈,跳過個數(shù)為層差的數(shù)據(jù)段,找到包含要引用的變量所在的數(shù)據(jù)段基址,然后通過偏移地址訪問它。在過程返回時,解釋程序通過返回地址恢復(fù)指令指針的值到調(diào)用前的地址,通過當(dāng)前段基址恢復(fù)數(shù)據(jù)段安排指針,通過動態(tài)鏈恢復(fù)局部段基址指針。實現(xiàn)子過程的返回。對于主程序來說,解釋程序會遇到返回地址為0的請況,這時就認(rèn)為程序運(yùn)行結(jié)束。層數(shù)的局部數(shù)據(jù)段基址。這在使用st。、lod等訪問局部變量的指令中會經(jīng)常用相應(yīng)的動作。當(dāng)遇到主程序中的返回指令時,指令指針會指到0位置,把這樣一個條件作為終至循環(huán)的條件,保證程序運(yùn)行可以正常的結(jié)束。programpl0(input,output));(*PL/0編譯程序與代碼生成解釋運(yùn)行程序*)label99;(*聲明出錯跳轉(zhuǎn)標(biāo)記*)norw二11;(*ofreservedwords*)(*保存字的個數(shù)*)txmax=100;(*lengthofidentifiertable*)(*標(biāo)識符表的長度(容ft)*)nmax=14;(水maxnumberofdigitsinnumbers*)(*數(shù)字允許的最長位數(shù)*)al=10;(*lengthofidentifiers*)(*標(biāo)識符最長長度*)amax=2047;(*maximumaddress*)(水尋址空間水)levmax=3;(*maxdepthofblocknesting*)(*最大允許的塊嵌套層數(shù)*)容納代碼行數(shù))*)symbol=(nul,ident,number,plus,minus,times,sllss,leq,gtr,geq,lparen,rparen,comma,semicolon,pbeginsym,endsym,ifsym,thensconstsym,varsym,procsym);(*symobl類型標(biāo)識了不同類型的詞匯*)alfa=packedarray[1..al]ofchar;(*alfa類型用于標(biāo)識符*)objectl=(constant,variable,procedur);(*objectl為三種標(biāo)識符的fct=(lit,opr,lod,sto,cal,int,jmp,jpc);(*fct類型分別標(biāo)識a:0.·amax;(*displacementaddrjmp0,ajumptoajpc0,ajumpconditionaltoaid:alfa;(*lastcc:integer;(*charactercount*)(*行緩沖區(qū)指針*)塊總在ex所指位置生成新的代碼*)line:array[1..81]ofchar;(*行緩沖區(qū),用于從文件讀出一行,供詞法分析獵取單詞時之用*)a:alfa;&詞法分析器中用于臨時存放正在分析的詞*)word:array[1..norw]ofalfa;(*保存字表*)類型*)ssym:array]ofsymbol:(*一些符號對應(yīng)的symbol類型表*)和項開頭符號集合*)table:array[0..txmaxZofrecord(*constant:(*假如是常量名*)variable,procedur:(*假如是變量名或過程名*)(level,adr,size:integer)(*存放層差、偏移地址和大小*)fin,fout:text;(*fin文本文件用于指向輸入沒有用到*)sfile:string;(*存放PL/0源程序文件的文件名*)procedureerror〔n:integer〕;writein('****',’’:ccT,n:2〕;(*在屏幕ccT位置顯示!與出錯代碼提示,由于ccwritein(fal,’:cc~1,n:2);(*在文件與出錯代碼提示*)err:二err+1(*出錯總次數(shù)加一*)(*讀取原程序中下一個字符過程getch*)ifcc二11then〔*假如行緩沖區(qū)指針指向行緩沖區(qū)最終一個字符就從文件讀一行到行緩沖區(qū)*)ifeof(fin)then(*假如到達(dá)文件末尾*)writeProgramincomplete');(*出錯,退出程序水)11:=0;(*行緩沖區(qū)長度置0*)cc:=0;(*行緩沖區(qū)指針置行首*)write〔cx:4,’’〕;&輸出ex值,寬度為4*〕write(fal,ex:4,’’);(*輸出ex值,寬度為4到文件*)whilenoteoln(fi11:=11+1;(*行緩沖區(qū)長度加一*)(*可見,PL/0源程序要求每行的長度都小于81個字符*)read(fin,ch);(*我添加的代碼。由于PC上文本文件換行是以所以要把多余的LF從文件讀出,這里放在ch變量中是由于ch變量的值在下面即將被轉(zhuǎn)變,把這個多余值放在ch中沒有問題*)writein(fal);ifchin[’d’..’z’]then(*假如讀出的字符是一個字母,說明是保存字或k:=0;(廉標(biāo)識符緩沖區(qū)指針置0*)repeat(*這個循環(huán)用于依次讀出源文件中的字符構(gòu)成標(biāo)識符粉過k<althen(*假如標(biāo)識符長度沒有超過最大標(biāo)識符長度(假如超過,就取前面一局部,把多余的拋棄)*)以字母開頭,后面跟假設(shè)干個字母或數(shù)字*)ifk>=kkthen(*假如當(dāng)前獲得的標(biāo)識符長度大于等于kk*)repeat〔*這個循環(huán)用于把標(biāo)識符緩沖后部沒有填入相應(yīng)字母或空格的空間識符長度小于kk,就把a(bǔ)數(shù)組的后部沒有字母的空間用空格補(bǔ)足。假如讀到的標(biāo)識符長度大于等于kk,這時就不必在后面填空格了,由于它的后面確定全是空格。反之假如最近讀到的標(biāo)識符長度小于kk,那就需要從kk位置向前,把超過當(dāng)前標(biāo)識長度的空間填滿空格。以上的這樣一個規(guī)律,完全是出于程序性能的上考慮。其實完全可以簡潔的把a(bǔ)數(shù)組中a[k]元素以后的空間不管三七二十一全填空格。(*下面開頭二分法查找看讀出的標(biāo)識符是不是保存字之一*)ifid<=word[k]then(*假如當(dāng)前的標(biāo)識符小于k所指的保存字*)j:=k-1;(*移動j指針*)ifid>=word[k]then(*假如當(dāng)前的標(biāo)識符大于k所指的保存字*)中存的是保存字*)sym:=wsym[k]&找到保存字,把sym置為相應(yīng)的保存字值*)sym:二ident〔*未找到保存字,把sym置為ident類型,表示是標(biāo)識符*〕end(*至此讀出字符為字母即對保存字或標(biāo)識符的處理結(jié)束*)else(*假如讀出字符不是字母*)ifchin[’0'..’9’]then(*假如讀出字符是數(shù)字*)begin(*number*)(*開頭對數(shù)字進(jìn)行處理*)k:二0;(*數(shù)字位數(shù)*)num:二0;(*數(shù)字置為0*)sym:=number:(*置sym為number,表示這一次讀到的是數(shù)字*)repeat(*這個循環(huán)依次從源文件中讀出字符,組成數(shù)字粉num:=10*num+(ord(ch)-ord('0'));〔*num*10加上最近讀出的k:二k+1;(*數(shù)字位數(shù)加一*)〔*發(fā)出30號錯*〕end(*至此對數(shù)字的識別處理結(jié)束時過ch二’:then(*假如讀出的不字母也不是數(shù)字而是冒號時sym:二becomes;〔*sym的類型設(shè)為賦值號becomes*〕sym:=nul;(*假如不是讀到等號,那單獨(dú)的一個冒號就什么也不是*)end仕以上完成對賦值號的處理*)else〔*假如讀到不是字母也不是數(shù)字也不是冒號粉ifch=*<*then(*假如讀到小于號*)getch;〔*再讀一個字符*〕ifch=*二’then(*假如讀到等號*)sym:=leq;(*購成一個小于等于號*)else(*假如小于號后不是跟的等號*)sym:=1ss〔*那就是一個單獨(dú)的小于號*〕else(*假如讀到不是字母也不是數(shù)字也不是冒號也不是小于號*)過ch二’〉’then(*假如讀到大于號,處理過程類似getch;(*再讀一個字符歡)sym:二geq;(*購成一個大于等于號*)else〔*假如大于號后不是跟的等號*〕sym:=gtr〔*那就是一個單獨(dú)的大于號*〕else〔*假如讀到不是字母也不是數(shù)字也不是冒號也不是小于號也不是大于號begin(*那就說明它不是標(biāo)識符/保存字,也不是簡單的雙字節(jié)操作符,應(yīng)該是一個一般的符號*)sym:=ssym[ch]:〔*直接成符號表中查到它的類型,賦給sym*〕getch(*讀下一個字符*)(*整個過語句推斷結(jié)束*)(*詞法分析過程getsym總結(jié):從源文件中讀出假設(shè)干有效字符,組成一個token串,識別它的類型為保存字/標(biāo)識符/數(shù)字或是其它符號。假如是保存字,把sym置成相應(yīng)的保存字類型,假如是標(biāo)識符,把sym置成ident表示是標(biāo)識符,于此同時,id變量中存放的即為保留字字符串或標(biāo)識那么直接把sym置成相應(yīng)類型。經(jīng)過本過程后ch變量中存放的是下一個即將被識別的字符*)(*y,z:代碼的兩個操作數(shù)*)(*本過程用于把生成的U標(biāo)代碼寫入U標(biāo)代碼數(shù)組,供后面的解釋器解釋執(zhí)行*)proceduregen(x:fct;y,z:integer);ifex>exmaxthen〔*假如cx>cxmax表示半前生成的代碼行號大于允許的最大代碼行數(shù)*)(*測試當(dāng)前單詞是否合法過程test*)集(*s2:在某一出錯狀態(tài)下,可恢復(fù)語法分析正常工作的補(bǔ)充單詞集合*)(*n:出錯信息編號,當(dāng)當(dāng)前符號不屬于合法的si集合時發(fā)出的出錯信息時proceduretest(si,s2:symset:n:integer):ifnot(syminsi)then(*假如當(dāng)前符號不在si中*)error〔n〕;(*發(fā)出n號錯誤水)si:二si+s2;(*把s2集合補(bǔ)充進(jìn)si集合*)whilenot(syminsi)do(*通過循環(huán)找到下一個合法的符號,以恢復(fù)語法分析工作*)getsym(*fsys:用于出錯恢復(fù)的單詞集合*)procedureblock(lev,tx:integer;fsys:symset);dx:integer;〔*dataallocationindex*〕(*數(shù)據(jù)段內(nèi)存安排指針,指向下一個被安排空間在數(shù)據(jù)段中的偏移位置*)位置時(*記錄本層開頭時代碼段安排(*登陸符號表過程enter◆)begin(*enterobjecttx:=tx+1;(*符號表指針指向一個新的空位紂withtable[tx]do(*開頭登錄*)name:=id;(*name是符號的名字,對于標(biāo)識符,這里就是標(biāo)識符的名字時casekof(*依據(jù)不同的類型進(jìn)行不同的操作*)constant:(*假如是常量名*)過num>amaxthen(*在常量的數(shù)值大于允許的最大值的惜況下*)拋出31號錯誤時實際登陸的數(shù)字以0代替*)val:二num(*如是合法的數(shù)值,就登陸到符號表*)dx:=dx+1;(*偏移量自增一,為下一次做好預(yù)備*)procedur:(*假如要登陸的是過程名*)(*登錄符號過程沒有考慮到重復(fù)的定義的問題。假如消滅重復(fù)定義,那么以最終一次的定義為準(zhǔn)。*〕(*在符號表中查找指定符號所在位置的函數(shù)position*)(*返回值:要找的符號在符號表中的位置,假如找不到就返回0*)table[01.name:=id;〔*先把id放入符號表0號位置*〕position:=i(*返回找到的位置號,假如沒找到那么肯定正procedureconstdeclaration;符*)getsym;(水獵取下一個token*)〔1〕;〔*拋出1號錯誤*)(*這里其實自動進(jìn)行了錯誤訂正使編譯連續(xù)進(jìn)行,把賦值號當(dāng)作等號處理getsym;〔*獵取下一個token,等號或賦值號后應(yīng)接上數(shù)字*〕過sym=numberthen〔*假如的確是數(shù)字*〕enter〔constant〕;(*把這個常量登陸到符號表*)error(2)(*假如等號后接的不是數(shù)字,拋出2號錯誤時error(3)仕假如常量標(biāo)識符后接的不是等號或賦值號,拋出3號錯誤水)error(4)(*假如常量聲明過程遇到的第一個符號不為標(biāo)識符,拋出4號錯誤procedurevardeclaraifsym二identthen(*變量聲明過程開頭遇到的第一個符號必定應(yīng)為標(biāo)識符enter(variable);(*將標(biāo)識符登陸到符號表中*)getsym(*獵取下一個token,為后面作預(yù)備*)error(4)(*假如變量聲明過程遇到的第一個符號不是標(biāo)識符,拋出4號錯誤end(*vardeclaration水);iflistswitchthen〔*假如用戶選擇是要列出代碼的狀況下才列出代碼*〕fori:二cx0toex-1do(*從當(dāng)前層代碼開頭位置到當(dāng)前代碼位置T處,即為本分程序塊*)助記符和L與A操作數(shù)*)(*我修改的代碼:原程序此處在輸出i時,沒有指定占4個字符寬度,不美觀也與下面一句不配套。*)procedurestatemeexpression〔fsys:symset〕;procedureterm〔fsys:symset〕;procedurefactor(fsys:symset);test(facbegsys,fsys,24);(*開頭因子處理前,先檢查當(dāng)前token是否在(*假如不是合法的token,拋24號錯誤,并通過fsys集恢復(fù)使語法處理可以ifsym=identthen(*假如遇到的是標(biāo)識符*)ifi=0then(*假如查符號表返回為0,表示沒有找到標(biāo)識符*)error(11)(*拋出11號錯誤*)WithtableEi]do〔*假如在符號表中找到了當(dāng)前標(biāo)識符的位置,開頭生成相casekindofconstant:gen(lit,0,val):〔*假如這個標(biāo)識符對應(yīng)的是常量,值為val,生成lit指令,把Vdl放到棧頂*)variable:gen(lod,lev-level,adr);〔*假如標(biāo)識符是變量名,生成(*把位于距離當(dāng)前層level的層的偏移地址為adr的變量放到棧頂*)procedur:error〔21〕&假如在因子處理中遇到的標(biāo)識符是過程名,出錯了,拋21號錯*]ifnum>amaxthen〔*假如數(shù)字的大小超過允許最大值num:二0(*把數(shù)字按0值處理*)gen(lit,0,num);〔*生成lit指令,把這個數(shù)值字面常量放到棧頂*〕ifsym=lparenthen(*假如遇到的是左括號*)getsym;(*獵取一個token*)expression([rparen]+fsys);〔*遞歸調(diào)用expression子程序分析一個子表達(dá)式*)ifsym=rparenthen(*子表達(dá)式getsym〔*假如的確遇到右括號,讀取下一個tokerror〔22〕(*否那么拋出22test(fsys,facbegsys,23)(*一個因子處理完畢,遇到的token應(yīng)在fsys集合中*)(*假如不是,拋23號錯,并找到下一個因子的開頭,使語法分析可以連續(xù)運(yùn)行下去*)factor([times,slash]+fsys);〔*每一個項都應(yīng)當(dāng)由因子開頭,因此調(diào)用factor子程序分析因子*)mulop:二sym;(*保存當(dāng)前運(yùn)算符*)factor〔fsys+[times,slash]〕;〔*運(yùn)算符后應(yīng)是一個因子,故調(diào)factor(*生成乘法指令*)(*不是乘號肯定是除號,生成除法指令*)ifsymin[plus,minus]then示正負(fù)號*)beginaddop:二sym;〔*把當(dāng)前的正號或負(fù)號保存起getsym;(*獵取一個toterm〔fsys+[plus,minus]〕:〔*正負(fù)號后面應(yīng)當(dāng)是一個項,調(diào)term子程序分析*)ifaddop=minusthen(*假如保存下來的符號是負(fù)號*)gen〔opr,0,1〕(*生成一條1號操作指令:取反運(yùn)算*)(*假如不是負(fù)號就是正號,不需生成相應(yīng)的指令*)else(*假如不是山正負(fù)號開頭,就應(yīng)是一個項開頭*)whilesymin[plus,minus]do〔*項后應(yīng)是加運(yùn)算或減運(yùn)算*〕getsym;〔*獵取下一個token,加減運(yùn)算符后應(yīng)跟的是一個項*〕ifaddop=plusthegen(opr,0,3)(*生成3號操作指令:減法*)(*條件處理過程condition◆)procedurecondition(frelop:symbol;〔*用于臨時記錄token(這里肯定是一個二元規(guī)律運(yùn)算符)的內(nèi)容*)begirifsym=oddsymthen(*假如是odd運(yùn)算符(一元)*)getsym;(*獵取下一個token*)gen〔opr,0,6〕;〔生成6號操作指令:奇偶推斷運(yùn)算*〕else(*假如不是odd運(yùn)算符(那就肯定是二元規(guī)律運(yùn)算符)*)expression([eql,neq,1ss,leq,gtr,geq]+fsys);(*對表達(dá)式左部進(jìn)行處理計算*)ifnot(symin[eql,neq,lss,leg,gtr,geq])then(*假如token不是邏輯運(yùn)算符中的一個*)error(20)(*拋出20號錯誤*)relop:二sym;(*記錄下當(dāng)前的規(guī)律運(yùn)算符*)getsym;(*獵取下一個token*)expression(fsys);(*對表達(dá)式右部進(jìn)行處理訃算*)caserelopof(*假如剛剛的運(yùn)算符是下面的一種粉eql:gen(opr,0,8);(*等號:產(chǎn)生8號判等指令時neq:gen(opr,0,9);(*不等號:產(chǎn)生9號判不等指令粉lssgen(opr,0,1(歡大于等號號:產(chǎn)生11號判不小于指令*)leq:gen(opr,0,13);(*小于等于號:產(chǎn)生13號判不大于指令*)i:二position(id);(*在符號表中查到該標(biāo)識符所在位置*)ifi=0then(*假如沒找到*)error(11)(*拋出11號錯誤*)iftable[i].kind<>variablethen(*假如在符號表中找到該標(biāo)識符,但該標(biāo)識符不是變量名*)error(12);(*拋出12號錯誤水)error(34)(*拋出34號錯誤*)ifsym=identthen(*假如確為一個標(biāo)識符*)(*這里略有問題,還應(yīng)推斷一下這個標(biāo)識符是不是變量名,假如是常量名或過程名應(yīng)出錯*)i:=position(id)(*查符號表,找到它所在位置給i,找不到時i會為0i:二0;(*不是標(biāo)識符那么有問題,i置0作為出錯標(biāo)志*)ifi=0then(*假如有錯誤*)error〔35〕(*拋出35號錯誤*)else〔*否那么生成相應(yīng)的口標(biāo)代碼*〕gen(sto,lev-level,adr)(*生成sto指令,把讀入的值存入指定變量所在的空間*)右括號*)untilsym<>comma;〔*不斷生成代碼直到read語句的參數(shù)表中的變量遍歷完為止,這里遇到不是逗號,應(yīng)為右括號*)ifsym<>rparenthen(*假如不是我們預(yù)想中的右括號*)whilenot〔syminfsys〕do(*依靠fsys集,找到下一個合法的token,恢復(fù)語法分析*)getsym〔*假如read語句正常結(jié)束,得到下一個token,一般為分號或end*〕過sym=writesymthen〔*假如遇到/write語句*〕getsym;(*獵取下一token,應(yīng)為左括號*)repeat〔*依次獵取括號中的每一個值,進(jìn)行輸出*〕getsym;〔*獲得一個token,這里應(yīng)是一個標(biāo)識符*〕expression〔[rparen,comgen(opr,0,14)(*生成14號指令:向屏幕輸出*)untilsym>comma;〔*循環(huán)直到遇到的不再是逗號,這時應(yīng)是右括號*〕error(33)(*拋出33號錯誤*)gen〔opr,0,15〕&生成一個13號操作的目標(biāo)代碼,功能是輸出一個換行*)(*由此可知PL/0中的write語句與Pascal中的writein語句類似,是帶有輸出換行的水)error(11)(*拋出11號錯誤*)error(15);&假如call后跟的不是過程名,拋出15號錯誤粉getsym;〔*獵取一token應(yīng)是一個規(guī)律表達(dá)式*〕錯恢復(fù)集中參與then和do語句*)cxl:二ex;&登記當(dāng)前代碼安排指針位置*)生成條件跳轉(zhuǎn)指令,跳轉(zhuǎn)位置臨時填0,分析完語句后再statement〔fsys〕;(*分析thencodeEcxl1.a-cx(*上一行指令(cxl所指的)的跳轉(zhuǎn)位置應(yīng)為當(dāng)前ex所指位置進(jìn)行分析處理*)whilesymin[semicolon]+statbegsysdo〔*假如分析完一句后遇到分號或語句開頭符循環(huán)分析下一句語句*)ifsym=semicolonthen〔*假如語句是分號(可能是空語句〕*)getsym〔*獵取下一token連續(xù)分析*〕error(10);&假如語句與語句間沒有分號,出10號錯*)statement([semicolon,error(17)(*假如不是end,拋出17號錯*)cxl:二ex;&登記當(dāng)前代碼安排位置,這是while循環(huán)的開頭位置*)condition〔[dosym]+fsys〕;〔*對這個規(guī)律表達(dá)式進(jìn)行分析汁算*)cx2:=ex;&登記當(dāng)前代碼安排位置,這是while的do中的語句的開頭位置*)gen(jpc,0,0);&生成條件跳轉(zhuǎn)指令,跳轉(zhuǎn)位置臨時填0*)過sym=dosymthen〔*規(guī)律表達(dá)式后應(yīng)為do語句*〕error(18);(*if后缺少then,拋出18號錯誤*)statement(fsys);(*分析do后的語句塊*)gen(jmp,0,cxl);住循環(huán)跳轉(zhuǎn)到cx1位置,即再次進(jìn)行規(guī)律推斷時code[cx2].a:=ex(*把剛剛填0的跳轉(zhuǎn)位置改成當(dāng)前位置,完成while語號,假如沒有遇到,就拋19號錯*)dx:二3;(*地址指示器給出每層局部量當(dāng)前已安排到的相對位置。置初始值為3的緣由是:每一層最開頭的位置有三個空間用于存放靜態(tài)鏈tx0:二tx;&初始符號表指針指向當(dāng)前層的符號在符號表中的開頭位置*)table[txl.adr:=ex;(*符號表當(dāng)前位置登記當(dāng)前層代碼的開頭位置*)gen(jmp,0,0);&產(chǎn)生一行跳轉(zhuǎn)指令,跳轉(zhuǎn)位置臨時未知填0*)iflev>levmaxthen&假如當(dāng)前過程嵌套層數(shù)大發(fā)出32號錯誤*)repeat(*開頭循環(huán)處理源程序中全部的聲明局部*)ifsym=constsymthen〔*假如當(dāng)前token是const保存字,開頭進(jìn)行常量聲getsym;〔*獵取下一個token,正常應(yīng)為用作常量名的標(biāo)識符*〕repeat(*反復(fù)進(jìn)行常量聲明*)constdeclaration;(*聲明以肖前token為標(biāo)識符的常量*)whilesym=commado(*假如遇到了逗號那么反復(fù)聲明下一個常量*)聲明以的前token為標(biāo)識符的常量*)end;ifsym=semicolonthen(*假如常量聲明結(jié)束,應(yīng)遇到分號*)getsym〔*獵取下一個token,為下一輪循環(huán)做好預(yù)備*〕error(5)(*假如常量聲明語句結(jié)束后沒有遇到分號那么發(fā)出5號錯誤*)untilsym>ident(*假如遇到非標(biāo)識符,那么常量聲明結(jié)束*)它可以接受像下面的聲明方法,而依據(jù)課本上的EBNF范式不行得出下面的語即它可以接受分號或逗號隔開的常量聲明,而依據(jù)EBNF范式只可接受用逗號getsym;(*獵取下一個token,此處正常應(yīng)為用作變量名的一個標(biāo)識符*)vardeclaration;whilesym=commado(*假如遇到了逗號那么反復(fù)聲明下一個變量*)getsym;〔*獵取下一個token,這里正好應(yīng)當(dāng)是標(biāo)識符*〕vardeclaration;〔*聲明以肖前token為標(biāo)識符的變量*〕ifsym=semicolonthen〔水假如變量聲明結(jié)束,應(yīng)遇到分號*〕getsym(*獵取下一個token,error(5)(*假如變量聲明語句結(jié)束后沒有遇到分號那么發(fā)出5號錯誤*)untilsym>ident;(*假如遇到非標(biāo)識符,那么變量聲明結(jié)束*)(*這里也存在與上面的常量聲明一樣的毛?。号cPL/0的語法標(biāo)準(zhǔn)有沖突。*)ifsym=identthen(*假如token確為enter(procedur);〔*把這個過程登錄到名字表中*)error〔4〕;&否那么拋出4號錯誤水)ifsym=semicolonthengetsym〔*獵取下一,個token,預(yù)備進(jìn)行語法分析的遞歸調(diào)用*〕error〔5〕;(*否那么拋出5號錯誤*)block〔lev+1,tx,[semicolon]+fsys〕前層次加一,同時傳遞表頭索引、合法單詞符*)個end后的分號*)test(statbegsys+[ident,procsym],fsys,6);(*檢查當(dāng)前tok合法,不合法那么用fsys恢復(fù)語法分析同時拋6號錯*)error(5)(*假如過程聲明后的符號不是分號,拋出5號錯誤*)test(statbegsys+[ident],不合法那么用聲明開頭符號作出錯恢復(fù)、拋7號錯*〕untilnot(symindeclbegsys);(*直到聲明性的源程序分析完畢,連續(xù)向下執(zhí)行,分析主程序*)codeEtableEtx01.adrl.a:二ex;&把前面生成的跳轉(zhuǎn)語句的跳轉(zhuǎn)位置改成當(dāng)前位置*)withtable[tx0]do(*在符號表中記錄*)size:二dx;&長度為當(dāng)前數(shù)據(jù)代安排位置粉gen(int,0,dx);(*生成安排空間指令,安排dx個空間水)塊*)gen(opr,0,O):(*生成從子程序返回操作指令*)test(fsys,[],8);&用fsys檢查當(dāng)前狀態(tài)是否合法,不合法那么拋8號錯水)procedureinterpret;p,b,t:integer;(*programbass:array[1..stacksize]ofinteger;(*datastore◆)while1>0d

溫馨提示

  • 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論