编译器之词法分析

最近我们在做一个有关SNL语言的编译器,下面写了一下大概流程

词法分析器是编译过程的第一阶段,功能是

1.对以字符串形式输入的源程序(这里是把源程序从文件读出,也可以在控制台输入)按顺序进行扫描,根据SNL语言的词法规则识别具有独立意义的单词(符号)序列,
  如保留字(由语言系统自身定义的,通常是由字母组成的字符串),
     标识符(一般是由字母开头,字母、数字,或其它符号的任意组合构成的,这里SNL是由字母开头,后面加字母或数字组成的,分析起来较简单),
     常量(包括整数常数、 实数常数、字符串常量等),
     特殊符号(运算符和界限,运算符表示程 序中算术运算、逻辑运算、字符运算、赋值运算的确定的字符或字符串)等,

并输出与其等价的TOKEN序列(这里将Token序列保存在了文件中)。

2.报告词法错误!
   词法错误:语言字母表以外的非法字符
(用确定有限自动机可以容易识别)

**几个问题和处理方法**

 1.保留字的识别

     把保留字当作一般标识符识别,然后查找保留字表,就是这里的Lex表,如果有,把它当作保留字处理,如果没有按一般标识符处理。

 2.复合单词的识别   

     有一类单词是由两个或者两个以上符号组成的,有时前缀部分也可以是一个独立的单词

  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 }

 

posted @ 2015-04-17 15:06  PitBull  阅读(1317)  评论(0编辑  收藏  举报