(defmacro do-grid [[r rows c cols] & body] ...) — positional names, a [ ] pattern wherever an argument is a vector, and & for the tail. The reading of the list lives in Expand, below both sides that need it: Parse turns it into the bindings a macro body opens with, and Macro checks a call against the same reading before expanding it, so arity and shape are refused with the call's own location rather than with the Loc.from_macro stamp every node of an expansion carries. The breaking half: [args] used to bind the whole argument list and now binds the first argument. The whole list is [& args], and every defmacro in the tree — prelude, vendor, tests, the elisp fixtures — was migrated to it. One grammar, not a legacy mode.
72 lines
2.8 KiB
Plaintext
72 lines
2.8 KiB
Plaintext
;;;; 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 (= (len 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)
|