I'm quite new to Clojure and, I think, programming in general but I ended up trying to write some kind of DSL for producing PDFs files (using PDFBox, a java library). I want the final syntax to look like something this:
(document {:name "test" :bleed "22mm" ...}
(page {:height "560mm" ...) (text {:color "red"} "blabla") (line-break))
So the thing is that every expression is a macro that will do some stuff at eval time + recursively read every expression after the first argument and expand it with a conj'ed options as its first argument. For now, for example it looks like this:
(defmacro document
[{:keys [name] :as options}
& body]
`(let [~'pdoc ~'(PDDocument.)]
(do
~@(unfold (conj options {:pdoc 'pdoc}) ~body)
(doto ~'pdoc
(...)))
and unfold macro (the one expanding arguments):
(defmacro unfold
[options & body]
(loop [
[f & fs] body
opts options
output []]
(if (empty? f)
output
(recur fs
(conj opts (second f))
(conj output `(~(first f) ~(conj opts (second f)))))
)))
The problem is that I'm unable to make them work together. After some debugging I still get java.lang.NullPointerException:.
I sense that the problem comes with the way unfold outputs to document but I totally unable to get how macros react when nested. When I use macroexpand, nested macros are never expanded I don't know if it's expected or a programming error on my part. Anyway, when
(macroexpand '(unfold {...} (document) (document)))
I get
[(document {...}) (document {...})]
which, in my current understanding, seems ok as I use it unquote-splice'd afterwards. But I'm clearly missing something.
--------- Edit -----------
For a very unuseful
(document {:name "hey"}} (document {:name "ho"}))
I would like to end up with ((doto ...) being an example) :
(let [pdoc (.PDDocument)]
(do
(let [pdoc (.PDDocument)]
(do
(doto pdoc .save (str "ho" ".pdf"))))
(doto pdoc .save (str "hey" ".pdf"))))
Sorry for the very specific and long post but I didn't how to explain the problem without describing everything.
Thank you in advance, Bye
unfoldlike a function, but it's a macro. Either emit anunfold(which would need to emit adoto work) or makeunfolda function. Also, please do add the entire exception message. - ClojureMostlyunfoldin your code otherwise, then leave it a macro. If theunfoldis only used for the library to generate code, then definitely make it a function. So just FYI: A macro has two implicit parameters, that you're not passing in when calling it like a function. Hence (probably) the error. Also, it seems you don't wantunfoldto be a vararg? - ClojureMostly