Dangling else - Dangling else
O else pendente é um problema em programação de computador em que uma cláusula else opcional em uma instrução if – then (–else) resulta em condicionais aninhadas sendo ambíguas. Formalmente, a gramática livre de contexto de referência do idioma é ambígua , o que significa que há mais de uma árvore de análise correta .
Em muitas linguagens de programação, pode-se escrever código executado condicionalmente em duas formas: a forma if-then e a forma if-then-else - a cláusula else é opcional:
if a then s if b then s1 else s2
Isso dá origem a uma ambigüidade na interpretação quando há instruções aninhadas, especificamente sempre que uma forma if-then aparece como s1
uma forma if-then-else:
if a then if b then s else s2
Neste exemplo, s
é executado inequivocamente quando a
é verdadeiro e b
verdadeiro, mas pode-se interpretar s2
como sendo executado quando a
é falso (anexando assim o else ao primeiro se) ou quando a
é verdadeiro e b
é falso (anexando assim o else ao segundo se ) Em outras palavras, pode-se ver a declaração anterior como uma das seguintes expressões:
if a then (if b then s) else s2 if a then (if b then s else s2)
O problema do else pendente data do ALGOL 60 e foi resolvido de várias maneiras nas línguas subsequentes. Em analisadores LR , o resto pendente é o exemplo arquetípico de um conflito de redução de mudança .
Evitando ambigüidade, mantendo a sintaxe
Este é um problema que freqüentemente surge na construção do compilador , especialmente na análise sem scanner . A convenção ao lidar com o else pendente é anexar o else à instrução if próxima, permitindo gramáticas livres de contexto não ambíguas, em particular. Linguagens de programação como Pascal, C e Java seguem essa convenção, então não há ambigüidade na semântica da linguagem , embora o uso de um gerador de analisador possa levar a gramáticas ambíguas . Nestes casos, o agrupamento alternativo é realizado por blocos explícitos, como begin...end
em Pascal e {...}
em C.
Dependendo da abordagem de construção do compilador, pode-se tomar diferentes ações corretivas para evitar ambigüidade:
- Se o analisador for produzido por um gerador de analisador SLR, LR (1) ou LALR LR , o programador frequentemente dependerá do recurso de analisador gerado de preferir a mudança em vez de reduzir sempre que houver um conflito. Como alternativa, a gramática pode ser reescrita para remover o conflito, às custas de um aumento no tamanho da gramática (veja abaixo ).
- Se o analisador for escrito à mão, o programador pode usar uma gramática livre de contexto não ambígua . Alternativamente, pode-se contar com uma gramática não livre de contexto ou uma gramática de expressão de análise .
Evitando ambigüidade, alterando a sintaxe
O problema também pode ser resolvido tornando explícita a ligação entre um outro e seu se, dentro da sintaxe. Isso geralmente ajuda a evitar erros humanos.
As soluções possíveis são:
- Ter uma declaração "end if". Exemplos dessas linguagens são ALGOL 68 , Ada , Eiffel , PL / SQL , Visual Basic e AppleScript .
- Não permitindo que a instrução após um "then" seja um "if" em si (pode, entretanto, ser um par de colchetes de instrução contendo apenas uma cláusula if-then). Essa abordagem é seguida pelo ALGOL 60 .
- Exigindo colchetes (parênteses) quando um "outro" segue um "se".
- Exigindo que todo "se" seja emparelhado com um "outro". Para evitar um problema semelhante em relação à semântica em vez de sintaxe, Racket se desvia do Scheme ao considerar um
if
erro sem uma cláusula de fallback, distinguindo efetivamente expressões condicionais (ou sejaif
) de declarações condicionais (ou seja ,when
eunless
, que não têm cláusulas de fallback). - Usando palavras-chave diferentes para as instruções "if" de uma alternativa e duas alternativas. S-algol usa
if e do s
para o caso de uma alternativa eif e1 then e2 else e3
para o caso geral. - Exigindo colchetes incondicionalmente, como Swift e Modula-2 . Isso é efetivamente verdadeiro no Python, pois suas regras de recuo delimitam cada bloco, não apenas aqueles nas instruções "if". Para reduzir a desordem resultante, Modula-2 elimina o abridor de bloco em níveis não funcionais.
Exemplos
Seguem exemplos concretos.
C
Em C , a gramática lê, em parte:
statement = ... | selection-statement selection-statement = ... | IF ( expression ) statement | IF ( expression ) statement ELSE statement
Assim, sem outras regras, o enunciado
if (a) if (b) s; else s2;
poderia ser analisado ambiguamente como se fosse:
if (a)
{
if (b)
s;
else
s2;
}
ou:
if (a)
{
if (b)
s;
}
else
s2;
Na prática, em C, a primeira árvore é escolhida, associando a else
com a mais próxima if
.
Evitando o conflito em analisadores LR
O exemplo acima pode ser reescrito da seguinte maneira para remover a ambigüidade:
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: ... ;
Quaisquer outras regras gramaticais relacionadas a instruções também podem ter que ser duplicadas dessa maneira se elas podem terminar direta ou indiretamente com um statement
ou selection-statement
não-terminal.
No entanto, fornecemos uma gramática que inclui instruções if e 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: ... ;
Finalmente, fornecemos a gramática que proíbe declarações IF ambíguas.
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: ... ;
Com esta gramática, a análise de "if (a) if (b) c else d" falha:
statement open_statement IF '(' expression ')' closed_statement ELSE open_statement 'if' '(' 'a' ')' closed_statement 'else' 'd'
e, em seguida, a análise falha ao tentar corresponder closed_statement
a "if (b) c". Uma tentativa com closed_statement
falha da mesma maneira.