【发布时间】:2015-05-07 14:24:11
【问题描述】:
我正在从 clojure 函数生成 emacs elisp 代码。我最初开始使用 defmacro,但我意识到,因为我要跨平台并且无论如何必须手动将代码评估到 elisp 环境中,我可以很容易地使用标准的 clojure 函数。但基本上我正在做的是非常宏观的。
我这样做是因为我的目标是创建一个 DSL,我将从该 DSL 中生成 elisp、clojure/java、clojurescript/javascript 甚至是 haskell 中的代码。
我的“宏”如下所示:
(defn vt-fun-3 []
(let [hlq "vt"]
(let [
f0 'list
f1 '(quote (defun vt-inc (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))]
`(~f0 ~f1 ~f2)
)))
这会生成一个包含两个函数定义的列表——生成的 elisp defun 和一个单元测试:
(list (quote (defun vt-inc (n) (+ n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
然后从一个 emacs 暂存缓冲区,我利用 clomacs https://github.com/clojure-emacs/clomacs 导入到 elisp 环境中:
(clomacs-defun vt-fun-3 casc-gen.core/vt-fun-3)
(progn
(eval (nth 0 (eval (read (vt-fun-3)))))
(eval (nth 1 (eval (read (vt-fun-3))))))
然后我可以从这里运行函数和单元测试:
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
注意:与所有宏一样,语法引用和转义非常脆弱。我花了一段时间才弄清楚在 elisp 中正确评估它的正确方法(整个 "(quote (list..)" 前缀的东西)。
无论如何,正如第一个“let”上存在“hlq”(高级限定符)所暗示的那样,我想用这个 hlq 作为任何生成符号的前缀,而不是对其进行硬编码。
不幸的是,例如,当我在“f1”上使用标准引号和转义符时:
f1 '(quote (defun ~hlq -inc (n) (+ n 1)))
这会生成:
(list (quote (defun (clojure.core/unquote hlq) -inc (n) (+ n 1)))
(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
换句话说,它用 'clojure.core/unquote' 代替了“~”,这不是我想要的。
clojure 语法反引号:
f1 `(quote (defun ~hlq -inc (n) (+ n 1)))
没有这个问题:
(list (quote (casc-gen.core/defun vt casc-gen.core/-inc (casc-gen.core/n) (clojure.core/+ casc-gen.core/n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
它可以根据需要正确转义并插入“vt”(我仍然需要努力连接到名称的词干,但我并不担心)。
问题解决了,对吧?不幸的是,语法引号完全限定了所有符号,这是我不想要的,因为代码将在 elisp 下运行。
有没有办法在使用语法引号(反引号)时关闭符号的限定?
在我看来,语法引用比标准引用更“有能力”。这是真的?或者,您能否通过诡计,始终使标准引号的行为与语法引号相同?如果您无法使用语法引号关闭限定条件,我如何才能使用标准引号?尝试将其作为 defmacro 执行此操作是否会有所收获?
最坏的情况是我必须对生成的 elisp 运行正则表达式并手动删除任何限定条件。
【问题讨论】: