Yacc: Distinguish integers from floating point numbers

I have to write a program that does 2 + 2 = 4 and 2.2 + 2 = 4.2.

I have already done this in such a way as to consider everything as a floating point, but this is "wrong." I have to distinguish them. Here is what I still have:

%{ #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 is that each expression can have only 1 type. Now everything is basically floating, therefore, as long as the operations are correct, this is the wrong decision.

I was thinking of 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 do this, but the tutorials that I saw didn’t really help me.

+4
source share
3 answers

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

etc.

this way you can check operands while truncating rules that will necessarily generate new valid types. For instance:

 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 in the same way as in Lex + Yacc, but I think so.

+1
source

Encode the data type in the yilival / union structure.

Instead of writing all possible combinations, for example. + operator, defines only 1 rule for the + operator in yacc and checks the validity of data types (stored in yylval) at run time.

Use a container or array to store all valid combinations and use this container to verify validity at run time. If you did not find the right combination, you can give at least a decent runtime error, for example, "Sorry boss, you cannot add a date and a float." instead of a syntax error (which you would get if you defined separate rules in yacc).

As the final icing on the cake, add the logic of "automatic conversion". If you did not find the correct combination, try converting one of the operands to another type. One such typical hard-coded conversion is "int to float". For instance. if your container allows you to add only 2 integers or 2 floats, and the user enters 1 + 3.14 (which is equal to the integer + float), you will not find the correct combination in the container. Convert int to float and view the container again. If the number of conversions is not so large, it should be fast enough.

0
source

I think the @Jack answer gave work, but it can be more concise to base all your rules on calculating floats, and then on the top most rules (the last one to be evaluated) check that the result is an integer or float and prints the corresponding result.

Your main method will 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 rules 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 only need one token (NUMBER instead of FLOAT and INTEGER), and you also need to add another %type operator to the source code to account for the operators. Your %union statement will contain double val; and char op; .

0
source

Source: https://habr.com/ru/post/1302910/


All Articles