关于bison
yacc(Yet Another Compiler Compiler),是一個經典的生成語法分析器的工具。yacc生成的編譯器主要是用C語言寫成的語法解析器(Parser),需要與詞法解析器Lex一起使用,再把兩部份產生出來的C程序一并編譯。
Bison 基本上與?Yacc?兼容,并且在 Yacc 之上進行了改進。它經常和?Flex?(一個自動的詞法分析器生成器)一起使用。
此軟件的源代碼是可自由獲得的,在?GPL?下發布。
?
BNF
巴科斯范式(BNF: Backus-Naur Form 的縮寫)是由 John Backus 和 Peter Naur 首先引入的用來描述計算機語言語法的符號集。
在BNF中,雙引號中的字("word")代表著這些字符本身。而double_quote用來代表雙引號。
在雙引號外的字(有可能有下劃線)代表著語法部分。
< > : 內包含的為必選項。?
[ ] : 內包含的為可選項。?
{ } : 內包含的為可重復0至無數次的項。?
|? : 表示在其左右兩邊任選一項,相當于"OR"的意思。?
::= :?是“被定義為”的意思 或者單一的冒號
"..." : 術語符號?
[...] : 選項,最多出現一次?
{...} : 重復項,任意次數,包括 0 次?
(...) : 分組
|?? : 并列選項,只能選一個
?
下面是是用BNF來定義的Java語言中的For語句的實例:
| 1 2 3 4 | for?(initialization; termination; ?????increment) { ????statement(s) } |
BNF定義如下:
| 1 2 3 4 5 6 | FOR_STATEMENT ::= ????"for"?"("?( variable_declaration? | ??( expression?";"?)? |??";"??) ??????[ expression ]?";" ??????[ expression ]??";" ?????")"?( statement |?"{"?statement?"}"?) |
BNF處理1*2 + 3*4 +5簡單的算術表達式:
| 1 2 3 4 5 6 7 8 | <exp> ::= <factor> ????| <exp> + <factor> <factor> ::= NUMBER ????| <factor> * NUMBER ? exp被定義為是一個factor或者factor+exp ? factor被定義是NUMBER或者factor*NUMBER |
?
例子1:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | %{?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? #include <stdio.h> %} ? /*聲明token*/ %token NUMBER %token ADD SUB DIV MUL ABS %token EOL ? %% calclist:?/*空規則*/ ????| calclist exp EOL { printf("= %d\n",$2); } ????; ? exp: factor ???| exp ADD factor { $$ = $1 + $3; } ???| exp SUB factor { $$ = $1 - $3; } ???; ? factor: term ???| factor MUL term { $$ = $1 * $3; } ???| factor DIV term { $$ = $1 / $3; } ???; ? term:NUMBER ????| ABS term { $$ = $2>0 ?? $2 : -$2; } ????|?"+"?term { $$ = $2; } ????; ? %% int?main(int?argc,?char?** argv) { ????printf(">"); ????yyparse(); ????return?0; } ? yyerror(char?*s) { ????fprintf(stderr,"error:%s\n",s); } |
bison程序包括與flex程序相同的三個部分結構:聲明部分、規則部分、C代碼部分。
1、聲明部分:
聲明部分包含了會被原樣拷貝到目標分析程序開頭的C代碼,同樣也通過%{和%}來聲明。
%token記號聲明,以便于告訴bison在語法分析程序中的記號的名稱。通常,記號總是使用大寫。
任何沒有聲明為記號的語法符號必須出現在至少一條規則的左邊(左邊表示規則的定義)
2、規則部分:
簡單的BNF定義的規則。bison使用單一的冒號而不是::=,分號被用來表示規則的結束。
在flex中每個規則之后,使用花括號括起。
?
bison會自動分析語法,記住每條被匹配的規則,所以動作代碼只需要維護每個語法符號關聯的語義值。
bison語法分析器也執行一些額外的動作,例如創建數據結構以便后續使用。
?
第一條規則左邊的語法符號是語法起始符號(start symbol),整個輸入必須被它匹配。
每個bison規則中的語法符號都有一個語義值,目標符號(冒號左邊的語法符號)的值在動作中代碼用$$代替,
右邊語法符號的語義值依次為$1,$2,直到這條規則的結束。當詞法分析器返回記號時,記號值總是存儲在yyval里,
其他語法符號的語義規則在語法分析器的規則里進行設置,例如本例子的 factor、term和exp符號的語義值就是它們所
描述的表達式值。
?
例子中,頭兩條規則定義了calclist語法符號,通過循環來讀入用換行符結束的表達式并且打印結果。
| 1 2 3 4 | calclist: /*空規則*/ ????| calclist exp EOL { printf("= %d\n",$2); } ????| calclist EOL { printf("> "); } /* blank line or a comment */?????????????????????????????????????????????????????????? ????; |
calclist的定義使用一種常見的雙規則遞歸定義來實現一個序列或者列表:
第一個規則為空,不進行任何匹配
第二個規則添加一個項目到列表中,對應的動作是通過$2打印出exp的值
第三個規則實現輸入空行
?
其余的規則實現計算器,帶有操作符的規則(exp ADD factor? 和ABS term)在語義值上進行相應的算術操作。
右邊僅有一個語法符號的規則是組合文法,例如exp:factor,一種表達式exp就是一個因子factor。
如果一個規則缺少現實的動作,語法分析器將把$1賦予$$,這是i一個內部設定。
?
詞法分析器程序
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | %option noyywrap %{ #include "fb1-5.tab.h" %} ? %% "+"?????{?return?ADD; } "-"?????{?return?SUB; } "*"?????{?return?MUL; } "/"?????{?return?DIV; } ? "|"?????{return?ABS; } ^[-+][0-9]+ { yylval = atoi(yytext);?return?NUMBER;} [0-9]+? { yylval = atoi(yytext);?return?NUMBER;} ? \n????? {return?EOL; }?????????????????????????????????????????????????????????????????????????????????????????????????????? [ \t]?? {}? .?????? { yyerror("Mystery character=%c!",*yytext);} %% |
?
1、由于在語法分析中聲明了token,故這里使用的話,需要進行引用,在聲明部分添加include文件
2、返回記號的時候,記號對應的值是存儲在yylval變量中
?
聯合編譯flex和bison程序
對應的makefile文件內容為:
| 1 2 3 4 | fb1-5: fb1-5.l fb1-5.y ????????bison -d fb1-5.y ????????flex fb1-5.l ????????gcc -o $@ fb1-5.tab.c lex.yy.c -lfl |
bison 使用-d選項(用于定義文件)運行,創建fb1-5.tab.c和fb1-5.tab.h文件
flex創建lex.yy.c,然后將兩者和flex的庫文件編譯在一起
?
測試結果
| 1 2 3 4 5 6 | [root@typhoeus79 bison]# ./fb1-5 > > -5+10 = 5 -5*4+20 = 0 |
二義性文法:并不多見
語法分析為什么不寫成這樣?
| 1 2 3 4 5 6 7 | exp:exp ADD exp ??????| exp SUB exp ??????| exp MUL exp ??????| exp DIV exp ??????| ABS exp ??????| NUMBER ??????; |
原因在于優先級和二義性。
分開的term、factor和exp的語法符號可以讓bison首先處理ABS,接著是MUL和DIV,然后是ADD和SUB。
通常來說,一旦一種文法有不同的優先級,語法分析器就需要為每種優先級制定一條規則。
?
下面的文法如何?
| 1 2 3 4 5 | exp: exp ADD exp ??????|?? exp SUB exp ??????| factor; ? factor和term部分相似 |
存在二義性。例如1-2+3的輸入可能被分析為(1-2)+3,也可能被分析為1-(2+3)
如果一種文法是有歧義的,bison會報告沖突(conflicts),并且標出針對給定輸入哪兒會有兩種不同的分析。
?
增加其他規則
支持小括號
詞法解析中添加如下:
| 1 2 | "("???? { return OP; } ")"???? { return CP; } |
語法解析中添加:
| 1 2 3 4 5 | term:NUMBER ????| ABS term { $$ = $2>0 ?? $2 : -$2; } ????| "+" term { $$ = $2; } ????| "-" term { $$ = -$2; } ????| OP exp CP { $$ = $2;} |
?如果想支持如下計算,應該怎么搞呢?
| 1 2 3 4 5 6 7 8 | >(10-2)+(-10+2) = 0 >(100-2)*2 = 196 >(+10-2) = 8 >10-(-10+10)? = 10 |
需要詞法分析,重點需要區分正常的加減號以及前綴加減號
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | %option noyywrap %{ #include "fb1-5.tab.h" int?flag=1; int?flag2 = 1; ? %} ? %% "+"?????{ ????????????if(flag){ ????????????????flag = 1; ????????????????//printf("ADD\n"); ????????????????return?ADD; ????????????}else ????????????{ ????????????????flag2 = 1; ????????????} ????????} "-"?????{ ????????????if(flag){ ????????????????//printf("SUB\n"); ????????????????flag = 1; ????????????????return?SUB; ????????????}else ????????????{ ????????????????//printf("Flag=%d,Here\n",flag); ????????????????flag2 = -1; ????????????} ????????} "*"?????{?return?MUL; } "/"?????{?return?DIV; } "("?????{?? flag = 0; ????????????//printf("OP\n"); ????????????return?OP; } ")"?????{ ????????????flag =1; ????????????flag2 = 1; ????????????//printf("CP\n"); ????????????return?CP; } ? ? "|"?????{return?ABS; } ^[-+][0-9]+ { yylval = atoi(yytext);?return?NUMBER;} [0-9]+? { ????????????if(flag) ????????????????yylval = atoi(yytext); ????????????else{ ????????????????yylval = flag2*atoi(yytext); ????????????????flag = 1; ????????????} ????????????//printf("NUMBER2=%d\n",yylval); ????????????return?NUMBER;} ? \n????? {return?EOL; }?????????????????????????????????????????????????????????????????????????????????????????????????????? [ \t]?? {}? .?????? { yyerror("Mystery character=%c!",*yytext);} %% |
總結
- 上一篇: SICK激光——DT35使用教程
- 下一篇: 华为 C8800 C8650 tun.k