web-dev-qa-db-fra.com

Réformer la grammaire pour supprimer le décalage réduit les conflits en si-alors-sinon

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.

18
Aakash Anuj

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.

37
akim

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 -> whateverwhatever 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.

5
Chris Dodd

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
;
0
harry