Macros¶
LisPy's macro system is highly inspired by Clojure's macro system. You can check out the Chapter 7 and Chapter 8 of "Brave Clojure" for more information about Clojure's macro system.
Expression Nodes¶
These nodes are defined in src/lispython/core/nodes.py.
You can check out how to manipulate these nodes in
- the definitions of nodes themselves
- the definitions of built-in macros in
src/lispython/macros/sugar.lpy.
quote¶
syntax-quote¶
unquote¶
unquote-splicing¶
Macro Definition¶
Just change def in function definition to defmacro. And macros usually return a quoted expression.
Macro Example¶
(defmacro when [pred *body]
(return `(if ~pred
(do ~@body))))
(defmacro cond [*body]
(def recur [*body]
(if (< (len body) 4)
(return `(if ~@body))
(do (= [test then *orelse] body)
(return `(if ~test ~then ~(recur *orelse))))))
(return (recur *body)))
(defmacro -> [x *fs]
(if (== 0 (len fs))
(return x))
(= [f *rest] fs)
(if (isinstance f Paren)
(do (f.list.insert 1 x)
(return `(-> ~f ~@rest)))
(return `(-> (~f ~x) ~@rest))))
src/lispython/macros/sugar.lpy.
as->¶
Unlike -> (thread first) and ->> (thread last), as-> lets you name the threaded value and place it anywhere in each form:
This expands to:
Useful when the threaded value doesn't always go in the first or last position.
gensym¶
gensym generates unique symbols to avoid variable name collisions in macros. It is automatically available inside defmacro bodies.
(gensym) ;; => Symbol("__gensym_0")
(gensym "tmp") ;; => Symbol("__tmp_1")
(gensym 'tmp) ;; => Symbol("__tmp_2") (also accepts a quoted Symbol)
The prefix can be a string or a symbol. Each call increments a global counter, so every generated symbol is unique.
Why gensym?¶
Without gensym, a macro that introduces a local variable can accidentally shadow a variable in the caller's scope:
;; BAD: if the caller has a variable named `tmp`, this breaks
(defmacro broken-swap [a b]
(return `(do (= tmp ~a)
(= ~a ~b)
(= ~b tmp))))
Using gensym prevents this:
Now (swap x y) expands to something like:
__tmp_0 won't collide with user variables.