base: New macro setf-default.
authorMark Wooding <mdw@ncipher.com>
Fri, 2 Jun 2006 15:26:33 +0000 (16:26 +0100)
committerMark Wooding <mdw@ncipher.com>
Fri, 2 Jun 2006 15:44:08 +0000 (16:44 +0100)
I've thought about adding this for a while, and it just became worth it.
Deployed where sensible.

mdw-base.lisp
optparse.lisp
str.lisp

index 73f85e7..2c463e9 100644 (file)
@@ -34,7 +34,7 @@
           #:stringify #:mappend #:listify #:fix-pair #:pairify #:parse-body
           #:whitespace-char-p
           #:slot-uninitialized
-          #:nlet #:while #:until #:case2 #:ecase2
+          #:nlet #:while #:until #:case2 #:ecase2 #:setf-default
           #:with-gensyms #:let*/gensyms #:with-places
           #:locp #:locf #:ref #:with-locatives
           #:update-place #:update-place-after
   "Like `case2', but signals an error if no clause matches the SCRUTINEE."
   (do-case2-like 'ecase vform clauses))
 
+(defmacro setf-default (&rest specs &environment env)
+  "Like setf, but only sets places which are currently nil.
+
+   The arguments are an alternating list of PLACEs and DEFAULTs.  If a PLACE
+   is nil, the DEFAULT is evaluated and stored in the PLACE; otherwise the
+   default is /not/ stored.  The result is the (new) value of the last
+   PLACE."
+  (labels ((doit (specs)
+            (cond ((null specs) nil)
+                  ((null (cdr specs))
+                   (error "Odd number of arguments for SETF-DEFAULT."))
+                  (t
+                   (let ((place (car specs))
+                         (default (cadr specs))
+                         (rest (cddr specs)))
+                     (multiple-value-bind
+                         (vars vals store-vals writer reader)
+                         (get-setf-expansion place env)
+                       `(let* ,(mapcar #'list vars vals)
+                          (or ,reader
+                              (multiple-value-bind ,store-vals ,default
+                                ,writer))
+                          ,@(and rest (list (doit rest))))))))))
+    (doit specs)))
+
 ;;;--------------------------------------------------------------------------
 ;;; with-places
 
index ff301ee..08192d0 100644 (file)
@@ -464,7 +464,7 @@ Ambiguous long option `~A' -- could be any of:~{~%  --~A~}"
    if RADIX is nil.  Returns two values: the integer parsed (or nil if there
    wasn't enough for a sensible parse), and the index following the
    characters of the integer."
-  (unless end (setf end (length string)))
+  (setf-default end (length string))
   (labels ((simple (i r goodp sgn)
             (multiple-value-bind
                 (a i)
@@ -773,8 +773,7 @@ Ambiguous long option `~A' -- could be any of:~{~%  --~A~}"
     (flet ((emit ()
             (write-string string stream :start start :end i)
             (setf start i)))
-      (unless end
-       (setf end (length string)))
+      (setf-default end (length string))
       (loop
         (unless (< i end)
           (emit)
index 1cb0cc7..104c85a 100644 (file)
--- a/str.lisp
+++ b/str.lisp
@@ -47,8 +47,7 @@
    If QUOTEDP, then allow quoting and backslashifying; otherwise don't.  The
    START and END arguments limit the portion of the string to be processed;
    the default to 0 and nil (end of string), as usual."
-  (unless start (setf start 0))
-  (unless end (setf end (length string)))
+  (setf-default start 0 end (length string))
   (let ((i start)
        (q nil)
        (e nil)