【编译原理】Flex/Bison实现计算器

Flex/Bison实现计算器

Flex、Bison介绍

Flex介绍可以看一看下面这篇,有详细介绍:
Flex介绍,使用
Bison 基本上与 Yacc 兼容,并且在 Yacc 之上进行了改进。它经常和 Flex (词法分析器生成器)一起使用。Bison的文件以.y为后缀,生成的文件为 xxx.tab.c和xxx.tab.h

源程序结构

声明部分
%%
翻译规则
%%
辅助性C语言例程

声明部分

  • %{}%声明
    主要包括规则部分的语义过程和过程部分所需的头文件的引用说明、数据定义、全局变量以及函数原型定义等。
  • %声明
    对词法单元的声明,eg:%token DIGIT
    用户可以使用%left%right来指明其后面的运算符具有相同的优先级和结合性规则。

翻译规则

<产生式头>→<产生体>1|<产生体>2|……|<产生体>n

另一种写法

<产生式头>:
<产生体>1 {<语义动作>1}
|<产生体>2 {<语义动作>2}
……
|<产生体>n {<语义动作>n}

eg:

line: expr ‘\n’ {printf("%lf",$1);}

$$表示左部语法变量的属性值,$i表示右边第i个文法符号的值,语义动作是根据$i计算$$的值

辅助性C语言例程

使用Dev-C++编译运行

  • calc.l
%{
    #include<stdlib.h>
    void yyerror(char*);
    #include "calc.tab.h"  
%}

%%
\.?[0-9]+|[0-9]+\.[0-9]*	{yylval=atof(yytext);return INTEGER;}
[-+()/*,\n]	{return *yytext;}
[ \t]    ;

.    	 yyerror("Error");

%%
int yywrap(void)
{
  return 1;
}

  • calc.y
%token	INTEGER
%left	'+' '-'
%left   '*' '/'
%{
	#define YYSTYPE double
	#include<stdio.h>
    #include<stdlib.h>
	#include<math.h>
	void yyerror(char*);
    int yylex(void);
%}

%%

program:
    program expr '\n'	{printf("%lf\n",$2);}
	|
    ;
expr:
    INTEGER
	|expr '+' expr	{$$ = $1 + $3;}
    |expr '-' expr	{$$ = $1 - $3;}
    |expr '*' expr	{$$ = $1 * $3;}
    |expr '/' expr	{$$ = $1 / $3;}
	|'('expr')'     {$$ = $2;}
	;
    
%%

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


int main(void)
{
	printf("A simple calculator:\n");
    yyparse();
    return 0;
}

  • 编译运行

1、在Flex、Bison安装目录下使用cmd。执行:

win_flex -ocalc.c calc.l
win_bison -ocalc.tab.h calc.y  //注意-o后面没有空格

2、编译calc.c
使用Dev-C++打开calc.c,编译运行就可以了哦。
在这里插入图片描述

使用MinGW在cmd运行

  • MinGw的安装

MinGw的安装在这里就不一一赘述了,一路next,安装成功后,记得配置环境变量,一定需要配置。

这里的calc.lcalc.y与之前的是一样的。下面是编译运行:

win_flex  calc.l
win_bison -o calc.tab.h calc.y  //注意-o后面有没有空格都可以
gcc -o calc lex.yy.c calc.tab.h  //编译
calc		//运行calc.exe

在这里插入图片描述

源码

  • calc.l
%{
	
	#include "calc.tab.h"
    #define YYSTYPE double
	void yyerror(char*);
	int yywrap();
%}

%%
[a-z] {yylval = *yytext - 'a';return VARIABLE;}
\.?[0-9]+|[0-9]+\.[0-9]* {yylval = atof(yytext);return INTEGER;}
[-+()=/*&|~!^,@\n] {return *yytext;}
sin {return SIN;}
cos {return COS;}
tan {return TAN;}

sqrt {return SQRT;}
log	{return LOG;}
pow {return POW;}
[ \t] ;

.	yyerror("error");

%%
int yywrap() {
	return 1;
}

  • calc.y
%token  INTEGER VARIABLE SIN COS TAN POW SQRT LOG
%left   '+' '-'
%left   '*' '/'
%left   '&'
%left   '|'
%left   '^'
%right  '@''~'
%left   '!'
%right	UMINUS

%{
	#include<stdio.h>   
    #include<math.h>
    #define YYSTYPE double
    #define pi 3.1415926 
	void yyerror(char*);
    int yylex(void);
    double sym[26];
%}

%%
program:
    program statement '\n'
    |
    ;
statement:
     expr    {printf("%g\n", $1);}
     |VARIABLE '=' expr    {sym[(int)$1] = $3;}
     ;
expr:
    INTEGER
    |VARIABLE	{$$ = sym[(int)$1];}
    |expr '+' expr    {$$ = $1 + $3;}
    |expr '-' expr    {$$ = $1 - $3;}
    |expr '*' expr    {$$ = $1 * $3;}
    |expr '/' expr    {$$ = $1 / $3;}
    |expr '&' expr    {$$ = (int)$1 & (int)$3;}
    |expr '|' expr    {$$ = (int)$1 | (int)$3;}
    |'~' expr         {$$ = ~(int)$2;}
    |'@' expr         {$$ = sqrt($2);}
    |expr '@' expr    {$$ = $1*sqrt($3);}
    |expr '!'         {int i=1,s=1;for(;i<=$2;i++)s*=i;$$=s;}
	|'-' expr %prec UMINUS {$$ = -$2;} 
	|POW'('expr','expr')'      {$$ = pow($3,$5);}
    |SIN'('expr')'       {$$ = sin($3*pi/180.0);}
    |COS'('expr')'       {$$ = cos($3*pi/180.0);}
    |TAN'('expr')'       {$$ = tan($3*pi/180.0);}
	|SQRT'('expr')'		{$$ = sqrt($3);}
	|LOG'('expr')'		{$$ = log10($3);}
	|'('expr')'       {$$ = $2;}
    ;
	
%%

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

int main(void) {
    yyparse();
    return 0;
}

几个小说明

1、undefined reference to ‘yylex’、In function ‘main’、WinMain
可以参考:yylex
2、lex.yy.c:(.rdata$.refptr.yylval[.refptr.yylval]+0x0): undefined reference to ‘yylval’
未定义yylval、编译运行不对,修改编译

win_flex -ocalc.c calc.l
win_bison -ocalc.tab.h calc.y  //注意-o后面没有空格,calc为文件名

3、undefined reference to ‘yywrap’
可以在前面加上%option noyywrap,或者直接写上

int yywrap(void)
{
  return 1;
}

4、有可能在本地有输出,但在在线平台没有输出,将calc.y中输出部分改成下面这样

program:
    program statement '\n'
    |
    ;
statement:
    expr    {printf("%lf\n", $1);}
    |
    ;

5、输出时整数小数区分,以及小数后面多余0的处理

取整判断小数与整数,取余去掉多余的0
不过通过多次尝试,然后发现C语言提供了简便方法,哈哈哈

如果对你有帮助,点个赞呗。

参考文献

【1】《编译原理》(龙书)
【2】《编译原理》第二版 蒋宗礼、姜守旭

  • 15
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值