Camlp4 es un sistema de software para escribir analizadores extensibles para lenguajes de programación. Proporciona un conjunto de bibliotecas OCaml que se utilizan para definir gramáticas, así como extensiones de sintaxis cargables de dichas gramáticas. Camlp4 significa Caml Preprocessor y Pretty-Printer y una de sus aplicaciones más importantes fue la definición de extensiones específicas del dominio de la sintaxis de OCaml .
Camlp4 fue parte de la distribución oficial de OCaml, desarrollada en el INRIA . Su autor original es Daniel de Rauglaudre. La versión 3.10.0 de OCaml, publicada en mayo de 2007, introdujo una versión de Camlp4 significativamente modificada e incompatible con versiones anteriores . De Rauglaudre mantiene una versión independiente compatible con versiones anteriores, que ha sido renombrada Camlp5. Todos los ejemplos a continuación son para Camlp5 o la versión anterior de Camlp4 (versiones 3.09 y anteriores).
La versión 4.08, publicada en el verano de 2019, [1] fue la última versión oficial de esta biblioteca. Actualmente está obsoleta; [2] en su lugar, se recomienda utilizar las bibliotecas PPX (PreProcessor eXtensions) [3] [4] . [5]
Un preprocesador Camlp4 funciona cargando una colección de módulos compilados que definen un analizador sintáctico y un programa de impresión : el analizador sintáctico convierte un programa de entrada en una representación interna. Esta representación interna constituye el árbol de sintaxis abstracta (AST). Puede generarse en forma binaria, por ejemplo, puede pasarse directamente a uno de los compiladores de OCaml , o puede convertirse nuevamente en un programa de texto sin formato. La noción de sintaxis concreta se refiere al formato en el que se representa la sintaxis abstracta .
Por ejemplo, la expresión de OCaml (1 + 2) también se puede escribir ((+) 1 2) o (((+) 1) 2). La diferencia está solo a nivel de la sintaxis concreta, ya que estas tres versiones son representaciones equivalentes del mismo árbol de sintaxis abstracta. Como lo demuestra la definición de una sintaxis revisada para OCaml, el mismo lenguaje de programación puede usar diferentes sintaxis concretas. Todas ellas convergerían en un árbol de sintaxis abstracta en un formato único que un compilador puede manejar.
El árbol sintáctico abstracto se encuentra en el centro de las extensiones sintácticas, que en realidad son programas OCaml. Aunque la definición de gramáticas debe realizarse en OCaml, el analizador que se está definiendo o ampliando no está necesariamente relacionado con OCaml, en cuyo caso el árbol sintáctico que se está manipulando no es el de OCaml. Se proporcionan varias bibliotecas que facilitan la manipulación específica de los árboles sintácticos de OCaml.
Los lenguajes específicos de dominio son una de las principales aplicaciones de Camlp4. Dado que OCaml es un lenguaje multiparadigma, con un nivel superior interactivo y un compilador de código nativo, se puede utilizar como backend para cualquier tipo de lenguaje original. Lo único que tiene que hacer el desarrollador es escribir una gramática de Camlp4 que convierta el lenguaje específico de dominio en cuestión en un programa OCaml normal. También se pueden utilizar otros lenguajes de destino, como C.
Si el lenguaje de destino es OCaml, se pueden definir complementos de sintaxis simples o azúcar sintáctico para proporcionar una expresividad que no es fácil de lograr utilizando las características estándar del lenguaje OCaml. Una extensión de sintaxis se define mediante un módulo OCaml compilado, que se pasa al ejecutable camlp4o junto con el programa para procesarlo.
Camlp4 incluye un lenguaje específico de dominio ya que proporciona extensiones de sintaxis que facilitan el desarrollo de extensiones de sintaxis. Estas extensiones permiten una definición compacta de gramáticas ( EXTEND
declaraciones) y citas como <:expr< 1 + 1 >>, es decir, deconstruir y construir árboles de sintaxis abstracta en sintaxis concreta.
El siguiente ejemplo define una extensión de sintaxis de OCaml. Proporciona una nueva palabra clave , memo
, que se puede utilizar como reemplazo de function
y proporciona memorización automática de funciones con coincidencia de patrones . La memorización consiste en almacenar los resultados de cálculos anteriores en una tabla de modo que el cálculo real de la función para cada argumento posible se realice como máximo una vez.
Este es pa_memo.ml, el archivo que define la extensión de sintaxis:
deje que sea único = deje que n = ref 0 en fun () -> incr n ; "__pa_memo" ^ cadena_de_int ! nEXTENDER GLOBAL : Pcaml.expr ; Pcaml . expr : NIVEL "expr1" [ [ "memo" ; OPT "|" ; pel = LISTA1 match_case SEP "|" -> let tbl = unique () en let x = unique () en let resultado = unique () en <: expr < let $ lid : tbl $ = Hashtbl . crear 100 en fun $ lid : x $ -> intentar Hashtbl . encontrar $ lid : tbl $ $ lid : x $ con [ No_encontrado -> let $ lid : resultado $ = coincidir $ lid : x $ con [ $ lista : pel $ ] en hacer { Hashtbl . reemplazar $ lid : tbl $ $ lid : x $ $ lid : resultado $; $ lid : resultado $ } ] >> ] ]; match_case : [ [ p = Pcaml . patt ; w = OPT [ "cuando" ; e = Pcaml . expr -> e ]; "->" ; e = Pcaml . expr -> ( p , w , e ) ] ]; FIN
Ejemplo de programa que utiliza esta extensión de sintaxis:
sea contador = ref 0 (*contador global de multiplicaciones*)(* factorial con memorización *) sea rec fac = memo 0 -> 1 | n cuando n > 0 -> ( incr contador ; n * fac ( n - 1 )) | _ -> invalid_arg "fac"deje que se ejecute n = deje que el resultado sea fac n en deje que el recuento sea ! contador en Printf . printf "%i! = %i número de multiplicaciones hasta el momento = %i \n " n resultado recuento sea _ = Lista . iter ejecutar [ 5 ; 4 ; 6 ]
La salida del programa es la siguiente, mostrando que la función fac (factorial) sólo calcula productos que no fueron calculados previamente:
5! = 120 número de multiplicaciones hasta ahora = 54! = 24 número de multiplicaciones hasta ahora = 56! = 720 número de multiplicaciones hasta ahora = 6