bison shift-reduce-conflict

bison - La reforma de la gramática para eliminar el cambio reduce el conflicto en if-then-else



shift-reduce-conflict (3)

¿Cómo elimino el conflicto de cambio y reducción de bison para la gramática dada?

selection-stmt -> if ( expression ) statement | if ( expression ) statement else statement

Una solución que dé la gramática modificada sería altamente apreciada.


Debe reconocer el hecho de que la statement intermedia en el caso if-else no puede ser (o terminar con) un estado de suspensión if (y si no hay otra cosa). La forma más sencilla de hacerlo es dividir la regla stmt en dos:

stmt -> stmt-ending-with-dangling-if | stmt-not-ending-with-dangling-if stmt-not-ending-with-dangling-if -> if ( expression ) stmt-not-ending-with-dangling-if else stmt-not-ending-with-dangling-if | ...other statements not ending with dangling if... stmt-ending-with-dangling-if -> if ( expression ) stmt | if ( expression ) stmt-not-ending-with-dangling-if else stmt-ending-with-dangling-if | ...other statements ending with dangling if...

Cualquier otro stmt -> whatever regla donde whatever que no termine con un stmt va en la regla stmt-not-ending-with-if , mientras que cualquier regla del stmt que NO termine en stmt se divide en dos versiones; una versión que not-ending-with-if regla not-ending-with-if y una versión que dangling-if regla que dangling-if .

editar

Una gramática más completa con otras producciones:

stmt : stmt-ending-with-dangling-if | stmt-not-ending-with-dangling-if stmt-not-ending-with-dangling-if : IF ''('' expr '')'' stmt-not-ending-with-dangling-if ELSE stmt-not-ending-with-dangling-if | WHILE ''('' expr '')'' stmt-not-ending-with-dangling-if | DO stmt WHILE ''('' expr '')'' '';'' | expr '';'' | ''{'' stmt-list ''}'' stmt-ending-with-dangling-if: IF ''('' expr '')'' stmt | IF ''('' expr '')'' stmt-not-ending-with-dangling-if ELSE stmt-ending-with-dangling-if | WHILE ''('' expr '')'' stmt-ending-with-dangling-if

Reglas como WHILE (expr) stmt se dividen en dos (ya que terminan con stmt ), mientras que las reglas como expr; no haga.


Hacer más alto nivel que las declaraciones normales, como:

statements: statements lineEnd statement | statements lineEnd IfStat | statements lineEnd IfElseStat | IfStat | IfElseStat ; IfStat: if ( statement ) ; IfElse: IfStat else statement ;


Hay una solución mucho más simple. Si sabe cómo funcionan los analizadores LR, entonces sabe que el conflicto ocurre aquí:

if ( expression ) statement * else statement

donde la estrella marca la posición actual del cursor. La pregunta que el analizador debe responder es "¿debo cambiar o reducir?". Por lo general, desea vincular el else al más cercano if , lo que significa que desea cambiar el token de else ahora. Reducir ahora significaría que quiere que la else espera espere a ser "antigua" if .

Ahora quiere "decirle" al generador de su analizador que "cuando hay un conflicto de cambio / reducción entre el token "else" y la regla" stm -> if (exp) stm ", entonces el token debe ganar". Para hacerlo, "dé un nombre" a la precedencia de su regla (por ejemplo, "then" ), y especifique que "then" tiene menos precedencia que "else" . Algo como:

// Precedences go increasing, so "then" < "else". %nonassoc "then" %nonassoc "else" %% stm: "if" "(" exp ")" stm %prec "then" | "if" "(" exp ")" stm "else" stm

utilizando la sintaxis de Bison.

En realidad, mi respuesta favorita es incluso dar a "then" y "else" la misma prioridad. Cuando las precedentes son iguales, para romper el vínculo entre el token que se desea cambiar y la regla que se desea reducir, Bison / Yacc buscará la asociatividad. Aquí, desea promover la asociatividad por el derecho, por así decirlo (más exactamente, quiere promover el "cambio"), por lo que:

%right "then" "else" // Same precedence, but "shift" wins.

Será suficiente.