user2927848 user2927848 - 17 days ago 8
C Question

Bison Grammar For Basic Calculator Issue

So my grammar below 'works'. However, it has a small caveat now I can do stuff like

1.0-----------------2.0

and it will flip flop between 2 and -2 until it gets to 1 op 2 then will evaluate. Still new to bison and unclear on how it is best to implement a fix for this. I have 1 idea in mind with raising an error with every combination of '+' '-' in increments of 3 but that is 8 grammar rules and I'm not even sure how to throw an error in bison. I imagine there is a cleaner more understandable way to do this.

Flex Lexer

%option nounistd
%option noyywrap

%{
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include "parser.tab.h"
#define isatty _isatty
#define fileno _fileno
%}

%%
[ \t]+
\n {return '\n';}
[0-9]+(\.[0-9]+)? {yylval.number=atof(yytext); return NUMBER;}
. {return yytext[0];}
%%


Bison Grammar

%{
#include <stdio.h>
#include <math.h>
extern int yylex(void);
int yyerror(const char* c) { printf("%s\n",c); return 0;}
%}

%union
{
double number;
}

%type <number> exp
%token <number> NUMBER

%left '+' '-'
%left '*' '/'
%right '^'

%start commands
%%
commands
: /*empty*/
| commands line
;

line
: '\n'
| exp '\n' {printf("=%f\n",$1);}
| error '\n' {printf("encountered an error!\n");}
;

exp
: NUMBER { $$ = $1;}
| exp '+' exp {$$ = $1 + $3;}
| exp '-' exp {$$ = $1 - $3;}
| exp '*' exp {$$ = $1 * $3;}
| exp '/' exp {$$ = $1 / $3;}
| exp '^' exp {$$ = pow($1,$3);}
| '-' exp {$$ = -$2;}
| '+' exp {$$ = $2;}
| '(' exp ')' {$$ = $2;}
;
%%

Answer

This is correct and expected behaviour for arithmetic evaluation, and you will find that it works identically in any language which doesn't implement the -- decrement operator.

If you have a -- operator, you would normally implement that in your lexer with a rule like:

"--"  { return DECREMENT; }

That will guarantee that a---b is lexed as "a", "--", "-", "b" and a----b as "a", "--", "--", "b". (The latter is a syntax error.) That's a result of the "maximal munch" rule, which is required by most language standards and implemented by most scanner generators. (Writing code like that is generally discouraged but not forbidden.)

In C, you cannot use two consecutive post-decrement operators, since a post-decrement expression is not an lvalue. That can be enforced in the grammar by requiring the argument of pre- and post-decrement and -increment operators to be an lvalue. But in C++, you cannot easily determine correctness syntactically; although it would be horrible style, nothing stops you from overloading operator--(int) for some type to return a reference.

If you have a language without a decrement operator but you want, for some aesthetic reason, to ban expressions with two consecutive unary operators, then you can do that in the same way as hinted above, eg.:

 value: NUMBER | '(' expr ')'
 term:  value | '-' value | '+' value
 expr:  term | expr '-' expr | expr '+' expr | expr '*' expr | expr '/' expr | ...

Here, you cannot have --a (or -+a) because a unary operator can only be applied to a value and a value cannot start with a unary operator. So the end user woyld be forced to use parentheses. But you should at least have ready a satisfactory answer for the end user who wants to know why you feel it necessary to impose that restriction.

Comments