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 s1uma forma if-then-else:

if a then if b then s else s2

Neste exemplo, sé executado inequivocamente quando aé verdadeiro e bverdadeiro, mas pode-se interpretar s2como 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...endem 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 iferro sem uma cláusula de fallback, distinguindo efetivamente expressões condicionais (ou seja if) de declarações condicionais (ou seja , whene unless, 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 spara o caso de uma alternativa e if e1 then e2 else e3para 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 elsecom 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 statementou selection-statementnã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_statementa "if (b) c". Uma tentativa com closed_statementfalha da mesma maneira.

Veja também

Referências