| This is one crazy macro, one of the wildest I have done, because it is pretty interesting handling optional and keyword parameters at runtime to boot. Begins with some utilities needed by dsb itself. But first the usage: First of all, we want runtime destructuring, so the expansion itself has to deal with possibly missing optional parameters and then keyword parameters offered in any order. Second, we want optional and keyword parameters to accept default values, and we want those optionally to be forms referring back to earlier parameters. So: (let data (list 1 2 nil nil 'p 5)
(dsb (x y &o (a (+ y 1)) z &k (p 98) (q (+ a 1))) data
;; we want to see identical pairs, cuz next I
;; print first a variables runtime binding
;; and then its expected value
(prs "args" x 1 y a 3 z nil p 5 q 4)
(prn)))
;; above -> args 1 1 2 2 3 3 nil nil 5 5 4 4That is more than a little lame as test harnesses go. :) Now first my add-ons: (mac assert (c . msg)
`(unless ,c
(prs "Assert NG:" ',c 'deets: ,@msg)
(ero "See console for assert failure deets")))
(mac push-end (x place)
`(if (no ,place)
(= ,place (list ,x))
(aif (lastcons ,place)
(do (= (cdr it) (cons ,x nil))
,place))))
(def cadrif (x) (when (acons x) (cadr x)))
(def nth (i lst)
"Indexed list access but returns NIL if index out of bounds"
(let x -1
(some [when (is (++ x) i) _] lst)))
(def lastcons (seq)
(when (acons seq)
(if (no (cdr seq))
seq
(lastcons (cdr seq)))))
And now the money: (mac dsb (params data . body)
(w/uniq (tree kvs)
`(withs (,tree ,data
,@(with (reqs nil key? nil opt? nil keys nil opts nil)
(each p params
(prs 'param p)(prn)
(if
(is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
(assert (no key?) "&k cannot precede &o:" ',params)
(= opt? t))
(is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
(= key? t))
key? (push-end p keys)
opt? (push-end p opts)
(do ;(assert (~acons p) "Reqd parameters need not be defaulted:" p)
(prs 'push-end p 'place reqs)
(push-end p reqs)
(prs 'reqs-now reqs) (prn))))
(with (n -1)
(+ (mappend [list _ `(nth ,(++ n) ,tree)] reqs)
(mappend [list (carif _) `(or (nth ,(++ n) ,tree)
,(cadrif _))] opts)
`(,kvs (pair (nthcdr ,(++ n) ,tree)))
(mappend [list (carif _)
`(or (alref ,kvs ',(carif _))
,(cadrif _))] keys)))))
,@body)))
That was lightly tested and I wager is especially easy to break given the helter-skelter mixture of compile-time and runtime values it juggles. The scotch probably does not help either. :) |