摘要: 本文通过制作一个简单的命令行计算器来学习flex/bison编译工具的使用。
1. 目标
- 支持命令行输入表达式进行计算
- 支持整数加减乘除四则运算
- 支持绝对值运算
- 支持括号运算
2. 使用工具
- flex
- bison
- gcc
3. 源码
3.1 calc.l
%{
#include "calc.tab.h"
extern void yyerror(char* msg, ...);
%}
%%
" + " { printf("ADD\n"); return ADD; }
" - " { printf("SUB\n"); return SUB; }
" * " { printf("MUL\n"); return MUL; }
" / " { printf("DIV\n"); return DIV; }
"|" { printf("ABS\n"); return ABS; }
[-+][0-9]+ { yylval = atoi(yytext); printf("NUMBER %d\n", yylval); return NUMBER; }
[0-9]+ { yylval = atoi(yytext); printf("NUMBER %d\n", yylval); return NUMBER; }
\n { printf("EOL\n"); return EOL; }
[ \t] { printf("SPA\n"); }
"(" { printf("LFT\n"); return LB; }
")" { printf("RGT\n"); return RB; }
. { yyerror("invalid character: %c!", *yytext); }
%%
3.2 calc.y
%{
#include <stdio.h>
#include "calc.tab.h"
extern int yylex(void);
extern void yyerror(char* msg);
%}
%token NUMBER
%token ADD SUB MUL DIV ABS LB RB
%token EOL
%%
prog: exp EOL { printf("==> RESULT = %d\n",$1); }
;
exp: exp ADD term { $$ = $1 + $3; printf("==> %d + %d = %d \n", $1, $3, $$); }
| exp SUB term { $$ = $1 - $3; printf("==> %d - %d = %d \n", $1, $3, $$); }
| term;
term: term MUL factor { $$ = $1 * $3; printf("==> %d * %d = %d \n", $1, $3, $$); }
| term DIV factor { $$ = $1 / $3; printf("==> %d / %d = %d \n", $1, $3, $$); }
| factor
factor: NUMBER { $$ = $1; }
| ABS exp ABS { $$ = $2 >= 0 ? $2: -$2; }
| LB exp RB { $$ = $2; }
;
%%
int main(int argc,char **argv)
{
printf("Please enter a mathematics expression: ");
yyparse();
return 0;
}
void yyerror(char *s)
{
fprintf(stderr,"error: %s\n",s);
}
3.3 makefile
cat makefile
calc : calc.tab.c calc.yy.c
gcc -o calc calc.tab.c calc.yy.c -lfl
calc.tab.c : calc.y
bison -d calc.y
calc.yy.c : calc.l
flex -o calc.yy.c calc.l
4. 测试
命令行输入:./calc
测试用例1:
Please enter a mathematics expression: 1 + 2 * 3 + 4 * | 5 - 78| * 3
NUMBER 1
ADD
NUMBER 2
MUL
NUMBER 3
==> 2 * 3 = 6
ADD
==> 1 + 6 = 7
NUMBER 4
MUL
ABS
SPA
NUMBER 5
SUB
NUMBER 78
ABS
==> 5 - 78 = -73
==> 4 * 73 = 292
MUL
NUMBER 3
==> 292 * 3 = 876
EOL
==> 7 + 876 = 883
==> RESULT = 883
测试用例2:
1 - 2 * 3 / (1 + 2) + (1 - 2 + (|3 - 4| * 3))
NUMBER 1
SUB
NUMBER 2
MUL
NUMBER 3
==> 2 * 3 = 6
DIV
LFT
NUMBER 1
ADD
NUMBER 2
RGT
==> 1 + 2 = 3
==> 6 / 3 = 2
ADD
==> 1 - 2 = -1
LFT
NUMBER 1
SUB
NUMBER 2
ADD
==> 1 - 2 = -1
LFT
ABS
NUMBER 3
SUB
NUMBER 4
ABS
==> 3 - 4 = -1
MUL
NUMBER 3
==> 1 * 3 = 3
RGT
RGT
==> -1 + 3 = 2
EOL
==> -1 + 2 = 1
==> RESULT = 1