Preprocessadores sintácticos foram introduzidos com a família de linguagens Lisp. A sua função é transformar árvores de sintaxe de acordo com uma série de regras definidas pelo utilizador. Para algumas linguagens de programação, as regras são escritas na mesma linguagem que o programa (reflexão em tempo de compilação). Este é o caso com Lisp e OCaml. Algumas outras linguagens dependem de uma linguagem totalmente externa para definir as transformações, como o pré-processador XSLT para XML, ou sua contraparte estaticamente digitada CDuce.
Pré-processadores sintáticos são tipicamente usados para personalizar a sintaxe de uma linguagem, estender uma linguagem adicionando novas primitivas, ou incorporar uma linguagem de programação específica de domínio (DSL) dentro de uma linguagem de propósito geral.
Personalizar sintaxeEditar
Um bom exemplo de personalização de sintaxe é a existência de duas sintaxes diferentes na linguagem de programação Objective Caml. Programas podem ser escritos indiferentemente usando a “sintaxe normal” ou a “sintaxe revista”, e podem ser bem impressos com qualquer sintaxe sob demanda.
Simplesmente, vários programas escritos em OCaml customizam a sintaxe da linguagem pela adição de novos operadores.
Estendendo uma linguagemEditar
Os melhores exemplos de extensão de linguagem através de macros são encontrados na família de linguagens Lisp. Enquanto as linguagens, por si só, são simples núcleos funcionais tipados dinamicamente, as distribuições padrão de Scheme ou Common Lisp permitem uma programação imperativa ou orientada a objetos, assim como a tipagem estática. Quase todas estas características são implementadas por pré-processamento sintáctico, embora se note que a fase de “expansão macro” da compilação é tratada pelo compilador em Lisp. Isto ainda pode ser considerado uma forma de pré-processamento, uma vez que ocorre antes de outras fases de compilação.
Especializando uma linguagemEditar
Uma das características incomuns da família de linguagens Lisp é a possibilidade de usar macros para criar uma DSL interna. Tipicamente, em um grande projeto baseado em Lisp, um módulo pode ser escrito em uma variedade dessas minilinguagens, um talvez usando um dialeto baseado em SQL do Lisp, outro escrito em um dialeto especializado para GUIs ou pretty-printing, etc. A biblioteca padrão do Lisp comum contém um exemplo deste nível de abstração sintática na forma da macro LOOP, que implementa uma minilíngua parecida com Algol para descrever iteração complexa, enquanto ainda permite o uso de operadores Lisp padrão.
O pré-processador/língua MetaOCaml fornece características similares para DSLs externas. Este pré-processador pega a descrição da semântica de uma linguagem (isto é, um intérprete) e, combinando interpretação em tempo de compilação e geração de código, transforma essa definição em um compilador para a linguagem de programação OCaml – e a partir dessa linguagem, seja para bytecode ou para código nativo.