最近我们在做一个有关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 }