;;;; Macros: a defmacro in the file, called from the file. ;;;; ;;;; There is no interpreter, so every macro below was compiled into a shared ;;;; object and dlopened into the compiler before this file's first line was ;;;; parsed. What arrives here is the expansion; nothing at run time knows a ;;;; macro was involved. ;;;; ;;;; Every macro below is written [& args] and picks its arguments apart by ;;;; hand, which is what a macro looked like before parameter lists: & binds ;;;; the whole call as a slice of forms, and (len args) is how many were ;;;; written. macro-params.flan is the other spelling of the same grammar — ;;;; named parameters, [ ] patterns, & only for the tail — and the two files ;;;; are kept apart on purpose, so that each is a whole program in one style. ;; The simplest one there is: two forms, in order. It proves the call site's ;; arguments arrive as forms and come back as code. (defmacro both [& args] `(do ~(at args 0) ~(at args 1))) ;; Splicing, which is the only reason ~@ exists: the body is however many forms ;; were written, and they go where a list is expected. (defmacro when2 [& args] `(if ~(at args 0) (do ~@(form-rest args 1)))) ;; Expansion is not hygienic -- Common Lisp's rule and Clojure's, settled in ;; plan.org's open decision 2 -- so a macro that needs a name of its own asks ;; for one. gensym is a prelude function the loaded module runs while it runs, ;; and the name it answers starts with ~, which is a delimiter, so no symbol ;; the reader can produce is able to collide with it. ;; ;; Without this, `twice` would bind `tmp` and the caller's own `tmp` would be ;; shadowed inside it. The two calls below are the difference. (defmacro twice [& args] (let [v (gensym)] `(let [~v ~(at args 0)] (+ ~v ~v)))) ;; A macro that answers a call to another macro. This costs the pre-pass ;; nothing: `both` is inside the quasiquote, so it is part of what this macro ;; *returns* and is expanded again after it returns, and `announce` can be ;; compiled without `both` existing. (defmacro announce [& args] `(both (print "-> ") ~(at args 0))) ;; This is the one that makes the pre-pass a fixpoint rather than a sweep. The ;; call to `id` is not inside a quasiquote, so it runs while *this macro is ;; being compiled* -- which means `id` has to be compiled and dlopened first, ;; and until it is, `id` is a name nothing defines and this body will not ;; compile at all. So round 0 takes `id`, round 1 expands this against it, and ;; the module that finally answers a call holds both. (defmacro id [& args] (at args 0)) (defmacro quiet [& args] (id `(println "a macro that called a macro"))) ;; And a macro that expands into a call to itself, which is what every ;; conditional macro in every Lisp is. It gets smaller each time and stops at ;; the empty case, so the expander's fuel never comes into it. (defmacro all-of [& args] (if (= (len args) 0) `true `(if ~(at args 0) (all-of ~@(form-rest args 1)) false))) (defn main [] i32 (both (print "a") (println "b")) (when2 true (print "c") (println "d")) (when2 false (println "not printed")) ;; 21 + 21. The argument is evaluated once, into the gensym'd binding. (print (twice 21)) (println "") ;; The caller's own `tmp` is untouched by the one the macro bound, because ;; the macro did not bind `tmp`. (let [tmp 5] (print (twice tmp)) (print " ") (print tmp) (println "")) (announce (println "announced")) (quiet) (print (all-of)) (println "") (print (all-of true true true)) (println "") (print (all-of true false true)) (println "") 0)