【问题标题】:yacc: Distinguish Integers from Floating Point Numbersyacc:从浮点数中区分整数
【发布时间】:2010-03-02 20:42:21
【问题描述】:

我应该编写一个执行 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,然后替换其中的所有内容,但这似乎真的错了。我不知道如何完成这项工作,而且我看到的教程并没有真正帮助我。

【问题讨论】:

  • 我认为 factor_int 和 factor_float 方法是正确的。这基本上就是真正的编译器的工作方式。每个表达式要么是浮点数,要么是整数。
  • 所以如果我有 8 种不同的类型,我会拥有 2^8 个表达式和 2 个操作数?太疯狂了!!!或者我做错了数学。
  • 你听说过 lex 吗?我认为较新的称为 flex。
  • 是的,我用过一点,但我不明白如何将 yacc 与它很好地集成。我看到的教程没有递归,比如$$ = $1 + $3
  • 您可以使用 lex 来生成您的 yylex 方法。您不必自己实现它。正常流程是,有一个 lex 文件,它会生成一个 .c 文件,您可以将其与 yacc 文件一起编译以获得解析器。

标签: yacc compiler-theory


【解决方案1】:

基本上你可以这样做:

%{
#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 是否支持所有内容,但我认为是这样..

【讨论】:

    【解决方案2】:

    在 yylval 结构/联合中编码数据类型。

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

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

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

    【讨论】:

    • 很抱歉,您能给我举一个“检查 yylval 中数据类型的有效性”的例子吗?虽然 1 + 3.14 不工作也让我很不爽,因为通过编写所有可能的组合,该操作实际上是有效的。
    【解决方案3】:

    我认为@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;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-06
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 2017-07-21
      • 2012-06-10
      相关资源
      最近更新 更多