狠狠干影院/欧美午夜电影在线观看/高黄文/国产精品一区二区在线观看完整版

編譯原理實(shí)驗(yàn)報(bào)告一

| 瀏覽次數(shù):

 實(shí)驗(yàn)一

 詞法分析程序?qū)崿F(xiàn) 一、實(shí)驗(yàn)?zāi)康呐c要求

 通過(guò)編寫和調(diào)試一個(gè)詞法分析程序,掌握在對(duì)程序設(shè)計(jì)語(yǔ)言的源程序進(jìn)行掃描的過(guò)程中,將字符流形式的源程序轉(zhuǎn)化為一個(gè)由各類單詞符號(hào)組成的流的詞法分析方法

 二、實(shí)驗(yàn)內(nèi)容

 基本實(shí)驗(yàn):

 題目:若某一程序設(shè)計(jì)語(yǔ)言中的單詞包括五個(gè)關(guān)鍵字begin、end、if、then、else;標(biāo)識(shí)符;無(wú)符號(hào)常數(shù);六種關(guān)系運(yùn)算符;一個(gè)賦值符和四個(gè)算術(shù)運(yùn)算符,試構(gòu)造能識(shí)別這些單詞的詞法分析程序(各類單詞的分類碼參見(jiàn)表 I)。

 表 表 I

  語(yǔ)言中的各類單詞符號(hào)及其分類碼表

 單詞符號(hào)

 類別編碼

 類別碼的助記符

 單詞值

 begin

 1

 BEGIN

  end

 2

 END

  if

 3

 IF

  then

 4

 THEN

  else

 5

 ELSE

  標(biāo)識(shí)符

 6

 ID

 字母打頭的字母數(shù)字串

 無(wú)符號(hào)常數(shù)

 7

 UCON

 機(jī)內(nèi)二進(jìn)制表示

 <

 8

 LT

 <=

 9

 LE

  =

 10

 EQ

  <>

 11

 NE

  >

 12

 GT

  >=

 13

 GE

  :=

 14

 IS

  +

 15

 PL

  -

 16

 MI

  *

 17

 MU

  /

 18

 DI

  :

 輸入:由符合和不符合所規(guī)定的單詞類別結(jié)構(gòu)的各類單詞組成的源程序文件。

 輸出:把所識(shí)別出的每一單詞均按形如(CLASS,VALUE)的二元式形式輸出,并將結(jié)果放到某個(gè)文件中。對(duì)于標(biāo)識(shí)符和無(wú)符號(hào)常數(shù),CLASS字段為相應(yīng)的類別碼的助記符;VALUE 字段則是該標(biāo)識(shí)符、常數(shù)的具體值;對(duì)于關(guān)鍵字和運(yùn)算符,采用一詞一類的編碼形式,僅需在二元式的 CLASS 字段上放置相應(yīng)單詞的類別碼的助記符,VALUE 字段則為“空”。

 三、實(shí)現(xiàn)方法與環(huán)境

 詞法分析是編譯程序的第一個(gè)處理階段,可以通過(guò)兩種途徑來(lái)構(gòu)造詞

 法分析程序。其一是根據(jù)對(duì)語(yǔ)言中各類單詞的某種描述或定義(如BNF),用手工的方式(例如可用 C 語(yǔ)言)構(gòu)造詞法分析程序。一般地,可以根據(jù)文法或狀態(tài)轉(zhuǎn)換圖構(gòu)造相應(yīng)的狀態(tài)矩陣,該狀態(tài)矩陣連同控制程序一起便組成了編譯器的詞法分析程序;也可以根據(jù)文法或狀態(tài)轉(zhuǎn)換圖直接編寫詞法分析程序。構(gòu)造詞法分析程序的另外一種途徑是所謂的詞法分析程序的自動(dòng)生成,即首先用正規(guī)式對(duì)語(yǔ)言中的各類單詞符號(hào)進(jìn)行詞型描述,并分別指出在識(shí)別單詞時(shí),詞法分析程序所應(yīng)進(jìn)行的語(yǔ)義處理工作,然后由一個(gè)所謂詞法分析程序的構(gòu)造程序?qū)ι鲜鲂畔⑦M(jìn)行加工。如美國(guó) BELL 實(shí)驗(yàn)室研制的 LEX 就是一個(gè)被廣泛使用的詞法分析程序的自動(dòng)生成工具。

