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中的声明的定义文件
- Bison的使用
- Win flex-bison 的简单使用
- WINDOWS下bison和flex的使用
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和 LLVM编写你自己的编译器
- 【转】使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器[zz]
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用Flex Bison 和LLVM编写自己的编译器
- 使用flex和bison实现的sql引擎解析
- Unbuntu下使用bison编写文本解释器的方法
- 使用flex和bison实现的sql引擎解析
- 自定义View,转载大神的页面
- 常用的正则表达式
- 各领域公开数据集下载
- JSP知识点总结
- jQuery知识点(1)-版本选择与下载配置
- Bison的使用
- 面向对象程序设计上机练习七(类和对象)
- 使用redis list作为消息队列
- ssh密钥的生成与使用
- RDD和DataFram转换
- java 异常处理
- error C4996: '*': This function or variable may be unsafe. Consider using * instead
- SQLPlus设置
- oralce函数wm_concat 替代办法