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
任何建议,问题,欢迎:[email protected] 
|