Dangling autre - Dangling else
Le else pendant est un problème de programmation informatique dans lequel une clause else facultative dans une instruction if–then(–else) rend les conditions imbriquées ambiguës. Formellement, la grammaire sans contexte de référence du langage est ambiguë , ce qui signifie qu'il y a plus d'un arbre d'analyse correct .
Dans de nombreux langages de programmation, on peut écrire du code exécuté de manière conditionnelle sous deux formes : la forme if-then et la forme if-then-else – la clause else est facultative :
if a then s if b then s1 else s2
Cela donne lieu à une ambiguïté d'interprétation lorsqu'il y a des instructions imbriquées, en particulier chaque fois qu'une forme if-then apparaît comme s1
dans une forme if-then-else :
if a then if b then s else s2
Dans cet exemple, s
est exécuté sans ambiguïté quand a
est vrai et b
est vrai, mais on peut interpréter s2
comme étant exécuté quand a
est faux (en attachant ainsi le else au premier if) ou quand a
est vrai et b
est faux (en attachant ainsi le else au second si ). En d'autres termes, on peut voir l'énoncé précédent comme l'une des expressions suivantes :
if a then (if b then s) else s2 if a then (if b then s else s2)
Le problème d'autre qui traîne remonte à ALGOL 60 et a été résolu de diverses manières dans les langues suivantes. Dans les analyseurs syntaxiques LR , l'autre pendant est l'exemple archétypal d'un conflit décalage-réduction .
Eviter l'ambiguïté tout en gardant la syntaxe
C'est un problème qui revient souvent dans la construction d'un compilateur , en particulier l' analyse sans analyseur . La convention lorsqu'il s'agit de l'autre pendant en suspens est d'attacher l'autre à l'instruction if voisine, permettant des grammaires sans contexte sans ambiguïté, en particulier. Les langages de programmation comme Pascal, C et Java suivent cette convention, il n'y a donc aucune ambiguïté dans la sémantique du langage , bien que l'utilisation d'un générateur d'analyseur puisse conduire à des grammaires ambiguës . Dans ces cas, le regroupement alternatif est accompli par des blocs explicites, comme begin...end
en Pascal et {...}
en C.
Selon l'approche de construction du compilateur, on peut prendre différentes mesures correctives pour éviter toute ambiguïté :
- Si l'analyseur est produit par un générateur d' analyseur SLR, LR(1) ou LALR LR , le programmeur s'appuiera souvent sur la fonctionnalité d'analyseur généré en préférant Shift à Reduce en cas de conflit. Alternativement, la grammaire peut être réécrite pour supprimer le conflit, au prix d'une augmentation de la taille de la grammaire (voir ci - dessous ).
- Si l'analyseur est écrit à la main, le programmeur peut utiliser une grammaire sans contexte non ambiguë . Alternativement, on peut s'appuyer sur une grammaire non contextuelle ou une grammaire d'expression d'analyse .
Éviter l'ambiguïté en changeant la syntaxe
Le problème peut également être résolu en rendant explicite le lien entre un else et son if, dans la syntaxe. Cela permet généralement d'éviter les erreurs humaines.
Les solutions possibles sont :
- Avoir une déclaration "end if". Des exemples de tels langages sont ALGOL 68 , Ada , Eiffel , PL/SQL , Visual Basic et AppleScript .
- Interdire à l'instruction qui suit un « alors » d'être elle-même un « si » (cela peut cependant être une paire de crochets d'instruction contenant uniquement une clause si-alors). Cette approche est suivie par ALGOL 60 .
- Exiger des accolades (parenthèses) lorsqu'un "else" suit un "if".
- Exiger que chaque « if » soit associé à un « else ». Pour éviter un problème similaire concernant la sémantique plutôt que la syntaxe, Racket s'écarte de Scheme en considérant une
if
clause sans clause de secours comme une erreur, distinguant efficacement les expressions conditionnelles (ieif
) des instructions conditionnelles (iewhen
etunless
, qui n'ont pas de clauses de secours). - Utilisation de mots-clés différents pour les instructions "if" à une alternative et à deux alternatives. S-algol utilise
if e do s
pour le cas à une alternative etif e1 then e2 else e3
pour le cas général. - Exiger des accolades inconditionnellement, comme Swift et Modula-2 . C'est effectivement vrai en Python car ses règles d'indentation délimitent chaque bloc, pas seulement ceux des instructions "if". Pour réduire l'encombrement résultant, Modula-2 supprime l'ouvre-bloc sur les niveaux non fonctionnels.
Exemples
Des exemples concrets suivent.
C
En C , la grammaire lit, en partie :
statement = ... | selection-statement selection-statement = ... | IF ( expression ) statement | IF ( expression ) statement ELSE statement
Ainsi, sans autre règle, l'énoncé
if (a) if (b) s; else s2;
pourrait être analysé de manière ambiguë comme s'il s'agissait de :
if (a)
{
if (b)
s;
else
s2;
}
ou alors:
if (a)
{
if (b)
s;
}
else
s2;
En pratique en C le premier arbre est choisi, en associant le else
au plus proche if
.
Éviter le conflit dans les parseurs LR
L'exemple ci-dessus pourrait être réécrit de la manière suivante pour lever l'ambiguïté :
statement: open_statement | closed_statement ; open_statement: IF '(' expression ')' statement | IF '(' expression ')' closed_statement ELSE open_statement ; closed_statement: non_if_statement | IF '(' expression ')' closed_statement ELSE closed_statement ; non_if_statement: ... ;
Toute autre règle de grammaire liée à l'instruction peut également devoir être dupliquée de cette manière si elle peut se terminer directement ou indirectement par un statement
ou un selection-statement
non-terminal.
Cependant, nous donnons une grammaire qui inclut à la fois les instructions if et while.
statement: open_statement | closed_statement ; open_statement: IF '(' expression ')' statement | IF '(' expression ')' closed_statement ELSE open_statement | WHILE '(' expression ')' open_statement ; closed_statement: simple_statement | IF '(' expression ')' closed_statement ELSE closed_statement | WHILE '(' expression ')' closed_statement ; simple_statement: ... ;
Enfin, nous donnons la grammaire qui interdit les instructions IF ambiguës.
statement: open_statement | closed_statement ; open_statement: IF '(' expression ')' simple_statement | IF '(' expression ')' open_statement | IF '(' expression ')' closed_statement ELSE open_statement | WHILE '(' expression ')' open_statement ; closed_statement: simple_statement | IF '(' expression ')' closed_statement ELSE closed_statement | WHILE '(' expression ')' closed_statement ; simple_statement: ... ;
Avec cette grammaire, l'analyse de "if (a) if (b) c else d" échoue :
statement open_statement IF '(' expression ')' closed_statement ELSE open_statement 'if' '(' 'a' ')' closed_statement 'else' 'd'
puis l'analyse échoue en essayant de correspondre closed_statement
à "if (b) c". Une tentative avec closed_statement
échoue de la même manière.