




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Flex & Bison 使用教程使用说明· 本文需要读者对C语言有一定的了解作为基础 · 本文中所涉及的例子可以用本站提供的全自动化Makefile一文中提供的Makefile进行编译 · 读者如果在Linux下,可以直接使用,Windows用户需要Cygwin()环境 · 本文中的工具,需要用户安装flex和bison软件包 1.介绍编译器是软件开发中的核心部件,其作用是其他任何软件所不能取代的。编译器在工作过程中,往往完成如下的任务: 1. 读取源代码并且获得程序的结构描述 2. 分析程序结构,并且生成相应的目标代码 在UNIX早期时代,编
2、写一个编译器是一件非常耗时的工作。人们为了简化开发过程,开发了Lex和YACC程序来解决第一个任务,根据用户描述的语言,生成能够解决问题的C/C+语言代码,供开发者使用。 1. 将源代码文件分解为各种词汇(Lex) 2. 找到这些词汇的组成方式(YACC) GNU软件协会开发了Flex和BISON,其功能与LEX和YACC基本兼容,并且在Lex和YACC提供的功能的基础上进行了各种扩展。 2.Flex入门Lex能够用来编写那些输入数据流(字符串)能够用正则表达式描述的程序,它可以根据正则表达式的描述,将输入数据流分类为各类词汇,为后来的语法分析做准备。 2.1.正则表达式正则表达式是通过对各种
3、词组类型所包含的字符类型的归纳,描述所需词组组成格式的方法,比如下面的例子描述了所有数字类型的字符串,表明无论在何时起,只要有0-9字符出现,就进入该状态,说明是字符,直到非0-9字符结束: 0123456789+为了简化书写起见,也可以写成如下的格式: 0-9+对于任意单词,其中只能包含字母,所以单词的正则表达式为: a-zA-Z+Flex支持如下类型的正则表达式: x 符合字符串"x". 除了换行以外的任何字符xyz
4、160; 一个字符类,在这个例子中,输入必须符合要么是'x要么是'y'要么是'z'abj-oZ 一个带范围的字符类,符合任何满足'a', 'b', 从'j'到'o'还有'Z'A-Z 一个取反的字符类,比如任何字母除了大写字母。 A-Zn 任何字符除了大写字母和换行r* 零个或更多r,r可以是任意正则表达式r+ &
5、#160; 一个或更多rr? 零个或最多一个rr2,5 任何2到5个rr2, 2个或更多rr4 正好4个rname 对name的扩展"xyz"foo" 符合正则表达式 xyz"foo 的字符串X 如果X是一个'a'
6、, 'b', 'f', 'n', 'r', 't', 或者'v', 则按照ASCII码x转义符进行处理,否则,其他的'X'将用来做取消处理符处理0 一个ASCII码空字符123 内容为八进制123的char型x2a 内容为十六进制0x2a的char型(r
7、) 符合一个r,括号是用来越过优先级的rs 正则表达式r,紧跟着一个r|s 要么是r要么是sr 一个r,但是必须是在一行的开始r$ 一个r,但是必须是在行末<s>r 一个r,但是之前字符串必须符合条件s<s1,s2,s3>r
8、60; 同上,但是必须之前字符串符合s1或者s2或者s3<*>r 不考虑开始字符串类型,只符合r<<EOF>> 文件末尾<s1,s2><<EOF>> 前面符合s1或者s2的文件末尾2.2.第一个Lex代码按照上一节我们讲述的正则表达式例子,我们尝试第一次使用Lex来产生我们所需要的程序,实践一遍Lex的使用以及gcc编译器如何编译和生成所
9、需的二进制。 Flex甚至Bison代码都有如下的编写格式: /* 定义段 */%/* Flex、Bison代码段(规则) */%/* 辅助代码段,C语言 */首先,使用vi编译器,输入以下代码(test.l): / 定义段代码% / 这种括号说明内部的代码不许flex处理,直接进入.C文件#include <stdio.h>%/ 词法规则段代码01
10、23456789+ printf("NUMBER"); / 数字类型字符串a-zA-Z+ printf("WO"); / 单词类型字符串(WORD) printf(&q
11、uot;RD"); %/ 辅助C语言函数代码(直接写C语言,无须括号,我们这里无内容)下面我们首先使用lex程序生成所需的状态机代码: flex -otest.c test.l # 从正则表达式声称对应的C语言代码,注意-o后不要有空格(flex bug?)gcc test.c -o test -lfl # 从C语言代码生成二进制,从而运行,-lfl说明链接libfl.a库文件./test
12、60; # 运行刚刚生成的二进制下面我们来对刚生成的二进制进行试验,以弄清楚flex到底是做什么的,在test程序中输入以下内容,按下Ctrl-D可以退出test程序: 3505hellowhat is 3505通过这些试验,相信您已经明白了Flex的用途,每当一个正则表达式符合时,flex生成的代码就会自动调用对应的函数,或者运行对应的程序。下面我们对这个例子进行一些深入研究,处理一个类似C语言的程序配置脚本代码。首先,一个演示脚本如下: logging category lame-servers
13、 null; ; category cname null; ;zone "." type hint; file "/etc/bind/db.root"在这个脚本中,我们可以看到一系列的词汇类型: · 单词(WORD),比如'zone', 'type' · 文件名(FILENAME),比如'/etc/bind/db.root' · 引号(QUOTE),比如文件名两边的 · 左花括号(OBRA
14、CE),'' · 右花括号(EBRACE),'' · 分号(SEMICOLON),'' 我们修改后的Lex代码如下: %#include <stdio.h>%a-zA-Za-zA-Z0-9* printf("WORD "); /* 字符串 */a-zA-Z0-9/.-+ printf("FILENAME "); /* 文件名 */"
15、 printf("QUOTE "); /* 引号" */ printf("OBRACE "); /* 左花括号 */
16、160; printf("EBRACE "); /* 右花括号 */; printf("SEMICOLON "); /* 分号 */n printf("n");
17、60; /* 换行 */ t+ /* 忽略空白字符 */%int yywrap(void) /* 当词法分析器到了文件末尾做什么 */ return 1; /* 返回1,说明停止前进,0则继续 */void yyerror(char *s) /* 错误信息打印函数 */ fprintf(stderr,
18、 "%sn", s); return 0;int main(int argc, char *argv) FILE *fp; fp = fopen(argv1, "r"); /* 首先打开要被处理的文件(参数1) */ yyin = fp;
19、60;/* yyin是lex默认的文件输入指针,则不处理控制台输入 */ yylex(); /* 调用lex的工作函数,从这里开始,lex的词法分析器开始工作 */ fclose(fp); return 0;到 这里,我们已经完全可以对一个包含各种类型词组的源代码文件进行分析,得出其中各类型词组的排列顺序。在一个
20、规范的基于语法的源代码中,词组的顺序从一定 意义上来说,就是语法。对于源代码,lex的处理能力也就到这里为止,但是我们并没有完全展示lex的所有功能,读者如果有兴趣,可以继续深入阅读本文提 供的参考文献。如何进行语法分析?我们在下面的章节中讲开始讲述Bison的基本使用方法,以及如何使用Bison进行语法分析。 3.Bison入门3.1.基础理论Bison 采用与YACC相同的描述语言,这种语言是BNF语法(Backus Naur Form)的一种,最早被John Backus和Peter Naur用于ALGOL60语言的开发工作。BNF语法可以用来表达与内容无关的,基于语法的语言,几乎所有的
21、现代编程语言都可以用BNF进行描述。作为 一个例子,一个进行加法和乘法的数学表达式语法可以如下表达: E : E '+' EE : E '*' EE : id在这三句中,E在Bison语言中代表表达式,一个表达式可以是一个数字,也可以是一个变量名称,也可以是几个变量名称的组合,比如: 11+2aa+b*c而以上三句Bison语言就能够表达这些语法结构。 3.2.Bison初探我们在下面作一个计算器,通过这个实例让大家明白Flex和Bison的相互关系和如何共同工作。首先,建立test2ll.l文件,并输入以下内容: /* 计算器的词法分析器描述,Flex语言 *
22、/% /* 直接翻译为C语言 */#include <stdlib.h> /* 包含标准库文件 */void yyerror(char *); /* 这是我们上面用到的错误报告函数 */#include "test2yy.h" /* 这个头文件由Bison自动生成 */%0-9+
23、; yylval = atoi(yytext); /* yytext是flex内部用于指向当前词汇的字符串指针 */ return INTEGER; /* INTEGER是从test2yy.h中包含过来的,在Bison中定义 */
24、 -+n return *yytext; t ; /* 跳过空白字符 */. yyerror("invalid character"); /* 产生一个错误,说明是无效字符 */%int yywrap(void) return 1; /* 文件结束时退出 */以
25、 上就是计算器使用的Flex语言,描述了我们将会遇到的各种词汇的格式,比如0-9+说明了,只有连续的从'0'到'9'的字符串,才被分析为 INTEGER类型,如果遇到制表符、空格等,直接忽略,遇到加减符号则返回字符指针,其它的则报告语法错误。下面是我们的Bison语法文件 test2yy.y: /* 注:您先抄写,注解见下文 */%#include <stdio.h>int yylex(void);void yyerror(char *);%token INTEGER
26、 /* Flex语言中INTEGER定义在此 */%program: program expr 'n' printf("%dn", $2); | ;expr: INTEGER
27、160; $ = $1; | expr '+' expr $ = $1 + $3; /* (1) */ | expr '-' expr $ = $1 - $3; /* (2) */ ;%void yyerror(char *s)
28、 fprintf(stderr, "%sn", s);int main(void) yyparse(); return 0;编译过程: flex -otest2ll.c test2ll.lbison -otest2yy.c test2yy.y -d # 注意-d,用于产生对应的头文件gcc test2yy.c test2ll.c -o test2在 这个例子中,我们遇到了许多没有见过的用法,Bison的书写格式基本与Flex的相同,只是规则的定义语法不同。其中,$N(N为数字)代表语
29、法描述 中,第N个词汇的内容,比如(1)中的$1代表'+'左边的expr,$3代表右边expr的内容,其中的N是指当前语法的词汇顺序,从1开始计数。 而$则代表这一段语法处理完后的结果,每一个语法对应的处理都有输入$N和输出$。 该例子中还有一个特殊的地方,就是第归调用,在Bison中,语法规则可以是第归的,这也是Bison之处(或者说是YACC的强大之处)。举个例子: program: program expr 'n' printf("%dn", $2); &
30、#160; | ;这里,program的定义就是任何符合program的表达式后面紧接着expr和'n',扫描到该表达式后,将expr处理的结果打印到屏幕。最后的|是“或者”的意思,也就是说program也可以是空的,什么都不写,分号代表该语义定义结束。 有了第归之后,Bison才可以说是一个能够应对任何状况的语法分析器,当然,这里还需要读者对以上所提供代码进行深入的研究和分析,考虑清楚后,会发现无论是C语言,还是Bison,第归永远是一个神奇的解决方案。 3.3.计算器程序的深入研
31、究以上我们设计的计算器只能进行加减计算,并且里面还有一些软件缺陷,对于一个实用的计算器来说,我们必须能够支持: 1. 变量定义 2. 变量赋值 3. 变量与数字立即处理 于是我们假想设计出来的计算器能有如下的操作过程(输入和输出): 输入:3 * (4 + 5)结果:27输入:x = 3 * (4 + 5)输入:y = 5输入:x结果:27输入:y结果:5输入:x + 2 * y结果:37这样看,这个计算器的功能已经相当强大了,下面我们着手实现,首先修改上面例子的Flex文件为如下: % #include <stdlib.h> v
32、oid yyerror(char *); #include "test2yy.h"%a-z /* 变量类型词汇,限制:变量只能用一个字符 */ yylval = *yytext -
33、'a' return VARIABLE; 0-9+ /* 整数 */
34、0; yylval = atoi(yytext); return INTEGER; +-()=/*n return *yytext; /* 数学计算符号 */ t
35、 /* 跳过空白符号 */%int yywrap(void) return 1;下面是我们新的Bison文件: %token INTEGER VARIABLE%left '+' '-'%left '*' '/'% void yyerror(char *); int yylex(void);
36、160; int sym26;%program: program statement 'n' | ;statement: expr printf("%dn", $1); | VARIABLE '=' expr
37、; sym$1 = $3; ;expr: INTEGER | VARIABLE $ = sym$1; | expr '+' expr $ = $1 + $3; | expr '-' expr &
38、#160; $ = $1 - $3; | expr '*' expr $ = $1 * $3; | expr '/' expr $ = $1 / $3; | '(' expr ')' &
39、#160; $ = $2; ;%void yyerror(char *s) fprintf(stderr, "%sn", s); return 0;int main(void) yyparse(); return 0;现 在我们对该例子中引入的新功能介绍,%left,%right,%token都是用来声明词汇的,区别在于,%token声明的词汇与左右优先级无关, 而%left的处理时,先处理左边的,%right先处理右边的,例如遇
40、到字符串"1 - 2 - 5",到底是处理为"(1 - 2) - 5",还是处理为"1 - (2 - 5)"?%left处理为前者,而%right处理为后者(注:第一个计算器代码中就有这个缺陷,比如执行1-2+3,得到的结果就是-4,作为一个练 习,读者可以使用这里讲解的%left自行更正)。 4.Flex和Bison高级应用在下面的例子中,我们便写了一个更高级的计算器程序,其操作实例如下: x = 0;while(x < 3) print(x); x = x + 1;例子中
41、得到的输出结果如下: 012首先是我们的全局头文件test3.h: typedef enum typeCon, typeId, typeOpr nodeEnum;/* constants */typedef struct int value; /* value of constant */ conNodeType;/* identifie
42、rs */typedef struct int i; /* subscript to sym array */ idNodeType;/* operators */typedef struct int oper; /* operator */ int nops; /* number
43、 of operands */ struct nodeTypeTag *op1; /* operands (expandable) */ oprNodeType;typedef struct nodeTypeTag nodeEnum type; /* type of node */ /* union must be last entry in nodeType */ &
44、#160; /* because operNodeType may dynamically increase */ union conNodeType con; /* constants */ idNodeType id;
45、160; /* identifiers */ oprNodeType opr; /* operators */ ; nodeType;extern int sym26;下面是Flex语言文件test3ll.l: %#include <stdlib.h>#include "test3.h"#include &q
46、uot;test3yy.h"void yyerror(char *);%a-z yylval.sIndex = *yytext - 'a' return VARIABLE
47、; 0-9+ yylval.iValue = atoi(yytext); return
48、INTEGER; -()<>=+*/;. return *yytext; ">=" retu
49、rn GE;"<=" return LE;"=" return EQ;"!=" return NE;"while" return WHILE;"if"
50、 return IF;"else" return ELSE;"print" return PRINT; tn+ /* ignore whitespace */. yyerror("Unkno
51、wn character");%int yywrap(void) return 1;然后是我们的Bison文件test3yy.y: %#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include "test3.h"/* prototypes */nodeType *opr(int oper, int nops, .);nodeType *id(int i);nodeType *con(int value);v
52、oid freeNode(nodeType *p);int yylex(void);void yyerror(char *s);int sym26;%union int iValue; /* integer value */ char sIndex; /* symbol table index */ nodeType *nPtr; /* node pointer */;%toke
53、n <iValue> INTEGER%token <sIndex> VARIABLE%token WHILE IF PRINT%nonassoc IFX%nonassoc ELSE%left GE LE EQ NE '>' '<'%left '+' '-'%left '*' '/'%nonassoc UMINUS%type <nPtr> stmt expr stmt_list%program: fu
54、nction exit(0); ;function: function stmt ex($2); freeNode($2); |
55、 ;stmt: '' $ = opr('', 2, NULL, NULL); | expr ''
56、0; $ = $1; | PRINT expr '' $ = opr(PRINT, 1, $2); | VARIABLE '=' expr '' $ = opr('=', 2, id($1), $3);
57、; | WHILE '(' expr ')' stmt $ = opr(WHILE, 2, $3, $5); | IF '(' expr ')' stmt %prec IFX $ = opr(IF, 2, $3, $5); | IF '(' expr ')' stmt ELSE stmt $ = opr(I
58、F, 3, $3, $5, $7); | '' stmt_list '' $ = $2; ;stmt_list: stmt
59、 $ = $1; | stmt_list stmt $ = opr('', 2, $1, $2); ;expr: INTEGER $ =
60、con($1); | VARIABLE $ = id($1); | '-' expr %prec UMINUS $ = opr(UMINUS, 1, $2); | expr '+' expr
61、 $ = opr('+', 2, $1, $3); | expr '-' expr $ = opr('-', 2, $1, $3); | expr '*' expr
62、60; $ = opr('*', 2, $1, $3); | expr '/' expr $ = opr('/', 2, $1, $3); | expr '<' expr
63、0; $ = opr('<', 2, $1, $3); | expr '>' expr $ = opr('>', 2, $1, $3); | expr GE expr
64、160; $ = opr(GE , 2, $1, $3); | expr LE expr $ = opr(LE , 2, $1, $3); | expr NE expr $ = opr(NE , 2, $1, $3);
65、60; | expr EQ expr $ = opr(EQ , 2, $1, $3); | '(' expr ')' $ = $2; ;%#define SIZEO
66、F_NODETYPE (char*)&p->con - (char*)p)nodeType *con(int value) nodeType *p; size_t nodeSize; /* allocate node */ nodeSize = SIZEOF_NODETYPE + sizeof(conNodeType);
67、160; if(p = malloc(nodeSize) = NULL) yyerror("out of memory"); /* copy information */ p->type = typeCon; p->con.value = value; &
68、#160; return p;nodeType *id(int i) nodeType *p; size_t nodeSize; /* allocate node */ nodeSize = SIZEOF_NODETYPE + sizeof(idNodeType); if(p = malloc(nodeSize) = NULL
69、) yyerror("out of memory"); /* copy information */ p->type = typeId; p->id.i = i; return p;nodeType *opr(int oper, int no
70、ps, .) va_list ap; nodeType *p; size_t nodeSize; int i; /* allocate node */ nodeSize = SIZEOF_NODETYPE + sizeof(oprNodeType) +
71、0; (nops - 1) * sizeof(nodeType*); if(p = malloc(nodeSize) = NULL) yyerror("out of memory"); /* copy information */
72、60; p->type = typeOpr; p->opr.oper = oper; p->opr.nops = nops; va_start(ap, nops); for(i = 0; i < nops; i+) p->opr.opi = v
73、a_arg(ap, nodeType*); va_end(ap); return p;void freeNode(nodeType *p) int i; if(!p) return; if(p->type = typeOpr)
74、; for(i=0; i<p->opr.nops; i+) freeNode(p->opr.opi); free(p);void yyerror(char *s) fprintf(stdout, "%sn", s);
75、int main(void) yyparse(); return 0;上面的Flex和Bison代码所作的工作是根据语法建立语意描述树结构。延续我们上面计算器的例子,我们写出如何翻译这些语言的实现部分(对生成的树进行第归分析): #include <stdio.h>#include "test3.h"#include "test3yy.h"int ex(nodeType *p)
76、if(!p) return 0; switch(p->type) case typeCon: return p->con.value; case typeId: return symp->id.i;
77、 case typeOpr: switch(p->opr.oper) case WHILE: while(ex(p->opr.op0)
78、 ex(p->opr.op1); return 0; case IF:
79、; if(ex(p->opr.op0) ex(p->opr.op1); e
80、lse if(p->opr.nops > 2) ex(p->opr.op2); return 0;
81、 case PRINT: printf("%dn", ex(p->opr.op0); return 0; case &
82、#39;': ex(p->opr.op0); return ex(p->opr.op1); case '=':
83、 return symp->opr.op0->id.i = ex(p->opr.op1); case UMINUS: return -ex(p->
84、opr.op0); case '+': return ex(p->opr.op0) + ex(p->opr.op1); case '-':
85、160; return ex(p->opr.op0) - ex(p->opr.op1); case '*': return ex(p->opr.op0) * ex(
86、p->opr.op1); case '/': return ex(p->opr.op0) / ex(p->opr.op1); case '<':
87、 return ex(p->opr.op0) < ex(p->opr.op1); case '>': return ex(p->
88、;opr.op0) > ex(p->opr.op1); case GE: return ex(p->opr.op0) >= ex(p->opr.op1); case LE:
89、 return ex(p->opr.op0) <= ex(p->opr.op1); case NE: return ex(p->opr.op0)
90、!= ex(p->opr.op1); case EQ: return ex(p->opr.op0) = ex(p->opr.op1);
91、160; 一般实际的编译器都是以汇编代码输出的,所以我们在这里进行一些深入研究,得出了另一个版本的ex函数实现,能够实现汇编代码的输出(compiler.c): #include <stdio.h>#include "test3.h"#include "test3yy.h"static int lbl;int ex(nodeType *p)
92、0; int lbl1, lbl2; if(!p) return 0; switch(p->type) case typeCon: printf("tpusht%dn", p->con.value);
93、; break; case typeId: printf("tpusht%cn", p->id.i + 'a'); break; case typeOpr:
94、160; switch(p->opr.oper) case WHILE: printf("L%03d:n", lbl1 = lbl+);
95、60; ex(p->opr.op0); printf("tjstL%03dn", lbl2 = lbl+); ex(p->opr.op1);
96、 printf("tjztL%03dn", lbl1); printf("L%03d:n", lbl2); brea
97、k; case IF: ex(p->opr.op0); if(p->opr.nops > 2) &
98、#160; /* if else */ printf("tjstL%03dn", lbl1 = lbl+);
99、 ex(p->opr.op1); printf("tjmptL%03dn", lbl2 = lbl+);
100、; printf("L%03d:n", lbl1); ex(p->opr.op2); printf("L%03d:n", lbl2); else
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 45850.1-2025包装袋试验条件第1部分:纸袋
- 汉字构成方式课件
- 2025年内蒙古巴彦淖尔市乌拉特前旗第三中学九年级下学期学业水平考试模拟(三模)化学试卷(含答案)
- 汉字听写大赛课件封面
- “职”引未来知到智慧树答案
- 春节放假的通知模板集合四篇
- 2025湖南省国内旅游合同
- 军事理论-综合版知到智慧树答案
- 水龙吟课件苏轼
- Ubuntu Linux 22.04系统管理与服务器配置 课件 项目5 配置常规网络与使用远程服务
- 2024年重庆永川区招聘社区工作者后备人选笔试真题
- 医学技术专业讲解
- 唯奋斗最青春+课件-2026届跨入高三第一课主题班会
- 2025民办中学教师劳务合同模板
- 2025年南康面试题目及答案
- 2025年事业单位考试贵州省毕节地区纳雍县《公共基础知识》考前冲刺试题含解析
- 高中喀斯特地貌说课课件
- 黄冈初一上数学试卷
- 2025年中国花盆人参行业市场发展前景及发展趋势与投资战略研究报告
- QGDW11337-2023输变电工程工程量清单计价规范
- 留疆战士考试试题及答案
评论
0/150
提交评论