0

I'm trying to write a small parser. Unfortunately I get a "shift-reduce conflict". Grammars are not my strong point, and I only need to get this one small thingy done. Here's the reduced grammar that produces the error:

stmts_opt -> stmts
;

stmts -> stmt 
| stmts stmt
| stmsts
;

stmt -> id 
| ITERATE content_stmt 
| IF test then content_stmt ELSE content_stmt
| IF test then content_stmt
;

content_stmt: BEGIN stmt_opt END
| stmt
;

A solution giving the modified grammar would be highly appreciated.

Edit:

I modified my grammar suiting @rici's answer but the problem persists. Here is my actual grammar productions:

prog:   BEGIN_PROG def_sprogram BEGIN_EXEC  stmts_opt END_EXEC END_PROG
            { () }
;

def_sprogram:   /* empty */     { () }
| define_new def_sprogram       { () }
;


define_new:         DEFINE_NEW_INSTRUCTION ID AS content_stmt SEMI   { }
;

stmts_opt:  /* empty */     { () }
|           stmts           { () }
;

stmts:      stmt            { () }
|           stmts SEMI stmt { () }
|           stmts SEMI      { () }
;

content_stmt:  BEGIN stmts_opt END { () }
|       stmt    { () }
;

stmt:       open_stmt { () }
|           closed_stmt { () }
;

open_stmt:  ITERATE INT TIMES open_stmt { () }  
|           WHILE test DO open_stmt { () }
|           IF test THEN closed_stmt ELSE open_stmt { () }
|           IF test THEN stmt { () }
;

closed_stmt:  simple_stmt   { () }  
|           ITERATE INT TIMES closed_stmt   { () }  
|           WHILE test DO closed_stmt { () }
|           IF test THEN closed_stmt ELSE closed_stmt { () }
;

Here is the example I am testing on:

BEGINNING-OF-PROGRAM
  BEGINNING-OF-EXECUTION

    IF not-next-to-a-beeper THEN
      move;

    IF not-facing-north THEN

        turnleft;


    ELSE <--- ERROR
        turnleft;

    IF not-facing-east THEN
      IF not-facing-west THEN
        turnleft;

    turnoff
  END-OF-EXECUTION
END-OF-PROGRAM

I am getting the error in the first ELSE. I also tried to declare a simple precedence as suggested by @rici:

%nonassoc THEN
%nonassoc ELSE

but that didn't resolve the error neither.

AbdelKh
  • 499
  • 7
  • 19
  • If the only shift-reduce conflict is from the dangling else ambiguity, your grammar will work fine because yacc/bison chooses the correct resolution. If you want to get rid of the warning, search here for "dangling else". – rici Nov 05 '17 at 00:58
  • I am using camlyacc and I am getting an error not a warning. I tried to search here but my grammar is slightly different (with BEGIN and END), the suggested solutions didn't work for me. – AbdelKh Nov 05 '17 at 00:59
  • Making that an error is annoying. It shouldn't do that. But begin and end tokens should not have any effect whatsoever. What did you try which did not work? – rici Nov 05 '17 at 01:04
  • https://stackoverflow.com/a/32826695/1566221 contains both a precedence solution and a rewritten grammar; one of those should work. (I apologise for the greek but you can probably work it out.) – rici Nov 05 '17 at 01:12
  • I tried this solution http://www.parsifalsoft.com/ifelse.html. I replaced stmt by open_stmt and close_stmt and did the necessary changes according to the given solution. However, that didn't work and things got complicated quickly so I gave up this solution. I guessed that the problem is in content_stmt since it's different from the example given. – AbdelKh Nov 05 '17 at 01:13

1 Answers1

1

The simplest solution to the "dangling else" shift-reduce conflict is to force resolution in favour of shifting the ELSE token. Since camlyacc does support precedence declarations (according to the rather sketchy documentation I was able to find) this should be as simple as adding the following to your declaration section (before the first %%):

%nonassoc THEN
%nonassoc ELSE

(The associativity doesn't matter because there is nowhere in the grammar where THEN and ELSE can associate.)

If you want to use the explicit "matched/unmatched" (or "open/closed statements"), it is sufficient to note that BEGIN stmts_opt END is a "matched" (or "closed" statement) since it cannot accept a THEN. The other matched statements are

matched_stmt: BEGIN stmts_opt END
            | ITERATE matched_stmt
            | IF test THEN matched_stmt ELSE matched_stmt
            | /* Any other kind of simple statement */

The unmatched statements are

unmatched_stmt: ITERATE unmatched_stmt
              | IF test THEN matched_stmt
              | IF test THEN unmatched_stmt
              | IF test THEN matched_stmt ELSE unmatched_stmt

Many people prefer to create a non-terminal which includes matched_stmt and unmatched_stmt. However, in your case you seem to not want to nest BEGINEND blocks, restricting those to the content of a compound statement. So your stmt would be all of the above except for the BEGIN stmts_opt END right-hand side.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Thank you for your answer. I tried both your solutions and non one them worked for me. I am almost sure I am mistaken somewhere. I updated the question with my complete new grammar after including the matched_stmt and unmatched_stmt. – AbdelKh Nov 05 '17 at 08:44
  • @Joker00: There is a reason why we usually insist on a [mcve] and why we discourage editing questions after they have been answered. I'm pretty sure that my answer responded correctly to your initial question, in that it resolved the shift-reduce conflict, which I presume was reported when you tried to build your parser. In your edited question, you report a *different problem* (a syntax error *during execution of the parser*) which is the result of a *different grammar* (which includes semicolon statement delimiters). Your handling of semicolons is causing the syntax error.... – rici Nov 05 '17 at 16:00
  • ... Note that a `stmt`, as you have defined it, *does not include the trailing semi-colon*. The syntax for an if statement (`IF test THEN closed_stmt ELSE...`) does not include a semicolon. But `IF not-facing-north THEN turnleft; ELSE` does include a semicolon, so it doesn't match the rule. – rici Nov 05 '17 at 16:03