From 4cb1950c5f9fd6472fe1e1c23a63372f1b1714a2 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Mon, 31 Aug 2015 20:41:57 -0700 Subject: [PATCH] Add some macros. * Add a variety of let-based macros to abstract common patterns. --- docs/manual.scr | 62 +++++++++++++++++++++++++++++++++++++++++++ macros.lisp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ package.lisp | 10 +++++++ 3 files changed, 142 insertions(+) create mode 100644 macros.lisp diff --git a/docs/manual.scr b/docs/manual.scr index a08d9b6..7a5d16c 100644 --- a/docs/manual.scr +++ b/docs/manual.scr @@ -194,6 +194,36 @@ will be returned as an alist: @end(section) +@begin(section) +@title(Macros) + +@begin(deflist) + +@term(Let macros) +@begin(def) + +These are macros that combine a @c(let) or @c(let*) form with a test +based on the results of the bindings. This is useful for reducing +common boilerplate where a let is used to perform some operations, and +some code should be executed based on the results of those bindings. + +@cl:with-package[name="kutils"]( +@cl:doc(macro condlet) +@cl:doc(macro condlet*) +@cl:doc(macro iflet) +@cl:doc(macro iflet*) +@cl:doc(macro unlesslet) +@cl:doc(macro unlesslet*) +@cl:doc(macro whenlet) +@cl:doc(macro whenlet*) +) + +@end(def) + +@end(deflist) + +@end(section) + @begin(section) @title(Symbol index) @@ -220,6 +250,12 @@ described in "Miscellaneous utilities" under "General".) @item(@c(compose) is a function defined in @c(on.lisp), described in "On Lisp utilities".) +@item(@c(condlet) is a macro defined in @c(macros.lisp), described in +"Macros".) + +@item(@c(condlet*) is a macro defined in @c(macros.lisp), described in +"Macros".) + @item(@c(copy-hash-table) is a function defined in @c(kutils-hash-tables.lisp), described in "Hash-table related utilities".) @@ -257,6 +293,12 @@ utilities".) @c(kutils-hash-tables.lisp), described in "Hash-table related utilities".) +@item(@c(iflet) is a macro defined in @c(macros.lisp), described in +"Macros".) + +@item(@c(iflet*) is a macro defined in @c(macros.lisp), described in +"Macros".) + @item(@c(interpose) is a function defined in @c(kutils.lisp), described in "Miscellaneous utilities" under "Clojure-inspired functions".) @@ -295,6 +337,18 @@ Lisp utilities".) @item(@c(take) is a function defined in @c(kutils.lisp), described in "Miscellaneous utilities" under "Clojure-inspired functions".) +@item(@c(unlesslet) is a macro defined in @c(macros.lisp), described in +"Macros".) + +@item(@c(unlesslet*) is a macro defined in @c(macros.lisp), described in +"Macros".) + +@item(@c(whenlet) is a macro defined in @c(macros.lisp), described in +"Macros".) + +@item(@c(whenlet*) is a macro defined in @c(macros.lisp), described in +"Macros".) + @item(@c(with-new-hash-table) is a macro defined in @c(kutils-hash-tables.lisp), described in "Hash-table related utilities".) @@ -318,6 +372,8 @@ Alphabetical documentation for all exported symbols. @cl:doc(function build-vector) @cl:doc(function cartprod2) @cl:doc(function compose) +@cl:doc(macro condlet) +@cl:doc(macro condlet*) @cl:doc(function copy-hash-table) @cl:doc(macro defmacro!) @cl:doc(function drop) @@ -329,6 +385,8 @@ Alphabetical documentation for all exported symbols. @cl:doc(function group) @cl:doc(function hash-table-to-alist) @cl:doc(function hashkeys) +@cl:doc(macro iflet) +@cl:doc(macro iflet*) @cl:doc(function interpose) @cl:doc(function macroexpand-n) @cl:doc(macro mapv) @@ -341,6 +399,10 @@ Alphabetical documentation for all exported symbols. @cl:doc(macro sethash) @cl:doc(function symb) @cl:doc(function take) +@cl:doc(macro unlesslet) +@cl:doc(macro unlesslet*) +@cl:doc(macro whenlet) +@cl:doc(macro whenlet*) @cl:doc(macro with-new-hash-table) @cl:doc(function zip)) diff --git a/macros.lisp b/macros.lisp new file mode 100644 index 0000000..f6bcd3b --- /dev/null +++ b/macros.lisp @@ -0,0 +1,70 @@ +;;;; macros.lisp + +(in-package #:kutils) + +;;; Various utility macros. + +(defmacro when (bindings &body body) + "Evaluate the bindings in a let form; if they all evaluate to T, +evaluate @c(body) in an implicit @c(progn)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let (,@bindings) + (when (and ,@(mapcar #'first bindings)) + ,@body)))) + +(defmacro whenlet* (bindings &body body) + "Evaluate @c(bindings) in a @c(let*) form; if they all evaluate to T, +evaluate @c(body), which is wrapped in an implicit @c(progn)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let* (,@bindings) + (when (and ,@(mapcar #'first bindings)) + ,@body)))) + +(defmacro unlesslet (bindings &body body) + "Evaluate the bindings in a let form; if they don't all evaluate to T, +evaluate @c(body) in an implicit @c(progn)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let (,@bindings) + (unless (and ,@(mapcar #'first bindings)) + ,@body)))) + +(defmacro unlesslet* (bindings &body body) + "Evaluate @c(bindings) in a @c(let*) form; if they are all not NIL, +evaluate @c(body), which is wrapped in an implicit @c(progn)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let* (,@bindings) + (unless (and ,@(mapcar #'first bindings)) + ,@body)))) + +(defmacro iflet (bindings &body (then &optional else)) + "Evaluate @c(bindings) in a @c(let) form; if they are all T, execute +the @c(then) form. Otherwise, execute the @c(else) form." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let* (,@bindings) + (if (and ,@(mapcar #'first bindings)) + (progn ,then) + (progn ,else))))) + +(defmacro iflet* (bindings &body (then &optional else)) + "Evaluate @c(bindings) in a @c(let*) form; if they are all T, execute +the @c(then) form. Otherwise, execute the @c(else) form." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let* (,@bindings) + (if (and ,@(mapcar #'first bindings)) + (progn ,then) + (progn ,else))))) + +(defmacro condlet (bindings &body forms) + "Evaluate @c(bindings) in a @c(let) form, then evaluate @c(forms) in +a @c(cond)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let (,@bindings) + (cond ,@forms)))) + +(defmacro condlet* (bindings &body forms) + "Evaluate @c(bindings) in a @c(let*) form, then evaluate @c(forms) in +a @c(cond)." + (let ((bindings (if (listp (first bindings)) bindings (list bindings)))) + `(let (,@bindings) + (cond ,@forms)))) + diff --git a/package.lisp b/package.lisp index 1200b88..8042db2 100644 --- a/package.lisp +++ b/package.lisp @@ -40,4 +40,14 @@ #:copy-hash-table #:hash-table-to-alist #:alist-to-hash-table + + ;; macros.lisp + #:whenlet + #:whenlet* + #:unlesslet + #:unlesslet* + #:iflet + #:iflet* + #:condlet + #:condlet* ))