Giusto per affermare il problema, il problema di Dangling Else è un'ambiguità nella specifica della sintassi del codice in cui potrebbe non essere chiaro, in caso di if e elect successivi, a cui appartiene altrimenti a quale if.
L'esempio più semplice e classico:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
Non è chiaro, per coloro che non conoscono a memoria le specifiche della specifica della lingua, che ifottiene il else(e questo particolare frammento di codice è valido in mezza dozzina di lingue, ma può funzionare diversamente in ciascuna).
Il costrutto Dangling Else rappresenta un potenziale problema per le implementazioni del parser senza scanner, perché la strategia è di snellire il flusso di file di un carattere alla volta, fino a quando il parser vede che ha abbastanza tokenize (digita nell'assembly o nel linguaggio intermedio che sta compilando) . Ciò consente al parser di mantenere uno stato minimo; non appena pensa di avere abbastanza informazioni per scrivere i token che viene analizzato nel file, lo farà. Questo è l'obiettivo finale di un parser senza scanner; compilation veloce, semplice e leggera.
Supponendo che le nuove righe e gli spazi bianchi prima o dopo la punteggiatura non abbiano senso (come nella maggior parte dei linguaggi in stile C), questa affermazione sembrerebbe al compilatore come:
if(conditionA)if(conditionB)doFoo();else doBar;
Perfettamente analizzabile su un computer, quindi vediamo. Ricevo un personaggio alla volta finché non ho:
if(conditionA)
Oh, so cosa significa (in C #), significa " pushcondizione A nello stack di valutazione e quindi chiama brfalseper saltare all'istruzione dopo il punto e virgola successivo se non è vero". In questo momento non vedo un punto e virgola, quindi per ora imposterò il mio salto di salto nello spazio successivo dopo questa istruzione e aumenterò tale offset quando inserirò più istruzioni fino a quando vedrò un punto e virgola. Continuando ad analizzare ...
if(conditionB)
OK, questo analizza una coppia simile di operazioni IL e va immediatamente dopo le istruzioni che ho appena analizzato. Non vedo un punto e virgola, quindi aumenterò l'offset del salto della mia precedente istruzione della lunghezza dei miei due comandi (uno per il push e uno per il break) e continuerò a cercare.
doFoo();
Ok, è facile. Questo è " calldoFoo". Ed è un punto e virgola che vedo? Bene, è fantastico, questa è la fine della linea. Incrementerò entrambi gli offset dei salti dei miei blocchi della lunghezza di questi due comandi e dimenticherò che mi è mai importato. OK, andare avanti ...
else
... Uh Oh. Questo non è così semplice come sembrava. OK, ho dimenticato quello che stavo solo facendo, ma elsesignifica che c'è una frase di interruzione condizionale da qualche parte che ho già visto, quindi lasciami guardare indietro ... sì, eccolo brfalse, subito dopo aver premuto "condizione B" la pila, qualunque cosa fosse. OK, ora ho bisogno di un incondizionato breakcome la prossima affermazione. L'affermazione che seguirà ora è sicuramente l'obiettivo della mia pausa condizionale, quindi mi assicurerò di averla giusta e aumenterò l'interruzione incondizionata che ho inserito. Passando ...
doBar();
Questo è facile. " calldoBar". E c'è un punto e virgola e non ho mai visto parentesi graffe. Quindi, l'incondizionato breakdovrebbe passare alla frase successiva, qualunque essa sia, e posso dimenticare di essermi mai preoccupato.
Quindi, cosa abbiamo ... (nota: sono le 22:00 e non ho voglia di convertire offset bit in esadecimali o compilare l'intera shell IL di una funzione con questi comandi, quindi questo è solo pseudo-IL usando i numeri di riga dove normalmente ci sarebbero offset di byte):
ldarg.1 //conditionA
brfalse <line 6> //jumps to "break"
ldarg.2 //conditionB
brfalse <line 7> //jumps to "call doBar"
call doFoo
break <line 8> //jumps beyond statement in scope
call doBar
<line 8 is here>
Bene, che in realtà viene eseguito correttamente, SE la regola (come nella maggior parte dei linguaggi in stile C) è che la elseva con il più vicino if. Indentato per seguire l'annidamento dell'esecuzione, verrebbe eseguito in questo modo, se condizioneA è falsa, l'intero resto dello snippet viene ignorato:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
... ma lo fa per caso, perché l'interruzione associata ifall'istruzione esterna passa breakall'istruzione alla fine dell'interiore if , che porta il puntatore dell'esecuzione oltre l'intera istruzione. È un salto extra non necessario e, se questo esempio fosse più complesso, potrebbe non funzionare più se analizzato e tokenizzato in questo modo.
Inoltre, cosa accadrebbe se la specifica del linguaggio dicesse che un penzolante elseappartiene al primo if, e se la condizione A è falsa, allora viene eseguita la doBar, mentre se la condizione A è vera ma non la condizione B, allora non accade nulla del genere?
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
Il parser aveva dimenticato il primo ifmai esistito, e quindi questo semplice algoritmo di parser non avrebbe prodotto il codice corretto, per non parlare di un codice efficiente.
Ora, il parser potrebbe essere abbastanza intelligente da ricordare le ifs elseche ha per un tempo più lungo, ma se la specifica della lingua dice che una singola elsedopo due ifs corrisponde alla prima if, ciò causa un problema con due ifs con elses corrispondenti :
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
else
doBaz();
Il parser vedrà il primo else, corrisponderà al primo if, quindi vedrà il secondo e andrà nel panico modalità "cosa diavolo stavo facendo di nuovo". A questo punto, il parser ha ottenuto un sacco di codice in uno stato mutevole che avrebbe preferito piuttosto inviare al filestream di output.
Esistono soluzioni a tutti questi problemi e what-ifs. Ma, o il codice che deve essere così intelligente aumenta la complessità dell'algoritmo del parser, o le specifiche del linguaggio che consentono al parser di essere così stupido aumentano la verbosità del codice sorgente del linguaggio, come richiedendo istruzioni di terminazione come end if, o parentesi che indicano nidificate si blocca se l' ifistruzione ha un else(entrambi comunemente visti in altri stili di linguaggio).
Questo è solo uno, un semplice esempio di un paio di ifaffermazioni e guarda tutte le decisioni che il compilatore ha dovuto prendere, e dove avrebbe potuto facilmente incasinare comunque. Questo è il dettaglio dietro quell'innocua dichiarazione di Wikipedia nella tua domanda.