日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

编译原理实验二:Bison

發(fā)布時(shí)間:2024/3/26 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编译原理实验二:Bison 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

編譯原理實(shí)驗(yàn)二:Bison

實(shí)驗(yàn)要求

1.了解Bision基礎(chǔ)知識(shí),如何將文法產(chǎn)生式轉(zhuǎn)換為Bison語句

2.閱讀/src/common/SyntaxTree.c,對(duì)應(yīng)頭文件 /include/SyntaxTree.h,理解分析樹生成的過程。

3.了解Bison與Flex的協(xié)同工作過程,理解pass_node函數(shù)并改寫lab1代碼。了解yylval工作原理。

4.補(bǔ)全 src/parser/syntax_analyzer.y 文件和lexical_analyzer.l文件

實(shí)驗(yàn)難點(diǎn)

1.Bison的使用

Bison是一個(gè)語法分析器的生成工具,用于生成語法分析器。Bison可以將LALR文法轉(zhuǎn)換可編譯的c代碼。Bison文件的擴(kuò)展名為.y,在Bison文件中給出LALR文法以及一些分析動(dòng)作,編譯就可以產(chǎn)生一個(gè)語法分析器。

Bison文件以.y結(jié)尾,與Lex文件的編寫規(guī)則類似,由%%區(qū)分的三部分構(gòu)成:

%{ /* 這部分代碼會(huì)被原樣拷貝到生成的 .c 文件的開頭 */ #include <stdio.h> int yylex(void); void yyerror(const char *s); %}/*一些指令,如用%start指定起始符號(hào),%token定義token*/ %start reimu %token REIMU%% /*解析規(guī)則*/ 產(chǎn)生式 {動(dòng)作代碼}%% /*輔助函數(shù),會(huì)被復(fù)制到生成的.c文件的末尾*/

Bison需要一個(gè)yylex來獲取下一個(gè)詞法單元,還需要一個(gè)yyerror提供報(bào)錯(cuò)。定義主函數(shù),調(diào)用yyparse(),就可以讓語法分析器工作。

在語法分析過程中,語法分析樹的葉子節(jié)點(diǎn)是一個(gè)具體的語義值,該值的類型是YYSTYPE,在Bison中用%union指明。不同的節(jié)點(diǎn)對(duì)應(yīng)著不同的終結(jié)符,可能為不同的類型,因此union中可以包含不同的數(shù)據(jù)類型。可以指明一個(gè)終結(jié)符或是非終結(jié)符的類型,以便后續(xù)的使用。可以使用%type <>或%token <>指明類型。其中%token是在聲明詞法單元名的同時(shí)指明類型,聲明的token會(huì)由Bison導(dǎo)出到最終的.h文件中,讓詞法分析器也可以直接使用。

%token <num> NUMBER /*聲明詞法單元名,并在<>中指明類型*/ %type <typex> expr /*指明類型*/ ... %union{char op;double num; }

下面說明語法分析時(shí)的動(dòng)作怎么編寫。以一個(gè)邊進(jìn)行語法分析邊按照語義執(zhí)行的計(jì)算器為例,識(shí)別到加法語句的動(dòng)作為:

E→E+E {E=E1+E2}

在Bison中的實(shí)現(xiàn):

term : term ADDOP factor{switch $2 {case '+': $$ = $1 + $3; break;case '-': $$ = $1 - $3; break;}}

其中$$表示當(dāng)前節(jié)點(diǎn),$1,$2,$3表示產(chǎn)生式的成分,也是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。由于采用自底向上分析(LALR)文法,構(gòu)建語法樹是推導(dǎo)的過程,這些子節(jié)點(diǎn)是已經(jīng)解析的,當(dāng)前節(jié)點(diǎn)則是規(guī)約產(chǎn)生的。使用節(jié)點(diǎn)union的哪個(gè)類型操作,是已經(jīng)用<>在開頭的%token和%type中指明的。

實(shí)際的編譯器中,語法分析相應(yīng)的動(dòng)作通常是建立抽象語法樹,進(jìn)行語義分析,或是直接產(chǎn)生中間或目標(biāo)代碼。在本實(shí)驗(yàn)中,動(dòng)作為自底向上構(gòu)建語法分析樹。

2.Bison與Flex的協(xié)同工作

Bison需要一個(gè)yylex來完成詞法分析,這部分的工作是詞法分析器完成的。詞法分析器不僅要將詞素識(shí)別為詞法單元并返回詞法單元值,還要返回詞法單元的屬性。這是通過yylval完成的。yylval是使用Bison生成的.c文件中聲明的一個(gè)全局的變量,類型為YYSTYPE,即在Bison文件中%union聲明的類型,使用這個(gè)類型將屬性值傳遞。進(jìn)行詞法分析時(shí),只要將屬性值存入yylval,語法分析器就可以從yylval獲取識(shí)別到的詞法單元的屬性值。

在實(shí)驗(yàn)中,yylval僅包含一個(gè)語法分析樹的節(jié)點(diǎn)指針。節(jié)點(diǎn)中包含一個(gè)節(jié)點(diǎn)名。對(duì)于語法分析樹的葉子節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)名就是詞法單元的值,對(duì)于非葉子節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)名為語法成分名。在以下分析語法分析樹的生成過程中,分析了節(jié)點(diǎn)及分析樹是如何構(gòu)造的。

