
Update of /project/cells/cvsroot/cells In directory clnet:/tmp/cvs-serv28246 Modified Files: cells.lpr Added Files: cells-store.lisp Log Message: --- /project/cells/cvsroot/cells/cells.lpr 2008/02/02 00:09:28 1.30 +++ /project/cells/cvsroot/cells/cells.lpr 2008/04/22 14:50:56 1.31 @@ -1,4 +1,4 @@ -;; -*- lisp-version: "8.1 [Windows] (Feb 1, 2008 18:35)"; cg: "1.103.2.10"; -*- +;; -*- lisp-version: "8.1 [Windows] (Apr 3, 2008 23:47)"; cg: "1.103.2.10"; -*- (in-package :cg-user) @@ -24,7 +24,8 @@ (make-instance 'module :name "family.lisp") (make-instance 'module :name "fm-utilities.lisp") (make-instance 'module :name "family-values.lisp") - (make-instance 'module :name "test-propagation.lisp")) + (make-instance 'module :name "test-propagation.lisp") + (make-instance 'module :name "cells-store.lisp")) :projects (list (make-instance 'project-module :name "utils-kt\\utils-kt")) :libraries nil --- /project/cells/cvsroot/cells/cells-store.lisp 2008/04/22 14:50:56 NONE +++ /project/cells/cvsroot/cells/cells-store.lisp 2008/04/22 14:50:56 1.1 ;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*- #| Cells Store -- Dependence on a Hash-Table Copyright (C) 2008 by Peter Hildebrandt This library is free software; you can redistribute it and/or modify it under the terms of the Lisp Lesser GNU Public License (http://opensource.franz.com/preamble.html), known as the LLGPL. This library is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lisp Lesser GNU Public License for more details. |# (in-package :cells) (export! cells-store bwhen-c-stored c?-with-stored with-store-item store-add store-lookup store-remove) (defmacro c?-with-stored ((var key store &optional default) &body body) `(c? (bwhen-c-stored (,var ,key ,store ,default) ,@body))) (defmacro bwhen-c-stored ((var key store &optional if-not) &body body) (with-gensyms (gkey gstore glink gifnot) `(let ((,gkey ,key) (,gstore ,store) (,gifnot ,if-not)) (let ((,glink (query-c-link ,gkey ,gstore))) (declare (ignorable ,glink)) (trc nil "executing bwhen-c-stored" self :update-tick ,glink :lookup (store-lookup ,gkey ,gstore)) (bif (,var (store-lookup ,gkey ,gstore)) (progn ,@body) ,gifnot))))) (defmodel cells-store (family) ((data :accessor data :initarg :data :cell nil)) (:default-initargs :data (make-hash-table))) ;;; infrastructure for manipulating the store and kicking rules (defmethod entry (key (store cells-store)) (gethash key (data store))) (defmethod (setf entry) (new-data key (store cells-store)) (setf (gethash key (data store)) new-data)) (defmethod c-link (key (store cells-store)) (car (entry key store))) (defmethod (setf c-link) (new-c-link key (store cells-store)) (if (consp (entry key store)) (setf (car (entry key store)) new-c-link) (setf (entry key store) (cons new-c-link nil))) new-c-link) (defmethod item (key (store cells-store)) (cdr (entry key store))) (defmethod (setf item) (new-item key (store cells-store)) (if (consp (entry key store)) (setf (cdr (entry key store)) new-item) (setf (entry key store) (cons nil new-item))) new-item) ;;; c-links (defmodel c-link () ((value :accessor value :initform (c-in 0) :initarg :value))) (defmethod query-c-link (key (store cells-store)) (trc "c-link> query link" key store (c-link key store)) (value (or (c-link key store) (setf (c-link key store) (make-instance 'c-link))))) (defmethod kick-c-link (key (store cells-store)) (bwhen (link (c-link key store)) (trc "c-link> kick link" key store link) (with-integrity (:change :kick-c-link) (incf (value link))))) (defmacro with-store-item ((item key store) &body body) `(prog1 (symbol-macrolet ((,item '(item key store))) (progn ,@body)) (kick-c-link ,key ,store))) (defmacro with-store-entry ((key store &key quiet) &body body) `(prog1 (progn ,@body) (unless ,quiet (kick-c-link ,key ,store)))) ;;; item management (defmethod store-add (key (store cells-store) object &key quiet) (with-store-entry (key store :quiet quiet) (when (item key store) (trc "overwriting item" key (item key store))) (setf (item key store) object))) (defmethod store-lookup (key (store cells-store) &optional default) (when (mdead (item key store)) (with-store-entry (key store) (trc "looked up dead item -- resetting to nil" key store) (setf (item key store) nil))) (or (item key store) default)) (defmethod store-remove (key (store cells-store) &key quiet) (with-store-entry (key store :quiet quiet) (setf (item key store) nil))) ;;; unit test (export! test-cells-store) (defmodel test-store-item (family) ()) (defvar *observers*) (defobserver .value ((self test-store-item)) (trc " changed value" :self self :to (value self)) (when (boundp '*observers*) (push self *observers*))) (defmacro with-assert-observers ((desc &rest asserted-observers) &body body) `(let ((*observers* nil)) (trc ,desc " -- checking observers") ,@body (let ((superfluous-observers (loop for run in *observers* if (not (member run (list ,@asserted-observers))) collect run)) (failed-observers (loop for asserted in (list ,@asserted-observers) if (not (member asserted *observers*)) collect asserted))) (trc "called observers on" *observers* :superflous superfluous-observers :failed failed-observers) (assert (not superfluous-observers)) (assert (not failed-observers))))) (defmacro assert-values ((desc) &body objects-and-values) `(progn (trc ,desc) ,@(loop for (obj val) in objects-and-values collect `(assert (eql (value ,obj) ,val))))) (defun test-cells-store () (trc "testing cells-store -- making objects") (let* ((store (make-instance 'cells-store)) (foo (make-instance 'test-store-item :value (c?-with-stored (v :foo store 'nothing) (bwhen (val (value v)) val)))) (foo+1 (make-instance 'test-store-item :value (c?-with-stored (v :foo store 'nothing) (bwhen (val (value v)) (1+ val))))) (bar (make-instance 'test-store-item :value (c?-with-stored (v :bar store 'nothing) (bwhen (val (value v)) val)))) (bar-1 (make-instance 'test-store-item :value (c?-with-stored (v :bar store 'nothing) (bwhen (val (value v)) (1- val))))) (bypass-lookup? (make-instance 'family :value (c-in t))) (baz (make-instance 'test-store-item :value (c? (if (value bypass-lookup?) 'no-lookup (bwhen-c-stored (v :bar store 'nothing) (value v))))))) (assert-values ("assert fresh initialization") (foo 'nothing) (foo+1 'nothing) (bar 'nothing) (bar-1 'nothing)) (with-assert-observers ("adding foo" foo foo+1) (store-add :foo store (make-instance 'family :value (c-in nil)))) (assert-values ("added foo = nil") (foo nil) (foo+1 nil) (bar 'nothing) (bar-1 'nothing)) (with-assert-observers ("changing foo" foo foo+1) (setf (value (store-lookup :foo store)) 1)) (assert-values ("changed foo = 1") (foo 1) (foo+1 2) (bar 'nothing) (bar-1 'nothing)) (with-assert-observers ("adding bar = 42" bar bar-1) (store-add :bar store (make-instance 'family :value (c-in 42)))) (assert-values ("changed foo = 1") (foo 1) (foo+1 2) (bar 42) (bar-1 41)) (with-assert-observers ("changing bar to 2" bar bar-1) (setf (value (store-lookup :bar store)) 2)) (assert-values ("changed foo = 1") (foo 1) (foo+1 2) (bar 2) (bar-1 1)) (assert-values ("baz w/o lookup") (baz 'no-lookup)) (with-assert-observers ("activating lookup" baz) (setf (value bypass-lookup?) nil)) (assert-values ("baz w/lookup") (baz 2)) (with-assert-observers ("deleting foo" foo foo+1) (store-remove :foo store)) (assert-values ("deleted foo") (foo 'nothing) (foo+1 'nothing) (bar 2) (bar-1 1)) (with-assert-observers ("deleting bar" bar bar-1 baz) (store-remove :bar store)) (assert-values ("deleted bar") (foo 'nothing) (foo+1 'nothing) (bar 'nothing) (bar-1 'nothing) (baz 'nothing)) (with-assert-observers ("de-activating lookup" baz) (setf (value bypass-lookup?) t)) (assert-values ("baz w/o lookup") (baz 'no-lookup))))
participants (1)
-
ktilton