實驗一 詞法分析
一、實驗目的
編制一個讀單詞過程,從輸入的源程式中,識別出各個具有獨立意義的單詞,即基本保留字、識別符號、常數、運算子、分隔符五大類。並依次輸出各個單詞的內部編碼及單詞符號自身值。
二、實驗題目
如源程式為C語言。輸入如下一段:
main()
{
int a=-5,b=4,j;
if(a>=b)
j=a-b;
else j=b-a;
}
要求輸出如下:
(2,”main”)(5,”(”)(5,”)”)
(5,”{”)(1,”int”)(2,”a”)
(4,”=”)(3,”-5”)(5,”,”)
(2,”b”)(4,”=”)(3,”4”)
(5,”,”)(2,”j”)(5,”;”)
(1,”if”)(5,”(”)(2
(4,”>=”)(2,”b”)(5,”)”)
(2,”j”)(4,”=”)(2,”a”)
(4,”-”)(2,”b”)(5,”;”)
(1,”else”)(2,”j”)(4,”=”)
(2,”b”)(4,”-”)(2,”a”)
(5, ”;”)(5,”}”)
三、實驗理論依據
(一)識別各種單詞符號
1、 程式語言的單詞符號一般分為五種:
1) 關鍵字(保留字/ 基本字)if 、while 、begin…
2) 識別符號:常量名、變數名…
3) 常數:34 、56.78 、true 、‘a’ 、…
4) 運算子:+ 、- 、* 、/ 、〈、and
5) 界限符:,;() { } /*…
2、 識別單詞:掌握單詞的構成規則很重要
1) 識別符號的識別:字母| 下劃線+( 字母/ 數字/ 下劃線)
2) 關鍵字的識別:與識別符號相同,最後查表
3) 常數的識別
4) 界符和算符的識別
3、 大多數程式設計語言的單詞符號都可以用轉換圖來識別,如圖1-1
圖1-1
4、 詞法分析器輸出的單詞符號常常表示為二元式:(單詞種別,單詞符號的屬性值)
2) 關鍵字可視其全體為一種,也可以一字一種。採用一字一種得分法實際處理起來較為方便。
3) 識別符號一般統歸為一種
4) 常數按型別(整、實、布林等)分種
5) 運算子可採用一符一種的方法。
6) 界符一般一符一種的分法。
(二)超前搜尋方法
1、 詞法分析時,常常會用到超前搜尋方法。
如當前待分析字串為“a>+” ,當前字元為“>” ,此時,分析器倒底是將其分析為大於關係運算符還是大於等於關係運算符呢?
顯然,只有知道下一個字元是什麼才能下結論。於是分析器讀入下一個字元’+’ ,這時可知應將’>’ 解釋為大於運算子。但此時,超前讀了一個字元’+’ ,所以要回退一個字元,詞法分析器才能正常執行。又比如:‘+’ 分析為正號還是加法符號。
(三)預處理
預處理工作包括對空白符、跳格符、回車符和換行符等編輯性字元的處理,及刪除註解等。由一個預處理子程式來完成。
四、詞法分析器的設計
1、 設計方法:
1) 寫出該語言的詞法規則。
2) 把詞法規則轉換為相應的狀態轉換圖。
3) 把各轉換圖的初態連在一起,構成識別該語言的自動機
4) 設計掃描器
2、 把掃描器作為語法分析的一個過程,當語法分析需要一個單詞時,就呼叫掃描器。掃描器從初態出發,當識別一個單詞後便進入終態,送出二元式
圖1-2 取單詞程式框圖
五、程式
#include<stdio.h> #include<string.h> #include <ctype.h> #include <malloc.h> #include <stdlib.h> #define NULL 0 FILE *fp; char cbuffer; char *key[34]= {"if","else","for","while","do","return","break","continue","auto","double", "int","struct","long","switch","case","enum","register","typedef","char","extern" "union","const","float","short","unsigned","signed","void","default","goto","sizeof", "volatile","static","inline","restrict" }; int atype,id=4; /*1:關鍵字 2:識別符號 3:常數 4:運算子 5:界符*/ int search(char searchchar[ ],int wordtype) /*判斷單詞是保留字還是識別符號*/ { int i=0; int p; switch (wordtype) { case 1: for (i=0; i<=32; i++) { if (strcmp(key[i],searchchar)==0) { p=i+1; /*是保留字則p為非0且不重複的整數*/ break; } else p=0; /*不是保留字則用於返回的p=0*/ } return(p); } } char alphaprocess(char buffer) { int atype; /*保留字陣列中的位置*/ int i=-1; char alphatp[20]; while((isalpha(buffer))||(isdigit(buffer))||buffer=='_') { alphatp[++i]=buffer; buffer=fgetc(fp); } /*讀一個完整的單詞放入alphatp陣列中*/ alphatp[i+1]='\0'; atype=search(alphatp,1);/*對此單詞呼叫search函式判斷型別*/ if(atype!=0) { printf("%s, (1.關鍵字,%d)\n",alphatp,atype-1); id=1; } else { printf("(%s ,2.識別符號)\n",alphatp); id=2; } return(buffer); } char digitprocess(char buffer) { int i=-1; char digittp[20]; while ((isdigit(buffer))||buffer=='.'||buffer=='e')//1.小數點 2.科學計數法 { digittp[++i]=buffer; buffer=fgetc(fp); } digittp[i+1]='\0'; printf("(%s ,3.常數)\n",digittp); id=3; return(buffer); } char otherprocess(char buffer) { char ch[20]; ch[0]=buffer; ch[1]='\0'; if(ch[0]==','||ch[0]==';'||ch[0]=='{'||ch[0]=='}'||ch[0]=='('||ch[0]==')')/*界符*/ { printf("(%s ,5.界符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='*'||ch[0]=='/'||ch[0]=='%') /*3.取餘識別符號*/ { if(ch[0]=='%') /*4.格式說明*/ { buffer=fgetc(fp); if(buffer=='c'||buffer=='s'||buffer=='d'||buffer=='f') { ch[1]=buffer; ch[2]='\0'; printf("(%s,格式說明)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='*')/*5.*=,/=*/ { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='=') { ch[2]='\0'; printf("(%s,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='/') { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='=') { ch[2]='\0'; printf("(%s,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[1]=='*'||ch[1]=='/')/*6.註釋*/ { int i=1; if(ch[1]=='*') { while(ch[i]!='/') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,註釋)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } else { while(ch[i]!='\n') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,註釋)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } } printf("(%s ,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='='||ch[0]=='!'||ch[0]=='<'||ch[0]=='>') { buffer=fgetc(fp); if(buffer=='=') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,4.運算子)\n",ch); } else { printf("(%s ,4.運算子)\n",ch); id=4; return(buffer); } buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='+'||ch[0]=='-') { if(id==4) /*在當前符號以前是運算子,則此時為正負號*/ { buffer=fgetc(fp); int i=1; ch[i]=buffer; /*7.多位整數*/ if(isdigit(ch[i])) { while(isdigit(ch[i])||ch[i]=='e'||ch[i]=='.') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,3.常數)\n",ch); id=3; //buffer=fgetc(fp); id=4; return(buffer); } buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='+')/*8.自增...++、+=、-=,--*/ { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='='||ch[1]=='+') { ch[2]='\0'; printf("(%s,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } ch[1]='\0'; printf("(%s ,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='-') { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='='||ch[1]=='-'||ch[1]=='>')//9.指向結構體成員運算子 { ch[2]='\0'; printf("(%s,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } ch[1]='\0'; printf("(%s ,4.運算子)\n",ch); //buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='.')/*10.多位小數*/ { buffer=fgetc(fp); int i=1; ch[i]=buffer; if(isdigit(ch[i])) { while(isdigit(ch[i])) { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,3.常數)\n",ch); id=4; return(buffer); } } if(ch[0]=='#')//11.include<>標頭檔案 { buffer=fgetc(fp); int i=1; ch[i]=buffer; if(ch[i]=='i') { while(ch[i]!='>') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,#include<>)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[i]=='d') /*12.define*/ { while(isalpha(ch[i])) { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,#define巨集定義)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='&'||ch[0]=='^'||ch[0]=='|') /*13.邏輯運算子*/ { buffer=fgetc(fp); if(buffer=='&') { ch[1]=buffer; ch[2]='\0'; buffer=fgetc(fp); printf("(%s ,4.運算子)\n",ch); id=4; return(buffer); } else if(buffer=='|') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } else { printf("(%s ,4.運算子)\n",ch); id=4; return(buffer); } } if(ch[0]=='['||ch[0]==']')/*14.下標運算子*/ { printf("(%s ,4.運算子)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='\''||ch[0]=='\"')/*15.引號*/ { printf("(%s ,引號)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='\\')/*16.轉義字元*/ { buffer=fgetc(fp); if(buffer=='n'||buffer=='t'||buffer=='b'||buffer=='r'||buffer=='f'||buffer=='\\'||buffer=='\''||buffer=='\"') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,轉義字元)\n",ch); } buffer=fgetc(fp); id=4; return(buffer); } } int main() { if ((fp=fopen("H:\\example.c","r"))==NULL) /*只讀方式開啟一個檔案*/ printf("error"); else { printf("1:關鍵字 2:識別符號 3:常數 4:運算子 5:界符\n"); cbuffer = fgetc(fp); /*fgetc( )函式:從磁碟檔案讀取一個字元*/ while (cbuffer!=EOF) { if(cbuffer==' '||cbuffer=='\n'||cbuffer=='\t') /*掠過空格和回車符*/ /*17.tab鍵*/ cbuffer=fgetc(fp); else if(isalpha(cbuffer)) cbuffer=alphaprocess(cbuffer); else if (isdigit(cbuffer)) cbuffer=digitprocess(cbuffer); else cbuffer=otherprocess(cbuffer); } } return 0; }
六、結果