Alla ricerca di una chiara definizione di cosa sono un "tokenizer", un "parser" e un "lexer" e come sono collegati tra loro e utilizzati?


151

Sto cercando una chiara definizione di cosa sono un "tokenizer", un "parser" e un "lexer" e come sono collegati tra loro (ad esempio, un parser usa un tokenizer o viceversa)? Devo creare un programma che passerà attraverso i file sorgente c / h per estrarre la dichiarazione dei dati e le definizioni.

Ho cercato esempi e posso trovare alcune informazioni, ma sto davvero lottando per afferrare i concetti sottostanti come regole grammaticali, alberi di analisi e albero di sintassi astratto e come si collegano tra loro. Alla fine questi concetti devono essere archiviati in un programma reale, ma 1) come sono, 2) ci sono implementazioni comuni.

Ho esaminato Wikipedia su questi argomenti e programmi come Lex e Yacc, ma non avendo mai seguito una classe di compilatore (EE major), trovo difficile capire appieno cosa sta succedendo.

Risposte:


166

Un tokenizer suddivide un flusso di testo in token, in genere cercando spazi bianchi (schede, spazi, nuove righe).

Un lexer è fondamentalmente un tokenizer, ma di solito attribuisce ulteriore contesto ai token: questo token è un numero, quel token è un valore letterale di stringa, l'altro token è un operatore di uguaglianza.

Un parser prende il flusso di token dal lexer e lo trasforma in un albero di sintassi astratto che rappresenta il (solitamente) programma rappresentato dal testo originale.

L'ultima volta che ho controllato, il miglior libro sull'argomento era "Compilatori: principi, tecniche e strumenti" di solito conosciuti come "Il libro del drago".


8
Senza dubbio "The Dragon Book" è un buon libro, ma richiede che il lettore abbia una buona conoscenza di CS. Un libro con un richiamo più pratico sarebbe "Scrivere compilatori e interpreti" di Ronald Mak, "Modern Compiler Implementation", Andrew Appel; "Costruzione del compilatore", Niklaus Wirth; "Compilazione con C # e Java" e "Compilatori e generatori di compilatori: un'introduzione con C ++" di Pat Terry; e, naturalmente, "The Definitive ANTLR Reference" di Terrence Parr.
Andre Artus,

5
Giusto per essere sicuro, non sto bussando alla tua raccomandazione. "The Dragon Book" è stato il mio primo libro sulla tecnologia dei compilatori, ma è stato difficile se paragonato, per esempio, al libro di Wirth, che è un libro che puoi ascoltare in poche ore. All'epoca avevo poche opzioni in quanto era l'unico libro su cui potevo mettere le mani (era il 1991, prima di Amazon e del WWW). L'ho avuto e una raccolta di file di testo prodotti da Jack W. Crenshaw chiamato "COSTRUIAMO UN COMPILATORE" (grazie Jack!). Questo è ancora il libro da ottenere per una comprensione più completa dei principi, ma la maggior parte dei programmatori ha solo bisogno di un'introduzione pragmatica.
Andre Artus,

10
Non sarei d'accordo che un parser / per definizione / produca un albero di sintassi astratto. I parser possono produrre tutti i tipi di output diversi. Ad esempio, è comune che un parser produca una sequenza di chiamate ad alcune interfacce del builder - vedi Builder Pattern nel libro Gang of Four patterns. Il punto chiave è che il parser analizza una sequenza di token per determinare se la sequenza è conforme o meno ad una grammatica (solitamente senza contesto) e può produrre un output basato sulla struttura grammaticale della sequenza.
Theodore Norvell,

2
"Let's Build a Compiler" è qui: compilers.iecc.com/crenshaw . Ho trovato il link da qui: prog21.dadgum.com/30.html
Roger Lipscombe,

1
@Pithkos: se questi sono gli unici vincoli, tutto ciò che hai detto è che la funzione accetta un input in un dominio (matematico) senza nome e produce e produce in un altro dominio senza nome, ad es. F (X) -> Y Praticamente questo significa puoi solo chiamarla "funzione". Se insisti che il dominio di X è <StreamOfCharacter, Grammar> e il dominio di Y è Tree con la proprietà che riflette la forma della grammatica, allora F (X, G) -> T sarebbe qualcosa che chiamerei un parser. Spesso curry F rispetto a G perché G non cambia spesso, quindi F [G] (X) -> T è ciò che comunemente vedi come parser.
Ira Baxter,

