yacc:区分整数与浮点数

时间:2010-03-02 20:42:22

标签: yacc compiler-theory

我应该编写一个2 + 2 = 4和2.2 + 2 = 4.2的程序。

我已经完成了它,所以它将所有东西视为浮点,但这是“错误的”。我必须区分它们。这是我到目前为止所做的:

%{
#include <stdio.h>
#include <ctype.h>
%}

%token <dval> FLOAT
%token <ival> INTEGER

%union
{
   float dval;
   int ival;
}

%type <dval> command exp term factor

%%

command : exp           {printf("%f\n",$1);}
    ;

exp : exp '+' term      {$$ = $1 + $3;}
    | exp '-' term      {$$ = $1 - $3;}
    | term          {$$ = $1;}
    ;

term    : term '*' factor   {$$ = $1 * $3;}
    | factor        {$$ = $1;}
    ;

factor : '(' exp ')'        {$$ = $2;}
    | FLOAT         {$$ = $1;}
    | INTEGER       {$$ = $1;}
    ;

%% 

int main()
{ 
  return yyparse();
}

int yylex()
{
   int c;
   while( (c=getchar()) == ' ');
   if( isdigit(c) )
   {
      ungetc(c, stdin);
      float f1;
      scanf("%f", &f1);
      int i1 = (int) f1;
      if(f1 == 0)
      {
         yylval.ival = 0;
     return INTEGER;
      }
      else if( (((float) i1) / f1 ) == 1)
      {
     yylval.ival = i1;
         return INTEGER;
      }
      else
      {
     yylval.dval = f1;
     return FLOAT;
      }
      //scanf("%f",&yylval.dval);
      //return(NUMBER);
   }
   if(c == '\n') return 0;
   return c;
}

int yyerror(char *s)
{
   fprintf(stderr,"%s\n",s);
   return 0;
}

我遇到的问题是每个表达式只能有一种类型。现在一切都基本浮动,所以虽然操作是正确的,但这不是正确的解决方案。

我考虑定义更多的表达式,基本上有factor_int和factor_float,然后替换其中的所有内容,但这似乎是错误的。我不知道如何完成这项工作,而且我见过的教程并没有真正帮助我。

3 个答案:

答案 0 :(得分:2)

基本上你可以这样做:

%{
#include <stdio.h>
#include <ctype.h>

struct number
{
  union
  {
    int ival;
    float fval;
  }
  char type;
}

char INT_TYPE = 1;
char FLOAT_TYPE = 2;

%}

%union
{
   struct number value;
}

%token <value> FLOAT INTEGER command exp term factor

int yylex()
{
   ...
   if(f1 == 0)
   {
     yylval.value.type = INT_TYPE;
     yylval.value.ival = 0
   }
   ...
}

依旧......

通过这种方式,您可以在减少规则时检查操作数,确保生成新的正确类型。例如:

exp : exp '+' term {
   if ($1.type == INT_TYPE && $3.type == INT_TYPE)
   {
      $$.type = INT_TYPE;
      $$.ival = $1.ival + $3.ival;
   }
   else if ($1.type == INT_TYPE && $3.type == FLOAT_TYPE)
   {
      // this is a sort of implicit conversion to float
      $$.type = FLOAT_TYPE; 
      $$.fval = $1.ival + $3.fval;
   }
   // and so on

}

PS。我用Flex + Bison做了类似的事情,我不知道Lex + Yacc是否支持所有内容但我认为是这样......

答案 1 :(得分:0)

在yylval struct / union中编码数据类型。

而不是写出例如每个可能的组合。 +运算符,只为yacc中的+运算符定义1个规则,并在运行时检查数据类型(存储在yylval中)的有效性。

使用容器或数组存储所有有效组合,并使用此容器在运行时检查有效性。如果你找不到有效的组合,你至少可以给出一个像“抱歉酋长,你不能添加日期和浮动”的运行时错误。而不是语法错误(如果你在yacc中定义单独的规则,你会得到)。

作为最后的锦上添花,添加'自动转换'逻辑。如果找不到有效的组合,请尝试将其中一个操作数转换为另一种类型。一个这样的典型硬编码转换是“int to float”。例如。如果您的容器只允许添加2个整数或2个浮点数并且用户输入1 + 3.14(整数+浮点数),则您将无法在容器中找到有效组合。将int转换为float并再次查看容器。如果转化次数不是那么大,那就应该足够快了。

答案 2 :(得分:0)

我认为@Jack给出的答案会起作用,但是根据浮点数计算的所有规则,然后在最重要的规则(最后要评估的)上检查以查看结果可能更简洁是一个整数或浮点数并打印出适当的结果。

您的主要方法将简化为:

main(){
 return yyparse();
}

int yylex(void){
 int c;
   while((c = getchar()) == ' ');
   if (isdigit(c)){
     ungetc(c, stdin);
     scanf("%lf", &yylval);
     return NUMBER;
   }
   if ( c == '\n'){
  return 0;
 }
   return c;
}

int yyerror(char * s){
 fprintf(stderr, "%s\n", s);
   return 0;
}

并且您最重要的规则应更改为:

/*This is where we distinguish between float and integer*/
    command  : exp{
        if((((int)$1) / $1) ==  1){
         printf("%d\n", (int)$1);
        }
        else{
         printf("%lf\n", $1);
        }
       }
      ;

使用这种方法,您只需要一个令牌(NUMBER而不是FLOAT和INTEGER),并且还需要您在源代码中再添加一个%type语句来说明运算符。然后,您的%union语句将包含double val;char op;