In quale processo si verifica l'errore di sintassi? (tokenizzazione o analisi)


23

Sto cercando di capire la compilazione e l'interpretazione, passo dopo passo per capire un'immagine totale. Quindi ho fatto una domanda leggendo http://www.cs.man.ac.uk/~pjj/farrell/comp3.html questo articolo

Dice :

La fase successiva del compilatore si chiama Parser. Questa parte del compilatore ha una comprensione della grammatica della lingua. È responsabile dell'identificazione degli errori di sintassi e della traduzione di un programma privo di errori in strutture di dati interne che possono essere interpretate o scritte in un'altra lingua.

Ma non sono riuscito a capire come il tokenizer possa correttamente tokenizzare il flusso dato che ha l'errore di sintassi.

Dovrebbe essere bloccato lì o fornire alcune informazioni errate al parser. Voglio dire, la tokenizzazione non è anche una specie di traduttore?

Quindi, come ha appena superato le righe di codice corrotte durante la tokenizzazione.

C'è un esempio di token all'interno del link sopra nell'intestazione The Tokenizer .

A quanto ho capito la forma del token sembra, se nel token del codice c'è qualcosa di sbagliato verrebbe danneggiato.

Potresti chiarire per favore il mio malinteso?

Risposte:


32

Un tokenizer è solo un'ottimizzazione del parser. È perfettamente possibile implementare un parser senza tokenizer.

Un tokenizer (o lexer o scanner) taglia l'input in un elenco di token. Alcune parti della stringa (commenti, spazi bianchi) vengono generalmente ignorate. Ogni token ha un tipo (il significato di questa stringa nella lingua) e un valore (la stringa che compone il token). Ad esempio, lo snippet di origine PHP

$a + $b

potrebbe essere rappresentato dai token

Variable('$a'),
Plus('+'),
Variable('$b')

Il tokenizer non considera se un token è possibile in questo contesto. Ad esempio, l'input

$a $b + +

produrrebbe felicemente il flusso di token

Variable('$a'),
Variable('$b'),
Plus('+'),
Plus('+')

Quando quindi il parser consuma questi token, noterà che due variabili non possono susseguirsi, e nemmeno due operatori di infix. (Si noti che altre lingue hanno sintassi diverse in cui tale flusso di token può essere legale, ma non in PHP).

Un parser potrebbe comunque non riuscire nella fase di tokenizer. Ad esempio, potrebbe esserci un carattere illegale:

$a × ½ — 3

Un tokenizer PHP non sarebbe in grado di far corrispondere questo input alle sue regole e produrrebbe un errore prima dell'inizio dell'analisi principale.

Più formalmente, i tokenizer vengono utilizzati quando ogni token può essere descritto come una lingua normale . I token possono quindi essere abbinati in modo estremamente efficiente, possibilmente implementati come DFA. Al contrario, la grammatica principale è di solito senza contesto e richiede un algoritmo di analisi più complicato e meno performante come LALR.


Quindi potremmo pensare al tokenizer come parte del parser e l'errore di sintassi può verificarsi sia nella fase di tokenizzazione sia nell'analisi della fase in base al modulo di errore di sintassi. Grazie per il chiarimento.
FZE

4
@FZE: potresti pensare in quel modo, ma è fuorviante. Lexing non è "solo un'ottimizzazione del parser". Piuttosto, il lexing mappa una rappresentazione fisica (una sequenza di caratteri) in una rappresentazione logica (i token rappresentati da quei personaggi). Questo isola il parser dalle minuzie come viene rappresentata la fine di una linea o se si decide di rappresentare una logica e come ando &&qualcos'altro. È (principalmente) separato e diverso dall'analisi. L'ottimizzazione (se presente) è un effetto collaterale quasi accidentale.
Jerry Coffin,

@JerryCoffin grazie per l'ulteriore spiegazione che ha più senso ora.
FZE

2
@JerryCoffin, Amon ha ragione, questa è la differenza non fondamentale. È possibile creare una grammatica BNF coerente che copre entrambe le parti "lexer" e "parser". Di solito classifichiamo le regole in basso livello (es. Numero, operatore addizione) e alto livello (somma), ma la grammatica stessa non fa tale distinzione.
Paul Draper,

1
@PaulDraper Non sono sicuro se separare una lingua normale come prima fase sia la scelta giusta. Ad esempio le coppie abbinate (non regolari) potrebbero essere necessarie per descrivere i letterali di stringa in alcune lingue, ma ha ancora senso gestirli nella prima fase. Evitare / minimizzare il back-tracking sembra una linea guida migliore.
Codici InCos

16

Di solito ti aspetteresti che la maggior parte degli errori di sintassi provenga dal parser, non dal lexer.

Il lexer genererebbe un errore se (e principalmente solo se) c'è qualcosa nell'input che non può essere tokenizzato. In molte lingue, tuttavia, quasi ogni sequenza di caratteri può essere trasformata in token di qualche tipo, quindi gli errori qui sono abbastanza insoliti.

Il parser genererà un errore se l'input contiene token validi, ma quei token non sono organizzati in modo da formare dichiarazioni / espressioni valide nella lingua di destinazione. Questo è molto più comune di regola.


11

Tokenizer divide semplicemente il flusso di caratteri in token. Dal tokenizer POV questo è completamente valido:

1 * * 1

e si traduce in qualcosa del genere: ["1", MULTIPLY, MULTIPLY, "1"] solo il parser può rifiutare tali espressioni - sa che l'operatore moltiplicare non può seguire un altro operatore moltiplicare. Ad esempio in JavaScript questo produce:

Uncaught SyntaxError: Unexpected token *(…)

Ci sono errori che potrebbero essere rilevati dal tokenizer. Ad esempio non finiti stringhe letterali: "abco numeri non validi: 0x0abcdefg. Potrebbero comunque essere segnalati come errori di sintassi:

Uncaught SyntaxError: Unexpected token ILLEGAL

Si noti tuttavia che il token non è stato riconosciuto e viene riportato come ILLEGAL.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.