diff options
Diffstat (limited to 'derrida.lisp')
-rw-r--r-- | derrida.lisp | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/derrida.lisp b/derrida.lisp index 442c890..b0a1cc0 100644 --- a/derrida.lisp +++ b/derrida.lisp @@ -88,17 +88,77 @@ And would return `(let ((,plist-var ,plist)) (symbol-macrolet ,macrolet-bindings ,@body)))) -(defun get-nested (plist-tree &rest indicators) - "PLIST-TREE is plist some of whose values are also - PLISTS. INDICATORS are keyes to the plists." - (if (or (null plist-tree) (null indicators)) - plist-tree - (apply #'get-nested (getf plist-tree (car indicators)) (cdr indicators)))) - -(defmacro pluck-nested (keypaths plist &body body) - "Pluck nested binds variables to paths into a plist tree." +(defmacro getf-path (place &rest indicators) + "(GETF-PATH PLACE K1 ... KN) expands to (GETF (... (GETF PLACE K1) ...) KN) + +E.g + > (let ((pl '(:x (:a 10 :b 20) :y (:c 30)))) + (incf (getf-path pl :x :b)) + pl) + (:X (:A 10 :B 21) :Y (:C 30))" + (if + (null indicators) place + (reduce (lambda (nested key) `(getf ,nested ,key)) + indicators + :initial-value place))) + + +(defmacro with-keypaths (keypaths plist &body body) + "Bind SETFable places to locations in a nested PLIST and evaluate BODY. + +E.g. + > (defvar *pl* '(:x (:a 10 :b 20) :y (:c 30))) + *PL* + > (with-keypaths + ((b :x :b) + (c :y :c) + (d :y :d)) + (incf b) + (setf c (* c b)) + (setf d :HELLO)) + :HELLO + > *pl* + (:X (:A 10 :B 21) :Y (:D :HELLO :C 630)) +" (let ((tmp-plist (gensym "plist"))) `(let ((,tmp-plist ,plist)) - (let ,(loop for (var . path) in keypaths - collect `(,var (get-nested ,tmp-plist ,@path))) + (symbol-macrolet + ,(loop :for (var . path) :in keypaths + :collect `(,var (getf-path ,tmp-plist ,@path))) + ,@body)))) + +(defmacro slot-path-value (place &rest slots) + "(SLOT-PATH-VALUE PLACE S1 ... SN) expands into (SLOT-VALUE (...(SLOT-VALUE PLACE S1) ..) SN). It gives you a SETFable in a nested tree of class instances." + (if (null slots) place + (reduce (lambda (nested slot) `(slot-value ,nested ,slot)) + slots + :initial-value place))) + +(defmacro with-slot-paths (slotpaths root &body body) +" Bind SETFable places to locations in a nested tree of CLOS instances and evaluate BODY. + + > (defclass moo () + ((x :initform 10))) + #<STANDARD-CLASS DERRIDA::MOO> + > (defclass zoo () + ((a-moo :initform (make-instance 'moo)))) + #<STANDARD-CLASS DERRIDA::ZOO> + > (defvar *z* (make-instance 'zoo)) + *Z* + > (with-slot-paths + ((x 'a-moo 'x)) *z* + (setf x (+ x 20))) + 30 + > (describe (slot-value *z* 'a-moo)) + #<MOO {100AEB1293}> + [standard-object] + + Slots with :INSTANCE allocation: + X = 30 +" + (let ((tmp-root (gensym "root"))) + `(let ((,tmp-root ,root)) + (symbol-macrolet + ,(loop :for (var . path) :in slotpaths + :collect `(,var (slot-path-value ,tmp-root ,@path))) ,@body)))) |