Dato che vuoi imparare come funzionano i lexer, presumo che tu voglia veramente sapere come funzionano i generatori di lexer.
Un generatore di lexer prende una specifica lessicale, che è un elenco di regole (coppie di token di espressioni regolari) e genera un lexer. Questo lexer risultante può quindi trasformare una stringa di input (carattere) in una stringa di token in base a questo elenco di regole.
Il metodo più comunemente usato consiste principalmente nel trasformare un'espressione regolare in un automix finito deterministico (DFA) tramite un automa non deterministico (NFA), più alcuni dettagli.
Una guida dettagliata su questa trasformazione può essere trovata qui . Nota che non l'ho letto da solo, ma sembra abbastanza buono. Inoltre, quasi tutti i libri sulla costruzione del compilatore presenteranno questa trasformazione nei primi capitoli.
Se sei interessato alle diapositive delle lezioni sui corsi sull'argomento, non ci sono dubbi su una quantità infinita di loro da corsi sulla costruzione di compilatori. Dalla mia università, puoi trovare tali diapositive qui e here .
Ci sono poche altre cose che non sono comunemente usate nei lexer o trattate nei testi, ma sono comunque molto utili:
In primo luogo, la gestione di Unicode è in qualche modo non banale. Il problema è che l'input ASCII è largo solo 8 bit, il che significa che puoi facilmente avere una tabella di transizione per ogni stato in DFA, perché hanno solo 256 voci. Tuttavia, Unicode, essendo largo 16 bit (se si utilizza UTF-16), richiede 64k tabelle per ogni voce in DFA. Se hai grammatiche complesse, questo potrebbe iniziare a occupare un po 'di spazio. Il riempimento di queste tabelle inizia anche a richiedere un po 'di tempo.
In alternativa, è possibile generare alberi intervallati. Un albero di intervallo può contenere le tuple ('a', 'z'), ('A', 'Z') per esempio, che è molto più efficiente della memoria rispetto alla tabella completa. Se si mantengono intervalli non sovrapposti, è possibile utilizzare qualsiasi albero binario bilanciato per questo scopo. Il tempo di esecuzione è lineare nel numero di bit necessari per ogni carattere, quindi O (16) nel caso Unicode. Tuttavia, nel migliore dei casi, di solito sarà un po 'meno.
Un altro problema è che i lexer generati comunemente hanno in realtà una prestazione quadratica nel caso peggiore. Anche se questo comportamento nel caso peggiore non è comunemente visto, potrebbe morderti. Se incontri il problema e vuoi risolverlo, puoi trovare un articolo che descrive come ottenere il tempo lineare qui .
Probabilmente vorrai essere in grado di descrivere le espressioni regolari in forma di stringa, come normalmente appaiono. Tuttavia, l'analisi di queste descrizioni di espressioni regolari in NFA (o forse prima in una struttura intermedia ricorsiva) è un po 'un problema di uovo di gallina. Per analizzare le descrizioni delle espressioni regolari, l'algoritmo Shunting Yard è molto adatto. Wikipedia sembra avere una pagina estesa su l'algoritmo .