Comment puis-je supprimer le conflit de réduction des quarts de travail chez le bison pour une grammaire donnée?
selection-stmt -> if ( expression ) statement |
if ( expression ) statement else statement
Une solution donnant la grammaire modifiée serait hautement appréciée.
Il existe une solution beaucoup plus simple. Si vous savez comment fonctionnent les analyseurs syntaxiques LR, alors vous savez que le conflit se produit ici:
if ( expression ) statement * else statement
où l'étoile marque la position actuelle du curseur. La question à laquelle l'analyseur doit répondre est "devrais-je changer ou devrais-je réduire"? Généralement, vous souhaitez lier la variable else
à la variable if
la plus proche, ce qui signifie que vous souhaitez déplacer le jeton else
maintenant. Réduire maintenant signifie que vous voulez que la else
attende d'être liée à une "ancienne" if
.
Maintenant, vous voulez "dire" à votre générateur d'analyseur que "quand il y a un décalage/conflit entre le jeton "else"
et la règle" stm -> if (exp) stm ", le jeton doit gagner". Pour ce faire, "donnez un nom" à la priorité de votre règle (par exemple, "then"
) et spécifiez que "then"
a une priorité inférieure à "else"
. Quelque chose comme:
// Precedences go increasing, so "then" < "else".
%nonassoc "then"
%nonassoc "else"
%%
stm: "if" "(" exp ")" stm %prec "then"
| "if" "(" exp ")" stm "else" stm
en utilisant la syntaxe de Bison.
En fait, ma réponse préférée est même de donner à "then"
et à "else"
la même priorité. Lorsque les précédents sont égaux, pour rompre le lien entre le jeton qui veut être déplacé et la règle qui veut être réduite, Bison/Yacc se penchera sur l’associativité. Ici, vous voulez promouvoir la droite-associativité pour ainsi dire (plus exactement, vous voulez promouvoir le "décalage"), donc:
%right "then" "else" // Same precedence, but "shift" wins.
suffira.
Vous devez reconnaître le fait que la variable statement
moyenne dans le cas if-else ne peut pas être (ou se terminer par) une pendaison si (une si sans autre). Le moyen le plus simple de procéder consiste à scinder la règle stmt
en deux:
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...
Toute autre règle stmt -> whatever
où whatever
ne se termine pas par un stmt
va dans la règle stmt-not-ending-with-if
, tandis que toute règle stmt
se terminant par stmt
est divisée en deux versions; une version not-ending-with-if
dans la règle not-ending-with-if
et une version dangling-if
dans la règle dangling-if
.
modifier
Une grammaire plus complète avec d'autres productions:
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
Les règles telles que WHILE (expr) stmt
sont scindées en deux (car elles se terminent par stmt
), alors que les règles telles que expr;
ne le sont pas.
faire si sinon des déclarations de niveau supérieur à la normale, comme:
statements:
statements lineEnd statement
| statements lineEnd IfStat
| statements lineEnd IfElseStat
| IfStat
| IfElseStat
;
IfStat:
if ( statement )
;
IfElse:
IfStat else statement
;