Bison的使用

来源:互联网 发布:无线点菜机软件 编辑:程序博客网 时间:2024/06/15 16:37

一、Bison对输入的匹配

  bison是基于你所给定的语法来生成一个可以识别这个语法中有效“语句”的语法分析器。例如下面的这个例子:
statement:NAME ‘=’ expression
expression:NUMBER ‘+’ NUMBER
| NUMBER ‘?’ NUMBER
;
其中statement是递归的开始语句,NAME为递归终止符号,expression为递归式,同样的NUMBER也为递归终止符号,所以这句话可以理解为NAME=NUMBER+NUMBER/NUMBER?NUMBER,此处应注意终结符号和非终结符号是不同的.

二、移进/规约分析

1、移进

从一中的例子可以看出,bison在对语句进行分析时,会有一个递归的过程,当它分析到expression这个非终止符时,会递归到下一个语句,左值是expression开始的这个语句。此时bison会把expression先压入内部堆栈,接着切换到一个新的状态,这个状态会对应着刚刚压入内部堆栈的状态,这种过程叫移进.

2、规约

那么同样的,当expression匹配完成后,此时发现已经达到终态,可以完全匹配,所以要将栈中的expression状态移出,这样的过程叫规约.

3、二者的分析

现在举一个具体的例子来对上面的概念进行说明。现在有一个表达式a=13+15,bison先对其进行移进匹配:
首先NAME=expression这个语句没有组成规则,所以要先进行移进,
按照顺序a(此时NAME本是是终结符,所以直接被消除)
a =
a = 13
a = 13 +
a = 13 + 15
13 + 15已经可以匹配expression了,所以消除expression,此时就可以规约statement,将NAME=expression替换成statement.

三、Bison的LALR(1)

bison使用的是LALR(1),即自左向右一次只分析一个记号,看下面的例子
statement: a B C
| d B G
a: A | Z
d: Y|U
此处如果输入A B C,bison是会报错的,因为对于另一个输入Y B G来看,bison必须要通过C,G来区别究竟是a还是b记号,但是可惜的是bison一次只能识别一个记号,所以这2个输入bison识别到B时会出现二义性.

四、对bison和flex实现的一个计算器的代码分析

extern int yylineno;void yyerror(char* s,...);struct ast{   int nodetype;   struct ast* l;   struct ast* r;};struct numval{   int nodetype;   double number;};struct ast* newcast(int nodetype,struct ast* l,struct ast* r);struct ast* newnum(double d);double eval(struct ast*);void treefree(struct ast*);

先来看这里的代码,这是对计算器的一个声明,第一行的yylineno是来自于flex中,注意在flex中要使用%option yylineno来选择使用这个变量,yyerror是一个对原有函数的简单扩展,ast是定义即将生成的抽象语法树的节点类型,newcast,newnum是分别用来创建抽象语法树的节点的,
eval是遍历整棵树获得表达式的结果,treefree自然是释放整棵树.

%{#include <stdio.h>#include <stdlib.h>#include "calc.h"%}%union{   struct ast* a;   double d;}%token <d> NUMBER%token EOL%type <a> exp factor term

再看这里,是bison文件中的第一部分的声明,union表明即将使用的终结符号的类型有struct ast*和double,也就是一中的NAME和NUMBER的类型。%token指定了使用的终结符的符号,< d >的作用是指定NUMBER的类型为double,在union中可以看到d的类型为double,而EOL没有为其指定类型,故其类型默认为int.%type的作用是用来定义一个非终结字符,也就是一中的expression,同样的< a >是指定这些字符的类型为struct ast*

%%calclist:  | calclist exp EOL {printf("=%4.4g\n",eval($2));                      treefree($2);                      printf("> ");  }  | calclist EOL {printf("> ");} ;exp:factor  | exp '+' factor{ $$=newcast('+',$1,$3);}  | exp '-' factor{ $$=newcast('-',$1,$3);}  ;factor:term    | factor '*' term {$$=newcast('*',$1,$3);}   | factor '/' term {$$=newcast('/',$1,$3);}  ;term: NUMBER {$$=newnum($1);}   | '|' term{$$=newcast('|',$2,NULL);}   | '(' exp ')' {$$=$2;}   | '-' term {$$=newcast('M',$2,NULL);}  ;%%

  这是bison的第二部分,是用来定义各种动作的,首先来说第一句,它可以匹配2种结果,一种是规约了exp和EOL之后所得到的表达式的结果,一种是只规约EOL的结果,当然这个地方为了计算器能够连续进行计算,所以将开始符号calclist进行了移进。接下来,来看第二个符号exp,因为表达式可能是嵌套式的,例如4-5+3这种,所以exp同样在右式当中也要移进,这里注意一点,$$代替的是表达式的左式,即exp,$1、$2对应的是右式中的第一个符号和第二个符号.
  特别要注意的是这个地方通过factor,term的移进顺序指定了一个优先级,可以看出,在第二句中,bison一定会先移进factor,接着到factor,会先移进term,之后在term中才会进行规约,这样就实现了绝对值,(,负号的优先级高于乘除,乘除会高于加减

%option noyywrap nodefault yylineno%{   #include "calc.tab.h"   #include "calc.h"%}EXP ([Ee][-+]?[0-9]+)%%"+" |"-" |"*" |"/" |"(" |")" | {return yytext[0];}[0-9]+"."[0-9]*{EXP}? |"."?[0-9]+{EXP}? {yylval.d=atof(yytext);return NUMBER;}\n {return EOL;}"//".*[ \t] {}. {yyerror("Mystery character %c\n",*yytext);}%%

接下来就是flex中的文件,首先为了保证flex能正常使用bison中定义的符号,当然要包含bison生成的头文件 #include “calc.tab.h”。%option noyywrap是为了保证生成的文件在c中的可移植性.下面的就是对数的各种模式定义以及符号定义。当然有一点需要注意,yylval已经在bison中被定义为union类型了,因此浮点数的值应该赋给yylval.d

#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include "calc.h"struct ast* newcast(int nodetype,struct ast* l,struct ast* r){   struct ast* a=malloc(sizeof(struct ast));   if(!a){     yyerror("out of space");     exit(0);   }   a->nodetype=nodetype;   a->l=l;   a->r=r;   return a;}struct ast* newnum(double d){    struct numval* a=malloc(sizeof(struct numval));    if(!a){      yyerror("out of space");      exit(0);    }   a->nodetype='K';   a->number=d;   return (struct ast*)a;}double eval(struct ast* a){   double v;   switch(a->nodetype){     case 'K':v=((struct numval*)a)->number;break;     case '+':v=eval(a->l)+eval(a->r);break;     case '-':v=eval(a->l)-eval(a->r);break;     case '*':v=eval(a->l)*eval(a->r);break;     case '/':v=eval(a->l)/eval(a->r);break;     case '|':v=eval(a->l);if(v<0)v=-v;break;     case 'M':v=-eval(a->l);break;     default:printf("internal error:bad node %c\n",a->nodetype);   }  return v;}void treefree(struct ast* a){    switch(a->nodetype){        case '+':        case '-':        case '*':        case '/':           treefree(a->r);        case '|':        case 'M':             treefree(a->l);        case 'K':            free(a);            break;         default:printf("internal error:free bad node %c\n",a->nodetype);    }}void yyerror(char* s,...){   va_list ap;   va_start(ap,s);   fprintf(stderr,"%d: error: ",yylineno);   vprintf(stderr,s,ap);   fprintf(stderr,"\n");}int main(){    printf("> ");   return yyparse();}

这是bison中的声明的定义文件

原创粉丝点击