自制编译器学习3:Flex和Bison简介

Flex和Bison安装

Ubuntu环境下直接安装即可:

apt-get install flex bison

第一个Flex程序

首先给出代码(fb1-1.l)

/* Companion source code for "flex & bison", published by O'Reilly
 * Media, ISBN 978-0-596-15597-1
 * Copyright (c) 2009, Taughannock Networks. All rights reserved.
 * See the README file for license conditions and contact info.
 * $Header: /home/johnl/flnb/code/RCS/fb1-1.l,v 2.1 2009/11/08 02:53:18 johnl Exp $
 */

/* fb1-1 just like unix wc */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}

%%

[a-zA-Z]+	{ words++; chars += strlen(yytext); }
\n		{ chars++; lines++; }
.		{ chars++; }

%%

main()
{
  yylex();
  printf("%8d%8d%8d\n", lines, words, chars);
}

flex程序分为3部分,每一部分通过符号%%来分割,第一部分包含声明和选项设置,第二部分是一系列的模式和动作,第三部分是会被拷贝到词法生成器里面的C代码。

第一部分,%{%}之间的代码会被照抄到生成C文件的头部,可以看到在这个实例中只是定义了3个整型变量。

第二部分,每一个模式匹配位于一行的开始,接着是匹配时要执行的的C代码,C代码为{}之间的一行或者多行代码。[a-zA-Z]+用来匹配一个单词,大括号内表示可以匹配任意大小写字母,+表示匹配一个或多个前面的字符,即一串字母或者一个单词,flex中,yytext总是被指定为本次匹配的输入文本,.表示匹配任意一个字符。

第三部分,主程序,调用flex的词法分析例程,并输出结果。

执行flex

flex -o fb1-1.c fb1-1.l

词法生成器翻译成c程序,c文件为:fb1-1.c

使用gcc编译生成可执行文件:

gcc fb1-1.c -lfl -o fb1-1

注意编译时需要链接库(-lfl),执行该程序,输入3个字符串后按ctrl+d退出,输出结果如下:

 flex和bison协同工作

来看第二段代码:

/* Companion source code for "flex & bison", published by O'Reilly
 * Media, ISBN 978-0-596-15597-1
 * Copyright (c) 2009, Taughannock Networks. All rights reserved.
 * See the README file for license conditions and contact info.
 * $Header: /home/johnl/flnb/code/RCS/fb1-3.l,v 2.1 2009/11/08 02:53:18 johnl Exp $
 */

/* recognize tokens for the calculator and print them out */

%%
"+"	{ printf("PLUS\n"); }
"-"	{ printf("MINUS\n"); }
"*"	{ printf("TIMES\n"); }
"/"	{ printf("DIVIDE\n"); }
"|"     { printf("ABS\n"); }
[0-9]+	{ printf("NUMBER %s\n", yytext); }
\n      { printf("NEWLINE\n"); }
[ \t] { }
.	{ printf("Mystery character %s\n", yytext); }
%%

这里只给出了模式匹配,flex的lfl库提供了极小的主程序来调用词法分析器,这里已经足够,词法分析器的生成和编译与之前相同,生成可执行词法分析程序,执行后输入12+34和13/34做词法分析,执行结果如下:

考虑一个更完成的计算器词分析器:

/* Companion source code for "flex & bison", published by O'Reilly
 * Media, ISBN 978-0-596-15597-1
 * Copyright (c) 2009, Taughannock Networks. All rights reserved.
 * See the README file for license conditions and contact info.
 * $Header: /home/johnl/flnb/code/RCS/fb1-4.l,v 2.1 2009/11/08 02:53:18 johnl Exp $
 */

/* recognize tokens for the calculator and print them out */

%{
   enum yytokentype {
     NUMBER = 258,
     ADD = 259,
     SUB = 260,
     MUL = 261,
     DIV = 262,
     ABS = 263,
     EOL = 264 /* end of line */
   };

   int yylval;

%}

%%
"+"	{ return ADD; }
"-"	{ return SUB; }
"*"	{ return MUL; }
"/"	{ return DIV; }
"|"     { return ABS; }
[0-9]+	{ yylval = atoi(yytext); return NUMBER; }
\n      { return EOL; }
[ \t]   { /* ignore white space */ }
.	{ printf("Mystery character %c\n", *yytext); }
%%
main()
{
  int tok;

  while(tok = yylex()) {
    printf("%d", tok);
    if(tok == NUMBER) printf(" = %d\n", yylval);
    else printf("\n");
  }
}

