关于bison

博客介绍了yacc和Bison这两个生成语法分析器的工具,yacc生成的解析器用C语言写成,需与Lex配合;Bison与Yacc兼容且有改进,常和Flex搭配。还阐述了巴科斯范式(BNF)用于描述计算机语言语法的规则,以及bison程序结构、词法分析器程序、联合编译等内容,提及了二义性文法问题。

yacc(Yet Another Compiler Compiler),是一个经典的生成语法分析器的工具。yacc生成的编译器主要是用C语言写成的语法解析器(Parser),需要与词法解析器Lex一起使用,再把两部份产生出来的C程序一并编译。

Bison 基本上与 Yacc 兼容,并且在 Yacc 之上进行了改进。它经常和 Flex (一个自动的词法分析器生成器)一起使用。

此软件的源代码是可自由获得的,在 GPL 下发布。

 

BNF

巴科斯范式(BNF: Backus-Naur Form 的缩写)是由 John Backus 和 Peter Naur 首先引入的用来描述计算机语言语法的符号集。

在BNF中,双引号中的字("word")代表着这些字符本身。而double_quote用来代表双引号。

在双引号外的字(有可能有下划线)代表着语法部分。

< > : 内包含的为必选项。 
[ ] : 内包含的为可选项。 
{ } : 内包含的为可重复0至无数次的项。 
|  : 表示在其左右两边任选一项,相当于"OR"的意思。 
::= : 是“被定义为”的意思 或者单一的冒号
"..." : 术语符号 
[...] : 选项,最多出现一次 
{...} : 重复项,任意次数,包括 0 次 
(...) : 分组

|   : 并列选项,只能选一个

 

下面是是用BNF来定义的Java语言中的For语句的实例:

1

2

3

4

for (initialization; termination;

     increment) {

    statement(s)

}

BNF定义如下:

1

2

3

4

5

6

FOR_STATEMENT ::=

    "for" "(" ( variable_declaration  |

  ( expression ";" )  |  ";"  )

      [ expression ] ";"

      [ expression ]  ";"

     ")" ( statement | "{" statement "}" )

BNF处理1*2 + 3*4 +5简单的算术表达式:

1

2

3

4

5

6

7

8

<exp> ::= <factor>

    | <exp> + <factor>

<factor> ::= NUMBER

    | <factor> * NUMBER

 

exp被定义为是一个factor或者factor+exp

 

factor被定义是NUMBER或者factor*NUMBER

 

例子1:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

%{                                                                                                                          

#include <stdio.h>

%}

 

/*声明token*/

%token NUMBER

%token ADD SUB DIV MUL 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; }

    "+" term { $$ = $2; }

    ;

 

%%

int main(int argc, char ** argv)

{

    printf(">");

    yyparse();

    return 0;

}

 

yyerror(char *s)

{

    fprintf(stderr,"error:%s\n",s);

}

bison程序包括与flex程序相同的三个部分结构:声明部分、规则部分、C代码部分。

1、声明部分:

声明部分包含了会被原样拷贝到目标分析程序开头的C代码,同样也通过%{和%}来声明。

%token记号声明,以便于告诉bison在语法分析程序中的记号的名称。通常,记号总是使用大写。

任何没有声明为记号的语法符号必须出现在至少一条规则的左边(左边表示规则的定义)

2、规则部分:

简单的BNF定义的规则。bison使用单一的冒号而不是::=,分号被用来表示规则的结束。

在flex中每个规则之后,使用花括号括起。

 

bison会自动分析语法,记住每条被匹配的规则,所以动作代码只需要维护每个语法符号关联的语义值。

bison语法分析器也执行一些额外的动作,例如创建数据结构以便后续使用。

 

第一条规则左边的语法符号是语法起始符号(start symbol),整个输入必须被它匹配。

每个bison规则中的语法符号都有一个语义值,目标符号(冒号左边的语法符号)的值在动作中代码用$$代替,

右边语法符号的语义值依次为$1,$2,直到这条规则的结束。当词法分析器返回记号时,记号值总是存储在yyval里,

其他语法符号的语义规则在语法分析器的规则里进行设置,例如本例子的 factor、term和exp符号的语义值就是它们所

描述的表达式值。

 

例子中,头两条规则定义了calclist语法符号,通过循环来读入用换行符结束的表达式并且打印结果。

1

2

3

4

calclist: /*空规则*/

    | calclist exp EOL { printf("= %d\n",$2); }

    | calclist EOL { printf("> "); } /* blank line or a comment */                                                          

    ;

