設計、編制、調試一個詞法分析程序,對單詞進行識別和編碼,加深對詞法分析原理的理解。ios
1.選定語言,編輯任意的源程序保存在文件中;git
2.對文件中的代碼預處理,刪除製表符、回車符、換行符、註釋、多餘的空格並將預處理後的代碼保存在文件中;ide
3.掃描處理後的源程序,分離各個單詞符號,顯示分離的單詞類型。函數
對於實驗內容1,選擇編寫c語言的源程序存放在code.txt中,設計一個c語言的詞法分析器,主要包含三部分,一部分是預處理函數,第二部分是掃描判斷單詞類型的函數,第三部分是主函數,調用其它函數;編碼
對於實驗內容2,主要實如今預處理函數processor()中,使用文檔操做函數打開源程序文件(code.txt),去除兩種類型(「//」,「/*…*/」)的註釋、多餘的空格合併爲一個、換行符、回車符等,而後將處理後的保存在另外一個新的文件(afterdel.txt)中,最後關閉文檔。spa
對於實驗內容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文件code
3.afterdel.txt文件內容以下,通過預處理後去除了多於內容
4.下面是程序詞法分析後獲得的結果
該詞法分析器功能基本具有,可以實現預約要求,本次實驗讓我瞭解如何設計編制並調試詞法分析程序,加深了我對詞法分析器原理的理解。詞法分析是編譯的第一階段。詞法分析器的主要任務是讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列,這個詞法單元序列被輸出到語法分析器進行語法分析。另外,因爲詞法分析器在編譯器中負責讀取源程序,所以除了識別詞素以外,它還會完成一些其餘任務,好比過濾掉源程序中的註釋和空白,將編譯器生成的錯誤消息與源程序的位置關聯起來等。