編譯原理實驗-詞法分析器
一、 實驗目的
設計、編制、除錯一個詞法分析程式,對單詞進行識別和編碼,加深對詞法分析原理的理解。
二、實驗內容
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