yacc: различают целые числа от чисел с плавающей точкой - PullRequest
4 голосов
/ 02 марта 2010

Я должен написать программу, которая выполняет 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, а затем о замене всего в нем, но этокажется, действительно неправильно.Я понятия не имею, как это сделать, и учебники, которые я видел, на самом деле не помогли мне.

Ответы [ 3 ]

2 голосов
/ 03 марта 2010

В основном вы можете сделать что-то вроде этого:

%{
#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, но я так думаю ..

0 голосов
/ 21 ноября 2010

Я думаю, что ответ, который дал @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;.

0 голосов
/ 02 марта 2010

Кодируйте тип данных в вашей структуре / объединении yylval.

Вместо написания каждой возможной комбинации, например, оператор +, задайте только 1 правило для оператора + в yacc и проверьте правильность типов данных (хранящихся в yylval) во время выполнения.

Используйте контейнер или массив для хранения всех допустимых комбинаций и используйте этот контейнер для проверки правильности во время выполнения. Если вы не нашли правильную комбинацию, вы можете выдать хотя бы приличную ошибку времени выполнения, например «Извините, начальник, вы не можете добавить дату и число с плавающей запятой». вместо синтаксической ошибки (которую вы получите, если определите отдельные правила в yacc).

В качестве финальной глазури на торте добавьте логику «автоматического преобразования». Если вы не нашли правильную комбинацию, попробуйте преобразовать один из операндов в другой тип. Одним из таких типичных жестко закодированных преобразований является «int to float». Например. если ваш контейнер позволяет добавлять только 2 целых числа или 2 числа с плавающей запятой и пользователь вводит 1 + 3,14 (то есть целое число + число с плавающей запятой), вы не найдете в контейнере допустимой комбинации. Преобразуйте int в float и посмотрите снова в контейнер. Если количество конверсий не так велико, оно должно быть достаточно быстрым.

...