Das ist eine ziemlich vage Frage, denke ich, allein schon wegen der Tiefe des Themas. Ein Compiler kann jedoch in zwei separate Teile zerlegt werden: eine obere Hälfte und eine untere Hälfte. Die obere Hälfte nimmt im Allgemeinen die Quellsprache und wandelt sie in eine Zwischendarstellung um, und die untere Hälfte kümmert sich um die plattformspezifische Codegenerierung.
Nichtsdestotrotz ist eine Idee für einen einfachen Weg, sich diesem Thema zu nähern (zumindest die, die wir in meinem Compiler-Kurs verwendet haben), den Compiler in den beiden oben beschriebenen Teilen zu bauen. Insbesondere erhalten Sie einen guten Überblick über den gesamten Prozess, wenn Sie nur die obere Hälfte bauen.
Wenn Sie nur die obere Hälfte machen, können Sie die Erfahrung sammeln, wie man den lexikalischen Analysator und den Parser schreibt, und dann mit der Erzeugung von "Code" beginnen (die erwähnte Zwischendarstellung). Es nimmt also Ihr Quellprogramm und konvertiert es in eine andere Repräsentation und führt einige Optimierungen durch (wenn Sie wollen), was das Herzstück eines Compilers ist. Die untere Hälfte nimmt dann diese Zwischendarstellung und erzeugt die Bytes, die benötigt werden, um das Programm auf einer bestimmten Architektur auszuführen. Die untere Hälfte nimmt zum Beispiel Ihre Zwischendarstellung und erzeugt eine ausführbare PE-Datei.
Einige Bücher zu diesem Thema, die ich besonders hilfreich fand, waren Compiler - Prinzipien und Techniken (oder das Drachenbuch, wegen des niedlichen Drachens auf dem Umschlag). Es enthält einige großartige Theorien und behandelt kontextfreie Grammatiken auf eine wirklich zugängliche Weise. Für die Erstellung des lexikalischen Analysators und Parsers werden Sie wahrscheinlich die *nix-Tools lex und yacc verwenden. Und uninteressanterweise heißt das Buch " lex und yacc " knüpft dort an, wo das Drachenbuch mit diesem Teil aufgehört hat.