?。?/p>

 處理過(guò)程簡(jiǎn)述:在一個(gè)程序設(shè)計(jì)語(yǔ)言中,一般都含有若干類單詞符號(hào),為此可首先為每類單詞建立一張狀態(tài)轉(zhuǎn)換圖,然后將這些狀態(tài)轉(zhuǎn)換圖合并成一張統(tǒng)一的狀態(tài)圖,即得到了一個(gè)有限自動(dòng)機(jī),再進(jìn)行必要的確定化和狀態(tài)數(shù)最小化處理,最后添加當(dāng)進(jìn)行狀態(tài)轉(zhuǎn)移時(shí)所需執(zhí)行的語(yǔ)義動(dòng)作,就可以據(jù)此構(gòu)造詞法分析程序了。

 為了使詞法分析程序結(jié)構(gòu)比較清晰,且盡量避免某些枝節(jié)問(wèn)題的糾纏,我們假定要編譯的語(yǔ)言中,全部關(guān)鍵字都是保留字,程序員不得將它們作為源程序中的標(biāo)識(shí)符;在源程序的輸入文本中,關(guān)鍵字、標(biāo)識(shí)符、無(wú)符號(hào)常數(shù)之間,若未出現(xiàn)關(guān)系和算術(shù)運(yùn)算符以及賦值符,則至少須用一個(gè)空白字符加以分隔。作了這些限制以后,就可以把關(guān)鍵字和標(biāo)

 識(shí)符的識(shí)別統(tǒng)一進(jìn)行處理。即每當(dāng)開(kāi)始識(shí)別一個(gè)單詞時(shí),若掃視到的第一個(gè)字符為字母,則把后續(xù)輸入的字母或數(shù)字字符依次進(jìn)行拼接,直至掃視到非字母、數(shù)字字符為止,以期獲得一個(gè)盡可能長(zhǎng)的字母數(shù)字字符串,然后以此字符串查所謂保留字表(此保留字表要事先造好),若查到此字符串,則取出相應(yīng)的類別碼;反之,則表明該字符串應(yīng)為一標(biāo)識(shí)符。

 采用上述策略后,針對(duì)表 I 中的部分單詞可以參考教材 P80 的圖 3-22(見(jiàn)圖 1)

  圖 圖 1 1

 識(shí)別表 I I 所列語(yǔ)言中的部分單詞的 A DFA 及相關(guān)的語(yǔ)義過(guò)程

  圖 圖 1 1 中所出現(xiàn)的語(yǔ)義變量及語(yǔ)義函數(shù)的含義和功能說(shuō)明如下:

 函數(shù) GETCHAR:每調(diào)用一次,就把掃描指示器當(dāng)前所指示的源程序字符送入字符變量 ch,然后把掃描指示器前推一個(gè)字符位置。

 字符數(shù)組 TOKEN:用來(lái)依次存放一個(gè)單詞詞文中的各個(gè)字符。

 函數(shù) CAT:每調(diào)用一次,就把當(dāng)前 ch 中的字符拼接于 TOKEN 中所存字符串的右邊。

 函數(shù) LOOKUP:每調(diào)用一次,就以 TOKEN 中的字符串查保留字表,若

 查到,就將相應(yīng)關(guān)鍵字的類別碼賦給整型變量 c;否則將 c 置為零。

 函數(shù) RETRACT:每調(diào)用一次,就把掃描指示器回退一個(gè)字符位置(即退回多讀的那個(gè)字符)。

 函數(shù) OUT:一般僅在進(jìn)入終態(tài)時(shí)調(diào)用此函數(shù),調(diào)用的形式為 OUT(c,VAL)。其中,實(shí)參 c 為相應(yīng)單詞的類別碼助記符;實(shí)參 VAL 為 TOKEN(即詞文)或?yàn)榭沾?。函?shù) OUT 的功能是,在送出一個(gè)單詞的內(nèi)部表示之后,返回到調(diào)用該詞法分析程序的那個(gè)程序。

  總的來(lái)說(shuō),開(kāi)發(fā)一種新語(yǔ)言時(shí),由于它的單詞符號(hào)在不停地修改,采用 LEX 等工具生成的詞法分析程序比較易于修改和維護(hù)。一旦一種語(yǔ)言確定了,則采用手工編寫詞法分析程序效率更高。

 四.源程序

 #include <stdio.h>

 #include <ctype.h>

 #include <string.h>

 #include <math.h>

  #define ID 6

 #define INT 7

 #define LT 8

 #define LE 9

 #define EQ 10

 #define NE 11

 #define GT 12

 #define GE 13

 #define IS 14

 #define PL 15

 #define MI 16

 #define MU 17

 #define DI 18

 #define MAX_KEY_NUMBER 20//關(guān)鍵字的數(shù)量

 #define KEY_WORD_END "waiting for your expanding"

 //關(guān)鍵字結(jié)束標(biāo)記

 char *KeyWordTable[MAX_KEY_NUMBER]={"begin","end", "if", "then", "else", KEY_WORD_END};

 char TOKEN[20]="";

 char ch=" ";//用于存儲(chǔ)帶判斷的字符

 int row=1;//row 標(biāo)識(shí)錯(cuò)誤在第幾行

  #define DIGIT 1

 #define POINT 2

 #define OTHER 3

 #define POWER 4

 #define PLUS 5

 #define MINUS 6

 #define UCON 7

 //假設(shè)無(wú)符號(hào)常量的類數(shù)是 7

 #define ClassOther 200

 #define EndState -1

 int index=0;//保存已讀的字符串的索引

 int w,n,p,e,d;

 int Class;

 //用于表示類的詞

 int ICON;

 float FCON;

 static int CurrentState;

 //用于目前的當(dāng)前狀態(tài),初始值:0

 int EXCUTE (int state, int symbol,FILE *fp,char JudgeStr[],int row,int index);

 int GetChar (char ch);

 int HandleError (char StrJudge[],int row);

  ///////////////////查保留字表,判斷是否為關(guān)鍵字

 int lookup (char *token)

 {

  int n=0;

  while (strcmp(KeyWordTable[n], KEY_WORD_END)) //strcmp 比

 較兩串是否相同,若相同返回 0

  {

 if (!strcmp(KeyWordTable[n], token)) //比較 token 所指向的關(guān)鍵字和保留字表中哪個(gè)關(guān)鍵字相符

 {

  return n+1; //根據(jù)單詞分類碼表 I,設(shè)置正確的關(guān)鍵字類別碼,并返回此類別碼的值

  break;

 }

 n++;

  }

  return 6; //單詞不是關(guān)鍵字,而是標(biāo)識(shí)符

 }

 ///////////////////輸出分析結(jié)果

 void out (int i, char* pStr)

 {

  char Mnemonic[5];

  if(1==i)

  {

 strcpy(Mnemonic,"BEGIN");

  }

  else if(2==i)

  {

 strcpy(Mnemonic,"END");

  }

  else if(3==i)

  {

 strcpy(Mnemonic,"IF");

  }

  else if(4==i)

  {

 strcpy(Mnemonic,"THEN");

  }

  else if(5==i)

  {

 strcpy(Mnemonic,"ELSE");

  }

  else if(6==i)

  {

 strcpy(Mnemonic,"ID");

  }

  else if(7==i)

  {

 strcpy(Mnemonic,"INT");

  }

  else if(8==i)

  {

 strcpy(Mnemonic,"LT");

  }

  else if(9==i)

  {

 strcpy(Mnemonic,"LE");

  }

  else if(10==i)

  {

 strcpy(Mnemonic,"EQ");

  }

  else if(11==i)

  {

 strcpy(Mnemonic,"NE");

  }

  else if(12==i)

  {

 strcpy(Mnemonic,"GT");

  }

  else if(13==i)

  {

 strcpy(Mnemonic,"GE");

  }

  else if(14==i)

  {

 strcpy(Mnemonic,"IS");

  }

  else if(15==i)

  {

 strcpy(Mnemonic,"PL");

  }

  else if(16==i)

  {

 strcpy(Mnemonic,"MI");

  }

  else if(17==i)

  {

 strcpy(Mnemonic,"MU");

  }

  else if(18==i)

  {

 strcpy(Mnemonic,"DI");

  }

  else

  {

 strcpy(Mnemonic,"Unkown Type");

  }

  printf("(%s

 )對(duì)應(yīng) %s\n",Mnemonic,pStr);

 }

 ///////////////////報(bào)錯(cuò)

 void report_error (int row)

 {

  printf("%s

 Error! In the %d row\n",TOKEN,row);

 }

 ///////////////////掃描程序

 void scanner(FILE *fp)//總的判斷函數(shù)開(kāi)始就應(yīng)該判斷已讀取的字符是否為空字符,不為則不用再讀,直接進(jìn)行判斷,否則再讀

 {

  int i, c;

  fseek(fp,-1,1);//首先回溯一個(gè)字符,就是將文件所有的字符都在 scanner 內(nèi)部判斷,外部 while 循環(huán)不會(huì)浪費(fèi)任何字符

  ch=fgetc (fp);//scanner 中要想判斷字符,必須開(kāi)頭先讀一個(gè)字符

  while(" "==ch||"\n"==ch||"\t"==ch)//將文件中的所有空字符浪費(fèi)在這里

  {

 if("\n"==ch)

 {

  row++;

 }

 ch=fgetc (fp);

  }

 if(EOF==ch)

  {

 return;

  }//必須在這里判斷一下

  if (isalpha (ch))

 /*it must be a identifer!*/

  {

 TOKEN[0]=ch; ch=fgetc (fp); i=1;

 while (isalnum (ch))

 {

  TOKEN[i]=ch; i++;

  ch=fgetc (fp);

 }

 TOKEN[i]= "\0";

 fseek(fp,-1,1);

 /* retract*/

 c=lookup (TOKEN);

 if (c!=6) out (c,TOKEN); else out (c,TOKEN);

  }

  else if(isdigit(ch)|| "."==ch)

  {

 fseek (fp,-1,1);//首先回溯一個(gè)字符,下面為了循環(huán)內(nèi)部使用先讀字符后判斷的格式。

 int Type;

  CurrentState=0;

 i=0;

 do

 {

  ch=fgetc(fp);

  TOKEN[i]=ch;

  i++;

  TOKEN[i]="\0";//為隨時(shí)輸出字符串做準(zhǔn)備

  Type=GetChar(ch);

  EXCUTE (CurrentState,Type,fp,TOKEN,row,i);

 }while(CurrentState!=EndState);

  }

  else

  switch(ch)

  {

 case "<": ch=fgetc(fp);

  if(ch=="=")out(LE,"<=");

  else if(ch==">") out (NE,"<>");

  else

  {

 fseek (fp,-1,1);

 out (LT,"<");

  }

 break;

 case "=":

  {

 ch=fgetc(fp);

 if("="==ch)

 {

  out(EQ, "==");

 }

 else

 {

  fseek (fp,-1,1);

  out(IS, "=");

 }

  }

 break;

 case ">": ch=fgetc(fp);

  if(ch=="=")out(GE,">=");

  else

  {

 fseek(fp,-1,1);

 out(GT,">");

  }

 break;

 case "+":

  {

 out(PL,"+");

  }

  break;

 case "-":

  {

 out(MI,"-");

  }

  break;

 case "*":

  {

 out(MU,"*");

  }

  break;

 case "/":

  {

 out(DI,"/");

  }

  break;

  default: report_error(row); break;

  }

  return;

 }

 ///////////////////判斷矩陣執(zhí)行程序

 int EXCUTE (int state, int symbol,FILE *fp,char JudgeStr[],int row,int index)//row 用于指示出錯(cuò)的行數(shù),index 用于為待輸出的字符串賦結(jié)束符‘\0’時(shí)用

  {

  switch (state)

  {

  case 0:switch (symbol)

  {

 case DIGIT:

 n=0;p=0;e=1;w=d;CurrentState=1;Class=UCON;break;

 case POINT: w=0;n=0;p=0;e=1;CurrentState=3;Class=UCON;break;

 default:

  {

 Class=ClassOther;

 CurrentState=EndState;

 printf("無(wú)符號(hào)數(shù)的第一個(gè)字符是非法的!\n");

  }

  }

  break;

 case 1:switch (symbol)

  {

 case DIGIT: w=w*10+d;break;

 //CurrentState=1

 case POINT: CurrentState=2;break;

 case POWER: CurrentState=4;break;

 default:

  {

 if (ch!=EOF)//如果是因?yàn)樽x到文件結(jié)束字符而終止識(shí)別,就不應(yīng)該回退,否則可能造成死循環(huán)

 {

  fseek(fp,-1,1);//遇到其他的字符,可能是一條語(yǔ)句中的其他字符,需后退,因?yàn)橹骱瘮?shù)外層循環(huán)每次都要讀一個(gè)字符進(jìn)行判斷,而這個(gè)判讀不回溯,所以在內(nèi)部把這個(gè)多讀的字符回溯

 }

 ICON=w;CurrentState=EndState;

  JudgeStr[index-1]="\0";

 printf("(UCON,%i)對(duì)應(yīng) %s\n",ICON,JudgeStr);

  }break;

  }

  break;

 case 2:switch (symbol)

  {

 case DIGIT: n++;w=w*10+d;break;

 case POWER: CurrentState=4;break;

 default:

  {

 if (ch!=EOF)

 {

  fseek(fp,-1,1);

 }

  FCON=w*pow(10,e*p-n);CurrentState=EndState;

 JudgeStr[index-1]="\0";

 printf("(UCON,%f) 對(duì) 應(yīng) 于 %s\n",FCON,JudgeStr);

  }

  }

  break;

 case 3:switch (symbol)

  {

 case DIGIT: n++;w=w*10+d;CurrentState=2;break;

 default:

  {

 HandleError(JudgeStr,row);CurrentState=EndState;//識(shí)別無(wú)符號(hào)數(shù)產(chǎn)生錯(cuò)誤時(shí),不應(yīng)該再回溯,應(yīng)該把造成錯(cuò)誤的那個(gè)字符算到錯(cuò)誤的無(wú)符號(hào)數(shù)字符串中,再向下面識(shí)別單詞時(shí)跳過(guò)這個(gè)字符,不回溯就能達(dá)到這個(gè)目的

  }

  }

  break;

 case 4:switch (symbol)

  {

 case DIGIT: p=p*10+d;CurrentState=6;break;

 case MINUS: e=-1;CurrentState=5;break;

 case PLUS: CurrentState=5;break;

 default:

  {

 /* if (ch!=EOF)

 {

  fseek(fp,-1,1);

 }*/

 HandleError(JudgeStr,row);CurrentState=EndState;

  }

  }

  break;

 case 5:switch (symbol)

  {

 case DIGIT: p=p*10+d;CurrentState=6;break;

 default:

  {

 HandleError(JudgeStr,row);CurrentState=EndState;//判斷一個(gè)無(wú)符號(hào)數(shù)的最后一個(gè)字符應(yīng)該都是多余讀取的,所以為了防止引起后面再次判斷下一無(wú)符號(hào)數(shù)時(shí)產(chǎn)生呑字符的現(xiàn)象,都應(yīng)該回溯一個(gè)字符

  }

  }

  break;

 case 6:switch (symbol)

  {

 case DIGIT:p=p*10+d;break;

 default:

  {

 if (ch!=EOF)

 {

  fseek(fp,-1,1);

 }

 FCON=w*pow(10,e*p-n);CurrentState=EndState;

 JudgeStr[index-1]="\0";

 printf("(UCON,%f)對(duì)應(yīng) %s\n",FCON,JudgeStr);

  }break;

  }

  break;

  }

  return CurrentState;

 }

  ///////////////////無(wú)符號(hào)數(shù)判斷過(guò)程中的字符類型判斷程序

 int GetChar (char ch)

 {

  if(isdigit(ch)) {d=ch-"0";return DIGIT;}

  if (ch==".") return POINT;

  if (ch=="E"||ch=="e") return POWER;

  if (ch=="+") return PLUS;

  if (ch=="-") return MINUS;

  return OTHER;

 }

 ///////////////////判斷出錯(cuò)報(bào)錯(cuò)程序

 int HandleError (char StrJudge[],int row)

 {

  printf ("Row: %d*****%s Error!\n",row,StrJudge); return 0;

 }

 ///////////////////主程序

 int main(int argc, char* argv[])

 {

 FILE *p=fopen("D:\\YWD.txt","r");

  if(ch=fgetc(p)==EOF)//不管小括號(hào)內(nèi)的判斷是否成功,p 指針都會(huì)向后移一個(gè)位置,判斷不成功,ch 中存的字符不變

  {

 printf("The file is null.\n");

 return 0;

  }

  printf("第一個(gè)字母是:%c\n",ch);

  do

  {

 scanner(p);

  }while(ch=fgetc(p)!=EOF);

  fclose(p);

  return 0;

 }

  五.測(cè)試用例及運(yùn)行結(jié)果分析

 測(cè)試用例:begin 8E-5+8*7/1.5

 運(yùn) 行 結(jié) 果 :

推薦訪問(wèn): 編譯 原理 實(shí)驗(yàn)

【編譯原理實(shí)驗(yàn)報(bào)告一】相關(guān)推薦

工作總結(jié)最新推薦

NEW