Dato che vuoi imparare come funzionano i lexer, presumo che tu voglia davvero sapere come funzionano i generatori di lexer.
Un generatore di lexer accetta una specifica lessicale, che è un elenco di regole (coppie di token di espressione regolare) e genera un lexer. Questo lexer risultante può quindi trasformare una stringa di input (carattere) in una stringa token secondo questo elenco di regole.
Il metodo più comunemente usato consiste principalmente nel trasformare un'espressione regolare in automi finiti deterministici (DFA) tramite automi non deterministici (NFA), oltre a qualche dettaglio.
È possibile trovare una guida dettagliata per eseguire questa trasformazione qui . Nota che non l'ho letto da solo, ma sembra abbastanza buono. Inoltre, quasi ogni libro sulla costruzione di compilatori presenterà questa trasformazione nei primi capitoli.
Se sei interessato a diapositive di lezioni di corsi sull'argomento, non vi è dubbio che una quantità infinita di loro dai corsi sulla costruzione di compilatori. Dalla mia università, puoi trovare queste slide qui e qui .
Ci sono alcune altre cose che non sono comunemente impiegate nei lexer o trattate nei testi, ma sono comunque abbastanza utili:
Innanzitutto, la gestione di Unicode è in qualche modo non banale. Il problema è che l'ingresso ASCII è largo solo 8 bit, il che significa che puoi avere facilmente una tabella di transizione per ogni stato nel DFA, perché hanno solo 256 voci. Tuttavia, Unicode, essendo largo 16 bit (se usi UTF-16), richiede 64k tabelle per ogni voce nel DFA. Se hai grammatiche complesse, questo potrebbe iniziare a occupare abbastanza spazio. Anche riempire queste tabelle inizia a richiedere parecchio tempo.
In alternativa, è possibile generare alberi ad intervalli. Un albero di intervallo può contenere le tuple ('a', 'z'), ('A', 'Z'), ad esempio, che è molto più efficiente in termini di 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, sarà di solito un po 'meno.
Un altro problema è che i lexer generati comunemente hanno effettivamente prestazioni quadratiche nel caso peggiore. Sebbene questo comportamento nel peggiore dei casi non sia comune, potrebbe morderti. Se si riscontra un problema e si desidera risolverlo, è possibile trovare un documento che descrive come ottenere un tempo lineare qui .
Probabilmente vorrai essere in grado di descrivere espressioni regolari in forma di stringa, come appaiono normalmente. Tuttavia, analizzare queste descrizioni di espressioni regolari in NFA (o forse prima una struttura intermedia ricorsiva) è un po 'un problema con l'uovo di gallina. Per analizzare le descrizioni delle espressioni regolari, l'algoritmo Shunting Yard è molto adatto. Wikipedia sembra avere una pagina estesa sull'algoritmo .