編譯器之詞法分析

最近咱們在作一個有關SNL語言的編譯器,下面寫了一下大概流程git

詞法分析器是編譯過程的第一階段,功能是正則表達式

1.對以字符串形式輸入的源程序(這裏是把源程序從文件讀出,也能夠在控制檯輸入)按順序進行掃描,根據SNL語言的詞法規則識別具備獨立意義的單詞(符號)序列,
  如保留字(由語言系統自身定義的,一般是由字母組成的字符串),
     標識符(通常是由字母開頭,字母、數字,或其它符號的任意組合構成的,這裏SNL是由字母開頭,後面加字母或數字組成的,分析起來較簡單),
     常量(包括整數常數、 實數常數、字符串常量等),
     特殊符號(運算符和界限,運算符表示程 序中算術運算、邏輯運算、字符運算、賦值運算的肯定的字符或字符串)等,算法

並輸出與其等價的TOKEN序列(這裏將Token序列保存在了文件中)。數組

2.報告詞法錯誤!
   詞法錯誤:語言字母表之外的非法字符
(用肯定有限自動機能夠容易識別)數據結構

**幾個問題和處理方法** 編碼

 1.保留字的識別 spa

     把保留字看成通常標識符識別,而後查找保留字表,就是這裏的Lex表,若是有,把它看成保留字處理,若是沒有按通常標識符處理。設計

 2.複合單詞的識別   3d

     有一類單詞是由兩個或者兩個以上符號組成的,有時前綴部分也能夠是一個獨立的單詞code

  3.數的轉換  

      詞法分析應把字符串轉換成數,「123」當作123

  4.向前看幾個字符的處理    

      爲了識別出一個單詞須要向前看好幾個單詞

  5.控制字符的處理  

      1)無用的空格符和製表符刪掉(這裏是自動忽略)  

      2)字符串內的空格不能刪  

      3)換行符不能直接刪除,用於錯誤定位

      4)註釋   

         源程序中的註釋沒有任何語法和語義上的意義,在進行詞法分析時能夠直接將註釋忽略,沒必要生成TOKEN

      5)直接在語義信息部分存儲   

         語義信息的長度有限制時,直接將標識符或常量自己存儲於TOKEN中的語義(語義信息是爲了 後面進行的語義分析和代碼生成提供信息)

   構造詞法分析器的步驟:   

      1.肯定詞法分析器的接口,即肯定詞法分析器是做爲語法分析的一個子程序仍是做爲獨立一遍   

      2.肯定單次分類和TOKEN結構   

      3.肯定每一類單詞的描述正則表達式->NFA->DFA   

      4.設計算法實現DFA 單詞的內部表示:

         TOKEN     TOKEN一般包含兩部分:token-type,attribute-value     token-type: 說明單詞的類別,爲語法分析提供信息     attribute-value:單詞的屬性,爲語義分析和代碼生成提供信息 (這裏  TOKEN包含4部分:在源程序中的行數,Lex表中的詞法信息編碼,語法信息,語義信息 這裏用肯定有限自動機DFA識別符號串)

        輸入:以EOF做爲結束符的符號序列    輸出:token序列

       數據結構:

        struct Token{        

             int line;//行數  

             LEX lexType;//記錄單詞的詞法信息枚舉編碼

             char LexInfor[StrMaxSize];//詞法信息       

             char SemInfor[StrMaxSize];//語義

       };  

    程序以下:

  1 /*************
  2         SNL語法分析程序輸出TOKEN序列,數據表示是鏈表形式
  3         巴科斯範式表示: <標識符> := 字母 { 字母 | 數字}
  4 
  5 */
  6 #include "scanner.h"
  7  char* LEXSTR[] = {
  8     "0","program", "procedure","type","var","if","then","else","fi",
  9     "while","do","endwh","begin","end","read","write","array",
 10     "of","record","return","integer","char",
 11     "ID", "INTC",
 12     ":=","=","<","+","-","*","/","(",")",".",":",";",",","[","]","..",
 13     "EOF" , "SPACE" , "ERROR" , "\\" ,"#" //"#"爲結束標誌          //43
 14 };
 15 char* Word[]={
 16     /*********保留字****** */ 
 17     "0","PROGRAM","PROCEDURE","TYPE","VAR","IF","THEN","ELSE","FI","WHILE",
 18         "DO","ENDWH","BEGIN","END","READ","WRITE","ARRAY","OF","RECORD","RETURN",
 19         
 20     "INTEGER","CHAR",/******類型*******/
 21 
 22     /******標識符,整數常量**/  
 23     "ID","INTC",
 24 
 25     /*****單雙分界符,數組下標限界符*******/
 26     "ASSIGN","EQ","LT","ADD","SUB","MUL","DIV","LPAREN","RPAREN",
 27     "DOT","COLON","SEMI","COMMA","LMIDPAREN","RMIDPAREN","UNDERANGE",
 28 
 29     /*//特殊符號:文件結束符,空格符,出錯標號*/
 30     "EOFF","SPACE","ERROR1","UNDIV"
 31 
 32 };
 33 TokenNode::TokenNode(int lineNum,int code,const char *lexInfor,const char*semanInfor){
 34             token.line=lineNum;
 35             token.lexType=LEX(code);
 36             strcpy(token.LexInfor,lexInfor);
 37             strcpy(token.SemInfor,semanInfor);
 38 }
 39 List::~List(){
 40     while(start!=NULL){
 41         TokenNode*p=start;
 42         start=start->next;
 43         delete p; 
 44     }
 45 }
 46 
 47 bool List::OutToFile(const char *filename)
 48 {
 49     if(start==NULL)
 50         return false;
 51     TokenNode* ptr=start;
 52     FILE* file=fopen(filename,"w+");//若文件存在,清零重寫
 53     if(NULL==file)
 54         return 0;
 55     fprintf(file,"%5s\t%3s\t%10s\t%2s\n", "LineNUM" , "LEX" , "LexStr" , "SemStr");    
 56     for(;ptr!=NULL;ptr=ptr->next)
 57     {
 58             fprintf(file,"%5d\t%3d\t%10s\t%2s\n",ptr->token.line , ptr->token.lexType , ptr->token.LexInfor , ptr->token.SemInfor );
 59     }
 60     fclose(file);
 61     return 1;
 62 }
 63 bool List::OutToScreen()
 64 {
 65     if(start==NULL)
 66         return false;
 67     printf("%5s\t%3s\t%10s\t%s\n", "LineNUM" , "LEX" , "LexStr" , "SemStr");
 68     TokenNode* ptr=start;
 69     for(;ptr!=NULL;ptr=ptr->next)
 70     {
 71             printf("%5d\t%3d\t%10s\t%2s\n",ptr->token.line , ptr->token.lexType , ptr->token.LexInfor , ptr->token.SemInfor );
 72     }
 73     return 1;
 74 }
 75 
 76 bool List::insertNode(TokenNode*p){
 77         if(p==NULL)
 78             return false;
 79         if(end==NULL){
 80             start=end=p;
 81             end->next=NULL;
 82         }
 83         else{
 84             end->next=p;
 85             end=p;
 86             end->next=NULL;
 87 
 88         }
 89         return true;
 90 }
 91     
 92 List* Scanner::scanner(const char* sourceFile,const char* tokenFile){
 93         FILE*fp;
 94         fp=fopen(sourceFile,"r");
 95         if(fp==NULL){
 96                 printf("Failed to open the file.");
 97                 exit(0);
 98                     //return 1;
 99         }
100         int lineNum=1;        //從第一行開始
101         bool flag=true;
102         TokenNode* ptr;
103         List *list=new List;//新建一個TokenList,用以保存分析獲得的TOKEN序列
104         while(flag){
105             char ch=getNextChar(fp);//cout<<(int)ch<<" ";
106             switch(theState(ch)){
107                 case S1:                         //字母,,區分標識符仍是保留字
108                     ungetNextChar(fp);           //爲了後面拼接字符串,而不用傳過去
109                     isLetterStr(fp,lineNum,list);
110                     break;
111                 case S2:  
112                     ungetNextChar(fp);
113                     isNumStr(fp,lineNum ,list);
114                     break;
115                 case S3:                                   //分隔符
116                     ungetNextChar(fp);
117                     isSignStr(fp,lineNum,list);
118                     break;
119                 case S4:                                             //註釋
120                     ungetNextChar(fp);
121                     isCommentStr(fp,lineNum);
122                     break;
123                 case S5:                                               //空格 製表符  
124                         //    reservedLookUp(fp);                  //*!不能回退字符,否則在下面的while會死循環
125                      
126                     while(ch==' '||ch=='\t')       //怎麼寫成&&了?
127                         ch=getNextChar(fp);
128                     if(ch!=EOF)
129                         ungetNextChar(fp);
130                     break;
131                 case S6:                                     ////回車換行。回車符'\r'
132                     lineNum++;
133                     break;
134                 case S7:  
135                     flag=false;              
136                 
137                     ptr=new TokenNode(lineNum,EOFF,"EOF","文件結束符,無語義信息");
138                     insertTokenList(list,ptr);
139                     fclose(fp);
140                     break;
141                 case S8:
142                 
143                         //ungetNextChar(fp);
144                         cerr<<"Error! line"<<lineNum<<" :出現非法字符"<<endl;
145                          ptr=new TokenNode(lineNum,ERROR1,"ERROR","錯誤未定義字符");
146                         insertTokenList(list,ptr);
147                 }
148         }
149 
150                 fclose(fp);
151                 if(list->Start()!=0)
152                      tokenlist=list;
153                  if(!list->OutToFile(tokenFile))
154                       cerr<<"Error:cannot open the LexFile!"<<endl;
155                  if(!list->OutToScreen())
156                        cerr<<"Error:cannot write on Screen!"<<endl;
157                 return list;
158         
159 }
160 State Scanner::theState(char ch){
161         
162             if(isLetter(ch)){             //字母
163                 return S1;
164             }else if(isDigit(ch)){        //數字
165                 return S2;
166             }else if(isSymbol(ch)){       //符號
167                 return S3;
168             }else if(ch=='{'){          //註釋
169                 return S4;
170             }else if(ch==' '||ch=='\t'){    
171                 return S5;
172             }else if(ch=='\n'||ch=='\r'){   //回車換行。回車符'\r'
173                 return S6;
174             }else if(ch==EOF){             
175                 return S7;
176             }else
177                 return S8;
178 }
179 bool Scanner::isLetter(char ch){
180             if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
181                 return 1; 
182             return 0;
183 }
184 bool Scanner::isDigit(char ch){
185             if(ch>='0'&&ch<='9')
186                 return 1;
187             return 0;
188 }
189 bool Scanner::isSymbol(char ch){
190             if(ch=='+'||ch==':'||ch=='='||ch=='<'||ch=='+'||ch=='-'
191                       ||ch=='*'||ch=='/'||ch=='('||ch==')'||ch=='.'
192                       ||ch==';'||ch==','||ch=='['||ch==']'||ch=='\\')
193                       return 1;
194             return 0;
195 }
196 int Scanner::getCode(const char* ch){
197             for(int i=0;LEXSTR[i]!="$";i++){
198                 if(strcmp(ch,LEXSTR[i])==0){
199                     return i; 
200                 }
201             }
202             return -1;
203 }
204 int Scanner::isReservedWord(const char* ch){
205             for(int i=0;i<22;i++){
206                 if(strcmp(ch,LEXSTR[i])==0){             //!!!!!!!不能用==
207                     return i; 
208                 }
209             }
210             return -1;
211 }
212 void Scanner::insertTokenList(List* list,TokenNode* ptr)    
213 {
214             ptr->next=NULL;
215             list->insertNode(ptr);
216 }
217 void Scanner::isLetterStr(FILE*fp,int lineNum,List*list){
218     string str1;
219     char ch=getNextChar(fp);
220     while(isLetter(ch)||isDigit(ch))
221     {
222         str1+=ch;
223         ch=getNextChar(fp);
224     }
225     if(ch!=EOF)
226         ungetNextChar(fp);
227     int lex=isReservedWord(str1.c_str());
228     if(lex!=-1)//c_str() 以 char* 形式傳回 str 內含字符串
229     {    
230         TokenNode*    ptr=new TokenNode(lineNum,lex,Word[lex],"保留字,無語義信息");
231         insertTokenList(list,ptr);
232     }else{
233         TokenNode*ptr=new TokenNode(lineNum,ID,"ID",str1.c_str());
234         insertTokenList(list,ptr);
235     }
236 }
237     
238 void Scanner::isNumStr(FILE*fp,int lineNum,List*list) {
239           string str2;
240           char ch;
241           ch=getNextChar(fp);
242           while(isDigit(ch)){
243               str2+=ch;
244               ch=getNextChar(fp);
245           }
246           if(ch!=EOF)
247               ungetNextChar(fp); 
248           TokenNode *ptr; 
249           ptr=new TokenNode(lineNum,INTC,"INTC",str2.c_str());
250           insertTokenList(list,ptr);
251 }
252 void Scanner::isSignStr(FILE*fp,int lineNum,List*list){
253     char ch;
254     ch=getNextChar(fp);
255     char ch1;
256     TokenNode *ptr;
257     if(ch==':')
258     {
259         if(getNextChar(fp)=='=')
260         {
261             ptr=new TokenNode(lineNum,getCode(":="),"ASSIGN","雙分隔符,無語義信息");
262             insertTokenList(list,ptr);
263             return ;
264         }
265         else
266             ungetNextChar(fp);
267         return;
268     }
269     if(ch=='.') 
270     {
271         ch1=getNextChar(fp);
272         if(ch1=='.')
273         {
274             ptr=new TokenNode(lineNum,getCode(".."),"UNDERANGE","數組限界符,無語義信息");
275             insertTokenList(list,ptr);
276             return;
277         }
278         if(ch1!=EOF)
279             ungetNextChar(fp);
280         ptr=new TokenNode(lineNum,getCode("."),"DOT","程序結束符,無語義信息");
281         insertTokenList(list,ptr);
282         return;
283     }
284     string str;
285       str+=ch;
286     ptr=new TokenNode(lineNum,getCode(str.c_str()),Word[getCode(str.c_str())],"單分隔符,無語義信息");
287     insertTokenList(list,ptr);    
288 }
289 void Scanner:: isCommentStr(FILE*fp,int lineNum) {
290     char ch;
291     
292     ch=getNextChar(fp);
293     TokenNode *ptr; 
294     while(ch!='}')
295     {
296         if(ch=='\n')
297             lineNum++;
298         else if(ch==EOF)
299         {
300             cout<<"Error, line"<<lineNum<<" : 到達文件尾,仍找到不註釋結束符,請檢查源程序後編譯!"<<endl;
301             fclose(fp);
302             break;
303         }
304         ch=getNextChar(fp);
305     }
306         
307 }
308 int Scanner:: getlex(const char *ch)
309 {
310     for(int i=0;strcmp(LEXSTR[i],"$")!=0;i++)
311     {
312         if(strcmp(ch,LEXSTR[i])==0)
313             return i;
314     }
315     return -1;
316 }
相關文章
相關標籤/搜索