用Lex(flex)和yacc(bison)写的简单计算器

类别:编程语言 点击:0 评论:0 推荐:
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]

本文地址:http://com.8s8s.com/it/it24318.htm