2

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.

6
  • What do you need to do that slurp and spit won't do for you? Commented Oct 25, 2016 at 1:43
  • @jmargolisvt: name of the current file. In clojure, I can get name of current file via file -- in cljs, there appears to be funky things going on. Commented Oct 25, 2016 at 2:06
  • Can you give an example of what you want to do? Commented Oct 25, 2016 at 2:19
  • @AlanThompson: my example above is poorly written. The above should be a cheating "quine" -- a program that prints itself. Commented Oct 25, 2016 at 2:27
  • @AlanThompson: does the above clarification make sense? (Sorry for the confusing question). Commented Oct 25, 2016 at 2:28

2 Answers 2

3

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)
Sign up to request clarification or add additional context in comments.

8 Comments

I've used reader conditionals before, they're the clojure equiv of C's #ifdefs -- however, it's not clear to me how they're relevant in this question.
1) thanks for your patience 2) I still don't think I have properly explained the question. Does stackoverflow.com/questions/40230525/… make sense?
You can't really do the slurp, since CLJS is compiled (into javascript). So, when it runs, only the compiled code JS code is available, not the CLJS source code.
I agree that at runtime, this is not possible. Thus, I was hoping to play with some macro magic and do it at compile time. :-)
Perhaps you should send and email to the author, he may have some good ideas for enhancements. An email to the Clojure Google groups mailing list would also be a good idea.
|
3

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

(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.

2 Comments

What would be the command to compile this? I got the error `clj -m cljs.main --optimizations advanced -c hello_world.core Unexpected error (FileNotFoundException) compiling at (REPL:1). Could not locate my_macro__init.class, my_macro.clj or my_macro.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.'
Might just be that my-macro.clj needs to be named my_macro.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.