自制编译器学习3:Flex和Bison简介
Flex和Bison安裝
Ubuntu環境下直接安裝即可:
apt-get install flex bison第一個Flex程序
首先給出代碼(fb1-1.l):
/* Companion source code for "flex & bison", published by O'Reilly* Media, ISBN 978-0-596-15597-1* Copyright (c) 2009, Taughannock Networks. All rights reserved.* See the README file for license conditions and contact info.* $Header: /home/johnl/flnb/code/RCS/fb1-1.l,v 2.1 2009/11/08 02:53:18 johnl Exp $*//* fb1-1 just like unix wc */ %{ int chars = 0; int words = 0; int lines = 0; %}%%[a-zA-Z]+ { words++; chars += strlen(yytext); } \n { chars++; lines++; } . { chars++; }%%main() {yylex();printf("%8d%8d%8d\n", lines, words, chars); }flex程序分為3部分,每一部分通過符號%%來分割,第一部分包含聲明和選項設置,第二部分是一系列的模式和動作,第三部分是會被拷貝到詞法生成器里面的C代碼。
第一部分,%{%}之間的代碼會被照抄到生成C文件的頭部,可以看到在這個實例中只是定義了3個整型變量。
第二部分,每一個模式匹配位于一行的開始,接著是匹配時要執行的的C代碼,C代碼為{}之間的一行或者多行代碼。[a-zA-Z]+用來匹配一個單詞,大括號內表示可以匹配任意大小寫字母,+表示匹配一個或多個前面的字符,即一串字母或者一個單詞,flex中,yytext總是被指定為本次匹配的輸入文本,.表示匹配任意一個字符。
第三部分,主程序,調用flex的詞法分析例程,并輸出結果。
執行flex
flex -o fb1-1.c fb1-1.l將詞法生成器翻譯成c程序,c文件為:fb1-1.c
使用gcc編譯生成可執行文件:
gcc fb1-1.c -lfl -o fb1-1注意編譯時需要鏈接庫(-lfl),執行該程序,輸入3個字符串后按ctrl+d退出,輸出結果如下:
?flex和bison協同工作
來看第二段代碼:
/* Companion source code for "flex & bison", published by O'Reilly* Media, ISBN 978-0-596-15597-1* Copyright (c) 2009, Taughannock Networks. All rights reserved.* See the README file for license conditions and contact info.* $Header: /home/johnl/flnb/code/RCS/fb1-3.l,v 2.1 2009/11/08 02:53:18 johnl Exp $*//* recognize tokens for the calculator and print them out */%% "+" { printf("PLUS\n"); } "-" { printf("MINUS\n"); } "*" { printf("TIMES\n"); } "/" { printf("DIVIDE\n"); } "|" { printf("ABS\n"); } [0-9]+ { printf("NUMBER %s\n", yytext); } \n { printf("NEWLINE\n"); } [ \t] { } . { printf("Mystery character %s\n", yytext); } %%這里只給出了模式匹配,flex的lfl庫提供了極小的主程序來調用詞法分析器,這里已經足夠,詞法分析器的生成和編譯與之前相同,生成可執行詞法分析程序,執行后輸入12+34和13/34做詞法分析,執行結果如下:
考慮一個更完成的計算器詞分析器:
/* Companion source code for "flex & bison", published by O'Reilly* Media, ISBN 978-0-596-15597-1* Copyright (c) 2009, Taughannock Networks. All rights reserved.* See the README file for license conditions and contact info.* $Header: /home/johnl/flnb/code/RCS/fb1-4.l,v 2.1 2009/11/08 02:53:18 johnl Exp $*//* recognize tokens for the calculator and print them out */%{enum yytokentype {NUMBER = 258,ADD = 259,SUB = 260,MUL = 261,DIV = 262,ABS = 263,EOL = 264 /* end of line */};int yylval;%}%% "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } "/" { return DIV; } "|" { return ABS; } [0-9]+ { yylval = atoi(yytext); return NUMBER; } \n { return EOL; } [ \t] { /* ignore white space */ } . { printf("Mystery character %c\n", *yytext); } %% main() {int tok;while(tok = yylex()) {printf("%d", tok);if(tok == NUMBER) printf(" = %d\n", yylval);else printf("\n");} }輸出結果如下:
?我們來分析下代碼,flex做詞分析返回記號流時,每個記號由記號編號(token number)和記號值(token's value)組成,其中記號編號為一個小的整數,bison自動從258開始編號,并且創建一個編號定義的.h文件,這里為了方便理解通過enum進行了手工定義。主程序調用yylex()返回記號值,對于NUMBER轉換為int。
語法分析器
下面基于flex和bison來設計一個基本的語法分析器,bison遵循BNF(BackusNaur Form)語法,
在BNF里,::=(:)是被定義為的意思,|表示其左右兩邊任選一項, 左邊的名稱是語法符號(symbol),有效的BNF是具有遞歸性的,例如處理 1×5+2×3這種簡單的算術表達式:
<exp> ::= <factor>| <exp> + <factor> <factor> ::= NUMBER| <factor> * NUMBERexp被定義為一個factor 或者 factor + exp,而facotr被定義為NUMBER或者factor×NUMBER,每個規則最左邊是非終結符,冒號右邊是非終結符的推導規則,如果推導規則是多個那么通過|隔開。
我們給出bison代碼的示例(fb1-5.y):
/* Companion source code for "flex & bison", published by O'Reilly* Media, ISBN 978-0-596-15597-1* Copyright (c) 2009, Taughannock Networks. All rights reserved.* See the README file for license conditions and contact info.* $Header: /home/johnl/flnb/code/RCS/fb1-5.y,v 2.1 2009/11/08 02:53:18 johnl Exp $*//* simplest version of calculator */%{ # include <stdio.h> %}/* declare tokens */ %token NUMBER %token ADD SUB MUL DIV ABS %token OP CP %token EOL%%calclist: /* nothing 空規則 從輸入開頭開始匹配*/ | calclist exp EOL { printf("= %d\n> ", $2); } /*EOL代表一個表達式的結束*/| calclist EOL { printf("> "); } /* blank line or a comment */;exp: factor| exp ADD exp { $$ = $1 + $3; }| exp SUB factor { $$ = $1 - $3; }| exp ABS factor { $$ = $1 | $3; };factor: term| factor MUL term { $$ = $1 * $3; }| factor DIV term { $$ = $1 / $3; };term: NUMBER| ABS term { $$ = $2 >= 0? $2 : - $2; }| OP exp CP { $$ = $2; }; %% main() {printf("> "); yyparse(); }yyerror(char *s) {fprintf(stderr, "error: %s\n", s); }bison程序同樣包含三部分:聲明部分,規則匹配部分和C代碼部分。
聲明部分同樣是用%{%}包括且要原樣拷貝到C代碼,隨后%token記號聲明,便于告訴bison在語法分析程序中記號的名稱,記號通常總是大寫,未聲明為記號的語法符號必須出現在至少一條規則的左邊。
規則部分通過簡單的BNF定義規則,bison使用單一的冒號而不是::=,同時由于行間隔并不明顯,用分號來表示規則的結束,C的動作代碼在每條規則之后用花括號括起來。
我們來詳細看下匹配規則,第一個語法符號calclist,第一個匹配是空值,第二個是表達式(exp)+結束符(EOL),第三個是只有結束符的情況。
接下來的語法符號是term->factor->exp,匹配規則:
term: NUMBER 或者 絕對值的 term或者 加括號的 exp;
factor: term 或者 factor和term的乘法(左遞歸) 或者factor和term(左遞歸)的除法 或者 絕對值;
exp: factor 或者 exp和exp的加法 或者 exp和factor的乘法(左遞歸) 或者 exp和factor(左遞歸)的除法。
顯然是由小到大組成的(并不是絕對的,term也會匹配exp),并且優先級越高的越靠前,由于BNF語法具有遞歸性,這樣的順序可以保證運算優先級的正確性。
目標符號(冒號左邊的語法符號)的值在動作代碼中用$$代替,右邊語法符號的語義值依次為$1 $2 $3 ......。當詞法分析器返回記號時,記號值總是存儲在yyval中。
聯合編譯flex和bison程序
flex代碼(fb1-5.l)如下:
/* Companion source code for "flex & bison", published by O'Reilly* Media, ISBN 978-0-596-15597-1* Copyright (c) 2009, Taughannock Networks. All rights reserved.* See the README file for license conditions and contact info.* $Header: /home/johnl/flnb/code/RCS/fb1-5.l,v 2.1 2009/11/08 02:53:18 johnl Exp $*//* recognize tokens for the calculator and print them out */%{ # include "fb1-5.tab.h" %}%% "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } "/" { return DIV; } "|" { return ABS; } "(" { return OP; } ")" { return CP; } [0-9]+ { yylval = atoi(yytext); return NUMBER; }\n { return EOL; } "//".* [ \t] { /* ignore white space */ } . { yyerror("Mystery character %c\n", *yytext); } %%我們專門寫一個Makefile文件:
fb1-5: fb1-5.l fb1-5.ybison -d fb1-5.yflex -o fb1-5.c fb1-5.lgcc -o $@ fb1-5.tab.c fb1-5.c -lflclean:rm -f fb1-5 \fb1-5.c fb1-5.tab.c fb1-5.tab.h執行
bison -d fb1-5.y會生成文件:fb1-5.tab.h 和 fb1-5.tab.c,在flex文件中調用該頭文件即可以實現聯合編譯,最后生成可執行程序fb1-5,測試結果如下:
可以看到,由于缺少對負數的支持,所以輸入負數會報錯。先講到這里,本節介紹了如何設計最基本的詞法分析器和語法分析器,后面會對flex和bison做更詳細介紹。
end
總結
以上是生活随笔為你收集整理的自制编译器学习3:Flex和Bison简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java下bison_windows下b
- 下一篇: 那些因素会影响微型真空泵间接抽水的效果?