I preprocessori sintattici sono stati introdotti con la famiglia di linguaggi Lisp. Il loro ruolo è quello di trasformare gli alberi sintattici secondo un certo numero di regole definite dall’utente. Per alcuni linguaggi di programmazione, le regole sono scritte nello stesso linguaggio del programma (riflessione a tempo di compilazione). Questo è il caso di Lisp e OCaml. Alcuni altri linguaggi si basano su un linguaggio completamente esterno per definire le trasformazioni, come il preprocessore XSLT per XML, o la sua controparte staticamente tipizzata CDuce.
I preprocessori sintattici sono tipicamente usati per personalizzare la sintassi di un linguaggio, estendere un linguaggio aggiungendo nuove primitive, o incorporare un linguaggio di programmazione specifico del dominio (DSL) dentro un linguaggio generico.
Personalizzazione della sintassiEdit
Un buon esempio di personalizzazione della sintassi è l’esistenza di due diverse sintassi nel linguaggio di programmazione Objective Caml. I programmi possono essere scritti indifferentemente usando la “sintassi normale” o la “sintassi riveduta”, e possono essere graficamente stampati con entrambe le sintassi su richiesta.
Similmente, un certo numero di programmi scritti in OCaml personalizzano la sintassi del linguaggio tramite l’aggiunta di nuovi operatori.
Estensione di un linguaggioModifica
I migliori esempi di estensione del linguaggio tramite macro si trovano nella famiglia dei linguaggi Lisp. Mentre i linguaggi, da soli, sono semplici nuclei funzionali tipizzati dinamicamente, le distribuzioni standard di Scheme o Common Lisp permettono la programmazione imperativa o orientata agli oggetti, così come la tipizzazione statica. Quasi tutte queste caratteristiche sono implementate dal preprocessing sintattico, anche se va notato che la fase di “espansione delle macro” della compilazione è gestita dal compilatore in Lisp. Questa può ancora essere considerata una forma di pre-elaborazione, poiché avviene prima di altre fasi di compilazione.
Specializzare un linguaggioModifica
Una delle caratteristiche insolite della famiglia di linguaggi Lisp è la possibilità di usare macro per creare un DSL interno. Tipicamente, in un grande progetto basato su Lisp, un modulo può essere scritto in una varietà di tali minilinguaggi, uno forse usando un dialetto di Lisp basato su SQL, un altro scritto in un dialetto specializzato per GUI o pretty-printing, ecc. La libreria standard di Common Lisp contiene un esempio di questo livello di astrazione sintattica nella forma della macro LOOP, che implementa un minilinguaggio simile all’Algol per descrivere l’iterazione complessa, mentre permette ancora l’uso di operatori Lisp standard.
Il preprocessore/linguaggio MetaOCaml fornisce caratteristiche simili per DSL esterni. Questo preprocessore prende la descrizione della semantica di un linguaggio (cioè un interprete) e, combinando l’interpretazione a tempo di compilazione e la generazione di codice, trasforma quella definizione in un compilatore per il linguaggio di programmazione OCaml e da quel linguaggio, sia in bytecode che in codice nativo.