簡述
Bison是在Yacc上改寫並添加了大量特性後誕生的語法分析生成器,在編譯前端(詞法分析->語法分析->語義分析)中處在中間的位置,它可以用來生成特定的語法分析程序。
安裝Bison:
apt-get install bison
沒有專用於Bison的IDE,可以在VSCode安裝Lex Flex Yacc Bison插件,可以讓Bison語法高亮。
Bsion和Flex協同工作
在之前按照課本,用Flex生成了一個簡易計算器的詞法分析程序,這節書寫Bison生成它的語法分析程序,然後讓它們協同工作,來完成這個求解器。
Bison部分
Bison寫在.y
文件中,也和Flex一樣由兩個%%
分割成了聲明部分、規則部分、例程部分。下面是書寫的Bison文件SimpleCalculator.y
(課本程序是錯的,這裏改正了):
/*1 聲明部分*/
%{
#include <stdio.h>
%}
//聲明記號(Token)
%token NUMBER
%token ADD SUB MUL DIV ABS
%token EOL
%%
/*2 規則部分,在這裏寫BNF規則,左部是語法符號(Symbol)*/
//計算列表的語法
calclist: //空,即計算列表::=\epsilon
| calclist exp EOL { printf("= %d\n", $2); } //或者,計算列表::=計算列表.表達式.文件尾
; //分號表示規則結束
//表達式的語法
exp: term { $$ = $1; } //表達式:=項
| exp ADD term { $$ = $1 + $3; } //或者,表達式::=表達式+項
| exp SUB term { $$ = $1 - $3; } //或者,表達式::=表達式-項
;
//項的語法
term: factor { $$ = $1; } //項::=因子
| term MUL factor { $$ = $1 * $3; } //或者,項::=項*因子
| term DIV factor { $$ = $1 / $3; } //或者,項::=項/因子
;
//因子的語法
factor: NUMBER { $$ = $1; } //因子::=數字
| ABS factor { $$ = $2>=0 ? $2 : -$2; } //或者,因子::=絕對值.因子
;
%%
/*3 C代碼部分*/
//主函數中調用解析器yyparse()
int main(int argc, char **argv) {
yyparse();
return 0;
}
//定義解析出錯時的處理
void yyerror(char *s) {
fprintf(stderr, "error: %s\n", s);
}
這裏在聲明部分用%token
聲明瞭若干記號,這些記號實際上後面會給Flex使用,而Bison會自動爲這些記號編號。
接下來在規則部分,用BNF來書寫了上下文無關文法,這裏用{c代碼}
將求解的計算寫在了每條子規則的後面。這裏$$
表示規則左部的語法符號(Symbol),$1
表示規則右部的第一個語法符號,以此類推。
最後在例程部分,Bison生成的語法分析程序要調用yyparse()
進行語法分析。
Flex部分
Flex部分和之前的基本一樣。下面是修改的Flex文件SimpleCalculator.l
:
%{
//包含Bison翻譯出來的頭文件,文件中就定義了各個記號(Token)的編號,以及yylval記號值
//SimpleCalculator.tab.h是由SimpleCalculator.y這個Bison文件經過`bison -d`命令翻譯得來的
#include "SimpleCalculator.tab.h"
%}
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"|" { return ABS; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; } //匹配到數字時,將其轉爲int寫入記號值的變量中
\n { return EOL; }
[ \t] { } //忽略空白符
. { printf("Mystery charactor %c\n", *yytext); } //其它字符是不合法的,提示錯誤
%%
//這部分例程不寫了,因爲Bison生成的語法分析器會調用這裏Flex生成的詞法分析器
去掉聲明部分,是因爲聲明部分直接使用Bison編譯出的.h
文件中對記號(Token)的聲明,直接就統一了。
去掉例程部分,是因爲詞法分析的yylex()
要給Bison生成的語法分析器去調用,編譯的時候編譯到一起就可以了。
Makefile
創建Makefile
文件:
SimpleCalculator: SimpleCalculator.l SimpleCalculator.y
bison -d SimpleCalculator.y
flex SimpleCalculator.l
gcc -o $@ SimpleCalculator.tab.c lex.yy.c -lfl
clean:
rm -f SimpleCalculator lex.yy.c SimpleCalculator.tab.c SimpleCalculator.tab.h
第一句是用Bison翻譯SimpleCalculator.y
,生成語法分析器SimpleCalculator.tab.c
和其頭文件SimpleCalculator.tab.h
。
第二句是用Flex翻譯SimpleCalculator.l
生成詞法分析器lex.yy.c
。
第三句是用GCC將語法分析器、詞法分析器、Flex庫文件這三者編譯到一起,-o
指定輸出的文件名,這裏$@
代表所有的目標文件,也就是SimpleCalculator。
編譯:
make SimpleCalculator
運行
lzh@DESKTOP-HCSIG2E:/mnt/e/Compiler/flex_bison$ ./SimpleCalculator
12+34*|0-3
= 9
123-456
= -333