1. 程式人生 > >編譯原理實驗-詞法分析器

編譯原理實驗-詞法分析器

一、 實驗目的

設計、編制、除錯一個詞法分析程式,對單詞進行識別和編碼,加深對詞法分析原理的理解。

二、實驗內容

1.選定語言,編輯任意的源程式儲存在檔案中;

2.對檔案中的程式碼預處理,刪除製表符、回車符、換行符、註釋、多餘的空格並將預處理後的程式碼儲存在檔案中;

3.掃描處理後的源程式,分離各個單詞符號,顯示分離的單詞型別。

三、實驗思路

       對於實驗內容1,選擇編寫c語言的源程式存放在code.txt中,設計一個c語言的詞法分析器,主要包含三部分,一部分是預處理函式,第二部分是掃描判斷單詞型別的函式,第三部分是主函式,呼叫其它函式;

       對於實驗內容2,主要實現在預處理函式processor()中,使用文件操作函式開啟源程式檔案(code.txt),去除兩種型別(“//”,“/*…*/”)的註釋、多餘的空格合併為一個、換行符、回車符等,然後將處理後的儲存在另一個新的檔案(afterdel.txt)中,最後關閉文件。

       對於實驗內容3,開啟處理後的檔案,然後呼叫掃描函式,從檔案裡讀取一個單詞呼叫判斷單詞型別的函式與之前建立的符號表進行對比判斷,最後格式化輸出。

四、編碼設計

程式碼參考了兩篇博主的,做了部分改動,添加了預處理函式等

  1 #include<iostream>
  2 #include<fstream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<string>
  6 #include<cstdlib>
  7  
  8 using namespace std;
  9  
 10 int aa;// fseek的時候用來接著的
 11 string  word="";
 12 string  reserved_word[20];//保留
 13 char buffer;//每次讀進來的一個字元
 14 int num=0;//每個單詞中當前字元的位置
 15 int line=1; //行數
 16 int row=1; //列數,就是每行的第幾個
 17 bool flag; //檔案是否結束了
 18 int flag2;//單詞的型別
 19  
 20 
 21 //預處理函式
 22 int processor(){//預處理函式
 23 FILE *p;
 24  int  falg = 0,len,i=0,j=0;
 25  char str[1000],str1[1000],c;
 26  if((p=fopen("code.txt","rt"))==NULL){
 27   printf("無法開啟要編譯的源程式");
 28   return  0;
 29  }
 30  else{
 31   //fgets(str,1000,p);
 32   while((c=getc(p))!=EOF){
 33    str[i++] = c; 
 34   }
 35   fclose(p);
 36   str[i] = '\0';
 37   for(i=0;i<strlen(str);i++){
 38    if(str[i]=='/'&&str[i+1]=='/'){
 39     while(str[i++]!='\n'){}
 40    }//單行註釋
 41    else if(str[i]=='/'&&str[i+1]=='*'){
 42     while(!(str[i]=='*'&&str[i+1]=='/')){i++;}
 43     i+=2;
 44    }//多行註釋
 45    else if(str[i]==' '&&str[i+1]==' '){
 46     while(str[i]==' '){i++;}
 47     i--;
 48     if(str1[j-1]!=' ')
 49        str1[j++]=' ';
 50    }//多個空格,去除空格
 51    else if(str[i]=='\n') {
 52     if(str1[j-1]!=' ')
 53        str1[j++]=' ';
 54    }//換行處理,
 55    else if(str[i]==9){
 56     while(str[i]==9){
 57      i++;
 58     }
 59     if(str1[j-1]!=' ')
 60      str1[j++]=' ';
 61     i--;
 62    }//tab鍵處理
 63    else str1[j++] = str[i];//其他字元處理
 64   }
 65   str1[j] = '\0';
 66   if((p = fopen("afterdel.txt","w"))==NULL){
 67    printf("can not find it!");
 68    return 0;
 69   }
 70   else{
 71    if(fputs(str1,p)!=0){
 72     printf("預處理失敗!");
 73    }
 74    else printf("預處理成功!");
 75   }
 76   fclose(p);
 77  }
 78  return 0;
 79  }
 80 
 81  //設定保留字
 82 void set_reserve()
 83 {
 84     reserved_word[1]="return";
 85     reserved_word[2]="def";
 86     reserved_word[3]="if";
 87     reserved_word[4]="else";
 88     reserved_word[5]="while";
 89     reserved_word[6]="return";
 90     reserved_word[7]="char";
 91     reserved_word[8]="for";
 92     reserved_word[9]="and";
 93     reserved_word[10]="or";
 94     reserved_word[11]="int";
 95     reserved_word[12]="bool";
 96 }
 97  
 98 //看這個字是不是字母
 99 bool judge_word(char x)
100 {
101     if(x>='a' && x<='z' || x>='A' && x<='Z' ){
102         return true;
103     }
104     else return false;
105 }
106  
107 //看這個字是不是數字
108 bool judge_number(char x)
109 {
110     if(x>='0' && x<='9'){
111         return true;
112     }
113     else return false;
114 }
115  
116 //看這個字元是不是界符
117 bool judge_jiefu(char x)
118 {
119     if(x=='('||x==')'||x==','||x==';'||x=='{'||x=='}'){
120         return true;
121     }
122     else return false;
123 }
124  
125  
126 //加減乘
127 bool judge_yunsuanfu1(char x)
128 {
129     if(x=='+'||x=='-'||x=='*')
130     {
131         return true;
132     }
133     else return false;
134 }
135  
136 //等於 賦值,大於小於 大於等於,小於等於,大於小於
137 bool judge_yunsuannfu2(char x)
138 {
139     if(x=='='|| x=='>'||x=='<'||x=='&'||x=='||'){
140         return true;
141     }
142     else return false;
143 }
144  
145  
146 //這個最大的函式的總體作用是從檔案裡讀一個單詞
147 int scan(FILE *fp)
148 {
149     buffer=fgetc(fp);//讀取一個字元
150     if(feof(fp)){//檢測結束符
151         flag=0;return 0;
152     }
153     else if(buffer==' ')
154     {
155         row++;
156         return 0;
157     }
158     else if(buffer=='\n')
159     {
160         row=1;
161         return 0;
162     }
163     //如果是字母開頭或'_' 看關鍵字還是普通單詞
164     else if(judge_word(buffer) || buffer=='_')
165     {
166         word+=buffer;
167         row++;
168         while((buffer=fgetc(fp)) && (judge_word(buffer) || judge_number(buffer) || buffer=='_'))
169         {
170             word+=buffer;
171             row++;
172         }
173         if(feof(fp)){
174                 flag=0;
175                 return 1;
176         }
177         for(int i=1;i<=12;i++){
178             if(word==reserved_word[i]){
179                 aa=fseek(fp,-1,SEEK_CUR);//如果執行成功,stream將指向以fromwhere為基準,偏移offset(指標偏移量)個位元組的位置,函式返回0。
180                 return 3;
181             }
182         }
183         aa=fseek(fp,-1,SEEK_CUR);
184         return 1;
185     }
186  
187     //開始是加減乘 一定是型別4
188     else if(judge_yunsuanfu1(buffer))
189     {
190         word+=buffer;
191         row++;
192         return 4;
193     }
194  
195     //開始是數字就一定是數字
196     else if(judge_number(buffer))
197     {
198         word+=buffer;
199         row++;
200         while((buffer=fgetc(fp)) && judge_number(buffer))
201         {
202             word+=buffer;
203             row++;
204         }
205         if(feof(fp)){
206             flag=0;
207             return 2;
208         }
209         aa=fseek(fp,-1,SEEK_CUR);
210         return 2;
211     }
212  
213     //檢驗界符
214     else if(judge_jiefu(buffer))
215     {
216         word+=buffer;
217         row++;
218         return 6;
219     }
220  
221     //檢驗 <=、  >=、  <>、  == =、 <、>
222     else if(judge_yunsuannfu2(buffer))
223     {
224         row++;
225         word+=buffer;
226         if(buffer=='<')   //為了檢驗題目中的<> <=
227         {
228             buffer=fgetc(fp);
229             if(buffer=='>' || buffer=='=')
230             {
231                 word+=buffer;
232                 row++;
233                 return 5;
234             }
235         }
236         //檢驗  >= ==
237         else{
238             buffer=fgetc(fp);
239             if(buffer=='=')
240             {
241                 word+=buffer;
242                 row++;
243                 return 5;
244             }
245         }
246         if(feof(fp)){
247                 flag=0;
248         }
249         aa=fseek(fp,-1,SEEK_CUR);
250         return 4;
251     }
252  
253     //首字元是/ 有可能是除號 也有可能是註釋
254     else if(buffer=='/')
255     {
256         row++;
257         word+=buffer;
258         buffer=fgetc(fp);
259         aa=fseek(fp,-1,SEEK_CUR);
260             return 4;
261     }
262  
263     else {
264         word+=buffer;
265         row++;
266         return -1;
267     }
268 }
269  
270 int main()
271 {
272     set_reserve();//設定保留字
273     processor();
274     cout<<"open "<<"afterdel.txt"<<endl;
275     flag=1;
276     FILE *fp;
277     if(!(fp=fopen("afterdel.txt","r")))
278     {
279         cout<<"not found the file or other error "<<endl;
280         flag=0;
281     }
282  
283     while(flag==1)
284     {
285         //flag2 返回的型別
286         flag2=scan(fp);//反覆呼叫函式提取單詞
287  
288         if(flag2==1)
289         {
290             cout<<"type:1 identifier      "<<word<<endl;
291             if(word.length()>20)
292             cout<<"ERROR Identifier length cannot exceed 20 characters"<<endl;
293             word.erase(word.begin(),word.end());
294         }
295         else if(flag2==3)
296         {
297             cout<<"type:3 reserved word   "<<word<<endl;
298             word.erase(word.begin(),word.end());
299         }
300         else if(flag2==4)
301         {
302             cout<<"type:4 unary_operator  "<<word<<endl;
303             word.erase(word.begin(),word.end());
304         }
305         else if(flag2==2)
306         {
307             cout<<"type:2 positive number "<<word<<endl;
308             //if(word[0]=='0')
309             //cout<<"ERROR: The first digit cannot be 0!"<<endl;
310             word.erase(word.begin(),word.end());
311         }
312         else if(flag2==6)
313         {
314             cout<<"type:6 Separator       "<<word<<endl;
315             word.erase(word.begin(),word.end());
316         }
317         else if(flag2==5)
318         {
319             cout<<"type:5 double_operator "<<word<<endl;
320             word.erase(word.begin(),word.end());
321         }
322         //非法字元
323         else if(flag2==-1)
324         {
325            cout<<"Illegal character      "<<word<<endl;
326            word.erase(word.begin(),word.end());
327         }
328     }
329  
330         int a=fclose(fp);
331         cout<<"press e to close"<<endl;
332         char end;
333         while(cin>>end && end!='e'){
334             cout<<"只有e可以關閉"<<endl;
335         }
336     return 0;
337 }

 

 

 

 

五、實驗結果

1.下面是編寫的一段源程式,命名為code.txt

 

 

 

 

 

 

 

 

 

 

2.經過程式執行後,在專案目錄下生成了一個新的名為afterdel.txt檔案

 

3.afterdel.txt檔案內容如下,經過預處理後去除了多於內容

 

4.下面是程式詞法分析後得到的結果

 

 

 

 

 

 

六、實驗總結

       該詞法分析器功能基本具備,能夠實現預定要求,本次實驗讓我瞭解如何設計編制並除錯詞法分析程式,加深了我對詞法分析器原理的理解。詞法分析是編譯的第一階段。詞法分析器的主要任務是讀入源程式的輸入字元,將它們組成詞素,生成並輸出一個詞法單元序列,這個詞法單元序列被輸出到語法分析器進行語法分析。另外,由於詞法分析器在編譯器中負責讀取源程式,因此除了識別詞素之外,它還會完成一些其他任務,比如過濾掉源程式中的註釋和空白,將編譯器生成的錯誤訊息與源程式的位置關聯起來等。

&n