calclist的定义使用一种常见的双规则递归定义来实现一个序列或者列表:

第一个规则为空,不进行任何匹配

第二个规则添加一个项目到列表中,对应的动作是通过$2打印出exp的值

第三个规则实现输入空行

 

其余的规则实现计算器,带有操作符的规则(exp ADD factor  和ABS term)在语义值上进行相应的算术操作。

右边仅有一个语法符号的规则是组合文法,例如exp:factor,一种表达式exp就是一个因子factor。

如果一个规则缺少现实的动作,语法分析器将把$1赋予$$,这是i一个内部设定。

 

词法分析器程序

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

%option noyywrap

%{

#include "fb1-5.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);}

%%

 

1、由于在语法分析中声明了token,故这里使用的话,需要进行引用,在声明部分添加include文件

2、返回记号的时候,记号对应的值是存储在yylval变量中

 

联合编译flex和bison程序

对应的makefile文件内容为:

1

2

3

4

fb1-5: fb1-5.l fb1-5.y

        bison -d fb1-5.y

        flex fb1-5.l

        gcc -o $@ fb1-5.tab.c lex.yy.c -lfl

bison 使用-d选项(用于定义文件)运行,创建fb1-5.tab.c和fb1-5.tab.h文件

flex创建lex.yy.c,然后将两者和flex的库文件编译在一起

 

测试结果

1

2

3

4

5

6

[root@typhoeus79 bison]# ./fb1-5

>

> -5+10

= 5

-5*4+20

= 0

二义性文法:并不多见

语法分析为什么不写成这样?

1

2

3

4

5

6

7

exp:exp ADD exp

      | exp SUB exp

      | exp MUL exp

      | exp DIV exp

      | ABS exp

      | NUMBER

      ;

原因在于优先级和二义性。

分开的term、factor和exp的语法符号可以让bison首先处理ABS,接着是MUL和DIV,然后是ADD和SUB。

通常来说,一旦一种文法有不同的优先级,语法分析器就需要为每种优先级制定一条规则。

 

下面的文法如何?

1

2

3

4

5

exp: exp ADD exp

      |   exp SUB exp

      | factor;

 

factor和term部分相似

存在二义性。例如1-2+3的输入可能被分析为(1-2)+3,也可能被分析为1-(2+3)

如果一种文法是有歧义的,bison会报告冲突(conflicts),并且标出针对给定输入哪儿会有两种不同的分析。

 

增加其他规则

支持小括号

词法解析中添加如下:

1

2

"("     { return OP; }

")"     { return CP; }

语法解析中添加:

1

2

3

4

5

term:NUMBER

    | ABS term { $$ = $2>0 ?  $2 : -$2; }

    | "+" term { $$ = $2; }

    | "-" term { $$ = -$2; }

    | OP exp CP { $$ = $2;}

 如果想支持如下计算,应该怎么搞呢?

1

2

3

4

5

6

7

8

>(10-2)+(-10+2)

= 0

>(100-2)*2

= 196

>(+10-2)

= 8

>10-(-10+10) 

= 10

需要词法分析,重点需要区分正常的加减号以及前缀加减号

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

%option noyywrap

%{

#include "fb1-5.tab.h"

int flag=1;

int flag2 = 1;

 

%}

 

%%

"+"     {

            if(flag){

                flag = 1;

                //printf("ADD\n");

                return ADD;

            }else

            {

                flag2 = 1;

            }

        }

"-"     {

            if(flag){

                //printf("SUB\n");

                flag = 1;

                return SUB;

            }else

            {

                //printf("Flag=%d,Here\n",flag);

                flag2 = -1;

            }

        }

"*"     return MUL; }

"/"     return DIV; }

"("     {   flag = 0;

            //printf("OP\n");

            return OP; }

")"     {

            flag =1;

            flag2 = 1;

            //printf("CP\n");

            return CP; }

 

 

"|"     {return ABS; }

^[-+][0-9]+ { yylval = atoi(yytext); return NUMBER;}

[0-9]+  {

            if(flag)

                yylval = atoi(yytext);

            else{

                yylval = flag2*atoi(yytext);

                flag = 1;

            }

            //printf("NUMBER2=%d\n",yylval);

            return NUMBER;}

 

\n      {return EOL; }                                                                                                      

[ \t]   {} 

.       { yyerror("Mystery character=%c!",*yytext);}

%%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值