flex&bison系列第三章:写一个简单的计算器Calculator

系列文章目录

flex&bison系列第一章:flex Hello World
flex&bison系列第二章:写一个简单的单词统计工具Word Counter
flex&bison系列第三章:写一个简单的计算器Calculator

LLVM系列



前言

在此记录下基于flex与bison写一个简单的计算器程序(Calculator)的过程,以备查阅。

本章的重点是为了初步了解bison的用法。

开发环境的配置请参考第一章 《flex&bison系列第一章:flex Hello World》。

为简单起见,我们只支持加减乘除四种运算,比如“1 + 2”、“1 * 2”等等。从第二章,我们知道可以用简单的正则表达式来识别整数(示例):

[0-9]+

我们可以再进一步,识别负整数(示例):

^[-+][0-9]+

以上主要是词法分析,一般写在flex脚本中。而语法分析,一般写在bison脚本中。

在bison脚本中,我们用(BNF范式)来定义表达式的语法。比如,乘法的定义如下(示例):

%token number

factor: number
      | factor mutiple number { $$ = $1 * $3; }   
      ;

这里的“number”代表一个token,这是词法分析首先要识别的基本单元,即整数。而“factor”仅仅是个单词而已,没有特殊的意义,我们可以简单地把它理解为乘法和除法里的“因素”。factor的写法其实是个递归的写法,相当于C语言的如下写法(示例):

int multiple(int factor, int number)
{
    return factor * number;
}

// factor = number
int factor = 2;

// factor = 2 * 3 = 6
// factor = factor mutiple number
factor = multiple(factor, 3);

// factor = 6 * 4 = 24
// factor = factor mutiple number
factor = multiple(factor, 4);

或者,我们也可以这样直接的写出来(示例):

// factor = 2 * 3 * 4 = 24
// factor = factor mutiple number
factor = multiple(multiple(factor, 3), 4);

一、Simple Calculator

这个Simple Calculator程序要做的事很简单,具体如下:

  1. 利用flex脚本检测各种操作符、整数等token
  2. 利用bison脚本计算出结果
  3. 当检测到回车键时,输出最后结果

首先,我们要写一个简单的flex脚本文件(示例):

/**
 *  simple-calculator.l
 */

%{
    #include "simple-calculator.tab.h"
%}

%option noyywrap

%%

"+"     { return ADD; }
"-"     { return SUB; }
"*"     { return MUL; }
"/"     { return DIV; }

^[-+][0-9]+ { yylval = atoi(yytext); return NUMBER;}
[0-9]+  { yylval = atoi(yytext); return NUMBER;}

\n      {return EOL; }
[ \t]   {} 
.       { printf("Unrecognized character: %s\n", yytext);}

%%

注意到,这里有一些变量没有定义,如:

ADD, SUB, MUL, DIV, NUMBER, EOL

其实,这些变量都是在我们的bison脚本中定义的。bison脚本如下(示例):

/**
 *  simple-calculator.y
 */

%{
    #include <stdio.h>

    extern int yylex();
    void yyerror(const char* s)
    {
        printf("Error: %s\n", s);
    }
%}

%token NUMBER
%token ADD SUB MUL DIV
%token EOL

%%

calclist: /* Empty rule */
    | calclist exp EOL { printf("= %d\n", $2); }
    ;

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

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

%%

int main(int argc, char **argv)
{
    yyparse();

    return 0;
}

注意到这里比较有意思的是,加减法和乘除法分开写的:

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

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

这是为了保证乘除法的优先级比加减法更高。以下写法则达不到“优先运算乘除法”的效果(示例):

exp: NUMBER
   | exp MUL NUMBER { $$ = $1 * $3; }
   | exp DIV NUMBER { $$ = $1 / $3; }     
   | exp ADD NUMBER { $$ = $1 + $3; }
   | exp SUB NUMBER { $$ = $1 - $3; } 
   ;

二、编译

1. 生成C代码

运行如下命令即可生成C代码(示例):

flex -o simple-calculator.yy.c simple-calculator.l
bison -d -o simple-calculator.tab.c simple-calculator.y

其生成的C代码文件名为“simple-calculator.yy.c”、“simple-calculator.tab.c”、“simple-calculator.tab.h”。

2. 编译

我们可以用clang或gcc对以上生成的C代码进行编译(示例):

# Set up C++ standard library and header path for clang
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)

# Build with clang
clang -o simple-calculator simple-calculator.tab.c simple-calculator.yy.c

# Or, build with gcc
gcc -o simple-calculator simple-calculator.tab.c simple-calculator.yy.c

以上命令会生成一个名为“simple-calculator”的可执行程序。

三、运行

可以用如下命令运行Simple Calculator程序(示例):

# Use Enter to print the result
./simple-calculator

接下来,程序会等待用户的输入。我们可以随意打一个整数算式(示例):

-2 + 3 * 4

用回车键即可打印出结果(示例):

= 10

总结

我们基于flex和bison,用C++写了一个很简单的计算器程序,并且编译运行成功。完整源码示例请参看:
https://github.com/wuzhanglin/flex-bison-examples

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值