Emacs 在宏中使用clojure语法引号时,是否可以关闭符号限定?

Emacs 在宏中使用clojure语法引号时,是否可以关闭符号限定?,emacs,clojure,elisp,Emacs,Clojure,Elisp,我正在从clojure函数生成emacs elisp代码。我最初使用defmacro,但我意识到,由于我要跨平台,并且必须手动将代码评估到elisp环境中,所以我可以同样轻松地使用标准clojure函数。但基本上我所做的是非常宏观的 我这样做是因为我的目标是创建一个DSL,从中我将生成elisp、clojure/java、clojurescript/javascript甚至haskell中的代码 我的“宏”如下所示: (defn vt-fun-3 [] (let [hlq "vt"]

我正在从clojure函数生成emacs elisp代码。我最初使用defmacro,但我意识到,由于我要跨平台,并且必须手动将代码评估到elisp环境中,所以我可以同样轻松地使用标准clojure函数。但基本上我所做的是非常宏观的

我这样做是因为我的目标是创建一个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)))))
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
然后,从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))))))
从这里,我可以运行函数和单元测试:

(list (quote (defun vt-inc (n) (+ n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
注意:与所有宏一样,引用和转义的语法非常脆弱。我花了一段时间才找到在elisp中正确评估它的正确方法(整个前缀)

无论如何,正如第一个“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上运行正则表达式并手动删除任何限定条件。

在使用语法引号时,无法“关闭”符号限定条件。但是,您可以这样做:

(let [hlq 'vt] `(~'quote (~'defun ~hlq ~'-inc (~'n) (~'+ ~'n 1))))
这无疑是相当乏味的。没有语法引号的等价物是:

(let [hlq 'vt] (list 'quote (list 'defun hlq '-inc '(n) '(+ n 1))))
但是,在使用标准
quote
作为整个表单的前缀时,无法获得所需的输出


至于使用
defmacro
的问题,据我所知,我不认为使用宏会有任何好处。

根据justncon的输入,这是我的最终解决方案。我不得不做一些额外的格式化,以使函数名上的字符串concat正确,但一切都非常类似他建议:

(defn vt-gen-4 []
  (let [hlq 'vt]
   (let [
         f1 `(~'quote (~'defun ~(symbol (str hlq "-inc")) (~'n) (~'+ ~'n 1)))
         f2 `(~'quote (~'defun ~(symbol (str hlq "-inc-test")) () (~'should (~'= (~(symbol (str hlq "-inc")) 7) 8))))
         ]
     `(~'list ~f1 ~f2))))
我学到的是:

  • 语法引号是一种方法,您只需要知道如何在元素级别控制取消引号

  • ~'(tilde quote)是我在这里的朋友。在语法quote表达式中,如果在函数或变量之前指定~',它将按指定传递给调用方

  • 以表达式(+1)为例

    下面简要介绍了此表达式将如何根据不同级别的转义在语法引号表达式中展开:

    (defn vt-foo []
      (println "(+ 1 1) -> " `(+ 1 1))      -->  (clojure.core/+ 1 1)
      (println "~(+ 1 1) -> " `~(+ 1 1))    -->  2
      (println "~'(+ 1 1) -> " `~'(+ 1 1))  --> (+ 1 1)
      )
    
    最后一行是我想要的,第一行是我得到的

  • 如果你转义一个函数,那么不要转义任何你想要转义的参数 要在宏展开时调用“str”函数并将变量“hlq”展开为其值“vt”,请执行以下操作:
  • 我猜转义涉及转义单元中的所有内容。通常转义原子(如字符串或符号),但如果是列表,则列表中的所有内容也会自动转义,因此不要重复转义

    4) FWIW,在得到最终答案之前,我结束了编写正则表达式解决方案的工作。这绝对不是那么好:

    (defn vt-gen-3 []
      (let [hlq "vt"]
        (let
            [
             f0 'list
             f1 `(quote (defun ~(symbol (str  hlq "-inc")) (n) (+ n 1)))
             f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))
             ]
          `(~f0 ~f1 ~f2)
          ))
      )
    
    ;; this strips out any qualifiers like "casc-gen.core/"
    (defn vt-gen-3-regex []
      (clojure.string/replace (str (vt-gen-3)) #"([\( ])([a-zA-Z0-9-\.]+\/)" "$1" ))
    
  • 宏观扩张非常微妙,需要大量实践