Bison - when is %prec really needed for unary operators?

1.1k views Asked by At

I am currently playing with Flex and Bison for a first time. I have read about contextual precedence on Bison manual page. Tried to build a minimal example without using %prec directive as I am not that familiar with what it really does. Here is my minimal example.

Flex file

%option noyywrap
%{
#include <iostream>
#include "parser.h"

int lineNum = 1;
%}

%%

[ \t]+          ;
\n              { lineNum++; }
\/\/(.*)        ;
"+"             { return PLUS; }
"-"             { return MINUS; }
"*"             { return MULTIPLY; }

[0-9]+          {
                    yylval.int_val = atoi(yytext);
                    return INT;
                }

.               { std::cout << "Unknown token " << yytext << " at line " << lineNum << std::endl; yyterminate(); }

%%

Bison file

%{
#include <iostream>
#include <string>

extern int lineNum;
extern int yylex();
void yyerror(const char* e) { std::cerr << "ERROR ON LINE " << lineNum << ": " << e << std::endl; }

extern int eval;
%}

%union
{
    int int_val;
}

%define parse.error verbose

%token <int_val> INT PLUS MINUS MULTIPLY

%type <int_val> expr

%left PLUS MINUS
%left MULTIPLY

%start expr

%%

expr    :   expr PLUS expr { $$ = $1 + $3; eval = $$; }
        |   expr MINUS expr { $$ = $1 - $3; eval = $$; }
        |   expr MULTIPLY expr { $$ = $1 * $3; eval = $$; }
        |   MINUS expr { $$ = -$2; eval = $$; }
        |   INT
        ;

%%

Main cpp file

#include <iostream>

int eval = 0;
extern int yyparse();

int main()
{
    yyparse();
    std::cout << eval << std::endl;
    return 0;
}

I have not done deep testing but for every single combination using unary minus I could come up with, I got the right result. Am I just that lucky, or the %prec directive is only needed in some special cases? Also I would appreciate example when the directive is needed so I can evaluate shifts and reduces on the stack by myself.

Thanks

1

There are 1 answers

1
rici On BEST ANSWER

Your code produces the wrong parse but the correct result, because the unary minus operator is equivalent to multiplying by -1, and multiplication is associative. So even though it is parsing -2*3 as -(2*3) instead of (-2)*3, the resulting value is the same.

In the case of expressions like -2-3, you get the correct parse because you've declared - as being left-associative, so (-2)-3 is preferred over -(2-3), just like (1-2)-3 would be preferred over 1-(2-3).

So when don't you need to declare precedence for unary minus? If

  • The only operator which is both prefix and infix is −

  • Every operator ⊕ with higher precedence than (binary) −, −(ab)=(−a)⊕b;

  • You don't care about getting a correct parse tree.

This works for *, but generally fails for (integer) / and %.