最近咱們在作一個有關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 }