Lisp / Clojure DSL的中间表示

时间:2012-05-26 02:46:38

标签: clojure functional-programming lisp code-generation dsl

我在Clojure中设计了一个用于驱动代码生成器的DSL(在这种情况下用于程序图像合成 - clisk),并且无法计算出中间值的最佳表示形式。

最初,DSL由返回一个或多个表单的函数组成,例如: (示例性)

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

然后可以组合这些函数来构建更大的代码块。

这很简单,结果表格可以直接输入代码生成器。然而,我现在已经确定了这种方法看起来有些弱点,例如,如果需要传递一些辅助数据(例如,无法以BufferedImages等形式编码的对象,对优化有用的元数据等)。 / p>

我确信这是Lisp世界中一个已解决的问题 - 这种DSL的最佳中间代表通常是什么?

3 个答案:

答案 0 :(得分:7)

任何时候你需要一个用于生成代码的中间表示,我脑海中最明显的一点是抽象语法树(AST)。您的示例表示是列表,根据我的经验,它不像表单那样灵活。对于任何事情而言,除了琐碎的代码生成之外,我都不会打败灌木丛,只需要完整的AST表示。通过使用列表,您可以将更多工作推送到生成端,以解析类型和第一项所指的信息。转移到AST表示将为您提供更大的灵活性并使系统的更多分离,代价是解析方面的更多工作(或生成表单的函数的更多工作)。发电方也会做更多的工作,但是这些组件中的许多组件可以解耦,因为它们的输入将更加结构化。

就AST应该是什么样子而言,我会复制Christophe Grand的活跃,他使用{:tag <tag name> :attrs <map of attrs> :content <some collection>}

或clojure脚本使用的内容{:op <some operator> :children <some collection>}

这使得它非常通用,因为您可以定义窥视:children的任意步行者,并且可以遍历任何结构,而无需确切知道:op:tag是什么。

然后对于原子组件,您可以将其包装在一个映射中,并为其提供一些类型信息(关于DSL的语义),它与对象的实际类型无关。 {:atom <the object> :type :background-image}

在代码生成方面,当遇到一个原子时,你的代码可以在:type上发送,然后,如果你愿意,可以进一步调度对象的实际类型。从集合表单生成也很容易,发送到:op /:tag,然后与孩子一起重复。对于儿童使用的集合,我会详细阅读有关Google群组的讨论。他们的结论对我很有启发。

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

总而言之,对于儿童,如果存在语义排序重要性,例如在if语句中,则使用地图{:conditional z :then y :else x}。如果它只是一个参数列表,那么你可以使用一个向量。

答案 1 :(得分:1)

我想我不明白。我自己只使用列表或结构。

在Lisp中,列表可以包含任何内容。我应该说,CONS单元格可以指向任何东西,因此列表可以包含任何内容。因此,几乎任何其他数据结构(结构,数组,地图等)都可以。

现在,这些结构无法通过PRINT呈现,或呈现为可读的(通过READ),但这并不意味着它们无法存储和操作。

您是否需要将此表示外部化?

答案 2 :(得分:1)

不是真的答案,因为我不知道Clojure在这方面是如何工作的,但是在CL中有专门为这种情况设计的读者宏:即你要定义你的功能来打印不可打印的对象+一个读者宏从你打印它们的方式读取它们。要定义对象的打印方式,您需要定义一个新方法print-object,它专门用于您需要的对象类型,并set-macro-character将函数添加到知道如何读取设计对象的可读表中

有许多事情需要注意,但有些通常像计时器炸弹一样的情况是,当允许对象以递归方式引用自身时,在这种情况下,打印需要考虑以前打印的对象。