[bknr-cvs] r2523 - branches/trunk-reorg/bknr/datastore/src/data

hhubner at common-lisp.net hhubner at common-lisp.net
Sun Feb 17 21:27:26 UTC 2008


Author: hhubner
Date: Sun Feb 17 16:27:25 2008
New Revision: 2523

Modified:
   branches/trunk-reorg/bknr/datastore/src/data/txn.lisp
Log:
Improve DEFTRANSACTION:

Define wrapper function with docstring, if supplied.  Use lambda list
specified in DEFTRANSACTION for the wrapper function.  Handle
docstrings correctly.  Insert IN-TRANSACTION-P check after
declarations and docstring in generated function.


Modified: branches/trunk-reorg/bknr/datastore/src/data/txn.lisp
==============================================================================
--- branches/trunk-reorg/bknr/datastore/src/data/txn.lisp	(original)
+++ branches/trunk-reorg/bknr/datastore/src/data/txn.lisp	Sun Feb 17 16:27:25 2008
@@ -240,26 +240,90 @@
 (defmethod execute-transaction ((executor transaction) transaction)
   (execute-unlogged transaction))
 
+(defun find-doc (body)
+  "Given a function definition BODY, extract the docstring, if any.
+Skips over any declarations that precede the docstring.  See also CLHS
+3.4.11"
+  (do ((body body (cdr body)))
+      ((or (not (listp (car body)))
+           (not (eq 'declare (caar body))))
+       (when (and (stringp (car body))
+                  (cdr body))
+         (car body)))))
+
+(defun insert-after-declarations (body forms-to-insert)
+  "Given a function definition body, insert FORMS-TO-INSERT after all
+declarations and documentation in BODY."
+  (loop for rest on body
+        for form = (car rest)
+        with decls
+        with doc
+        while (or (and (listp form) (eq 'declare (car form)))
+                  (and (not doc) (cdr rest) (stringp form)))
+        when (stringp form)
+        do (setf doc form)
+        do (push form decls)
+        finally (return-from insert-after-declarations (append (nreverse decls) forms-to-insert rest))))
+
+(defun make-args (args)
+  "Parse the lambda list ARGS, returning a list that contains the
+arguments in the lambda list prepared so that the list can be applied
+to a function accepting that lambda list.
+
+For example:
+
+ (MAKE-ARGS '(A B &OPTIONAL C &REST D &KEY E F)) => (A B C :E E :F F)
+
+It is used to forward arguments to a transaction wrapper generated by
+DEFTRANSACTION to the actual transaction so that the wrapper function
+can be declared with the lambda list of the transaction function
+itself,"
+  (do ((args args (cdr args))
+       result
+       in-keywords-p)
+      ((not args)
+       (nreverse result))
+    (let ((arg (funcall (if (listp (car args)) #'caar #'car) args)))
+      (cond
+        ((eql #\& (aref (symbol-name arg) 0))
+         (case arg
+           (&optional)
+           (&rest (setf args (cdr args))) ; skip argument, too
+           (&key (setf in-keywords-p t))
+           (otherwise (error "unsupported lambda list option ~A in DEFTRANSACTION" arg))))
+        (t
+         (when in-keywords-p
+           (push (intern (symbol-name arg) :keyword) result))
+         (push arg result))))))
+
 (defmacro deftransaction (name (&rest args) &rest body)
-  "Define a transaction function tx-NAME and a function NAME executing tx-NAME in the context
-of the current store. The arguments to NAME will be serialized to the transaction-log, and
-should must be supported by the binary encoder. tx-NAME will be called during a roll-forward."
-  (dolist (arg args)
-    (when (listp arg)
-      (error "can't have argument defaults in transaction declaration for transaction ~A, please implement a wrapper" name)))
-  (let ((args-name (gensym))
-	(tx-name (intern (string-upcase (concatenate 'string "tx-" (symbol-name name)))
-			 (symbol-package name))))
-    `(progn
-      (defun ,tx-name ,args
-	(unless (in-transaction-p)
-	  (error 'not-in-transaction))
-	, at body)
-      (defun ,name (&rest ,args-name)
-	(execute (make-instance 'transaction
-				:function-symbol ',tx-name
-				:timestamp (get-universal-time)
-				:args ,args-name))))))
+  "Define a transaction function tx-NAME and a function NAME executing
+tx-NAME in the context of the current store. The arguments to NAME
+will be serialized to the transaction-log, and should must be
+supported by the binary encoder. tx-NAME will be called during a
+roll-forward."
+  (let ((name name)
+        (args args)
+        (body body))
+    (dolist (arg args)
+      (when (listp arg)
+        (error "can't have argument defaults in transaction declaration for transaction ~A, please implement a wrapper" name)))
+    (let ((tx-name (intern (format nil "TX-~A" name)
+                           (symbol-package name))))
+      `(progn
+        (defun ,tx-name ,args
+          ,@(insert-after-declarations body
+                                       '((unless (in-transaction-p)
+                                           (error 'not-in-transaction)))))
+        (defun ,name ,args
+          ,@(let ((doc (find-doc body)))
+                 (when doc (list (format nil "[Transaction function wrapper ~A invokes a store transaction]~%~A" name doc))))
+          ,@(let ((rest (member '&rest args)))
+                 (when rest `((declare (ignore ,(second rest))))))
+          (execute (make-instance 'transaction
+                                  :function-symbol ',tx-name
+                                  :timestamp (get-universal-time)
+                                  :args (list ,@(make-args args)))))))))
 
 (defmethod encode-object ((object transaction) stream)
   (%write-char #\T stream)



More information about the Bknr-cvs mailing list