Clojure + Clojurescript:读取当前文件代码的宏

Clojure + Clojurescript: Macro to read code of current file

我已经尝试过的

(defmacro magic []
  (slurp *file*))

这在 clojure 中工作正常,但在 clojurescript 中不行(至少不能用 lein figwheel)。

原问题

我需要以下内容才能在 Clojure 和 Clojurescript 中工作。我认为宏是正确的解决方案,但我对其他技术持开放态度。

我想要一种将 当前 文件作为字符串读取的方法。例如,

(ns my-test
  (:require blah))

(def foo 20)

(println (blah/magic))

这应该会导致(被打印出来)

(ns my-test
  (:require blah))

(def foo 20)

(println (blah/magic))

如果我只需要它在 Clojure 中工作,我可以对当前文件做一些有趣的事情并在 运行 时间读取它。但是,我也需要它在 Clojurescript 中工作(而且我不想设置一些 REST API 来服务 *.cljs 文件)——因此,有没有办法在编译时通过一些宏来做到这一点?

澄清

假设你想写一个 "cheating quine" -- 你会怎么做?可能类似于 (println (slurp *file*))。现在,问题是什么?当运行在 lein figwheel 下时,这在 clojurescript 中不起作用。

你需要 Reader 这样的条件:

(defn build-list []
  (list #?@(:clj  [5 6 7 8]
            :cljs [1 2 3 4])))

请在此处查看文档:

http://clojure.org/guides/reader_conditionals

https://github.com/clojure/clojurescript/wiki/Using-cljc

更新 1

如果要修改当前执行的源代码,可以随时构造一个字符串,使用eval:

(eval (read-string "(defn darth [] (println \"I have the ultimate power now...\" ))" ))

(darth)
;=> I have the ultimate power now...

但是,由于 ClojureScript 是经过编译的,我认为没有任何简单的方法可以找到原始源代码,如果那是您想要的。

更新 2

这是一个有趣的问题。我最接近的是这样的:

(ns clj.core)

(defmacro fred [& forms]
  (doseq [f forms]
    (println `~f)))

(fred 
  (+ 1 2)
  (* 2 3)
)

(defn -main [& args])

结果:

> lein run    
(+ 1 2)
(* 2 3)

这里的聚会已经很晚了,但我遇到了类似的问题,经过一天的搜索,我发现 *file* 的 ClojureScript 对应项是 cljs.analyzer/*cljs-file*

您还需要小心地为您的宏创建一个仅限宏的命名空间(除了 defmacros 之外什么都没有)并将其制作为 .clj 文件。此外,根据我的经验,(:require-macros [your-macro-ns]) 必须需要该名称空间,而不是正常的 (:require ...).

所以你的问题的答案是

my_macro.clj

(ns my-macro)

(defmacro compile-time-slurp-self []
  (slurp cljs.analyzer/*cljs-file*))

main.cljs

(ns main
  (:require-macros [my-macro]))

(def foo 420)

(println (my-macro/compile-time-slurp-self))

应该注意的是,由于 slurp 需要 Clojure,因此它仅适用于基于 Clojure 的 ClojureScript 编译器,而不适用于本身在 ClojureScript 上运行的新的自举编译器。据我了解,后者主要用于基于浏览器的 REPL 和 Java 不可用的开发环境,因此只要您不在浏览器中构建代码就可以了。