Search code examples
bisonyacc

shift/reduce conflict in grammar with optional semicolon


Sample repository

I'd like to do something like this:

// match used as a statement, semicolon is optional
match (1) {}

// match used as an expression, semicolon is required
1 + match (2) {};
statement
    : expression_without_block T_SEMICOLON
    | expression_with_block
;

expression
    : expression_without_block
    | expression_with_block
;

expression_without_block
    : scalar
    | expression_without_block T_PLUS expression
    | T_PLUS expression
;

expression_with_block
    : T_MATCH T_LEFT expression T_RIGHT T_CURLY_LEFT T_CURLY_RIGHT
;

The idea is that expression_with_block can't be used at the beginning of a statement, making the following unambiguous:

match (1) {}
+2;

// Can't mean
(match (1) {} + 2);
// because of the line "expression_without_block T_PLUS expression"

The grammar causes a shift/reduce conflicts but I have no idea why. The output says the following (but I'm not sure what to do with it):

State 11 conflicts: 1 shift/reduce

...

State 11

    5 expression: expression_without_block .
    8 expression_without_block: expression_without_block . T_PLUS expression

    T_PLUS  shift, and go to state 14

    T_PLUS    [reduce using rule 5 (expression)]
    $default  reduce using rule 5 (expression)

The full output file can be found here.

I'm also not even sure this is the right approach because then something like this wouldn't work:

// `match` can't be at the lhs of an expression, even in sub-expressions
func_call(match (10) {} + 20);

Is there a way to achieve what I'm describing here in bison? I'm not a bison expert so I'd really appreciate some help. Thanks!


Solution

  • It's a very classic kind of conflict that happen with left recursive rules, but as the default resolution of a shift/reduce conflict is to do a shift, then everything will be fine here.

    Here is how you should read the bison trace:

    T_PLUS  shift, and go to state 14              -- when next token is T_PLUS shift action is selected
    
    T_PLUS    [reduce using rule 5 (expression)]   -- the action between [] was disabled by Bison default conflict resolution
    

    To override Bison's default conflict resolution (which is not necessary here), we may use operator precedence (see https://www.gnu.org/software/bison/manual/bison.html#Precedence-Decl) or precedence for non operator (see https://www.gnu.org/software/bison/manual/bison.html#Non-Operators)