BISON
文章目錄
- 工作原理
- .y文件結(jié)構(gòu)
- 原理
- 編譯運(yùn)行
- 語義 (語義值類型,語義動作)
- 聲明部分(終結(jié)符,編碼,結(jié)合律,優(yōu)先級)
- 優(yōu)先級和結(jié)合律
- bison 無法處理的語法
- 語法與內(nèi)置函數(shù)
- 編譯選項
- 沖突與出錯處理
- 沖突
- 出錯處理
- 跟蹤分析過程
- flex & bison
- 原理
- 例子(后綴表達(dá)式的計算):
工作原理
.y文件結(jié)構(gòu)
- YYSTYPE 語義值類型
- %token NUM ?定義終結(jié)符NUM
- yyerror 必須用戶自己提供
- int yylex() ? 這個例子中使用自己編寫的yylex,實際會使用flex編譯的文件所提供的yylex
- 產(chǎn)生式右邊為空表示 ?\epsilon?
- 產(chǎn)生式后面的c語言代碼稱為語義動作
- $$ 表示產(chǎn)生式左部的語義值
- $1,$2,…分別表示右邊第一,第二個符號的語義值
- yylval 是全局變量,表示當(dāng)前所識別的詞形的語義值。
原理
- 對輸入.y文件中的形式文法構(gòu)架LALR分析表,并生成基于該分析表的語法分析器C語言源程序.tab.c。
- bison 通過 int yylex() 的返回值獲得單詞的編碼;通過全局變量YYSTYPE yylval獲得當(dāng)前單詞的語義值.
- 分析方法采用自頂向上的移進(jìn)/規(guī)約法,完成規(guī)約時,yyparse()將執(zhí)行bison源文件對應(yīng)產(chǎn)生式的語義動作。
- 必要的語法環(huán)境:yylex(),main(), yyerror(), 其中yylex可由用戶提供,也可由flex源程序提供,然后和bison源程序一起連接成可執(zhí)行文件
編譯運(yùn)行
- bision rpcalc.y
- gcc -o rpcalc rpcalc.tab.c
- rpcalc (啟動程序)
語義 (語義值類型,語義動作)
語義值類型:
- 由于在移進(jìn)、規(guī)約的時候是拷貝進(jìn)棧,所以當(dāng)語義值的類型是指針時,所指的地址一定不能是局部變量。安全的做法是:生命周期開始時通過動態(tài)申請內(nèi)存所指的值保存,生命周期結(jié)束時釋放該內(nèi)存(動態(tài)內(nèi)存分配)
- 通過%union 在聲明部分講YYSTYPE定義為C語言的union結(jié)構(gòu)。
- 對于非終結(jié)符,通過 %type \<union 分量名> 非終結(jié)符名 來規(guī)定語義動作中以何種方式訪問語義值對應(yīng)的類型。
- 對于非終結(jié)符,使用 %token \<union 分量名> 終結(jié)符名,或者%type規(guī)定。
- 在語義動作中,使用$<comp_name>m訪問
語義值聲明周期:
- 在語義棧中存在的時間
- 比如 NUM的生命周期從yylex()通過scanf("%d",&yylval)創(chuàng)建,然后壓入語義棧中,隨后規(guī)約exp:NUM {$$=$1}, NUM的語義值從棧中退出,exp的語義值壓入。
語義動作的翻譯:
源文件中的
exp: exp exp '\' {$$ = $1 \ $2;}會被翻譯為:
(yyval)=(yyvsp[(1)?(3)])/(yyvsp[(2)?(3)]);(yyval) = (yyvsp[(1) - (3)]) / (yyvsp[(2) - (3)]);(yyval)=(yyvsp[(1)?(3)])/(yyvsp[(2)?(3)]);
yyvsp 是一個棧棧頂是yyvasp[0], 越往棧底走,索引依此減1,這個例子的存儲數(shù)據(jù)為:
(棧頂)’\’ $2 $1,
語義動作:
- 尾部語義動作
產(chǎn)生式尾部的語義動作在發(fā)現(xiàn)該產(chǎn)生式句柄時已經(jīng)在棧頂形成,需要對該產(chǎn)生式進(jìn)行規(guī)約時進(jìn)行。語義動作包括計算綜合屬性(S屬性)和產(chǎn)生副作用
每個產(chǎn)生式有一個默認(rèn)動作 {$$=$1;}
- 產(chǎn)生式中間語義動作
用來解決**繼承屬性(L屬性)**的計算
在這里插入圖片描述
聲明部分(終結(jié)符,編碼,結(jié)合律,優(yōu)先級)
bison將終結(jié)符分為兩類:字符(Symbol)和單詞(Token). Token需要聲明,字符在產(chǎn)生式(語法規(guī)則)中直接用字符常量,注意加引號’ '。如 exp: exp exp + 是錯的
- token對終結(jié)符的編碼規(guī)則
- 0或負(fù)數(shù): 文件結(jié)束標(biāo)記
- 字符單詞:對應(yīng)的ASCII碼
- error: 256
- 非字符單詞從258開始順序編碼, 在.tab.c中可以找到具體定義
在使用單獨的詞法分析模塊的時候,需要bison需要使用 -d 選項,這樣講token的宏定義等輸出到.tab.h 文件中,這樣詞法分析模塊可以include此頭文件來使用宏定義的token。
- 規(guī)定結(jié)合律
%left ‘+’ ‘-’
優(yōu)先級和結(jié)合律
%left '+' '-' %left '*' '/' %left UMINUS %% exp : exp '+' exp | exp '-' exp | exp '*' exp | exp '/' exp | '-' exp %prec UMINUS;- 結(jié)合性:%left 左結(jié)合; %right 右結(jié)合;%nonassoc 不能結(jié)合;
- 出現(xiàn)在同一個%left后的單詞優(yōu)先級相同
- 優(yōu)先級:出現(xiàn)在不同 “%left” 的終結(jié)符,其運(yùn)算優(yōu)先級別按照 “%left” 在源文件中出現(xiàn) 的先后次序由低到高排列, 即:UMINUS的優(yōu)先級最高,乘法和除法次之,加法和 減法最低.
- 由于一元減和二元減的運(yùn)算符相同,在語法中出現(xiàn)一元減的產(chǎn)生式通過%prec UNIMUS設(shè)定一元減和UMINUS具有相同的優(yōu)先級.
bison 無法處理的語法
?bison語法分析器可以使用兩種分析方法,一種是LALR(1), 另一種是GLR(通用的自左向右)。GLR更強(qiáng)大但更麻煩。
? LALR(1)不能處理的語法有:
- 有歧義的語法
- 需要向前查看超過1個記號才能確定是否匹配的語法。
語法與內(nèi)置函數(shù)
| yylval | 當(dāng)前終結(jié)符語義值的全局變量 |
| YYSTYPE | 語義值數(shù)據(jù)類型 |
編譯選項
| -d | 輸出文件 .tab.h |
| -v | 輸出分析表文件 name.output |
| -t | 在輸出文件(.tab.c)中設(shè)置宏YYDEBUG 為1, 從而打開分析器的調(diào)試代碼,使得最后生成的分析器文件能夠?qū)Ψ治鲞^程進(jìn)行追蹤。 |
沖突與出錯處理
沖突
- 輸出沖突的報警信息:打開-t選項,在LALR(1)分析表文件.output文件中指出沖突所在
- 如果是S/R沖突,優(yōu)先S
- 如果是RR沖突,排列在前者優(yōu)先。
出錯處理
- 如果在 bison 輸入文件中沒有任何提供任何出錯處理,輸出分析器在工作時,如果發(fā)現(xiàn)語法錯誤 (即在當(dāng)前狀態(tài)下面對當(dāng)前的終結(jié)符不能進(jìn)行任何移進(jìn)或歸約 操作), yyparse()在傳參 “syntax error” 調(diào)用用戶提供的void yyerror(char const *)函數(shù)后,返回1退出yyparse().
- 通常yyerror可定義如下:
- bison 通過預(yù)留的終結(jié)符 error 提供從出錯狀態(tài)恢復(fù)分析的機(jī)制, 如:
stmnts:/?emptystring?/stmnts: /* empty\ string */ stmnts:/?empty?string?/ ∣stmnts′\n′| stmnts\ \ '\backslash n'∣stmnts??′\n′ ∣stmntsexp′\n′| stmnts\ \ exp \ \ '\backslash n'∣stmnts??exp??′\n′ ∣stmntserror′\n′| stmnts\ \ error\ \ '\backslash n'∣stmnts??error??′\n′
在產(chǎn)生式右邊有保留終結(jié)符 error 的規(guī)則稱為錯誤恢復(fù)規(guī)則,輸出分析器在一 個含有錯誤恢復(fù)規(guī)則的狀態(tài)出錯時,首先從分析棧中彈出語法符號直到能 將 error 移進(jìn),在棧頂形成錯誤恢復(fù)規(guī)則中 error 前的句型為止,如上例中在 分析exp 時出錯,將彈出所有的已分析的表達(dá)式成分,將 error 壓棧到項目, 此時棧中符號為:{stnmts error . ‘\n’}; 然后,跳過所有的輸入直到遇到錯誤恢 復(fù)規(guī)則規(guī)中error的后隨符號為止. 如上例中將跳過所有的輸入直到’\n’ 才恢 復(fù)分析. - 在錯誤恢復(fù)規(guī)則中可以像其他規(guī)則一樣有語義動作,但是不能引用終結(jié) 符error的語義值.
- 在某一錯誤恢復(fù)規(guī)則起作用時,為了防止分析器頻繁報錯,輸出分析器規(guī)定只 有在連續(xù)移進(jìn)三個單詞后才恢復(fù)報錯,在錯誤恢復(fù)規(guī)則語義動作中,可以使用 bison 提供的 C 語言宏 “yyerrok;” 解消上述限定,即刻恢復(fù)報錯機(jī)制.
- 在激活錯誤恢復(fù)規(guī)則后,當(dāng)前向前查看的單詞將會繼續(xù)作為向前查看符號進(jìn)行 分析,如果用戶希望跳過該單詞,可在語義動作中用 bison 提供的宏 “yyclearin;” 跳過當(dāng)前單詞. 但是由于該操作是在歸約出錯產(chǎn)生式時才起作用, 因此如果錯誤恢復(fù)規(guī)則中error 之后還有終結(jié)符,只有移進(jìn)該終結(jié)符才能有語 義動作,因此 “yyclearin;” 將無任何效果. “yyclearin;” 有效當(dāng)且僅當(dāng)它是 在緊隨error之后的動作中
跟蹤分析過程
調(diào)用yyparse()前,置yydebug非零,如下的main.c
main() { extern int yydebug;yydebug = 1;yyparse(); }flex & bison
原理
?ex 輸出的單詞掃描程序正是int yylex(), 為了讓 bison 輸出的語法分析源程序能與 ?ex 輸出的詞法分析源程序協(xié)同工作,必須保證單詞編碼的一致,以及 語義值數(shù)據(jù)類型的一致, 這要求在 ?ex 源程序中:
- 1 定義部分包含 bison 輸出的頭文件.tab.h
- 2 根據(jù)語法規(guī)則中的單詞設(shè)計匹配單詞的正規(guī)表達(dá)式,及需要過濾的白字符 (white space) 和注釋, 如: ‘\n’ 如果是語義規(guī)則所需的單詞,就不能冒然 過濾掉
- 3 在識別單詞的 C 語言動作中,如果該單詞有語義值,應(yīng)先計算其語義值并 賦值到全局變量yylval,如果是字符單詞,return yytext[0];,否 則return 單詞名;
- 4 為了保證詞法分析能處理所有的字符,對語法分析不支持的字符的的動作報錯并過濾
- 5 bison 使用的單詞名不能與 ?ex 中自帶的宏名有沖突. 如: BEGIN不能作為 單詞名, 否則return BEGIN;將調(diào)用 ?ex 預(yù)置的宏BEGIN, 而不能返回期望的單 詞編碼, 可修改單詞名為SBEGIN來避免沖突.
例子(后綴表達(dá)式的計算):
rpcacle.y:
%{ // 逆波蘭表達(dá)式的計算 // #define YYSTYPE int 默認(rèn)int #include<stdio.h>void yyerror(char*); int yylex(); %}%token NUM %token ADD SUB MUL DIV %token EOL%% input: /*empty equal to epsilon*/| input line; line: EOL| exp EOL {printf("\%d\n",$1);}; exp: NUM {$$ = $1;}| exp exp ADD {$$ = $1+$2;}| exp exp SUB {$$ = $1-$2;}| exp exp MUL {$$ = $1*$2;}| exp exp DIV {$$ = $1 / $2;}; %%int main(void){ int n=yyparse();printf("%d",n);return n; }void yyerror(char*s){printf("%s\n",s); }rpcalc.l
%{ #include "rpcalc_fb.tab.h"%}%% "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } "/" { return DIV; }[0-9]+ { yylval = atoi(yytext); return NUM; }\n { return EOL; } "//".* [ \t] { /* ignore white space */ } . { printf("Mystery character %c\n", *yytext); } %%總結(jié)
- 上一篇: 使用IDEA2020创建jsp文件
- 下一篇: 手动查杀木马