yacc:区分整数和浮点数

发布于 2024-08-24 03:23:37 字数 1510 浏览 2 评论 0原文

我应该编写一个程序来执行 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;
}

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

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

I'm supposed to write a program that does 2 + 2 = 4 and 2.2 + 2 = 4.2.

I've already done it so that it treats everything as a floating point, but that's "wrong". I have to distinguish them. Here's what I have so far:

%{
#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;
}

The problem I have is that each expression can only have 1 type. Right now everything is basically float, so while the operations are right, this isn't the right solution.

I thought about defining more expressions, basically having factor_int and factor_float, and then replacing everything in it, but that seems really wrong. I have no idea how to get this done though, and the tutorials I've seen haven't really helped me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫性小仙女 2024-08-31 03:23:37

基本上你可以做这样的事情:

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

Basically you can do something like this:

%{
#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
   }
   ...
}

and so on..

in this way you can check operands when reducing rules being sure to generate new correct types.. for example:

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. I did something similar with Flex+Bison, I don't know if everything is supported as well as in Lex+Yacc but I think so..

顾铮苏瑾 2024-08-31 03:23:37

对 yylval 结构/联合中的数据类型进行编码。

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

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

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

Encode the data type within your yylval struct/union.

Instead of writing every possible combination of e.g. the + operator, define only 1 rule for the + operator in yacc, and check the validity of the data types (stored in yylval) at run time.

Use a container or array to store all valid combinations, and use this container to check the validity at run time. If you don't find a valid combination, you can give at least a decent run time error like "Sorry Chief, you cannot add a date and a float." instead of syntax error (which you would get if you would define separate rules in yacc).

As the final icing on the cake, add 'automatic-conversion' logic. If you don't find a valid combination, try to convert one of the operands to another type. One such a typical hard-coded conversion is "int to float". E.g. if your container only allows adding 2 integers or 2 floats and the user enters 1+3.14 (which is integer + float), you will not find a valid combination in the container. Convert the int to float and look again in the container. If the number of conversions is not that big, it should be fast enough.

与之呼应 2024-08-31 03:23:37

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

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

I think the answer @Jack gave will work, but it might be more concise to base all of your rules on the calculation of floats, then on the top most rule (the last to be evaluated) check to see if the result is an integer or a float and print the appropriate result.

Your main method would be reduced to:

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;
}

and your top most rule should be changed to:

/*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);
        }
       }
      ;

Using this approach you would only need one token (NUMBER instead of FLOAT and INTEGER) and it would also require you to add one more %type statement to your source code to account for operators. Your %union statement would then contain double val; and char op;.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文