Bison
是一種通用的語法分析器生成器
,Yacc 的 GNU 版。它將LALR(1)上下文無關文法
的描述轉化成分析該文法的C程序
。
它經常和 Flex
一起使用。
基礎:瞭解BNF(可參考: 語法規範:BNF與ABNF)
先來了解一下Bison:
bison程序包括與flex程序相同的三個部分結構:聲明部分、規則部分、C代碼部分,三個部分兩兩之間用
%%
隔開。
(1) 聲明部分:
%{到%}部分會被拷貝到目標分析程序的開頭。
%token記號我的理解是詞法分析器得到的結果,用於傳入語法分析器進行分析。通常,記號總是使用大寫。
(2) 規則部分:
1、:
用於定義規則
。;
表示一個規則的結束
。
2、在每個規則之後,使用{}
括起,表明如何處理。
3、規則按照語法樹從上到下
定義規則,第一條規則爲語法起始符號
,必須與輸入匹配。
4、目標符號
(冒號左邊的語法符號)的值
在動作中代碼用$$
代替,右邊語法符號的語義值依次爲$1
,$2
,直到這條規則的結束。
5、如果一個規則缺少現實的動作
,語法分析器將把$1
賦予$$
,這是i一個內部設定。
返回記號的時候,記號對應的值是存儲在yylval變量中。
(3) C代碼部分:
主程序,負責調用yyparse(),並輸出結果。
一個實現+-*/|
的簡單計算器:
-
編寫Bison語法分析器:
$ vim SimpleCal.y
寫入以下內容:
%{
#include <stdio.h>
%}
%token NUMBER
%token ADD SUB MUL DIV 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; }
;
%%
main(int argc,char **argv)
{
yyparse();
}
yyerror(char *s)
{
fprintf(stderr,"error: %s\n",s);
}
-
編寫對應Flex詞法分析器:
因爲Bison目標程序是語法分析器,所以還需要寫一個Flex程序來實現詞法分析:
$ vim SimpleCal.l
寫入以下內容:
%{
#include "SimpleCal.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);}
/*這裏輸出有點問題...*/
%%
SimpleCal.tab.h
爲Bison
生成對應程序的頭文件。
-
編譯與執行:
$ flex -o SimpleCal.yy.c SimpleCal.l
$ bison -o SimpleCal.tab.c SimpleCal.y
$ gcc -o SimpleCal SimpleCal.tab.* SimpleCal.yy.c -lfl
$ ./SimpleCal
1-29*3+50+|30
= -6
q
error: Mystery character: %c!
error: syntax error
沒有()
的支持,貌似|
取絕對值沒啥用。
寫一個shell腳本方便編譯運行:(我makefile不熟悉)
$ touch SimpleCal.sh
$ chmod u+x SimpleCal.sh
$ vim SimpleCal.sh
然後寫入以下內容:
#!/bin/bash
echo "flex執行開始!"
flex -o SimpleCal.yy.c SimpleCal.l
echo "bison執行開始!"
bison -o SimpleCal.tab.c SimpleCal.y
echo "gcc編譯開始!"
gcc -o SimpleCal SimpleCal.yy.c SimpleCal.tab.* -lfl
echo "刪除中間文件..."
rm SimpleCal.yy.c SimpleCal.tab.*
echo "執行生成最終目標文件..."
./SimpleCal