2012-05-26 14 views
6

Estoy diseñando una DSL en Clojure que se utiliza para controlar un generador de código (en este caso para la síntesis de imágenes de procedimiento - clisk) y tengo problemas para encontrar la mejor representación para valores intermedios .Representación intermedia para Lisp/Clojure DSL

Originalmente, la DSL consistía en funciones que devolvían uno o más formularios, p. (ilustrativo)

(v+ 1.0 [1.0 'y]) 
=> ['(+ 1.0 1.0) '(+ 1.0 y)] 

Estas funciones se pueden componer para crear bloques de código más grandes.

Esto era simple y los formularios resultantes podían introducirse directamente en el generador de códigos. Sin embargo, ahora he identificado lo que parecen ser algunas debilidades con este enfoque, por ejemplo, si es necesario pasar algunos datos auxiliares (por ejemplo, objetos que no se pueden codificar en formularios como imágenes almacenadas, metadatos útiles para la optimización, etc.).

Estoy seguro de que este es un problema resuelto en el mundo de Lisp: ¿cuál sería normalmente la mejor representación intermedia para este tipo de DSL?

Respuesta

7

Cada vez que necesite una representación intermedia que se utilizará para generar código, lo más obvio que se me viene a la mente es un árbol de sintaxis abstracta (AST). Su representación de ejemplo son listas, que en mi experiencia no es tan flexible de una forma. Para cualquier cosa más que la generación de código trivial, no me iría por las ramas, y simplemente iría con una representación completa de AST. Mediante el uso de listas, empuja más trabajo al lado de la generación para analizar la información, como los tipos y lo que significa el primer elemento. Pasar a una representación AST le dará más flexibilidad y desacoplará más del sistema, a costa de más trabajo del lado de análisis (o más trabajo de las funciones que generan los formularios). El lado de la generación también hará más trabajo, pero muchos de esos componentes se pueden desacoplar ya que sus entradas serán más estructuradas.

En términos de lo que debe el aspecto AST como, yo sería copiar cualquiera Enlive de Christophe Grand, donde utiliza {:tag <tag name> :attrs <map of attrs> :content <some collection>}

o qué secuencia de comandos utiliza clojure, {:op <some operator> :children <some collection>}.

Esto hace que sea bastante general ya que se puede definir caminantes arbitrarias que se asoma al :children y puede atravesar cualquier estructura sin saber exactamente de lo que son los :op 's' o :tag s.

Luego, para los componentes atómicos, puede envolverlo en un mapa y darle algún tipo de información (con respecto a la semántica de su DSL) que sea independiente del tipo real del objeto. {:atom <the object> :type :background-image}.

En el lado de la generación de código, al encontrar un átomo, el código puede enviarse en el :type y luego, si lo desea, enviarlo en el tipo real del objeto. La generación a partir de formularios de recopilación también es fácil, se envía en: op /: tag y luego se repite con los hijos. Para qué colección usar para niños, leí más sobre la discusión en los grupos de google. Sus conclusiones fueron esclarecedoras para mí.

https://groups.google.com/forum/#!topic/clojure-dev/vZLVKmKX0oc/discussion

En resumen, para los niños, si había pedido importancia semántica como en una sentencia if, a continuación, utilizar un mapa {:conditional z :then y :else x}. Si fuera solo una lista de argumentos, entonces podrías usar un vector.

+0

muchas gracias, ¡solo el tipo de información que estaba buscando! – mikera

1

Supongo que no entiendo. Solo usaría listas o estructuras yo mismo.

En Lisp, las listas pueden contener, bueno, cualquier cosa. Debo decir que una célula CONS puede señalar cualquier cosa y, por lo tanto, la lista puede contener cualquier cosa. Entonces puede prácticamente cualquier otra estructura de datos (estructuras, matrices, mapas, etc.).

Ahora, estas estructuras no pueden ser renderizables, mediante IMPRESIÓN, o renderizadas en algo legible (por LEÍDO), pero eso no significa que no puedan ser almacenadas y manipuladas.

¿Hay alguna razón por la que necesite externalizar esta representación?

+0

en última instancia, quiero compilar los formularios utilizando, p. usando algo como '(eval \' (fn [xyz] ~ form-to-compile)) '- aunque esto no funciona con objetos incrustados (ver http://stackoverflow.com/questions/10735701/embedding-arbitrary- objects-in-clojure-code) – mikera

1

Realmente no es una respuesta, porque no sé cómo funciona Clojure en este sentido, pero en CL hay macros de lector específicamente diseñadas para este caso: es decir, definirías tu función para imprimir objetos no imprimibles + un lector macro que los lee por la forma en que los ha impreso. Para definir la forma en que se imprimen los objetos, debe definir un nuevo método print-object que se especialice en el tipo de objeto que necesita y set-macro-character para agregar una función a la lectura que sepa cómo leer el objeto de su diseño.

Hay muchas cosas que debe tener en cuenta, pero algunas que suelen actuar como bombas de temporizador son los casos en que los objetos pueden referenciarse recursivamente, en cuyo caso la impresión debe tener en cuenta los objetos impresos previamente.