[Flex&Bison]协同工作简介
来源:互联网 发布:电商与淘宝有什么区别 编辑:程序博客网 时间:2024/06/05 08:17
1. 本节主要演示一个简单的模拟bc计算器的程序,主要功能就是解析整型数的四则运算,先给出bison程序:
%{#include <stdlib.h>#include <stdio.h>%}/* 定义两个记号,D_INT表示整型类型,EOL表示换行(End Of Line) */%tokenD_INT%tokenEOL/* 以下4组BNF范式来定义构造语法树的规则 *//* 每条规则都有一个对应的C语言动作,表示一旦bison使用该规则去构造语法树(的一部分),则执行相应的动作 */%%/* 由于这是第一组规则,因此cac将作为语法起始符号,cac:的第一行用空规则表示 */cac : /* cac -> cac exp EOL *//* |表示从左边的cac推到到右边的表达式有多种不同的方式,即或的意思 *//* :可以理解为推导符号-> *//* :的左边为目标符号,其值用$$表示 *//* :右边的语法符号的值从左到右依次为$1、$2、$3…… */| cac exp EOL { printf( "ans = %d\n", $2 ); }; /* ;表示一组规则的结束,同时也作为行分隔符美观代码 */exp : fac /* exp -> fac|exp+fac|exp-fac *//* 虽然exp -> fac的这条规则没有定义动作,但是bison会默认处理成$$ = $1 *//* 这非常符合常理 */| exp '+' fac { $$ = $1 + $3; }| exp '-' fac { $$ = $1 - $3; };fac : trm /* fac -> trm|fac*trm|fac/trm */| fac '*' trm { $$ = $1 * $3; }| fac '/' trm {/* 判断除零错误 */if ( !$3 ) { perror( "Divide by zero!" ); exit( EXIT_SUCCESS ); }else $$ = $1 / $3;};trm : D_INT /* exp -> int||trm */| '|' trm { $$ = $2 < 0 ? -$2 : $2; };%%intmain( int arc, char **argv ) {yyparse(); /* bison自己生成的语法分析例程 */return 0;}intyyerror( char *s_err ) { /* 当分析产生错误时将自动调用该例程来解决错误 */fprintf( stderr, "error: %s\n", s_err );return 0;}经过bison编译后得到的.h文件:
/* Tokens. */#ifndef YYTOKENTYPE# define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { D_INT = 258, EOL = 259 };#endif/* Tokens. */#define D_INT 258#define EOL 259#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLAREDtypedef int YYSTYPE;# define yystype YYSTYPE /* obsolescent; will be withdrawn */# define YYSTYPE_IS_DECLARED 1# define YYSTYPE_IS_TRIVIAL 1#endifextern YYSTYPE yylval;可以看到在.l文件和.y文件中都可以定义标记,只不过.l文件中定义标记必须通过enum yytokentype的方式定义(但前提是.y文件中必须定义!)并且需要定义YYTOKENTYPE宏,同时可以看到.y中定义的标记的编号是从258开始的,因此不会和ASCII字符的值相冲突;
在这就是yylval是一个外部链接过来的全局变量,该变量是在flex的库中定义(和yylex()一样),因此最后cc编译时要链接lex库才行!
2. Bison的语法分析基本原理以及对上述程序的补充说明:
1) 语法分析的主要任务就是用flex解析出来的所有标记构造出一棵语法树;
2) 而bison最主要的任务就是确定一种将标记转化为语法树的规则;
3) bison就是使用上下文无关文法来确定这组规则的,而描述该文法的标准书写格式就是BNF范式;
4) bison程序的结构和flex相同,也分为三段,第一段和第三段和flex的意义相同,而第二段则是规则段,用来确定构造语法树的规则;
5) 规则段使用BNF来描述,其实就是一组组产生式规则,规则中包含终结符和非终结符,其中%token声明的记号都属于终结符,而在规则段中出现的未在%token中声明过的符号都是非终结符,它们一定能从终结符推导出来,任何没有声明为%token的符号必须至少出现在:左边一次,否则就意味着像C语言中的变量声明了但未使用的情形,可能会导致错误!
6) 规则段中的第一组规则是最高级别的规则,其左部就是该语法的起始符号,最终推导一定要归结到该符号,否则就意味着原文本语法错误!
7) 以上使用左递归的方式描述了四则运算的法则,其中将加减和乘除分开,使得乘除优先级高于加减,同时定义了用|模拟取绝对值运算符,并且其优先级最高!
8) 在每条规则中,每个终结符和非终结符都是由具体值的,:左边的非终结符的值默认已经从其它推导获得(计算而得),可以直接用$n的方式访问,而左边的目标符号的值确实未知的,需要通过动作来决定它,例如$$ = $1 + $3,这也就是定义语法意义的本质!
3. 再看flex程序:
%{/* 由bc.y通过bison编译而得 *//* 里面定义了记号D_INT和EOL */#include "bc.tab.h"%}/* 为整型数的正则表达式定义一个宏名 */INT[-+]?[0-9]+/* 由于char类型所表示字符处于ASCII范围 *//* 而bison程序中定义的标记的编号是从258开始的 *//* 因此直接返回字符和返回编号两者之间不会冲突 *//* flex和bison之间的关系就是bison利用flex的yylex()去解析 *//* 原文中的各个记号,解析出来一个记号,yylex()就将该记号的编号返回给bison供它分析 *//* 同时flex会将解析出来的记号值存放在yytext中,并且可以将记号值所表示的数值解析到yylval中保存 *//* 而这两个变量的值都会自动传给bison,因此在bison程序中也可以访问这两个值 *//* 这样bison就可以充分利用flex的词法分析了 */%%"+"{ return '+'; }"-"{ return '-'; }"*"{ return '*'; }"/"{ return '/'; }"|"{ return '|'; }{INT}{ yylval = atoi( yytext ); return D_INT; }\n{ return EOL; }[ \t]{ }.{ fprintf( stderr, "Illegal character %c\n", *yytext ); }%%lex.bc.y是由bison -d bc.y得来的!
4. Makefile:
bc.out: bc.l bc.ybison -o bc.tab.c -d bc.yflex -o lex.bc.c bc.lcc -o $@ -ll bc.tab.c lex.bc.c
5. 运行结果:
$ ./bc.out2 + 832 * |-3ans = 2498
6.
- [Flex&Bison]协同工作简介
- flex bison
- bison & flex
- flex&bison
- flex bison
- flex&&bison
- [Flex&Bison]正则表达式简介以及简单的Flex程序
- 《flex与bison》读书笔记--ch01 flex和bison简介(上)
- Flex/Lex&Bison/Yacc
- flex and bison.[link]
- Bison-Flex 笔记
- flex&bison 学习笔记
- Bison-Flex 笔记
- Bison-Flex 笔记
- Bison-Flex 笔记
- Bison-Flex 笔记
- Bison-Flex 笔记
- Flex和Bison
- 为ffmpeg添加自定义滤镜
- js的位运算符
- Android——HttpClient 实现POST和Get请求,Connection链接
- JSP中使用哪种输出方式更好?
- 开发问题及解决 java.lang.ClassCastException:android.widget.LinearLayout$LayoutParams
- [Flex&Bison]协同工作简介
- http get 与 post
- C/C++笔试题目大全
- C++模板学习
- 约瑟夫环问题
- Guava缓存器源码分析——CacheBuilder
- fedora19下配置samba服务器与windows7共享文件夹
- 目标黑马,我的自学之路:第十六章 基础测试实例
- 分层有限状态机的C++实现