Syntactic preprocessors were introduced with the
Lisp family of languages. Their role is to transform syntax trees according to a number of user-defined rules. For some programming languages, the rules are written in the same language as the program (compile-time reflection). This is the case with
Lisp and
OCaml. Some other languages rely on a fully external language to define the transformations, such as the
XSLT preprocessor for
XML, or its statically typed counterpart CDuce. Syntactic preprocessors are typically used to customize the syntax of a language, extend a language by adding new primitives, or embed a
domain-specific programming language (DSL) inside a general purpose language.
Customizing syntax A good example of syntax customization is the existence of two different syntaxes in the
Objective Caml programming language. Programs may be written indifferently using the "normal syntax" or the "revised syntax", and may be pretty-printed with either syntax on demand. Similarly, a number of programs written in
OCaml customize the syntax of the language by the addition of new operators.
Extending a language The best examples of language extension through macros are found in the
Lisp family of languages. While the languages, by themselves, are simple dynamically typed functional cores, the standard distributions of
Scheme or
Common Lisp permit imperative or object-oriented programming, as well as static typing. Almost all of these features are implemented by syntactic preprocessing, although it bears noting that the "macro expansion" phase of compilation is handled by the compiler in Lisp. This can still be considered a form of preprocessing, since it takes place before other phases of compilation.
Specializing a language One of the unusual features of the
Lisp family of languages is the possibility of using macros to create an internal DSL. Typically, in a large
Lisp-based project, a module may be written in a variety of such
minilanguages, one perhaps using a
SQL-based dialect of
Lisp, another written in a dialect specialized for
GUIs or pretty-printing, etc.
Common Lisp's standard library contains an example of this level of syntactic abstraction in the form of the LOOP macro, which implements an Algol-like minilanguage to describe complex iteration, while still enabling the use of standard Lisp operators. The
MetaOCaml preprocessor/language provides similar features for external DSLs. This preprocessor takes the description of the semantics of a language (i.e. an interpreter) and, by combining compile-time interpretation and code generation, turns that definition into a compiler to the
OCaml programming language—and from that language, either to bytecode or to native code. ==General purpose preprocessor==