1.實驗要求ios
輸入爲一個以類C語言編寫的源程序spa
輸出爲一組二元組序列構成的文本文件,一行爲一個二元組,二元組中間以逗號隔開設計
實驗報告上要求附上DFA code
2.語言說明:orm
保留字:unsigned、break、return、void、case、float、char、for、while、continue、if、default、do、int、switch、double、long、elseblog
運算符:+,-,*,/,>,>=,<,<=,==,!=,&&,||,!token
界限符:{ }( ); ,ip
常量:十進制無符號數ci
標識符:以字母或下劃線開始,後面跟上字母或數字字符串
3. 實驗原理
詞法分析是編譯的第一階段。詞法分析器的主要任務是讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列,這個詞法單元序列被輸出到語法分析器進行語法分析。另外,因爲詞法分析器在編譯器中負責讀取源程序,所以除了識別詞素以外,它還會完成一些其餘任務,好比過濾掉源程序中的註釋和空白,將編譯器生成的錯誤消息與源程序的位置關聯起來等。詞法分析器的做用以下:
讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列;
過濾掉源程序中的註釋和空白;
將編譯器生成的錯誤消息與源程序的位置關聯起來;
4.DFA設計:
5.實驗代碼
#include<iostream> #include<string> #include<map> #include<vector> #include<iomanip> #include<ctype.h> #include<fstream> using namespace std; typedef pair<int,int> mp; const string key_word[]={"unsigned","break","return","void","case","float","char","for","while","continue","if","default","do","int","switch","double","long","else"};//保留字 const string operators[]={"+","-","*","/",">",">=","<","<=","==","!=","&&","||","!"};//運算符 const char jiefu[]={',',';','(',')','{','}'};//界符 map<string,mp> flag_table;//標識符表等 map<string,mp>num_table;//常量表 map<string,mp>str_table;//字符串表 map<string,mp>head_table;//頭文件表 map<string,mp>char_table;//字符表 map<char,mp>fenjiefu_table;//分界符表 int keymark[40],operatormark[40]; int isjiefu(char ch) { for(int i=0;i<5;i++) if(ch==jiefu[i]) return 1; return 0; } //判斷是否是關鍵字 int iskey(string ch) { int i; for(i=0;i<17;i++) if(key_word[i]==ch) return i; return -1; } int isope(string ch) { int i; for(i=0;i<12;i++) if(operators[i]==ch) return i; return -1; } int ischar(char ch) { if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z') return 1; return -1; } int isnumber(char ch) { if(ch>='0'&&ch<='9') return 1; return -1; } //得到關鍵字或者標識符 void get_keyflag(char* ptr,FILE* f) { string token; token+=*ptr; while(1) { *(++ptr)=fgetc(f); if(ischar(*ptr)==-1&&isnumber(*ptr)==-1&&*ptr!='_') break; token+=*ptr; } ungetc(*ptr,f); //*ptr='\0'; int h=iskey(token); if(h>=0)//是關鍵字 { if(!keymark[h]) keymark[h]=1; cout<<token<<" , "<<"保留字"<<endl; } else//標識符 { mp tmp; if(flag_table.find(token)==flag_table.end())//嶄新的標記符 { tmp=make_pair(1,flag_table.size()+1); flag_table[token]=tmp; } else { map<string,mp>::iterator it; it=flag_table.find(token); tmp=it->second; } cout<<token<<" , "<<"標識符"<<endl; } } //獲取數字 void get_num(char* ptr,FILE* f) { string token; token+=*ptr; while(1) { *(++ptr)=fgetc(f); if(isnumber(*ptr)!=1) break; token+=*ptr; } ungetc(*ptr, f); mp tmp; if(num_table.find(token)==num_table.end())//新的數字 { tmp=make_pair(2,num_table.size()+1);//2表明着數字 num_table[token]=tmp; } else { map<string,mp>::iterator it; it=num_table.find(token); tmp=it->second; } cout<<token<<" , "<<"常量"<<">"<<endl; } //得到字符串 void get_string(char* ptr,FILE* f) { string token; *(++ptr)=fgetc(f); token+=*(ptr); while(1) { *(++ptr)=fgetc(f); if(feof(f)||*ptr=='"') break; token+=*(ptr); } if(*(ptr)=='"') { map<string,mp>::iterator it; it=str_table.find(token); mp tmp; if(it==str_table.end())//新的字符串 { tmp=make_pair(3,str_table.size()+1); str_table[token]=tmp; } else {tmp=it->second;} cout<<token<<" , "<<"標識符"<<endl; } //找不到匹配的字符串 else { cout<<"程序錯誤!"<<endl; } } void double_operator(char* ptr,FILE* f) { string token,tmp; token+=*ptr; tmp+=*ptr; *(++ptr)=fgetc(f); token+=*ptr; int p=isope(token); if(p>=0) { operatormark[p]=1; cout<<token<<" , "<<"運算符"<<endl; } else { ungetc(*ptr,f); p=isope(tmp); operatormark[p]=1; cout<<tmp<<" , "<<"運算符"<<endl; } } void single_operator(char* ptr,FILE* f) { string token; token+=*ptr; int index=isope(token); if(index>=0) { operatormark[index]=1; cout<<token<<" , "<<"運算符"<<endl; } else { if(*ptr<0||*ptr>127) cout<<"輸入的不是ascii碼"<<endl; } } void acehandle(char* ptr,FILE* f) { string token; token+=*ptr; while(1) { ptr++; *ptr=fgetc(f); if(*ptr==' '||*ptr=='"'||*ptr=='<') break; token+=*ptr; } if(*ptr=='<'||*ptr=='"') ungetc(*ptr,f); if(token=="#define") { cout<<token<<","<<"宏定義"<<endl; } if(token=="#include") { cout<<setw(5)<<"<"<<setw(10)<<token<<setw(20)<<"預處理"<<setw(20)<<">"<<endl; *ptr=fgetc(f); cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl; string tmp; *ptr=fgetc(f); tmp+=*ptr; while(1) { *(++ptr)=fgetc(f); if(*ptr=='>'||*ptr=='"') break; tmp+=*ptr; } mp p; if(head_table.find(tmp)==head_table.end()) { p=make_pair(4,head_table.size()+1); head_table[tmp]=p; } else { map<string,mp>::iterator it; it=head_table.find(tmp); p=it->second; } cout<<setw(5)<<"<"<<setw(10)<<tmp<<setw(10)<<p.first<<setw(10)<<"頭文件"<<setw(10)<<p.second<<">"<<endl; cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl; } } void handlechar(char ch,FILE* f) { char* ptr=new char[1000]; *ptr=ch; if(ischar(*ptr)==1)//以字母開頭的--關鍵字或者標識符 { get_keyflag(ptr,f);} else if(isnumber(*ptr)==1) { get_num(ptr,f);} else if(*ptr=='#') { acehandle(ptr,f);} else if(*ptr=='"') { get_string(ptr,f);} else if(*ptr=='+'||*ptr=='-'||*ptr=='*'||*ptr=='/'||*ptr=='='||*ptr=='%'||*ptr=='>'||*ptr=='<') { double_operator(ptr,f);} else if(isjiefu(*ptr)==1) { mp tmp; map<char,mp>::iterator it; if(fenjiefu_table.find(*ptr)==fenjiefu_table.end()) { tmp=make_pair(6,fenjiefu_table.size()+1); fenjiefu_table[*ptr]=tmp;} else { it=fenjiefu_table.find(*ptr); tmp=it->second;} cout<<*ptr<<" , "<<"界符"<<endl; } else { single_operator(ptr,f);} } int main() { FILE* f; f=fopen("input.txt","r"); if(f==NULL) { cout<<"讀入文件錯誤"<<endl; return 0;} else { char ch=fgetc(f); while(!feof(f)) { if(ch!=' '&&ch!='\n'&&ch!='\t') handlechar(ch,f); ch=fgetc(f);} } fclose(f); return 0; }
輸入的類C詞法
char word[10]; char pro[100][100] = { "PROGRAM", "BEGIN", "END", "VAR", "INTEGER", "WHILE", "IF", "THEN", "ELSE", "DO", "PROCEDURE" , "char","int","if","else","var" ,"return","break","do","while","for","double","float","short"}; int n = 0; word[n++] = a[i++]; while ((a[i] >= 'A'&&a[i] <= 'Z') || (a [i] >= '0' && a[i] <= '9')||(a[i]>='a'&&a[i]<='z')) { word[n++] = a[i++]; } word[n] = '\0'; i--;
程序運行輸出結果:
6.分析討論:
(1)碰見問題:二目運算符沒法識別 ,致使拆分識別爲兩個單目運算符;第二個問題就是若是識別的詞法帶有註釋,會報錯。
(2)解決方法: 修改識別運算符代碼部分加入雙目運算符的識別
(3)後續改進思路:一個不方便的地方就是沒能實現經過輸入文件名讀取文件的數據,每次讀入新的文件仍須要修改代碼。