Clojure + Clojurescript: Macro to read code of current file

吃可爱长大的小学妹 提交于 2019-12-24 05:57:50

问题


What I've Already Tried

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

This works fine in clojure, but not in clojurescript (atleast not with lein figwheel).

Original Question

I need the following to work in both Clojure and Clojurescript. I think a macro is the right solution, but I'm open to other techniques.

I want a way to read the current file as a string. For example,

(ns my-test
  (:require blah))

(def foo 20)

(println (blah/magic))

this should then result in (being printed out)

(ns my-test
  (:require blah))

(def foo 20)

(println (blah/magic))

If I only needed this to work in Clojure, I could do funny things with the current file and reading it at run time. However, I need this also to work in Clojurescript (and I don't want to setup some REST API to serve *.cljs files) -- thus, is there some way to do this at compile time via some macro?

Clarification

Suppose you wanted to write a "cheating quine" -- how would you do it? Probably something like (println (slurp *file*)). Now, what's the problem? This doesn't work in clojurescript when running under lein figwheel.


回答1:


You need Reader Conditionals like this:

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

Please see the docs here:

http://clojure.org/guides/reader_conditionals

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

UPDATE 1

If you want to modify the currently executing source code, you can always construct a string and use eval:

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

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

However, since ClojureScript is compiled, I don't think there is any simple way of finding the source original source code, if that's what you're after.

UPDATE 2

It's an interesting problem. The closest I've come so are is something like this:

(ns clj.core)

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

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

(defn -main [& args])

with results:

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



回答2:


Very late to the party here but I had a similar problem and after a day of searching I found the ClojureScript counterpart to *file* to be cljs.analyzer/*cljs-file*.

You also need to be careful to create a macro-only namespace (with nothing but defmacros) for your macro and make that a .clj file. Furthermore, in my experience, that namespace had to be required with the (:require-macros [your-macro-ns]) rather than the normal (:require ...).

So the answer to your question would be

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 20)

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

It should be noted that, since slurp needs Clojure, this only works with the Clojure-based ClojureScript compiler, not the new bootstrapped compiler that, itself, runs on ClojureScript. From what I understand the latter is mostly used for browser-based REPLs and dev environments where Java is not available so as long as you're not building your code in the browser you should be fine.



来源:https://stackoverflow.com/questions/40229721/clojure-clojurescript-macro-to-read-code-of-current-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!