用Lex(flex)和yacc(bison)寫的簡單計算器

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]

發佈了35 篇原創文章 · 獲贊 0 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章