flan/test/programs/macro-params.flan
Joseph Ferano 57fe91f303 Five records become one, and every citation lands somewhere
FIX.org, NEXT.md, DISCUSS.org, docs/DISCUSS.md and the session handoff at the
root are one TODO.org now: 293 entries under seven subsystem headings, each
carrying an org keyword that says where it stands. A DONE entry is a few lines
saying what was decided and what that rules out; the reasoning that would not
compress — the embedding spike and the four reports the hand-written x86
backend was built from — moved into docs/BUILT.md instead, and its entries
point there in one line.

Every entry was checked against the tree before it got a keyword, and the
prose was wrong in both directions. Things the deleted files called open were
built: the first-evaluation stall, main being redefinable, macro parameter
lists, the type-limit constants, the array constructors, the byte fills,
inc/dec, the discard's fontification, the Emacs buffers, rt_die's _exit, the
backtrace surface, and the acceptance failure that could print and still exit
zero. Things they called done were not: the backend reports' no-plan buckets
had gone stale in the other direction, the value-dependent defvar was
superseded rather than built, and macro-expansion source locations are on an
unmerged lane, so that entry is NEXT and names the branch.

Every comment that cited one of the five by name now cites a heading that
exists, in TODO.org or in docs/BUILT.md. The session reports under
docs/handoffs/ keep naming the files they worked on, because rewriting them
would falsify what those sessions did; each carries a note saying where the
content went.
2026-09-21 21:05:48 +07:00

72 lines
2.9 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 (TODO.org, "A macro has a parameter list, and [args] is the first argument"):
;; 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)