3.分析樹的生成過程

分析樹的相關(guān)數(shù)據(jù)結(jié)構(gòu)和方法定義在/include/SyntaxTree.h文件中,分析樹的節(jié)點(diǎn)記錄了父節(jié)點(diǎn),子節(jié)點(diǎn)的指針,以及子節(jié)點(diǎn)數(shù)和節(jié)點(diǎn)名信息,相關(guān)的方法包括生成新的節(jié)點(diǎn),添加子節(jié)點(diǎn),創(chuàng)建語法樹等。

//語法分析樹的節(jié)點(diǎn) struct _syntax_tree_node {struct _syntax_tree_node * parent;struct _syntax_tree_node * children[10];int children_num;char name[SYNTAX_TREE_NODE_NAME_MAX]; }; typedef struct _syntax_tree_node syntax_tree_node; //相關(guān)函數(shù) syntax_tree_node * new_anon_syntax_tree_node(); //創(chuàng)建新節(jié)點(diǎn) syntax_tree_node * new_syntax_tree_node(const char * name); int syntax_tree_add_child(syntax_tree_node * parent, syntax_tree_node * child); //添加子節(jié)點(diǎn) void del_syntax_tree_node(syntax_tree_node * node, int recursive); //刪除節(jié)點(diǎn) syntax_tree* new_syntax_tree(); //創(chuàng)建語法分析樹 void del_syntax_tree(syntax_tree * tree); //刪除分析樹 void print_syntax_tree(FILE * fout, syntax_tree * tree); //輸出分析樹

每個(gè)終結(jié)符都對(duì)應(yīng)著一個(gè)葉子節(jié)點(diǎn),這個(gè)葉子節(jié)點(diǎn)在詞法分析時(shí)就可以產(chǎn)生。在自底向上的分析過程中,首先產(chǎn)生的是葉子節(jié)點(diǎn),在用產(chǎn)生式進(jìn)行規(guī)約時(shí)向上構(gòu)建語法分析樹。葉子節(jié)點(diǎn)的產(chǎn)生在詞法分析器中的pass_node()函數(shù)中實(shí)現(xiàn),創(chuàng)建一個(gè)新的節(jié)點(diǎn),并將其指針賦值給yylval,節(jié)點(diǎn)名為其成分(非終結(jié)符名或終結(jié)符名),這樣語法分析器就可以使用該節(jié)點(diǎn)構(gòu)造語法分析樹。

//生成節(jié)點(diǎn)并存入yylval傳遞給語法分析器 void pass_node(char *text){yylval.node = new_syntax_tree_node(text); } //識(shí)別詞法單元時(shí)調(diào)用pass_node \+ { pos_start = pos_end; pos_end += 1; pass_node(yytext); return ADD; }

詞法分析完成了葉子節(jié)點(diǎn)的產(chǎn)生,剩下的工作就由語法分析來完成了。構(gòu)建的過程就是在每使用一個(gè)產(chǎn)生式進(jìn)行規(guī)約時(shí),建立一個(gè)新的節(jié)點(diǎn)表示當(dāng)前產(chǎn)生式的非終結(jié)符,然后將產(chǎn)生式中的成分,也就是子節(jié)點(diǎn)的指針存入這個(gè)新節(jié)點(diǎn)中。當(dāng)最后使用起始產(chǎn)生式規(guī)約時(shí),產(chǎn)生的新節(jié)點(diǎn)就是語法分析樹的根節(jié)點(diǎn),就完成了向上構(gòu)建語法分析樹的工作。實(shí)驗(yàn)在Bison的.y文件中,已經(jīng)給出了創(chuàng)建新節(jié)點(diǎn)并建立節(jié)點(diǎn)關(guān)系的函數(shù),為node()函數(shù),參數(shù)為產(chǎn)生式的非終結(jié)符名,產(chǎn)生式成分個(gè)數(shù)(也即子節(jié)點(diǎn)個(gè)數(shù)),子節(jié)點(diǎn)的指針。

