Il mio lexer dovrebbe consentire quello che è ovviamente un errore di sintassi?

0

Questo è un po 'come una versione concreta della domanda In arrivo con i token per un lexer .

Sto scrivendo un lexer per un piccolo sottoinsieme di HTML. Mi chiedo che cosa dovrei fare quando il flusso di input termina e sono in uno stato in cui ho riconosciuto correttamente un token, ma so che sarà un errore di sintassi.

Sottolineo "lo so" perché questo è l'essere umano che conosco, perché sono consapevole delle regole grammaticali che sono "regole del parser" (rispetto alle "regole del lexer"). So che questo non è corretto: <b>hello</b , ma non c'è nulla che impedisca al lexer di emettere quanto segue.

Token: BEGIN-OPEN-TAG
Token: TAG-NAME           Value: b
Token: END-TAG
Token: DATA               Value: hello
Token: BEGIN-CLOSE-TAG    
Token: TAG-NAME           Value: b

Quindi parser prenderebbe questo come un errore e lo segnalerà. La ragione per cui so che posso lanciare un errore prima è solo perché sono a conoscenza del parser e delle regole definite lì. Ottengo dei vantaggi dal contrassegnare questo come sequenza di token non valida, o dovrei provare a mantenere tale logica lontana dal lexer? Quando dovrebbe mai un lexer emettere un errore?

Dovrebbe consentire <b<hello</b> allora? Come dovrebbe un lexer gestire un < casuale nel mezzo del testo: The \lt sign is <b><</b> ? Tornando indietro? O dovrei registrarlo come [data] [<] [tagname] [>] [<] [</] [tagname] [>] e poi far sapere al parser che [<] è valido nel mezzo dei dati?

Quanto sopra non sono domande su cui mi aspetto una risposta, ma più di "se decido sulla domanda di cui sopra, allora è un abisso di linee più sfocate, motivo per cui ho tutti questi dubbi". Sto attraversando un periodo difficile per decidere a cosa dovrebbe interessare il lexer. Se lo faccio preoccupare troppo, sto creando un parser allo stesso tempo. Se non lo faccio abbastanza, sto praticamente facendo una procedura "split at whitespace".

    
posta Lazar Ljubenović 02.12.2018 - 20:31
fonte

4 risposte

8

Il tuo lexer non sarà mai in grado di diagnosticare tutti gli errori di sintassi a meno che tu non lo renda potente come lo stesso parser. Si tratterebbe di una grande e totalmente inutile quantità di lavoro, e l'unico vantaggio sarebbe che i documenti illegali sono riconosciuti come illegali molto leggermente più veloci. Questo non è un valore sufficiente per il prezzo elevato.

Quindi dovresti mantenere il tuo lexer il più semplice possibile, emettendo primitive e non preoccupandoti del suo piccolo punto sulle regole di sintassi. Il tuo codice base sarà molto più facile da capire se ogni componente fa una sola cosa.

    
risposta data 02.12.2018 - 20:39
fonte
2

In primo luogo un avvertimento: Dipende molto da quale sottoinsieme di HTML. HTML5 non ha affatto il concetto di errore. Fondamentalmente ogni sequenza di caratteri è valida e ha un analisi definita. Assumerò che stai analizzando qualcosa di simile a XHTML.

In teoria abbiamo una netta separazione tra lexer e parser: Lexer divide un testo in una sequenza di token e quindi il parser costruisce un albero fuori dai token secondo la grammatica. Ogni componente dovrebbe generare un errore se non è in grado di completare il suo compito. Quindi un lexer dovrebbe lanciare un errore se non può produrre un token valido (ad esempio se c'è una stringa non chiusa, o una sequenza di caratteri che non è un token valido), mentre gli errori grammaticali da token validi dovrebbero essere rilevati dal parser.

Non c'è una buona ragione per fare in modo che lo scanner rilevi errori che verranno rilevati dal parser, poiché si tratterebbe di una duplicazione della logica, che è una cosa negativa. (Il parser potrebbe essere leggermente più veloce nel rilevare determinati errori, ma nel complesso sarebbe probabilmente più lento in generale se si hanno i controlli ridondanti, quindi si sarebbe ottimizzando l'analisi dei documenti non validi mentre si penalizza l'analisi dei documenti validi. Non è un ottimo scambio!)

Ma in realtà molte lingue sono progettate in un modo che rende necessario sfocare questa netta separazione. HTML non ha una sola grammatica lessicale. Ne ha almeno due: uno per i contenuti dei tag e uno per i cdata (il testo al di fuori dei tag). Ad esempio il testo Hello world sarebbe due token all'interno di un tag ma un token come cdata. Quindi lo scanner deve almeno tenere traccia del più recente < o > per utilizzare la modalità di scansione corretta.

Considerarlo come due distinte grammatiche lessicali significa << o <b< è un errore a livello di scanner, perché% non_quota% non è un carattere valido all'interno di un tag, non può produrre un token.

Ma vogliamo ancora evitare la duplicazione della logica, quindi se lo scanner, per necessità, fa il lavoro di riconoscere i tag come unità, non c'è motivo di scartare queste informazioni. Invece di emettere solo una sequenza di token che costituiscono un tag, lo scanner potrebbe emettere un tag come una struttura che contatta i suoi token. Il parser analizzerà quindi i token costitutivi in tagname e attributi e così via, e al livello successivo la coppia inizia e termina tag in una struttura ad albero della linea DOM.

    
risposta data 02.12.2018 - 23:02
fonte
1

Dipende dall'ambito della lingua di destinazione e dai casi d'uso. Se si desidera considerare </b> (ma non </ b> ) come parola chiave, il lexer può identificarlo come tale. </b verrebbe probabilmente rilasciato come < / b , che il parser può quindi lanciare come < is unexpected token o no </b> for <b> che sembra soddisfacente.

Ma è probabilmente meglio solo per lessare i caratteri e lasciare che il parser combini < / b > in un tag di chiusura. Ti consente di riutilizzare il comportamento per costruire tutti dei tag di chiusura, rendendo più coerenti il comportamento (e le condizioni di errore). E generalmente il parser ha più contesto per fornire un messaggio di errore più bello e utile del lexer.

    
risposta data 02.12.2018 - 20:40
fonte
0

Il tuo lexer dovrebbe rilevare l'errore di sintassi per i token malformati, e solo questo. Ma in generale, i token dovrebbero essere abbastanza complessi da evitare di restituire token il cui unico scopo è delimitare l'inizio o la fine di altri token.

Ad esempio, sarebbe più pratico considerare < *([a-zA-Z])+ *> come un singolo token di apertura e lasciare che il lexer catturi i token sconosciuti (come <> ). Trattarlo nel parser porterebbe a regole grammaticali complesse non necessarie. Le tue regole grammaticali si concentreranno invece sull'assicurare un recinto adeguato di ciascun tag.

    
risposta data 03.12.2018 - 14:32
fonte

Leggi altre domande sui tag