Lex文件如下:
%{
#include "cal.tab.h"
%}
%option noyywrap
integer [0-9]+
dreal ([0-9]*"."[0-9]+)
ereal ([0-9]*"."[0-9]+[EedD][+-]?[0-9]+)
real {dreal}|{ereal}
nl \n
plus "+"
minus "-"
times "*"
divide "/"
lp "("
rp ")"
module "%"
power "^"
%%
[ \t] ; /*skip any blanks */
{integer} { sscanf(yytext, "%d", &yylval.integer);
return INTEGER;
}
{real} { sscanf(yytext, "%lf", &yylval.real);/*yylval = atof(yytext); it doesn't work under MSVSC*/
return REAL;
}
{plus} { return PLUS;}
{minus} { return MINUS;}
{times} { return TIMES;}
{divide} { return DIVIDE;}
{module} { return MODULE;}
{power} { return POWER;}
{lp} { return LP;}
{rp} { return RP;}
{nl} { return NL;}
. { return yytext[0];}
以上是Lex文件的代码(cal.l),lex是用来得到token。
有了token之后呢,就用yacc(本人用的是GNU的可以在windows下面运行的bison)才处理这些符号。也就是写出一个个的状态,最后得到分析结果。
下面是yacc文件的代码(cal.y):
%{
#include <stdio.h>
#include <math.h>
%}
%union{ double real; /* real value */
int integer; /* integer value */
}
%token <real> REAL
%token <integer> INTEGER
%start lines
%token NUMBER NL
%token PLUS MINUS TIMES DIVIDE MODULE POWER LP RP
%type <real> rexpr
%type <integer> iexpr
%left PLUS MINUS /*left associative */
%left TIMES DIVIDE MODULE /*left associative */
%left POWER
%left UNARYMINUS
%%
lines: /* nothing */
| lines line NL
| lines error NL
{ yyerror();yyerrok; }
;
line : iexpr
{printf("%d\n",$1);}
| rexpr
{printf("%lf\n",$1);}
;
iexpr: INTEGER
{ $$ = $1; }
| iexpr PLUS iexpr
{ $$ = $1 + $3;}
| iexpr MINUS iexpr
{ $$ = $1 - $3;}
| iexpr TIMES iexpr
{ $$ = $1 * $3;}
| iexpr DIVIDE iexpr
{ if($3)
$$ = $1 / $3;
else
{
$$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);
}
}
| iexpr MODULE iexpr
{ $$ = $1 % $3; }
| iexpr POWER iexpr
{ $$ = pow($1, $3);}
| MINUS iexpr %prec UNARYMINUS
{ $$ = - $2;}
| LP iexpr RP
{ $$ = $2;}
| LP iexpr error
{ $$ = $2; yyerror("missing ')'"); yyerrok;}
| PLUS iexpr %prec UNARYMINUS
{ $$ = $2;}
;
rexpr :REAL
{ $$ = $1; }
| rexpr PLUS rexpr
{ $$ = $1 + $3; }
| rexpr MINUS rexpr
{ $$ = $1 - $3; }
| rexpr TIMES rexpr
{ $$ = $1 * $3; }
| rexpr DIVIDE rexpr
{
if ($3)
$$ = $1 / $3;
else
{
$$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);
}
}
| rexpr POWER rexpr
{ $$ = pow($1,$3); }
| LP rexpr RP
{ $$ = $2; }
| LP rexpr error
{ $$ = $2; yyerror("missing ')'"); yyerrok;}
| MINUS rexpr %prec UNARYMINUS
{ $$ = -$2; }
| PLUS rexpr %prec UNARYMINUS
{ $$ = $2;}
| iexpr PLUS rexpr
{ $$ = (double)$1 + $3;}
| iexpr MINUS rexpr
{ $$ = (double)$1 - $3;}
| iexpr TIMES rexpr
{ $$ = (double)$1 * $3;}
| iexpr DIVIDE rexpr
{ if($3)
$$ = (double)$1 / $3;
else
{ $$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);
}
}
| iexpr POWER rexpr
{ $$ = pow((double)$1,$3); }
| rexpr PLUS iexpr
{ $$ = $1 + (double)$3;}
| rexpr MINUS iexpr
{ $$ = $1 - (double)$3;}
| rexpr TIMES iexpr
{ $$ = $1 * (double)$3;}
| rexpr DIVIDE iexpr
{ if($3)
$$ = $1 / (double)$3;
else
{ $$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);
}
}
| rexpr POWER iexpr
{ $$ = pow($1,(double)$3); }
;
%%
void main()
{
yyparse();
}
int yyerror(char* msg)
{
printf("Error: %s encountered \n", msg);
}
这样一个支持+,-,×,/,^,以及括号运算的计算器就做成了。所用时间不会超过半个小时,如果用c,或c++写个算符优先文法的话可是一个不小的工程。由此可见lex和yacc的魅力了!
编译命令是:fLex cal.l
bison -d -v cal.y
pause
任何建议,问题,欢迎:buaa_chenx@hotmail.com