基於Flex構造詞法分析器
【問題描述】設計c語言常見單詞的正規式,編制lex源文件,利用flex編譯得到詞法分析的.c文件,繼而對該文件編譯得到詞法分析器。 【輸入形式】輸入一段c語言程序 【輸出形式】各類單詞的token字,或者給出程序中的單詞錯誤。 【樣例輸入】 int main(){ int a = 10; double b = 20.9; if(a <= b) a+=b; else a = 0; return a; }
【樣例輸出】 line1:(type, int) line1:(keyword, main) line1:(bracket, () line1:(bracket, )) line1:(bracket, {) line2:(type, int) line2:(identify, a) line2:(OPT, =) line2:(integer, 10) line2:(bracket, ;) line3:(type, double) line3:(identify, b) line3:(OPT, =) line3:(decimal, 20.9) line3:(bracket, ;) line4:(keyword, if) line4:(bracket, () line4:(identify, a) line4:(OPT, <=) line4:(identify, b) line4:(bracket, )) line5:(identify, a) line5:(OPT, +=) line5:(identify, b) line5:(bracket, ;) line6:(keyword, else) line6:(identify, a) line6:(OPT, =) line6:(integer, 0) line6:(bracket, ;) line7:(keyword, return) line7:(identify, a) line7:(bracket, ;) line8:(bracket, })
【樣例說明】需要識別的關鍵字包括void, int, main, double, return, float, if, else, do, while, for, scanf, printf, char, sqrt, abs, 運算符(算術、關系、邏輯、位);需要識別的其他單詞有標識符, 整數(十進制形式、指數形式),實數(十進制形式、指數形式),字符串;過濾注釋及空格。 【評分標准】根據設計文檔的質量、lex文件的正確性,代碼的正確性、代碼的時間空間復雜度、識別單詞的種類等綜合評分 |
flex就簡單學了一下,最后看了別人的代碼,模仿着寫了一個。
.I文件
/*e.l*/ %option main %option yywrap %{ #include <stdio.h> #include <string.h> int Row=1; char *Ans = ""; %} KeyWord("main"|"double"|"return"|"float"|"if"|"else"|"do"|"while"|"for"|"scanf"|"printf"|"char"|"sqrt"|"abs"|"float") OPT ("+"|"-"|"%"|"*"|"/"|"+="|"-="|"*="|"/="|">"|"<"|"<="|">="|"=="|"="|"&"|"\|"|"!"|"<<"|">>") Identify ({Letter}|_)({Letter}|_|{Digit})* Digit [0-9] UnsignedInteger [1-9]{Digit}* Integer ("+"|"-")?{Digit}+("e"(("+"|"-"){UnsignedInteger})?)? Decimal {Integer}.{Digit}+ Float {Integer}.{Digit}+("e"(("+"|"-"){UnsignedInteger})?)? Letter [a-zA-Z] Comment ("/*"|"*").* Type ("int"|"double"|"short"|"char"|"void"|"long") Bracket ("("|")"|"{"|"}"|"["|"]"|";"|","|"\"") Typeidentity ("%"|"&")[a-z] Next("\n") %% {Type} { AddOutput(Row, "type", yytext); } {KeyWord} { AddOutput(Row, "keyword", yytext); } {OPT} { AddOutput(Row, "OPT", yytext); } {Identify} { AddOutput(Row, "identify", yytext); } {Integer} { AddOutput(Row, "integer", yytext); } {Decimal} { AddOutput(Row, "decimal", yytext); } {Float} { char *p = strchr(yytext, 'e'); if (p && yytext[strlen(yytext)-1] == 'e'){ printf("Error at Line %d: Illegal floating point number \"%s\".\n",Row,yytext); return; } else { AddOutput(Row, "float", yytext); } } {Next} {++Row;} {Typeidentity} { AddOutput(Row, "typeidentify", yytext); } {Bracket} { AddOutput(Row, "bracket", yytext); } {Comment} {} . {} %% int yywrap() { printf("%s", Ans); return 1; } void AddOutput(int Row, char* type, char* text){ char str[50]; sprintf(str, "line%d:(%s, %s)\n", Row, type, text); char *tmp = Ans; Ans = (char *) malloc(strlen(tmp) + strlen(str) + 1); sprintf(Ans, "%s%s", tmp, str); if(strlen(tmp) > 0) free(tmp); return ; }
把這個文件拖到 win_flex.exe 生成 lex.yy.c
第一題(得分1.00)
一開始沒有接觸過Flex工具,通過老師推薦的教程《flex與bison》對它有了大致的了解,做了幾個書上的案例,結合CSDN上完成詞法分析器的教程,完成了第一題。
Lex源文件很大一部分都是抄書上和教程上的,但因為學Lex的時間太短了,對於這個Lex的一些語句還不是特別理解,只是勉強能解決簡單的詞法分析任務,並不清楚Lex內函數的實現細節。
使用Lex來完成詞法分析任務能大大減少我們的工作量。源文件僅僅使用了八十多行代碼就完成了任務,相對於自己手寫的兩百行代碼,極大的降低了任務的復雜度。更重要的是通過lex生成的代碼質量高,可靠性好,比手寫詞法分析器產生的Bug應該會少一些。
但這並不意味着手寫代碼就不如Lex生成的代碼。因為Lex生成的lex.yy.c文件有一千九百行,很不方便程序員對代碼進一步修改,並且並不是每個人都很清楚函數的實現細節,如果真的出現Bug,對於普通人來說很難解決。
雖然通過了四組測試案例,但是還是有一些問題沒有解決:
1.關鍵字和數據類型的問題沒有解決。
在樣例中可以看到,Int屬於type類型,而float屬於keyword類型。對於這種既是數據類型,又是關鍵字的詞,題干中沒有明確說明如何划分。
2. 我設置的小數類型是這樣的
Decimal {Integer}.{Digit}+,
但在我自己測試的時候出現了這樣的問題,他把20-1識別成了一個小數。
圖一 存在的BUG1
3. 我設置的整數類型是這樣的
Integer ("+"|"-")?{Digit}+("e"(("+"|"-"){UnsignedInteger})?)?
可是測試的時候把2e3識別成了小數
圖一 存在的BUG2