Les préprocesseurs syntaxiques ont été introduits avec la famille des langages Lisp. Leur rôle est de transformer les arbres syntaxiques selon un certain nombre de règles définies par l’utilisateur. Pour certains langages de programmation, les règles sont écrites dans le même langage que le programme (réflexion à la compilation). C’est le cas de Lisp et OCaml. Certains autres langages s’appuient sur un langage entièrement externe pour définir les transformations, comme le préprocesseur XSLT pour XML, ou son homologue typé statiquement CDuce.
Les préprocesseurs syntaxiques sont généralement utilisés pour personnaliser la syntaxe d’un langage, étendre un langage en ajoutant de nouvelles primitives, ou intégrer un langage de programmation spécifique à un domaine (DSL) à l’intérieur d’un langage général.
Personnalisation de la syntaxeEdit
Un bon exemple de personnalisation de la syntaxe est l’existence de deux syntaxes différentes dans le langage de programmation Objective Caml. Les programmes peuvent être écrits indifféremment en utilisant la « syntaxe normale » ou la « syntaxe révisée », et peuvent être joliment imprimés avec l’une ou l’autre syntaxe sur demande.
De même, un certain nombre de programmes écrits en OCaml personnalisent la syntaxe du langage par l’ajout de nouveaux opérateurs.
Extension d’un langageEdit
Les meilleurs exemples d’extension de langage par des macros se trouvent dans la famille des langages Lisp. Alors que les langages, par eux-mêmes, sont de simples noyaux fonctionnels typés dynamiquement, les distributions standard de Scheme ou Common Lisp permettent une programmation impérative ou orientée objet, ainsi qu’un typage statique. Presque toutes ces caractéristiques sont implémentées par un prétraitement syntaxique, bien qu’il faille noter que la phase d' »expansion de macro » de la compilation est gérée par le compilateur de Lisp. Cela peut encore être considéré comme une forme de prétraitement, puisqu’il a lieu avant les autres phases de compilation.
Spécialisation d’un langageEdit
L’une des caractéristiques inhabituelles de la famille des langages Lisp est la possibilité d’utiliser des macros pour créer un DSL interne. Typiquement, dans un grand projet basé sur Lisp, un module peut être écrit dans une variété de tels minilangages, l’un utilisant peut-être un dialecte de Lisp basé sur SQL, un autre écrit dans un dialecte spécialisé pour les interfaces graphiques ou la pretty-printing, etc. La bibliothèque standard de Common Lisp contient un exemple de ce niveau d’abstraction syntaxique sous la forme de la macro LOOP, qui met en œuvre un minilangage de type Algol pour décrire une itération complexe, tout en permettant l’utilisation des opérateurs Lisp standard.
Le préprocesseur/langage MetaOCaml fournit des fonctionnalités similaires pour les DSL externes. Ce préprocesseur prend la description de la sémantique d’un langage (c’est-à-dire un interprète) et, en combinant l’interprétation au moment de la compilation et la génération de code, transforme cette définition en un compilateur vers le langage de programmation OCaml – et à partir de ce langage, soit en bytecode, soit en code natif.