X-Git-Url: https://git.distorted.org.uk/~mdw/lisp/blobdiff_plain/ec18c92a0624f298b1b316163578dc5b88c02067..5fa07b7aaf3aaeaedaf283527dc58d69b69fe07e:/mdw-base.lisp diff --git a/mdw-base.lisp b/mdw-base.lisp index edebebb..4d67b7a 100644 --- a/mdw-base.lisp +++ b/mdw-base.lisp @@ -23,6 +23,9 @@ ;;; along with this program; if not, write to the Free Software Foundation, ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +;;;-------------------------------------------------------------------------- +;;; Package things. + (defpackage #:mdw.base (:use #:common-lisp) (:export #:compile-time-defun @@ -31,9 +34,14 @@ #:whitespace-char-p #:slot-uninitialized #:with-gensyms #:let*/gensyms #:with-places - #:locp #:locf #:ref #:with-locatives)) + #:locp #:locf #:ref #:with-locatives + #:update-place #:update-place-after + #:incf-after #:decf-after)) (in-package #:mdw.base) +;;;-------------------------------------------------------------------------- +;;; Some simple macros to get things going. + (defmacro compile-time-defun (name args &body body) "Define a function which can be used by macros during the compilation process." @@ -56,9 +64,11 @@ converted to their print representations." (symbol (symbol-name str)) (t (with-output-to-string (s) (princ str s))))) + (compile-time-defun listify (x) "If X is a (possibly empty) list, return X; otherwise return (list X)." (if (listp x) x (list x))) + (compile-time-defun do-fix-pair (x y defaultp) "Helper function for fix-pair and pairify." (flet ((singleton (x) (values x (if defaultp y x)))) @@ -67,6 +77,7 @@ converted to their print representations." ((atom (cdr x)) (values (car x) (cdr x))) ((cddr x) (error "Too many elements for a pair.")) (t (values (car x) (cadr x)))))) + (compile-time-defun fix-pair (x &optional (y nil defaultp)) "Return two values extracted from X. It works as follows: (A) -> A, Y @@ -76,6 +87,7 @@ converted to their print representations." A -> A, Y where Y defaults to A if not specified." (do-fix-pair x y defaultp)) + (compile-time-defun pairify (x &optional (y nil defaultp)) "As for fix-pair, but returns a list instead of two values." (multiple-value-call #'list (do-fix-pair x y defaultp))) @@ -110,6 +122,9 @@ where Y defaults to A if not specified." structure definitions without doom ensuing." (error "No initializer for slot.")) +;;;-------------------------------------------------------------------------- +;;; Generating symbols. + (defmacro with-gensyms (syms &body body) "Everyone's favourite macro helper." `(let (,@(mapcar (lambda (sym) `(,sym (gensym ,(symbol-name sym)))) @@ -133,13 +148,18 @@ gensyms will be bound to the corresponding VALUE." `(progn ,@body) (car (more (mapcar #'pairify (listify binds))))))) +;;;-------------------------------------------------------------------------- +;;; with-places + (defmacro %place-ref (getform setform newtmp) "Grim helper macro for with-places." (declare (ignore setform newtmp)) getform) + (define-setf-expander %place-ref (getform setform newtmp) "Grim helper macro for with-places." (values nil nil newtmp setform getform)) + (defmacro with-places ((&key environment) places &body body) "A hairy helper, for writing setf-like macros. PLACES is a list of binding pairs (VAR PLACE), where PLACE defaults to VAR. The result is that BODY is @@ -173,10 +193,38 @@ reading or setting the value of the corresponding PLACE." body)))))))))) (car (more (mapcar #'pairify (listify places)))))))) +;;;-------------------------------------------------------------------------- +;;; Update-in-place macros built using with-places. + +(defmacro update-place (op place arg &environment env) + "Update PLACE with the value of OP PLACE ARG, returning the new value." + (with-places (:environment env) (place) + `(setf ,place (,op ,place ,arg)))) + +(defmacro update-place-after (op place arg &environment env) + "Update PLACE with the value of OP PLACE ARG, returning the old value." + (with-places (:environment env) (place) + (with-gensyms (x) + `(let ((,x ,place)) + (setf ,place (,op ,x ,arg)) + ,x)))) + +(defmacro incf-after (place &optional (by 1)) + "Increment PLACE by BY, returning the old value." + `(update-place-after + ,place ,by)) + +(defmacro decf-after (place &optional (by 1)) + "Decrement PLACE by BY, returning the old value." + `(update-place-after - ,place ,by)) + +;;;-------------------------------------------------------------------------- +;;; Locatives. + (defstruct (loc (:predicate locp) (:constructor make-loc (reader writer))) "Locative data type. See `locf' and `ref'." (reader (slot-uninitialized) :type function) (writer (slot-uninitialized) :type function)) + (defmacro locf (place &environment env) "Slightly cheesy locatives. (locf PLACE) returns an object which, using the `ref' function, can be used to read or set the value of PLACE. It's @@ -189,13 +237,17 @@ work." `(let* (,@(mapcar #'list valtmps valforms)) (make-loc (lambda () ,getform) (lambda (,@newtmps) ,setform))))) + (declaim (inline loc (setf loc))) + (defun ref (loc) "Fetch the value referred to by a locative." (funcall (loc-reader loc))) + (defun (setf ref) (new loc) "Store a new value in the place referred to by a locative." (funcall (loc-writer loc) new)) + (defmacro with-locatives (locs &body body) "LOCS is a list of items of the form (SYM [LOC-EXPR]), where SYM is a symbol and LOC-EXPR evaluates to a locative. If LOC-EXPR is omitted, it