;;;; A macro's parameter list: positional names, [ ] patterns, and &. ;;;; ;;;; macros.flan is the other half of this and is deliberately not merged with ;;;; it: everything there is written [& args] and picks its arguments apart by ;;;; hand, which is what every macro in the tree looked like before this. Here ;;;; the parameter list does the picking, and the two files together are the ;;;; claim that both spellings are the same grammar rather than two. ;;;; ;;;; Nothing below checks its own arity. It cannot be reached with the wrong ;;;; one: lib/expand.ml's check_call runs over the call *before* the macro is ;;;; expanded, so a miscount is refused at the call with the call's own ;;;; location — see macro-arity.flan and the three beside it. ;; The shape the feature was asked for (DISCUSS.org): a binding vector ;; destructured in the signature, and & for the body. Without a parameter list ;; this is (at args 0), a match on Form.Vec to unwrap it, four more (at ...) ;; inside that, and (form-rest args 1) for the body. (defmacro do-grid [[r rows c cols] & body] `(dotimes [~r ~rows] (dotimes [~c ~cols] ~@body))) ;; One positional parameter, which is where the grammar changed: [x] used to ;; bind the whole argument list and now binds the first argument. The gensym is ;; the ordinary reason it is there — expansion is not hygienic — and not ;; anything to do with the parameter list. (defmacro doubled [x] (let [v (gensym)] `(let [~v ~x] (+ ~v ~v)))) ;; Patterns nest, because a pattern's elements are patterns. And & is not only ;; the top level's: the tail of a pattern is the tail of that vector. (defmacro nested [[a [b c]] & body] `(do (print ~a) (print ~b) (print ~c) ~@body)) ;; & inside a pattern, which is the same & and means the same thing one level ;; down: the tail of the vector written at the call. (defmacro first-of [[a & more]] `(do (print ~a) ~@more)) ;; & with nothing after it at the call: the rest is an empty slice, ~@ splices ;; nothing, and the expansion is the wrapper alone. The arity check says "at ;; least 1" and one is what this is given. (defmacro shout [label & body] `(do (print ~label) ~@body (println "!"))) ;; The whole argument list, which is what [args] used to mean and is now spelled ;; [& args]. Every macro in the tree was migrated to this line, so it is the ;; one that has to keep working unchanged. (defmacro all-of [& args] (if (= (length args) 0) `true `(if ~(at args 0) (all-of ~@(form-rest args 1)) false))) (defn main [] i32 (do-grid [i 2 j 3] (print i) (print j)) (println "") (print (doubled 21)) (println "") (nested [1 [2 3]] (println " nested")) (first-of [4 (print " and") (println " more")]) (shout "alone") (shout "with" (print " body")) (print (all-of)) (print " ") (print (all-of true true true)) (print " ") (print (all-of true false true)) (println "") 0)