每当您需要用于生成代码的中间表示时,我想到的最明显的事情就是抽象语法树(AST)。您的示例表示是列表,根据我的经验,它的形式不那么灵活。除了琐碎的代码生成之外,我不会拐弯抹角,而只是使用完整的 AST 表示。通过使用列表,您将更多的工作推到生成端来解析信息,例如类型和第一项的含义。转向 AST 表示将为您提供更大的灵活性并解耦更多的系统,但代价是解析方面的工作更多(或生成表单的函数的工作更多)。一代人也将做更多的工作,
就 AST 应该是什么样子而言,我会复制 Christophe Grand 的 enlive,他使用的地方{:tag <tag name> :attrs <map of attrs> :content <some collection>}
或者 clojure 脚本使用什么,{:op <some operator> :children <some collection>}
.
这使得它非常通用,因为您可以定义任意步行者,这些步行者可以窥视:children
并可以遍历任何结构,而无需确切知道:op
's 或:tag
's 是什么。
然后对于原子组件,您可以将其包装在映射中并为其提供一些类型信息(关于您的 DSL 的语义),这些信息与对象的实际类型无关。{:atom <the object> :type :background-image}
.
在代码生成方面,当遇到原子时,您的代码可以在 上分派:type
,然后,如果您愿意,可以进一步分派对象的实际类型。从集合表单生成也很容易,在 :op/:tag 上调度,然后与孩子一起重复。对于儿童使用什么集合,我会阅读更多关于谷歌群组的讨论。他们的结论对我很有启发。
https://groups.google.com/forum/#!topic/clojure-dev/vZLVKmKX0oc/discussion
总而言之,对于儿童来说,如果在 if 语句中存在语义排序重要性,则使用 map {:conditional z :then y :else x}
。如果它只是一个参数列表,那么你可以使用一个向量。