;; Here I use name-with-attributes to create a macro writing
;; macro defmacro! that accepts the same args as defmacro itself,
;; i.e., an optional docstring and an optional metadata map.
;; (In contrast to defmacro, overloading is not supported.)
;; Only a helper...
(defn bang-symbol?
"Returns true, if sym is a symbol with name ending in a exclamation
mark (bang)."
[sym]
(and (symbol? sym)
(= (last (name sym)) \!)))
(defmacro defmacro!
"Defines a macro name with the given docstring, args, and body.
All args ending in an exclamation mark (!, bang) will be evaluated
only once in the expansion, even if they are unquoted at several
places in body. This is especially important for args whose
evaluation has side-effecs or who are expensive to evaluate."
{:arglists '([name doc-string? attr-map? [params*] body])}
[name & args]
(let [[name attrs] (tm/name-with-attributes name args)
meta-map (meta name)
args (first attrs)
body (next attrs)
bang-syms (filter bang-symbol? (flatten args))
rep-map (apply hash-map
(mapcat (fn [s] [s `(quote ~(gensym))])
bang-syms))]
`(defmacro ~name
~@(when (seq meta-map) [meta-map])
~args
`(let ~~(vec (mapcat (fn [[s t]] [t s]) rep-map))
~(clojure.walk/prewalk-replace ~rep-map ~@body)))))
;; Here I use name-with-attributes to create a macro writing
;; macro defmacro! that accepts the same args as defmacro itself,
;; i.e., an optional docstring and an optional metadata map.
;; (In contrast to defmacro, overloading is not supported.)
;; Only a helper...
(defn bang-symbol?
"Returns true, if sym is a symbol with name ending in a exclamation
mark (bang)."
[sym]
(and (symbol? sym)
(= (last (name sym)) \!)))
(defmacro defmacro!
"Defines a macro name with the given docstring, args, and body.
All args ending in an exclamation mark (!, bang) will be evaluated
only once in the expansion, even if they are unquoted at several
places in body. This is especially important for args whose
evaluation has side-effecs or who are expensive to evaluate."
{:arglists '([name doc-string? attr-map? [params*] body])}
[name & args]
(let [[name attrs] (tm/name-with-attributes name args)
meta-map (meta name)
args (first attrs)
body (next attrs)
bang-syms (filter bang-symbol? (flatten args))
rep-map (apply hash-map
(mapcat (fn [s] [s `(quote ~(gensym))])
bang-syms))]
`(defmacro ~name
~@(when (seq meta-map) [meta-map])
~args
`(let ~~(vec (mapcat (fn [[s t]] [t s]) rep-map))
~(clojure.walk/prewalk-replace ~rep-map ~@body)))))
Comments top
No comments for name-with-attributes. Log in to add a comment.