2010-03-02 6 views
4

Je suis supposé écrire un programme qui fait 2 + 2 = 4 et 2.2 + 2 = 4.2.yacc: Distinguer les entiers des nombres à virgule flottante

Je l'ai déjà fait pour que tout soit considéré comme un flottant, mais c'est "faux". Je dois les distinguer. Voici ce que j'ai jusqu'à présent:

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

Le problème que j'ai est que chaque expression ne peut avoir qu'un seul type. À l'heure actuelle, tout est essentiellement flottant, alors même si les opérations sont bonnes, ce n'est pas la bonne solution.

J'ai pensé à définir plus d'expressions, en ayant essentiellement factor_int et factor_float, puis en remplaçant tout, mais cela semble vraiment faux. Je n'ai aucune idée de comment y arriver, et les tutoriels que j'ai vus ne m'ont pas vraiment aidé.

+0

Je pense que l'approche factor_int et factor_float est correcte. C'est essentiellement comment fonctionnent les vrais compilateurs. Chaque expression est flottante ou intégrale. –

+0

Donc, si j'ai 8 types différents, je vais avoir, 2^8 expressions avec 2 opérandes? C'est fou!!! Ou je fais mal les maths. –

+0

Avez-vous entendu parler de lex? Je pense que le plus récent s'appelle flex. –

Répondre

1

vous pouvez en principe faire quelque chose comme ceci:

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

et ainsi de suite ..

de cette façon vous pouvez vérifier opérandes lors de la réduction des règles étant sûr de générer de nouveaux types corrects .. par exemple:

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. J'ai fait quelque chose de similaire avec Flex + Bison, je ne sais pas si tout est supporté aussi bien que dans Lex + Yacc mais je le pense ..

0

Encodez le type de données dans votre structure/union yylval.

Au lieu d'écrire toutes les combinaisons possibles de par ex. l'opérateur +, définit seulement 1 règle pour l'opérateur + dans yacc, et vérifie la validité des types de données (stockés dans yylval) lors de l'exécution.

Utilisez un conteneur ou une baie pour stocker toutes les combinaisons valides et utilisez ce conteneur pour vérifier la validité au moment de l'exécution. Si vous ne trouvez pas une combinaison valide, vous pouvez donner au moins une erreur d'exécution correcte, par exemple "Désolé chef, vous ne pouvez pas ajouter de date et de flottant". au lieu de l'erreur de syntaxe (que vous auriez si vous définissiez des règles séparées dans yacc).

Comme la cerise finale sur le gâteau, ajoutez la logique de «conversion automatique». Si vous ne trouvez pas une combinaison valide, essayez de convertir l'un des opérandes en un autre type. Une telle conversion codée en dur typique est "int to float". Par exemple. Si votre conteneur ne permet que d'ajouter 2 entiers ou 2 flottants et que l'utilisateur entre 1 + 3.14 (qui est un entier + un flottant), vous ne trouverez pas de combinaison valide dans le conteneur. Convertissez l'int en float et regardez à nouveau dans le conteneur. Si le nombre de conversions n'est pas très important, il devrait être assez rapide.

+0

Je suis désolé de demander, mais pourriez-vous me donner un exemple de "vérification de la validité des types de données dans yylval"? Bien que le 1 + 3.14 ne fonctionne pas, ça me brûle aussi, car en écrivant toutes les combinaisons possibles, cette opération est valide. –

+0

Voir la réponse de Jack. – Patrick

0

Je pense que la réponse donnée par @Jack fonctionnera, mais ça pourrait être plus concis pour baser toutes vos règles sur le calcul des flottants, puis sur la règle la plus haute (la dernière à être évaluée) vérifier pour voir si le résultat est un entier ou un flottant et imprimer le résultat approprié.

Votre principale méthode serait réduite à:

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

et votre plus haut sommet règle devrait être modifiée pour:

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

Cette approche vous besoin d'un seul jeton (NUMÉRO au lieu de FLOAT et INTEGER) et il vous faudrait également ajouter une autre instruction %type à votre code source pour prendre en compte les opérateurs. Votre instruction %union contiendrait alors double val; et char op;.

Questions connexes