Compiler construction Clojure中编译器开发的习惯用法

Compiler construction Clojure中编译器开发的习惯用法,compiler-construction,clojure,Compiler Construction,Clojure,我想探索Clojure对于编译器开发的强大功能,但我找不到开始的示例 我是一个完全的新手(来自Ruby),但我相信Clojure应该是实现这一目标的理想选择 让我们精确地说出我在寻找什么: 从clojure中定义的简单的AST开始(比如简单的顺序语言:if、while、func、assign、expression) 此AST的简单访问者(例如漂亮的打印机) 我不太感兴趣的词法/句法分析(因为我认为s表达式足以满足我的DSL语法) Clojure中的正确习惯用法是什么?下面是我能想到的最简单的

我想探索Clojure对于编译器开发的强大功能,但我找不到开始的示例

我是一个完全的新手(来自Ruby),但我相信Clojure应该是实现这一目标的理想选择

让我们精确地说出我在寻找什么:

  • 从clojure中定义的简单的AST开始(比如简单的顺序语言:if、while、func、assign、expression)
  • 此AST的简单访问者(例如漂亮的打印机)
  • 我不太感兴趣的词法/句法分析(因为我认为s表达式足以满足我的DSL语法)

Clojure中的正确习惯用法是什么?

下面是我能想到的最简单的简单示例,使用带有关键字运算符的s表达式构建的AST树:

;; functions map, can be easily extended with new functions
;; map is of keyword -> code generating function
(def funcs {:if 
                 (fn [cond exp1 exp2] `(if ~cond ~exp1 ~exp2))
            :neg 
                 (fn [exp1] `(- 0 ~exp1))
            :plus 
                 (fn [& exps] `(+ ~@exps))})

;; compile directly to Clojure source code
(defn my-compile [code]
 (cond 
   (sequential? code)   ;; if we have a list, look up the function in funcs
     (cons (funcs (first code)) (map compile (rest code))) 
   :else                ;; treat anything else as a constant literal
     code))

;; example compilation to a Clojure expression
(my-compile `(:if true (:neg 10) (:plus 10 20 30)))
=> (if true (clojure.core/- 0 10) (clojure.core/+ 10 20 30))

;; evaluate compiled code
(eval (my-compile `(:if true (:neg 10) (:plus 10 20 30))))
=> -10
希望这足以给你一些想法/让你开始。要考虑的明显的扩展是:

  • 使用元数据编译到AST树,而不是直接编译到Clojure源代码。Clojure
    defrecord
    可能适合作为AST节点表示
  • 添加其他运算符、循环构造、“转到”等
  • 简单的优化,例如在编译时计算常量表达式
  • 具有某种形式的执行上下文,允许赋值、动态变量查找等。编译器输出可以是一个函数,它将初始上下文作为输入并返回最终上下文

您不必将搜索限制在Clojure示例中。其他Lisp中有许多示例可以很容易地移植到Clojure。或者我同意。这句话一般也适用于函数式语言,但我想把它局限于Clojure,因为对于非Lispian人来说,它似乎比旧的Lisp更容易访问/吸引人。有人说“有史以来最好的lisp”?不,函数式语言通常是一个完全不同的主题。Clojure是一个Lisp,从某种意义上说,它是Lisp中最重要的部分——一个体面的宏系统。这就是Lisp的不同之处,它比任何其他语言都更适合编译器的开发。看看我的第二个链接,它使用专门的DSL定义访问者。这正是一种惯用的Lisp方式。所有的口齿不清都是一样的。至于“有史以来最好的口齿不清”,我个人不会同意。不带cons的Lisp不太像Lisp。而且
recur
很可怕。另外,你也可以看看Racket——整个东西非常棒,它在里面实现了许多不同的编译器。它可能是最丰富的惯用Lisp编译器代码源。