views:

154

answers:

2

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.

A: 

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.

Patrick
i'm sorry to ask, but could you give me an example of "checking the validity of data types in yylval"? although the 1 + 3.14 not working kinda bums me too, since by writing all possible combinations that operation is actually valid.
master chief
See Jack's answer.
Patrick
A: 

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..

Jack