输出结果如下:

 我们来分析下代码,flex做词分析返回记号流时,每个记号记号编号(token number)和记号值(token's value)组成,其中记号编号为一个小的整数,bison自动从258开始编号,并且创建一个编号定义的.h文件,这里为了方便理解通过enum进行了手工定义。主程序调用yylex()返回记号值,对于NUMBER转换为int。

语法分析器

下面基于flex和bison来设计一个基本的语法分析器,bison遵循BNF(BackusNaur Form)语法,

在BNF里,::=(:)是被定义为的意思,|表示其左右两边任选一项, 左边的名称是语法符号(symbol),有效的BNF是具有递归性的,例如处理 1×5+2×3这种简单的算术表达式:

<exp> ::= <factor>
    | <exp> + <factor>
<factor> ::= NUMBER
    | <factor> * NUMBER

exp被定义为一个factor 或者 factor + exp,而facotr被定义为NUMBER或者factor×NUMBER,每个规则最左边是非终结符,冒号右边是非终结符的推导规则,如果推导规则是多个那么通过|隔开。

我们给出bison代码的示例(fb1-5.y):

/* Companion source code for "flex & bison", published by O'Reilly
 * Media, ISBN 978-0-596-15597-1
 * Copyright (c) 2009, Taughannock Networks. All rights reserved.
 * See the README file for license conditions and contact info.
 * $Header: /home/johnl/flnb/code/RCS/fb1-5.y,v 2.1 2009/11/08 02:53:18 johnl Exp $
 */

/* simplest version of calculator */

%{
#  include <stdio.h>
%}

/* declare tokens */
%token NUMBER
%token ADD SUB MUL DIV ABS
%token OP CP
%token EOL

%%

calclist: /* nothing 空规则 从输入开头开始匹配*/  
 | calclist exp EOL { printf("= %d\n> ", $2); } /*EOL代表一个表达式的结束*/
 | calclist EOL { printf("> "); } /* blank line or a comment */
 ;

exp: factor
 | exp ADD exp { $$ = $1 + $3; }
 | exp SUB factor { $$ = $1 - $3; }
 | exp ABS factor { $$ = $1 | $3; }
 ;

factor: term
 | factor MUL term { $$ = $1 * $3; }
 | factor DIV term { $$ = $1 / $3; }
 ;

term: NUMBER
 | ABS term { $$ = $2 >= 0? $2 : - $2; }
 | OP exp CP { $$ = $2; }
 ;
%%
main()
{
  printf("> "); 
  yyparse();
}

yyerror(char *s)
{
  fprintf(stderr, "error: %s\n", s);
}

bison程序同样包含三部分:声明部分,规则匹配部分和C代码部分。

声明部分同样是用%{%}包括且要原样拷贝到C代码,随后%token记号声明,便于告诉bison在语法分析程序中记号的名称,记号通常总是大写,未声明为记号的语法符号必须出现在至少一条规则的左边。

规则部分通过简单的BNF定义规则,bison使用单一的冒号而不是::=,同时由于行间隔并不明显,用分号来表示规则的结束,C的动作代码在每条规则之后用花括号括起来。

我们来详细看下匹配规则,第一个语法符号calclist,第一个匹配是空值,第二个是表达式(exp)+结束符(EOL),第三个是只有结束符的情况。

接下来的语法符号是term->factor->exp,匹配规则:

term: NUMBER 或者 绝对值的 term或者 加括号的 exp;

factor: term 或者 factor和term的乘法(左递归) 或者factor和term(左递归)的除法 或者 绝对值;

exp: factor 或者 exp和exp的加法 或者 exp和factor的乘法(左递归) 或者 exp和factor(左递归)的除法。

显然是由小到大组成的(并不是绝对的,term也会匹配exp),并且优先级越高的越靠前,由于BNF语法具有递归性,这样的顺序可以保证运算优先级的正确性。

目标符号(冒号左边的语法符号)的值在动作代码中用$$代替,右边语法符号的语义值依次为$1 $2 $3 ......。当词法分析器返回记号时,记号值总是存储在yyval中。

联合编译flex和bison程序

flex代码(fb1-5.l)如下:

/* Companion source code for "flex & bison", published by O'Reilly
 * Media, ISBN 978-0-596-15597-1
 * Copyright (c) 2009, Taughannock Networks. All rights reserved.
 * See the README file for license conditions and contact info.
 * $Header: /home/johnl/flnb/code/RCS/fb1-5.l,v 2.1 2009/11/08 02:53:18 johnl Exp $
 */

/* recognize tokens for the calculator and print them out */

%{
# include "fb1-5.tab.h"
%}

%%
"+"	{ return ADD; }
"-"	{ return SUB; }
"*"	{ return MUL; }
"/"	{ return DIV; }
"|"     { return ABS; }
"("     { return OP; }
")"     { return CP; }
[0-9]+	{ yylval = atoi(yytext); return NUMBER; }

\n      { return EOL; }
"//".*  
[ \t]   { /* ignore white space */ }
.	{ yyerror("Mystery character %c\n", *yytext); }
%%

我们专门写一个Makefile文件:

fb1-5:	fb1-5.l fb1-5.y
	bison -d fb1-5.y
	flex -o fb1-5.c fb1-5.l
	gcc -o $@ fb1-5.tab.c fb1-5.c -lfl

clean:
	rm -f fb1-5 \
	fb1-5.c fb1-5.tab.c fb1-5.tab.h

执行

bison -d fb1-5.y

会生成文件:fb1-5.tab.h 和 fb1-5.tab.c,在flex文件中调用该头文件即可以实现联合编译,最后生成可执行程序fb1-5,测试结果如下:

可以看到,由于缺少对负数的支持,所以输入负数会报错。先讲到这里,本节介绍了如何设计最基本的词法分析器和语法分析器,后面会对flex和bison做更详细介绍。

end

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值