




版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
PL/0編譯器源程序分析PL/0語言是Pascal語言的一個子集,我們這里分析的PL/0的編譯程序包括了對PL/0語言源程序進行分析處理、編譯生成類PCODE代碼,并在虛擬機上解釋運行生成的類PCODE代碼的功能。PL/0語言編譯程序采用以語法分析為核心、一遍掃描的編譯方法。詞法分析和代碼生成作為獨立的子程序供語法分析程序調用。語法分析的同時,提供了出錯報告和出錯恢復的功能。在源程序沒有錯誤編譯通過的情況下,調用類PCODE解釋程序解釋執(zhí)行生成的類PCODE代碼。詞法分析子程序分析:詞法分析子程序名為getsym,功能是從源程序中讀出一個單詞符號(token),把它的信息放入全局變量sym、id和num中,語法分析器需要單詞時,直接從這三個變量中獲得。(注意!語法分析器每次用完這三個變量的值就立即調用getsym子程序獲取新的單詞供下一次使用。而不是在需要新單詞時才調用getsym過程。)getsym過程通過反復調用getch子過程從源程序過獲取字符,并把它們拼成單詞。getch過程中使用了行緩沖區(qū)技術以提高程序運行效率。詞法分析器的分析過程:調用getsym時,它通過getch過程從源程序中獲得一個字符。如果這個字符是字母,則繼續(xù)獲取字符或數(shù)字,最終可以拼成一個單詞,查保留字表,如果查到為保留字,則把sym變量賦成相應的保留字類型值;如果沒有查到,則這個單詞應是一個用戶自定義的標識符(可能是變量名、常量名或是過程的名字),把sym置為ident,把這個單詞存入id變量。查保留字表時使用了二分法查找以提高效率。如果getch獲得的字符是數(shù)字,則繼續(xù)用getch獲取數(shù)字,并把它們拼成一個整數(shù),然后把sym置為number,并把拼成的數(shù)值放入num變量。如果識別出其它合法的符號(比如:賦值號、大于號、小于等于號等),則把sym則成相應的類型。如果遇到不合法的字符,把sym置成nul。語法分析子程序分析:語法分析子程序采用了自頂向下的遞歸子程序法,語法分析同時也根據(jù)程序的語意生成相應的代碼,并提供了出錯處理的機制。語法分析主要由分程序分析過程(block)、常量定義分析過程(constdeclaration)、變量定義分析過程(vardeclaration)、語句分析過程(statement)、表達式處理過程(expression)、項處理過程(term)、因子處理過程(factor)和條件處理過程(condition)構成。這些過程在結構上構成一個嵌套的層次結構。除此之外,還有出錯報告過程(error)、代碼生成過程(gen)、測試單詞合法性及出錯恢復過程(test)、登錄名字表過程(enter)、查詢名字表函數(shù)(position)以及列出類PCODE代碼過程(listcode)作過語法分析的輔助過程。由PL/0的語法圖可知:一個完整的PL/0程序是由分程序和句號構成的。因此,本編譯程序在運行的時候,通過主程序中調用分程序處理過程block來分析分程序部分(分程序分析過程中還可能會遞歸調用block過程),然后,判斷最后讀入的符號是否為句號。如果是句號且分程序分析中未出錯,則是一個合法的PL/0程序,可以運行生成的代碼,否則就說明源PL/0程序是不合法的,輸出出錯提示即可。下面按各語法單元分析PL/0編譯程序的運行機制。分程序處理過程:語法分析開始后,首先調用分程序處理過程(block)處理分程序。過程入口參數(shù)置為:0層、符號表位置0、出錯恢復單詞集合為句號、聲明符或語句開始符。進入block過程后,首先把局部數(shù)據(jù)段分配指針設為3,準備分配3個單元供運行期存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。然后用tx0記錄下當前符號表位置并產(chǎn)生一條jmp指令,準備跳轉到主程序的開始位置,由于當前還沒有知到主程序究竟在何處開始,所以jmp的目標暫時填為0,稍后再改。同時在符號表的當前位置記錄下這個jmp指令在代碼段中的位置。在判斷了嵌套層數(shù)沒有超過規(guī)定的層數(shù)后,開始分析源程序。首先判斷是否遇到了常量聲明,如果遇到則開始常量定義,把常量存入符號表。接下去用同樣的方法分析變量聲明,變量定義過程中會用dx變量記錄下局部數(shù)據(jù)段分配的空間個數(shù)。然后如果遇到procedure保留字則進行過程聲明和定義,聲明的方法是把過程的名字和所在的層次記入符號表,過程定義的方法就是通過遞歸調用block過程,因為每個過程都是一個分程序。由于這是分程序中的分程序,因此調用block時需把當前的層次號lev加一傳遞給block過程。分程序聲明部分完成后,即將進入語句的處理,這時的代碼分配指針cx的值正好指向語句的開始位置,這個位置正是前面的jmp指令需要跳轉到的位置。于是通過前面記錄下來的地址值,把這個jmp指令的跳轉位置改成當前cx的位置。并在符號表中記錄下當前的代碼段分配地址和局部數(shù)據(jù)段要分配的大?。╠x的值)。生成一條int指令,分配dx個空間,作為這個分程序段的第一條指令。下面就調用語句處理過程statement分析語句。分析完成后,生成操作數(shù)為0的opr指令,用于從分程序返回(對于0層的主程序來說,就是程序運行完成,退出)。常量定義過程:通過循環(huán),反復獲得標識符和對應的值,存入符號表。符號表中記錄下標識符的名字和它對應的值。變量定義過程:與常量定義類似,通過循環(huán),反復獲得標識符,存入符號表。符號表中記錄下標識符的名字、它所在的層及它在所在層中的偏移地址。語句處理過程:語句處理過程是一個嵌套子程序,通過調用表達式處理、項處理、因子處理等過程及遞歸調用自己來實現(xiàn)對語句的分析。語句處理過程可以識別的語句包括賦值語句、read語句、write語句、call語句、if語句、while語句。當遇到begin/end語句時,就遞歸調用自己來分析。分析的同時生成相應的類PCODE指令。賦值語句的處理:首先獲取賦值號左邊的標識符,從符號表中找到它的信息,并確認這個標識符確為變量名。然后通過調用表達式處理過程算得賦值號右部的表達式的值并生成相應的指令保證這個值放在運行期的數(shù)據(jù)棧頂。最后通過前面查到的左部變量的位置信息,生成相應的sto指令,把棧頂值存入指定的變量的空間,實現(xiàn)了賦值操作。read語句的處理:確定read語句語法合理的前提下(否則報錯),生成相應的指令:第一條是16號操作的opr指令,實現(xiàn)從標準輸入設備上讀一個整數(shù)值,放在數(shù)據(jù)棧頂。第二條是sto指令,把棧頂?shù)闹荡嫒雛ead語句括號中的變量所在的單元。write語句的處理:與read語句相似。在語法正確的前提下,生成指令:通過循環(huán)調用表達式處理過程分析write語句括號中的每一個表達式,生成相應指令保證把表達式的值算出并放到數(shù)據(jù)棧頂并生成14號操作的opr指令,輸出表達式的值。最后生成15號操作的opr指令輸出一個換行。call語句的處理:從符號表中找到call語句右部的標識符,獲得其所在層次和偏移地址。然后生成相應的cal指令。至于調用子過程所需的保護現(xiàn)場等工作是由類PCODE解釋程序在解釋執(zhí)行cal指令時自動完成的。if語句的處理:按if語句的語法,首先調用邏輯表達式處理過程處理if語句的條件,把相應的真假值放到數(shù)據(jù)棧頂。接下去記錄下代碼段分配位置(即下面生成的jpc指令的位置),然后生成條件轉移jpc指令(遇0或遇假轉移),轉移地址未知暫時填0。然后調用語句處理過程處理then語句后面的語句或語句塊。then后的語句處理完后,當前代碼段分配指針的位置就應該是上面的jpc指令的轉移位置。通過前面記錄下的jpc指令的位置,把它的跳轉位置改成當前的代碼段指針位置。begin/end語句的處理:通過循環(huán)遍歷begin/end語句塊中的每一個語句,通過遞歸調用語句分析過程分析并生成相應代碼。while語句的處理:首先用cx1變量記下當前代碼段分配位置,作為循環(huán)的開始位置。然后處理while語句中的條件表達式生成相應代碼把結果放在數(shù)據(jù)棧頂,再用cx2變量記下當前位置,生成條件轉移指令,轉移位置未知,填0。通過遞歸調用語句分析過程分析do語句后的語句或語句塊并生成相應代碼。最后生成一條無條件跳轉指令jmp,跳轉到cx1所指位置,并把cx2所指的條件跳轉指令的跳轉位置改成當前代碼段分配位置。表達式、項、因子處理:根據(jù)PL/0語法可知,表達式應該是由正負號或無符號開頭、由若干個項以加減號連接而成。而項是由若干個因子以乘除號連接而成,因子則可能是一個標識符或一個數(shù)字,或是一個以括號括起來的子表達式。根據(jù)這樣的結構,構造出相應的過程,遞歸調用就完成了表達式的處理。把項和因子獨立開處理解決了加減號與乘除號的優(yōu)先級問題。在這幾個過程的反復調用中,始終傳遞fsys變量的值,保證可以在出錯的情況下跳過出錯的符號,使分析過程得以進行下去。邏輯表達式的處理:首先判斷是否為一元邏輯表達式:判奇偶。如果是,則通過調用表達式處理過程分析計算表達式的值,然后生成判奇指令。如果不是,則肯定是二元邏輯運算符,通過調用表達式處理過程依次分析運算符左右兩部分的值,放在棧頂?shù)膬蓚€空間中,然后依不同的邏輯運算符,生成相應的邏輯判斷指令,放入代碼段。判斷單詞合法性與出錯恢復過程分析:本過程有三個參數(shù),s1、s2為兩個符號集合,n為出錯代碼。本過程的功能是:測試當前符號(即sym變量中的值)是否在s1集合中,如果不在,就通過調用出錯報告過程輸出出錯代碼n,并放棄當前符號,通過詞法分析過程獲取一下單詞,直到這個單詞出現(xiàn)在s1或s2集合中為止。這個過程在實際使用中很靈活,主要有兩個用法:在進入某個語法單位時,調用本過程,檢查當前符號是否屬于該語法單位的開始符號集合。若不屬于,則濾去開始符號和后繼符號集合外的所有符號。在語法單位分析結束時,調用本過程,檢查當前符號是否屬于調用該語法單位時應有的后繼符號集合。若不屬于,則濾去后繼符號和開始符號集合外的所有符號。通過這樣的機制,可以在源程序出現(xiàn)錯誤時,及時跳過出錯的部分,保證語法分析可以繼續(xù)下去。語法分析過程中調用的其它子過程相對比較簡單,請參考源程序的注釋。類PCODE代碼解釋執(zhí)行過程分析這個過程模擬了一臺可以運行類PCODE指令的棧式計算機。它擁有一個棧式數(shù)據(jù)段用于存放運行期數(shù)據(jù)、擁有一個代碼段用于存放類PCODE程序代碼。同時還擁用數(shù)據(jù)段分配指針、指令指針、指令寄存器、局部段基址指針等寄存器。解釋執(zhí)行類PCODE代碼時,數(shù)據(jù)段存儲分配方式如下:對于源程序的每一個過程(包括主程序),在被調用時,首先在數(shù)據(jù)段中開辟三個空間,存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA。靜態(tài)鏈記錄了定義該過程的直接外過程(或主程序)運行時最新數(shù)據(jù)段的基地址。動態(tài)鏈記錄調用該過程前正在運行的過程的數(shù)據(jù)段基址。返回地址記錄了調用該過程時程序運行的斷點位置。對于主程序來說,SL、DL和RA的值均置為0。靜態(tài)鏈的功能是在一個子過程要引用它的直接或間接父過程(這里的父過程是按定義過程時的嵌套情況來定的,而不是按執(zhí)行時的調用順序定的)的變量時,可以通過靜態(tài)鏈,跳過個數(shù)為層差的數(shù)據(jù)段,找到包含要引用的變量所在的數(shù)據(jù)段基址,然后通過偏移地址訪問它。在過程返回時,解釋程序通過返回地址恢復指令指針的值到調用前的地址,通過當前段基址恢復數(shù)據(jù)段分配指針,通過動態(tài)鏈恢復局部段基址指針。實現(xiàn)子過程的返回。對于主程序來說,解釋程序會遇到返回地址為0的情況,這時就認為程序運行結束。解釋程序過程中的base函數(shù)的功能,就是用于沿著靜態(tài)鏈,向前查找相差指定層數(shù)的局部數(shù)據(jù)段基址。這在使用sto、lod等訪問局部變量的指令中會經(jīng)常用到。類PCODE代碼解釋執(zhí)行的部分通過循環(huán)和簡單的case判斷不同的指令,做出相應的動作。當遇到主程序中的返回指令時,指令指針會指到0位置,把這樣一個條件作為終至循環(huán)的條件,保證程序運行可以正常的結束。以下源程序是以清華大學出版社《編譯原理》中的源代碼為基礎作了少量改動而成。程序在TurboPascal7.0上編譯運行通過。************************************************************************************programpl0(fa,fa1,fa2);(*PL/0編譯程序與代碼生成解釋運行程序*)(*PL/0compilerwithcodegeneration*)label99;(*聲明出錯跳轉標記*)(*在TurboPascal7.0中已不允許跨過程的GOTO轉移,因此后面的GOTO語句均被我去除了,因此這里的label也沒有意義了*)const(*常量定義*)norw=13;(*ofreservedwords*)(*保留字的個數(shù)*)txmax=100;(*lengthofidentifiertable*)(*標識符表的長度(容量)*)nmax=14;(*maxnumberofdigitsinnumbers*)(*數(shù)字允許的最長位數(shù)*)al=10;(*lengthofidentifiers*)(*標識符最長長度*)amax=2047;(*maximumaddress*)(*尋址空間*)levmax=3;(*maxdepthofblocknesting*)(*最大允許的塊嵌套層數(shù)*)cxmax=200;(*sizeofcodearray*)(*類PCODE目標代碼數(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,constsym,varsym,procsym);(*symobl類型標識了不同類型的詞匯*)alfa=packedarray[1..al]ofchar;(*alfa類型用于標識符*)object1=(constant,variable,procedur);(*object1為三種標識符的類型*)(*原程序在此使用object作為類型名稱,在支持面向對象的TurboPascal7.0中編譯不能通過*)(*wirthusedtheword"procedure"there,whickwon'twork!*)(*上面一行是課本上的程序清單中的注釋,說本程序的原作者Wirth在這里用了procedure這個詞作為標識符類型,是不可以的。事實上Wirth原本在這里用的詞是prozedure,是可以的。*)symset=setofsymbol;(*symset是symbol類型的一個集合類型,可用于存放一組symbol*)fct=(lit,opr,lod,sto,cal,int,jmp,jpc);(*fct類型分別標識類PCODE的各條指令*)instruction=packedrecordf:fct;(*functioncode*)l: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*)(*詞法分析器輸出結果之用,存放最近一次識別出來的token的類型*)id:alfa;(*lastidentifierread*)(*詞法分析器輸出結果之用,存放最近一次識別出來的標識符的名字*)num:integer;(*lastnumberread*)(*詞法分析器輸出結果之用,存放最近一次識別出來的數(shù)字的值*)cc:integer;(*charactercount*)(*行緩沖區(qū)指針*)ll: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;(*保留字表中每一個保留字對應的symbol類型*)ssym:array[''..'^']ofsymbol;(*一些符號對應的symbol類型表*)(*wirthuses"array[char]"here*)mnemonic:array[fct]ofpackedarray[1..5]ofchar;(*類PCODE指令助記符表*)declbegsys,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位置顯示!與出錯代碼提示,由于cc是行緩沖區(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*)cc:=0;(*行緩沖區(qū)指針置行首*)write(cx:4,'');(*輸出cx值,寬度為4*)write(fa1,cx:4,'');(*輸出cx值,寬度為4到文件*)whilenoteoln(fin)do(*當未到行末時*)beginll:=ll+1;(*行緩沖區(qū)長度加一*)read(fin,ch);(*從文件讀入一個字符到ch*)write(ch);(*在屏幕輸出ch*)write(fa1,ch);(*把ch輸出到文件*)line[ll]:=ch;(*把讀到的字符存入行緩沖區(qū)相應的位置*)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;ifchin['a'..'z']then(*如果讀出的字符是一個字母,說明是保留字或標識符*)begink:=0;(*標識符緩沖區(qū)指針置0*)repeat(*這個循環(huán)用于依次讀出源文件中的字符構成標識符*)ifk<althen(*如果標識符長度沒有超過最大標識符長度(如果超過,就取前面一部分,把多余的拋棄)*)begink:=k+1;a[k]:=ch;end;getch(*讀下一個字符*)untilnot(chin['a'..'z','0'..'9']);(*直到讀出的不是字母或數(shù)字,由此可知PL/0的標識符構成規(guī)則是:以字母開頭,后面跟若干個字母或數(shù)字*)ifk>=kkthen(*如果當前獲得的標識符長度大于等于kk*)kk:=k(*令kk為當前標識符長度*)elserepeat(*這個循環(huán)用于把標識符緩沖后部沒有填入相應字母或空格的空間用空格補足*)a[kk]:='';kk:=kk-1untilkk=k;(*在第一次運行這個過程時,kk的值為al,即最大標識符長度,如果讀到的標識符長度小于kk,就把a數(shù)組的后部沒有字母的空間用空格補足。這時,kk的值就成為a數(shù)組前部非空格字符的個數(shù)。以后再運行getsym時,如果讀到的標識符長度大于等于kk,就把kk的值變成當前標識符的長度。這時就不必在后面填空格了,因為它的后面肯定全是空格。反之如果最近讀到的標識符長度小于kk,那就需要從kk位置向前,把超過當前標識長度的空間填滿空格。以上的這樣一個邏輯,完全是出于程序性能的上考慮。其實完全可以簡單的把a數(shù)組中a[k]元素以后的空間不管三七二十一全填空格。*)(*下面開始二分法查找看讀出的標識符是不是保留字之一*)id:=a;(*最后讀出標識符等于a*)i:=1;(*i指向第一個保留字*)j:=norw;(*j指向最后一個保留字*)repeatk:=(i+j)div2;(*k指向中間一個保留字*)ifid<=word[k]then(*如果當前的標識符小于k所指的保留字*)j:=k-1;(*移動j指針*)ifid>=word[k]then(*如果當前的標識符大于k所指的保留字*)i:=k+1(*移動i指針*)untili>j;(*循環(huán)直到找完保留字表*)ifi-1>jthen(*如果i-1>j表明在保留字表中找到相應的項,id中存的是保留字*)sym:=wsym[k](*找到保留字,把sym置為相應的保留字值*)elsesym:=ident(*未找到保留字,把sym置為ident類型,表示是標識符*)end(*至此讀出字符為字母即對保留字或標識符的處理結束*)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得到相應的數(shù)值*)k:=k+1;(*數(shù)字位數(shù)加一*)getchuntilnot(chin['0'..'9']);(*直到讀出的字符不是數(shù)字為止*)ifk>nmaxthen(*如果組成的數(shù)字位數(shù)大于最大允許的數(shù)字位數(shù)*)error(30)(*發(fā)出30號錯*)end(*至此對數(shù)字的識別處理結束*)elseifch=':'then(*如果讀出的不字母也不是數(shù)字而是冒號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到的是等號,正好可以與冒號構成賦值號*)beginsym:=becomes;(*sym的類型設為賦值號becomes*)getch(*再讀出下一個字*)endelsesym:=nul;(*如果不是讀到等號,那單獨的一個冒號就什么也不是*)end(*以上完成對賦值號的處理*)else(*如果讀到不是字母也不是數(shù)字也不是冒號*)ifch='<'then(*如果讀到小于號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到等號*)beginsym:=leq;(*購成一個小于等于號*)getch(*讀一個字符*)endelse(*如果小于號后不是跟的等號*)sym:=lss(*那就是一個單獨的小于號*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號也不是小于號*)ifch='>'then(*如果讀到大于號,處理過程類似于處理小于號*)begingetch;(*再讀一個字符*)ifch='='then(*如果讀到等號*)beginsym:=geq;(*購成一個大于等于號*)getch(*讀一個字符*)endelse(*如果大于號后不是跟的等號*)sym:=gtr(*那就是一個單獨的大于號*)endelse(*如果讀到不是字母也不是數(shù)字也不是冒號也不是小于號也不是大于號*)begin(*那就說明它不是標識符/保留字,也不是復雜的雙字節(jié)操作符,應該是一個普通的符號*)sym:=ssym[ch];(*直接成符號表中查到它的類型,賦給sym*)getch(*讀下一個字符*)end(*整個if語句判斷結束*)end(*getsym*);(*詞法分析過程getsym總結:從源文件中讀出若干有效字符,組成一個token串,識別它的類型為保留字/標識符/數(shù)字或是其它符號。如果是保留字,把sym置成相應的保留字類型,如果是標識符,把sym置成ident表示是標識符,于此同時,id變量中存放的即為保留字字符串或標識符名字。如果是數(shù)字,把sym置為number,同時num變量中存放該數(shù)字的值。如果是其它的操作符,則直接把sym置成相應類型。經(jīng)過本過程后ch變量中存放的是下一個即將被識別的字符*)(*目標代碼生成過程gen*)(*參數(shù):x:要生成的一行代碼的助記符*)(*y,z:代碼的兩個操作數(shù)*)(*本過程用于把生成的目標代碼寫入目標代碼數(shù)組,供后面的解釋器解釋執(zhí)行*)proceduregen(x:fct;y,z:integer);beginifcx>cxmaxthen(*如果cx>cxmax表示當前生成的代碼行號大于允許的最大代碼行數(shù)*)beginwrite('programtoolong');(*輸出"程序太長",退出*)close(fa);close(fa1);close(fin);halt(0){goto99}(*我修改的代碼,由于TurboPascal7.0中不允許跨過程的goto,就只能用上面的方法退出程序了。*)end;withcode[cx]do(*把代碼寫入目標代碼數(shù)組的當前cx所指位置*)beginf:=x;l:=y;a:=z;end;cx:=cx+1(*移動cx指針指向下一個空位*)end(*gen*);(*測試當前單詞是否合法過程test*)(*參數(shù):s1:當語法分析進入或退出某一語法單元時當前單詞符合應屬于的集合*)(*s2:在某一出錯狀態(tài)下,可恢復語法分析正常工作的補充單詞集合*)(*n:出錯信息編號,當當前符號不屬于合法的s1集合時發(fā)出的出錯信息*)proceduretest(s1,s2:symset;n:integer);beginifnot(symins1)then(*如果當前符號不在s1中*)beginerror(n);(*發(fā)出n號錯誤*)s1:=s1+s2;(*把s2集合補充進s1集合*)whilenot(symins1)do(*通過循環(huán)找到下一個合法的符號,以恢復語法分析工作*)getsymendend(*test*);(*語法分析過程block*)(*參數(shù):lev:這一次語法分析所在的層次*)(*tx:符號表指針*)(*fsys:用于出錯恢復的單詞集合*)procedureblock(lev,tx:integer;fsys:symset);vardx:integer;(*dataallocationindex*)(*數(shù)據(jù)段內(nèi)存分配指針,指向下一個被分配空間在數(shù)據(jù)段中的偏移位置*)tx0:integer;(*initialtableindex*)(*記錄本層開始時符號表位置*)cx0:integer;(*initialcodeindex*)(*記錄本層開始時代碼段分配位置*)(*登陸符號表過程enter*)(*參數(shù):k:欲登陸到符號表的符號類型*)procedureenter(k:object1);begin(*enterobjectintotable*)tx:=tx+1;(*符號表指針指向一個新的空位*)withtable[tx]do(*開始登錄*)beginname:=id;(*name是符號的名字,對于標識符,這里就是標識符的名字*)kind:=k;(*符號類型,可能是常量、變量或過程名*)casekof(*根據(jù)不同的類型進行不同的操作*)constant:(*如果是常量名*)beginifnum>amaxthen(*在常量的數(shù)值大于允許的最大值的情況下*)beginerror(31);(*拋出31號錯誤*)num:=0;(*實際登陸的數(shù)字以0代替*)end;val:=num(*如是合法的數(shù)值,就登陸到符號表*)end;variable:(*如果是變量名*)beginlevel:=lev;(*記下它所屬的層次號*)adr:=dx;(*記下它在當前層中的偏移量*)dx:=dx+1;(*偏移量自增一,為下一次做好準備*)end;procedur:(*如果要登陸的是過程名*)level:=lev(*記錄下這個過程所在層次*)endendend(*enter*);[樓主]|Posted:2006-03-2100:00angel級別:管理員精華:23發(fā)帖:489威望:534點金錢:5330RMB貢獻值:0點好評度:0點注冊時間:2006-02-26最后登錄:2006-08-24(*登錄符號過程沒有考慮到重復的定義的問題。如果出現(xiàn)重復定義,則以最后一次的定義為準。*)(*在符號表中查找指定符號所在位置的函數(shù)position*)(*參數(shù):id:要找的符號*)(*返回值:要找的符號在符號表中的位置,如果找不到就返回0*)functionposition(id:alfa):integer;vari:integer;begin(*findidentifierintable*)table[0].name:=id;(*先把id放入符號表0號位置*)i:=tx;(*從符號表中當前位置也即最后一個符號開始找*)<>iddo(*如果當前的符號與要找的不一致*)i:=i-1;(*找前面一個*)position:=i(*返回找到的位置號,如果沒找到則一定正好為0*)end(*position*);(*常量聲明處理過程constdeclaration*)procedureconstdeclaration;beginifsym=identthen(*常量聲明過程開始遇到的第一個符號必然應為標識符*)begingetsym;(*獲取下一個token*)ifsymin[eql,becomes]then(*如果是等號或賦值號*)beginifsym=becomesthen(*如果是賦值號(常量生明中應該是等號)*)error(1);(*拋出1號錯誤*)(*這里其實自動進行了錯誤糾正使編譯繼續(xù)進行,把賦值號當作等號處理*)getsym;(*獲取下一個token,等號或賦值號后應接上數(shù)字*)ifsym=numberthen(*如果的確是數(shù)字*)beginenter(constant);(*把這個常量登陸到符號表*)getsym(*獲取下一個token,為后面作準備*)endelseerror(2)(*如果等號后接的不是數(shù)字,拋出2號錯誤*)endelseerror(3)(*如果常量標識符后接的不是等號或賦值號,拋出3號錯誤*)endelseerror(4)(*如果常量聲明過程遇到的第一個符號不為標識符,拋出4號錯誤*)end(*constdeclaration*);(*變量聲明過程vardeclaration*)procedurevardeclaration;beginifsym=identthen(*變量聲明過程開始遇到的第一個符號必然應為標識符*)beginenter(variable);(*將標識符登陸到符號表中*)getsym(*獲取下一個token,為后面作準備*)endelseerror(4)(*如果變量聲明過程遇到的第一個符號不是標識符,拋出4號錯誤*)end(*vardeclaration*);(*列出當前一層類PCODE目標代碼過程listcode*)procedurelistcode;vari:integer;begin(*listcodegeneratedforthisblock*)iflistswitchthen(*如果用戶選擇是要列出代碼的情況下才列出代碼*)beginfori:=cx0tocx-1do(*從當前層代碼開始位置到當前代碼位置-1處,即為本分程序塊*)withcodedobeginwriteln(i:4,mnemonic[f]:5,l:3,a:5);(*顯示出第i行代碼的助記符和L與A操作數(shù)*)(*我修改的代碼:原程序此處在輸出i時,沒有指定占4個字符寬度,不美觀也與下面一句不配套。*)writeln(fa,i:4,mnemonic[f]:5,l:3,a:5)(*同時把屏顯打印到文件*)end;endend(*listcode*);(*語句處理過程statement*)(*參數(shù)說明:fsys:如果出錯可用來恢復語法分析的符號集合*)procedurestatement(fsys:symset);vari,cx1,cx2:integer;(*表達式處理過程expression*)(*參數(shù)說明:fsys:如果出錯可用來恢復語法分析的符號集合*)procedureexpression(fsys:symset);varaddop:symbol;(*項處理過程term*)(*參數(shù)說明:fsys:如果出錯可用來恢復語法分析的符號集合*)procedureterm(fsys:symset);varmulop:symbol;(*因子處理過程factor*)(*參數(shù)說明:fsys:如果出錯可用來恢復語法分析的符號集合*)procedurefactor(fsys:symset);vari:integer;begintest(facbegsys,fsys,24);(*開始因子處理前,先檢查當前token是否在facbegsys集合中。*)(*如果不是合法的token,拋24號錯誤,并通過fsys集恢復使語法處理可以繼續(xù)進行*)whilesyminfacbegsysdo(*循環(huán)處理因子*)beginifsym=identthen(*如果遇到的是標識符*)begini:=position(id);(*查符號表,找到當前標識符在符號表中的位置*)ifi=0then(*如果查符號表返回為0,表示沒有找到標識符*)error(11)(*拋出11號錯誤*)elsewithtabledo(*如果在符號表中找到了當前標識符的位置,開始生成相應代碼*)casekindofconstant:gen(lit,0,val);(*如果這個標識符對應的是常量,值為val,生成lit指令,把val放到棧頂*)variable:gen(lod,lev-level,adr);(*如果標識符是變量名,生成lod指令,*)(*把位于距離當前層level的層的偏移地址為adr的變量放到棧頂*)procedur:error(21)(*如果在因子處理中遇到的標識符是過程名,出錯了,拋21號錯*)end;getsym(*獲取下一token,繼續(xù)循環(huán)處理*)endelseifsym=numberthen(*如果因子處理時遇到數(shù)字*)beginifnum>amaxthen(*如果數(shù)字的大小超過允許最大值amax*)beginerror(31);(*拋出31號錯*)num:=0(*把數(shù)字按0值處理*)end;gen(lit,0,num);(*生成lit指令,把這個數(shù)值字面常量放到棧頂*)getsym(*獲取下一token*)endelseifsym=lparenthen(*如果遇到的是左括號*)begingetsym;(*獲取一個token*)expression([rparen]+fsys);(*遞歸調用expression子程序分析一個子表達式*)ifsym=rparenthen(*子表達式分析完后,應遇到右括號*)getsym(*如果的確遇到右括號,讀取下一個token*)elseerror(22)(*否則拋出22號錯誤*)end;test(fsys,facbegsys,23)(*一個因子處理完畢,遇到的token應在fsys集合中*)(*如果不是,拋23號錯,并找到下一個因子的開始,使語法分析可以繼續(xù)運行下去*)endend(*factor*);begin(*term*)factor([times,slash]+fsys);(*每一個項都應該由因子開始,因此調用factor子程序分析因子*)whilesymin[times,slash]do(*一個因子后應當遇到乘號或除號*)beginmulop:=sym;(*保存當前運算符*)getsym;(*獲取下一個token*)factor(fsys+[times,slash]);(*運算符后應是一個因子,故調factor子程序分析因子*)ifmulop=timesthen(*如果剛才遇到乘號*)gen(opr,0,4)(*生成乘法指令*)elsegen(opr,0,5)(*不是乘號一定是除號,生成除法指令*)endend(*term*);begin(*expression*)ifsymin[plus,minus]then(*一個表達式可能會由加號或減號開始,表示正負號*)beginaddop:=sym;(*把當前的正號或負號保存起來,以便下面生成相應代碼*)getsym;(*獲取一個token*)term(fsys+[plus,minus]);(*正負號后面應該是一個項,調term子程序分析*)ifaddop=minusthen(*如果保存下來的符號是負號*)gen(opr,0,1)(*生成一條1號操作指令:取反運算*)(*如果不是負號就是正號,不需生成相應的指令*)endelse(*如果不是由正負號開頭,就應是一個項開頭*)term(fsys+[plus,minus]);(*調用term子程序分析項*)whilesymin[plus,minus]do(*項后應是加運算或減運算*)beginaddop:=sym;(*把運算符保存下來*)getsym;(*獲取下一個token,加減運算符后應跟的是一個項*)term(fsys+[plus,minus]);(*調term子程序分析項*)ifaddop=plusthen(*如果項與項之間的運算符是加號*)gen(opr,0,2)(*生成2號操作指令:加法*)else(*否則是減法*)gen(opr,0,3)(*生成3號操作指令:減法*)endend(*expression*);(*條件處理過程condition*)(*參數(shù)說明:fsys:如果出錯可用來恢復語法分析的符號集合*)procedurecondition(fsys:symset);varrelop:symbol;(*用于臨時記錄token(這里一定是一個二元邏輯運算符)的內(nèi)容*)beginifsym=oddsymthen(*如果是odd運算符(一元)*)begingetsym;(*獲取下一個token*)expression(fsys);(*對odd的表達式進行處理計算*)gen(opr,0,6);(*生成6號操作指令:奇偶判斷運算*)endelse(*如果不是odd運算符(那就一定是二元邏輯運算符)*)beginexpression([eql,neq,lss,leq,gtr,geq]+fsys);(*對表達式左部進行處理計算*)ifnot(symin[eql,neq,lss,leq,gtr,geq])then(*如果token不是邏輯運算符中的一個*)error(20)(*拋出20號錯誤*)elsebeginrelop:=sym;(*記錄下當前的邏輯運算符*)getsym;(*獲取下一個token*)expression(fsys);(*對表達式右部進行處理計算*)caserelopof(*如果剛才的運算符是下面的一種*)eql:gen(opr,0,8);(*等號:產(chǎn)生8號判等指令*)neq:gen(opr,0,9);(*不等號:產(chǎn)生9號判不等指令*)lss:gen(opr,0,10);(*小于號:產(chǎn)生10號判小指令*)geq:gen(opr,0,11);(*大于等號號:產(chǎn)生11號判不小于指令*)gtr:gen(opr,0,12);(*大于號:產(chǎn)生12號判大于指令*)leq:gen(opr,0,13);(*小于等于號:產(chǎn)生13號判不大于指令*)endendendend(*condition*);begin(*statement*)ifsym=identthen(*所謂"語句"可能是賦值語句,以標識符開頭*)begini:=position(id);(*在符號表中查到該標識符所在位置*)ifi=0then(*如果沒找到*)error(11)(*拋出11號錯誤*)elseiftable.kind<>variablethen(*如果在符號表中找到該標識符,但該標識符不是變量名*)beginerror(12);(*拋出12號錯誤*)i:=0(*i置0作為錯誤標志*)end;getsym;(*獲得下一個token,正常應為賦值號*)ifsym=becomesthen(*如果的確為賦值號*)getsym(*獲取下一個token,正常應為一個表達式*)elseerror(13);(*如果賦值語句的左部標識符號后所接不是賦值號,拋出13號錯誤*)expression(fsys);(*處理表達式*)ifi<>0then(*如果不曾出錯,i將不為0,i所指為當前語名左部標識符在符號表中的位置*)withtabledogen(sto,lev-level,adr)(*產(chǎn)生一行把表達式值寫往指定內(nèi)存的sto目標代碼*)endelseifsym=readsymthen(*如果不是賦值語句,而是遇到了read語句*)begingetsym;(*獲得下一token,正常情況下應為左括號*)ifsym<>lparenthen(*如果read語句后跟的不是左括號*)error(34)(*拋出34號錯誤*)elserepeat(*循環(huán)得到read語句括號中的參數(shù)表,依次產(chǎn)生相應的“從鍵盤讀入”目標代碼*)getsym;(*獲得一個token,正常應是一個變量名*)ifsym=identthen(*如果確為一個標識符*)(*這里略有問題,還應判斷一下這個標識符是不是變量名,如果是常量名或過程名應出錯*)i:=position(id)(*查符號表,找到它所在位置給i,找不到時i會為0*)elsei:=0;(*不是標識符則有問題,i置0作為出錯標志*)ifi=0then(*如果有錯誤*)error(35)(*拋出35號錯誤*)else(*否則生成相應的目標代碼*)withtabledobegingen(opr,0,16);(*生成16號操作指令:從鍵盤讀入數(shù)字*)gen(sto,lev-level,adr)(*生成sto指令,把讀入的值存入指定變量所在的空間*)end;getsym(*獲取下一個token,如果是逗號,則read語還沒完,否則應當是右括號*)untilsym<>comma;(*不斷生成代碼直到read語句的參數(shù)表中的變量遍歷完為止,這里遇到不是逗號,應為右括號*)ifsym<>rparenthen(*如果不是我們預想中的右括號*)beginerror(33);(*拋出33號錯誤*)whilenot(syminfsys)do(*依靠fsys集,找到下一個合法的token,恢復語法分析*)getsymendelsegetsym(*如果read語句正常結束,得到下一個token,一般為分號或end*)endelseifsym=writesymthen(*如果遇到了write語句*)begingetsym;(*獲取下一token,應為左括號*)ifsym=lparenthen(*如確為左括號*)beginrepeat(*依次獲取括號中的每一個值,進行輸出*)getsym;(*獲得一個token,這里應是一個標識符*)expression([rparen,comma]+fsys);(*調用expression過程分析表達式,用于出錯恢復的集合中加上右括號和逗號*)gen(opr,0,14)(*生成14號指令:向屏幕輸出*)untilsym<>comma;(*循環(huán)直到遇到的不再是逗號,這時應是右括號*)ifsym<>rparenthen(*如果不是右括號*)error(33)(*拋出33號錯誤*)elsegetsym(*正常情況下要獲取下一個token,為后面準備好*)end;gen(opr,0,15)(*生成一個15號操作的目標代碼,功能是輸出一個換行*)(*由此可知PL/0中的write語句與Pascal中的writeln語句類似,是帶有輸出換行的*)endelseifsym=callsymthen(*如果是call語句*)begingetsym;(*獲取token,應是過程名型標識符*)ifsym<>identthen(*如果call后跟的不是標識符*)error(14)(*拋出14號錯誤*)elsebegini:=position(id);(*從符號表中找出相應的標識符*)ifi=0then(*如果沒找到*)error(11)(*拋出11號錯誤*)elsewithtabledo(*如果找到標識符位于符號表第i位置*)ifkind=procedurthen(*如果這個標識符為一個過程名*)gen(cal,lev-level,adr)(*生成cal目標代碼,呼叫這個過程*)elseerror(15);(*如果call后跟的不是過程名,拋出15號錯誤*)getsym(*獲取下一token,為后面作準備*)endendelseifsym=ifsymthen(*如果是if語句*)begingetsym;(*獲取一token應是一個邏輯表達式*)condition([thensym,dosym]+fsys);(*對邏輯表達式進行分析計算,出錯恢復集中加入then和do語句*)ifsym=thensymthen(*表達式后應遇到then語句*)getsym(*獲取then后的token,應是一語句*)elseerror(16);(*如果if后沒有then,拋出16號錯誤*)cx1:=cx;(*記下當前代碼分配指針位置*)gen(jpc,0,0);(*生成條件跳轉指令,跳轉位置暫時填0,分析完語句后再填寫*)statement(fsys);(*分析then后的語句*)code[cx1].a:=cx(*上一行指令(cx1所指的)的跳轉位置應為當前cx所指位置*)endelseifsym=beginsymthen(*如果遇到begin*)begingetsym;(*獲取下一個token*)statement([semicolon,endsym]+fsys);(*對begin與end之間的語句進行分析處理*)whilesymin[semicolon]+statbegsysdo(*如果分析完一句后遇到分號或語句開始符循環(huán)分析下一句語句*)beginifsym=semicolonthen(*如果語句是分號(可能是空語句)*)getsym(*獲取下一token繼續(xù)分析*)elseerror(10);(*如果語句與語句間沒有分號,出10號錯*)statement([semicolon,endsym]+fsys)(*分析一個語句*)end;ifsym=endsymthen(*如果語句全分析完了,應該遇到end*)getsym(*的確是end,讀下一token*)elseerror(17)(*如果不是end,拋出17號錯*)endelseifsym=whilesymthen(*如果遇到while語句*)begincx1:=cx;(*記下當前代碼分配位置,這是while循環(huán)的開始位置*)getsym;(*獲取下一token,應為一邏輯表達式*)condition([dosym]+fsys);(*對這個邏輯表達式進行分析計算*)cx2:=cx;(*記下當前代碼分配位置,這是while的do中的語句的開始位置*)gen(jpc,0,0);(*生成條件跳轉指令,跳轉位置暫時填0*)ifsym=dosymthen(*邏輯表達式后應為do語句*)getsym(*獲取下一token*)elseerror(18);(*if后缺少then,拋出18號錯誤*)statement(fsys);(*分析do后的語句塊*)gen(jmp,0,cx1);(*循環(huán)跳轉到cx1位置,即再次進行邏輯判斷*)code[cx2].a:=cx(*把剛才填0的跳轉位置改成當前位置,完成while語句的處理*)end;test(fsys,[],19)(*至此一個語句處理完成,一定會遇到fsys集中的符號,如果沒有遇到,就拋19號錯*)end(*statement*);begin(*block*)dx:=3;(*地址指示器給出每層局部量當前已分配到的相對位置。置初始值為3的原因是:每一層最開始的位置有三個空間用于存放靜態(tài)鏈SL、動態(tài)鏈DL和返回地址RA*)tx0:=tx;(*初始符號表指針指向當前層的符號在符號表中的開始位置*)table[tx].adr:=cx;(*符號表當前位置記下當前層代碼的開始位置*)gen(jmp,0,0);(*產(chǎn)生一行跳轉指令,跳轉位置暫時未知填0*)iflev>levmaxthen(*如果當前過程嵌套層數(shù)大于最大允許的套層數(shù)*)error(32);(*發(fā)出32號錯誤*)repeat(*開始循環(huán)處理源程序中所有的聲明部分*)ifsym=constsymthen(*如果當前token是const保留字,開始進行常量聲明*)begingetsym;(*獲取下一個token,正常應為用作常量名的標識符*)repeat(*反復進行常量聲明*)constdeclaration;(*聲明以當前token為標識符的常量*)whilesym=commado(*如果遇到了逗號則反復聲明下一個常量*)begingetsym;(*獲取下一個token,這里正好應該是標識符*)constdeclaration(*聲明以當前token為標識符的常量*)end;ifsym=semicolonthen(*如果常量聲明結束,應遇到分號*)getsym(*獲取下一個token,為下一輪循環(huán)做好準備*)elseerror(5)(*如果常量聲明語句結束后沒有遇到分號則發(fā)出5號錯誤*)untilsym<>ident(*如果遇到非標識符,則常量聲明結束*)end;(*此處的常量聲明的語法與課本上的EBNF范式有不同之處:它可以接受像下面的聲明方法,而根據(jù)課本上的EBNF范式不可得出下面的語法:consta=3,b=3;c=6;d=7,e=8;即它可以接受分號或逗號隔開的常量聲明,而根據(jù)EBNF范式只可接受用逗號隔開的聲明*)ifsym=varsymthen(*如果當前token是var保留字,開始進行變量聲明,與常量聲明類似*)begingetsym;(*獲取下一個token,此處正常應為用作變量名的一個標識符*)repeat(*反復進行變量聲明*)vardeclaration;(*以當前token為標識符聲明一個變量*)whilesym=commado(*如果遇到了逗號則反復聲明下一個變量*)begingetsym;(*獲取下一個token,這里正好應該是標識符*)vardeclaration;(*聲明以當前token為標識符的變量*)end;ifsym=semicolonthen(*如果變量聲明結束,應遇到分號*)getsym(*獲取下一個token,為下一輪循環(huán)做好準備*)elseerror(5)(*如果變量聲明語句結束后沒有遇到分號則發(fā)出5號錯誤*)untilsym<>ident;(*如果遇到非標識符,則變量聲明結束*)(*這里也存在與上面的常量聲明一樣的毛?。号cPL/0的語法規(guī)范有沖突。*)end;whilesym=procsymdo(*循環(huán)聲明各子過
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 民企招聘活動方案
- 畢業(yè)整容活動策劃方案
- 武漢足球接龍活動方案
- 正家風傳家訓活動方案
- 植樹節(jié)小區(qū)游園活動方案
- 植樹節(jié)公司活動方案
- 水帶打靶活動方案
- 永濟燒烤活動方案
- 河北省各地紀念活動方案
- 汽車贈品活動方案
- 2025年第二屆全國安康杯安全生產(chǎn)知識競賽題庫及答案(共190題)
- 護士法律法規(guī)知識培訓課件
- DB11-T 2398-2025 水利工程巡視檢查作業(yè)規(guī)范
- 2025年光伏行業(yè)上半年發(fā)展回顧與下半年形勢展望
- 輸血管理相關制度
- 2025至2031年中國紙巾用香精行業(yè)投資前景及策略咨詢研究報告
- 老年性癡呆病人的護理與管理
- 無固定期限勞工合同通知書
- GB/T 45161-2024液氫容器用安全閥技術規(guī)范
- 《中醫(yī)推拿按摩教程》課件
- 煤炭采購及運輸?shù)暮弦?guī)性流程
評論
0/150
提交評論