版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
PL/0編譯器源程序分析PL/0語言是Pascal語言的一個子集,我們這里分析的PL/0的編譯程序包括了對PL/0語言源程序進行分析處理、編譯生成類PCODE代碼,并在虛擬機上解釋運行生成的類PCODE代碼的功能。PL/0語言編譯程序采用以語法分析為核心、一遍掃描的編譯方法。詞法分析和代碼生成作為獨立的子程序供語法分析程序調(diào)用。語法分析的同時,提供了出錯報告和出錯恢復(fù)的功能。在源程序沒有錯誤編譯通過的情況下,調(diào)用類PCODE解釋程序解釋執(zhí)行生成的類PCODE代碼。詞法分析子程序分析:詞法分析子程序名為getsym,功能是從源程序中讀出一個單詞符號(token),把它的信息放入全局變量sym、id和num中,語法分析器需要單詞時,直接從這三個變量中獲得。(注意!語法分析器每次用完這三個變量的值就立即調(diào)用getsym子程序獲取新的單詞供下一次使用。而不是在需要新單詞時才調(diào)用getsym過程。)getsym過程通過反復(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)的代碼,并提供了出錯處理的機制。語法分析主要由分程序分析過程(block)、常量定義分析過程(constdeclaration)、變量定義分析過程(vardeclaration)、語句分析過程(statement)、表達式處理過程(expression)、項處理過程(term)、因子處理過程(factor)和條件處理過程(condition)構(gòu)成。這些過程在結(jié)構(gòu)上構(gòu)成一個嵌套的層次結(jié)構(gòu)。除此之外,還有出錯報告過程(error)、代碼生成過程(gen)、測試單詞合法性及出錯恢復(fù)過程(test)、登錄名字表過程(enter)、查詢名字表函數(shù)(position)以及列出類PCODE代碼過程(listcode)作過語法分析的輔助過程。由PL/0的語法圖可知:一個完整的PL/0程序是由分程序和句號構(gòu)成的。因此,本編譯程序在運行的時候,通過主程序中調(diào)用分程序處理過程block來分析分程序部分(分程序分析過程中還可能會遞歸調(diào)用block過程),然后,判斷最后讀入的符號是否為句號。如果是句號且分程序分析中未出錯,則是一個合法的PL/0程序,可以運行生成的代碼,否則就說明源PL/0程序是不合法的,輸出出錯提示即可。下面按各語法單元分析PL/0編譯程序的運行機制。分程序處理過程:語法分析開始后,首先調(diào)用分程序處理過程(block)處理分程序。過程入口參數(shù)置為:0層、符號表位置0、出錯恢復(fù)單詞集合為句號、聲明符或語句開始符。進入block過程后,首先把局部數(shù)據(jù)段分配指針設(shè)為3,準備分配3個單元供運行期存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。然后用tx0記錄下當(dāng)前符號表位置并產(chǎn)生一條jmp指令,準備跳轉(zhuǎn)到主程序的開始位置,由于當(dāng)前還沒有知到主程序究竟在何處開始,所以jmp的目標(biāo)暫時填為0,稍后再改。同時在符號表的當(dāng)前位置記錄下這個jmp指令在代碼段中的位置。在判斷了嵌套層數(shù)沒有超過規(guī)定的層數(shù)后,開始分析源程序。首先判斷是否遇到了常量聲明,如果遇到則開始常量定義,把常量存入符號表。接下去用同樣的方法分析變量聲明,變量定義過程中會用dx變量記錄下局部數(shù)據(jù)段分配的空間個數(shù)。然后如果遇到procedure保留字則進行過程聲明和定義,聲明的方法是把過程的名字和所在的層次記入符號表,過程定義的方法就是通過遞歸調(diào)用block過程,因為每個過程都是一個分程序。由于這是分程序中的分程序,因此調(diào)用block時需把當(dāng)前的層次號lev加一傳遞給block過程。分程序聲明部分完成后,即將進入語句的處理,這時的代碼分配指針cx的值正好指向語句的開始位置,這個位置正是前面的jmp指令需要跳轉(zhuǎn)到的位置。于是通過前面記錄下來的地址值,把這個jmp指令的跳轉(zhuǎn)位置改成當(dāng)前cx的位置。并在符號表中記錄下當(dāng)前的代碼段分配地址和局部數(shù)據(jù)段要分配的大?。╠x的值)。生成一條int指令,分配dx個空間,作為這個分程序段的第一條指令。下面就調(diào)用語句處理過程statement分析語句。分析完成后,生成操作數(shù)為0的opr指令,用于從分程序返回(對于0層的主程序來說,就是程序運行完成,退出)。常量定義過程:通過循環(huán),反復(fù)獲得標(biāo)識符和對應(yīng)的值,存入符號表。符號表中記錄下標(biāo)識符的名字和它對應(yīng)的值。變量定義過程:與常量定義類似,通過循環(huán),反復(fù)獲得標(biāo)識符,存入符號表。符號表中記錄下標(biāo)識符的名字、它所在的層及它在所在層中的偏移地址。語句處理過程:語句處理過程是一個嵌套子程序,通過調(diào)用表達式處理、項處理、因子處理等過程及遞歸調(diào)用自己來實現(xiàn)對語句的分析。語句處理過程可以識別的語句包括賦值語句、read語句、write語句、call語句、if語句、while語句。當(dāng)遇到begin/end語句時,就遞歸調(diào)用自己來分析。分析的同時生成相應(yīng)的類PCODE指令。賦值語句的處理:首先獲取賦值號左邊的標(biāo)識符,從符號表中找到它的信息,并確認這個標(biāo)識符確為變量名。然后通過調(diào)用表達式處理過程算得賦值號右部的表達式的值并生成相應(yīng)的指令保證這個值放在運行期的數(shù)據(jù)棧頂。最后通過前面查到的左部變量的位置信息,生成相應(yīng)的sto指令,把棧頂值存入指定的變量的空間,實現(xiàn)了賦值操作。read語句的處理:確定read語句語法合理的前提下(否則報錯),生成相應(yīng)的指令:第一條是16號操作的opr指令,實現(xiàn)從標(biāo)準輸入設(shè)備上讀一個整數(shù)值,放在數(shù)據(jù)棧頂。第二條是sto指令,把棧頂?shù)闹荡嫒雛ead語句括號中的變量所在的單元。write語句的處理:與read語句相似。在語法正確的前提下,生成指令:通過循環(huán)調(diào)用表達式處理過程分析write語句括號中的每一個表達式,生成相應(yīng)指令保證把表達式的值算出并放到數(shù)據(jù)棧頂并生成14號操彳^的opr指令,輸出表達式的值。最后生成15號操彳^的opr指令輸出一個換行。call語句的處理:從符號表中找到call語句右部的標(biāo)識符,獲得其所在層次和偏移地址。然后生成相應(yīng)的cal指令。至于調(diào)用子過程所需的保護現(xiàn)場等工作是由類PCODE解釋程序在解釋執(zhí)行cal指令時自動完成的。if語句的處理:按if語句的語法,首先調(diào)用邏輯表達式處理過程處理if語句的條件,把相應(yīng)的真假值放到數(shù)據(jù)棧頂。接下去記錄下代碼段分配位置(即下面生成的jpc指令的位置),然后生成條件轉(zhuǎn)移jpc指令(遇0或遇假轉(zhuǎn)移),轉(zhuǎn)移地址未知暫時填0。然后調(diào)用語句處理過程處理then語句后面的語句或語句塊。then后的語句處理完后,當(dāng)前代碼段分配指針的位置就應(yīng)該是上面的jpc指令的轉(zhuǎn)移位置。通過前面記錄下的jpc指令的位置,把它的跳轉(zhuǎn)位置改成當(dāng)前的代碼段指針位置。begin/end語句的處理:通過循環(huán)遍歷begin/end語句塊中的每一個語句,通過遞歸調(diào)用語句分析過程分析并生成相應(yīng)代碼。while語句的處理:首先用cx1變量記下當(dāng)前代碼段分配位置,作為循環(huán)的開始位置。然后處理while語句中的條件表達式生成相應(yīng)代碼把結(jié)果放在數(shù)據(jù)棧頂,再用cx2變量記下當(dāng)前位置,生成條件轉(zhuǎn)移指令,轉(zhuǎn)移位置未知,填0。通過遞歸調(diào)用語句分析過程分析do語句后的語句或語句塊并生成相應(yīng)代碼。最后生成一條無條件跳轉(zhuǎn)指令jmp,跳轉(zhuǎn)到cx1所指位置,并把cx2所指的條件跳轉(zhuǎn)指令的跳轉(zhuǎn)位置改成當(dāng)前代碼段分配位置。表達式、項、因子處理:根據(jù)PL/0語法可知,表達式應(yīng)該是由正負號或無符號開頭、由若干個項以加減號連接而成。而項是由若干個因子以乘除號連接而成,因子則可能是一個標(biāo)識符或一個數(shù)字,或是一個以括號括起來的子表達式。根據(jù)這樣的結(jié)構(gòu),構(gòu)造出相應(yīng)的過程,遞歸調(diào)用就完成了表達式的處理。把項和因子獨立開處理解決了加減號與乘除號的優(yōu)先級問題。在這幾個過程的反復(fù)調(diào)用中,始終傳遞fsys變量的值,保證可以在出錯的情況下跳過出錯的符號,使分析過程得以進行下去。邏輯表達式的處理:首先判斷是否為一元邏輯表達式:判奇偶。如果是,則通過調(diào)用表達式處理過程分析計算表達式的值,然后生成判奇指令。如果不是,則肯定是二元邏輯運算符,通過調(diào)用表達式處理過程依次分析運算符左右兩部分的值,放在棧頂?shù)膬蓚€空間中,然后依不同的邏輯運算符,生成相應(yīng)的邏輯判斷指令,放入代碼段。判斷單詞合法性與出錯恢復(fù)過程分析:本過程有三個參數(shù),si、s2為兩個符號集合,n為出錯代碼。本過程的功能是:測試當(dāng)前符號(即sym變量中的值)是否在si集合中,如果不在,就通過調(diào)用出錯報告過程輸出出錯代碼n,并放棄當(dāng)前符號,通過詞法分析過程獲取一下單詞,直到這個單詞出現(xiàn)在si或s2集合中為止。這個過程在實際使用中很靈活,主要有兩個用法:在進入某個語法單位時,調(diào)用本過程,檢查當(dāng)前符號是否屬于該語法單位的開始符號集合。若不屬于,則濾去開始符號和后繼符號集合外的所有符號。在語法單位分析結(jié)束時,調(diào)用本過程,檢查當(dāng)前符號是否屬于調(diào)用該語法單位時應(yīng)有的后繼符號集合。若不屬于,則濾去后繼符號和開始符號集合外的所有符號。通過這樣的機制,可以在源程序出現(xiàn)錯誤時,及時跳過出錯的部分,保證語法分析可以繼續(xù)下去。語法分析過程中調(diào)用的其它子過程相對比較簡單,請參考源程序的注釋。類PCODE代碼解釋執(zhí)行過程分析這個過程模擬了一臺可以運行類PCODE指令的棧式計算機。它擁有一個棧式數(shù)據(jù)段用于存放運行期數(shù)據(jù)、擁有一個代碼段用于存放類PCODE程序代碼。同時還擁用數(shù)據(jù)段分配指針、指令指針、指令寄存器、局部段基址指針等寄存器。解釋執(zhí)行類PCODE代碼時,數(shù)據(jù)段存儲分配方式如下:對于源程序的每一個過程(包括主程序),在被調(diào)用時,首先在數(shù)據(jù)段中開辟三個空間,存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。靜態(tài)鏈記錄了定義該過程的直接外過程(或主程序)運行時最新數(shù)據(jù)段的基地址。動態(tài)鏈記錄調(diào)用該過程前正在運行的過程的數(shù)據(jù)段基址。返回地址記錄了調(diào)用該過程時程序運行的斷點位置。對于主程序來說,SL、DL和RA的值均置為0。靜態(tài)鏈的功能是在一個子過程要引用它的直接或間接父過程(這里的父過程是按定義過程時的嵌套情況來定的,而不是按執(zhí)行時的調(diào)用順序定的)的變量時,可以通過靜態(tài)鏈,跳過個數(shù)為層差的數(shù)據(jù)段,找到包含要引用的變量所在的數(shù)據(jù)段基址,然后通過偏移地址訪問它。在過程返回時,解釋程序通過返回地址恢復(fù)指令指針的值到調(diào)用前的地址,通過當(dāng)前段基址恢復(fù)數(shù)據(jù)段分配指針,通過動態(tài)鏈恢復(fù)局部段基址指針。實現(xiàn)子過程的返回。對于主程序來說,解釋程序會遇到返回地址為0的情況,這時就認為程序運行結(jié)束。解釋程序過程中的base函數(shù)的功能,就是用于沿著靜態(tài)鏈,向前查找相差指定層數(shù)的局部數(shù)據(jù)段基址。這在使用sto、lod等訪問局部變量的指令中會經(jīng)常用到。類PCODE代碼解釋執(zhí)行的部分通過循環(huán)和簡單的case判斷不同的指令,做出相應(yīng)的動作。當(dāng)遇到主程序中的返回指令時,指令指針會指到0位置,把這樣一個條件作為終至循環(huán)的條件,保證程序運行可以正常的結(jié)束。以下源程序是以清華大學(xué)出版社《編譯原理》中的源代碼為基礎(chǔ)作了少量改動而成。程序在TurboPascal7.0上編譯運行通過。************************************************************************************TOC\o"1-5"\h\zprogrampl0(fa,fa1,fa2);(*PL/0編譯程序與代碼生成解釋運行程序*)(*PL/0compilerwithcodegeneration*)label99;(*聲明出錯跳轉(zhuǎn)標(biāo)記*)(*在TurboPascal7.0中已不允許跨過程的GOTO轉(zhuǎn)移,因此后面的GOTO語句均被我去除了,因此這里的label也沒有意義了*)const(*常量定義*)norw=13;(*ofreservedwords*)(*保留字的個數(shù)*)txmax=100;(*lengthofidentifiertable*)(*標(biāo)識符表的長度(容量)*)nmax=14;(*maxnumberofdigitsinnumbers*)(*數(shù)字允許的最長位數(shù)*)al=10;(*lengthofidentifiers*)(*標(biāo)識符最長長度*)amax=2047;(*maximumaddress*)(*尋址空間*)levmax=3;(*maxdepthofblocknesting*)(*最大允許的塊嵌套層數(shù)*)cxmax=200;(*sizeofcodearray*)(*類PCODE目標(biāo)代碼數(shù)組長度(可容納代碼行數(shù))*)type(*類型定義*)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,TOC\o"1-5"\h\zconstsym,varsym,procsym);(*symobl類型標(biāo)識了不同類型的詞匯*)alfa=packedarray[1..al]ofchar;(*alfa類型用于標(biāo)識符*)object1=(constant,variable,procedur);(*object1為三種標(biāo)識符的類型*)(*原程序在此使用object作為類型名稱,在支持面向?qū)ο蟮腡urboPascal7.0中編譯不能通過*)(*wirthusedtheword"procedure"there,whickwon'twork!*)(*上面一行是課本上的程序清單中的注釋,說本程序的原作者Wirth在這里用了procedure這個詞作為標(biāo)識符類型,是不可以的。事實上Wirth原本在這里用的詞是prozedure,是可以的。*)symset=setofsymbol;(*symset是symbol類型的一個集合類型,可用于存放一組symbol*)fct=(lit,opr,lod,sto,cal,int,jmp,jpc);(*fct類型分別標(biāo)識類PCODE的各條指令*)instruction=packedrecordf:fct;(*functioncode*)0..levmax;(*level*)a:0..amax;(*displacementaddr*)end;(*類PCODE指令類型,包含三個字段:指令f、層差l和另一個操作數(shù)a*)(*lit0,aloadconstantaopr0,aexecuteopralodl,aloadvariablel,astol,astorevariablel,acall,acallprocedureaatlevellint0,aincrementt-registerbyajmp0,ajumptoajpc0,ajumpconditionaltoa*)var(*全局變量定義*)fa:text;(*文本文件fa用于列出源程序*)fa1,fa2:text;(*文本文件fa1用于列出類PCODE代碼、fa2用于記錄解釋執(zhí)行類PCODE代碼的過程*)listswitch:boolean;(*truesetlistobjectcode*)(*如果本變量置true,程序編譯后將為列出類PCODE代碼,否則不列出類PCODE代碼*)ch:char;(*lastcharread*)(*主要用于詞法分析器,存放最近一次從文件中讀出的字符*)sym:symbol;(*lastsymbolread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識別出來的token的類型*)id:alfa;(*lastidentifierread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識別出來的標(biāo)識符的名字*)num:integer;(*lastnumberread*)(*詞法分析器輸出結(jié)果之用,存放最近一次識別出來的數(shù)字的值*)cc:integer;(*charactercount*)(*行緩沖區(qū)指針*)integer;(*linelength*)(*行緩沖區(qū)長度*)kk:integer;(*引入此變量是出于程序性能考慮,見getsym過程注釋*)cx:integer;(*codeallocationindex*)(*代碼分配指針,代碼生成模塊總在cx所指位置生成新的代碼*)line:array[1..81]ofchar;(*行緩沖區(qū),用于從文件讀出一行,供詞法分析獲取單詞時之用*)a:alfa;(*詞法分析器中用于臨時存放正在分析的詞*)code:array[0..cxmax]ofinstruction;(*生成的類PCODE代碼表,存放編譯得到的類PCODE代碼*)word:array[1..norw]ofalfa;(*保留字表*)wsym:array[1..norw]ofsymbol;(*保留字表中每一個保留字對應(yīng)的symbol類型*)ssym:array[''..'A']ofsymbol;(*一些符號對應(yīng)的symbol類型表*)(*wirthuses"array[char]"here*)mnemonic:array[fct]ofpackedarray[1..5]ofchar;(*類PCODE指令助記符表*)TOC\o"1-5"\h\zdeclbegsys,statbegsys,facbegsys:symset;(*聲明開始、表達式開始和項開始符號集合*)table:array[0..txmax]ofrecord(*符號表*)name:alfa;(*符號的名字*)casekind:object1of(*符號的類型*)constant:(*如果是常量名*)(val:integer);(*val中放常量的值*)variable,procedur:(*如果是變量名或過程名*)(level,adr,size:integer)(*存放層差、偏移地址和大小*)(*"size"lackinginorginal.Ithinkitbelonshere*)end;fin,fout:text;(*fin文本文件用于指向輸入的源程序文件,fout程序中沒有用到*)fname:string;(*存放PL/0源程序文件的文件名*)(*我修改的代碼:原程序在此處使用alfa類型,無法在TurboPascal7.0中通過,readln函數(shù)的參數(shù)不能為alfa型*)err:integer;(*出錯總次數(shù)*)(*出錯處理過程error*)(*參數(shù):n:出錯代碼*)procedureerror(n:integer);beginwriteln('****','':cc-1,'!',n:2);(*在屏幕cc-1位置顯示!與出錯代碼提示,由于ccTOC\o"1-5"\h\z是行緩沖區(qū)指針,所以!所指位置即為出錯位置*)writeln(fa1,'****','':cc-1,'!',n:2);(*在文件cc-1位置輸出!與出錯代碼提示*)err:=err+1(*出錯總次數(shù)加一*)end(*error*);(*詞法分析過程getsym*)proceduregetsym;vari,j,k:integer;(*讀取原程序中下一個字符過程getch*)proceduregetch;beginifcc=llthen(*如果行緩沖區(qū)指針指向行緩沖區(qū)最后一個字符就從文件讀一行到行緩沖區(qū)*)beginifeof(fin)then(*如果到達文件末尾*)beginwrite('Programincomplete');(*出錯,退出程序*)close(fa);close(fa1);close(fin);halt(0);{goto99}(*我修改的代碼,由于TurboPascal7.0中不允許跨過程的goto,就只能用上面的方法退出程序了。*)end;ll:=0;(*行緩沖區(qū)長度置0*)TOC\o"1-5"\h\zcc:=0;(*行緩沖區(qū)指針置行首*)write(cx:4,'');(*輸出cx值,寬度為4*)write(fa1,cx:4,'');(*輸出cx值,寬度為4到文件*)whilenoteoln(fin)do(*當(dāng)未到行末時*)beginll:=ll+1;(*行緩沖區(qū)長度加一*)read(fin,ch);(*從文件讀入一個字符到ch*)write(ch);(*在屏幕輸出ch*)write(fa1,ch);(*把ch輸出到文件*)line[ll]:=ch;(*把讀到的字符存入行緩沖區(qū)相應(yīng)的位置*)end;(*可見,PL/0源程序要求每行的長度都小于81個字符*)writeln;ll:=ll+1;(*行緩沖區(qū)長度加一,用于容納即將讀入的回車符CR*)read(fin,line[ll]);(*把#13(CR)讀入行緩沖區(qū)尾部*)read(fin,ch);(*我添加的代碼。由于PC上文本文件換行是以#13#10(CR+LF)表示的,所以要把多余的LF從文件讀出,這里放在ch變量中是由于ch變量的值在下面即將被改變,把這個多余值放在ch中沒有問題*)writeln(fa1);end;cc:=cc+1;(*行緩沖區(qū)指針加一,指向即將讀到的字符*)ch:=line[cc](*讀出字符,放入全局變量ch*)end(*getch*);begin(*getsym*)while(ch='')or(ch=#13)do(*我修改的代碼:這句原來是用于讀一個有效的字符(跳過讀出的字符中多余的空格),但實際上還要跳過多余的回車*)getch;TOC\o"1-5"\h\zifchin['a'..'z']then(*如果讀出的字符是一個字母,說明是保留字或標(biāo)識符*)begink:=0;(*標(biāo)識符緩沖區(qū)指針置0*)repeat(*這個循環(huán)用于依次讀出源文件中的字符構(gòu)成標(biāo)識符*)ifk<althen(*如果標(biāo)識符長度沒有超過最大標(biāo)識符長度(如果超過,就取前面一部分,把多余的拋棄)*)begink:=k+1;a[k]:=ch;end;getch(*讀下一個字符*)untilnot(chin['a'..'z','0'..’9']);(*直到讀出的不是字母或數(shù)字,由此可知PL/0的標(biāo)識符構(gòu)成規(guī)則是:TOC\o"1-5"\h\z以字母開頭,后面跟若干個字母或數(shù)字*)ifk>=kkthen(*如果當(dāng)前獲得的標(biāo)識符長度大于等于kk*)kk:=k(*令kk為當(dāng)前標(biāo)識符長度*)elserepeat(*這個循環(huán)用于把標(biāo)識符緩沖后部沒有填入相應(yīng)字母或空格的空間用空格補足*)a[kk]:='';kk:=kk-1untilkk=k;(*在第一次運行這個過程時,kk的值為al,即最大標(biāo)識符長度,如果讀到的標(biāo)識符長度小于kk,就把a數(shù)組的后部沒有字母的空間用空格補足。這時,kk的值就成為a數(shù)組前部非空格字符的個數(shù)。以后再運行g(shù)etsym時,如果讀到的標(biāo)識符長度大于等于kk,就把kk的值變成當(dāng)前標(biāo)識符的長度。這時就不必在后面填空格了,因為它的后面肯定全是空格。反之如果最近讀到的標(biāo)識符長度小于kk,那就需要從kk位置向前,把超過當(dāng)前標(biāo)識長度的空間填滿空格。以上的這樣一個邏輯,完全是出于程序性能的上考慮。其實完全可以簡單的把a數(shù)組中a[k]元素以后的空間不管三七二十一全填空格。*)TOC\o"1-5"\h\z(*下面開始二分法查找看讀出的標(biāo)識符是不是保留字之一*)id:=a;(*最后讀出標(biāo)識符等于a*)i:=1;(*i指向第一個保留字*)j:=norw;(*j指向最后一個保留字*)repeatk:=(i+j)div2;(*k指向中間一個保留字*)ifid<=word[k]then(*如果當(dāng)前的標(biāo)識符小于k所指的保留字*)j:=k-1;(*移動j指針*)ifid>=word[k]then(*如果當(dāng)前的標(biāo)識符大于k所指的保留字*)i:=k+1(*移動i指針*)untili>j;(*循環(huán)直到找完保留字表*)ifi-1>jthen(*如果i-1>j表明在保留字表中找到相應(yīng)的項,id中存的是保留字*)sym:=wsym[k](*找到保留字,把sym置為相應(yīng)的彳留字值*)elsesym:=ident(*未找到保留字,把sym置為ident類型,表示是標(biāo)識符*)end(*至此讀出字符為字母即對保留字或標(biāo)識符的處理結(jié)束*)else(*如果讀出字符不是字母*)ifchin['0'..'9']then(*如果讀出字符是數(shù)字*)begin(*number*)(*開始對數(shù)字進行處理*)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加上最近讀出的字符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號錯*)end(*至此對數(shù)字的識別處理結(jié)束*)elseifch=':'then(*如果讀出的不字母也不是數(shù)字而是冒號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到的是等號,正好可以與冒號構(gòu)成賦值號*)beginsym:=becomes;(*sym的類型設(shè)為賦值號becomes*)getch(*再讀出下一個字*)endelse*)TOC\o"1-5"\h\zsym:=nul;(*如果不是讀到等號,那單獨的一個冒號就什么也不是end(*以上完成對賦值號的處理*)*)else(*如果讀到不是字母也不是數(shù)字也不是冒號*)ifch='<'then(*如果讀到小于號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到等號*)beginTOC\o"1-5"\h\zsym:=leq;(*購成一個小于等于號*)getch(*讀一個字符*)endelse(*如果小于號后不是跟的等號*)sym:=lss(*那就是一個單獨的小于號*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號也不是小于號*)ifch='>'then(*如果讀到大于號,處理過程類似于處理小于號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到等號*)beginsym:=geq;(*購成一個大于等于號*)getch(*讀一個字符*)endelse(*如果大于號后不是跟的等號*)sym:=gtr(*那就是一個單獨的大于號*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號也不是小于號也不是大于號*)begin(*那就說明它不是標(biāo)識符/保留字,也不是復(fù)雜的雙字節(jié)操作符,應(yīng)該是一個普通的符號*)sym:=ssym[ch];(*直接成符號表中查到它的類型,賦給sym*)getch(*讀下一個字符*)end(*整個if語句判斷結(jié)束*)end(*getsym*);(*詞法分析過程getsym總結(jié):從源文件中讀出若干有效字符,組成一個token串,識別它的類型為保留字/標(biāo)識符/數(shù)字或是其它符號。如果是保留字,把sym置成相應(yīng)的保留字類型,如果是標(biāo)識符,把sym置成ident表示是標(biāo)識符,于此同時,id變量中存放的即為保留字字符串或標(biāo)識符名字。如果是數(shù)字,把sym置為number,同時num變量中存放該數(shù)字的值。如果是其它的操作符,則直接把sym置成相應(yīng)類型。經(jīng)過本過程后ch變量中存放的是下一個即將被識別的字符*)(*目標(biāo)代碼生成過程gen*)TOC\o"1-5"\h\z(*參數(shù):x:要生成的一行代碼的助記符*)(*y,z:代碼的兩個操作數(shù)*)(*本過程用于把生成的目標(biāo)代碼寫入目標(biāo)代碼數(shù)組,供后面的解釋器解釋執(zhí)行*)proceduregen(x:fct;y,z:integer);beginifcx>cxmaxthen(*如果cx>cxmax表示當(dāng)前生成的代碼行號大于允許的最大代碼行數(shù)*)beginwrite('programtoolong');(*輸出"程序太長",退出*)close(fa);close(fa1);close(fin);halt(0){goto99}(*我修改的代碼,由于TurboPascal7.0中不允許跨過程的goto,就只能用上面的方法退出程序了。*)end;withcode[cx]do(*把代碼寫入目標(biāo)代碼數(shù)組的當(dāng)前cx所指位置*)beginf:=x;l:=y;a:=z;end;TOC\o"1-5"\h\zcx:=cx+1(*移動cx指針指向下一個空位*)end(*gen*);(*測試當(dāng)前單詞是否合法過程test*)(*參數(shù):s1:當(dāng)語法分析進入或退出某一語法單元時當(dāng)前單詞符合應(yīng)屬于的集合*)(*s2:在某一出錯狀態(tài)下,可恢復(fù)語法分析正常工作的補充單詞集合*)(*n:出錯信息編號,當(dāng)當(dāng)前符號不屬于合法的s1集合時發(fā)出的出錯信息*)proceduretest(s1,s2:symset;n:integer);beginifnot(symins1)then(*如果當(dāng)前符號不在s1中*)beginerror(n);(*發(fā)出n號錯誤*)s1:=s1+s2;(*把s2集合補充進s1集合*)whilenot(symins1)do(*通過循環(huán)找到下一個合法的符號,以恢復(fù)語法分析工作*)getsymendend(*test*);(*語法分析過程block*)(*參數(shù):lev:這一次語法分析所在的層次*)(*tx:符號表指針*)(*fsys:用于出錯恢復(fù)的單詞集合*)procedureblock(lev,tx:integer;fsys:symset);vardx:integer;(*dataallocationindex*)(*數(shù)據(jù)段內(nèi)存分配指針,指向下一個被分配空間在數(shù)據(jù)TOC\o"1-5"\h\z段中的偏移位置*)tx0:integer;(*initialtableindex*)(*記錄本層開始時符號表位置*)cx0:integer;(*initialcodeindex*)(*記錄本層開始時代碼段分配位置*)(*登陸符號表過程enter*)(*參數(shù):k:欲登陸到符號表的符號類型*)procedureenter(k:objectl);begin(*enterobjectintotable*)tx:=tx+1;(*符號表指針指向一個新的空位*)withtable[tx]do(*開始登錄*)beginname:=id;(*name是符
溫馨提示
- 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五版環(huán)保節(jié)能技術(shù)轉(zhuǎn)化第三方履約擔(dān)保協(xié)議3篇
- 二零二五年飛機租賃與購買合同3篇
- 2024版系統(tǒng)集成項目外包合同3篇
- 二零二五年度鋼結(jié)構(gòu)廠房項目施工圖紙及技術(shù)交底合同3篇
- 二零二五年餐飲店員工派遣及服務(wù)質(zhì)量保證合同3篇
- 二零二五版半地下室租賃合同附帶租賃雙方責(zé)任界定3篇
- 烏蘭察布醫(yī)學(xué)高等專科學(xué)?!缎W(xué)英語教學(xué)研究》2023-2024學(xué)年第一學(xué)期期末試卷
- 二零二五版?zhèn)€人住房貸款利率調(diào)整協(xié)議2篇
- 二零二五版房地產(chǎn)抵押權(quán)抵押合同示范文本3篇
- 二零二五年餐飲廚房整體承包運營協(xié)議3篇
- 農(nóng)化分析土壤P分析
- GB/T 18476-2001流體輸送用聚烯烴管材耐裂紋擴展的測定切口管材裂紋慢速增長的試驗方法(切口試驗)
- GA 1551.5-2019石油石化系統(tǒng)治安反恐防范要求第5部分:運輸企業(yè)
- 拘留所教育課件02
- 沖壓生產(chǎn)的品質(zhì)保障
- 《腎臟的結(jié)構(gòu)和功能》課件
- 2023年湖南聯(lián)通校園招聘筆試題庫及答案解析
- 上海市徐匯區(qū)、金山區(qū)、松江區(qū)2023屆高一上數(shù)學(xué)期末統(tǒng)考試題含解析
- 護士事業(yè)單位工作人員年度考核登記表
- 產(chǎn)科操作技術(shù)規(guī)范范本
- 人教版八年級上冊地理全冊單元測試卷(含期中期末試卷及答案)
評論
0/150
提交評論