Syntaktische Präprozessoren wurden mit der Lisp-Sprachfamilie eingeführt. Ihre Aufgabe ist es, Syntaxbäume nach einer Reihe von benutzerdefinierten Regeln zu transformieren. Bei einigen Programmiersprachen werden die Regeln in der gleichen Sprache wie das Programm geschrieben (Reflexion zur Kompilierzeit). Dies ist der Fall bei Lisp und OCaml. Einige andere Sprachen verlassen sich auf eine vollständig externe Sprache, um die Transformationen zu definieren, wie der XSLT-Präprozessor für XML oder sein statisch typisiertes Gegenstück CDuce.
Syntaktische Präprozessoren werden typischerweise verwendet, um die Syntax einer Sprache anzupassen, eine Sprache durch Hinzufügen neuer Primitive zu erweitern oder eine domänenspezifische Programmiersprache (DSL) in eine allgemeine Sprache einzubetten.
Anpassen der SyntaxBearbeiten
Ein gutes Beispiel für die Anpassung der Syntax ist das Vorhandensein von zwei verschiedenen Syntaxen in der Programmiersprache Objective Caml. Programme können wahlweise mit der „normalen Syntax“ oder der „überarbeiteten Syntax“ geschrieben und bei Bedarf mit einer der beiden Syntaxen hübsch ausgedruckt werden.
In ähnlicher Weise passen eine Reihe von Programmen, die in OCaml geschrieben wurden, die Syntax der Sprache durch das Hinzufügen neuer Operatoren an.
Erweitern einer SpracheBearbeiten
Die besten Beispiele für Spracherweiterungen durch Makros finden sich in der Lisp-Sprachfamilie. Während die Sprachen an sich einfache dynamisch typisierte funktionale Kerne sind, erlauben die Standarddistributionen von Scheme oder Common Lisp imperative oder objektorientierte Programmierung sowie statische Typisierung. Fast alle diese Funktionen werden durch syntaktische Vorverarbeitung implementiert, obwohl die „Makroexpansion“ in Lisp vom Compiler übernommen wird. Dies kann immer noch als eine Form der Vorverarbeitung angesehen werden, da sie vor anderen Phasen der Kompilierung stattfindet.
Spezialisierung einer SpracheBearbeiten
Eine der ungewöhnlichen Eigenschaften der Lisp-Sprachfamilie ist die Möglichkeit, Makros zur Erstellung einer internen DSL zu verwenden. Typischerweise kann in einem großen Lisp-basierten Projekt ein Modul in einer Vielzahl solcher Minisprachen geschrieben werden, eine vielleicht in einem SQL-basierten Dialekt von Lisp, eine andere in einem Dialekt, der für GUIs oder Pretty-Printing spezialisiert ist, usw. Die Standardbibliothek von Common Lisp enthält ein Beispiel für diese Ebene der syntaktischen Abstraktion in Form des LOOP-Makros, das eine Algol-ähnliche Minisprache zur Beschreibung komplexer Iterationen implementiert und gleichzeitig die Verwendung von Standard-Lisp-Operatoren ermöglicht.
Der MetaOCaml-Präprozessor/die MetaOCaml-Sprache bietet ähnliche Funktionen für externe DSLs. Dieser Präprozessor nimmt die Beschreibung der Semantik einer Sprache (d.h. eines Interpreters) und verwandelt diese Definition durch die Kombination von Kompilierzeitinterpretation und Codegenerierung in einen Compiler für die OCaml-Programmiersprache – und von dieser Sprache entweder in Bytecode oder in nativen Code.