关于bison
yacc(Yet Another Compiler Compiler),是一個(gè)經(jīng)典的生成語法分析器的工具。yacc生成的編譯器主要是用C語言寫成的語法解析器(Parser),需要與詞法解析器Lex一起使用,再把兩部份產(chǎn)生出來的C程序一并編譯。
Bison 基本上與?Yacc?兼容,并且在 Yacc 之上進(jìn)行了改進(jìn)。它經(jīng)常和?Flex?(一個(gè)自動(dòng)的詞法分析器生成器)一起使用。
此軟件的源代碼是可自由獲得的,在?GPL?下發(fā)布。
?
BNF
巴科斯范式(BNF: Backus-Naur Form 的縮寫)是由 John Backus 和 Peter Naur 首先引入的用來描述計(jì)算機(jī)語言語法的符號(hào)集。
在BNF中,雙引號(hào)中的字("word")代表著這些字符本身。而double_quote用來代表雙引號(hào)。
在雙引號(hào)外的字(有可能有下劃線)代表著語法部分。
< > : 內(nèi)包含的為必選項(xiàng)。?
[ ] : 內(nèi)包含的為可選項(xiàng)。?
{ } : 內(nèi)包含的為可重復(fù)0至無數(shù)次的項(xiàng)。?
|? : 表示在其左右兩邊任選一項(xiàng),相當(dāng)于"OR"的意思。?
::= :?是“被定義為”的意思 或者單一的冒號(hào)
"..." : 術(shù)語符號(hào)?
[...] : 選項(xiàng),最多出現(xiàn)一次?
{...} : 重復(fù)項(xiàng),任意次數(shù),包括 0 次?
(...) : 分組
|?? : 并列選項(xiàng),只能選一個(gè)
?
下面是是用BNF來定義的Java語言中的For語句的實(shí)例:
| 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簡單的算術(shù)表達(dá)式:
| 1 2 3 4 5 6 7 8 | <exp> ::= <factor> ????| <exp> + <factor> <factor> ::= NUMBER ????| <factor> * NUMBER ? exp被定義為是一個(gè)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:?/*空規(guī)則*/ ????| 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程序相同的三個(gè)部分結(jié)構(gòu):聲明部分、規(guī)則部分、C代碼部分。
1、聲明部分:
聲明部分包含了會(huì)被原樣拷貝到目標(biāo)分析程序開頭的C代碼,同樣也通過%{和%}來聲明。
%token記號(hào)聲明,以便于告訴bison在語法分析程序中的記號(hào)的名稱。通常,記號(hào)總是使用大寫。
任何沒有聲明為記號(hào)的語法符號(hào)必須出現(xiàn)在至少一條規(guī)則的左邊(左邊表示規(guī)則的定義)
2、規(guī)則部分:
簡單的BNF定義的規(guī)則。bison使用單一的冒號(hào)而不是::=,分號(hào)被用來表示規(guī)則的結(jié)束。
在flex中每個(gè)規(guī)則之后,使用花括號(hào)括起。
?
bison會(huì)自動(dòng)分析語法,記住每條被匹配的規(guī)則,所以動(dòng)作代碼只需要維護(hù)每個(gè)語法符號(hào)關(guān)聯(lián)的語義值。
bison語法分析器也執(zhí)行一些額外的動(dòng)作,例如創(chuàng)建數(shù)據(jù)結(jié)構(gòu)以便后續(xù)使用。
?
第一條規(guī)則左邊的語法符號(hào)是語法起始符號(hào)(start symbol),整個(gè)輸入必須被它匹配。
每個(gè)bison規(guī)則中的語法符號(hào)都有一個(gè)語義值,目標(biāo)符號(hào)(冒號(hào)左邊的語法符號(hào))的值在動(dòng)作中代碼用$$代替,
右邊語法符號(hào)的語義值依次為$1,$2,直到這條規(guī)則的結(jié)束。當(dāng)詞法分析器返回記號(hào)時(shí),記號(hào)值總是存儲(chǔ)在yyval里,
其他語法符號(hào)的語義規(guī)則在語法分析器的規(guī)則里進(jìn)行設(shè)置,例如本例子的 factor、term和exp符號(hào)的語義值就是它們所
描述的表達(dá)式值。
?
例子中,頭兩條規(guī)則定義了calclist語法符號(hào),通過循環(huán)來讀入用換行符結(jié)束的表達(dá)式并且打印結(jié)果。
| 1 2 3 4 | calclist: /*空規(guī)則*/ ????| calclist exp EOL { printf("= %d\n",$2); } ????| calclist EOL { printf("> "); } /* blank line or a comment */?????????????????????????????????????????????????????????? ????; |
calclist的定義使用一種常見的雙規(guī)則遞歸定義來實(shí)現(xiàn)一個(gè)序列或者列表:
第一個(gè)規(guī)則為空,不進(jìn)行任何匹配
第二個(gè)規(guī)則添加一個(gè)項(xiàng)目到列表中,對(duì)應(yīng)的動(dòng)作是通過$2打印出exp的值
第三個(gè)規(guī)則實(shí)現(xiàn)輸入空行
?
其余的規(guī)則實(shí)現(xiàn)計(jì)算器,帶有操作符的規(guī)則(exp ADD factor? 和ABS term)在語義值上進(jìn)行相應(yīng)的算術(shù)操作。
右邊僅有一個(gè)語法符號(hào)的規(guī)則是組合文法,例如exp:factor,一種表達(dá)式exp就是一個(gè)因子factor。
如果一個(gè)規(guī)則缺少現(xiàn)實(shí)的動(dòng)作,語法分析器將把$1賦予$$,這是i一個(gè)內(nèi)部設(shè)定。
?
詞法分析器程序
| 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,故這里使用的話,需要進(jìn)行引用,在聲明部分添加include文件
2、返回記號(hào)的時(shí)候,記號(hào)對(duì)應(yīng)的值是存儲(chǔ)在yylval變量中
?
聯(lián)合編譯flex和bison程序
對(duì)應(yīng)的makefile文件內(nèi)容為:
| 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選項(xiàng)(用于定義文件)運(yùn)行,創(chuàng)建fb1-5.tab.c和fb1-5.tab.h文件
flex創(chuàng)建lex.yy.c,然后將兩者和flex的庫文件編譯在一起
?
測(cè)試結(jié)果
| 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 ??????; |
原因在于優(yōu)先級(jí)和二義性。
分開的term、factor和exp的語法符號(hào)可以讓bison首先處理ABS,接著是MUL和DIV,然后是ADD和SUB。
通常來說,一旦一種文法有不同的優(yōu)先級(jí),語法分析器就需要為每種優(yōu)先級(jí)制定一條規(guī)則。
?
下面的文法如何?
| 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會(huì)報(bào)告沖突(conflicts),并且標(biāo)出針對(duì)給定輸入哪兒會(huì)有兩種不同的分析。
?
增加其他規(guī)則
支持小括號(hào)
詞法解析中添加如下:
| 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;} |
?如果想支持如下計(jì)算,應(yīng)該怎么搞呢?
| 1 2 3 4 5 6 7 8 | >(10-2)+(-10+2) = 0 >(100-2)*2 = 196 >(+10-2) = 8 >10-(-10+10)? = 10 |
需要詞法分析,重點(diǎn)需要區(qū)分正常的加減號(hào)以及前綴加減號(hào)
| 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);} %% |
總結(jié)
- 上一篇: SICK激光——DT35使用教程
- 下一篇: 华为 C8800 C8650 tun.k