5

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

4

3 回答 3

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 是否支持所有内容,但我认为是的..

于 2010-03-02T21:34:39.467 回答
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;and char op;

于 2010-11-21T01:15:57.753 回答
0

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

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

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

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

于 2010-03-02T20:59:09.293 回答