18

Esempio:

int x = 1;

Un lexer o un tokeniser lo suddividerà in token 'int', 'x', '=', '1', ';'.

Un parser prenderà quei token e li userà per capire in qualche modo:

  • abbiamo una dichiarazione
  • è una definizione di un numero intero
  • l'intero si chiama 'x'
  • 'x' deve essere inizializzato con il valore 1

9
Un lexer noterà che "int", "=" e ";" sono token senza ulteriore significato, che "x" è un nome identificativo o qualcosa del genere, il valore "x" e "1" è un numero intero o un numero, il valore "1". Un tokenizer non lo farà necessariamente.
David Thornley,

5

Direi che un lexer e un tokenizer sono sostanzialmente la stessa cosa e che dividono il testo nelle sue parti componenti (i "token"). Il parser quindi interpreta i token usando una grammatica.

Non sarei troppo impiccato per un uso terminologico preciso: le persone spesso usano il "parsing" per descrivere qualsiasi azione di interpretazione di un grumo di testo.


1
Con i parser PEG la distinzione tra tokenizer e parser è ancora meno chiara.
Andre Artus,

0

( aggiungendo alle risposte fornite )

  • Tokenizer rimuoverà anche tutti i commenti e restituirà i token solo alla Lexer.
  • Lexer definirà anche gli ambiti per tali token (variabili / funzioni)
  • Il parser costruirà quindi la struttura del codice / programma

1
Ciao @downvoter, puoi approfondire il motivo per cui hai effettivamente votato?
Koray Tugay,

1
Non sono il downvoter, ma penso che il downvote potrebbe essere stato perché la tua risposta non sembra corretta. Un tokenizer può rimuovere il rumore (in genere spazi bianchi ma forse anche commenti), ma spesso non alimenta il lexer. Un lexer basato su DFA tokenizzerà e identificherà quali token sono (ad esempio un numero, una stringa, un identificatore, ma anche uno spazio bianco o un commento), ma non può ambito questi poiché ciò richiederebbe l'albero di sintassi che sarà successivamente costruito da il parser.
Lucero,

1) Non capisco la tua apparente distinzione tra "lexer" e "tokenizer". Ho creato parser per oltre 50 lingue e non ho mai avuto due meccanismi separati che dividono il testo di origine in atomi, quindi per me questi sono solo sinonimi. 2) Se stai compilando, rimuovere commenti e spazi bianchi ha senso nel lexer. Se stai creando strumenti di trasformazione da fonte a fonte, non puoi perdere i commenti perché devono riapparire nel testo trasformato. Quindi rimuovere SEMPRE i commenti è sbagliato; possiamo discutere su come si riesce a preservare gli spazi bianchi. ...
Ira Baxter,

1
... [Gli strumenti che costruisco (vedi la mia biografia) catturano entrambi con adeguata fedeltà per riprodurli nel codice trasformato; andiamo oltre e catturiamo il formato degli atomi, comprese cose strane come le virgolette usate sulle stringhe di caratteri e il conteggio zero / iniziale zero sui numeri, il tutto al servizio di evitare che l'utente rifiuti il ​​risultato trasformato. Allora, cosa vi siete persi non solo fanno lexer non necessariamente mettono a nudo le informazioni, ma in realtà possono avere bisogno di informazioni di acquisizione sopra e al di là del token RAW]. ....
Ira Baxter,

... 3) I Lexer definiscono "ambiti" solo in parser irrimediabilmente goffi che fanno fatica a gestire le ambiguità sintattiche. I parser C e C ++ sono l'esempio canonico; vedere la mia discussione su stackoverflow.com/a/1004737/120163 ). Uno non deve farlo in quel (brutto) modo. Quindi trovo la tua risposta semplicemente sbagliata.
Ira Baxter,
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.