Da Sie lernen möchten, wie Lexer funktionieren, möchten Sie vermutlich wissen, wie Lexer-Generatoren funktionieren.
Ein Lexer-Generator verwendet eine lexikalische Spezifikation, bei der es sich um eine Liste von Regeln (Token-Paare aus regulären Ausdrücken) handelt, und generiert einen Lexer. Dieser resultierende Lexer kann dann eine Eingabe- (Zeichen-) Zeichenfolge gemäß dieser Liste von Regeln in eine Token-Zeichenfolge umwandeln.
Die am häufigsten verwendete Methode besteht hauptsächlich darin, einen regulären Ausdruck über einen nicht deterministischen Automaten (NFA) und einige Details in einen deterministischen endlichen Automaten (DFA) umzuwandeln.
Eine ausführliche Anleitung zur Durchführung dieser Transformation finden Sie hier . Beachten Sie, dass ich es nicht selbst gelesen habe, aber es sieht ganz gut aus. Außerdem wird in den ersten Kapiteln so gut wie jedes Buch zum Thema Compilerkonstruktion diese Transformation enthalten.
Wenn Sie sich für Vorlesungsfolien von Lehrveranstaltungen zum Thema interessieren, gibt es zweifellos unendlich viele von ihnen aus Lehrveranstaltungen zur Compilerkonstruktion. An meiner Universität finden Sie solche Folien hier und hier .
Es gibt noch einige Dinge, die normalerweise nicht in Lexern verwendet oder in Texten behandelt werden, die aber dennoch sehr nützlich sind:
Erstens ist der Umgang mit Unicode nicht ganz einfach. Das Problem ist, dass die ASCII-Eingabe nur 8 Bit breit ist. Dies bedeutet, dass Sie problemlos eine Übergangstabelle für jeden Status im DFA erstellen können, da sie nur 256 Einträge enthält. Da Unicode jedoch 16 Bit breit ist (wenn Sie UTF-16 verwenden), sind für jeden Eintrag im DFA 64-KB-Tabellen erforderlich. Wenn Sie komplexe Grammatiken haben, kann dies sehr viel Platz in Anspruch nehmen. Das Befüllen dieser Tische nimmt ebenfalls viel Zeit in Anspruch.
Alternativ können Sie Intervallbäume generieren. Ein Bereichsbaum kann beispielsweise die Tupel ('a', 'z'), ('A', 'Z') enthalten. Dies ist viel speichereffizienter als die vollständige Tabelle. Wenn Sie nicht überlappende Intervalle beibehalten, können Sie für diesen Zweck einen beliebigen ausgeglichenen Binärbaum verwenden. Die Laufzeit ist linear in der Anzahl der Bits, die Sie für jedes Zeichen benötigen, also O (16) im Unicode-Fall. Im besten Fall wird es jedoch in der Regel einiges weniger sein.
Ein weiteres Problem ist, dass die Lexer, wie sie üblicherweise erzeugt werden, tatsächlich eine quadratische Leistung im ungünstigsten Fall haben. Obwohl dieses Verhalten im schlimmsten Fall nicht häufig vorkommt, kann es Sie beißen. Wenn Sie in das Problem laufen und es lösen wollen, ein Papier beschreibt , wie die lineare Zeit zu erreichen , kann gefunden werden hier .
Möglicherweise möchten Sie reguläre Ausdrücke so beschreiben können, wie sie normalerweise angezeigt werden. Das Parsen dieser Beschreibungen regulärer Ausdrücke in NFAs (oder möglicherweise zuerst in eine rekursive Zwischenstruktur) ist jedoch ein kleines Henne-Ei-Problem. Zum Parsen von Beschreibungen regulärer Ausdrücke ist der Shunting Yard-Algorithmus sehr gut geeignet. Wikipedia scheint eine umfangreiche Seite über den Algorithmus zu haben .