gobo-eiffel / gobo

The Gobo Eiffel Project provides the Eiffel community with free and portable Eiffel tools and libraries.
https://sourceforge.net/projects/gobo-eiffel/
Other
59 stars 24 forks source link

An issue with operator precedence and associativity #49

Closed kwaxer closed 3 years ago

kwaxer commented 3 years ago

There seems to be an issue with associativity of an operator when the context-dependent precedence is used. I’ll use the calculator example from …/parse/example/calc. The original specifications for the operator associativity and for the expression look like

%left '-' '+'
%left '*' '/'
%left NEG
%right '^'
exp: NUM
    | exp '+' exp
    | exp '-' exp
    | exp '*' exp
    | exp '/' exp
    | '-' exp %prec NEG
    | '(' exp ')'
    ;

The calculator built with these rules produces the expected value -4 for the expression 1-2-3.

My (mis)understanding is that the specification can be changed into an equivalent one by replacing the artificial terminal symbol NEG with a real one ‘-’ and specifying the precedence of the binary version of it equal to that of ‘+’:

%left '+'
%left '*' '/'
%left '-'
%right '^'
exp: NUM
    | exp '+' exp
    | exp '-' exp %prec '+'
    | exp '*' exp
    | exp '/' exp
    | '-' exp
    | '(' exp ')'
    ;

The calculator built with these rules produces 2 for the expression 1-2-3, i.e. somehow it applies right associativity instead of the left one. Note that both ‘+’ and ‘-’ are declared as left-associative.

My expectation was that using %prec would either affect only precedence (using the newly requested one) or both precedence and associativity (taking them both from the "anchor"). But neither of these expectations turned out to be true. Is such behavior expected?

ebezault commented 3 years ago

I looked at the code of geyacc, and in particular this code, and it looks like there are two kinds of precedence taken into account when resolving conflicts. The precedence of the tokens and the precedence of the rules. The precedence of the tokens are defined in the %left lines and the like. By default the precedence of a rule is the precedence of the last token in that rule, but can be overridden by %prec. And likewise for associativity. Your modifications above preserve the precedence and associativity of the rules. But the token - now has a different precedence. That's why we see a different behavior at execution, because this code does not resolve the conflict in the same way.

ebezault commented 3 years ago

It's actually all explained in the "How Precedence Works" section of the documentation on Operator Precedence.