//產(chǎn)生一個(gè)語法分析樹新節(jié)點(diǎn)的函數(shù) syntax_tree_node *node(const char *node_name, int children_num, ...); //應(yīng)用該函數(shù)構(gòu)造語法分析樹,根節(jié)點(diǎn)的構(gòu)造 program : declaration-list { $$ = node("program", 1, $1); gt->root = $$; }

實(shí)驗(yàn)設(shè)計(jì)

1.詞法分析部分

完善詞法分析部分,即./src/parser/lexical_analyzer.l文件。只需要在識(shí)別動(dòng)作中添加pass_node(yytext)產(chǎn)生詞法單元葉子節(jié)點(diǎn),通過yylval傳遞給語法分析器。對(duì)于注釋,換行符和空格,不需要添加到語法分析樹當(dāng)中,因此創(chuàng)建節(jié)點(diǎn)和返回值,如果讀到了就更新lines與pos,保證出錯(cuò)時(shí)可以定位,然后進(jìn)行下一個(gè)詞法單元的識(shí)別就可以了。

%%\+ { pos_start = pos_end; pos_end += 1; pass_node(yytext); return ADD; } \- {pos_start = pos_end; pos_end++; pass_node(yytext); return SUB;} \* {pos_start = pos_end; pos_end++; pass_node(yytext); return MUL;} \/ {pos_start = pos_end; pos_end++; pass_node(yytext); return DIV;} \< {pos_start = pos_end; pos_end++; pass_node(yytext); return LT;} "<=" {pos_start = pos_end; pos_end+=2; pass_node(yytext); return LTE;} \> {pos_start = pos_end; pos_end++; pass_node(yytext); return GT;} ">=" {pos_start = pos_end; pos_end+=2; pass_node(yytext); return GTE;} "==" {pos_start = pos_end; pos_end+=2; pass_node(yytext); return EQ;} "!=" {pos_start = pos_end; pos_end+=2; pass_node(yytext); return NEQ;} \= {pos_start = pos_end; pos_end++; pass_node(yytext); return ASSIN;} \; {pos_start = pos_end; pos_end++; pass_node(yytext); return SEMICOLON;} \, {pos_start = pos_end; pos_end++; pass_node(yytext); return COMMA;} \( {pos_start = pos_end; pos_end++; pass_node(yytext); return LPARENTHESE;} \) {pos_start = pos_end; pos_end++; pass_node(yytext); return RPARENTHESE;} \[ {pos_start = pos_end; pos_end++; pass_node(yytext); return LBRACKET;} \] {pos_start = pos_end; pos_end++; pass_node(yytext); return RBRACKET;} \{ {pos_start = pos_end; pos_end++; pass_node(yytext); return LBRACE;} \} {pos_start = pos_end; pos_end++; pass_node(yytext); return RBRACE;} else {pos_start = pos_end; pos_end+=4; pass_node(yytext); return ELSE;} if {pos_start = pos_end; pos_end+=2; pass_node(yytext); return IF;} int {pos_start = pos_end; pos_end+=3; pass_node(yytext); return INT;} float {pos_start = pos_end; pos_end+=5; pass_node(yytext); return FLOAT;} return {pos_start = pos_end; pos_end+=6; pass_node(yytext); return RETURN;} void {pos_start = pos_end; pos_end+=4; pass_node(yytext); return VOID;} while {pos_start = pos_end; pos_end+=5; pass_node(yytext); return WHILE;} [a-zA-Z]+ {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext); return IDENTIFIER;} [a-zA-Z] {pos_start = pos_end; pos_end++; pass_node(yytext); return LETTER;} [0-9]+ {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext); return INTEGER;} [0-9]+\.|[0-9]*\.[0-9]+ {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext); return FLOATPOINT;} "[]" {pos_start = pos_end; pos_end+=2; pass_node(yytext); return ARRAY;} \n {lines++;pos_end = 1;} "/*"([^*]|\*+[^*/])*\*+"/" {for(int i=0;i<strlen(yytext);i++){if(yytext[i]=='\n') {lines++;pos_end = 1; //pos_start由pos_end得到,這里就不需要置1了}else pos_end++;} } [" "|\t] {pos_start = pos_end; pos_end+=strlen(yytext);} . {pos_start = pos_end; pos_end+=strlen(yytext);printf("lexical analyze error at line %d pos %d\n",lines,pos_start);}%%

2.語法分析部分

完善詞法分析部分,即./src/parser/lexical_analyzer.l文件。首先完成yylval的定義,在union中只含有一個(gè)節(jié)點(diǎn)指針。

%union {syntax_tree_node *node; }

接下來進(jìn)行終結(jié)符(詞法單元)的聲明和非終結(jié)符的類型聲明,類型都是語法分析樹的節(jié)點(diǎn)指針,其中終結(jié)符名要和詞法分析部分中的token一致,非終結(jié)符名和Cminus-f的語法規(guī)則中一致。聲明如下:

%start program %token <node> ADD SUB MUL DIV %token <node> LT LTE GT GTE EQ NEQ ASSIN %token <node> SEMICOLON COMMA LPARENTHESE RPARENTHESE LBRACKET RBRACKET LBRACE RBRACE %token <node> ELSE IF INT FLOAT RETURN VOID WHILE IDENTIFIER LETTER INTEGER FLOATPOINT ARRAY %type <node> type-specifier relop addop mulop %type <node> declaration-list declaration var-declaration fun-declaration local-declarations %type <node> compound-stmt statement-list statement expression-stmt iteration-stmt selection-stmt return-stmt %type <node> simple-expression expression var additive-expression term factor integer float call %type <node> params param-list param args arg-list program

最后補(bǔ)充語法規(guī)則的部分,規(guī)則按照給出的Cminus-f的語法編寫,動(dòng)作則是調(diào)用node()函數(shù)構(gòu)造語法分析樹的節(jié)點(diǎn),參數(shù)為子節(jié)點(diǎn)個(gè)數(shù)和使用$n表示的子節(jié)點(diǎn)的指針,當(dāng)產(chǎn)生式為空輸入時(shí),參數(shù)為0,子節(jié)點(diǎn)為空串。

program : declaration-list { $$ = node("program", 1, $1); gt->root = $$; } ; declaration-list : declaration-list declaration { $$ = node("declaration-list", 2, $1, $2); }| declaration { $$ = node("declaration-list", 1, $1); }; declaration : var-declaration { $$ = node("declaration", 1, $1); }| fun-declaration { $$ = node("declaration", 1, $1); }; var-declaration : type-specifier IDENTIFIER SEMICOLON { $$ = node("var-declaration", 3, $1, $2, $3); }| type-specifier IDENTIFIER LBRACKET INTEGER RBRACKET SEMICOLON { $$ = node("var-declaration", 6, $1, $2, $3, $4, $5, $6); }; type-specifier : INT { $$ = node("type-specifier", 1, $1); }| FLOAT { $$ = node("type-specifier", 1, $1); }| VOID { $$ = node("type-specifier", 1, $1); }; fun-declaration : type-specifier IDENTIFIER LPARENTHESE params RPARENTHESE compound-stmt { $$ = node("fun-declaration", 6, $1, $2, $3, $4, $5, $6); } ; params : param-list { $$ = node("params", 1, $1); }| VOID { $$ = node("params", 1, $1); }; param-list : param-list COMMA param { $$ = node("param-list", 3, $1, $2, $3); }| param { $$ = node("param-list", 1, $1); }; param : type-specifier IDENTIFIER { $$ = node("param", 2, $1, $2); }| type-specifier IDENTIFIER ARRAY { $$ = node("param", 3, $1, $2, $3); }; compound-stmt : LBRACE local-declarations statement-list RBRACE { $$ = node("compound-stmt", 4, $1, $2, $3, $4); } ; local-declarations : { $$ = node("local-declarations", 0); }| local-declarations var-declaration { $$ = node("local-declarations", 2, $1, $2); }; statement-list : { $$ = node("statement-list", 0); }| statement-list statement { $$ = node("statement-list", 2, $1, $2); }; statement : expression-stmt { $$ = node("statement", 1, $1); }| compound-stmt { $$ = node("statement", 1, $1); }| selection-stmt { $$ = node("statement", 1, $1); }| iteration-stmt { $$ = node("statement", 1, $1); }| return-stmt { $$ = node("statement", 1, $1); }; expression-stmt : expression SEMICOLON { $$ = node("expression-stmt", 2, $1, $2); }| SEMICOLON { $$ = node("expression-stmt", 1, $1); }; selection-stmt : IF LPARENTHESE expression RPARENTHESE statement { $$ = node("selection-stmt", 5, $1, $2, $3, $4, $5); }| IF LPARENTHESE expression RPARENTHESE statement ELSE statement { $$ = node("selection-stmt", 7, $1, $2, $3, $4, $5, $6, $7); }; iteration-stmt : WHILE LPARENTHESE expression RPARENTHESE statement { $$ = node("iteration-stmt", 5, $1, $2, $3, $4, $5); } ; return-stmt : RETURN SEMICOLON { $$ = node("return-stmt", 2, $1, $2); }| RETURN expression SEMICOLON { $$ = node("return-stmt", 3, $1, $2, $3); }; expression : var ASSIN expression { $$ = node("expression", 3, $1, $2, $3); }| simple-expression { $$ = node("expression", 1, $1); }; var : IDENTIFIER { $$ = node("var", 1, $1); }| IDENTIFIER LBRACKET expression RBRACKET { $$ = node("var", 4, $1, $2, $3, $4); }; simple-expression : additive-expression relop additive-expression { $$ = node("simple-expression", 3, $1, $2, $3); }| additive-expression { $$ = node("simple-expression", 1, $1); }; relop : LTE { $$ = node("relop", 1, $1); }| LT { $$ = node("relop", 1, $1); }| GT { $$ = node("relop", 1, $1); }| GTE { $$ = node("relop", 1, $1); }| EQ { $$ = node("relop", 1, $1); }| NEQ { $$ = node("relop", 1, $1); }; additive-expression : additive-expression addop term { $$ = node("additive-expression", 3, $1, $2, $3); }| term { $$ = node("additive-expression", 1, $1); }; addop : ADD { $$ = node("addop", 1, $1); }| SUB { $$ = node("addop", 1, $1); }; term : term mulop factor { $$ = node("term", 3, $1, $2, $3); }| factor { $$ = node("term", 1, $1); }; mulop : MUL { $$ = node("mulop", 1, $1); }| DIV { $$ = node("mulop", 1, $1); }; factor : LPARENTHESE expression RPARENTHESE { $$ = node("factor", 3, $1, $2, $3); }| var { $$ = node("factor", 1, $1); }| call { $$ = node("factor", 1, $1); }| integer { $$ = node("factor", 1, $1); }| float { $$ = node("factor", 1, $1); }; integer : INTEGER { $$ = node("integer", 1, $1); } ; float : FLOATPOINT { $$ = node("float", 1, $1); } ; call : IDENTIFIER LPARENTHESE args RPARENTHESE { $$ = node("call", 4, $1, $2, $3, $4); } ; args : { $$ = node("args", 0); }| arg-list { $$ = node("args", 1, $1); }; arg-list : arg-list COMMA expression { $$ = node("arg-list", 3, $1, $2, $3); }| expression { $$ = node("arg-list", 1, $1); };

完成了以上的補(bǔ)充后,語法分析和詞法分析就應(yīng)該都可以正常進(jìn)行了。嘗試編譯時(shí)提示缺少yyin的聲明,在語法分析函數(shù)parse中使用了yyin來進(jìn)行讀入,yyin是詞法分析Flex產(chǎn)生的變量,這里需要引入,因此在開頭補(bǔ)充引入該文件指針變量。

extern FILE *yyin;

實(shí)驗(yàn)結(jié)果驗(yàn)證

1.給出的測(cè)試樣例

編譯成功后執(zhí)行命令./tests/lab2/test_syntax.sh easy和./tests/lab2/test_syntax.sh normal生成語法分析樹。

diff命令驗(yàn)證結(jié)果是否正確。

2.自行編寫的測(cè)試樣例

編寫一個(gè)cminus-f程序進(jìn)行語法分析,產(chǎn)生語法分析樹。

int main(void){int i;int a[10];int j;i=0;j=10;quicksort(a,i,j);return 0; } void quicksort(int a[],int l, int r){int p;if(l<r){p = partition(a,l,r);quicksort(a,l,p-1);quicksort(a,l,p+1);} } int partition(int a[],int l,int r){int temp;temp = a[l];while(l<r){while(temp<=a[r]){r = r-1;if(l==r){a[l] = temp;return l;}}a[l] = a[r];while(temp>=a[l]){l = l+1;if(l==r){a[l] = temp;return l;}}} }

沒有語法錯(cuò)誤,相應(yīng)的語法分析樹也是正確的。部分結(jié)果如下:

編寫一個(gè)存在語法錯(cuò)誤的程序,cminus語法中變量不可以在一個(gè)聲明語句聲明多個(gè)同類型變量。

int func(int a[], int n){int i,sum;i = n;sum = 0;while(i>0){sum = sum+a[i];i--;}return ; }

程序給出了語法錯(cuò)誤的位置。

總結(jié)

以上是生活随笔為你收集整理的编译原理实验二:Bison的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。