A small macro to destructure lists based on literal values and tests. Useful in creating more complex macros and parsing command syntax, as in irc bots or other text interfaces.
(bind (key string-mode binding-mode on-failure) match-form expression body)
(match expression match-forms)
(destr-match-extras:defun name args (optional docstring) body)
(destr-match-extras:defmacro name args (optional docstring) body)
string-mode
changes how literal strings and list elements are compared.
string
is the default value, strings match other strings without case sensitivity (as if compared withequalp
)symbol
- literal strings ("foo"
) match symbols of the same name (mixed-case symbols are not matched)case-sensitive
regex
binding-mode
determines if free variables by default match a single list element or multiple.
single
- free variables match only a single element of the list, unless modified bymultiple
, as in(multiple foo)
multiple
- free variables match one or more elements of the list, and are always lists, unless the variable is designated assingle
mixed
- the default, bothsingle
andmultiple
modifiers may be used on free varibles, unmodified ones can match any number of elements, but ones that only match one item of the list will not be lists themselves.
on-failure
is evaluated and returned if the match fails. nil
by default.
choice &rest forms
matches the first form in the list that matchessingle var
matches only one element of the listmultiple var
matches a list of elementsoptional &rest forms
optionally matches all forms, sequentiallytest var function
makes the form match the condition described as well as the structure of the matchtake var function
same as test, except the result of the function is bound to the var instead of the normal resultsublist &rest forms
starts matching a sublist
(destructuring-match
(split " " "If You Give a Mouse a Cookie")
("if" "you" verb "a" noun "a" object)
(list verb noun object)) => ("Give" "Mouse" "Cookie")
(defun cookie (str)
(destructuring-match :on-failure "couldn't match list :(" (split " " str)
("if" "you" verb "a" noun "a" object)
(list verb noun object)))
(cookie "if you give a mouse a cookie") => ("give" "mouse" "cookie")
(cookie "if you give a moose a muffin") => ("give" "moose" "muffin")
(cookie "if you give a cat") => "couldn't match list :("
(cookie "random string") => "couldn't match list :("
(cookie "if you give a pig a purple pancake") => ("give" "pig" ("purple" "pancake"))
(destructuring-match '(1 2 3 4) (x y rest) (list x y rest)) => (1 2 (3 4))
(destructuring-match '(a b c d e f) (x 'd y) (list x y)) => ((a b c) (e f))
(destructuring-match '(a b c (d e f g)) (x (y 'f z)) (list x y z)) => ((a b c) (d e) g)
;; using symbols from the extra package
(defpackage my-package
(:shadowing-import-from destr-match-extras bind switch defun defmacro)
(:use cl destr-match))
(in-package my-package)
(bind '(1 2 3 4 5 6) (x y rest) rest) => (3 4 5 6)
;; this is useless but pretty fun
(defmacro map ((vars '-> body) 'over lists)
...)
(map (x -> (+ x 1)) over '(1 2 3)) => (2 3 4)
(map (x y -> (+ x y)) over '(1 2 3) '(7 8 9)) => (8 10 12)
You can use the same variable twice, or as many times as you want. The value after matching will be the binding evaluated last. This allows you to use _
for values you don't care about.
Using already bound lexical variables is fine, they'll be shadowed by the new binding, as normal.
This project requires quicklisp to run. It's been tested on sbcl, but should work on other CL implementations.
To install quicklisp, head over to quicklisp's website and follow
the instructions there. Make sure you run (ql:add-to-init-file)
, otherwise quicklisp won't be avaliable
when you start your interpreter.
To use it, clone this repo into ~/quicklisp/local-projects
, and run (ql:quickload 'destructuring-match)
.