[snow-cvs] r47 - in dependencies: . trunk trunk/cells trunk/cells/Use Cases trunk/cells/Use Cases/dow-jones trunk/cells/cells-test trunk/cells/doc trunk/cells/gui-geometry trunk/cells/tutorial trunk/cells/utils-kt trunk/cl-utilities-1.2.4 trunk/cl-utilities-1.2.4/doc trunk/named-readtables trunk/named-readtables/doc trunk/named-readtables/tests
Alessio Stalla
astalla at common-lisp.net
Tue Jan 26 20:20:07 UTC 2010
Author: astalla
Date: Tue Jan 26 15:20:07 2010
New Revision: 47
Log:
Importing lib folder to separate dependencies/ path.
Added:
dependencies/
dependencies/trunk/
dependencies/trunk/abcl.jar (contents, props changed)
dependencies/trunk/binding-2.0.6.jar (contents, props changed)
dependencies/trunk/cells/
dependencies/trunk/cells/README.txt (contents, props changed)
dependencies/trunk/cells/Use Cases/
dependencies/trunk/cells/Use Cases/dow-jones/
dependencies/trunk/cells/Use Cases/dow-jones/dow-jones.lpr (contents, props changed)
dependencies/trunk/cells/Use Cases/dow-jones/stock-exchange.lisp (contents, props changed)
dependencies/trunk/cells/cell-types.lisp (contents, props changed)
dependencies/trunk/cells/cells-manifesto.txt (contents, props changed)
dependencies/trunk/cells/cells-store.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/
dependencies/trunk/cells/cells-test/boiler-examples.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/build-sys.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/cells-test.asd (contents, props changed)
dependencies/trunk/cells/cells-test/cells-test.lpr (contents, props changed)
dependencies/trunk/cells/cells-test/deep-cells.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/df-interference.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/echo-setf.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/hello-world-q.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/hello-world.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/internal-combustion.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/lazy-propagation.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/output-setf.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/person.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/synapse-testing.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-cycle.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-cyclicity.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-ephemeral.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-family.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-kid-slotting.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-lazy.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test-synapse.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test.lisp (contents, props changed)
dependencies/trunk/cells/cells-test/test.lpr (contents, props changed)
dependencies/trunk/cells/cells.asd (contents, props changed)
dependencies/trunk/cells/cells.lisp (contents, props changed)
dependencies/trunk/cells/cells.lpr (contents, props changed)
dependencies/trunk/cells/constructors.lisp (contents, props changed)
dependencies/trunk/cells/defmodel.lisp (contents, props changed)
dependencies/trunk/cells/defpackage.lisp (contents, props changed)
dependencies/trunk/cells/doc/
dependencies/trunk/cells/doc/01-Cell-basics.lisp (contents, props changed)
dependencies/trunk/cells/doc/cell-doc.lisp (contents, props changed)
dependencies/trunk/cells/doc/cells-overview.pdf (contents, props changed)
dependencies/trunk/cells/doc/hw.lisp (contents, props changed)
dependencies/trunk/cells/doc/motor-control.lisp (contents, props changed)
dependencies/trunk/cells/family-values.lisp (contents, props changed)
dependencies/trunk/cells/family.lisp (contents, props changed)
dependencies/trunk/cells/fm-utilities.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/
dependencies/trunk/cells/gui-geometry/coordinate-xform.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/defpackage.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/geo-data-structures.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/geo-family.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/geo-macros.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/geometer.lisp (contents, props changed)
dependencies/trunk/cells/gui-geometry/gui-geometry.asd (contents, props changed)
dependencies/trunk/cells/gui-geometry/gui-geometry.lpr (contents, props changed)
dependencies/trunk/cells/initialize.lisp (contents, props changed)
dependencies/trunk/cells/integrity.lisp (contents, props changed)
dependencies/trunk/cells/link.lisp (contents, props changed)
dependencies/trunk/cells/load.lisp (contents, props changed)
dependencies/trunk/cells/md-slot-value.lisp (contents, props changed)
dependencies/trunk/cells/md-utilities.lisp (contents, props changed)
dependencies/trunk/cells/model-object.lisp (contents, props changed)
dependencies/trunk/cells/propagate.lisp (contents, props changed)
dependencies/trunk/cells/slot-utilities.lisp (contents, props changed)
dependencies/trunk/cells/synapse-types.lisp (contents, props changed)
dependencies/trunk/cells/synapse.lisp (contents, props changed)
dependencies/trunk/cells/test-cc.lisp (contents, props changed)
dependencies/trunk/cells/test-cycle.lisp (contents, props changed)
dependencies/trunk/cells/test-ephemeral.lisp (contents, props changed)
dependencies/trunk/cells/test-propagation.lisp (contents, props changed)
dependencies/trunk/cells/test-synapse.lisp (contents, props changed)
dependencies/trunk/cells/test.lisp (contents, props changed)
dependencies/trunk/cells/trc-eko.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/
dependencies/trunk/cells/tutorial/01-lesson.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/01a-dataflow.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/01b-change-handling.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/01c-cascade.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/02-lesson.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/03-ephemeral.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/04-formula-once-then-input.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/test.lisp (contents, props changed)
dependencies/trunk/cells/tutorial/tutorial.lpr (contents, props changed)
dependencies/trunk/cells/utils-kt/
dependencies/trunk/cells/utils-kt/core.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/datetime.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/debug.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/defpackage.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/detritus.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/flow-control.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/quad.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/split-sequence.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/strings.lisp (contents, props changed)
dependencies/trunk/cells/utils-kt/utils-kt.asd (contents, props changed)
dependencies/trunk/cells/utils-kt/utils-kt.lpr (contents, props changed)
dependencies/trunk/cells/variables.lisp (contents, props changed)
dependencies/trunk/cl-utilities-1.2.4/
dependencies/trunk/cl-utilities-1.2.4/README
dependencies/trunk/cl-utilities-1.2.4/cl-utilities.asd
dependencies/trunk/cl-utilities-1.2.4/collecting.lisp
dependencies/trunk/cl-utilities-1.2.4/compose.lisp
dependencies/trunk/cl-utilities-1.2.4/copy-array.lisp
dependencies/trunk/cl-utilities-1.2.4/doc/
dependencies/trunk/cl-utilities-1.2.4/doc/collecting.html
dependencies/trunk/cl-utilities-1.2.4/doc/compose.html
dependencies/trunk/cl-utilities-1.2.4/doc/copy-array.html
dependencies/trunk/cl-utilities-1.2.4/doc/expt-mod.html
dependencies/trunk/cl-utilities-1.2.4/doc/extremum.html
dependencies/trunk/cl-utilities-1.2.4/doc/index.html
dependencies/trunk/cl-utilities-1.2.4/doc/once-only.html
dependencies/trunk/cl-utilities-1.2.4/doc/read-delimited.html
dependencies/trunk/cl-utilities-1.2.4/doc/rotate-byte.html
dependencies/trunk/cl-utilities-1.2.4/doc/split-sequence.html
dependencies/trunk/cl-utilities-1.2.4/doc/style.css
dependencies/trunk/cl-utilities-1.2.4/doc/with-unique-names.html
dependencies/trunk/cl-utilities-1.2.4/expt-mod.lisp
dependencies/trunk/cl-utilities-1.2.4/extremum.lisp
dependencies/trunk/cl-utilities-1.2.4/once-only.lisp
dependencies/trunk/cl-utilities-1.2.4/package.lisp
dependencies/trunk/cl-utilities-1.2.4/package.sh (contents, props changed)
dependencies/trunk/cl-utilities-1.2.4/read-delimited.lisp
dependencies/trunk/cl-utilities-1.2.4/rotate-byte.lisp
dependencies/trunk/cl-utilities-1.2.4/split-sequence.lisp
dependencies/trunk/cl-utilities-1.2.4/test.lisp
dependencies/trunk/cl-utilities-1.2.4/with-unique-names.lisp
dependencies/trunk/commons-logging.jar (contents, props changed)
dependencies/trunk/miglayout-3.7.1.jar (contents, props changed)
dependencies/trunk/named-readtables/
dependencies/trunk/named-readtables/LICENSE
dependencies/trunk/named-readtables/cruft.lisp
dependencies/trunk/named-readtables/define-api.lisp
dependencies/trunk/named-readtables/doc/
dependencies/trunk/named-readtables/doc/named-readtables.html
dependencies/trunk/named-readtables/named-readtables.asd
dependencies/trunk/named-readtables/named-readtables.lisp
dependencies/trunk/named-readtables/package.lisp
dependencies/trunk/named-readtables/tests/
dependencies/trunk/named-readtables/tests/package.lisp
dependencies/trunk/named-readtables/tests/rt.lisp
dependencies/trunk/named-readtables/tests/tests.lisp
dependencies/trunk/named-readtables/utils.lisp
Added: dependencies/trunk/abcl.jar
==============================================================================
Binary file. No diff available.
Added: dependencies/trunk/binding-2.0.6.jar
==============================================================================
Binary file. No diff available.
Added: dependencies/trunk/cells/README.txt
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/README.txt Tue Jan 26 15:20:07 2010
@@ -0,0 +1,101 @@
+-*- text -*-
+
+***** About Cells *****
+
+(Installation instructions follow)
+
+Cells is a mature, stable extension to CLOS that allows you to create
+classes, the instances of which have slots whose values are determined
+by a formula. Think of the slots as cells in a spreadsheet (get it?),
+and you've got the right idea. You can use any arbitrary Common Lisp
+expression to specify the value of a cell. The Cells system takes care
+of tracking dependencies among cells, and propagating values. It is
+distributed under an MIT-style license.
+
+Documentation/support is in the form of:
+
+ the cells-devel mailing list (users and developers both welcome)
+ .\docs\01-cell-basics.lisp
+ .\docs\motor-control.lisp ;; actually Bill Clementson's blog entry
+ extensive examples in the Cells-test regression test suite
+ the companion Celtk module, which happens also to provide a substantial and
+ growing portable, native Common Lisp GUI.
+
+The above examples have all been tested against the current release of Cells.
+Now in .\doc is cells-overview.pdf. That is pretty rough and obsolete in re the
+code, but some of it might be enlightening.
+
+Cells is written in portable ANSI Common Lisp. It makes very
+light use of the introspective portions of the MOP, and contains a few
+workarounds for shortcomings in common implementations.
+
+Cells is known to currently work on the following Lisp implementations:
+
+ * Allegro
+ * SBCL
+ * CLISP
+ * LispWorks
+ * OpenMCL
+
+Partially supported are:
+
+ * CMUCL
+ * Corman Lisp
+ * MCL
+
+One of the Cells tests fails with CMUCL. This appears to be caused by
+a bug in CMUCL's CLOS implementation, but has not been investigated in
+great depth.
+
+Cells is believed to work with Corman CL, but has not been recently
+tested. In the past, MCL was supported, but a it does not currently
+pass the test suite. Ressurecting full support for any of these
+implementations should be easy.
+
+Porting Cells to an unsupported but ANSI-conforming Lisp
+implementation should be trivial: mostly a matter of determining the
+package where the MOP lives. In reality, however, you might have to
+find workarounds for bugs in ANSI compliance.
+
+***** Installation *****
+
+[ Cells follows the usual convention for asdf and asdf-installable
+ packages. If you know what that means, that's all you need to
+ know. ]
+
+Installation is trivial for asdf-install users:
+
+ (asdf-install:install :cells)
+
+Users without asdf-install will need to download the distribution from
+common-lisp.net. If your implementation does not come with ASDF,
+please complain to the implementor, then load the asdf.lisp file
+included in the Cells distribution.
+
+Unpack the distribution where you will.
+
+Unix users: If you do not already have an asdf central registry,
+create a directory calld asdf-registry under your home directory and
+push this onto asdf:*central-registry*. Create symlinks there to the
+cells.asd and cells-test.asd files in the distribution. Alternately,
+follow the instructions for Windows users.
+
+Windows and Classic Mac users: Push the directory where you unpacked
+the Cells distribution onto asdf:*central-registry*.
+
+You can now load Cells in the usual manner for asdf.
+
+SLIME:
+
+ ,load-system cells
+
+SBCL:
+
+ (require :cells)
+
+Other systems:
+
+ (asdf:oos 'asdf:load-op :cells)
+
+You may wish to run the test suite. To do so, use asdf to load the
+:cells-test system.
Added: dependencies/trunk/cells/Use Cases/dow-jones/dow-jones.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/Use Cases/dow-jones/dow-jones.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,81 @@
+;; -*- lisp-version: "7.0 [Windows] (Jun 10, 2005 13:34)"; cg: "1.54.2.17"; -*-
+
+(in-package :cg-user)
+
+(defpackage :CELLS)
+
+(define-project :name :dow-jones
+ :modules (list (make-instance 'module :name "stock-exchange.lisp"))
+ :projects (list (make-instance 'project-module :name
+ "..\\..\\cells"))
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :cells
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules '(:cg-dde-utils :cg.base :cg.bitmap-pane
+ :cg.bitmap-pane.clipboard :cg.bitmap-stream
+ :cg.button :cg.caret :cg.check-box :cg.choice-list
+ :cg.choose-printer :cg.clipboard
+ :cg.clipboard-stack :cg.clipboard.pixmap
+ :cg.color-dialog :cg.combo-box :cg.common-control
+ :cg.comtab :cg.cursor-pixmap :cg.curve
+ :cg.dialog-item :cg.directory-dialog
+ :cg.directory-dialog-os :cg.drag-and-drop
+ :cg.drag-and-drop-image :cg.drawable
+ :cg.drawable.clipboard :cg.dropping-outline
+ :cg.edit-in-place :cg.editable-text
+ :cg.file-dialog :cg.fill-texture
+ :cg.find-string-dialog :cg.font-dialog
+ :cg.gesture-emulation :cg.get-pixmap
+ :cg.get-position :cg.graphics-context
+ :cg.grid-widget :cg.grid-widget.drag-and-drop
+ :cg.group-box :cg.header-control :cg.hotspot
+ :cg.icon :cg.icon-pixmap :cg.item-list
+ :cg.keyboard-shortcuts :cg.lettered-menu
+ :cg.lisp-edit-pane :cg.lisp-text :cg.lisp-widget
+ :cg.list-view :cg.mci :cg.menu :cg.menu.tooltip
+ :cg.message-dialog :cg.multi-line-editable-text
+ :cg.multi-line-lisp-text :cg.multi-picture-button
+ :cg.multi-picture-button.drag-and-drop
+ :cg.multi-picture-button.tooltip :cg.os-widget
+ :cg.os-window :cg.outline
+ :cg.outline.drag-and-drop
+ :cg.outline.edit-in-place :cg.palette
+ :cg.paren-matching :cg.picture-widget
+ :cg.picture-widget.palette :cg.pixmap
+ :cg.pixmap-widget :cg.pixmap.file-io
+ :cg.pixmap.printing :cg.pixmap.rotate :cg.printing
+ :cg.progress-indicator :cg.project-window
+ :cg.property :cg.radio-button :cg.rich-edit
+ :cg.rich-edit-pane :cg.rich-edit-pane.clipboard
+ :cg.rich-edit-pane.printing :cg.sample-file-menu
+ :cg.scaling-stream :cg.scroll-bar
+ :cg.scroll-bar-mixin :cg.selected-object
+ :cg.shortcut-menu :cg.static-text :cg.status-bar
+ :cg.string-dialog :cg.tab-control
+ :cg.template-string :cg.text-edit-pane
+ :cg.text-edit-pane.file-io :cg.text-edit-pane.mark
+ :cg.text-or-combo :cg.text-widget :cg.timer
+ :cg.toggling-widget :cg.toolbar :cg.tooltip
+ :cg.trackbar :cg.tray :cg.up-down-control
+ :cg.utility-dialog :cg.web-browser
+ :cg.web-browser.dde :cg.wrap-string
+ :cg.yes-no-list :cg.yes-no-string :dde)
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags '(:top-level :debugger)
+ :build-flags '(:allow-runtime-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :default-command-line-arguments "+M +t \"Console for Debugging\""
+ :additional-build-lisp-image-arguments '(:read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :on-initialization 'cells::run-trading-day
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/Use Cases/dow-jones/stock-exchange.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/Use Cases/dow-jones/stock-exchange.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,954 @@
+(in-package :cells)
+
+#|
+
+The deal is this: explanations of chunks of code appear /below/ them.
+
+Now here are Ron's functional requirements: process a stream of messages from an
+imagined source of financial data. Actually, Ron has an intermediate process
+reading a real source and producing a somewhat-digested stream in Lisp-friendly
+format. Sample:
+
+(:date 5123 :weekday 3)
+(:index ((AA 29.30 7.3894672) (AIG 53.30 7.3894672)(AXP 53.00 7.3894672)
+(BA 59.87 7.3894672) (C 46.80 7.3894672) (CAT 87.58 7.3894672) (DD 47.74 7.3894672)
+(DIS 26.25 7.3894672) (GE 36.10 7.3894672) (GM 27.77 7.3894672) (HD 36.75 7.3894672)
+(HON 35.30 7.3894672) (HPQ 21.00 7.3894672) (IBM 76.47 7.3894672)
+(INTC 23.75 7.3894672) (JNJ 68.73 7.3894672) (JPM 35.50 7.3894672) (KO 43.76 7.3894672)
+(MCD 29.80 7.3894672) (MMM 76.76 7.3894672) (MO 65.99 7.3894672) (MRK 34.42 7.3894672)
+(MSFT 25.36 7.3894672) (PFE 27.5 7.3894672) (PG 54.90 7.3894672) (SBC 23.8 7.3894672)
+(UTX 100.96 7.3894672) (VZ 36.75 7.3894672) (WMT 48.40 7.3894672) (XOM 56.50 7.3894672)))
+(:trade INTC 0.001932 :last 23.75)
+(:trade MSFT 0.001932 :last 25.36)
+(:trade INTC 0.011931 :last 23.75)
+(:trade MSFT 0.011931 :last 25.36)
+(:trade MSFT 0.041965 :last 25.32)
+(:trade UTX 0.067027 :last 101.39)
+...etc...
+
+Date messages encode date as (+ (* (- year 2000) 1000) julian-days). Weekday is dicey,
+so the tutorial deduces the Lisp weekday and stores that.
+
+Index messages define which tickers are in the index and their weights.
+Entries are: (ticker-symbol initial-price index-weight)
+
+Trade messages are (ticker-symbol ticker-minute :LAST price)
+Ticker-minute is time since open, in minutes. Negative indicates pre-open trading.
+
+To get the ball rolling, we just want to print out each trade as received, with the
+addition of an indicator as to which way the price moved: -1, 0, or 1 for down, unchanged, or up.
+
+For the index, we want to track the minute of the last trade affecting the index, the
+weighted index value, and the last move of each index entry.
+
+|#
+(defparameter *trc-trades* t)
+
+#+test
+(run-trading-day)
+
+(defun run-trading-day ()
+ (cell-reset)
+ (let ((*trc-trades* nil)
+ (t-day (make-be 'trading-day)))
+
+ ;; - always call CELLS-RESET when starting a test run
+ ;; - (make-be ...) -> (to-be (make-instance ...))
+ ;; - TO-BE jumpstarts a Cells instance into the flow. (FN to-be)
+ #+(or)
+ (with-open-file (t-data (make-pathname
+ :directory '(:absolute "0dev" "cells" "Use Cases" "dow-jones")
+ :name "trades0504" :type "txt"))
+ (with-metrics (nil t "run-trading-day")
+ (loop for message = (read t-data nil :eof)
+ until (eq message :eof)
+ do (count-it :dow-message)
+ (setf (message t-day) message)))
+ )
+
+ (with-open-file (t-data (make-pathname
+ :directory '(:absolute "0dev" "cells" "Use Cases" "dow-jones")
+ :name "stock-exchange" :type "lisp"))
+ (with-metrics (nil t "run-trading-day")
+ (loop with in-data = nil
+ do (if (not in-data)
+ (setf in-data (msg-start (read-line t-data nil :eof)))
+ (let ((message (read t-data nil :eof)))
+ (count-it :dow-message)
+ (if (eql (car message) :close)
+ (loop-finish)
+ (setf (message t-day) message)))))))
+
+ (trc "index value = " (value (car (indexes t-day))))))
+
+;; --- trading day ---------------------------------
+;;
+
+(defmodel trading-day ()
+ ((message :initarg :message :accessor message
+ :initform (c-in nil) ;; c-in -> c-input, how data enters a model (see FN c-input)
+ :cell :ephemeral) ;; handling transient phenomena in a steady-state paradigm (FN ephemeral)
+
+ (date :initarg :date :accessor date
+ :initform (c? (or .cache ;; advanced trick using prior value (see FN date/.cache)
+ (when (eql :date (car (^message)))
+ (destructuring-bind (&key date weekday)
+ (^message)
+ (declare (ignore weekday)) ;; derive from date
+ (encode-julian-date (+ 2000 (floor date 1000)) (mod date 1000)))))))
+
+ (weekday :initarg :weekday :accessor weekday
+ :initform (c? (when (^date)
+ (multiple-value-bind (second minute hour date month year day daylight-p zone)
+ (decode-universal-time (^date))
+ (declare (ignorable second minute hour date month year daylight-p zone))
+ day))))
+
+ ;; not much new here, but astute readers will wonder if this cell gets optimized away
+ ;; when (^date) on its second evaluation uses its .cache and gets optimized away.
+ ;;
+ ;; yes. Just checked to be sure.
+
+ (trade :cell :ephemeral :initarg :trade :accessor trade
+ :initform (c? (when (eql :trade (car (^message)))
+ (message-to-trade (^message)))))
+ ;;
+ ;; nothing new here, but note that again we use the :ephemeral option
+ ;;
+ (indexes :initarg :indexes :accessor indexes
+ :initform (c? (with-c-cache ('cons)
+ (when (eql :index (car (^message)))
+ (make-be 'index
+ :trading-day self
+ :index-def (second (^message)))))))
+ (tickers :cell nil :reader tickers :initform (make-hash-table :rehash-size 50))
+ ))
+
+
+(def-c-output trade ((self trading-day) trade) ;; FN def-c-output
+ (when trade ;; FN trade setf optimization
+ (count-it :raw-trades)
+ (push trade (trades (ensure-ticker self (trade-ticker-sym trade))))))
+
+(defun trading-day-ticker (day sym)
+ (gethash sym (tickers day)))
+
+(defun (setf trading-day-ticker) (ticker day sym)
+ (setf (gethash sym (tickers day)) ticker))
+
+(defun ensure-ticker (trading-day ticker-sym &optional price minute)
+ (or (trading-day-ticker trading-day ticker-sym)
+ (setf (trading-day-ticker trading-day ticker-sym)
+ (make-be 'ticker :ticker-sym ticker-sym
+ :trades (c-in (when price
+ (list (make-trade :ticker-sym ticker-sym
+ :minute minute :price price))))))))
+
+(defmodel ticker (model)
+ ((ticker-sym :cell nil :initarg :ticker-sym :reader ticker-sym)
+ (trades :initarg :trades :accessor trades :initform (c-in nil))
+ (last-trade-info :reader last-trade-info
+ :initform (c? (bwhen (trade (first (^trades)))
+ (bif (penult-trade (and (trade-price trade)
+ (find-if 'trade-price (rest (^trades)))))
+ (let* ((last (trade-price trade))
+ (penult (trade-price penult-trade))
+ (move (cond
+ ((< last penult) -1)
+ ((= last penult) 0)
+ (t 1))))
+ (values
+ (cons penult-trade move)
+ (if (zerop move) :no-propagate :propagate)))
+ (values (cons trade 0) :propagate)))))))
+
+(defun last-trade (ticker)
+ (car (last-trade-info ticker)))
+(defun last-move (ticker)
+ (cdr (last-trade-info ticker)))
+
+(defun ticker-price (ticker)
+ (bwhen (trade (last-trade ticker))
+ (trade-price trade)))
+
+(defun ticker-trade-minute (ticker)
+ (bwhen (trade (last-trade ticker))
+ (trade-minute trade)))
+
+(def-c-output trades ((self ticker)) ;; FN trades def-c-output
+ (when *trc-trades*
+ (loop for trade in (set-difference new-value old-value)
+ do (format t "~&at ~a min, ~a at ~a, change ~a"
+ (trade-minute trade) (ticker-sym self) (trade-price trade)
+ (or (last-move self) "")))))
+
+;; --- index ---------------------------------------------------
+
+(defmodel index ()
+ ((index-def :cell nil :initarg :index-def :initform nil :accessor index-def)
+ (trading-day :cell nil :initarg :trading-day :initform nil :accessor trading-day)
+ (ticker-weights :initarg :ticker-weights :accessor ticker-weights
+ :initform (c? (loop for (ticker-sym price weight) in (index-def self)
+ collecting (cons (ensure-ticker (trading-day self) ticker-sym price -60)
+ ;; whoa, a mid-rule to-be! (FN ticker-weights rule)
+ weight))))
+
+ (state :reader state
+ :initform (let ((moves (make-hash-table :size 50)))
+ (c-formula (:lazy nil) ;; do not re-compute on every trade (see FN lazy)
+ (count-it :index-state-calc)
+ (clrhash moves) ;; Re-use OK since fresh cons triggers dataflow (FN state rule)
+ (let ((minutes (loop for (ticker . nil) in (ticker-weights self)
+ maximizing (ticker-trade-minute ticker))))
+ (without-c-dependency ;; dependency on trade minute suffices (see FN without-c-dependency)
+ (loop for (ticker . weight) in (ticker-weights self)
+ summing (* weight (ticker-price ticker)) into value
+ do (setf (gethash (ticker-sym ticker) moves) (last-move ticker))
+ finally (return (list minutes value moves))))))))
+
+ (value :reader value :initform (c? (second (^state))))
+ ;;
+ ;; allows dependency on just value, which will not change on unchanged trades (FN value cell)
+ ))
+
+
+(defun index-minutes (index) (first (state index)))
+(defun index-moves (index) (third (state index)))
+(defun index-ticker-sym-move (index ticker-sym) (gethash ticker-sym (index-moves index)))
+(defun index-ticker-move (index ticker) (index-ticker-sym-move index (ticker-sym ticker)))
+
+(def-c-output value ((self index))
+ (when *trc-trades*
+ (trc "index time:" (index-minutes self) :value new-value :was old-value)))
+
+;;; --- trade ---------------------------------------------------------------------
+
+(defstruct trade minute ticker-sym price)
+
+(defun message-to-trade (message)
+ (destructuring-bind (ticker-sym ticker-min &key last) (rest message)
+ (make-trade
+ :ticker-sym ticker-sym
+ :minute ticker-min
+ :price last)))
+
+;;; --- utilities ---------------------------------------------------------
+
+(defun encode-julian-date (year julian)
+ (+ (encode-universal-time 0 0 0 1 1 year )
+ (* (1- julian) 86400))) ;; seconds in a day
+
+;; I am sorry, that is all there is to tell. So we have a mindless main loop and a few declarations
+;; and somehow we get all the functionality desired. [OK, granted, this is a pretty simple
+;; batch process which would not be too complicated in non-Cells form. In that regard, it
+;; is a good tutorial use case but does not show off Cells very much.] Anyway...
+;;
+;; It occurs to me that the above notes do not convey how the damn thing works. So let us walk
+;; thru a hand-execution of the above sample data.
+;;
+;; (make-be 'trading-day) -> (to-be (make-instance 'trading-day))
+;;
+;; Each ruled Cell gets evaluated. Each Cell slot -- constant, input, or ruled -- is output.
+;; So with trading-day:
+;;
+;; message is input, and has no associated output function
+;;
+;; date is evaluated:
+;;; (or .cache
+;;; (when (eql :date (car (^message)))
+;;; (destructuring-bind (&key date weekday)
+;;; (^message)
+;;; (declare (ignore weekday)) ;; derive from date
+;;; (encode-julian-date (+ 2000 (floor date 1000)) (mod date 1000)))))
+;;
+;; .cache is nil, but so is (message self). NIL is returned, there is no output.
+;; date now has a dependency on message.
+;;
+;; weekday is evaluated
+;;; (c? (when (^date)
+;;; (multiple-value-bind (second minute hour date month year day daylight-p zone)
+;;; (decode-universal-time (^date))
+;;; (declare (ignorable second minute hour date month year daylight-p zone))
+;;; day))))
+;; date is nil, so weekday is NIL but has a dependency on date. No output is defined.
+;;
+;; trade is evaluated
+;;; (c? (when (eql :trade (car (^message)))
+;;; (message-to-trade (^message)))))
+;; message is NIL, so NIL is returned. trade now has a dependency on message. The output
+;; method on trade is invoked, but has no interest in NIL new values.
+;;
+;; indexes is evaluated:
+;;; (with-c-cache ('cons)
+;;; (when (eql :index (car (^message)))
+;;; (make-be 'index
+;;; :trading-day self
+;;; :index-def (second (^message)))))))
+;; message is NIL, so NIL is returned, a dependency on message created. No output defined.
+;;
+;; (setf (message t-day) <the :date message>)
+;;
+;; Many rules are dispatched: date, trade, and indexes. Only date processes :date messages.
+;; it returns a converted date, and still has a dependency on message. Weekday has a dependency
+;; on date, so that rule gets dispatched. It returns a weekday calculated off the date, and
+;; keeps the dependency on that. Other rules return
+;; NIL, which is the same value they had before. Nothing else is done (and in this case, that
+;; would only have been to call the output method on trade.
+;;
+;; (setf (message t-day) <the :index message>)
+;;
+;; The date rule runs and returns its .cache without accessing any cell. The Cell internals
+;; optimize away the fact that date ever had a rule or any kind of cell. It sees weekday
+;; was a dependent on date and nothing else, so it optimizes that away, too. Slots end up
+;; with the last values calculated, and now look to other rules as if they were constant
+;; all along.
+;;
+;; The trade rule runs and comes up empty again. The indexes rule runs and adds a new
+;; index list to its current contents, which happens to be NIL.
+;;
+;;;; make-be is called on the index instance. Each slot gets processed in turn in a
+;;;; fashion similar to that for trading-day. When the ticker-weights rule runs, ticker
+;;;; instances for each ticker in the index are created and passed to TO-BE, in the
+;;;; function ensure-ticker. No dependencies are created since index-def is not a Cell,
+;;;; so the ticker-weights cell gets optimized away.
+;;;;
+;;;; as each ticker is created and processed by TO-BE:
+;;;;;;;
+;;;; the state rule is evaluated and computes an initial index state off the data
+;;;; provided in the index-def. state ends up with dependencies on each ticker in the
+;;;; index.
+;; [rest under construction]
+;;
+
+;;; =============================================================================
+;;; Footnotes
+;;; =============================================================================
+;
+;; --- FN to-be --------------------------------------
+;; TO-BE jumpstarts a Cells instance into the flow. Literally, as in
+;; the dataflow. It evaluates ruled slots to establish dependencies (those
+;; get established during evaluation) and in turn arrange for state change
+;; within the model to propagate to the instance's ruled Cells. It also
+;; DEF-C-OUTPUTs all cell slots so the outside world is consistent
+;; with the model state. More on def-c-output below.
+;
+;; --- FN c-input ------------------------------------
+;;
+;; c-in is short for c-input, which simply means imperative application code
+;; can SETF this slot. (Note that this is just the initform for this slot,
+;; which can be overridden by subclasses or at make-instance time, and if
+;; the override is not another C-IN or C-INPUT, then all bets are off. ie, The
+;; SETF ability depends on the type of Cell (if any) associated at run-time
+;; with the slot of an instance. It
+;; is not an attribute of the slot as with the :cell slot option discussed just below.
+;;
+;; Anyway, C-IN lets us make a lot of points about Cells.
+;;
+;; First, no model is
+;; an island; the dataflow has to start somewhere. Just as a VisiCalc spreadsheet
+;; has cells where you can type, say, different interest rates to see how that
+;; effects the rest of a financial model, a Cell-based application model needs
+;; some way to interface with the outside world, if only the mouse and keyboard
+;; of a GUI application.
+;;
+;; The way we do that is by having conventional application code feed (SETF) data into
+;; the dataflow model at what we call cell inputs. In a typical GUI app, this means
+;; having callbacks registered with the window manager. The callbacks then take their
+;; arguments (window events such as mouse-downs and key-presses) and setf that
+;; info to slots of a window or system instance modelling the window or operating
+;; system, slots mediated by c-input Cells.
+;;
+;; In this simple use case we have just one stream of external inputs (messages
+;; from some financial data service) being SETFed into one slot, the message
+;; slot of an instance of the trading-day class.
+;;
+;; Second, the Cells design enforces discipline. So in case you are
+;; wondering, no, if you do not bind a C-INPUT to a slot of an instance, you cannot
+;; SETF that slot from imperative code. (Aside: (SETF SLOT-VALUE) /is/ a back door
+;; allowing you to wreak havoc on your dataflow model if you so choose (but it will
+;; wreak havoc).)
+;;
+;; Third, you might wonder why slots meant as inputs cannot just have no Cell at all
+;; associated with them at run-time, and then have the Cell internals accept that
+;; as a SETF-able state. Well, it is a long story, but it turns out that a lot of
+;; Cells overhead can be avoided if we distinguish a slot whose value will never
+;; change from an input slot which will be SETF'ed. A simple example of a constant
+;; slot would be the bounding rectangle of a push button. Those values have to be
+;; Cells because in other graphical elements sharing the same superclass, the bounding
+;; rectangle changes. A good example is the win32-style scroll bar thumb, which changes
+;; size to reflect how much of the total file is visible. Anyway, it turns out that
+;; a significant performance boost comes from having Cells which happen to access
+;; a constant value not record a dependency on that value and, where a rule evaluation
+;; turns out not to access any non-constant other Cell slot, likewise convert the ruled
+;; slot into a constant slot. Sorry you asked?
+;;
+;; --- FN ephemeral -----------------------------------------------------------
+;;
+;; Whoa, here is an advanced topic. Ephemeral means "fleeting". Before getting into
+;; that, the other options for the :cell option are T and NIL. T is the default.
+;; NIL means you get a normal slot having nothing to do with Cells. Now about
+;; that :ephemeral option: Messages are
+;; like events: they happen, then they are no more. This is a problem for
+;; Cells, which like a VisiCalc spreadsheet model (say, your household budget)
+;; is all about steady-state occasionally perturbed by inputs. That is vague.
+;; Here is a concrete example: suppose you have some game where the user has
+;; to press a key when two randomly moving shapes overlap. You will have a hit rule
+;; that says (abbreviated somewhat):
+;;
+;; (and (eql (event *sys*) :keypress) (shapes-overlap-p *sys*))
+;;
+;; OK, the key is pressed but the shapes do not overlap. No cigar. Now a few
+;; seconds later the shapes do overlap. The key is not being pressed, but the
+;; EVENT slot of the *sys* instance (modelling the computer system) still
+;; says :keypress. bad news. Obviously we need to process an event and then
+;; clear the value before processing any other model input. Now perhaps we could
+;; simply have imperative code which says:
+;;
+;; (setf (event *sys*) :keypress)
+;; (setf (event *sys*) nil)
+;;
+;; But that is different. That suggests an application semantic in which the
+;; EVENT slot changes from :keypress to NIL. It will trigger all the usual
+;; dataflow, to see if the model should react. But in fact what we /really/
+;; need is /not/ to clear the EVENT slot. What we really need is
+;; ephemeral SETF behavior from a mechanism designed for steady-state.
+;; We need the EVENT slot to take on a value just long enough to perturb our
+;; model and then cease to be without fanfare.
+;;
+;; So we extend the Cells model with the :ephemeral option on a slot, and have
+;; Cell internals watch out for that and silently clear the slot once a value
+;; has been propagated to other Cells and output (again, outputs
+;; are discussed below.)
+;;
+;; A final newbie note: watch the bouncing options. Ephemerality is a slot option,
+;; not something one tailors to the instance. Think about it. Think about the
+;; slot names. "message", "event". We want to get ephemeral behavior for these
+;; slots no matter what cell (input or ruled) we choose to associate with them.
+;; So it is more convenient and reliable to endow the slot itself with ephemerality.
+;; in other cases we see different instances enjoying different Cell-ish qualities
+;; for the same slot, sometimes constant, sometimes computed, sometimes being
+;; SETFed by imperative code outside the dataflow model. These variations are
+;; then found in the type of runtime Cell associated with the Cell slot.
+;;
+;; --- FN date/.cache --------------------------------------------------
+;;
+;;
+;; There is a lot going on here, too, including some premature optimization.
+;;
+;; First of all, .cache is just a local variable, bound by the expansion
+;; of the C? macro to the latest value calculated for this rule. It starts out as NIL, so
+;; the rule next reads the message slot of the same trading-day instance. How so?
+;;
+;; ^message is a macro written by the defmodel macro. It expands simply to:
+;;
+;; (message self)
+;;
+;; It used to expand to more, including vital Cell plumbing. Now I keep it around just
+;; because I love that self-documenting quality. And yes, I have adopted the
+;; Smalltalk "self" convention over the C++ "this" convention. There is no need
+;; to use these (^macros), just code (<slot-name> self) and you will establish a
+;; dependency on the message slot. What does dependency mean?
+;;
+;; Simply that the next time the message slot changes (the default test between old and
+;; new values is EQL, but can be overridden), the Cells engine will immediately kick
+;; the DATE rule to see if it wants to compute a different value.
+;;
+;; A very important point is that dependencies are established automatically simply
+;; by invoking the reader or accessor associated with a slot, and that this happens
+;; dynamically at run-time, not by inspection of code. A second point is that the
+;; dependency is established even if the read takes place in a called function.
+;;
+;; There is a backdoor. No dependencies are established in code wrapped by
+;; the macro WITHOUT-C-DEPENDENCY.
+;;
+;; Another important point is that dependencies are re-decided completely each time
+;; a rule is invoked. So this particular rule is an oddball: it will produce only one value, when a :date
+;; message is received
+;; and teh first non-NIL value is returned. On the next message (of any kind) .cache will be
+;; non-NIL and the rule will simply return that value.
+;; During this last evaluation the cell will not access, hence no longer
+;; depend on, the message slot or any other slot and it will get optimized away. This
+;; improves performance, since the message slot no longer bothers propagating to
+;; the date slot and Cell internals no longer have to invoke the rule. Otherwise, every
+;; new message for the entire day (none of which would be :date messages) would kick
+;; off this rule.
+;;
+;; --- FN with-c-cache ------------------------------------
+;;
+;; I am actually doing something new here. The idea is that again we deviate
+;; slightly from the spreadsheet paradigm and want to accumulate data
+;; from a stream of ephemeral values. Normally we calculate a slot value in
+;; its entirety from data at hand, even if only ephemerally. Here we want
+;; to add a newly computed result to a list of prior such results.
+;;
+;; with-c-cache will accept any two-argument function, and when the enclosed
+;; form returns a non-nil value, pass that and the .cache to the specified
+;; function.
+;;
+;; --- FN def-c-output --------------------------------------------
+;;
+;; Above is another optimization, and the long-awaited discussion of Cell
+;; output.
+;;
+;; Output reinforces the "no model is an island" theme. We create
+;; models to obtain interesting outputs from inputs, where the model
+;; provides the interest. For a RoboCup player simulation, the inputs are
+;; sensory information about the game, provided in a stream from a server
+;; application managing multiple client players and coaches. The outputs are
+;; messages to the server indicating player choices about turning, running,
+;; and kicking. In between, the game play model is supposed to compute
+;; actions producing more or less capable soccer play.
+;;
+;; --- FN trade setf optimization ---------------------------------------
+;
+;; But this is strange "output". It actually changes internal model state.
+;; It is no output at all, just feeding dataflow back into a different
+;; model input. Whassup?
+;;
+;; Like I said, it is an optimization. A ticker instance could have a
+;; rule which watched the message stream looking for trades on that ticker,
+;; but then every ticker would be watching the message stream.
+;;
+;; Instead, we simply leverage an "output" method to procedurally decide which
+;; ticker has been traded and directly add the trade to that ticker's list
+;; of trades.
+;;
+;; --- FN trades def-c-output --------------------------------------
+;;
+;; Now the above is a proper output. Merely a print trace to standard output, but
+;; that happens to be all the output we want just now. In a real trading application,
+;; there probably would not be an output on this slot. Some gui widget might "output"
+;; by telling the OS to redraw it, or some trader instance might decide to output
+;; a buy order to an exchange, but that is about it.
+;;
+;; --- FN ticker-weights rule --------------------------------------
+;;
+;; A curiosity here is that ensure-ticker will often be making and to-be-ing new model
+;; instances while this rule is running. No problem, though it would be possible to
+;; get into trouble if such destructive (well, constructive) operations triggered
+;; dataflow back to this same rule. Here we are safe; it does not. In fact...
+;;
+;; This rule runs once and then gets optimized away, because in this simple case
+;; index-def is a constant, not even a cell. Should we someday want to handle
+;; changes to an index during a trading-day, this would have to change.
+;;
+;; --- FN lazy ------------------------------------------------------
+;;
+;; Lazy ruled cells do not get calculated until someone asks their value,
+;; and once they are evaluated and dependencies have been established,
+;; they merely will be flagged "obsolete" should any of those dependencies
+;; change in value.
+;;
+;; --- FN state rule ------------------------------------------------
+;;
+;; c? ends up wrapping its body in a lambda form which becomes the rule for this
+;; slot, and here that lambda form will close over the MOVES hash-table. Neat, eh?
+;; What is going on is that we do not anticipate in the application design that
+;; any cell will depend in isolation on the move of one ticker in the index. So
+;; we can allocate just one hashtable at make-instance time and reuse that each
+;; time the rule gets evaluated. Cells depending on the state Cell will know
+;; when that aggregate value gets recomputed because the finally clause conses
+;; up a new list each time.
+;;
+;; --- FN without-c-dependency -------------------------------------
+;;
+;; Our application knowledge tells us the dependency on ticker minute will suffice
+;; to keep index state up to date, so we save a lot of internal cells overhead
+;; by taking a chance and disabling dependency creation within the wrapper
+;; with-c-output. (The danger is that someone later adds a desired dependency reference
+;; to the rule without noticing the wrapper.)
+;;
+;; --- FN value Cell --------------------------------------------------
+;;
+;; Weird, right? Well, we noticed that many trades came thru at the same price
+;; sequentially. The rule above for STATE gets kicked off on each trade, and the
+;; index gets recomputed. Because it is an aggregate, we get a new list for state
+;; even if the trade was at an unchanged priced and the index value does not change.
+;;
+;; Now suppose there was some BUY! rule which cared only about the index value, and not
+;; the latest minute traded of that value, which /would/ change if a new trade at
+;; an unchanged price were received. Because a new list gets consed up (never mind the
+;; new trade minute), The BUY! rule would get kicked off because of the new list in the
+;; the STATE slot. Not even overriding the default EQL test with EQUAL would work,
+;; because the trade minute would have changed.
+;;
+;; What to do? The above. Let VALUE get recalculated unnecessarily and return unchanged,
+;; then code the BUY! rule to use VALUE. VALUE will get kicked off, but not BUY!, which
+;; would likely be computationally intense.
+;;
+
+#| TRADEDATA
+(:date 5123 :weekday 3)
+(:index ((AA 29.30 7.3894672) (AIG 53.30 7.3894672)(AXP 53.00 7.3894672)
+(BA 59.87 7.3894672) (C 46.80 7.3894672) (CAT 87.58 7.3894672) (DD 47.74 7.3894672)
+(DIS 26.25 7.3894672) (GE 36.10 7.3894672) (GM 27.77 7.3894672) (HD 36.75 7.3894672)
+(HON 35.30 7.3894672) (HPQ 21.00 7.3894672) (IBM 76.47 7.3894672)
+(INTC 23.75 7.3894672) (JNJ 68.73 7.3894672) (JPM 35.50 7.3894672) (KO 43.76 7.3894672)
+(MCD 29.80 7.3894672) (MMM 76.76 7.3894672) (MO 65.99 7.3894672) (MRK 34.42 7.3894672)
+(MSFT 25.36 7.3894672) (PFE 27.5 7.3894672) (PG 54.90 7.3894672) (SBC 23.8 7.3894672)
+(UTX 100.96 7.3894672) (VZ 36.75 7.3894672) (WMT 48.40 7.3894672) (XOM 56.50 7.3894672)))
+(:trade INTC 0.001932 :last 23.75)
+(:trade MSFT 0.001932 :last 25.36)
+(:trade INTC 0.011931 :last 23.75)
+(:trade MSFT 0.011931 :last 25.36)
+(:trade MSFT 0.041965 :last 25.32)
+(:trade UTX 0.067027 :last 101.39)
+(:trade INTC 0.067062 :last 23.82)
+(:trade MSFT 0.070397 :last 25.37)
+(:trade INTC 0.070397 :last 23.82)
+(:trade MSFT 0.074167 :last 25.32)
+(:trade INTC 0.081800 :last 23.83)
+(:trade MSFT 0.097178 :last 25.33)
+(:trade MSFT 0.106488 :last 25.32)
+(:trade INTC 0.110410 :last 23.82)
+(:trade INTC 0.124263 :last 23.83)
+(:trade MSFT 0.130411 :last 25.33)
+(:trade INTC 0.143792 :last 23.81)
+(:trade MSFT 0.143792 :last 25.33)
+(:trade DIS 0.150441 :last 26.25)
+(:trade INTC 0.160480 :last 23.82)
+(:trade MSFT 0.160480 :last 25.33)
+(:trade HPQ 0.166767 :last 21.00)
+(:trade INTC 0.178832 :last 23.82)
+(:trade MSFT 0.183710 :last 25.33)
+(:trade DIS 0.187167 :last 26.25)
+(:trade AIG 0.193117 :last 53.60)
+(:trade INTC 0.196399 :last 23.81)
+(:trade PFE 0.200523 :last 27.51)
+(:trade MSFT 0.200523 :last 25.33)
+(:trade GE 0.202185 :last 36.11)
+(:trade MSFT 0.207199 :last 25.37)
+(:trade BA 0.209810 :last 59.75)
+(:trade INTC 0.210524 :last 23.83)
+(:trade MSFT 0.230556 :last 25.37)
+(:trade INTC 0.230556 :last 23.83)
+(:trade BA 0.234812 :last 59.76)
+(:trade MSFT 0.240580 :last 25.37)
+(:trade INTC 0.247233 :last 23.83)
+(:trade MSFT 0.256892 :last 25.37)
+(:trade UTX 0.257729 :last 101.33)
+(:trade GE 0.261942 :last 36.11)
+(:trade AIG 0.267072 :last 53.60)
+(:trade MSFT 0.272956 :last 25.36)
+(:trade INTC 0.275617 :last 23.83)
+(:trade WMT 0.280660 :last 48.40)
+(:trade SBC 0.284975 :last 23.78)
+(:trade GE 0.289229 :last 36.10)
+(:trade MSFT 0.292285 :last 25.35)
+(:trade DIS 0.295646 :last 26.30)
+(:trade HPQ 0.303630 :last 21.04)
+(:trade IBM 0.305629 :last 76.60)
+(:trade INTC 0.307321 :last 23.81)
+(:trade INTC 0.310671 :last 23.81)
+(:trade SBC 0.316331 :last 23.76)
+(:trade AIG 0.322292 :last 53.60)
+(:trade MSFT 0.324057 :last 25.36)
+(:trade MCD 0.324057 :last 29.79)
+(:trade UTX 0.325694 :last 101.15)
+(:trade INTC 0.327348 :last 23.81)
+(:trade IBM 0.336878 :last 76.60)
+(:trade MSFT 0.342414 :last 25.37)
+(:trade MSFT 0.345710 :last 25.37)
+(:trade HD 0.346983 :last 36.82)
+(:trade BA 0.347295 :last 59.80)
+(:trade MCD 0.360765 :last 29.80)
+(:trade HPQ 0.364067 :last 21.03)
+(:trade MSFT 0.364067 :last 25.37)
+(:trade SBC 0.367409 :last 23.79)
+(:trade MSFT 0.392928 :last 25.36)
+(:trade AIG 0.407453 :last 53.55)
+(:trade HPQ 0.407533 :last 21.03)
+(:trade SBC 0.407533 :last 23.79)
+(:trade MSFT 0.407533 :last 25.36)
+(:trade INTC 0.407533 :last 23.82)
+(:trade HPQ 0.407533 :last 21.03)
+(:trade HD 0.407545 :last 36.84)
+(:trade BA 0.413185 :last 59.80)
+(:trade INTC 0.414117 :last 23.81)
+(:trade PFE 0.420796 :last 27.51)
+(:trade DIS 0.424120 :last 26.30)
+(:trade AIG 0.424654 :last 53.58)
+(:trade INTC 0.427471 :last 23.81)
+(:trade XOM 0.429865 :last 56.85)
+(:trade IBM 0.431927 :last 76.65)
+(:trade HPQ 0.432407 :last 21.04)
+(:trade HD 0.432507 :last 36.84)
+(:trade MCD 0.439207 :last 29.80)
+(:trade MSFT 0.442518 :last 25.36)
+(:trade DIS 0.442518 :last 26.30)
+(:trade MSFT 0.453747 :last 25.36)
+(:trade PFE 0.458821 :last 27.52)
+(:trade IBM 0.459026 :last 76.66)
+(:trade HON 0.467342 :last 35.36)
+(:trade XOM 0.469083 :last 56.88)
+(:trade INTC 0.470871 :last 23.80)
+(:trade SBC 0.476712 :last 23.79)
+(:trade BA 0.476730 :last 59.80)
+(:trade MCD 0.479248 :last 29.80)
+(:trade HPQ 0.479248 :last 21.03)
+(:trade AIG 0.480883 :last 53.57)
+(:trade MSFT 0.482567 :last 25.36)
+(:trade INTC 0.482567 :last 23.80)
+(:trade IBM 0.484223 :last 76.73)
+(:trade MSFT 0.494243 :last 25.36)
+(:trade AIG 0.497551 :last 53.57)
+(:trade PFE 0.497569 :last 27.53)
+(:trade INTC 0.504245 :last 23.80)
+(:trade HD 0.504660 :last 36.84)
+(:trade IBM 0.504849 :last 76.73)
+(:trade GM 0.507621 :last 30.53)
+(:trade SBC 0.511484 :last 23.79)
+(:trade HPQ 0.514265 :last 21.04)
+(:trade HD 0.514798 :last 36.85)
+(:trade MSFT 0.517601 :last 25.32)
+(:trade WMT 0.524286 :last 48.46)
+(:trade IBM 0.524286 :last 76.74)
+(:trade INTC 0.529220 :last 23.80)
+(:trade HPQ 0.536813 :last 21.04)
+(:trade PG 0.537627 :last 54.91)
+(:trade PFE 0.540979 :last 27.54)
+(:trade INTC 0.544290 :last 23.80)
+(:trade PG 0.547549 :last 54.91)
+(:trade XOM 0.547624 :last 56.85)
+(:trade HON 0.547687 :last 35.40)
+(:trade UTX 0.550986 :last 101.33)
+(:trade HD 0.555694 :last 36.85)
+(:trade MSFT 0.560792 :last 25.35)
+(:trade INTC 0.564337 :last 23.80)
+(:trade XOM 0.566779 :last 56.85)
+(:trade BA 0.567359 :last 59.81)
+(:trade HON 0.581023 :last 35.41)
+(:trade INTC 0.589796 :last 23.80)
+(:trade BA 0.596050 :last 59.80)
+(:trade CAT 0.612134 :last 87.83)
+(:trade WMT 0.618386 :last 48.44)
+(:trade INTC 0.620474 :last 23.80)
+(:trade MCD 0.624417 :last 29.80)
+(:trade MSFT 0.627748 :last 25.35)
+(:trade BA 0.630881 :last 59.83)
+(:trade AIG 0.634410 :last 53.56)
+(:trade MCD 0.637785 :last 29.79)
+(:trade HON 0.637785 :last 35.40)
+(:trade INTC 0.649577 :last 23.79)
+(:trade BA 0.655889 :last 59.85)
+(:trade HD 0.662287 :last 36.83)
+(:trade AIG 0.669431 :last 53.53)
+(:trade HON 0.671133 :last 35.44)
+(:trade MCD 0.674457 :last 29.79)
+(:trade MO 0.683443 :last 66.20)
+(:trade INTC 0.687668 :last 23.79)
+(:trade MSFT 0.691181 :last 25.35)
+(:trade PFE 0.694477 :last 27.54)
+(:trade MSFT 0.720936 :last 25.35)
+(:trade GM 0.726237 :last 30.50)
+(:trade WMT 0.730056 :last 48.40)
+(:trade IBM 0.740544 :last 76.74)
+(:trade PG 0.744569 :last 54.91)
+(:trade HON 0.752103 :last 35.46)
+(:trade CAT 0.753014 :last 87.85)
+(:trade MO 0.763918 :last 66.20)
+(:trade MSFT 0.764592 :last 25.35)
+(:trade HON 0.771289 :last 35.46)
+(:trade BA 0.772935 :last 59.75)
+(:trade JPM 0.773229 :last 35.51)
+(:trade MSFT 0.774612 :last 25.35)
+(:trade PG 0.776267 :last 54.91)
+(:trade AIG 0.781168 :last 53.54)
+(:trade HD 0.782946 :last 36.87)
+(:trade CAT 0.784614 :last 87.85)
+(:trade XOM 0.786285 :last 56.88)
+(:trade MSFT 0.792950 :last 25.36)
+(:trade UTX 0.794689 :last 101.40)
+(:trade INTC 0.797969 :last 23.78)
+(:trade IBM 0.801301 :last 76.74)
+(:trade HD 0.809652 :last 36.87)
+(:trade JPM 0.809652 :last 35.51)
+(:trade MSFT 0.811489 :last 25.37)
+(:trade MO 0.812994 :last 66.20)
+(:trade IBM 0.816563 :last 76.75)
+(:trade MCD 0.828046 :last 29.77)
+(:trade UTX 0.829055 :last 101.37)
+(:trade MSFT 0.833420 :last 25.36)
+(:trade GM 0.837650 :last 30.50)
+(:trade IBM 0.838004 :last 76.75)
+(:trade HON 0.838531 :last 35.47)
+(:trade XOM 0.841372 :last 56.88)
+(:trade MCD 0.841894 :last 29.78)
+(:trade KO 0.853202 :last 43.98)
+(:trade UTX 0.858235 :last 101.38)
+(:trade INTC 0.864331 :last 23.82)
+(:trade PFE 0.869104 :last 27.55)
+(:trade HON 0.873063 :last 35.48)
+(:trade IBM 0.873095 :last 76.77)
+(:trade HD 0.873132 :last 36.87)
+(:trade XOM 0.884796 :last 56.86)
+(:trade UTX 0.884820 :last 101.38)
+(:trade HON 0.888886 :last 35.48)
+(:trade INTC 0.891420 :last 23.81)
+(:trade CAT 0.895715 :last 87.86)
+(:trade MO 0.898111 :last nil) ;; 66.19)
+(:trade XOM 0.898111 :last 56.87)
+(:trade IBM 0.899775 :last 76.78)
+(:trade BA 0.899775 :last 59.83)
+(:trade MSFT 0.901469 :last 25.38)
+(:trade HD 0.906673 :last 36.86)
+(:trade HPQ 0.908113 :last 21.03)
+(:trade CAT 0.916467 :last 87.85)
+(:trade BA 0.916467 :last 59.83)
+(:trade MSFT 0.918773 :last 25.38)
+(:trade PFE 0.926271 :last 27.57)
+(:trade MO 0.926288 :last 66.18)
+(:trade WMT 0.929791 :last 48.40)
+(:trade KO 0.932333 :last 43.98)
+(:trade JNJ 0.933224 :last 68.15)
+(:trade PG 0.936516 :last 54.91)
+(:trade INTC 0.938989 :last 23.81)
+(:trade IBM 0.942596 :last 76.78)
+(:trade XOM 0.944052 :last 56.89)
+(:trade INTC 0.944885 :last 23.81)
+(:trade BA 0.946486 :last 59.85)
+(:trade IBM 0.958178 :last 76.78)
+(:trade INTC 0.959853 :last 23.81)
+(:trade JPM 0.959897 :last 35.50)
+(:trade WMT 0.961498 :last 48.40)
+(:trade MCD 0.963195 :last 29.77)
+(:trade HPQ 0.966525 :last 21.03)
+(:trade AIG 0.968663 :last 53.54)
+(:trade XOM 0.978210 :last 56.89)
+(:trade AIG 0.979896 :last 53.55)
+(:trade CAT 0.979896 :last 87.85)
+(:trade MCD 0.984732 :last 29.77)
+(:trade PG 0.985307 :last 54.90)
+(:trade WMT 0.995716 :last 48.41)
+(:trade MSFT 1.005256 :last 25.38)
+(:trade PFE 1.005256 :last 27.55)
+(:trade JPM 1.008448 :last 35.48)
+(:trade CAT 1.011343 :last 87.86)
+(:trade XOM 1.011825 :last 56.88)
+(:trade INTC 1.012667 :last 23.79)
+(:trade JNJ 1.018655 :last 68.15)
+(:trade KO 1.021589 :last 43.99)
+(:trade INTC 1.026597 :last 23.78)
+(:trade HD 1.029577 :last 36.85)
+(:trade MSFT 1.029936 :last 25.39)
+(:trade JPM 1.033267 :last 35.49)
+(:trade C 1.064996 :last 46.80)
+(:trade CAT 1.065946 :last 87.85)
+(:trade MCD 1.066687 :last 29.75)
+(:trade MRK 1.066687 :last 34.33)
+(:trade PFE 1.066687 :last 27.55)
+(:trade INTC 1.066687 :last 23.79)
+(:trade INTC 1.066687 :last 23.79)
+(:trade XOM 1.068360 :last 56.88)
+(:trade JPM 1.068360 :last 35.49)
+(:trade XOM 1.068360 :last 56.89)
+(:trade KO 1.068360 :last 43.99)
+(:trade MRK 1.070274 :last 34.34)
+(:trade HON 1.073312 :last 35.49)
+(:trade PFE 1.080025 :last 27.55)
+(:trade MCD 1.080025 :last 29.75)
+(:trade INTC 1.080025 :last 23.79)
+(:trade AIG 1.083337 :last 53.55)
+(:trade GM 1.083420 :last 30.55)
+(:trade XOM 1.086739 :last 56.89)
+(:trade HON 1.093425 :last 35.49)
+(:trade HPQ 1.093425 :last 21.03)
+(:trade INTC 1.093425 :last 23.79)
+(:trade MSFT 1.093425 :last 25.37)
+(:trade JPM 1.098339 :last 35.49)
+(:trade IBM 1.099113 :last 76.86)
+(:trade XOM 1.104257 :last 56.89)
+(:trade MCD 1.104268 :last 29.74)
+(:trade GE 1.108379 :last 36.14)
+(:trade MSFT 1.108408 :last 25.40)
+(:trade XOM 1.115052 :last 56.89)
+(:trade JPM 1.118397 :last 35.50)
+(:trade GM 1.118397 :last 30.55)
+(:trade C 1.125426 :last 46.78)
+(:trade MCD 1.132390 :last 29.74)
+(:trade WMT 1.133494 :last 48.40)
+(:trade MRK 1.135099 :last 34.33)
+(:trade MSFT 1.135099 :last 25.39)
+(:trade INTC 1.135099 :last 23.78)
+(:trade INTC 1.146096 :last 23.79)
+(:trade KO 1.146108 :last 43.99)
+(:trade WMT 1.155346 :last 48.41)
+(:trade PG 1.158447 :last 54.90)
+(:trade WMT 1.162645 :last 48.41)
+(:trade HON 1.162660 :last 35.52)
+(:trade KO 1.162672 :last 43.98)
+(:trade JNJ 1.166783 :last 68.20)
+(:trade DIS 1.166815 :last 26.34)
+(:trade HD 1.166856 :last 36.90)
+(:trade MCD 1.171129 :last 29.74)
+(:trade INTC 1.175130 :last 23.79)
+(:trade JPM 1.178485 :last 35.50)
+(:trade KO 1.178485 :last 43.98)
+(:trade MSFT 1.184447 :last 25.39)
+(:trade AIG 1.191811 :last 53.56)
+(:trade WMT 1.195138 :last 48.41)
+(:trade MSFT 1.199050 :last 25.39)
+(:trade MO 1.201440 :last 66.18)
+(:trade INTC 1.201841 :last 23.80)
+(:trade DIS 1.201841 :last 26.34)
+(:trade JNJ 1.202292 :last 68.20)
+(:trade C 1.205172 :last 46.79)
+(:trade KO 1.205172 :last 43.98)
+(:trade WMT 1.209557 :last 48.40)
+(:trade INTC 1.209927 :last 23.79)
+(:trade VZ 1.209962 :last 34.75)
+(:trade MSFT 1.213558 :last 25.37)
+(:trade C 1.220169 :last 46.79)
+(:trade DIS 1.220225 :last 26.34)
+(:trade PFE 1.220225 :last 27.55)
+(:trade JNJ 1.220921 :last 68.20)
+(:trade MMM 1.223614 :last 76.70)
+(:trade INTC 1.226875 :last 23.79)
+(:trade DIS 1.230230 :last 26.34)
+(:trade HPQ 1.230230 :last 21.03)
+(:trade HON 1.230230 :last 35.52)
+(:trade PFE 1.230230 :last 27.56)
+(:trade SBC 1.230230 :last 23.78)
+(:trade C 1.236915 :last 46.79)
+(:trade MSFT 1.240577 :last 25.40)
+(:trade DIS 1.243960 :last 26.34)
+(:trade SBC 1.250258 :last 23.78)
+(:trade MCD 1.250258 :last 29.74)
+(:trade MSFT 1.250258 :last 25.40)
+(:trade INTC 1.253588 :last 23.79)
+(:trade HON 1.253588 :last 35.53)
+(:trade MCD 1.257704 :last 29.74)
+(:trade MSFT 1.262803 :last 25.37)
+(:trade KO 1.271926 :last 43.99)
+(:trade JPM 1.271926 :last 35.51)
+(:trade VZ 1.276339 :last 34.75)
+(:trade MSFT 1.280283 :last 25.40)
+(:trade HPQ 1.280283 :last 21.03)
+(:trade DIS 1.288624 :last 26.34)
+(:trade GE 1.288664 :last 36.14)
+(:trade JPM 1.288664 :last 35.51)
+(:trade AIG 1.290300 :last 53.59)
+(:trade CAT 1.290300 :last 87.86)
+(:trade IBM 1.290300 :last 76.85)
+(:trade SBC 1.291940 :last 23.77)
+(:trade XOM 1.301948 :last 56.88)
+(:trade DIS 1.303625 :last 26.34)
+(:trade AIG 1.304047 :last 53.60)
+(:trade KO 1.305316 :last 43.99)
+(:trade JPM 1.305316 :last 35.51)
+(:trade C 1.305316 :last 46.79)
+(:trade KO 1.314761 :last 43.99)
+(:trade DIS 1.316972 :last 26.35)
+(:trade HON 1.316972 :last 35.54)
+(:trade CAT 1.317022 :last 87.86)
+(:trade IBM 1.317022 :last 76.85)
+(:trade GE 1.318640 :last 36.15)
+(:trade WMT 1.320354 :last 48.41)
+(:trade HPQ 1.322354 :last 21.04)
+(:trade AIG 1.331152 :last 53.59)
+(:close)
+|#
+
+(defun msg-start (m)
+ (search "TRADEDATA" m))
+
Added: dependencies/trunk/cells/cell-types.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cell-types.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,190 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defstruct (cell (:conc-name c-))
+ model
+ slot-name
+ value
+
+ inputp ;; t for old c-variable class
+ synaptic
+ (caller-store (make-fifo-queue) :type cons) ;; (C3) probably better to notify callers FIFO
+
+ (state :nascent :type symbol) ;; :nascent, :awake, :optimized-away
+ (value-state :unbound :type symbol) ;; {:unbound | :unevaluated | :uncurrent | :valid}
+ ; uncurrent (aka dirty) new for 06-10-15. we need this so
+ ; c-quiesce can force a caller to update when asked
+ ; in case the owner of the quiesced cell goes out of existence
+ ; in a way the caller will not see via any kids dependency. Saw
+ ; this one coming a long time ago: depending on cell X implies
+ ; a dependency on the existence of instance owning X
+ (pulse 0 :type fixnum)
+ (pulse-last-changed 0 :type fixnum) ;; lazys can miss changes by missing change of X followed by unchange of X in subsequent DP
+ (pulse-observed 0 :type fixnum)
+ lazy
+ (optimize t)
+ debug
+ md-info)
+
+
+
+;_____________________ print __________________________________
+
+#+sigh
+(defmethod print-object :before ((c cell) stream)
+ (declare (ignorable stream))
+ #+shhh (unless (or *stop* *print-readably*)
+ (format stream "[~a~a:" (if (c-inputp c) "i" "?")
+ (cond
+ ((null (c-model c)) #\0)
+ ((eq :eternal-rest (md-state (c-model c))) #\_)
+ ((not (c-currentp c)) #\#)
+ (t #\space)))))
+
+(defmethod print-object ((c cell) stream)
+ (declare (ignorable stream))
+ (if *stop*
+ (format stream "<~d:~a ~a/~a = ~a>"
+ (c-pulse c)
+ (subseq (string (c-state c)) 0 1)
+ (symbol-name (or (c-slot-name c) :anoncell))
+ (md-name (c-model c))
+ (type-of (c-value c)))
+ (let ((*print-circle* t))
+ #+failsafe (format stream "~a/~a" (c-model c)(c-slot-name c))
+ (if *print-readably*
+ (call-next-method)
+ (progn
+ (c-print-value c stream)
+ (format stream "<~d:~a ~a/~a = ~a>"
+ (c-pulse c)
+ (subseq (string (c-state c)) 0 1)
+ (symbol-name (or (c-slot-name c) :anoncell))
+ (print-cell-model (c-model c))
+ (if (consp (c-value c))
+ "LST" (c-value c))))))))
+
+(export! print-cell-model)
+
+(defgeneric print-cell-model (md)
+ (:method (other) (print-object other nil)))
+
+(defmethod trcp :around ((c cell))
+ (and ;*c-debug*
+ (or (c-debug c)
+ (call-next-method))))
+
+(defun c-callers (c)
+ "Make it easier to change implementation"
+ (fifo-data (c-caller-store c)))
+
+(defun caller-ensure (used new-caller)
+ (unless (find new-caller (c-callers used))
+ (trc nil "caller-ensure fifo-adding new-caller" new-caller :used used)
+ (fifo-add (c-caller-store used) new-caller)))
+
+(defun caller-drop (used caller)
+ (fifo-delete (c-caller-store used) caller))
+
+; --- ephemerality --------------------------------------------------
+;
+; Not a type, but an option to the :cell parameter of defmodel
+;
+(defun ephemeral-p (c)
+ (eql :ephemeral (md-slot-cell-type (type-of (c-model c)) (c-slot-name c))))
+
+(defun ephemeral-reset (c)
+ (when (ephemeral-p c) ;; so caller does not need to worry about this
+ ;
+ ; as of Cells3 we defer resetting ephemerals because everything
+ ; else gets deferred and we cannot /really/ reset it until
+ ; within finish_business we are sure all callers have been recalculated
+ ; and all outputs completed.
+ ;
+ ; ;; good q: what does (setf <ephem> 'x) return? historically nil, but...?
+ ;
+ ;;(trcx bingo-ephem c)
+ (with-integrity (:ephemeral-reset c)
+ (trc nil "!!!!!!!!!!!!!! ephemeral-reset resetting:" c)
+ (md-slot-value-store (c-model c) (c-slot-name c) nil)
+ (setf (c-value c) nil))))
+
+; -----------------------------------------------------
+
+(defun c-validate (self c)
+ (when (not (and (c-slot-name c) (c-model c)))
+ (format t "~&unadopted cell: ~s md:~s" c self)
+ (c-break "unadopted cell ~a ~a" self c)
+ (error 'c-unadopted :cell c)))
+
+(defstruct (c-ruled
+ (:include cell)
+ (:conc-name cr-))
+ (code nil :type list) ;; /// feature this out on production build
+ rule)
+
+(defun c-optimized-away-p (c)
+ (eq :optimized-away (c-state c)))
+
+;----------------------------
+
+(defmethod trcp-slot (self slot-name)
+ (declare (ignore self slot-name)))
+
+(defstruct (c-dependent
+ (:include c-ruled)
+ (:conc-name cd-))
+ ;; chop (synapses nil :type list)
+ (useds nil :type list)
+ (usage (blank-usage-mask)))
+
+(defun blank-usage-mask ()
+ (make-array 16 :element-type 'bit
+ :initial-element 0))
+
+(defstruct (c-drifter
+ (:include c-dependent)))
+
+(defstruct (c-drifter-absolute
+ (:include c-drifter)))
+
+;_____________________ accessors __________________________________
+
+(defmethod c-useds (other) (declare (ignore other)))
+(defmethod c-useds ((c c-dependent)) (cd-useds c))
+
+(defun c-validp (c)
+ (eql (c-value-state c) :valid))
+
+(defun c-unboundp (c)
+ (eql :unbound (c-value-state c)))
+
+
+;__________________
+
+(defmethod c-print-value ((c c-ruled) stream)
+ (format stream "~a" (cond ((c-validp c) (cons (c-value c) "<vld>"))
+ ((c-unboundp c) "<unb>")
+ ((not (c-currentp c)) "dirty")
+ (t "<err>"))))
+
+(defmethod c-print-value (c stream)
+ (declare (ignore c stream)))
+
Added: dependencies/trunk/cells/cells-manifesto.txt
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-manifesto.txt Tue Jan 26 15:20:07 2010
@@ -0,0 +1,592 @@
+In the text that follows, [xxx] signifies a footnote named "xxx" and
+listed alphabetically at the end.
+
+Summary
+-------
+Cells is a mature, stable extension to CLOS[impl] allowing one to create classes
+whose instances can have slot values determined by instance-specific formulas.
+
+Example
+-------
+For example, in a text editor application we might have (condensed):
+
+ (make-instance 'menu-item
+ :label "Cut"
+ :enabled (c? (bwhen (f (focus *window*))
+ (and (typep f 'text-widget)
+ (selection-range f)))))
+
+Translated, the enabled state of the Cut menu item follows
+whether or not the user is focused on a text-edit widget and
+whether they have in fact selected a range of text.
+
+Meanwhile, the selection-range rule might be:
+
+(let (start)
+ (c? (if (mouse-down? .w.)
+ (bwhen (c (mouse-pos-to-char self (mouse-pos .w.)))
+ (if start
+ (list start c)
+ (setf start c)))
+ (setf start nil))))
+
+Now the only imperative code needed is some glue reading the OS event loop
+converting raw mouse down and mouse move events into window (the .w. symbol-macro)
+attributes such as mouse-down? and mouse-pos. The desired functionality is achieved
+by declarative rules which (like selection-range above) are entirely responsible for
+deciding the selection range.
+
+A final trick comes from slot observers. Suppose we are thinly wrapping a C GUI and need to
+do something in the C library to actually make menu items available or not.
+It might look something like this:
+
+ (defobserver enabled ((self menu-item) new-value old-value old-value-bound?)
+ (menu-item-set (c-ptr self) (if new-value 1 0)))
+
+ie, Some model attributes must be propagated outside the model as they change, and observers
+are callbacks we can provide to handle change.
+
+Motivation
+----------
+As a child I watched my father toil at home for hours over paper
+spreadsheets with pencil and slide rule. After he changed one value,
+he had to propagate that change to other cells by first remembering
+which other ones included the changed cell in their computation.
+Then he had to do the calculations for those, erase, enter...
+and then repeat that process to propagate those changes in a
+cascade across the paper.
+
+VisiCalc let my father take the formula he had in mind and
+put it into (declare it to) the electronic spreadsheet. Then VisiCalc
+could do the tedious work: recalculating, knowing what to recalculate,
+and knowing in what order to recalculate.
+
+Cells do for programmers what electronic spreadsheets did for my father.
+Without Cells, CLOS slots are like cells of a paper spreadsheet.
+A single key-down event can cause a cascade of change throughout an
+application. The programmer has to arrange for it all to happen,
+all in the right order: delete any selected text, insert
+the new character, re-wrap the text, update the undo mechanism, revisit
+the menu statuses ("Cut" is no longer enabled), update the scroll bars,
+possibly scroll the window, flag the file as unsaved...
+
+Here is a real-world case study:
+
+"The last company I worked with made a product that was a control unit
+for some mechanical devices, presenting both sensor readings coming in
+from those devices and an interface to program the devices. Consider
+it like a very sophisticated microwave oven, perhaps with a
+temperature probe.
+
+"The UI code was a frighteningly complex rat's nest. Input data
+arriving from the sensors changed certain state values, which caused
+the display to update, but the system state also changed, and rules
+had to be evaluated, the outcome of which might be tuning to the
+running job or warning messages presented to the user, and in the
+meantime the user may be adjusting the running job. I'm sure there are
+even more interactions I'm leaving out.
+
+"There was no "large idea" in this code to organize these dependencies
+or orchestrate the data flow. The individual facilities were
+well-formed enough: "message" input and output, GUI widgets and forms,
+real-world entities modeled as entities in the code. However, the
+connections between these things were ad-hoc and not formalized. Every
+change to the system would provoke defects, and the failure usually
+involved not propagating some event, propagating it at the wrong time,
+or propagating it to the wrong recipients."
+ --- Steven Harris, on comp.lang.lisp
+
+What Mr. Harris describes is what Fred Brooks [bullet] said was an essential
+property of software development, meaning by essential that there was no
+way around it, and thus his prediction that a software silver bullet was
+in principle impossible.
+
+Which brings us to Cells. See also [axiom] Phillip Eby's developing axiomatic
+definition he is developing in support of Ryan Forseth's SoC project. Mr. Eby was
+inspired by his involvement to develop Trellis, his own Cells work-alike library
+for Python.
+
+DEFMODEL and Slot types
+-----------------------
+Classes, some of whose slots may be mediated by Cells, are defined by DEFMODEL, which is exactly
+like DEFCLASS but adds support for two slot definition options, :cell and :unchanged-if. Classes
+defined by DEFMODEL can inherit from normal CLOS classes.
+
+New slot definition options
+----------------------------
+
+ :cell {nil | t | :ephemeral}
+
+:cell is optional. The default is ":cell t", meaning the Cells engine will manage the slot to give
+it the spreadsheet-like characteristics. Specifying NIL signifies that this slot is entirely
+outside any handling by the Cells engine; it is just a plain CLOS slot.
+
+This next bit will not make sense until we have explained propagation of state change, but
+specifying :ephemeral causes the Cells engine to reset the apparent slot
+value to NIL immediately and only after fully propagating any value assumed by the slot, either
+by assignment to an input Cell (the vastly more common case) or by a rule calculation.
+
+Ephemeral cells are necessary to correctly model events in the otherwise steady-state
+spreadsheet paradigm.
+
+ :unchanged-if <function-name>
+
+Specifying :unchanged-if is optional. [Come to think of it, it should be an error to specify
+both :cell nil and :unchanged-if.] If specified, the named function is a predicate
+of two arguments, the new and old value in that order. The predicate determines if a subsequent
+slot value (either computed or assigned to an input) is unchanged in the sense that no propagation
+is necessary, either to dependent ruled cells or (getting ahead of ourselves again) "on change" observers.
+The default unchanged test is EQL.
+
+Cell types
+----------
+The Cells library allows the programmer to specify at make-instance time that a Cell
+slot of an instance be mediated for the life of that instance by one of:
+
+ -- a so-called "input" Cell;
+ -- a "ruled" Cell; or
+ -- no Cell at all.
+
+Note that different instances of the same class may do different things Cells-wise with the same slot.
+One label widget may have a fixed width of 42 and text "Hi, Mom!", where another might have
+an input Cell mediating the text (so edit logic can assign new values as the user types) and a
+rule mediating the width so the widget can have a minimum width of 42(so it does not disappear altogether)
+yet grow based on text length and relevant font metrics to always leave room for one more character
+(if the GUI design calls for that).
+
+To summarize, the class specification supplied with DEFMODEL specifies whether a slot can /ever/
+be managed by the Cells engine. For those that can, at and only at instance initialization time
+different instances can have different Cell types and rules specified to mediate the same slot.
+
+Input Cells
+-----------
+A slot mediated by an input Cell may be assigned new values at runtime. These are how Cell-based models
+get data from the world outside the model -- it cannot be rules all the way down. Typically, these
+input assignements are made by code polling OS events via some GetNextEvent API call, or by callbacks
+registered with an event system such as win32 WindowProc functions. Other code may poll sockets or
+serial inputs from an external device.
+
+Ruled Cells
+-----------
+Ruled Cells come with an instance-specific rule in the form of an anonymous function of two variables,
+the instance owning the slot and the prior value (if any) computed by the rule. These rules consist of
+arbitrarily complex Common Lisp code, and are invoked immediately after instance initialization (but see
+the next bit on lazy cells).
+
+When a rule runs, any dynamic read (either expressly in the rule source or during the execution of
+some function invoked by the rule) of a slot of any instance mediated by a Cell of any type establishes a
+runtime dependency of the ruled cell on the slot of the instance that was read. Note then that thanks
+to code branching, dependencies can vary after every rule invocation.
+
+Lazy Ruled Cells
+----------------
+Laziness is cell-specific, applies only to ruled cells, and comes in four varieties:
+
+ :once-asked -- this will get evaluated and "observed" on initialization, but then not get reevaluated
+immediately if dependencies change, rather only when read by application code.
+
+ :until-asked -- this does not get evaluated/observed until read by application code, but then it becomes
+un-lazy, eagerly reevaluated as soon as any dependency changes (not waiting until asked).
+
+ :always -- not evaluated/observed until read, and not reevaluated until read after a dependency changes.
+
+Dataflow
+--------
+When application code assigns a new value to an input Cell (a quick way of saying an instance slot mediated by
+an input Cell) -- typically by code polling OS events or a socket or an input device -- a cascade of recalculation
+ensues to bring direct and indirect ruled dependents current with the new value assigned to the input Cell.
+
+No Cell at All
+--------------
+Because of all that, it is an error to assign a new value to a slot of an instance not mediated by any Cell.
+The Cells engine can do a handy optimization by treating such slots as constants and not creating dependencies when ruled
+Cells read these. But then we cannot let these Cells vary and still guarantee data integrity, because
+we no longer know who else to update in light of such variation. The optimization, by the way, extends to
+eliminating ruled Cells which, after any computation, end up not depending on any other cell.
+
+Again, note that this is different from specifying ":cell nil" for some slot. Here, the Cells engine
+has been told to manage some slot, but for some instance the slot has been authored to bear some value
+for the lifetime of that instance.
+
+Observers
+---------
+To allow the emergent animated data model to operate usefully on the world outside the model--if only to
+update the screen--programmers may specify so-called observer callbacks dispatched according to: slot name,
+instance, new value, old value, and whether the old value actually existed (false only on the first go).
+Observers are inherited according to the rules of CLOS class inheritance. If multiple primary observer
+methods apply because of inheritance, they all get run, most specific last.
+
+ie, observers are a GF with PROGN method combination.
+
+Observers get called in two circumstances: as part of Model object initialization, in a processing step
+just after CLOS instance initialization, and when a slot changes value. Any observer of a Cell slot
+is guaranteed to be called at least once during intialization even if a cell slot is bound to a constant
+or if it is an input or ruled Cell that never changes value.
+
+It is legal for observer code to assign to input Cells, but (a) special syntax is required to defer execution
+until the observed state change has fully propagated; and (b) doing so compromises the declarative
+quality of an application -- one can no longer look to one rule to see how a slot (in this case the
+input slot being assigned by the observer) gets its value. A reasonable usage might be one with
+a cycle, where changing slot A requires a change to slot B, and changing slot B requires a change to
+slot A, such as the scroll thumb position and the amount a document has been scrolled.
+
+Finally, to make it possible for such a declarative model to talk intelligibly to imperative systems such as
+Tcl/Tk which sometimes requires a precise sequence of commands for something to work at all, a mechanism exists by
+which client code can (a) queue tasks for execution after a data change has fully propagated and (b) process
+those tasks with a client-supplied handler. Tasks are queued with arbitrary keying data which can be used by
+the handler to sort or compress the queued tasks.
+
+
+Data Integrity
+--------------
+When application code assigns to some input cell X, the Cells engine guarantees:
+
+ - recomputation exactly once of all and only state affected by the change to X, directly or indirectly through
+ some intermediate datapoint. note that if A depends on B, and B depends on X, when B gets recalculated
+ it may come up with the same value as before. In this case A is not considered to have been affected
+ by the change to X and will not be recomputed.
+
+ - recomputations, when they read other datapoints, must see only values current with the new value of X.
+ Example: if A depends on B and X, and B depends on X, when X changes and A reads B and X to compute a
+ new value, B must return a value recomputed from the new value of X.
+
+ - similarly, client observer callbacks must see only values current with the new value of X; and
+
+ - a corollary: should a client observer SETF a datapoint Y, all the above must
+ happen with values current with not just X, but also with the value of Y /prior/
+ to the change to Y.
+
+ - Deferred "client" code must see only values current with X and not any values current with some
+ subsequent change to Y queued by an observer
+
+Benefits
+--------
+Program state guaranteed to be self-consistent, without programmer effort. Dependencies are identified
+by the engine, and change propagation happens automatically.
+
+Greater object re-use. Slots of instances can be authored with rules, not just literal values. In a sense,
+we get greater reuse by allowing instances to override slot derivations instance by instance. But not slot
+expressions, which are still class-oriented. By this I mean the observers expressing changes in value are
+dispatched by the class of the instance and so are not instance-specific. (Such a thing has been
+suggested, however.) Another strong bit of class-orientation comes from the fact that code reading
+slot X of some instance Y obviously does so without knowing how the returned value was derived. It knows
+only that the slot is named X, and will do things with that value assuming only that it has the
+X attribute of the instance Y. So again: the derivation of a slot value is potentially instance-oriented
+under Cells, but its expression or manifestation is still class-oriented.
+
+Natural decomposition of overall application complexity into so many simple rules and slot observers.
+Let's return for a moment to VisiCalc and its descendants. In even the most complex financial spreadsheet
+model, no one cell rule accesses more than a relatively few other spreadsheet cells (counting a row or
+column range as one reference). Yet the complex model emerges. All the work of tracking dependencies
+is handled by the spreadsheet software, which requires no special declaration by the modeller. They simply
+write the Cell rule. In writing the rule, they are concerned only with the derivation of one datapoint from
+a population of other datapoints. No effort goes into arranging for the rule to get run at the right time,
+and certainly no energy is spent worrying about what other cells might be using the authored cell. That
+cell has certain semantics -- "account balance", perhaps -- and the modeller need only worry about writing
+a correct, static computation of those semantics.
+
+Same with Cells. :) The only difference is that VisiCalc has one "observer" requirement for all cells:
+update the screen. In Cells applications, a significant amount of application functionality -- indeed, all
+its outputs -- end up in cell observers. But as discussed above, this additional burden falls only on
+the class designer when they decide to add a slot to a class. As instances are created and different rules
+specified for different slots to achieve custom behavior, the effort is the same as for the VisiCalc user.
+
+Model Building
+--------------
+Everything above could describe one instance of one class defined by DEFMODEL. A real application has
+multiple instances of multiple classes. So...
+
+-- cells can depend on other cells from any other instance. Since a rule gets passed only "self", Cell users
+need something like the Family class included with the Cells package effectively to turn a collection of
+instances into a network searchable by name or type.
+
+-- The overall model population must be maintainable by Cell slots such as the "kids" slot of the Family
+class. The burden here is on the Cells engine to allow one cell of one child to ask for the value of a cell of
+another child and vice versa (with different Cells), when both children are the product of the same rule,
+or different rules when "cousins" are exchanging information. So we must gracefully traverse the parent/kids
+tree dispatching kids rules just in time to produce the other instance sought.
+
+-- kid-slotting: used almost exclusively so far for orderly GUI layout, a parent must be able to specify
+rules for specific slots of kids. Example: a "stack" class wants to provide rules for child geometry
+specifying left, right, or centered alignment and vertical stacking (with optional spacing) one below
+the other. The idea is that we want to author classes of what might be GUI subcomponents without worrying
+about how they will be arranged in some container.
+
+-- finalization: when an instance appears in the "old kids" but not in the "new kids", a Cells engine
+may need to arrange for all Cells to "unsubscribe" from their dependents. Cells takes care of that if
+one calls "not-to-be" on an instance.
+
+
+Suggested Applications
+----------------------
+Any application that must maintain an interesting, long-lived data model incorporating a stream of unpredictable
+data. Two examples: any GUI application and a RoboCup soccer client.
+
+An application needing to shadow data between two systems. Examples: a Lisp GUI imlemented by thinly wrapping a
+C GUI library, where Lisp-land activity must be propagated to the C GUI, and C GUI events must propagate
+to Lisp-land. See the Cells-Gtk or Celtk projects. Also, a persistent CLOS implementation that must echo
+CLOS instance data into, say, SQL tables.
+
+Prior Art (in increasing order of priorness (age))
+---------
+Functional reactive programming:
+ This looks to be the most active, current, and vibrant subset of folks working on this sort of stuff.
+ Links:
+ FlapJax (FRP-powered web apps) http://www.flapjax-lang.org/
+ http://lambda-the-ultimate.org/node/1771
+ http://www.haskell.org/frp/
+ FrTime (scheme FRP implementation, no great links) http://pre.plt-scheme.org/plt/collects/frtime/doc.txt
+
+Adobe Adam, originally developed only to manage complex GUIs. [Adam]
+
+COSI, a class-based Cells-alike used at STSCI in software used to
+schedule Hubble telescope viewing time. [COSI]
+
+Garnet's KR: http://www.cs.cmu.edu/~garnet/
+Also written in Lisp. Cells looks much like KR, though Cells was
+developed in ignorance of KR (or any other prior art). KR has
+an astonishing number of backdoors to its constraint
+engine, none of which have turned out to be necessary for Cells.
+
+The entire constraint programming field, beginning I guess with Guy Steele's
+PhD Thesis in which he develops a constraint programming language or two:
+ http://portal.acm.org/citation.cfm?id=889490&dl=ACM&coll=ACM
+ http://www.cs.utk.edu/~bvz/quickplan.html
+
+Flow-based programming, developed by J. Paul Morrison at IBM, 1971.
+ http://en.wikipedia.org/wiki/Flow-based_programming
+
+Sutherland, I. Sketchpad: A Man Machine Graphical Communication System. PhD thesis, MIT, 1963.
+Steele himself cites Sketchpad as inexplicably unappreciated prior
+art to his Constraints system:
+
+See also:
+ The spreadsheet paradigm: http://www.cs.utk.edu/~bvz/active-value-spreadsheet.html
+ The dataflow paradigm: http://en.wikipedia.org/wiki/Dataflow
+ Frame-based programming
+ Definitive-programming
+
+Commentary
+----------
+-- Jack Unrue, comp.lang.lisp
+"Cells provides the plumbing for data dependency management which every
+non-trivial program must have; a developer using Cells can focus on
+computing program state and reacting to state changes, leaving Cells to worry about
+how that state is propagated. Cells does this by enabling a declarative
+mechanism built via an extension to CLOS, and hence achieves its goal in a way
+that meshes well with with typical Common Lisp programming style."
+
+-- Bill Clementson, http://bc.tech.coop/blog/030911.html
+"Kenny Tilton has been talking about his Cells implementation on comp.lang.lisp
+for some time but I've only just had a look at it over the past few evenings.
+It's actually pretty neat. Kenny describes Cells as, conceptually, analogous to
+a spreadsheet cell (e.g. -- something in which you can put a value or a formula
+and have it updated automatically based on changes in other "cell" values).
+Another way of saying this might be that Cells allows you to define classes
+whose slots can be dynamically (and automatically) updated and for which
+standard observers can be defined that react to changes in those slots."
+
+-- "What is Cells?", Cells-GTk FAQ, http://common-lisp.net/project/cells-gtk/faq.html#q2
+"If you are at all familiar with developing moderately complex software that
+is operated through a GUI, then you have probably
+learned this lesson: Keeping what is presented through the GUI in-sync with what
+the user is allowed to do, and in-sync with the computational state of the
+program is often tedious, complicated work. .... Cells-GTK helps
+with these tasks by providing an abstraction over the details; each of the tasks
+just listed can be controlled by (a) formula that specify the value of
+attributes of graphic features in the part-subpart declaration (that declaration
+is called 'defpart' in cells-gtk); and, (b) formula that specify the value of CLOS slots."
+
+-- Phillip Eby, PyCells and peak.events,
+ http://www.eby-sarna.com/pipermail/peak/2006-May/002545.html
+"What I discovered is quite cool. The Cells system *automatically
+discovers* dynamic dependencies, without having to explicitly specify that
+X depends on Y, as long as X and Y are both implemented using cell
+objects. The system knows when you are computing a value for X, and
+registers the fact that Y was read during this computation, thus allowing
+it to automatically invalidate the X calculation if Y changes....
+Aside from the automatic dependency detection, the cells system has
+another trick that is able to significantly reduce the complexity of
+event cascades, similar to what I was trying (but failing) to do using
+the "scheduled thread" concept in peak.events.
+Specifically, the cells system understands how to make event-based updates
+orderly and deterministic, in a way that peak.events cannot. It
+effectively divides time into "propagation" and "non-propagation"
+states. Instead of simply making callbacks whenever a computed value
+changes, the system makes orderly updates by queueing invalidated cells for
+updating. Also, if you write code that sets a new value imperatively (as
+opposed to it being pulled declaratively), the actual set operation is
+deferred until all computed cells are up-to-date with the current state of
+the universe."
+
+_____________
+Uncommentary
+
+-- Peter Seibel, comp.lang.lisp:
+"I couldn't find anything that explained what [Cells] was and why I should care."
+
+-- Alan Crowe, comp.lang.lisp:
+"Further confession: I'm bluffing. I've grasped that Cells is
+interesting, but I haven't downloaded it yet, and I haven't
+checked out how it works or what /exactly/ it does."
+
+_________
+Footnotes
+
+[Adam] "Adam is a modeling engine and declarative language for describing constraints and
+relationships on a collection of values, typically the parameters to an
+application command. When bound to a human interface (HI) Adam provides
+the logic that controls the HI behavior. Adam is similar in concept to a spreadsheet
+or a forms manager. Values are set and dependent values are recalculated.
+Adam provides facilities to resolve interrelated dependencies and to track
+those dependencies, beyond what a spreadsheet provides."
+http://opensource.adobe.com/group__asl__overview.html#asl_overview_intro_to_adam_and_eve
+________
+[bullet] This resolves a problem Fred Brooks identified in 1987: ""The essence of a software
+entity is a construct of interlocking concepts: data sets, relationships among data items, algorithms,
+and invocations of functions... Software systems have orders-of-magnitude more states than
+computers do...a scaling-up of a software entity is not merely a repetition of the same elements
+in larger sizes; it is necessarily an increase in the number of different elements. In most cases,
+the elements interact with each other in some nonlinear fashion, and the complexity of the whole
+increases much more than linearly."
+-- http://www.virtualschool.edu/mon/SoftwareEngineering/BrooksNoSilverBullet.html
+______
+[COSI] "The Constraint Sequencing Infrastructure (COSI) is an extension to
+the Common Lisp Object System (*(CLOS)) which supports a constraint
+based object-oriented programming model. .....
+
+"A constraint is a specialized method which will be automatically
+re-run by the COSI infrastructure whenever any of its input values
+change. Input values are any of the object attributes that are
+accessed by the constraint, and which are therefore assumed to
+alter the processing within the constraint.
+
+"Whenever a state change occurs those constraints which depend upon
+that state are added to a propagation queue. When the system is
+queried a propagation cycle runs ensuring that the state of the
+system is consistent with all constraints prior to returning a value."
+-- http://www.cliki.net/ACL2/COSI?source
+______
+[impl] The Cells library as it stands is all about doing interesting things
+with slots of CLOS instances, but Cells is not only about CLOS or even Lisp.
+One Cells user is known to have mediated a global variable with a Cell, some work
+was done on having slots of DEFSTRUCTs mediated by Cells, and ports to C++, Java, and
+Python have been explored.
+
+_______
+[axiom] Phillip Eby's axiomatic specification of Cells:
+
+Data Pulse Axioms
+=================
+
+Overview: updates must be synchronous (all changed cells are updated at
+once), consistent (no cell rule sees out of date values), and minimal (only
+necessary rules run).
+
+1. Global Update Counter:
+ There is a global update counter. (Guarantees that there is a
+globally-consistent notion of the "time" at which updates occur.)
+
+2. Per-Cell "As Of" Value:
+ Every cell has a "current-as-of" update count, that is initialized with
+a value that is less than the global update count will ever be.
+
+3. Out-of-dateness:
+ A cell is out of date if its update count is lower than the update
+count of any of the cells it depends on.
+
+4. Out-of-date Before:
+ When a rule-driven cell's value is queried, its rule is only run if the
+cell is out of date; otherwise a cached previous value is
+returned. (Guarantees that a rule is not run unless its dependencies have
+changed since the last time the rule was run.)
+
+5. Up-to-date After:
+ Once a cell's rule is run (or its value is changed, if it is an input
+cell), its update count must be equal to the global update
+count. (Guarantees that a rule cannot run more than once per update.)
+
+6. Inputs Move The System Forward
+ When an input cell changes, it increments the global update count and
+stores the new value in its own update count.
+
+
+Dependency Discovery Axioms
+===========================
+
+Overview: cells automatically notice when other cells depend on them, then
+notify them at most once if there is a change.
+
+
+1. Thread-local "current rule cell":
+ There is a thread-local variable that always contains the cell whose
+rule is currently being evaluated in the corresponding thread. This
+variable can be empty (e.g. None).
+
+2. "Currentness" Maintenance:
+ While a cell rule's is being run, the variable described in #1 must be
+set to point to the cell whose rule is being run. When the rule is
+finished, the variable must be restored to whatever value it had before the
+rule began. (Guarantees that cells will be able to tell who is asking for
+their values.)
+
+3. Dependency Creation:
+ When a cell is read, it adds the "currently-being evaluated" cell as a
+listener that it will notify of changes.
+
+4. Dependency Creation Order:
+ New listeners are added only *after* the cell being read has brought
+itself up-to-date, and notified any *previous* listeners of the
+change. (Ensures that the listening cell does not receive redundant
+notification if the listened-to cell has to be brought up-to-date first.)
+
+5. Dependency Minimalism:
+ A listener should only be added if it does not already present in the
+cell's listener collection. (This isn't strictly mandatory, the system
+behavior will be correct but inefficient if this requirement isn't met.)
+
+6. Dependency Removal:
+ Just before a cell's rule is run, it must cease to be a listener for
+any other cells. (Guarantees that a dependency from a previous update
+cannot trigger an unnecessary repeated calculation.)
+
+7. Dependency Notification
+ Whenever a cell's value changes (due to a rule change or input change),
+it must notify all of its listeners that it has changed, in such a way that
+*none* of the listeners are asked to recalculate their value until *all* of
+the listeners have first been notified of the change. (This guarantees
+that inconsistent views cannot occur.)
+
+7a. Deferred Recalculation
+ The recalculation of listeners (not the notification of the listeners'
+out-of-dateness) must be deferred if a cell's value is currently being
+calculated. As soon as there are no cells being calculated, the deferred
+recalculations must occur. (This guarantees that in the absence of
+circular dependencies, no cell can ask for a value that's in the process of
+being calculated.)
+
+8. One-Time Notification Only
+ A cell's listeners are removed from its listener collection as soon as
+they have been notified. In particular, the cell's collection of listeners
+must be cleared *before* *any* of the listeners are asked to recalculate
+themselves. (This guarantees that listeners reinstated as a side effect of
+recalculation will not get a duplicate notification in the current update,
+or miss a notification in a future update.)
+
+9. Conversion to Constant
+ If a cell's rule is run and no dependencies were created, the cell must
+become a "constant" cell, and do no further listener additions or
+notification, once any necessary notifications to existing listeners are
+completed. (That is, if the rule's run changed the cell's value, it must
+notify its existing listeners, but then the listener collection must be
+cleared -- *again*, in addition to the clearing described in #8.)
+
+10. No Changes During Notification:
+ It is an error to change an input cell's value while change
+notifications are taking place.
+
+11. Weak Notification
+ Automatically created inter-cell links must not inhibit garbage
+collection of either cell. (Technically optional, but very easy to do.)
+
+
Added: dependencies/trunk/cells/cells-store.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-store.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,248 @@
+;; -*- 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 store-items)
+
+(defmacro c?-with-stored ((var key store &optional default) &body body)
+ `(c? (bwhen-c-stored (,var ,key ,store ,default)
+ , at 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
+ , at 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
+ , at body))
+ (kick-c-link ,key ,store)))
+
+
+(defmacro with-store-entry ((key store &key quiet) &body body)
+ `(prog1
+ (progn
+ , at 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)))
+
+(defmethod store-items ((store cells-store) &key (include-keys nil))
+ (loop for key being the hash-keys in (data store)
+ for val being the hash-values in (data store)
+ if (and (cdr val) include-keys) collect (cons key (cdr val))
+ else if (cdr val) collect it))
+
+;;; 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")
+ , at body
+ (let ((superfluous-observers (loop for run in *observers* if (not (member run (list , at asserted-observers))) collect run))
+ (failed-observers (loop for asserted in (list , at 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))))
\ No newline at end of file
Added: dependencies/trunk/cells/cells-test/boiler-examples.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/boiler-examples.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,290 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+;;
+;; OK, nothing new here, just some old example code I found lying around. FWIW...
+;;
+
+(defmodel boiler1 ()
+ ((id :cell nil :initarg :id :accessor id :initform (random 1000000))
+ (status :initarg :status :accessor status :initform nil) ;; vanilla cell
+ (temp :initarg :temp :accessor temp :initform nil)
+ (vent :initarg :vent :accessor vent :initform nil)
+ ))
+
+(def-cell-test boiler-1 ()
+
+ ;; resets debugging/testing specials
+ (cells-reset)
+
+ (let ((b (make-instance 'boiler1
+ :temp (c-in 20)
+ :status (c? (if (< (temp self) 100)
+ :on
+ :off))
+ :vent (c? (ecase (^status) ;; expands to (status self) and also makes coding synapses convenient
+ (:on :open)
+ (:off :closed))))))
+
+ (ct-assert (eql 20 (temp b)))
+ (ct-assert (eql :on (status b)))
+ (ct-assert (eql :open (vent b)))
+
+ (setf (temp b) 100) ;; triggers the recalculation of status and then of vent
+
+ (ct-assert (eql 100 (temp b)))
+ (ct-assert (eql :off (status b)))
+ (ct-assert (eql :closed (vent b)))
+ ))
+
+#+(or)
+(boiler-1)
+
+;
+; now let's see how output functions can be used...
+; and let's also demonstrate inter-object dependency by
+; separating out the thermometer
+;
+
+;;; note that thermometer is just a regular slot, it is
+;;; not cellular.
+
+(defmodel boiler2 ()
+ ((status :initarg :status :accessor status :initform nil)
+ (vent :initarg :vent :accessor vent :initform nil)
+ (thermometer :cell nil :initarg :thermometer :accessor thermometer :initform nil)
+ ))
+
+;;; defobserver ((slot-name) (&optional method-args) &body body
+
+;;; the defobserver macro defines a method with
+;;; three arguments -- by default, these arguments are named
+;;; self -- bound to the instance being operated on
+;;; old-value -- bound to the previous value of the cellular slot
+;;; named slot-name, of the instance being operated on.
+;;; new-value -- bound to the new value of said cellular slot
+
+;;; (this is why the variables self, old-value, and new-value can exist
+;;; below in the body, when it appears they are not defined in any
+;;; lexical scope)
+
+;;; the body of the macro defines code which is executed
+;;; when the the slot-name slot is initialized or changed.
+
+(defobserver status ((self boiler2))
+ (trc "output> boiler status" self :oldstatus= old-value :newstatus= new-value)
+ ;
+ ; << in real life call boiler api here to actually turn it on or off >>
+ ;
+ )
+
+(defobserver vent ((self boiler2))
+ (trc "output> boiler vent changing from" old-value :to new-value)
+ ;
+ ; << in real life call boiler api here to actually open or close it >>
+ ;
+ )
+
+
+(defmodel quiet-thermometer ()
+ ((temp :initarg :temp :accessor temp :initform nil)
+ ))
+
+(defmodel thermometer (quiet-thermometer)())
+
+;;; notice instead of oldvalue and newvalue, here the
+;;; old and new values are bound to parameters called oldtemp
+;;; and newtemp
+
+(defobserver temp ((self thermometer) newtemp oldtemp)
+ (trc "output> thermometer temp changing from" oldtemp :to newtemp))
+
+;--------------------------
+
+
+;;; here we introduce the to-be-primary construct, which causes
+;;; immediate initialization of cellular slots.
+
+;;; notice how the status cell of a boiler2 can depend
+;;; on the temp slot of a thermometer, illustrating how
+;;; dependencies can be made between the cellular slots of
+;;; instances of different classes.
+
+
+(def-cell-test boiler-2 ()
+ (cells-reset)
+ (let ((b (make-instance 'boiler2
+ :status (c? (eko ("boiler2 status c?")
+ (if (< (temp (thermometer self)) 100)
+ :on :off)))
+ :vent (c? (ecase (^status)
+ (:on :open)
+ (:off :closed)))
+ :thermometer (make-instance 'thermometer
+ :temp (c-in 20)))))
+
+ (ct-assert (eql 20 (temp (thermometer b))))
+ (ct-assert (eql :on (status b)))
+ (ct-assert (eql :open (vent b)))
+
+ (setf (temp (thermometer b)) 100)
+
+ (ct-assert (eql 100 (temp (thermometer b))))
+ (ct-assert (eql :off (status b)))
+ (ct-assert (eql :closed (vent b)))
+ ))
+
+#+(or)
+(boiler-2)
+
+;;; ***********************************************
+;;; ***********************************************
+;;; ***********************************************
+
+#| intro to cells, example 3 |#
+
+;;; ***********************************************
+;;; ***********************************************
+;;; ***********************************************
+
+
+;;; note: we use boiler2 and thermometer from example 2 in example 3,
+;;; along with their def-output methods defined in example 2.
+;;;
+;;; also: these do not use ct-assert to perform automatic testing, but
+;;; they do illustrate a possible real-world application of synapses. to
+;;; observe the difference made by synapses, one must look at the trace output
+;
+; now let's look at synapses, which mediate a dependency between two cells.
+; the example here has an input argument (sensitivity-enabled) which when
+; enables gives the temp cell an (fsensitivity 0.05) clause.
+
+; the example simulates a thermometer perhaps
+; malfunctioning which is sending streams of values randomly plus or minus
+; two-hundredths of a degree. does not sound serious, except...
+;
+; if you run the example as is, when the temperature gets to our on/off threshhold
+; of 100, chances are you will see the boiler toggle itself on and off several times
+; before the temperature moves away from 100.
+;
+; building maintenance personel will report this odd behavior, probably hearing the
+; vent open and shut and open again several times in quick succession.
+
+; the problem is traced to the cell rule which reacts too slavishly to the stream
+; of temperature values. a work order is cut to replace the thermometer, and to reprogram
+; the controller not to be so slavish. there are lots of ways to solve this; here if
+; you enable sensitivity by running example 4 you can effectively place a synapse between the
+; temperature cell of the thermometer and the status cell of the boiler which
+; does not even trigger the status cell unless the received value differs by the
+; specified amount from the last value which was actually relayed.
+
+; now the boiler simply cuts off as the temperature passes 100, and stays off even if
+; the thermometer temperature goes to 99.98. the trace output shows that although the temperature
+; of the thermometer is changing, only occasionally does the rule to decide the boiler
+; status get kicked off.
+;
+
+
+
+(def-cell-test boiler-3 (&key (sensitivity-enabled t))
+ (declare (ignorable sensitivity-enabled))
+ (cells-reset)
+ #+soon
+ (let ((b (make-instance 'boiler2
+ :status (c? (let ((temp (if sensitivity-enabled
+ (temp (thermometer self) (f-sensitivity 0.05))
+ (temp (thermometer self)))))
+ ;;(trc "status c? sees temp" temp)
+ (if (< temp 100) :on :off)
+ ))
+ :vent (c? (ecase (^status) (:on :open) (:off :closed)))
+ :thermometer (make-instance 'quiet-thermometer :temp (c-in 20))
+ )))
+ ;
+ ; let's simulate a thermometer which, when the temperature is actually
+ ; any given value t will indicate randomly anything in the range
+ ; t plus/minus 0.02. no big deal unless the actual is exactly our
+ ; threshold point of 100...
+ ;
+ (dotimes (x 4)
+ ;;(trc "top> ----------- set base to" (+ 98 x))
+ (dotimes (y 10)
+ (let ((newtemp (+ 98 x (random 0.04) -.02))) ;; force random variation around (+ 98 x)
+ ;;(trc "top> ----------- set temp to" newtemp)
+ (setf (temp (thermometer b)) newtemp))))))
+
+
+(def-cell-test boiler-4 () (boiler-3 :sensitivity-enabled t))
+
+;;
+;; de-comment 'trc statements above to see what is happening
+;;
+#+(or)
+(boiler-3)
+
+#+(or)
+(boiler-4)
+
+(def-cell-test boiler-5 ()
+
+ (cells-reset)
+ #+soon
+ (let ((b (make-instance 'boiler2
+ :status (c-in :off)
+ :vent (c? (trc "caculating vent" (^status))
+ (if (eq (^status) :on)
+ (if (> (temp (thermometer self) (f-debug 3)) 100)
+ :open :closed)
+ :whatever-off))
+ :thermometer (make-instance 'quiet-thermometer
+ :temp (c-in 20)))))
+
+ (dotimes (x 4)
+ (dotimes (n 4)
+ (incf (temp (thermometer b))))
+ (setf (status b) (case (status b) (:on :off)(:off :on))))))
+
+#+(or)
+
+(boiler-5)
+
+(def-cell-test f-debug (sensitivity &optional subtypename)
+ (declare (ignore sensitivity subtypename))
+ #+soon
+ (mk-synapse (prior-fire-value)
+ :fire-p (lambda (syn new-value)
+ (declare (ignorable syn))
+ (eko ("fire-p decides" prior-fire-value sensitivity)
+ (delta-greater-or-equal
+ (delta-abs (delta-diff new-value prior-fire-value subtypename) subtypename)
+ (delta-abs sensitivity subtypename)
+ subtypename)))
+
+ :fire-value (lambda (syn new-value)
+ (declare (ignorable syn))
+ (eko ("f-sensitivity relays")
+ (setf prior-fire-value new-value)) ;; no modulation of value, but do record for next time
+ )))
Added: dependencies/trunk/cells/cells-test/build-sys.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/build-sys.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,56 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cl-user; -*-
+;;;
+;;; Copyright © 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(defpackage #:cells-build-package
+ (:use #:cl))
+
+(in-package #:cells-build-package)
+
+(defun build-sys (system$ &key source-directory force)
+ (let (
+ ;;; --------------------------------------
+ ;;; Step 2: Implementation-specific issues
+ ;;;
+ ;;; Let's assume this is fixed in CMUCL 19a, and fix it later if need be.
+ #+cmu18
+ (ext:*derive-function-types* nil)
+
+ #+lispworks
+ (hcl::*handle-existing-defpackage* (list :add))
+ )
+
+ ;;----------------------------------------
+ ;; source-directory validation...
+ ;;
+ (assert (pathnamep source-directory)
+ (source-directory)
+ "source-directory not supplied, please edit build.lisp to specify the location of the source.")
+ (let ((project-asd (merge-pathnames (format nil "~a.asd" system$)
+ source-directory)))
+ (unless (probe-file project-asd)
+ (error "~a not found. revise build.lisp if asd file is somewhere else." project-asd)))
+
+ ;;;----------------------------------
+ ;;; ok. build...
+ ;;;
+ (push source-directory asdf:*central-registry*)
+ (asdf:operate 'asdf:load-op (intern system$ :keyword) :force force)))
\ No newline at end of file
Added: dependencies/trunk/cells/cells-test/cells-test.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/cells-test.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,26 @@
+;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+
+(asdf:defsystem :cells-test
+ :name "cells-test"
+ :author "Kenny Tilton <ktilton at nyc.rr.com>"
+ :maintainer "Kenny Tilton <ktilton at nyc.rr.com>"
+ :licence "MIT Style"
+ :description "Cells Regression Test/Documentation"
+ :long-description "Informatively-commented regression tests for Cells"
+ :serial t
+ :depends-on (:cells)
+ :components ((:file "test")
+ (:file "hello-world")
+ (:file "test-kid-slotting")
+ (:file "test-lazy")
+ (:file "person")
+ (:file "df-interference")
+ (:file "test-family")
+ (:file "output-setf")
+ (:file "test-cycle")
+ (:file "test-ephemeral")
+ (:file "test-synapse")
+ (:file "deep-cells")))
+
+
+
Added: dependencies/trunk/cells/cells-test/cells-test.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/cells-test.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,104 @@
+;; -*- lisp-version: "8.1 [Windows] (Oct 11, 2008 17:00)"; cg: "1.103.2.10"; -*-
+
+(in-package :cg-user)
+
+(defpackage :CELLS)
+
+(define-project :name :cells-test
+ :modules (list (make-instance 'module :name "test.lisp")
+ (make-instance 'module :name "hello-world.lisp")
+ (make-instance 'module :name "test-kid-slotting.lisp")
+ (make-instance 'module :name "test-lazy.lisp")
+ (make-instance 'module :name "person.lisp")
+ (make-instance 'module :name "df-interference.lisp")
+ (make-instance 'module :name "test-family.lisp")
+ (make-instance 'module :name "output-setf.lisp")
+ (make-instance 'module :name "test-cycle.lisp")
+ (make-instance 'module :name "test-ephemeral.lisp")
+ (make-instance 'module :name "test-synapse.lisp")
+ (make-instance 'module :name "deep-cells.lisp")
+ (make-instance 'module :name "clos-training.lisp")
+ (make-instance 'module :name "do-req.lisp"))
+ :projects (list (make-instance 'project-module :name "..\\cells"
+ :show-modules nil))
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :cells
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules (list :cg-dde-utils :cg.base :cg.bitmap-pane
+ :cg.bitmap-pane.clipboard :cg.bitmap-stream
+ :cg.button :cg.caret :cg.check-box
+ :cg.choice-list :cg.choose-printer
+ :cg.clipboard :cg.clipboard-stack
+ :cg.clipboard.pixmap :cg.color-dialog
+ :cg.combo-box :cg.common-control :cg.comtab
+ :cg.cursor-pixmap :cg.curve :cg.dialog-item
+ :cg.directory-dialog :cg.directory-dialog-os
+ :cg.drag-and-drop :cg.drag-and-drop-image
+ :cg.drawable :cg.drawable.clipboard
+ :cg.dropping-outline :cg.edit-in-place
+ :cg.editable-text :cg.file-dialog
+ :cg.fill-texture :cg.find-string-dialog
+ :cg.font-dialog :cg.gesture-emulation
+ :cg.get-pixmap :cg.get-position
+ :cg.graphics-context :cg.grid-widget
+ :cg.grid-widget.drag-and-drop :cg.group-box
+ :cg.header-control :cg.hotspot :cg.html-dialog
+ :cg.html-widget :cg.icon :cg.icon-pixmap
+ :cg.ie :cg.item-list :cg.keyboard-shortcuts
+ :cg.lamp :cg.lettered-menu :cg.lisp-edit-pane
+ :cg.lisp-text :cg.lisp-widget :cg.list-view
+ :cg.mci :cg.menu :cg.menu.tooltip
+ :cg.message-dialog
+ :cg.multi-line-editable-text
+ :cg.multi-line-lisp-text
+ :cg.multi-picture-button
+ :cg.multi-picture-button.drag-and-drop
+ :cg.multi-picture-button.tooltip :cg.ocx
+ :cg.os-widget :cg.os-window :cg.outline
+ :cg.outline.drag-and-drop
+ :cg.outline.edit-in-place :cg.palette
+ :cg.paren-matching :cg.picture-widget
+ :cg.picture-widget.palette :cg.pixmap
+ :cg.pixmap-widget :cg.pixmap.file-io
+ :cg.pixmap.printing :cg.pixmap.rotate
+ :cg.printing :cg.progress-indicator
+ :cg.project-window :cg.property
+ :cg.radio-button :cg.rich-edit
+ :cg.rich-edit-pane
+ :cg.rich-edit-pane.clipboard
+ :cg.rich-edit-pane.printing
+ :cg.sample-file-menu :cg.scaling-stream
+ :cg.scroll-bar :cg.scroll-bar-mixin
+ :cg.selected-object :cg.shortcut-menu
+ :cg.static-text :cg.status-bar
+ :cg.string-dialog :cg.tab-control
+ :cg.template-string :cg.text-edit-pane
+ :cg.text-edit-pane.file-io
+ :cg.text-edit-pane.mark :cg.text-or-combo
+ :cg.text-widget :cg.timer :cg.toggling-widget
+ :cg.toolbar :cg.tooltip :cg.trackbar :cg.tray
+ :cg.up-down-control :cg.utility-dialog
+ :cg.web-browser :cg.web-browser.dde
+ :cg.wrap-string :cg.yes-no-list
+ :cg.yes-no-string :dde)
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags (list :top-level :debugger)
+ :build-flags (list :allow-runtime-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :include-manifest-file-for-visual-styles t
+ :default-command-line-arguments "+M +t \"Console for Debugging\""
+ :additional-build-lisp-image-arguments (list :read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :build-number 0
+ :on-initialization 'cells::test-cells
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/cells-test/deep-cells.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/deep-cells.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,53 @@
+(in-package :cells)
+
+(defvar *client-log*)
+(defvar *obs-1-count*)
+
+(defmodel deep ()
+ ((cell-2 :cell :ephemeral :initform (c-in 'two) :accessor cell-2)
+ (cell-1 :initform (c? (list 'one (^cell-2) (^cell-3))) :accessor cell-1)
+ (cell-3 :initform (c-in 'c3-unset) :accessor cell-3)))
+
+(defobserver cell-1 ()
+ (trc "cell-1 observer raw now enqueing client to run first. (new,old)=" new-value old-value)
+ (with-integrity (:client 1)
+ (trc "cell-1 :client now running" new-value (incf *obs-1-count*))
+ (eko ("c1-obs->*client-log*: ")
+ (setf *client-log* (list new-value)))))
+
+(defobserver cell-2 ()
+ (trc "cell-2 observer raw now enqueing change and client to run second. (new,old)=" new-value old-value)
+ (with-integrity (:change)
+ (trc "cell-2 observer :change now running" *client-log*)
+ (ct-assert (equal *client-log* '((one two c3-unset) two c3-unset)))
+ (setf (^cell-3) (case new-value (two 'three) (otherwise 'trouble))))
+ (with-integrity (:client 2)
+ (trc "client cell-2 :client running")
+ (eko ("c2-obs->*client-log*: ")
+ (setf *client-log* (append *client-log* (list new-value))))))
+
+(defobserver cell-3 ()
+ (trc "cell-3 observer raw now enqueing client to run third. (new,old)=" new-value old-value)
+ (with-integrity (:client 3)
+ (trc "cell-3 observer :client now running" new-value)
+ (eko ("c3-obs->*client-log*: ")
+ (setf *client-log* (append *client-log* (list new-value))))))
+
+(defun deep-queue-handler (client-q)
+ (loop for (defer-info . task) in (prog1
+ (sort (fifo-data client-q) '< :key 'car)
+ (fifo-clear client-q))
+ do
+ (trc nil "!!! --- deep-queue-handler dispatching" defer-info)
+ (funcall task :user-q defer-info)))
+
+(def-cell-test go-deep ()
+ (cells-reset 'deep-queue-handler)
+ (setf *obs-1-count* 0)
+ (make-instance 'deep)
+ (ct-assert (eql 2 *obs-1-count*)) ;; because the cell-2 observer does a setf on something used by c1
+ (trc "testing *client-log*" *client-log*)
+ (ct-assert (tree-equal *client-log* '((one nil three) three))))
+
+
+
Added: dependencies/trunk/cells/cells-test/df-interference.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/df-interference.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,120 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defvar *eex* 0)
+
+(defmodel xx3 ()
+ ((aa :initform (c-in 0) :initarg :aa :accessor aa)
+ (dd :initform (c? (min 0 (+ (^cc) (^bb)))) :initarg :dd :accessor dd)
+ (ddx :initform (c? (+ (^cc) (^bb))) :initarg :ddx :accessor ddx)
+ (cc :initform (c? (+ (^aa) (^bb))) :initarg :cc :reader cc)
+ (bb :initform (c? (* 2 (^aa))) :initarg :bb :accessor bb)
+ (ee :initform (c? (+ (^aa) (^dd))) :initarg :ee :reader ee)
+ (eex :initform (c? (trc "in rule of eex, *eex* now" *eex*)
+ (+ (^aa) (^ddx))) :initarg :eex :reader eex)
+ ))
+
+(defobserver aa ((self xx3))
+ (trc nil "output aa:" new-value))
+
+(defobserver bb ((self xx3))
+ (trc nil "output bb:" new-value))
+
+(defobserver cc ((self xx3))
+ (trc nil "output cc:" new-value))
+
+(defobserver dd ((self xx3))
+ (trc nil "output dd:" new-value))
+
+(defobserver ee ((self xx3))
+ (trc nil "output ee:" new-value))
+
+(defobserver eex ((self xx3))
+ (incf *eex*)
+ (trc "output eex:" new-value *eex*))
+
+;;
+;; here we look at just one problem, what i call dataflow interference. consider
+;; a dependency graph underlying:
+;;
+;; - a depends on b and c, and...
+;; - b depends on c
+;;
+;; if c changes, depending on the accident of the order in which a and b happened to
+;; be first evaluated, a might appear before b on c's list of dependents (callers). then the
+;; following happens:
+;;
+;; - c triggers a
+;; - a calculates off the new value of c and an obsolete cached value for b
+;; - a outputs an invalid value and triggers any dependents, all of whom recalculate
+;; using a's invalid value
+;; - c triggers b
+;; - b recalculates and then triggers a, which then recalculates correctly and outputs and triggers
+;; the rest of the df graph back into line
+;;
+;; the really bad news is that outputs go outside the model: what if the invalid output caused
+;; a missile launch? sure, a subsequent correct calculation comes along shortly, but
+;; irrevocable damage may have been done.
+;;
+
+(def-cell-test df-test ()
+ (cells-reset)
+ (let* ((*eex* 0)
+ (it (make-instance 'xx3)))
+ (trc "eex =" *eex*)
+ (ct-assert (eql *eex* 1))
+ ;;(inspect it);;(cellbrk)
+ (ct-assert (and (eql (aa it) 0)(eql (bb it) 0)(eql (cc it) 0)))
+ (ct-assert (and (eql (dd it) 0)(eql (ddx it) 0)(eql (ee it) 0)(eql (eex it) 0)))
+
+ ;;;- interference handling
+ ;;;
+ (let ((*eex* 0))
+ (trc "--------- 1 => (aa it) --------------------------")
+ (setf (aa it) 1)
+
+ (ct-assert (and (eql (aa it) 1)(eql (bb it) 2)(eql (cc it) 3)))
+ (trc "dd,ddx:" (dd it) (ddx it) )
+ (ct-assert (and (eql (dd it) 0)(eql (ddx it) 5)))
+ (ct-assert (and (eql (ee it) 1)(eql (eex it) 6)))
+ (ct-assert (eql *eex* 1)))
+
+ (let ((*eex* 0))
+ (trc "--------- 2 => (aa it) --------------------------")
+ (setf (aa it) 2)
+ (ct-assert (and (eql (aa it) 2)(eql (bb it) 4)(eql (cc it) 6)
+ (eql (dd it) 0)(eql (ddx it) 10)(eql (ee it) 2)(eql (eex it) 12)))
+ (ct-assert (eql *eex* 1)))
+
+ (dolist (c (cells it))
+ (trc "cell is" c)
+ (when (typep (cdr c) 'cell)
+ (print `(notifier ,c))
+ (dolist (u (c-callers (cdr c)))
+ (print `(___ ,u)))))
+ ))
+
+
Added: dependencies/trunk/cells/cells-test/echo-setf.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/echo-setf.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,47 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defmodel bing (model)
+ ((bang :initform (c-in nil) :accessor bang)))
+
+(def-c-output bang ()
+ (bwhen (p .parent)
+ (setf (bang p) new-value)))
+
+(defmodel bings (bing family)
+ ()
+ (:default-initargs
+ :kids (c? (loop repeat 2
+ collect (make-instance 'bing)))))
+
+(defun cv-echo-setf ()
+ (cell-reset)
+ (let ((top (make-instance 'bings
+ :kids (c-in nil))))
+ (push (make-instance 'bings) (kids top))))
+
+#+(or)
+(cv-echo-setf)
Added: dependencies/trunk/cells/cells-test/hello-world-q.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/hello-world-q.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,81 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+;;;
+;;;(defstrudel computer
+;;; (happen :cell :ephemeral :initform (c-in nil))
+;;; (location :cell t
+;;; :initform (c? (case (^happen)
+;;; (:leave :away)
+;;; (:arrive :at-home)
+;;; (t (c-value c))))
+;;; :accessor location)
+;;; (response :cell :ephemeral :initform nil :initarg :response :accessor response)))
+
+(def-c-output response((self computer) new-response old-response)
+ (when new-response
+ (format t "~&computer: ~a" new-response)))
+
+(def-c-output happen((self computer))
+ (when new-value
+ (format t "~&happen: ~a" new-value)))
+
+(defun hello-world-q ()
+ (let ((dell (make-instance 'computer
+ :response (c? (bwhen (h (happen self))
+ (if (eql (^location) :at-home)
+ (case h
+ (:knock-knock "who's there?")
+ (:world "hello, world."))
+ "<silence>"))))))
+ (dotimes (n 2)
+ (setf (happen dell) :knock-knock))
+ (setf (happen dell) :arrive)
+ (setf (happen dell) :knock-knock)
+ (setf (happen dell) :world)
+ (values)))
+
+#+(or)
+(hello-world)
+
+#+(or)
+(traceo sm-echo)
+
+
+#| output
+
+happen: knock-knock
+computer: <silence>
+happen: knock-knock
+computer: <silence>
+happen: arrive
+happen: knock-knock
+computer: who's there?
+happen: world
+computer: hello, world.
+
+|#
+
Added: dependencies/trunk/cells/cells-test/hello-world.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/hello-world.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,78 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+
+(defmd computer ()
+ (happen (c-in nil) :cell :ephemeral)
+ (location (c? (case (^happen)
+ (:leave :away)
+ (:arrive :at-home)
+ (t .cache)))) ;; ie, unchanged
+ (response nil :cell :ephemeral))
+
+(defobserver response(self new-response old-response)
+ (when new-response
+ (format t "~&computer: ~a" new-response)))
+
+(defobserver happen()
+ (when new-value
+ (format t "~&happen: ~a" new-value)))
+
+(def-cell-test hello-world ()
+ (let ((dell (make-instance 'computer
+ :response (c? (bwhen (h (happen self))
+ (if (eql (^location) :at-home)
+ (case h
+ (:knock-knock "who's there?")
+ (:world "hello, world."))
+ "<silence>"))))))
+ (dotimes (n 2)
+ (setf (happen dell) :knock-knock))
+
+ (setf (happen dell) :arrive)
+ (setf (happen dell) :knock-knock)
+ (setf (happen dell) :leave)
+ (values)))
+
+#+(or)
+(hello-world)
+
+
+#| output
+
+happen: KNOCK-KNOCK
+computer: <silence>
+happen: KNOCK-KNOCK
+computer: <silence>
+happen: ARRIVE
+happen: KNOCK-KNOCK
+computer: who's there?
+happen: LEAVE
+computer: <silence>
+
+
+|#
+
Added: dependencies/trunk/cells/cells-test/internal-combustion.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/internal-combustion.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,362 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+
+(in-package :cells)
+
+(defmodel engine ()
+ ((fuel :cell nil :initarg :fuel :initform nil :accessor fuel)
+ (cylinders :initarg :cylinders :initform (c-in 4) :accessor cylinders)
+ (valves-per-cylinder :initarg :valves-per-cylinder :initform 2 :accessor valves-per-cylinder)
+ (valves :initarg :valves
+ :accessor valves
+ :initform (c? (* (valves-per-cylinder self)
+ (cylinders self))))
+ (mod3 :initarg :mod3 :initform nil :accessor mod3)
+ (mod3ek :initarg :mod3ek :initform nil :accessor mod3ek)
+ ))
+
+(defmethod c-unchanged-test ((self engine) (slotname (eql 'mod3)))
+ (lambda (new-value old-value)
+ (flet ((test (it) (zerop (mod it 3))))
+ (eql (test new-value) (test old-value)))))
+
+(defobserver mod3ek () (trc "mod3ek output" self))
+
+(defmethod c-unchanged-test ((self engine) (slotname (eql 'mod3ek)))
+ (lambda (new-value old-value)
+ (flet ((test (it) (zerop (mod it 3))))
+ (eql (test new-value) (test old-value)))))
+
+(defobserver cylinders ()
+ ;;(when *dbg* (break))
+ (trc "cylinders output" self old-value new-value))
+
+(defvar *propagations* nil)
+
+(defmodel engine-w-initform ()
+ ((cylinders :initform 33 :reader cylinders)))
+
+(defclass non-model ()())
+(defmodel faux-model (non-model)())
+(defmodel true-model ()())
+(defmodel indirect-model (true-model)())
+
+
+(def-cell-test cv-test-engine ()
+ (when *stop* (break "stopped! 2"))
+ ;;
+ ;; before we get to engines, a quick check that we are correctly enforcing the
+ ;; requirment that classes defined by defmodel inherit from model-object
+ ;;
+ (ct-assert (make-instance 'non-model))
+ (ct-assert (make-instance 'true-model))
+ (ct-assert (make-instance 'indirect-model))
+ (ct-assert (handler-case
+ (progn
+ (make-instance 'faux-model)
+ nil) ;; bad to reach here
+ (t (error) (trc "error is" error)
+ error)))
+ ;; --------------------------------------------------------------------------
+ ;; -- make sure non-cell slots still work --
+ ;;
+ ;; in mop-based implementations we specialize the slot-value-using-class accessors
+ ;; to make cells work. rather than slow down all slots where a class might have only
+ ;; a few cell-mediated slots, we allow a class to pick and choose which slots are cell-mediated.
+ ;;
+ ;; here we make sure all is well in re such mixing of cell and non-cell, by exercising first
+ ;; the reader and then the writer.
+ ;;
+ ;; the read is not much of a test since it should work even if through some error the slot
+ ;; gets treated as if it were cell. but the setf will fail since cell internals reject changes
+ ;; to cellular slots unless they are c-variable. (why this is so has to do with efficiency,
+ ;; and will be covered when we get to cells being optimized away.)
+ ;;
+ (ct-assert
+ (eql :gas (fuel (make-instance 'engine :fuel :gas))))
+ (ct-assert
+ (eql :diesel (setf (fuel (make-instance 'engine :fuel :gas)) :diesel)))
+ ;;
+ ;;
+ #+(or) ;; not an error: Cloucell needed to hold a Cell in a non cellular slot. duh.
+ (ct-assert
+ (handler-case
+ (progn
+ (make-instance 'engine :fuel (c-in :gas))
+ nil) ;; bad to reach here
+ (t (error) (trc "error is" error)
+ error)))
+ ;;
+ ;; ---------------------------------------------------------------------------
+ ;; (1) reading cellular slots (2) instantiated as constant, variable or ruled
+ ;;
+ ;; aside from the simple mechanics of successfuly accessing cellular slots, this
+ ;; code exercises the implementation task of binding a cell to a slot such that
+ ;; a standard read op finds the wrapped value, including a functional value (the c?)
+ ;;
+ ;; aside; the cell pattern includes a transparency requirement so cells will be
+ ;; programmer-friendly and in turn yield greater productivity gains. below we /initialize/
+ ;; the cylinders cell to (c-in 4) and then (c? (+ 2 2)), but when you read those slots the
+ ;; cell implementation structures are not returned, the value 4 is returned.
+ ;;
+ ;; aside: the value 4 itself occupies the actual slot. this helped when we used Cells
+ ;; with a persistent CLOS tool which maintained inverse indices off slots if asked.
+ ;;
+ (ct-assert
+ (progn
+ (eql 33 (cylinders (make-instance 'engine-w-initform)))))
+
+ (ct-assert
+ (eql 4 (cylinders (make-instance 'engine :cylinders 4))))
+
+ (ct-assert
+ (eql 4 (cylinders (make-instance 'engine :cylinders (c-in 4)))))
+
+ (ct-assert
+ (eql 4 (cylinders (make-instance 'engine :cylinders (c? (+ 2 2))))))
+
+ (ct-assert
+ (eql 16 (valves (make-instance 'engine
+ :cylinders 8
+ :valves (c? (* (cylinders self) (valves-per-cylinder self)))
+ :valves-per-cylinder (c? (floor (cylinders self) 4)))))) ;; admittedly weird semantics
+
+ ;; ----------------------------------------------------------
+ ;; initialization output
+ ;;
+ ;; cells are viewed in part as supportive of modelling. the output functions provide
+ ;; a callback allowing state changes to be manifested outside the dataflow, perhaps
+ ;; by updating the screen or by operating some real-world device through its api.
+ ;; that way a valve model instance could drive a real-world valve.
+ ;;
+ ;; it seems best then that the state of model and modelled should as much as possible
+ ;; be kept consistent with each other, and this is why we "output" cells as soon as they
+ ;; come to life as well as when they change.
+ ;;
+ ;; one oddball exception is that cellular slots for which no output is defined do not get outputted
+ ;; initially. why not? this gets a little complicated.
+ ;;
+ ;; first of all, outputting requires evaluation of a ruled cell. by checking first
+ ;; if a cell even is outputted, and punting on those that are not outputted we can defer
+ ;; the evaluation of any ruled cell bound to an unoutputted slot until such a slot is
+ ;; read by other code. i call this oddball because it is a rare slot that is
+ ;; neither outputted nor used directly or indirectly by an outputted slot. but i have had fairly
+ ;; expensive rules on debugging slots which i did not want kicked off until i had
+ ;; to check their values in the inspector. ie, oddball.
+ ;;
+
+ (macrolet ((output-init (newv cylini)
+ `(progn
+ (output-clear 'cylinders)
+ (output-clear 'valves)
+ (trc "starting output init test" ,newv ',cylini)
+ (make-instance 'engine
+ :cylinders ,cylini
+ :valves ,cylini)
+ (ct-assert (outputted 'cylinders))
+ (ct-assert (eql ,newv (output-new 'cylinders)))
+ ;(ct-assert (not (output-old-boundp 'cylinders)))
+ ;(ct-assert (not (outputted 'valves)))
+ )))
+ (output-init 6 6)
+ (output-init 10 (c-in 10))
+ (output-init 5 (c? (+ 2 3)))
+ )
+
+ ;; ----------------------------------------------------------------
+ ;; write cell slot
+ ;;
+ ;; for now only variable cells (slots mediated by c-variable structures) can be
+ ;; modified via setf. an exception (drifter cells) may get resurrected soon. but as mentioned
+ ;; above, an optimization discussed below requires rejection of changes to cellular slots
+ ;; instantiated without any cell, and for purity the cell engine rejects setf's of slots mediated
+ ;; by ruled cells. the idea being that we want the semantics of a ruled
+ ;; cell to be fully defined by its rule, not arbitrary setf's from anywhere in the code.
+ ;;
+ ;; aside: variable cells can be setf'ed from anywhere, a seeming loss of semantic
+ ;; control by the above purist view. but variables exist mainly to allow inputs to a dataflow model
+ ;; from outside the model, usually in an event-loop processing os events, so spaghetti dataflow
+ ;; should not follow from this.
+ ;;
+ ;; that said, in weak moments i resort to having the output of one cell setf some other variable cell,
+ ;; but i always think of these as regrettable gotos and maybe someday i will try to polish them out
+ ;; of existence test.
+ ;;
+ ;;-------------------------
+ ;;
+ ;; first verify acceptable setf...
+ ;;
+ (ct-assert
+ (let ((e (make-instance 'engine :cylinders (c-in 4))))
+ (setf (cylinders e) 6)
+ (eql 6 (cylinders e))))
+ ;;
+ ;; ...and two not acceptable...
+ ;;
+ (ct-assert
+ (handler-case
+ (let ((e (make-instance 'engine :cylinders 4)))
+ (setf (cylinders e) 6)
+ nil) ;; bad to reach here
+ (t (error)
+ (trc "error correctly is" error)
+ (cells-reset)
+ t))) ;; something non-nil to satisfy assert
+
+ (let ((e (make-instance 'engine :cylinders (c? (+ 2 2)))))
+ (assert *c-debug*)
+ (ct-assert
+ (handler-case
+ (progn
+ (setf (cylinders e) 6)
+ nil) ;; bad to reach here
+ (t (error) (trc "error correctly is" error)
+ (setf *stop* nil)
+ t))))
+ (when *stop* (break "stopped! 1"))
+ (cv-test-propagation-on-slot-write)
+ (cv-test-no-prop-unchanged)
+
+ ;;
+ ;; here we exercise a feature which allows the client programmer to override the default
+ ;; test of eql when comparing old and new values. above we defined nonsense slot mod3 (unoutputted)
+ ;; and mod3ek (outputted) with a custom "unchanged" test:
+ ;;
+
+ ;;
+ #+(or) (let ((e (make-instance 'engine
+ :mod3 (c-in 3)
+ :mod3ek (c-in 3)
+ :cylinders (c? (* 4 (mod3 self))))))
+
+ (ct-assert (eql 12 (cylinders e)))
+ (output-clear 'mod3)
+ (output-clear 'mod3ek)
+ (trc "mod3 outputes cleared, setting mod3s now")
+ (setf (mod3 e) 6
+ (mod3ek e) 6)
+ ;;
+ ;; both 3 and 6 are multiples of 3, so the engine guided by the above
+ ;; override treats the cell as unchanged; no output, no recalculation
+ ;; of the cylinders cell
+ ;;
+ (ct-assert (not (outputted 'mod3ek))) ;; no real need to check mod3 unoutputted
+ (ct-assert (eql 12 (cylinders e)))
+ ;;
+ ;; now test in the other direction to make sure change according to the
+ ;; override still works.
+ ;;
+ (setf (mod3 e) 5
+ (mod3ek e) 5)
+ (ct-assert (outputted 'mod3ek))
+ (ct-assert (eql 20 (cylinders e)))
+ )
+ )
+
+(def-cell-test cv-test-propagation-on-slot-write ()
+ ;; ---------------------------------------------------------------
+ ;; propagation (output and trigger dependents) on slot write
+ ;;
+ ;; propagation involves both outputing my change and notifying cells dependent on me
+ ;; that i have changed and that they need to recalculate themselves.
+ ;;
+ ;; the standard output callback is passed the slot-name, instance, new value,
+ ;; old value and a flag 'old-value-boundp indicating, well, whether the new value
+ ;; was the first ever for this instance.
+ ;;
+ ;; the first set of tests make sure actual change is handled correctly
+ ;;
+ (output-clear 'cylinders)
+ (output-clear 'valves)
+ (output-clear 'valves-per-cylinder)
+ (when *stop* (break "stopped!"))
+ (let ((e (make-instance 'engine
+ :cylinders 4
+ :valves-per-cylinder (c-in 2)
+ :valves (c? (* (valves-per-cylinder self) (cylinders self))))))
+ ;;
+ ;; these first tests check that cells get outputted appropriately at make-instance time (the change
+ ;; is from not existing to existing)
+ ;;
+ (ct-assert (and (eql 4 (output-new 'cylinders))
+ (not (output-old-boundp 'cylinders))))
+
+ (ct-assert (valves-per-cylinder e)) ;; but no output is defined for this slot
+
+ (ct-assert (valves e))
+ ;;
+ ;; now we test true change from one value to another
+ ;;
+ (setf (valves-per-cylinder e) 4)
+ ;;
+ (ct-assert (eql 16 (valves e)))
+ ))
+
+(def-cell-test cv-test-no-prop-unchanged ()
+ ;;
+ ;; next we check the engines ability to handle dataflow efficiently by /not/ reacting
+ ;; to coded setfs which in fact produce no change.
+ ;;
+ ;; the first takes a variable cylinders cell initiated to 4 and again setf's it to 4. we
+ ;; confirm that the cell does not output and that a cell dependent on it does not get
+ ;; triggered to recalculate. ie, the dependency's value has not changed so the dependent
+ ;; cell's cached value remains valid.
+ ;;
+ (cells-reset)
+ (output-clear 'cylinders)
+ (let* ((*dbg* t)
+ valves-fired
+ (e (make-instance 'engine
+ :cylinders (c-in 4)
+ :valves-per-cylinder 2
+ :valves (c-formula (:lazy t)
+ (setf valves-fired t)
+ (trc "!!!!!! valves")
+ (* (valves-per-cylinder self) (cylinders self))))))
+ (trc "!!!!!!!!hunbh?")
+ (ct-assert (outputted 'cylinders))
+ (output-clear 'cylinders)
+ (ct-assert (not valves-fired)) ;; no output is defined so evaluation is deferred
+ (trc "sampling valves....")
+ (let ()
+ (ct-assert (valves e)) ;; wake up unoutputted cell
+ )
+ (ct-assert valves-fired)
+ (setf valves-fired nil)
+
+ (ct-assert (and 1 (not (outputted 'cylinders))))
+ (setf (cylinders e) 4) ;; same value
+ (trc "same cyl")
+ (ct-assert (and 2 (not (outputted 'cylinders))))
+ (ct-assert (not valves-fired))
+
+ (setf (cylinders e) 6)
+ (ct-assert (outputted 'cylinders))
+ (ct-assert (not valves-fired))
+ (ct-assert (valves e))(ct-assert valves-fired)))
+
+#+(or)
+
+(cv-test-engine)
Added: dependencies/trunk/cells/cells-test/lazy-propagation.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/lazy-propagation.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,82 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defvar *area*)
+(defvar *density*)
+
+(defmodel cirkl ()
+ ((radius :initform (c-in 10) :initarg :radius :accessor radius)
+ (area :initform (c?_ (incf *area*) (trc "in area rule it is now" *area*)
+ (* pi (^radius) (^radius))) :initarg :area :accessor area)
+ (density :initform (c?_ (incf *density*)
+ (/ 1000 (^area))) :initarg :density :accessor density)))
+
+
+#+(or)
+(cv-laziness)
+
+(def-cell-test cv-laziness ()
+ (macrolet ((chk (area density)
+ `(progn
+ (assert (= ,area *area*) () "area is ~a, should be ~a" *area* ,area)
+ (assert (= ,density *density*) () "density is ~a, should be ~a" *density* ,density)
+ (trc nil "cv-laziness ok with:" ,area ,density)))
+ )
+ (let ((*c-debug* t))
+ (cells-reset)
+
+ (let* ((*area* 0)
+ (*density* 0)
+ (it (make-instance 'cirkl)))
+ (chk 0 0)
+
+ (print `(area is ,(area it)))
+ (chk 1 0)
+
+ (setf (radius it) 1)
+ (chk 1 0)
+
+ (print `(area is now ,(area it)))
+ (chk 2 0)
+ (assert (= (area it) pi))
+
+ (setf (radius it) 2)
+ (print `(density is ,(density it)))
+ (chk 3 1)
+
+ (setf (radius it) 3)
+ (chk 3 1)
+ (print `(area is ,(area it)))
+ (chk 4 1)
+ it))))
+
+#+(or)
+(cv-laziness)
+
+(defobserver area ()
+ (trc "area is" new-value :was old-value))
+
+
Added: dependencies/trunk/cells/cells-test/output-setf.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/output-setf.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,59 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defmodel bing (model)
+ ((bang :initform (c-in nil) :accessor bang)))
+
+(defobserver bang ()
+ (trc "new bang" new-value self)
+ (bwhen (p .parent)
+ (with-integrity (:change)
+ (setf (bang p) new-value)))
+ #+(or) (dolist (k (^kids))
+ (setf (bang k) (if (numberp new-value)
+ (1+ new-value)
+ 0))))
+
+(defmodel bings (bing family)
+ ()
+ (:default-initargs
+ :kids (c? (loop repeat 2
+ collect (make-instance 'bing
+ :fm-parent self
+ :md-name (copy-symbol 'kid))))))
+
+(def-cell-test cv-output-setf ()
+ (cells-reset)
+ (let ((top (make-instance 'bings
+ :md-name 'top
+ :kids (c-in nil))))
+ (push (make-instance 'bings
+ :fm-parent top) (kids top))
+ (dolist (k (kids (car (kids top))))
+ (setf (bang k) (kid-no k)))))
+
+#+(or)
+(cv-output-setf)
Added: dependencies/trunk/cells/cells-test/person.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/person.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,324 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defvar *name-ct-calc* 0)
+
+(defmodel person ()
+ ((speech :cell :ephemeral :initform (c-in nil) :initarg :speech :accessor speech)
+ (thought :cell :ephemeral :initform (c? (speech self)) :initarg :thought :accessor thought)
+ (names :initform nil :initarg :names :accessor names)
+ (pulse :initform nil :initarg :pulse :accessor pulse)
+ (name-ct :initarg :name-ct :accessor name-ct
+ :initform (c? "name-ct"
+ (incf *name-ct-calc*)
+ (length (names self))))))
+
+#+test
+(progn
+ (cells-reset)
+ (inspect
+ (make-instance 'person
+ :names '("speedy" "chill")
+ :pulse (c-in 60)
+ :speech (c? (car (names self)))
+ :thought (c? (when (< (pulse self) 100) (speech self))))))
+
+(defobserver names ((self person) new-names)
+ (format t "~&you can call me ~a" new-names))
+
+(defmethod c-unchanged-test ((self person) (slotname (eql 'names)))
+ 'equal)
+
+(defvar *thought* "failed")
+(defvar *output-speech* "failed")
+
+(defobserver thought ((self person) new-value)
+ (when new-value
+ (trc "output thought" self new-value)
+ (setq *thought* new-value)
+ (trc "i am thinking" new-value)))
+
+(defobserver speech ()
+ (setf *output-speech* new-value))
+
+(defmodel sick ()
+ ((e-value :cell :ephemeral :initarg :e-value :accessor e-value)
+ (s-value :initarg :s-value :reader s-value)))
+
+(defobserver s-value ()
+ :test)
+
+(defobserver e-value ()
+ :test)
+
+(def-cell-test cv-test-person ()
+ (cv-test-person-1)
+ (cv-test-person-3)
+ (cv-test-person-4)
+ (cv-test-person-5)
+ ;; (cv-test-talker)
+ )
+
+(def-cell-test cv-test-person-1 ()
+ ;;
+ ;; a recent exchange with someone who has developed with others a visual
+ ;; programming system was interesting. i mentioned my dataflow thing, he mentioned
+ ;; they liked the event flow model. i responded that events posed a problem for
+ ;; cells. consider something like:
+ ;;
+ ;; (make-instance 'button
+ ;; :clicked (c-in nil)
+ ;; :action (c? (when (clicked self) (if (- (time-now *cg-system*) (last-click-time.....
+ ;;
+ ;; well, once the button is clicked, that cell has the value t. the rest of the rule executes
+ ;; and does whatever, the rule completes. finis? no. the time-now cell of
+ ;; the system instance continues to tick-tick-tick. at each tick the action cell gets triggered,
+ ;; and (here is the problem) the clicked cell still says t.
+ ;;
+ ;; the problem is that clicked is event-ish. the semantics are not "has it ever been clicked",
+ ;; they are more like "when the /passing/ click occurs...". we could try requiring the programmer
+ ;; always to execute:
+ ;;
+ ;; (setf (clicked it) t)
+ ;; (setf (clicked it nil)
+ ;;
+ ;; ...but in fact cells like this often are ruled cells which watch mouse actions and check if the
+ ;; mouse up was in the control where the mousedown occurred. so where to put a line of code
+ ;; to change clicked back to nil? a deep fix seemed appropriate: teach cells about events, so...
+ ;;
+ ;; cellular slots can be defined to be :ephemeral if the slot will be used for
+ ;; event-like data. [defining slots and not cells as ephemeral means one cannot arrange for such a
+ ;; slot to have a non-ephemeral value for one instance and ephemeral values for other instances. we
+ ;; easily could go the other way on this, but this seems right.]
+ ;;
+ ;; the way ephemerals work is this: when a new value arrives in an ephemeral slot it is outputted and
+ ;; propagated to dependent cells normally, but then internally the slot value is cleared to nil.
+ ;; thus during the output and any dataflow direct or indirect the value is visible to other code, but
+ ;; no longer than that. note that setting the slot back to nil bypasses propagation: no output, no
+ ;; triggering of slot dependents.
+ ;;
+ ;;
+ (let ((p (make-instance 'person :speech (c-in nil))))
+ ;;
+ ;; - ephemeral c-variable cells revert to nil if setf'ed non-nil later
+ ;;
+ (setf (speech p) "thanks for all the fish")
+ (ct-assert (null (speech p)))
+ (ct-assert (equal *output-speech* "thanks for all the fish"))
+ (ct-assert (equal *thought* "thanks for all the fish")) ;; thought is ephemeral as well, so tricky test
+ ;;
+ ;; now check the /ruled/ ephemeral got reset to nil
+ ;;
+ (ct-assert (null (thought p)))))
+
+
+
+(def-cell-test cv-test-person-3 ()
+ ;; -------------------------------------------------------
+ ;; dynamic dependency graph maintenance
+ ;;
+ ;; dependencies of a cell are those other cells actually accessed during the latest
+ ;; invocation of the rule. note that a cellular slot may be constant, not mediated by a
+ ;; cell, in which case the access does not record a dependency.
+ ;;
+ (let ((p (make-instance 'person
+ :names (c-in '("speedy" "chill"))
+ :pulse (c-in 60)
+ :speech "nice and easy does it"
+ :thought (c? (if (> (pulse self) 180)
+ (concatenate 'string (car (names self)) ", slow down!")
+ (speech self))))))
+ ;;
+ ;; with the (variable=1) pulse not > 80, the branch taken leads to (constant=0) speech, so:
+ ;;
+ (ct-assert (eql 1 (length (cd-useds (md-slot-cell p 'thought)))))
+ ;;
+ ;; with the (variable=1) pulse > 80, the branch taken leads to (variable=1) names, so:
+ ;;
+ (setf (pulse p) 200)
+ (ct-assert (eql 2 (length (cd-useds (md-slot-cell p 'thought)))))
+ ;;
+ ;; let's check the engine's ability reliably to drop dependencies by lowering the pulse again
+ ;;
+ (setf (pulse p) 50)
+ (ct-assert (eql 1 (length (cd-useds (md-slot-cell p 'thought)))))))
+
+
+(def-cell-test cv-test-person-4 ()
+ (let ((p (make-instance 'person
+ :names '("speedy" "chill")
+ :pulse (c-in 60)
+ :speech (c? (car (names self)))
+ :thought (c? (when (< (pulse self) 100) (speech self))))))
+ ;;
+ ;; now let's see if cells are correctly optimized away when:
+ ;;
+ ;; - they are defined and
+ ;; - all cells accessed are constant.
+ ;;
+ (ct-assert (null (md-slot-cell p 'speech)))
+ #-its-alive!
+ (progn
+ (ct-assert (assoc 'speech (cells-flushed p)))
+ (ct-assert (c-optimized-away-p (cdr (assoc 'speech (cells-flushed p))))))
+
+ (ct-assert (not (c-optimized-away-p (md-slot-cell p 'thought)))) ;; pulse is variable, so cannot opti
+ (ct-assert (eql 1 (length (cd-useds (md-slot-cell p 'thought))))) ;; but speech is opti, so only 1 used
+ ))
+
+(def-cell-test cv-test-person-5 ()
+ ;;
+ ;; for now cells do not allow cyclic dependency, where a computation of a cell leads back
+ ;; to itself. we could do something like have the self-reference return the cached value
+ ;; or (for the first evaluation) a required seed value. we already have logic which says
+ ;; that, if setf on a variable cell cycles back to setf on the same cell we simply stop, so
+ ;; there is no harm on the propagation side. but so far no need for such a thing.
+ ;;
+ ;; one interesting experiment would be to change things so propagation looping back on itself
+ ;; would be allowed. we would likewise change things so propagation was breadth first. then
+ ;; state change, once set in motion, would continue indefinitely. (propagation would also have to
+ ;; be non-recursive.) we would want to check for os events after each propagation and where
+ ;; real-time synchronization was necessary do some extra work. this in contrast to having a timer
+ ;; or os null events artificially move forward the state of, say, a simulation of a physical system.
+ ;; allowing propagation to loop back on itslef means the system would simply run, and might make
+ ;; parallelization feasible since we already have logic to serialize where semantically necessary.
+ ;; anyway, a prospect for future investigation.
+ ;;
+ ;; make sure cyclic dependencies are trapped:
+ ;;
+ (cells-reset)
+ #+its-alive! t
+ #-its-alive!
+ (ct-assert
+ (handler-case
+ (progn
+ (pulse (make-instance 'person
+ :names (c? (trc "calculating names" self)
+ (maptimes (n (pulse self))))
+ :pulse (c? (trc "calculating pulse" self)
+ (length (names self)))))
+ nil)
+ (t (error)
+ (describe error)
+ (setf *stop* nil)
+ t))))
+;;
+;; we'll toss off a quick class to test tolerance of cyclic
+
+(defmodel talker8 ()
+ ((words8 :initform (c-input (:cyclicp t) "hello, world")
+ :initarg :words8 :accessor words8)
+ (idea8 :initform (c-in "new friend!") :initarg :idea8 :accessor idea8)
+ (mood8 :initform (c-in "happy as clam") :initarg :mood8 :accessor mood8)))
+
+(defmodel talker ()
+ ((words :initform (c-in "hello, world") :initarg :words :accessor words)
+ (idea :initform (c-in "new friend!") :initarg :idea :accessor idea)
+ ))
+
+(defobserver words ((self talker) new-words)
+ (trc "new words" new-words)
+ (setf (idea self) (concatenate 'string "idea " new-words)))
+
+(defmethod c-unchanged-test ((self talker) (slotname (eql 'words)))
+ 'string-equal)
+
+(defobserver idea ((self talker) new-idea)
+ (trc "new idea" new-idea)
+ (setf (words self) (concatenate 'string "say " new-idea)))
+
+(defmethod c-unchanged-test ((self talker) (slotname (eql 'idea)))
+ 'string-equal)
+
+(defobserver words8 ((self talker8) new-words8)
+ (trc "new words8, sets idea8 to same" new-words8 *causation*)
+ (with-integrity (:change)
+ (setf (idea8 self) (concatenate 'string "+" new-words8))))
+
+(defmethod c-unchanged-test ((self talker8) (slotname (eql 'words8)))
+ 'string-equal)
+
+(defobserver idea8 ((self talker8) new-idea8)
+ (trc "new idea8, sets mood8 to same" new-idea8 *causation*)
+ (with-integrity (:change)
+ (setf (mood8 self) (concatenate 'string "+" new-idea8))))
+
+(defmethod c-unchanged-test ((self talker8) (slotname (eql 'idea8)))
+ 'string-equal)
+
+(defobserver mood8 ((self talker8) new-mood8)
+ (trc "new mood8, sets words8 to same:" new-mood8 *causation*)
+ (with-integrity (:change)
+ (setf (words8 self) (concatenate 'string "+" new-mood8))))
+
+(defmethod c-unchanged-test ((self talker8) (slotname (eql 'mood8)))
+ 'string-equal)
+
+(defmacro ct-assert-error (&body body)
+ `(ct-assert
+ (handler-case
+ (prog1 nil
+ , at body)
+ (t (error)
+ (trc "ct-assert-error" error)
+ (setf *stop* nil)
+ t))))
+
+#+(or) ; FIXME: this test is borked
+(def-cell-test cv-test-talker ()
+ ;;
+ ;; make sure cyclic setf is trapped
+ ;;
+ (cells-reset)
+
+ ;;; (trc "start unguarded cyclic")
+ ;;;
+ ;;; (let ((tk (make-instance 'talker)))
+ ;;; (setf (idea tk) "yes")
+ ;;; (string-equal "yes" (words tk))
+ ;;; (setf (words tk) "no")
+ ;;; (string-equal "no" (idea tk)))
+
+ (trc "start guarded cyclic")
+
+ #+(or) (ct-assert-error
+ (let ((tk (make-instance 'talker)))
+ (setf (idea tk) "yes")
+ (ct-assert (string-equal "yes" (words tk)))
+ (setf (words tk) "no")
+ (ct-assert (string-equal "no" (idea tk)))))
+ ;;
+ ;; make sure cells declared to be cyclic are allowed
+ ;; and halt (because after the first cyclic setf the cell in question
+ ;; is being given the same value it already has, and propagation stops.
+ ;;
+ (make-instance 'talker8)
+ #+(or) (let ((tk (make-instance 'talker8)))
+ (setf (idea8 tk) "yes")
+ (string-equal "yes" (words8 tk))
+ (setf (words8 tk) "no")
+ (string-equal "no" (idea8 tk)))
+ )
Added: dependencies/trunk/cells/cells-test/synapse-testing.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/synapse-testing.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,77 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+(defmodel counter-10 ()
+ ((ct :initarg :ct :initform nil :accessor ct)
+ (ct10 :initarg :ct10 :initform nil
+ :accessor ct10)))
+
+(defun cv-test-f-sensitivity ()
+ (cell-reset)
+ (with-metrics (t nil "cv-test-f-sensitivity")
+ (let ((self (make-be 'counter-10
+ :ct (c-in 0)
+ :ct10 (c? (count-it :ct10-rule)
+ (f-sensitivity :dummy-id (10)
+ (^ct))))))
+ (cv-assert (zerop (^ct10)))
+ (loop for n below 30
+ do (cv-assert (eq (^ct10) (* 10 (floor (^ct) 10))))
+ (incf (ct self))))
+ (cv-assert (eql 4 (count-of :ct10-rule)))))
+
+(defun cv-test-f-delta ()
+ (cell-reset)
+ (with-metrics (t nil "cv-test-f-delta")
+ (let ((self (make-be 'counter-10
+ :ct (c-in 0)
+ :ct10 (c? (count-it :ct10-rule)
+ (trc "runnning ct10-rule 1")
+ (f-delta :dummy ()
+ (^ct))))))
+ (cv-assert (zerop (^ct10)))
+ (cv-assert (zerop (^ct)))
+ (loop for n below 4
+ do (trc "loop incf ct" n)
+ (incf (ct self) n)
+ (cv-assert (eql (^ct10) n))))
+ (cv-assert (eql 4 (count-of :ct10-rule))))
+
+ (with-metrics (t nil "cv-test-f-delta-sensitivity")
+ (let ((self (make-be 'counter-10
+ :ct (c-in 0)
+ :ct10 (c? (count-it :ct10-rule)
+ (f-delta :xxx (:sensitivity 4)
+ (^ct))))))
+ (cv-assert (null (^ct10)))
+ (cv-assert (zerop (^ct)))
+ (loop for n below 4
+ do (trc "loop incf ct" n)
+ (incf (ct self) n)
+ (ecase n
+ ((0 1 2) (cv-assert (null (^ct10))))
+ (3 (cv-assert (eql (^ct10) 6)))
+ (4 (cv-assert (eql (^ct10) 4)))))
+ (cv-assert (eql 2 (count-of :ct10-rule))))))
+
Added: dependencies/trunk/cells/cells-test/test-cycle.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-cycle.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,79 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+
+
+(defmodel m-cyc ()
+ ((m-cyc-a :initform (c-in nil) :initarg :m-cyc-a :accessor m-cyc-a)
+ (m-cyc-b :initform (c-in nil) :initarg :m-cyc-b :accessor m-cyc-b)))
+
+(defobserver m-cyc-a ()
+ (print `(output m-cyc-a ,self ,new-value ,old-value))
+ (with-integrity (:change)
+ (setf (m-cyc-b self) new-value)))
+
+(defobserver m-cyc-b ()
+ (print `(output m-cyc-b ,self ,new-value ,old-value))
+ (with-integrity (:change)
+ (setf (m-cyc-a self) new-value)))
+
+(def-cell-test m-cyc () ;;def-cell-test m-cyc
+ (let ((m (make-instance 'm-cyc)))
+ (print `(start ,(m-cyc-a m)))
+ (setf (m-cyc-a m) 42)
+ (assert (= (m-cyc-a m) 42))
+ (assert (= (m-cyc-b m) 42))))
+
+#+(or)
+(m-cyc)
+
+(defmodel m-cyc2 ()
+ ((m-cyc2-a :initform (c-in 0) :initarg :m-cyc2-a :accessor m-cyc2-a)
+ (m-cyc2-b :initform (c? (1+ (^m-cyc2-a)))
+ :initarg :m-cyc2-b :accessor m-cyc2-b)))
+
+(defobserver m-cyc2-a ()
+ (print `(output m-cyc2-a ,self ,new-value ,old-value))
+ #+(or) (when (< new-value 45)
+ (setf (m-cyc2-b self) (1+ new-value))))
+
+(defobserver m-cyc2-b ()
+ (with-integrity (:change self)
+ (print `(output m-cyc2-b ,self ,new-value ,old-value))
+ (when (< new-value 45)
+ (setf (m-cyc2-a self) (1+ new-value)))))
+
+(def-cell-test m-cyc2
+ (let ((m (make-instance 'm-cyc2)))
+ (print '(start))
+ (setf (m-cyc2-a m) 42)
+ (describe m)
+ (assert (= (m-cyc2-a m) 44))
+ (assert (= (m-cyc2-b m) 45))
+ ))
+
+#+(or)
+(m-cyc2)
+
+
Added: dependencies/trunk/cells/cells-test/test-cyclicity.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-cyclicity.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,94 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+(defmodel ring-node ()
+ ((router-ids :cell nil :initform nil :initarg :router-ids :accessor router-ids)
+ (system-status :initform (c-in 'up) :initarg :system-status :accessor system-status
+ :documentation "'up, 'down, or 'unknown if unreachable")
+ (reachable :initarg :reachable :accessor reachable
+ :initform (c? (not (null ;; convert to boolean for readable test output
+ (find self (^reachable-nodes .parent))))))))
+
+(defun up (self) (eq 'up (^system-status)))
+
+(defmodel ring-net (family)
+ (
+ (ring :cell nil :initform nil :accessor ring :initarg :ring)
+ (sys-node :cell nil :initform nil :accessor sys-node :initarg :sys-node)
+ (reachable-nodes :initarg :reachable-nodes :accessor reachable-nodes
+ :initform (c? (contiguous-nodes-up
+ (find (sys-node self) (^kids)
+ :key 'md-name))))
+ )
+ (:default-initargs
+ :kids (c? (assert (sys-node self))
+ (assert (find (sys-node self) (ring self)))
+ (loop with ring = (ring self)
+ for triples on (cons (last1 ring)
+ (append ring (list (first ring))))
+ when (third triples)
+ collect (destructuring-bind (ccw node cw &rest others) triples
+ (declare (ignorable others))
+ (print (list ccw node cw))
+ (make-instance 'ring-node
+ :md-name node
+ :router-ids (list ccw cw)))))))
+
+(defun contiguous-nodes-up (node &optional (visited-nodes (list)))
+ (assert (not (find (md-name node) visited-nodes)))
+
+ (if (not (up node))
+ (values nil (push (md-name node) visited-nodes))
+ (progn
+ (push (md-name node) visited-nodes)
+ (values
+ (list* node
+ (mapcan (lambda (router-id)
+ (unless (find router-id visited-nodes)
+ (multiple-value-bind (ups new-visiteds)
+ (contiguous-nodes-up (fm-other! node router-id) visited-nodes)
+ (setf visited-nodes new-visiteds)
+ ups)))
+ (router-ids node)))
+ visited-nodes))))
+
+(defun test-ring-net ()
+ (flet ((dump-net (net msg)
+ (print '----------------------)
+ (print `(*** dump-net ,msg ******))
+ (dolist (n (kids net))
+ (print (list n (system-status n)(reachable n)(router-ids n))))))
+ (cell-reset)
+ (let ((net (make-instance 'ring-net
+ :sys-node 'two
+ :ring '(one two three four five six))))
+ (dump-net net "initially")
+ (setf (system-status (fm-other! net 'three)) 'down)
+ (dump-net net "down goes three!!")
+ (setf (system-status (fm-other! net 'six)) 'down)
+ (dump-net net "down goes six!!!"))))
+
+#+do-it
+(test-ring-net)
+
\ No newline at end of file
Added: dependencies/trunk/cells/cells-test/test-ephemeral.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-ephemeral.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,64 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+
+(defmodel m-ephem ()
+ ((m-ephem-a :cell :ephemeral :initform nil :initarg :m-ephem-a :accessor m-ephem-a)
+ (m-test-a :cell nil :initform nil :initarg :m-test-a :accessor m-test-a)
+ (m-ephem-b :cell :ephemeral :initform nil :initarg :m-ephem-b :accessor m-ephem-b)
+ (m-test-b :cell nil :initform nil :initarg :m-test-b :accessor m-test-b)))
+
+(defobserver m-ephem-a ()
+ (setf (m-test-a self) new-value))
+
+(defobserver m-ephem-b ()
+ (trc "out ephem-B copying to test-B" new-value)
+ (setf (m-test-b self) new-value))
+
+(def-cell-test m-ephem
+ (let ((m (make-instance 'm-ephem
+ :m-ephem-a (c-in nil)
+ :m-ephem-b (c? (prog2
+ (trc "Start calc ephem-B")
+ (* 2 (or (^m-ephem-a) 0))
+ (trc "Stop calc ephem-B"))))))
+ (ct-assert (null (slot-value m 'm-ephem-a)))
+ (ct-assert (null (m-ephem-a m)))
+ (ct-assert (null (m-test-a m)))
+ (ct-assert (null (slot-value m 'm-ephem-b)))
+ (ct-assert (null (m-ephem-b m)))
+ (ct-assert (zerop (m-test-b m)))
+ (trc "setting ephem-A to 3")
+ (setf (m-ephem-a m) 3)
+ (ct-assert (null (slot-value m 'm-ephem-a)))
+ (ct-assert (null (m-ephem-a m)))
+ (ct-assert (eql 3 (m-test-a m)))
+ ;
+ (ct-assert (null (slot-value m 'm-ephem-b)))
+ (ct-assert (null (m-ephem-b m)))
+ (ct-assert (eql 6 (m-test-b m)))
+ ))
+
+
+
Added: dependencies/trunk/cells/cells-test/test-family.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-family.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,158 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+(defmodel human (family)
+ ((age :initarg :age :accessor age :initform 10)))
+
+(defobserver .kids ((self human))
+ (when new-value
+ (print `(i have ,(length new-value) kids))
+ (dolist (k new-value)
+ (trc "one kid is named" (md-name k) :age (age k)))))
+
+(defobserver age ((k human))
+ (format t "~&~a is ~d years old" (md-name k) (age k)))
+
+(def-cell-test cv-test-family ()
+ (cells-reset)
+ (let ((mom (make-instance 'human)))
+ ;
+ ; the real power of cells appears when a population of model-objects are linked by cells, as
+ ; when a real-word collection of things all potentially affect each other.
+ ;
+ ; i use the family class to create a simple hierarchy in which kids have a pointer to their
+ ; parent (.fm-parent, accessor fm-parent) and a parent has a cellular list of their .kids (accessor kids)
+ ;
+ ; great expressive power comes from having kids be cellular; the model population changes as
+ ; the model changes in other ways. but this creates a delicate timing problem: kids must be fully
+ ; spliced into the model before their ruled cellular slots can be accessed, because a cell rule
+ ; itself might try to navigate the model to get to a cell value of some other model-object.
+ ;
+ ; the cell engine handles this in two steps. first, deep in the state change handling code
+ ; the .kids slot gets special handling (this is new for 2002, and come to think of it i will
+ ; have to expose that hook to client code so others can create models from structures other
+ ; than family) during which the fm-parent gets populated, among other things. second, the output of
+ ; kids calls to-be on each kid.
+ ;
+ ; one consequence of this is that one not need call to-be on new instances being added to
+ ; a larger model family, it will be done as a matter of course.
+ ;
+ (push (make-instance 'human :fm-parent mom :md-name 'natalia :age (c-in 23)) (kids mom))
+ (push (make-instance 'human :fm-parent mom :md-name 'veronica :age (c? (- (age (fm-other natalia)) 6))) (kids mom))
+ (push (make-instance 'human :fm-parent mom :md-name 'aaron :age (c? (- (age (fm-other veronica)) 4))) (kids mom))
+ (push (make-instance 'human :fm-parent mom :md-name 'melanie :age (c? (- (age (fm-other veronica)) 12))) (kids mom))
+ ;
+ ; some of the above rules invoke the macro fm-other. that searches the model space, first searching the
+ ; kids of the starting point (which defaults to a captured 'self), then recursively up to the
+ ; parent and the parent's kids (ie, self's siblings)
+ ;
+ (flet ((nat-age (n)
+ (setf (age (fm-other natalia :starting mom)) n)
+ (dolist (k (kids mom))
+ (ct-assert
+ (eql (age k)
+ (ecase (md-name k)
+ (natalia n)
+ (veronica (- n 6))
+ (aaron (- n 10))
+ (melanie (- n 18))))))))
+ (nat-age 23)
+ (nat-age 30)
+ (pop (kids mom))
+ (nat-age 40))))
+
+#+(or)
+
+(cv-test-family)
+
+;------------ family-values ------------------------------------------
+;;;
+;;; while family-values is itself rather fancy, the only cell concept introduced here
+;;; is that cell rules have convenient access to the current value of the slot, via
+;;; the symbol-macro ".cache" (leading and trailing full-stops). to see this we need to
+;;; go to the definition of family-values and examine the rule for the kids cell:
+;;;
+;;; (c? (assert (listp (kidvalues self)))
+;;; (eko (nil "gridhost kids")
+;;; (let ((newkids (mapcan (lambda (kidvalue)
+;;; (list (or (find kidvalue .cache :key (kvkey self) :test (kvkeytest self))
+;;; (trc nil "family-values forced to make new kid" self .cache kidvalue)
+;;; (funcall (kidfactory self) self kidvalue))))
+;;; (^kidvalues))))
+;;; (nconc (mapcan (lambda (oldkid)
+;;; (unless (find oldkid newkids)
+;;; (when (fv-kid-keep self oldkid)
+;;; (list oldkid))))
+;;; .cache)
+;;; newkids))))
+;;;
+;;; for efficiency's sake, family-values (fvs) generate kids only as needed based on determining
+;;; kidvalues cell. wherever possible existing kids are kept. this is done by looking in the current
+;;; value of the kids slot for a kid matching each new kidvalue and reusing that. we cannot use the
+;;; accessor kids because the first time thru the cell is internally invalid, so the rule will get dispatched
+;;; again in an infinite loop if we go through the accessor protocol.
+;;;
+;;; mind you, we could just use slot-value; .cache is just a convenience.
+;;;
+(defmodel bottle (model)
+ ((label :initarg :label :initform "unlabeled" :accessor label)))
+
+#+(or)
+(cv-family-values)
+
+(def-cell-test cv-family-values ()
+ (let* ((kf-calls 0)
+ (wall (make-instance 'family-values
+ :kv-collector (lambda (mdv)
+ (eko ("kidnos")(when (numberp mdv)
+ (loop for kn from 1 to (floor mdv)
+ collecting kn))))
+ :value (c-in 5)
+ :kv-key #'value
+ :kid-factory (lambda (f kv)
+ (incf kf-calls)
+ (trc "making kid" kv)
+ (make-instance 'bottle
+ :fm-parent f
+ :value kv
+ :label (c? (format nil "bottle ~d out of ~d on the wall"
+ (^value)
+ (length (kids f)))))))))
+ (ct-assert (eql 5 kf-calls))
+
+ (setq kf-calls 0)
+ (decf (value wall))
+ (ct-assert (eql 4 (length (kids wall))))
+ (ct-assert (zerop kf-calls))
+
+ (setq kf-calls 0)
+ (incf (value wall))
+ (ct-assert (eql 5 (length (kids wall))))
+ (ct-assert (eql 1 kf-calls))
+
+ ))
+
+#+(or)
+(cv-family-values)
Added: dependencies/trunk/cells/cells-test/test-kid-slotting.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-kid-slotting.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,84 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defmd image (family) left top width height)
+
+(defun i-right (x) (+ (left x) (width x)))
+(defun i-bottom (x) (+ (top x) (height x)))
+
+(defmd stack (image)
+ justify
+ (.kid-slots :initform (lambda (self)
+ (declare (ignore self))
+ (list
+ (mk-kid-slot (left :if-missing t)
+ (c? (+ (left .parent)
+ (ecase (justify .parent)
+ (:left 0)
+ (:center (floor (- (width .parent) (^width)) 2))
+ (:right (- (width .parent) (^width)))))))
+ (mk-kid-slot (top)
+ (c? (bif (psib (psib))
+ (i-bottom psib)
+ (top .parent))))))
+ :accessor kid-slots
+ :initarg :kid-slots))
+;;
+;; kid-slotting exists largely so graphical containers can be defined which arrange their
+;; component parts without those parts' cooperation. so a stack class can be defined as shown
+;; and then arbitrary components thrown in as children and they will be, say, right-justified
+;; because they will be endowed with rules as necessary to achieve that end by the parent stack.
+;;
+;; note the ifmissing option, which defaults to nil. the stack's goal is mainly to manage the
+;; top attribute of each kid to match any predecessor's i-bottom attribute. the stack will as a
+;; a convenience arrange for horizontal justification, but if some kid chose to define its
+;; left attribute that would be honored.
+;;
+(def-cell-test cv-kid-slotting ()
+ (cells-reset)
+ (let ((stack (make-instance 'stack
+ :left 10 :top 20
+ :width 500 :height 1000
+ :justify (c-in :left)
+ :kids (c? (eko ("kids") (loop for kn from 1 to 4
+ collect (make-kid 'image
+ :top 0 ;; overridden
+ :width (* kn 10)
+ :height (* kn 50)))))
+ )))
+ (ct-assert (eql (length (kids stack)) 4))
+ (ct-assert (and (eql 10 (left stack))
+ (every (lambda (k) (eql 10 (left k)))
+ (kids stack))))
+ (ct-assert (every (lambda (k)
+ (eql (top k) (i-bottom (fm-prior-sib k))))
+ (cdr (kids stack))))
+
+ (setf (justify stack) :right)
+ (ct-assert (and (eql 510 (i-right stack))
+ (every (lambda (k) (eql 510 (i-right k)))
+ (kids stack))))
+ ))
Added: dependencies/trunk/cells/cells-test/test-lazy.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-lazy.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,141 @@
+(in-package :cells)
+
+(defvar *tests* ())
+
+
+(defmacro deftest (name form &rest values)
+ "Po man's RT."
+ (let ((test-name (intern (format nil "TEST ~A" name))))
+ `(progn
+ (defun ,test-name ()
+ (let ((name ',name)
+ (form ',form)
+ (expected-values ',values)
+ (actual-values (multiple-value-list
+ (handler-case ,form
+ (error (val) val)))))
+ (assert (equal actual-values ',values) (actual-values)
+ "Test ~S failed~% ~
+ Form: ~A~% ~
+ Expected values: ~{~S~^; ~}~% ~
+ Actual values: ~{~S~^; ~}"
+ name form expected-values actual-values)
+ ',name))
+ (pushnew ',name *tests*)
+ ',name)))
+
+(defun do-test (name)
+ (let ((test (intern (format nil "TEST ~A" name) (symbol-package name))))
+ (funcall test)))
+
+(defun cv-test-lazy ()
+ (every #'do-test (reverse *tests*)))
+
+(defmacro unbound-error-p (form)
+ `(handler-case
+ (progn
+ ;;(print `(checking unbound error ,',form))
+ ,form nil)
+ (unbound-cell () t)))
+
+(defun make-cell-valid (self slot)
+ (setf (c-state (md-slot-cell self slot)) :valid))
+
+(defmodel unbound-values ()
+ ((val1 :initform (c-input ()) :initarg val1 :accessor test-val1)
+ (val2 :initform (c-input ()) :initarg val2 :accessor test-val2)))
+
+(defmodel unbound-formulas (unbound-values)
+ ((formula :initform nil ;; no longer an exception made for unechoed slots re c-awakening
+ :accessor test-formula)
+ (lazy-formula :initform (c-formula (:lazy t)
+ (^test-val1)
+ (^test-val2))
+ :accessor test-lazy-formula)))
+
+(defmodel unbound-formulas2 (unbound-values)
+ ((formula :initform (c? (^test-val1)
+ (^test-val2))
+ :accessor test-formula)
+ (lazy-formula :initform (c-formula (:lazy t)
+ (^test-val1)
+ (^test-val2))
+ :accessor test-lazy-formula)))
+
+(deftest unbound-values
+ (let ((self (make-instance 'unbound-values)))
+ (values (unbound-error-p (test-val1 self))
+ (unbound-error-p (test-val2 self))))
+ t t)
+
+(deftest md-slot-makunbound
+ (let ((self (progn (make-instance 'unbound-values
+ 'val1 (c-in nil) 'val2 (c-in nil)))))
+ (md-slot-makunbound self 'val1)
+ (md-slot-makunbound self 'val2)
+ (values (unbound-error-p (test-val1 self))
+ (unbound-error-p (test-val2 self))))
+ t t)
+
+(deftest formula-depends-on-unbound
+ (let ((obj1 (progn (make-instance 'unbound-formulas)))
+ (obj2 (progn (make-instance 'unbound-formulas))))
+ (values ;(unbound-error-p (test-formula obj1))
+ (unbound-error-p (test-lazy-formula obj1))
+
+ (unbound-error-p (test-lazy-formula obj2))
+ ;(unbound-error-p (test-formula obj2))
+ ))
+ t t)
+
+(deftest unbound-ok-for-unbound-formulas
+ (unbound-error-p
+ (progn (let ((self (progn (make-instance 'unbound-formulas))))
+ (setf (test-val1 self) t
+ (test-val2 self) t))
+ (let ((self (progn (make-instance 'unbound-formulas))))
+ (setf (test-val2 self) t
+ (test-val1 self) t))))
+ nil)
+
+(deftest unbound-errs-for-eager
+ (let ((self (progn (make-instance 'unbound-formulas2
+ 'val1 (c-in 1) 'val2 (c-in 2)))))
+ (values (test-formula self)
+ (unbound-error-p (md-slot-makunbound self 'val1))
+ (unbound-error-p (md-slot-makunbound self 'val2))
+ ))
+ 2 t t
+ )
+
+(deftest unbound-ok-for-unchecked-lazy
+ (let ((self (progn (make-instance 'unbound-formulas
+ 'val1 (c-in 1) 'val2 (c-in 2)))))
+ (values (test-lazy-formula self)
+ (unbound-error-p (md-slot-makunbound self 'val1))
+ (unbound-error-p (md-slot-makunbound self 'val2))))
+ 2 nil nil)
+
+#+(or)
+(cv-test-lazy)
+
+(defparameter *lz1-count* 0)
+
+(defmd lz-simple ()
+ (lz1 (c?_ (incf *lz1-count*)
+ (* 2 (^lz2))))
+ (lz2 (c-in 0)))
+
+(defun lz-test ()
+ (cells-reset)
+ (let ((*lz1-count* 0)
+ (lz (make-instance 'lz-simple)))
+ (assert (zerop *lz1-count*))
+ (incf (lz2 lz))
+ (assert (zerop *lz1-count*))
+ (assert (= (lz1 lz) 2))
+ (assert (= 1 *lz1-count*))
+ lz))
+
+#+test
+(lz-test)
Added: dependencies/trunk/cells/cells-test/test-synapse.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test-synapse.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,122 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+(defmodel m-syn ()
+ ((m-syn-a :initform nil :initarg :m-syn-a :accessor m-syn-a)
+ (m-syn-b :initform nil :initarg :m-syn-b :accessor m-syn-b)
+ (m-syn-factor :initform nil :initarg :m-syn-factor :accessor m-syn-factor)
+ (m-sens :initform nil :initarg :m-sens :accessor m-sens)
+ (m-plus :initform nil :initarg :m-plus :accessor m-plus)
+ ))
+
+(defobserver m-syn-b ()
+ (print `(output m-syn-b ,self ,new-value ,old-value)))
+
+(def-cell-test m-syn-bool
+ (let* ((delta-ct 0)
+ (m (make-instance 'm-syn
+ :m-syn-a (c-in nil)
+ :m-syn-b (c? (incf delta-ct)
+ (trc "syn-b containing rule firing!!!!!!!!!!!!!!" delta-ct)
+ (bwhen (msg (with-synapse :xyz42 ()
+ (trc "synapse fires!!! ~a" (^m-syn-a))
+ (bIF (k (find (^m-syn-a) '(:one :two :three)))
+ (values k :propagate)
+ (values NIL :no-propagate))))
+ msg)))))
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (null (m-syn-b m)))
+ (setf (m-syn-a m) :nine)
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (null (m-syn-b m)))
+ (setf (m-syn-a m) :one)
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (eq :one (m-syn-b m)))
+ (setf (m-syn-a m) :nine)
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (eq :one (m-syn-b m)))))
+
+(def-cell-test m-syn
+ (let* ((delta-ct 0)
+ (sens-ct 0)
+ (plus-ct 0)
+ (m (make-instance 'm-syn
+ :m-syn-a (c-in 0)
+ :m-syn-b (c? (incf delta-ct)
+ (trc nil "syn-b rule firing!!!!!!!!!!!!!! new delta-ct:" delta-ct)
+ (eko (nil "syn-b rule returning")
+ (f-delta :syna-1 (:sensitivity 2)
+ (^m-syn-a))))
+ :m-syn-factor (c-in 1)
+ :m-sens (c? (incf sens-ct)
+ (trc nil "m-sens rule firing ~d !!!!!!!!!!!!!!" sens-ct)
+ (* (^m-syn-factor)
+ (f-sensitivity :sensa (3) (^m-syn-a))))
+ :m-plus (c? (incf plus-ct)
+ (trc nil "m-plus rule firing!!!!!!!!!!!!!!" plus-ct)
+ (f-plusp :syna-2 (- 2 (^m-syn-a)))))))
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (= 1 sens-ct))
+ (ct-assert (= 1 plus-ct))
+ (ct-assert (= 0 (m-sens m)))
+ (trc "make-instance verified. about to incf m-syn-a")
+ (incf (m-syn-a m))
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (= 1 sens-ct))
+ (ct-assert (= 1 plus-ct))
+ (ct-assert (= 0 (m-sens m)))
+ (trc "about to incf m-syn-a 2")
+ (incf (m-syn-a m) 2)
+ (trc nil "syn-b now" (m-syn-b m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 2 sens-ct))
+ (ct-assert (= 2 plus-ct))
+
+ (ct-assert (= 3 (m-sens m)))
+ (trc "about to incf m-syn-a")
+ (incf (m-syn-a m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 2 sens-ct))
+ (trc "about to incf m-syn-factor")
+ (incf (m-syn-factor m))
+ (ct-assert (= 3 sens-ct))
+ (ct-assert (= (m-sens m) (* (m-syn-factor m) (m-syn-a m))))
+ (trc "about to incf m-syn-a xxx")
+ (incf (m-syn-a m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 3 sens-ct))
+ (trc "about to incf m-syn-a yyyy")
+ (incf (m-syn-a m))
+ (ct-assert (= 3 delta-ct))
+ (ct-assert (= 4 sens-ct))
+ (ct-assert (= 2 plus-ct))
+ (describe m)
+ (print '(start))))
+
+(defobserver m-syn-a ()
+ (trc "!!! M-SYN-A now =" new-value))
+
+#+(or)
+(m-syn)
+
Added: dependencies/trunk/cells/cells-test/test.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,273 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+#| Synapse Cell Unification Notes
+
+- start by making Cells synapse-y
+
+- make sure outputs show right old and new values
+- make sure outputs fire when they should
+
+- wow: test the Cells II dictates: no output callback sees stale data, no rule
+sees stale data, etc etc
+
+- test a lot of different synapses
+
+- make sure they fire when they should, and do not when they should not
+
+- make sure they survive an evaluation by the caller which does not branch to
+them (ie, does not access them)
+
+- make sure they optimize away
+
+- test with forms which access multiple other cells
+
+- look at direct alteration of a caller
+
+- does SETF honor not propagating, as well as a c-ruled after re-calcing
+
+- do diff unchanged tests such as string-equal work
+
+|#
+
+#| do list
+
+
+-- test drifters (and can they be handled without creating a special
+subclass for them?)
+
+|#
+
+(eval-when (compile load)
+ (proclaim '(optimize (speed 2) (safety 3) (space 1) (debug 3))))
+
+(in-package :cells)
+
+(defvar *cell-tests* nil)
+
+#+go
+(test-cells)
+
+
+(defun test-cells ()
+ (dribble "/home/alessio/0algebra/cells-test.txt")
+ (progn ;prof:with-profiling (:type :time)
+ (time
+ (progn
+ (loop for test in (reverse *cell-tests*)
+ when t ; (eq 'cv-test-person-5 test)
+ do (cell-test-init test)
+ (funcall test))
+ (print (make-string 40 :initial-element #\*))
+ (print (make-string 40 :initial-element #\*))
+ (print "*** Cells-test successfully completed **")
+ (print (make-string 40 :initial-element #\*))
+ (print (make-string 40 :initial-element #\*)))))
+ ;(prof:show-call-graph)
+ (dribble))
+
+(defun cell-test-init (name)
+ (print (make-string 40 :initial-element #\!))
+ (print `(starting test ,name))
+ (print (make-string 40 :initial-element #\!))
+ (cells-reset))
+
+(defmacro def-cell-test (name &rest body)
+ `(progn
+ (pushnew ',name *cell-tests*)
+ (defun ,name ()
+ (cells-reset)
+ , at body)))
+
+(defmacro ct-assert (form &rest stuff)
+ `(progn
+ (print `(attempting ,',form))
+ (assert ,form () "Error with ~a >> ~a" ',form (list , at stuff))))
+
+;; test huge number of useds by one rule
+
+(defmd m-index (family)
+ :value (c? (bwhen (ks (^kids))
+ ;(trc "chya" (mapcar 'value ks))
+ (apply '+ (mapcar 'value ks)))))
+
+(def-cell-test many-useds
+ (let ((i (make-instance 'm-index)))
+ (loop for n below 100
+ do (push (make-instance 'model
+ :fm-parent i
+ :value (c-in n))
+ (kids i)))
+ (trc "index total" (value i))
+ (ct-assert (= 4950 (value i)))))
+
+#+test
+(many-useds)
+
+(defmd m-null ()
+ (aa :cell nil :initform nil :initarg :aa :accessor aa))
+
+
+(def-cell-test m-null
+ (let ((m (make-instance 'm-null :aa 42)))
+ (ct-assert (= 42 (aa m)))
+ (ct-assert (= 21 (let ((slot 'aa))
+ (funcall (fdefinition `(setf ,slot)) (- (aa m) 21) m))))
+ :okay-m-null))
+
+(defmd m-solo () m-solo-a m-solo-b)
+
+(def-cell-test m-solo
+ (let ((m (make-instance 'm-solo
+ :m-solo-a (c-in 42)
+ :m-solo-b (c? (trc "b fires")(* 2 (^m-solo-a))))))
+ (ct-assert (= 42 (m-solo-a m)))
+ (ct-assert (= 84 (m-solo-b m)))
+ (decf (m-solo-a m))
+ (ct-assert (= 41 (m-solo-a m)))
+ (ct-assert (= 82 (m-solo-b m)))
+ :okay-m-null))
+
+(defmd m-var () m-var-a m-var-b)
+
+(defobserver m-var-b ()
+ (print `(output m-var-b ,self ,new-value ,old-value)))
+
+(def-cell-test m-var
+ (let ((m (make-instance 'm-var :m-var-a (c-in 42) :m-var-b 1951)))
+ (ct-assert (= 42 (m-var-a m)))
+ (ct-assert (= 21 (decf (m-var-a m) 21)))
+ (ct-assert (= 21 (m-var-a m)))
+ :okay-m-var))
+
+(defmd m-var-output ()
+ cbb
+ (aa :cell nil :initform nil :initarg :aa :accessor aa))
+
+(defobserver cbb ()
+ (trc "output cbb" self)
+ (setf (aa self) (- new-value (if old-value-boundp
+ old-value 0))))
+
+(def-cell-test m-var-output
+ (let ((m (make-instance 'm-var-output :cbb (c-in 42))))
+ (ct-assert (eql 42 (cbb m)))
+ (ct-assert (eql 42 (aa m)))
+ (ct-assert (eql 27 (decf (cbb m) 15)))
+ (ct-assert (eql 27 (cbb m)))
+ (ct-assert (eql -15 (aa m)))
+ (list :okay-m-var (aa m))))
+
+(defmd m-var-linearize-setf () ccc ddd)
+
+(defobserver ccc ()
+ (with-integrity (:change)
+ (setf (ddd self) (- new-value (if old-value-boundp
+ old-value 0)))))
+
+(def-cell-test m-var-linearize-setf
+ (let ((m (make-instance 'm-var-linearize-setf
+ :ccc (c-in 42)
+ :ddd (c-in 1951))))
+
+ (ct-assert (= 42 (ccc m)))
+ (ct-assert (= 42 (ddd m)))
+ (ct-assert (= 27 (decf (ccc m) 15)))
+ (ct-assert (= 27 (ccc m)))
+ (ct-assert (= -15 (ddd m)))
+ :okay-m-var))
+
+;;; -------------------------------------------------------
+
+(defmd m-ruled ()
+ eee
+ (fff (c? (floor (^ccc) 2))))
+
+(defobserver eee ()
+ (print `(output> eee ,new-value old ,old-value)))
+
+(defobserver fff ()
+ (print `(output> eee ,new-value old ,old-value)))
+
+(def-cell-test m-ruled
+ (let ((m (make-instance 'm-ruled
+ :eee (c-in 42)
+ :fff (c? (floor (^eee) 2)))))
+ (trc "___Initial TOBE done____________________")
+ (print `(pulse ,*data-pulse-id*))
+ (ct-assert (= 42 (eee m)))
+ (ct-assert (= 21 (fff m)))
+ (ct-assert (= 36 (decf (eee m) 6)))
+ (print `(pulse ,*data-pulse-id*))
+ (ct-assert (= 36 (eee m)))
+ (ct-assert (= 18 (fff m)) m)
+ :okay-m-ruled))
+
+(defmd m-worst-case ()
+ (wc-x (c-input () 2))
+ (wc-a (c? (prog2
+ (trc "Start A")
+ (when (oddp (wc-x self))
+ (wc-c self))
+ (trc "Stop A"))))
+ (wc-c (c? (evenp (wc-x self))))
+ (wc-h (c? (or (wc-c self)(wc-a self)))))
+
+(defun dependency-dump (self)
+ (let ((slot-cells (loop for esd in (class-slots (class-of self))
+ for sn = (slot-definition-name esd)
+ for c = (md-slot-cell self sn)
+ when c
+ collect (cons sn c))))
+ (trc "dependencies of" self)
+ (loop for (sn . c) in slot-cells
+ do (trc "slot" sn :callers (mapcar 'c-slot-name (c-callers c))))))
+
+(def-cell-test m-worst-case
+ (let ((m (make-instance 'm-worst-case)))
+ (dependency-dump m)
+ (trc "___Initial TOBE done____________________")
+ (ct-assert (eql t (wc-c m)))
+ (ct-assert (eql nil (wc-a m)))
+ (ct-assert (eql t (wc-h m)))
+ (dependency-dump m)
+ (ct-assert (eql 3 (incf (wc-x m))))))
+
+(defmd c?n-class ()
+ aaa bbb
+ (sum (c? (+ (^aaa) (^bbb)))))
+
+(def-cell-test test-c?n ()
+ (let ((self (make-instance 'c?n-class
+ :aaa (c?n (+ (^bbb) 2))
+ :bbb (c-in 40))))
+ (ct-assert (= (^bbb) 40)) ;; make sure I have not broken (setf slot-value)...it happens
+ (ct-assert (= (^aaa) 42)) ;; make sure the rule ran and the value stored as the slot value
+ (ct-assert (= (^sum) 82)) ;; make sure a normal rule works off the others
+ (setf (^bbb) 100)
+ (ct-assert (= (^bbb) 100)) ;; just checking
+ (ct-assert (= (^aaa) 42)) ;; make sure the rule did not run again
+ (ct-assert (= (^sum) 142)) ;; ... but the other rule does fire
+ (setf (^aaa) -58)
+ (ct-assert (= (^aaa) -58)) ;; ... we can setf the once-ruled slot
+ (ct-assert (= (^sum) 42)) ;; ... propagation still works from the once-ruled, now-input slot
+ ))
Added: dependencies/trunk/cells/cells-test/test.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells-test/test.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,13 @@
+;; -*- lisp-version: "8.0 [Windows] (Mar 7, 2006 20:04)"; cg: "1.81"; -*-
+
+(in-package :cg-user)
+
+(defpackage :CELLS)
+
+(define-project :name :test
+ :modules (list (make-instance 'module :name "test.lisp")
+ (make-instance 'module :name "test-ephemeral.lisp")
+ (make-instance 'module :name "test-cycle.lisp")
+ (make-instance 'module :name "test-synapse.lisp")
+ (make-instance 'module :name "output-timing.lisp"))
+ :projects (list (make-instance 'project-module :name "..\\cells"))
\ No newline at end of file
Added: dependencies/trunk/cells/cells.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,47 @@
+;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+
+#+(or allegro lispworks cmu mcl clisp cormanlisp sbcl scl abcl)
+(progn
+(declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))
+
+(asdf:defsystem :cells
+ :name "cells"
+ :author "Kenny Tilton <kentilton at gmail.com>"
+ :maintainer "Kenny Tilton <kentilton at gmail.com>"
+ :licence "Lisp LGPL"
+ :description "Cells"
+ :long-description "Cells: a dataflow extension to CLOS."
+ :version "3.0"
+ :serial t
+ :depends-on (:utils-kt)
+ :components ((:file "defpackage")
+ (:file "trc-eko")
+ (:file "cells")
+ (:file "integrity")
+ (:file "cell-types")
+ (:file "constructors")
+ (:file "initialize")
+ (:file "md-slot-value")
+ (:file "slot-utilities")
+ (:file "link")
+ (:file "propagate")
+ (:file "synapse")
+ (:file "synapse-types")
+ (:file "model-object")
+ (:file "defmodel")
+ (:file "md-utilities")
+ (:file "family")
+ (:file "fm-utilities")
+ (:file "family-values")
+ (:file "test-propagation")
+ (:file "cells-store")
+ (:file "test-cc")))
+
+(defmethod perform ((o load-op) (c (eql (find-system :cells))))
+ (pushnew :cells *features*))
+
+(defmethod perform ((o test-op) (c (eql (find-system :cells))))
+ (oos 'load-op :cells-test))
+
+(defmethod perform ((o test-op) (c (eql :cells)))
+ (oos 'load-op :cells-test)))
Added: dependencies/trunk/cells/cells.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,190 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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.
+
+|#
+
+#| Notes
+
+I don't like the way with-cc defers twice, first the whole thing and then when the
+body finally runs we are still within the original integrity and each setf gets queued
+to UFB separately before md-slot-value-assume finally runs. I think all that is going on here
+is that we want the programmer to use with-cc to show they know the setf will not be returning
+a useful value. But since they have coded the with-cc we should be able to figure out a way to
+let those SETFs thru as if they were outside integrity, and then we get a little less UFBing
+but even better SETF behaves as it should.
+
+It would be nice to do referential integrity and notice any time a model object gets stored in
+a cellular slot (or in a list in such) and then mop those up on not-to-be.
+
+|#
+
+(in-package :cells)
+
+(defparameter *c-prop-depth* 0)
+(defparameter *causation* nil)
+
+(defparameter *data-pulse-id* 0)
+(define-symbol-macro .dpid *data-pulse-id*)
+(defparameter *finbiz-id* 0) ;; debugging tool only
+(define-symbol-macro .fbid *finbiz-id*)
+
+(export! .dpid .fbid)
+(defparameter *c-debug* nil)
+(defparameter *defer-changes* nil)
+(defparameter *within-integrity* nil)
+(defvar *istack*)
+(defparameter *client-queue-handler* nil)
+(defparameter *unfinished-business* nil)
+(defparameter *not-to-be* nil)
+
+(defparameter *awake* nil)
+(defparameter *awake-ct* nil)
+
+#+test
+(cells-reset)
+
+(defun cells-reset (&optional client-queue-handler &key debug)
+ (utils-kt-reset)
+ (setf
+ *c-debug* debug
+ *c-prop-depth* 0
+ *awake-ct* nil
+ *awake* nil
+ *not-to-be* nil
+ *data-pulse-id* 0
+ *finbiz-id* 0
+ *defer-changes* nil ;; should not be necessary, but cannot be wrong
+ *client-queue-handler* client-queue-handler
+ *within-integrity* nil
+ *unfinished-business* nil
+ *trcdepth* 0)
+ (trc nil "------ cell reset ----------------------------"))
+
+(defun c-stop (&optional why)
+ (setf *stop* t)
+ (print `(c-stop-entry ,why))
+ (format t "~&C-STOP> stopping because ~a" why) )
+
+(define-symbol-macro .stop
+ (c-stop :user))
+
+(defun c-stopped ()
+ *stop*)
+
+(export! .stopped .cdbg)
+
+(define-symbol-macro .cdbg
+ *c-debug*)
+
+(define-symbol-macro .stopped
+ (c-stopped))
+
+(defmacro c-assert (assertion &optional places fmt$ &rest fmt-args)
+ (declare (ignorable assertion places fmt$ fmt-args))
+ #+(or)`(progn)
+ `(unless *stop*
+ (unless ,assertion
+ ,(if fmt$
+ `(c-break ,fmt$ , at fmt-args)
+ `(c-break "failed assertion: ~a" ',assertion)))))
+
+(defvar *call-stack* nil)
+(defvar *depender* nil)
+;; 2008-03-15: *depender* let's us differentiate between the call stack and
+;; and dependency. The problem with overloading *call-stack* with both roles
+;; is that we miss cyclic reentrance when we use without-c-dependency in a
+;; rule to get "once" behavior or just when fm-traversing to find someone
+
+(defmacro def-c-trace (model-type &optional slot cell-type)
+ `(defmethod trcp ((self ,(case cell-type
+ (:c? 'c-dependent)
+ (otherwise 'cell))))
+ (and (typep (c-model self) ',model-type)
+ ,(if slot
+ `(eq (c-slot-name self) ',slot)
+ `t))))
+
+(defmacro without-c-dependency (&body body)
+ ` (let (*depender*)
+ , at body))
+
+(export! .cause)
+
+(define-symbol-macro .cause
+ (car *causation*))
+
+(define-condition unbound-cell (unbound-slot)
+ ((cell :initarg :cell :reader cell :initform nil)))
+
+(defgeneric slot-value-observe (slotname self new old old-boundp cell)
+ #-(or cormanlisp)
+ (:method-combination progn))
+
+#-cells-testing
+(defmethod slot-value-observe #-(or cormanlisp) progn
+ (slot-name self new old old-boundp cell)
+ (declare (ignorable slot-name self new old old-boundp cell)))
+
+#+hunh
+(fmakunbound 'slot-value-observe)
+; -------- cell conditions (not much used) ---------------------------------------------
+
+(define-condition xcell () ;; new 2k0227
+ ((cell :initarg :cell :reader cell :initform nil)
+ (app-func :initarg :app-func :reader app-func :initform 'bad-cell)
+ (error-text :initarg :error-text :reader error-text :initform "<???>")
+ (other-data :initarg :other-data :reader other-data :initform "<nootherdata>"))
+ (:report (lambda (c s)
+ (format s "~& trouble with cell ~a in function ~s,~s: ~s"
+ (cell c) (app-func c) (error-text c) (other-data c)))))
+
+(define-condition c-enabling ()
+ ((name :initarg :name :reader name)
+ (model :initarg :model :reader model)
+ (cell :initarg :cell :reader cell))
+ (:report (lambda (condition stream)
+ (format stream "~&unhandled <c-enabling>: ~s" condition)
+ (break "~&i say, unhandled <c-enabling>: ~s" condition))))
+
+(define-condition c-fatal (xcell)
+ ((name :initform :anon :initarg :name :reader name)
+ (model :initform nil :initarg :model :reader model)
+ (cell :initform nil :initarg :cell :reader cell))
+ (:report (lambda (condition stream)
+ (format stream "~&fatal cell programming error: ~s" condition)
+ (format stream "~& : ~s" (name condition))
+ (format stream "~& : ~s" (model condition))
+ (format stream "~& : ~s" (cell condition)))))
+
+
+(define-condition asker-midst-askers (c-fatal)
+ ())
+;; "see listener for cell rule cycle diagnotics"
+
+(define-condition c-unadopted (c-fatal) ()
+ (:report
+ (lambda (condition stream)
+ (format stream "~&unadopted cell >: ~s" (cell condition))
+ (format stream "~& >: often you mis-edit (c? (c? ...)) nesting is error"))))
+
+(defun c-break (&rest args)
+ (unless *stop*
+ (let ((*print-level* 5)
+ (*print-circle* t)
+ (args2 (mapcar 'princ-to-string args)))
+ (c-stop :c-break)
+ ;(format t "~&c-break > stopping > ~{~a ~}" args2)
+ (apply 'error args2))))
\ No newline at end of file
Added: dependencies/trunk/cells/cells.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/cells.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,57 @@
+;; -*- lisp-version: "8.1 [Windows] (Oct 11, 2008 17:00)"; cg: "1.103.2.10"; -*-
+
+(in-package :cg-user)
+
+(defpackage :CELLS)
+
+(define-project :name :cells
+ :modules (list (make-instance 'module :name "defpackage.lisp")
+ (make-instance 'module :name "trc-eko.lisp")
+ (make-instance 'module :name "cells.lisp")
+ (make-instance 'module :name "integrity.lisp")
+ (make-instance 'module :name "cell-types.lisp")
+ (make-instance 'module :name "constructors.lisp")
+ (make-instance 'module :name "initialize.lisp")
+ (make-instance 'module :name "md-slot-value.lisp")
+ (make-instance 'module :name "slot-utilities.lisp")
+ (make-instance 'module :name "link.lisp")
+ (make-instance 'module :name "propagate.lisp")
+ (make-instance 'module :name "synapse.lisp")
+ (make-instance 'module :name "synapse-types.lisp")
+ (make-instance 'module :name "model-object.lisp")
+ (make-instance 'module :name "defmodel.lisp")
+ (make-instance 'module :name "md-utilities.lisp")
+ (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 "cells-store.lisp")
+ (make-instance 'module :name "test-cc.lisp"))
+ :projects (list (make-instance 'project-module :name
+ "utils-kt\\utils-kt" :show-modules
+ nil))
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :cells
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules nil
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags (list :local-name-info)
+ :build-flags (list :allow-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :include-manifest-file-for-visual-styles t
+ :default-command-line-arguments "+cx +t \"Initializing\""
+ :additional-build-lisp-image-arguments (list :read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :build-number 0
+ :on-initialization 'cells::test-with-cc
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/constructors.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/constructors.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,219 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(eval-now!
+ (export '(.cache-bound-p
+
+ ;; Cells Constructors
+ c?n
+ c?once
+ c?n-until
+ c?1
+ c_1
+ c?+n
+
+ ;; Debug Macros and Functions
+ c?dbg
+ c_?dbg
+ c-input-dbg
+
+ )))
+
+;___________________ constructors _______________________________
+
+(defmacro c-lambda (&body body)
+ `(c-lambda-var (slot-c) , at body))
+
+(defmacro c-lambda-var ((c) &body body)
+ `(lambda (,c &aux (self (c-model ,c))
+ (.cache (c-value ,c))
+ (.cache-bound-p (cache-bound-p ,c)))
+ (declare (ignorable .cache .cache-bound-p self))
+ , at body))
+
+(defmacro with-c-cache ((fn) &body body)
+ (let ((new (gensym)))
+ `(or (bwhen (,new (progn , at body))
+ (funcall ,fn ,new .cache))
+ .cache)))
+
+;-----------------------------------------
+
+(defmacro c? (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :rule (c-lambda , at body)))
+
+(defmacro c?+n (&body body)
+ `(make-c-dependent
+ :inputp t
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :rule (c-lambda , at body)))
+
+(defmacro c?n (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! '(without-c-dependency , at body)
+ :inputp t
+ :value-state :unevaluated
+ :rule (c-lambda (without-c-dependency , at body))))
+
+(export! c?n-dbg)
+
+(defmacro c?n-dbg (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! '(without-c-dependency , at body)
+ :inputp t
+ :debug t
+ :value-state :unevaluated
+ :rule (c-lambda (without-c-dependency , at body))))
+
+(defmacro c?n-until (args &body body)
+ `(make-c-dependent
+ :optimize :when-value-t
+ :code #+its-alive! nil #-its-alive! ',body
+ :inputp t
+ :value-state :unevaluated
+ :rule (c-lambda , at body)
+ , at args))
+
+(defmacro c?once (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! '(without-c-dependency , at body)
+ :inputp nil
+ :value-state :unevaluated
+ :rule (c-lambda (without-c-dependency , at body))))
+
+(defmacro c_1 (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! '(without-c-dependency , at body)
+ :inputp nil
+ :lazy t
+ :value-state :unevaluated
+ :rule (c-lambda (without-c-dependency , at body))))
+
+(defmacro c?1 (&body body)
+ `(c?once , at body))
+
+(defmacro c?dbg (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :debug t
+ :rule (c-lambda , at body)))
+
+(defmacro c?_ (&body body)
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :lazy t
+ :rule (c-lambda , at body)))
+
+(defmacro c_? (&body body)
+ "Lazy until asked, then eagerly propagating"
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :lazy :until-asked
+ :rule (c-lambda , at body)))
+
+(defmacro c_?dbg (&body body)
+ "Lazy until asked, then eagerly propagating"
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',body
+ :value-state :unevaluated
+ :lazy :until-asked
+ :rule (c-lambda , at body)
+ :debug t))
+
+(defmacro c?? ((&key (tagp nil) (in nil) (out t))&body body)
+ (let ((result (copy-symbol 'result))
+ (thetag (gensym)))
+ `(make-c-dependent
+ :code ',body
+ :value-state :unevaluated
+ :rule (c-lambda
+ (let ((,thetag (gensym "tag"))
+ (*trcdepth* (1+ *trcdepth*))
+ )
+ (declare (ignorable self ,thetag))
+ ,(when in
+ `(trc "c??> entry" (c-slot-name c) (c-model c) (when ,tagp ,thetag)))
+ (count-it :c?? (c-slot-name c) (md-name (c-model c)))
+ (let ((,result (progn , at body)))
+ ,(when out `(trc "c?? result:" ,result (c-slot-name c) (when ,tagp ,thetag)))
+ ,result))))))
+
+(defmacro c-formula ((&rest keys &key lazy &allow-other-keys) &body forms)
+ (assert (member lazy '(nil t :once-asked :until-asked :always)))
+ `(make-c-dependent
+ :code #+its-alive! nil #-its-alive! ',forms
+ :value-state :unevaluated
+ :rule (c-lambda , at forms)
+ , at keys))
+
+(defmacro c-input ((&rest keys) &optional (value nil valued-p))
+ `(make-cell
+ :inputp t
+ :value-state ,(if valued-p :valid :unbound)
+ :value ,value
+ , at keys))
+
+(defmacro c-in (value)
+ `(make-cell
+ :inputp t
+ :value-state :valid
+ :value ,value))
+
+(export! c-in-lazy c_in)
+
+(defmacro c-in-lazy (&body body)
+ `(c-input (:lazy :once-asked) (progn , at body)))
+
+(defmacro c_in (&body body)
+ `(c-input (:lazy :once-asked) (progn , at body)))
+
+(defmacro c-input-dbg (&optional (value nil valued-p))
+ `(make-cell
+ :inputp t
+ :debug t
+ :value-state ,(if valued-p :valid :unbound)
+ :value ,value))
+
+(defmacro c... ((value) &body body)
+ `(make-c-drifter
+ :code ',body
+ :value-state :valid
+ :value ,value
+ :rule (c-lambda , at body)))
+
+(defmacro c-abs (value &body body)
+ `(make-c-drifter-absolute
+ :code ',body
+ :value-state :valid
+ :value ,value
+ :rule (c-lambda , at body)))
+
+
+(defmacro c-envalue (&body body)
+ `(make-c-envaluer
+ :envalue-rule (c-lambda , at body)))
+
Added: dependencies/trunk/cells/defmodel.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/defmodel.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,207 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+(defmacro defmodel (class directsupers slotspecs &rest options)
+ ;;(print `(defmodel sees directsupers ,directsupers using ,(or directsupers :model-object)))
+ (assert (not (find class directsupers))() "~a cannot be its own superclass" class)
+ `(progn
+ (setf (get ',class :cell-types) nil)
+ (setf (get ',class 'slots-excluded-from-persistence)
+ (loop for slotspec in ',slotspecs
+ unless (and (getf (cdr slotspec) :ps t)
+ (getf (cdr slotspec) :persistable t))
+ collect (car slotspec)))
+ (loop for slotspec in ',slotspecs
+ do (destructuring-bind
+ (slotname &rest slotargs
+ &key (cell t)
+ &allow-other-keys)
+ slotspec
+ (declare (ignorable slotargs))
+ (when cell
+ (setf (md-slot-cell-type ',class slotname) cell))))
+ ;; define slot macros before class so they can appear in
+ ;; initforms and default-initargs
+ ,@(loop for slotspec in slotspecs
+ nconcing (destructuring-bind
+ (slotname &rest slotargs
+ &key (cell t) (accessor slotname) reader
+ &allow-other-keys)
+ slotspec
+ (declare (ignorable slotargs ))
+ (when cell
+ (list (let* ((reader-fn (or reader accessor))
+ (deriver-fn (intern$ "^" (symbol-name reader-fn))))
+ `(eval-when (:compile-toplevel :execute :load-toplevel)
+ (unless (macro-function ',deriver-fn)
+ (defmacro ,deriver-fn ()
+ `(,',reader-fn self)))
+ #+sbcl (unless (fboundp ',reader-fn)
+ (defgeneric ,reader-fn (slot)))))))))
+
+ ;
+ ; ------- defclass --------------- (^slot-value ,model ',',slotname)
+ ;
+ (prog1
+ (defclass ,class ,(or directsupers '(model-object)) ;; now we can def the class
+ ,(mapcar (lambda (s)
+ (list* (car s)
+ (let ((ias (cdr s)))
+ (remf ias :persistable)
+ (remf ias :ps)
+ ;; We handle accessor below
+ (when (getf ias :cell t)
+ (remf ias :reader)
+ (remf ias :writer)
+ (remf ias :accessor))
+ (remf ias :cell)
+ (remf ias :owning)
+ (remf ias :unchanged-if)
+ ias))) (mapcar #'copy-list slotspecs))
+ (:documentation
+ ,@(or (cdr (find :documentation options :key #'car))
+ '("chya")))
+ (:default-initargs ;; nil ok and needed: acl oddity in re not clearing d-i's sans this
+ ,@(cdr (find :default-initargs options :key #'car)))
+ (:metaclass ,(or (cadr (find :metaclass options :key #'car))
+ 'standard-class)))
+
+ (defmethod shared-initialize :after ((self ,class) slot-names &rest iargs &key)
+ (declare (ignore slot-names iargs))
+ ,(when (and directsupers (not (member 'model-object directsupers)))
+ `(unless (typep self 'model-object)
+ (error "If no superclass of ~a inherits directly
+or indirectly from model-object, model-object must be included as a direct super-class in
+the defmodel form for ~a" ',class ',class))))
+
+ ;
+ ; slot accessors once class is defined...
+ ;
+ ,@(mapcar (lambda (slotspec)
+ (destructuring-bind
+ (slotname &rest slotargs
+ &key (cell t) unchanged-if (accessor slotname) reader writer type
+ &allow-other-keys)
+ slotspec
+
+ (declare (ignorable slotargs))
+ (when cell
+ (let* ((reader-fn (or reader accessor))
+ (writer-fn (or writer accessor))
+ )
+ `(progn
+ ,(when writer-fn
+ `(defmethod (setf ,writer-fn) (new-value (self ,class))
+ (setf (md-slot-value self ',slotname)
+ ,(if type
+ `(coerce new-value ',type)
+ 'new-value))))
+ ,(when reader-fn
+ `(defmethod ,reader-fn ((self ,class))
+ (md-slot-value self ',slotname)))
+ ,(when unchanged-if
+ `(def-c-unchanged-test (,class ,slotname) ,unchanged-if)))))))
+ slotspecs))
+ (loop for slotspec in ',slotspecs
+ do (destructuring-bind
+ (slotname &rest slotargs &key (cell t) owning &allow-other-keys)
+ slotspec
+ (declare (ignorable slotargs))
+ (when (and cell owning)
+ (setf (md-slot-owning-direct? ',class slotname) owning))))))
+
+(defun defmd-canonicalize-slot (slotname
+ &key
+ (cell nil cell-p)
+ (ps t ps-p)
+ (persistable t persistable-p)
+ (owning nil owning-p)
+ (type nil type-p)
+ (initform nil initform-p)
+ (initarg (intern (symbol-name slotname) :keyword))
+ (documentation nil documentation-p)
+ (unchanged-if nil unchanged-if-p)
+ (reader slotname reader-p)
+ (writer `(setf ,slotname) writer-p)
+ (accessor slotname accessor-p)
+ (allocation nil allocation-p))
+ (list* slotname :initarg initarg
+ (append
+ (when cell-p (list :cell cell))
+ (when ps-p (list :ps ps))
+ (when persistable-p (list :persistable persistable))
+ (when owning-p (list :owning owning))
+ (when type-p (list :type type))
+ (when initform-p (list :initform initform))
+ (when unchanged-if-p (list :unchanged-if unchanged-if))
+ (when reader-p (list :reader reader))
+ (when writer-p (list :writer writer))
+ (when (or accessor-p
+ (not (and reader-p writer-p)))
+ (list :accessor accessor))
+ (when allocation-p (list :allocation allocation))
+ (when documentation-p (list :documentation documentation)))))
+
+(defmacro defmd (class superclasses &rest mdspec)
+ `(defmodel ,class (, at superclasses model)
+ ,@(let (definitargs class-options slots)
+ (loop with skip
+ for (spec next) on mdspec
+ if skip
+ do (setf skip nil)
+ else do (etypecase spec
+ (cons
+ (cond
+ ((keywordp (car spec))
+ (assert (find (car spec) '(:documentation :metaclass)))
+ (push spec class-options))
+ ((find (cadr spec) '(:initarg :type :ps :persistable :cell :initform :allocation :reader :writer :accessor :documentation))
+ (push (apply 'defmd-canonicalize-slot spec) slots))
+ (t ;; shortform (slotname initform &rest slotdef-key-values)
+ (push (apply 'defmd-canonicalize-slot
+ (list* (car spec) :initform (cadr spec) (cddr spec))) slots))))
+ (keyword
+ (setf definitargs (append definitargs (list spec next)))
+ (setf skip t))
+ (symbol (push (list spec :initform nil
+ :initarg (intern (symbol-name spec) :keyword)
+ :accessor spec) slots)))
+ finally
+ (return (list* (nreverse slots)
+ (delete nil
+ (list* `(:default-initargs , at definitargs)
+ (nreverse class-options)))))))))
+
+
+
+#+test
+(progn
+ (defclass md-test-super ()())
+
+ (defmd defmd-test (md-test-super)
+ (aaa :cell nil :initform nil :initarg :aaa :accessor aaa) ;; defmd would have written the same
+ (aa2 :documentation "hi mom")
+ bbb
+ (ccc 42 :allocation :class)
+ (ddd (c-in nil) :cell :ephemeral)
+ :superx 42 ;; default-initarg
+ (:documentation "as if!")))
+
+
+
Added: dependencies/trunk/cells/defpackage.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/defpackage.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,64 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 2008 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :common-lisp-user)
+
+(defpackage :cells
+ (:use #:common-lisp #:utils-kt #+abcl #:sys)
+ (:import-from
+ ;; MOP
+ #+allegro #:excl
+ #+clisp #:clos
+ #+cmu #:mop
+ #+cormanlisp #:common-lisp
+ #+lispworks #:clos
+ #+sbcl #:sb-mop
+ #+openmcl-partial-mop #:openmcl-mop
+ #+(and mcl (not openmcl-partial-mop)) #:ccl
+ #+abcl #:mop
+ #-(or allegro clisp cmu cormanlisp lispworks mcl sbcl abcl)
+ #.(cerror "Provide a package name."
+ "Don't know how to find the MOP package for this Lisp.")
+
+ #:class-precedence-list
+ #-(and mcl (not openmcl-partial-mop)) #:class-slots
+ #:slot-definition-name
+ #:class-direct-subclasses
+ )
+ (:export #:cell #:.md-name
+ #:c-input #:c-in #:c-in8
+ #:c-formula #:c? #:c_? #:c?8 #:c?_ #:c??
+ #:with-integrity #:without-c-dependency #:self #:*parent*
+ #:.cache #:.with-c-cache #:c-lambda
+ #:defmodel #:defmd #:defobserver #:slot-value-observe #:def-c-unchanged-test
+ #:new-value #:old-value #:old-value-boundp #:c...
+ #:md-awaken
+ #:mkpart #:make-kid #:the-kids #:nsib #:value #:^value #:.value #:kids #:^kids #:.kids
+ #:cells-reset #:upper #:fm-max #:nearest #:fm-min-kid #:fm-max-kid #:mk-kid-slot
+ #:def-kid-slots #:find-prior #:fm-pos #:kid-no #:fm-includes #:fm-ascendant-common
+ #:fm-kid-containing #:fm-find-if #:fm-ascendant-if #:c-abs #:fm-collect-if #:psib
+ #:not-to-be #:ssibno
+ #:c-debug #:c-break #:c-assert #:c-stop #:c-stopped #:c-assert #:.stop #:delta-diff
+ #:wtrc #:wnotrc #:eko-if #:trc #:wtrc #:eko #:ekx #:trcp #:trcx)
+ #+allegro (:shadowing-import-from #:excl #:fasl-write #:fasl-read #:gc)
+ )
+
Added: dependencies/trunk/cells/doc/01-Cell-basics.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/doc/01-Cell-basics.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,431 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+#|
+
+[A minimal primer on cells, last tested on march 13, 2006 against cells3]
+
+cells
+-----
+think of a clos slot as a cell in a paper spreadsheet, a financial
+modeling tool popular enough to make visi-calc the first business
+killer app for microcomputers.
+
+as a child i watched my father toil at home for hours over paper
+spreadsheets with pencil and slide rule. after he changed one value,
+he had to propagate that change to other cells by first remembering
+which other ones included the changed cell in their computation.
+then he had to do the calculations for those, erase, enter...
+and then repeating that process to propagate those changes in a
+cascade across the paper.
+
+visi-calc let my father take the formula he had in mind and
+put it in (declare it to) the electronic spreadsheet. then visi-calc
+could do the tedious work: recalculating, knowing what to recalculate,
+and knowing in what order to recalculate.
+
+cells do for programmers what electronic spreadsheets did for my father.
+without cells, clos slots are like cells of a paper spreadsheet.
+a single key-down event can cause a cascade of change throughout an
+application. the programmer has to arrange for it all to happen,
+all in the right order: delete any selected text, insert
+the new character, re-wrap the text, update the undo mechanism, revisit
+the menu statuses ("cut" is no longer enabled), update the scroll bars,
+possibly scroll the window, flag the file as unsaved...
+
+with cells, the programmer looks at program state differently. one
+asks, "how could i compute, at any point of runtime, a value for
+a given slot of an arbitrary instance, based only on other runtime state
+(other slots of other instances)." great fun, by the way, as well as
+enforcing good programming practices like encapsulation.
+
+an example will help. consider indeed the state of the "cut" menu item.
+in some applications, programmers have a dozen places in their code
+where they tend to the status of the cut menu item. one might be:
+
+(defun do-clear (edit-structure)
+ (when (selected-range edit-structure)
+ <set up undo>
+ <toss selected text>
+ <etc><etc>
+ (menu-item-enable *edit-cut* nil)
+ (menu-item-enable *edit-copy* nil)
+ (menu-item-enable *edit-clear* nil)))
+
+other programmers wait until the user clicks on the edit menu,
+then decide just-in-time from program state whether the cut item
+should be enabled:
+
+(defmethod prep-for-display ((m edit-menu))
+ <lotsa other stuff>
+ (when (typep (focus *app*) 'text-edit-widget)
+ (menu-item-enable (find :cut (items m) :key #'item-name)
+ (not (null (selected-range (focus *app*)))))))
+
+this latter programmer is ready for cells, because they
+have already shifted from imperative to declarative thinking;
+they have learned to write code that works based not on what
+has happened lately, but instead only on the current program
+state (however it got that way).
+
+the cell programmer writes:
+
+(make-instance 'menu-item
+ :name :cut
+ :label "cut"
+ :cmd-key +control-x+
+ :actor #'do-cut
+ :enabled (c? (when (typep (focus *app*) 'text-edit-widget)
+ (not (null (selected-range (focus *app*)))))))
+
+...and now they can forget the menu item exists as they work
+on the rest of the application. the menu-item enabled status
+will stay current (correct) as the selected-range changes
+and as the focus itself changes as the user moves from field
+to field.
+
+that covers the spirit of cells. now let's look at the syntax
+and mechanics, with examples you can execute once you have
+loaded the cells package. see the read-me.txt file in the
+root directory into which the cello software was unzipped.
+
+we'll model a falling stone, where the distance fallen is half
+the product of the acceleration (due to gravity) and the
+square of the time falling.
+
+|#
+
+(in-package :cells)
+
+(defmodel stone ()
+ ((accel :cell t :initarg :accel :initform 0 :accessor accel)
+ (time-elapsed :cell t :initarg :time-elapsed
+ :initform (c-in 0)
+ :accessor time-elapsed)
+ (distance :cell t :initarg :distance :initform 0 :accessor distance))
+ (:default-initargs
+ :distance (c? (/ (* (accel self)
+ (expt (time-elapsed self) 2))
+ 2))))
+
+(defobserver accel ((self stone) new old old-bound-p)
+ (trc "observer sees accel" :new new :old old :oldp old-bound-p)) ;; TRC provides print diagnostics
+
+(defobserver time-elapsed ((self stone)) ;; short form (I'm lazy)
+ (trc "observer sees time-elapsed" :new new-value :old old-value :oldp old-value-boundp))
+
+(defobserver distance ((self stone))
+ (format t "~&observer sees distance fallen: ~d feet" new-value))
+
+
+#|
+let's look at non-standard syntax found in the forms above,
+in the order in which they appear:
+
+ (defmodel ...
+
+defmodel is just a defclass wrapper which also sets up plumbing for cells.
+
+ ... :cell t ...
+
+without this option, a model instance slot cannot be powered
+by a cell (and cell slot access overhead is avoided).
+
+with this option, one can specify what kind of cell
+is to be defined: ephemeral, delta or t (normal). we'll leave
+those esoteric cell slot types for another tutorial and just
+specify t to get normal cells (the ones used 99% of the time).
+
+ time-elapsed ... :initform (c-in 0)...
+
+(c-in <value>) allows the cellular slot (or "cell", for short)
+to be setf'ed. these are inputs to the dataflow,
+which usually flows from c? to c? but has to start somewhere.
+since modern interactve applications are event-driven, in
+real-world cello apps most cv dataflow inputs are slots closely
+corresponding to some system value, such as the position slots
+of a cell-powered mouse class. moving on...
+
+a naked value such as the 32 supplied for accel cannot be changed; a
+runtime error results from any such attempt. this makes cells faster,
+because some plumbing can be skipped: no dependency gets recorded between
+the distance traveled and the acceleration. on the other hand, a more
+elaborate model might have the acceleration varying according to the distance
+between the stone and earth (in which case we get into an advance
+topic for another day, namely how to handle circularity.)
+
+next: (:default-initargs
+ :distance (c? (/ (* (accel self)
+ (expt (time-elapsed self) 2))
+ 2)
+
+c? associates a rule with a cellular slot (or "cell", for short). any
+read operation on another cell (directly or during a function call)
+establishes a dependency of distance on that cell -- unless that cell
+can never change. why would a cell not be able to change?
+
+cell internals enforce a rule that a cell with a naked value (ie, not wrapped
+in cv or c?) cannot be changed by client code (ok, (setf slot-value) is a backdoor).
+cell internals enforce this, simply to make possible the optimization
+of leaving off the overhead of recording a pointless dependency.
+
+next: (defobserver...
+
+here is the signature for the defobserver macro:
+
+ (defmacro defobserver (slotname (&optional (self-arg 'self)
+ (new-varg 'new-value)
+ (oldvarg 'old-value)
+ (oldvargboundp 'old-value-boundp))
+ &body observer-body) ....)
+
+defobserver defines a generic method with method-combination progn,
+which one can specialize on any of the four
+parameters. the method gets called when the slot value changes, and during
+initial processing by shared-initialize (part of make-instance).
+
+shared-initialize brings a new model instance to life, including calling
+any observers defined for cellular slots.
+
+now evaluate the following:
+
+|#
+
+#+evaluatethis
+
+(progn
+ (cells-reset)
+ (defparameter *s2* (make-instance 'stone
+ :accel 32 ;; (constant) feet per second per second
+ :time-elapsed (c-in 0))))
+
+#|
+
+...and observe:
+0> observer sees accel :new 32 :old nil :oldp nil
+0> observer sees time-elapsed :new 0 :old nil :oldp nil
+observer sees distance fallen: 0 feet
+
+
+getting back to the output shown above, why observer output on a new instance? we want
+any new instance to come fully to life. that means
+evaluating every rule so the dependencies get established, and
+propagating cell values outside the model (by calling the observer
+methods) to make sure the model and outside world (if only the
+system display) are consistent.
+
+;-----------------------------------------------------------
+now let's get moving:
+
+|#
+
+#+evaluatethis
+
+(setf (time-elapsed *s2*) 1)
+
+#|
+...and observe:
+0> observer sees time-elapsed :new 1 :old 0 :oldp t
+observer sees distance fallen: 16 feet
+
+behind the scenes:
+- the slot value time-elapsed got changed from 0 to 1
+- the time-elapsed observer was called
+- dependents on time-elapsed (here just distance) were recalculated
+- go to the first step, this time for the distance slot
+
+;-----------------------------------------------------------
+to see some optimizations at work, set the cell time-elapsed to
+the same value it already has:
+|#
+
+#+evaluatethis
+
+(setf (time-elapsed *s2*) 1)
+
+#| observe:
+nothing, since the slot-value did not in fact change.
+
+;-----------------------------------------------------------
+to test the enforcement of the cell stricture against
+modifying cells holding naked values:
+|#
+
+#+evaluatethis
+
+(let ((*c-debug* t))
+ (handler-case
+ (setf (accel *s2*) 10)
+ (t (error)
+ (cells-reset) ;; clear a *stop* flag used to bring down a runaway model :)
+ (trc "error is" error)
+ error)))
+
+#| observe:
+c-setting-debug > constant accel in stone may not be altered..init to (c-in nil)
+0> error is #<simple-error @ #x210925f2>
+
+Without turning on *c-debug* one just gets the runtime error, not the explanation to standard output.
+
+;-----------------------------------------------------------
+nor may ruled cells be modified arbitrarily:
+|#
+
+#+evaluatethis
+
+(let ((*c-debug* t))
+ (handler-case
+ (setf (distance *s2*) 42)
+ (t (error)
+ (cells-reset)
+ (trc "error is" error)
+ error)))
+
+#| observe:
+c-setting-debug > ruled distance in stone may not be setf'ed
+0> error is #<simple-error @ #x2123e392>
+
+;-----------------------------------------------------------
+aside from c?, cv, and defobserver, another thing you will see
+in cello code is how complex views are constructed using
+the family class and its slot kids. every model-object has a
+parent slot, which gets used along with a family's kids slot to
+form simple trees navigable up and down.
+
+model-objects also have slots for md-name and value (don't
+worry camelcase-haters, that is a declining feature of my code).
+md-name lets the family trees we build be treated as namespaces.
+value just turns out to be very handy for a lot of things. for
+example, a check-box instance needs some place to indicate its
+boolean state.
+
+now let's see family in action, using code from the handbook of
+silly examples. all i want to get across is that a lot happens
+when one changes the kids slot. it happens automatically, and
+it happens transparently, following the dataflow implicit in the
+rules we write, and the side-effects we specify via observer functions.
+
+the silly example below just shows the summer (that which sums) getting
+a new value as the kids change, along with some observer output. in real-world
+applications, where kids represent gui elements often dependent on
+each other, vastly more can transpire before a simple push into a kids
+slot has run its course.
+
+evaluate:
+|#
+
+(defmodel summer (family)
+ ()
+ (:default-initargs
+ :kids (c-in nil) ;; or we cannot add any addend kids later
+ :value (c? (trc "val rule runs")
+ (reduce #'+ (kids self)
+ :initial-value 0
+ :key #'value))))
+
+(defobserver .value ((self summer))
+ (trc "the sum of the values of the kids is" new-value))
+
+(defobserver .kids ((self summer))
+ (trc "the values of the kids are" (mapcar #'value new-value)))
+
+;-----------------------------------------------------------
+; now just evaluate each of the following forms one by one,
+; checking results after each to see what is going on
+;
+#+evaluatethis
+
+(defparameter *f1* (make-instance 'summer))
+
+#|
+observe:
+0> the sum of the values of the kids is 0
+0> the values of the kids are nil
+
+;----------------------------------------------------------|#
+
+#+evaluatethis
+
+(push (make-instance 'model
+ :fm-parent *f1*
+ :value 1) (kids *f1*))
+
+#| observe:
+0> the values of the kids are (1)
+0> the sum of the values of the kids is 1
+
+;----------------------------------------------------------|#
+
+#+evaluatethis
+
+(push (make-instance 'model
+ :fm-parent *f1*
+ :value 2) (kids *f1*))
+
+#| observe:
+0> the values of the kids are (2 1)
+0> the sum of the values of the kids is 3
+
+;----------------------------------------------------------|#
+
+#+evaluatethis
+
+(setf (kids *f1*) nil)
+
+#| observe:
+0> the values of the kids are nil
+0> the sum of the values of the kids is 0
+
+now before closing, it occurs to me you'll need a little
+introduction to the semantics of ^slot-x macros generated
+by the defmodel macro. here is another way to define our stone:
+
+|#
+
+#+evaluatethis
+
+(setq *s2* (make-instance 'stone
+ :accel 2
+ :time-elapsed (c-in 3)
+ :distance (c? (+ (^accel) (^time-elapsed)))))
+
+#| in the olden days of cells, when they were called
+semaphors, the only way to establish a dependency
+was to use some form like:
+
+ (^some-slot some-thing)
+
+that is no longer necessary. now any dynamic access:
+
+(1) during evaluation of a form wrapped in (c?...)
+(2) to a cell, direct or inside some function
+(3) using accessors named in the defmodel form (not slot-value)
+
+...establishes a dependency. so why still have the ^slot macros?
+
+one neat thing about the ^slot macros is that the default
+argument is self, an anaphor set up by c? and its ilk, so
+one can make many rules a little easier to follow by simply
+coding (^slot). another is convenient specification of
+synapses on dependencies, a more advanced topic we can
+ignore a while.
+
+
+|#
Added: dependencies/trunk/cells/doc/cell-doc.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/doc/cell-doc.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,181 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+#|
+
+Deep thoughts: Where a program implements a model using interesting, long-lived state (such
+as the position of other players on a virtual soccer field in a game program), some state will
+be computed off of other such state. Not everything is raw input. eg, a player might
+have set himself a task such as "tackle opponent" based on a higher-level computation
+of what is going on in the game, and then "current task" is both computed yet long-lived.
+
+Spread throughout the application will be code here and code there
+which makes an interesting computation using other program state ("given what I can see,
+which player if any has the ball") and decides
+to do something, which may be (a) to act outside the program such as cause some component
+to be redrawn (say, to manifest its new color, in this case if a debugging hack uses
+the game display to show which player the algorithm has settled on) or (b) to cache the
+observation as a guide to other algorithms. My current task "tackle opponent" controls
+inter alia the player's choices on which way to turn and how fast to run in order
+to close on the opponent.
+
+Whenever a program receives an input, such as the mouse position or a keystroke or
+a message over a socket connection, some computations need to be repeated. In a
+multi-player game an external server will be deciding the position of the ball, and
+when that changes my program must rethink a lot of things which were decided based
+on the old position of the ball.
+
+Cells's job is to make sure that last bit goes smoothly, which we will define now.
+
+Suppose the system has reached the stable, valid state reached after
+autoinitialization of the initial model population...we'll worry about initialization
+ later. I like to think of a change to a variable such as the window's width as
+a /data pulse/, or /pulse/ for short. If we enumerate these pulses sequentially,
+we can state the Prime Directive of Cells as:
+
+ take a system gets from pulse n to n+1 smoothly.
+
+To handle concurrency, we can instead stamp pulses with the time. Then we can speak
+of time T and T+1, which will be time stamps such that no pulse known to the system
+has a time stamp between T and T+1. (Where we have concurrency and network latency,
+some regulating scheme will have to be found to make sure everyone has had a chance
+to "share" before T+1 is decided, given T and a new set of pulses. Let's duck that
+for now and assume a single thread in which each pulse also moves T to T+1.) Now
+we can restate the Cells manifesto:
+
+ take a system from time T to time T+1 smoothly
+
+Your next question should be, what does "smoothly" mean? First, some formal definitions.
+
+Let's call the slot changed by the triggering pulse X, as in "X marks the spot" where
+the system perturbation began. X might be the mouse position as fed to the application
+by the operating system.
+
+Now let's talk of Cells being "at" some time Tn or other. Time starts at T0. The application
+makes its first model instances and brings that cluster to life, sweeping the cluster
+evaluating ruled cells. Eventually they have all been computed, and we are at T1. After this
+everything is Tn or Tn+1.
+
+-- When a pulse Pn+1 occurs, it takes the system from Tn to Tn+1.
+
+Now suppose P is a change to slot X, the mouse position of some "system" instance we
+are using to model the application environment.
+
+-- We say slot X is now "at" time Tn+1, because it trivially reflects the value of Pn+1
+
+If another cell happens to have used X in its most recent calculation, it needs to be
+recalculated. Once it is recalculated, we say it too has reached Tn+1. And if any Cell
+did not involve in its calculation X, directly or indirectly through some other cell,
+then we also think of it as being at time T+1. It is current with pulse Pn+1 because
+Pn+1 changes nothing of relevance to it.
+
+With those definitions in mind, here are the qualities of a smooth
+transition from T to T+1:
+
+(1) Completeness: everyone gets to Tn+1: every internal calculation affected directly or
+indirectly by X will be recalculated.
+
+(1a) Completeness: any and only those Cs which actually change in value getting from Cn to Cn+1
+will have that change echoed.
+
+(2) Efficiency: only those calculations will execute. There is no reason to run a rule
+if nothing affecting its outcome has changed.
+
+(2a) Efficiency: a calculation is affected by a transition of some cell to Tn+1
+iff Cn+1 is different from Cn. ie, if X actually changes and some cell A which uses
+it dutifully recalculates but comes up with the same result (it might involve a min or
+max function), then some other cell B which uses A does not need to be recalculated.
+
+(3) Simplicity: calculations will run only once (no re-entrance). More efficient as well.
+This may seem obvious, but certain engineering attempts have resulted in reentrance.
+But then one has to worry about backtracking. The idea is to make
+programming easier, so we won't ask developers to worry about re-entrance. Not
+that we are encouraging side-effects in Cell rules. Anyway....
+
+(4) Consistency: no rule when it runs will access any cell not already at T+1.
+
+(5) Consistency II: akin to the first, no echo of n+1 will employ any data not at Tn+1.
+
+(6) Completeness II: Tn+2 does not happen until the transition to Tn+1 satisfies
+the above requirements.
+
+If we timestamp every Cell as it moves from Cn to Cn+1, it all just works if we
+move Tn to Tn+1 and follow the above requirements.
+
+First, Tn+1 was reached by X itself receiving pulse N+1 and becoming Xn+1.
+
+Rule 2 requires us to determine if pulse N+1 actually change X. In the case of
+a window being resized only vertically, the reshape event will include a "new"
+value for width which is the same as the old.
+
+If X turns out not to have changed, we do not move time to Tn+1. Efficiencies 2 and 2a.
+
+But if X has changed, we now have Tn+1 and X reaches Xn+1 trivially.
+
+Now rule 1 requires us to recalculate all of X's users, and if one of
+those changes, likewise notify their users. Eventually everyone gets notified, so
+we look good on Rule 1.
+
+But now we have a problem. What if A and B are users of X, but A also uses C which uses B?
+A's rule, when it runs, needs to see Cn+1 to satisfy rule 4. We cannot just run the rule
+for C because we do not know until B gets calculated whether /it/ will change. We know
+X has changed, but maybe B will come up with the same answer as before. In which case,
+by the definitions above, C is already Cn+1 and recalculating it would be a waste.
+
+The solution is a little tricky: descend the "used" links from C looking for X. When
+we come to a terminus (a c-variable which is not X), we flag that as being at n+1 and
+return nil. If at any ruled node all useds return nil, we flag the ruled cell as
+being at n+1 and return nil.
+
+But where we get to X, we return T. Where a ruled node gets T back from any used Cell
+it kicks off its own calculation, returning T iff it changes. But before returning it
+echos. Should that echo involve some user-level read of some cell which is at Cn,
+accessor processing will include these safeguards which check to see if any used value
+is at Tn+1 and recalculate "just in time". This means we need a special variable which
+indicates when data pulse propagation is underway:
+
+ (let ((*propagating* (setf *time* (get-internal-real-time))))....
+
+That way if *propagating* is false there is no need to do anything but return valid
+values.
+
+Anyway, it looks as if echo requirements can be satisfied, and that completes the
+picture. But we have a problem. If some cell H (for high up in the dependency graph)
+uses both A and C, it is possible for X to tell A to recalculate, which will lead
+to A asking C to recalculate, which will do so and tell H to recalculate, which will
+ask A for its current value. Deadlock, and again this cannot be detected via lookahead
+because H's rule may not branch to A until just this pulse.
+
+The trick is that all we need from C when it gets accessed is its value. yes, we can tell
+now that H must be recalculated at some point, but A has not gone after H and will not
+so recalculating H can wait. If A /does/ go after H the above framework will see to
+it that H gets recalculated. But in this case H can wait (but not be forgotten).
+
+So we simply add H to a fifo queue of deferred dependencies to be revisited before
+Tn+1 can be considered attained.
+
+
+
+|#
+
Added: dependencies/trunk/cells/doc/cells-overview.pdf
==============================================================================
Binary files (empty file) and dependencies/trunk/cells/doc/cells-overview.pdf Tue Jan 26 15:20:07 2010 differ
Added: dependencies/trunk/cells/doc/hw.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/doc/hw.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,72 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+
+(in-package :cells)
+
+(defmodel computer ()
+ ((hear :cell :ephemeral :accessor hear :initform (c-in nil))
+ (salutation :initarg :salutation :accessor salutation :initform "hello")
+ (response :initform nil :initarg :response
+ :unchanged-if string= :accessor response)))
+
+(def-c-output response ()
+ (when new-value
+ (format t "~&hear: ~a~%respond: ~a" (hear self) new-value)))
+
+(defun hello-world ()
+ (cell-reset)
+ (let ((system (make-instance 'computer
+ :response (c? (let ((r (case (hear self)
+ (:knock-knock "who's there?")
+ (:world (concatenate 'string
+ (salutation self)
+ ", "
+ (string (hear self))))
+ ((nil) "<silence>"))))
+ (if (string= r .cache)
+ (format nil "i said, \"~a\"" r)
+ r))))))
+ (format t "~&to-be initialization complete")
+ (setf (hear system) :knock-knock)
+ (setf (hear system) :knock-knock)
+ (setf (hear system) :world)
+ (setf (salutation system) "hiya")
+ (values)))
+
+#+(or)
+(hello-world)
+
+#| output
+
+hear: nil
+respond: <silence>
+hear: knock-knock
+respond: who's there?
+hear: knock-knock
+respond: i said, "who's there?"
+hear: world
+respond: hello, world
+
+|#
+
Added: dependencies/trunk/cells/doc/motor-control.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/doc/motor-control.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,157 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells -*-
+;;;
+;;; Copyright © 2004 by Bill Clementson
+;;;
+;;; Reprinted, reformatted, and modestly revised by permission.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+#|
+
+Experimenting with Cells
+----------------------------
+Thursday, September 11, 2003
+
+Kenny Tilton has been talking about his Cells implementation on comp.lang.lisp for some time
+but I've only just had a look at it over the past few evenings. It's actually pretty neat.
+Kenny describes Cells as, conceptually, analogous to a spreadsheet cell (e.g. -- something
+in which you can put a value or a formula and have it updated automatically based on changes
+in other "cell" values). Another way of saying this might be that Cells allows you to define
+classes whose slots can be dynamically (and automatically) updated and for which standard
+observers can be defined that react to changes in those slots.
+
+Hmmm, maybe an example works best. Here's one that's a variation on one of the examples
+included in the latest distribution. I'll create a "motor" object that reacts to changes
+in the motor's operating temperature. If the temperature exceeds 100 degrees, the motor will
+need to be shut off. If it is shut off, the flow from the fuel pump will also need to be
+closed (otherwise, we get a big pool of fuel on the floor).
+
+So, by using Cells in this example, the following will be demonstrated:
+
+ * Create slots whose values vary based on a formula. The formula can be defined at
+ either class definition time or at object instantiation time.
+
+ * Dynamically (and automatically) update dependent slot variables (maintaining consistency
+ between dependent class attributes).
+
+ * Create Observers that react to changes in slot values to handle "external"
+ actions (e.g. - GUI updates, external API calls, etc.).
+
+ * Automatically filter slot changes so that we only update dependent slots
+ when the right granularity of change occurs.
+
+First, define the motor class (Note: defmodel is a macro that wraps a class
+definition and several method definitions):
+|#
+
+(in-package :cells)
+
+(defmodel motor ()
+ ((status :initarg :status :accessor status :initform nil)
+ (fuel-pump :initarg :fuel-pump :accessor fuel-pump
+ :initform (c? (ecase (^status) (:on :open) (:off :closed))))
+ (temp :initarg :temp :accessor temp :initform (c-in 0))))
+
+#+test
+(progn
+ (cells-reset)
+ (setf (status (make-instance 'motor :status :on)) 42))
+
+#|
+
+Note that "status" is a cell with no initial value or formula, "fuel-pump" is
+a cell that has a formula that depends on the value of "status" (the ^status notation
+is shorthand to refer to a slot in the same instance), and "temp" is initialized to zero.
+
+Next, define observers (this is an optional step) using a Cells macro.
+These observers act on a change in a slot's value. They don't actually update
+any dependent slots (this is done automatically by Cells and the programmer
+doesn't have to explicitly call the slot updates), they just provide a mechanism
+for the programmer to handle outside dependencies. In this example, we're just
+printing a message; however, in a real program, we would be calling out to something
+like an Allen Bradley controller to turn the motor and fuel pump on/off.
+
+|#
+
+(defobserver status ((self motor))
+ (trc "motor status changing from" old-value :to new-value))
+
+(defobserver fuel-pump ((self motor))
+ (trc "motor fuel-pump changing from" old-value :to new-value))
+
+(defobserver temp ((self motor))
+ (trc "motor temperature changing from" old-value :to new-value))
+
+#|
+
+Then, create an instance of the motor. Note that we programmatically assign
+a formula to the "status" slot. The formula states that when the temperature
+rises above 100 degrees, we change the status to "off". Since the temperature may
+fluctuate around 100 degrees a bit before it moves decisively one way or
+the other (and we don't want the motor to start turning off and on as we get
+minor temperature fluctuations around the 100 degree mark), we use another
+Cells feature ("Synapses" allow for pre-defined filters to be applied to a
+slot's value before it is used to update other slots) to filter the temperatures
+for small variations. Note that the formula is being assigned to the "status"
+slot at instantiation time as this gives us the ability to create different
+formulas for different types of motors without subclassing "motor".
+
+|#
+
+#+evaluatethis
+
+(defparameter *motor1*
+ (make-instance 'motor
+ :status (c? (if (< (f-sensitivity :tmp (0.05) (^temp)) 100)
+ :on :off))))
+
+#|
+
+This alone produces the following results as the Cells engine gets the motor
+instance fully active, which requires getting the real-world motor
+in synch with the CLOS instance:
+
+0> motor status changing from | NIL | :TO :ON
+0> motor fuel-pump changing from | NIL | :TO :OPEN
+0> motor temperature changing from | NIL | :TO 0
+
+Then we test the operation of the motor by changing the motor's
+temperature (starting at 99 degrees and increasing it by 1 degree +/- a small random variation).
+
+|#
+
+#+evaluatethis
+
+(dotimes (x 2)
+ (dotimes (y 10)
+ (let ((newtemp (+ 99 x (random 0.07) -.02)))
+ (setf (temp *motor1*) newtemp))))
+
+#|
+
+This produces the following results, which will vary from run to run because of
+the use of a random amount to simulate real-world variability:
+
+0> motor temperature changing from NIL :TO 0
+0> motor temperature changing from 0 :TO 98.99401
+0> motor temperature changing from 98.99401 :TO 99.01954
+[snipped 8 intermediate readings]
+0> motor temperature changing from 99.00016 :TO 100.00181
+0> motor status changing from :ON :TO :OFF
+0> motor fuel-pump changing from :OPEN :TO :CLOSED
+0> motor temperature changing from 100.00181 :TO 100.0177
+0> motor temperature changing from 100.0177 :TO 99.98742
+0> motor temperature changing from 99.98742 :TO 99.99313
+[snipped 6 intermediate readings]
+
+Notice how the fsensitivity synapse prevents minor fluctuations around 100 degrees
+from causing the motor to start turning itself on and off in rapid succession,
+possibly causing it to flood or fail in some way.
+
+|#
\ No newline at end of file
Added: dependencies/trunk/cells/family-values.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/family-values.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,96 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(family-values family-values-sorted
+ sort-index sort-direction sort-predicate sort-key
+ ^sort-index ^sort-direction ^sort-predicate ^sort-key)))
+
+(defmodel family-values (family)
+ (
+ (kv-collector :initarg :kv-collector
+ :initform #'identity
+ :reader kv-collector)
+
+ (kid-values :initform (c? (when (kv-collector self)
+ (funcall (kv-collector self) (^value))))
+ :accessor kid-values
+ :initarg :kid-values)
+
+ (kv-key :initform #'identity
+ :initarg :kv-key
+ :reader kv-key)
+
+ (kv-key-test :initform #'equal
+ :initarg :kv-key-test
+ :reader kv-key-test)
+
+ (kid-factory :initform #'identity
+ :initarg :kid-factory
+ :reader kid-factory)
+
+ (.kids :initform (c? (c-assert (listp (kid-values self)))
+ (let ((new-kids (mapcan (lambda (kid-value)
+ (list (or (find kid-value .cache
+ :key (kv-key self)
+ :test (kv-key-test self))
+ (trc nil "family-values forced to make new kid"
+ self .cache kid-value)
+ (funcall (kid-factory self) self kid-value))))
+ (^kid-values))))
+ (nconc (mapcan (lambda (old-kid)
+ (unless (find old-kid new-kids)
+ (when (fv-kid-keep self old-kid)
+ (list old-kid))))
+ .cache)
+ new-kids)))
+ :accessor kids
+ :initarg :kids)))
+
+(defmethod fv-kid-keep (family old-kid)
+ (declare (ignorable family old-kid))
+ nil)
+
+(defmodel family-values-sorted (family-values)
+ ((sorted-kids :initarg :sorted-kids :accessor sorted-kids
+ :initform nil)
+ (sort-map :initform (c-in nil) :initarg :sort-map :accessor sort-map)
+ (.kids :initform (c? (c-assert (listp (kid-values self)))
+ (mapsort (^sort-map)
+ (the-kids
+ (mapcar (lambda (kid-value)
+ (trc "making kid" kid-value)
+ (or (find kid-value .cache :key (kv-key self) :test (kv-key-test self))
+ (trc nil "family-values forced to make new kid" self .cache kid-value)
+ (funcall (kid-factory self) self kid-value)))
+ (^kid-values)))))
+ :accessor kids
+ :initarg :kids)))
+
+(defun mapsort (map data)
+ ;;(trc "mapsort map" map)
+ (if map
+ (stable-sort data #'< :key (lambda (datum) (or (position datum map)
+ ;(trc "mapsort datum not in map" datum)
+ (1+ (length data)))))
+ data))
+
+(defobserver sorted-kids ()
+ (setf (sort-map self) new-value)) ;; cellular trick to avoid cyclicity
\ No newline at end of file
Added: dependencies/trunk/cells/family.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/family.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,264 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(eval-when (:compile-toplevel :execute :load-toplevel)
+ (export '(model value family dbg .pa
+ kids kid1 ^k1 kid2 ^k2 last-kid ^k-last perishable)))
+
+(defmodel model ()
+ ((.md-name :cell nil :initform nil :initarg :md-name :accessor md-name)
+ (.fm-parent :cell nil :initform nil :initarg :fm-parent :accessor fm-parent)
+ (.dbg-par :cell nil :initform nil)
+ (.value :initform nil :accessor value :initarg :value)
+ (register? :cell nil :initform nil :initarg :register? :reader register?)
+ (zdbg :initform nil :accessor dbg :initarg :dbg)))
+
+(defmethod not-to-be :around ((self model))
+ (setf (slot-value self '.dbg-par) (fm-parent self)) ;; before it gets zapped
+ (call-next-method))
+
+(defmethod initialize-instance :after ((self model) &key)
+ (when (register? self)
+ (fm-register self)))
+
+(defmethod print-cell-object ((md model))
+ (or (md-name md) :md?))
+
+(defmethod fm-parent (other)
+ (declare (ignore other))
+ nil)
+
+(defmethod (setf fm-parent) (new-value other)
+ (declare (ignore other))
+ new-value)
+
+(defmethod print-object ((self model) s)
+ #+shhh (format s "~a" (type-of self))
+ (format s "~a~a" (if (mdead self) "DEAD!" "")
+ (or (md-name self) (type-of self))))
+
+(define-symbol-macro .parent (fm-parent self))
+(define-symbol-macro .pa (fm-parent self))
+
+(defmethod md-name (other)
+ (trc "yep other md-name" other (type-of other))
+ other)
+
+(defmethod md-name ((nada null))
+ (unless (c-stopped)
+ (c-stop :md-name-on-null)
+ (break "md-name called on nil")))
+
+(defmethod md-name ((sym symbol)) sym)
+
+(defmethod shared-initialize :around ((self model) slotnames &rest initargs &key fm-parent)
+ (declare (ignorable initargs slotnames fm-parent))
+
+ (call-next-method)
+
+ (when (slot-boundp self '.md-name)
+ (unless (md-name self)
+ (setf (md-name self) (gentemp (string (c-class-name (class-of self)))))))
+
+ (when (and (slot-boundp self '.fm-parent)
+ (fm-parent self)
+ (zerop (adopt-ct self)))
+ (md-be-adopted self)))
+
+(defmodel perishable ()
+ ((expiration :initform nil :accessor expiration :initarg :expiration)))
+
+(defobserver expiration ()
+ (when new-value
+ (not-to-be self)))
+
+(defvar *parent* nil)
+
+(defmodel family (model)
+ ((.kid-slots :cell nil
+ :initform nil
+ :accessor kid-slots
+ :initarg :kid-slots)
+ (.kids :initform (c-in nil) ;; most useful
+ :owning t
+ :accessor kids
+ :initarg :kids)
+ (registry? :cell nil
+ :initform nil
+ :initarg :registry?
+ :accessor registry?)
+ (registry :cell nil
+ :initform nil
+ :accessor registry)))
+
+#+test
+(let ((c (find-class 'family)))
+ (mop::finalize-inheritance c)
+ (class-precedence-list c))
+
+(defmacro the-kids (&rest kids)
+ `(let ((*parent* self))
+ (packed-flat! , at kids)))
+
+(defmacro s-sib-no () `(position self (kids .parent)))
+
+(defmacro gpar ()
+ `(fm-grandparent self))
+
+(defmacro nearest (self-form type)
+ (let ((self (gensym)))
+ `(bwhen (,self ,self-form)
+ (if (typep ,self ',type) ,self (upper ,self ,type)))))
+
+(defun kid1 (self) (car (kids self)))
+
+(export! first-born-p)
+(defun first-born-p (self)
+ (eq self (kid1 .parent)))
+
+(defun kid2 (self) (cadr (kids self)))
+(defmacro ^k1 () `(kid1 self))
+(defmacro ^k2 () `(kid2 self))
+
+(defun last-kid (self) (last1 (kids self)))
+(defmacro ^k-last () `(last-kid self))
+
+;; /// redundancy in following
+
+(defmacro psib (&optional (self-form 'self))
+ (let ((self (gensym)))
+ `(bwhen (,self ,self-form)
+ (find-prior ,self (kids (fm-parent ,self))))))
+
+(defmacro nsib (&optional (self-form 'self))
+ (let ((self (gensym)))
+ `(bwhen (,self ,self-form)
+ (cadr (member ,self (kids (fm-parent ,self)))))))
+
+(defun prior-sib (self)
+ (let ((kid (gensym)))
+ `(let ((,kid ,self))
+ (find-prior ,kid (kids (fm-parent ,kid))))))
+
+(defun md-be-adopted (self &aux (fm-parent (fm-parent self)) (selftype (type-of self)))
+ (c-assert self)
+ (c-assert fm-parent)
+ (c-assert (typep fm-parent 'family))
+
+ (trc nil "md be adopted >" :kid self (adopt-ct self) :by fm-parent)
+
+ (when (plusp (adopt-ct self))
+ (c-break "2nd adopt ~a, by ~a" self fm-parent))
+
+ (incf (adopt-ct self))
+ (trc nil "getting adopted" self :by fm-parent)
+ (bwhen (kid-slots-fn (kid-slots (fm-parent self)))
+ (dolist (ks-def (funcall kid-slots-fn self) self)
+ (let ((slot-name (ks-name ks-def)))
+ (trc nil "got ksdef " slot-name (ks-if-missing ks-def))
+ (when (md-slot-cell-type selftype slot-name)
+ (trc nil "got cell type " slot-name )
+ (when (or (not (ks-if-missing ks-def))
+ (and (null (c-slot-value self slot-name))
+ (null (md-slot-cell self slot-name))))
+ (trc nil "ks missing ok " slot-name)
+ (multiple-value-bind (c-or-value suppressp)
+ (funcall (ks-rule ks-def) self)
+ (unless suppressp
+ (trc nil "md-install-cell " slot-name c-or-value)
+ (md-install-cell self slot-name c-or-value)))))))))
+
+(defobserver .kids ((self family) new-kids old-kids)
+ (c-assert (listp new-kids) () "New kids value for ~a not listp: ~a ~a" self (type-of new-kids) new-kids)
+ (c-assert (listp old-kids))
+ (c-assert (not (member nil old-kids)))
+ (c-assert (not (member nil new-kids)))
+ (bwhen (sample (find-if-not 'fm-parent new-kids))
+ (c-break "New as of Cells3: parent must be supplied to make-instance of ~a kid ~a"
+ (type-of sample) sample))
+ (trc nil ".kids output > entry" new-kids (mapcar 'fm-parent new-kids)))
+
+(defmethod kids ((other model-object)) nil)
+
+
+
+;------------------ kid slotting ----------------------------
+;
+(defstruct (kid-slotdef
+ (:conc-name nil))
+ ks-name
+ ks-rule
+ (ks-if-missing t))
+
+(defmacro mk-kid-slot ((ks-name &key if-missing) ks-rule)
+ `(make-kid-slotdef
+ :ks-name ',ks-name
+ :ks-rule (lambda (self)
+ (declare (ignorable self))
+ ,ks-rule)
+ :ks-if-missing ,if-missing))
+
+(defmacro def-kid-slots (&rest slot-defs)
+ `(lambda (self)
+ (declare (ignorable self))
+ (list , at slot-defs)))
+
+; --- registry "namespacing" ---
+
+(defmethod registry? (other) (declare (ignore other)) nil)
+
+(defmethod initialize-instance :after ((self family) &key)
+ (when (registry? self)
+ (setf (registry self) (make-hash-table :test 'eq))))
+
+(defmethod fm-register (self &optional (guest self))
+ (assert self)
+ (if (registry? self)
+ (progn
+ ;(trc "fm-registering" (md-name guest) :with self)
+ (setf (gethash (md-name guest) (registry self)) guest))
+ (fm-register (fm-parent self) guest)))
+
+(defmethod fm-check-out (self &optional (guest self))
+ (assert self () "oops ~a ~a ~a" self (fm-parent self) (slot-value self '.fm-parent))
+ (if (registry? self)
+ (remhash (md-name guest) (registry self))
+ (bif (p (fm-parent self))
+ (fm-check-out p guest)
+ (break "oops ~a ~a ~a" self (fm-parent self) (slot-value self '.fm-parent)))))
+
+(defmethod fm-find-registered (id self &optional (must-find? self must-find?-supplied?))
+ (or (if (registry? self)
+ (gethash id (registry self))
+ (bwhen (p (fm-parent self))
+ (fm-find-registered id p must-find?)))
+ (when (and must-find? (not must-find?-supplied?))
+ (break "fm-find-registered failed seeking ~a starting search at node ~a" id self))))
+
+(export! rg? rg!)
+
+(defmacro rg? (id)
+ `(fm-find-registered ,id self nil))
+
+(defmacro rg! (id)
+ `(fm-find-registered ,id self))
+
+
+
\ No newline at end of file
Added: dependencies/trunk/cells/fm-utilities.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/fm-utilities.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,735 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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.
+
+$Header: /project/cells/cvsroot/cells/fm-utilities.lisp,v 1.22 2008-10-12 01:21:07 ktilton Exp $
+|#
+
+
+(in-package :cells)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export
+ '(;; Family member creation
+ make-part
+ mk-part
+ mk-part-spec
+ upper
+ u^
+ container
+ container-typed
+
+ ;; Family member finding
+ fm-descendant-typed
+ fm-ascendant-typed
+ fm-kid-named
+ fm-descendant-named
+ fm-ascendant-named
+ fm-ascendant-some
+ fm-ascendant-if
+ fm-descendant-if
+ fm-descendant-common
+ fm-collect-if
+ fm-collect-some
+ fm-value-dictionary
+ fm-max
+ fm-traverse
+ fm-traverse-bf
+ fm-ordered-p
+ sub-nodes
+ fm-ps-parent
+ with-like-fm-parts
+ do-like-fm-parts
+ true-that
+ fm-do-up
+ fm-gather
+ fm-find-all
+ fm-find-next
+ fm-find-next-within
+ fm-find-prior
+ fm-find-prior-within
+ fm-find-last-if
+ fm-prior-sib
+ fm-next-sib-if
+ fm-next-sib
+ ^fm-next-sib
+ fm-find-if
+
+ ;; Family ordering
+ fm-kid-add
+ fm-kid-insert-last
+ fm-kid-insert-first
+ fm-kid-insert
+ fm-kid-remove
+ fm-quiesce-all
+ fm-kid-replace
+
+ ;; Family high-order ops
+ fm-min-kid
+ fm-max-kid
+ fm-other
+ fmv
+ fm-otherx
+ fm-other-v
+ fm-otherv?
+ fm-other?
+ fm-other!
+ fm^
+ fm?
+ fm!
+ fm!v
+ fm-other?!
+ fm-collect
+ fm-map
+ fm-mapc
+ fm-pos
+ fm-count-named
+ fm-top
+ fm-first-above
+ fm-nearest-if
+ fm-includes
+ fm-ancestor-p
+ fm-kid-containing
+ fm-ascendant-p
+ fm-find-one
+ fm-find-kid
+ fm-kid-typed
+
+ ;; Other family stuff
+ make-name
+ name-root
+ name-subscript
+ kid-no
+
+ ;; Debug flags
+ *fmdbg*
+
+ )))
+
+(defparameter *fmdbg* nil)
+
+(defun make-part (partname part-class &rest initargs)
+ ;;(trc "make-part > name class" partname partclass)
+ (when part-class ;;a little programmer friendliness
+ (apply #'make-instance part-class (append initargs (list :md-name partname)))))
+
+(defmacro mk-part (md-name (md-class) &rest initargs)
+ `(make-part ',md-name ',md-class , at initargs
+ :fm-parent (progn (assert self) self)))
+
+(defmethod make-part-spec ((part-class symbol))
+ (make-part part-class part-class))
+
+(defmethod make-part-spec ((part model))
+ part)
+
+
+(defmacro upper (self &optional (type t))
+ `(container-typed ,self ',type))
+
+(defmacro u^ (type)
+ `(upper self ,type))
+
+(defmethod container (self) (fm-parent self))
+
+;;;(defmethod container-typed ((self model-object) type)
+;;; (let ((parent (container self))) ;; fm- or ps-parent
+;;; (cond
+;;; ((null parent) nil)
+;;; ((typep parent type) parent)
+;;; (t (container-typed parent type)))))
+
+(defmethod container-typed ((self model-object) type)
+ (let ((parent (fm-parent self))) ;; fm- or ps-parent
+ (cond
+ ((null parent) nil)
+ ((typep parent type) parent)
+ (t (container-typed parent type)))))
+
+(defun fm-descendant-typed (self type)
+ (when self
+ (or (find-if (lambda (k) (typep k type)) (kids self))
+ (some (lambda (k)
+ (fm-descendant-typed k type)) (kids self)))))
+
+(defun fm-kid-named (self name)
+ (find name (^kids) :key 'md-name))
+
+(defun fm-descendant-named (parent name &key (must-find t))
+ (fm-find-one parent name :must-find must-find :global-search nil))
+
+(defun fm-ascendant-named (parent name)
+ (when parent
+ (or (when (eql (md-name parent) name)
+ parent)
+ (fm-ascendant-named (fm-parent parent) name))))
+
+(defun fm-ascendant-typed (parent name)
+ (when parent
+ (or (when (typep parent name)
+ parent)
+ (fm-ascendant-typed (fm-parent parent) name))))
+
+(defun fm-ascendant-some (parent some-function)
+ (when (and parent some-function)
+ (or (funcall some-function parent)
+ (fm-ascendant-some (fm-parent parent) some-function))))
+
+(defun fm-ascendant-if (self test)
+ (when (and self test)
+ (or (when (funcall test self)
+ self)
+ (fm-ascendant-if .parent test))))
+
+(defun fm-descendant-if (self test)
+ (when (and self test)
+ (or (when (funcall test self)
+ self)
+ (loop for k in (^kids)
+ thereis (fm-descendant-if k test)))))
+
+(defun fm-ascendant-common (d1 d2)
+ (fm-ascendant-some d1 (lambda (node)
+ (when (fm-includes node d2)
+ node))))
+
+(defun fm-collect-if (tree test &optional skip-top dependently)
+ (let (collection)
+ (fm-traverse tree (lambda (node)
+ (unless (and skip-top (eq node tree))
+ (when (funcall test node)
+ (push node collection))))
+ :with-dependency dependently)
+ (nreverse collection)))
+
+(defun fm-collect-some (tree test &optional skip-top dependently)
+ (let (collection)
+ (fm-traverse tree (lambda (node)
+ (unless (and skip-top (eq node tree))
+ (bwhen (s (funcall test node))
+ (push s collection))))
+ :with-dependency dependently)
+ (nreverse collection)))
+
+(defun fm-value-dictionary (tree value-fn &optional include-top)
+ (let (collection)
+ (fm-traverse tree
+ (lambda (node)
+ (when (or include-top (not (eq node tree)))
+ (bwhen (v (funcall value-fn node))
+ (push (cons (md-name node) v) collection)))))
+ (nreverse collection)))
+
+(defun fm-max (tree key)
+ (let (max)
+ (fm-traverse tree (lambda (node)
+ (if max
+ (setf max (max max (funcall key node)))
+ (setf max (funcall key node)))))
+ max))
+
+
+(defun fm-traverse (family applied-fn &key skip-node skip-tree global-search opaque with-dependency)
+ ;;(when *fmdbg* (trc "fm-traverse" family skipTree skipNode global-search))
+
+ (when family
+ (labels ((tv-family (fm)
+ (etypecase fm
+ (cons (loop for md in fm do (tv-family md)))
+ (model-object
+ (unless (eql fm skip-tree)
+ (let ((outcome (and (not (eql skip-node fm)) ;; skipnode new 990310 kt
+ (funcall applied-fn fm))))
+ (unless (and outcome opaque)
+ (dolist (kid (kids fm))
+ (tv-family kid))
+ ;(tv-family (mdValue fm))
+ )))))))
+ (flet ((tv ()
+ (tv-family family)
+ (when global-search
+ (fm-traverse (fm-parent family) applied-fn
+ :global-search t
+ :skip-tree family
+ :skip-node skip-node
+ :with-dependency t)))) ;; t actually just defaults to outermost call
+ (if with-dependency
+ (tv)
+ (without-c-dependency (tv))))))
+ (values))
+
+(defun fm-traverse-bf (family applied-fn &optional (cq (make-fifo-queue)))
+ (when family
+ (flet ((process-node (fm)
+ (funcall applied-fn fm)
+ (when (kids fm)
+ (fifo-add cq (kids fm)))))
+ (process-node family)
+ (loop for x = (fifo-pop cq)
+ while x
+ do (mapcar #'process-node x)))))
+
+#+test-bf
+(progn
+ (defmd bftree (family)
+ (depth 0 :cell nil)
+ (id (c? (klin self)))
+ :kids (c? (when (< (depth self) 4)
+ (loop repeat (1+ (depth self))
+ collecting (make-kid 'bftree :depth (1+ (depth self)))))))
+
+ (defun klin (self)
+ (when self
+ (if .parent
+ (cons (kid-no self) (klin .parent))
+ (list 0))))
+
+ (defun test-bf ()
+ (let ((self (make-instance 'bftree)))
+ (fm-traverse-bf self
+ (lambda (node)
+ (print (id node)))))))
+
+(defun fm-ordered-p (n1 n2 &aux (top (fm-ascendant-common n1 n2)))
+ (assert top)
+ (fm-traverse top (lambda (n)
+ (cond
+ ((eq n n1)(return-from fm-ordered-p t))
+ ((eq n n2)(return-from fm-ordered-p nil))))))
+
+
+(defmethod sub-nodes (other)
+ (declare (ignore other)))
+
+(defmethod sub-nodes ((self family))
+ (kids self))
+
+(defmethod fm-ps-parent ((self model-object))
+ (fm-parent self))
+
+(defmacro with-like-fm-parts ((parts-var (self like-class)) &body body)
+ `(let (,parts-var)
+ (fm-traverse ,self (lambda (node)
+ ;;(trc "with like sees node" node (type-of node) ',likeclass)
+ (when (typep node ',like-class)
+ (push node ,parts-var)))
+ :skip-node ,self
+ :opaque t)
+ (setf ,parts-var (nreverse ,parts-var))
+ (progn , at body)))
+
+(defmacro do-like-fm-parts ((part-var (self like-class) &optional return-var) &body body)
+ `(progn
+ (fm-traverse ,self (lambda (,part-var)
+ (when (typep ,part-var ',like-class)
+ , at body))
+ :skip-node ,self
+ :opaque t)
+ ,return-var)
+ )
+
+;;
+;; family member finding
+;;
+
+
+#|
+ (defun fm-member-named (kidname kids)
+ (member kidname kids :key #'md-name))
+ |#
+
+(defun true-that (that) (declare (ignore that)) t)
+;;
+;; eventually fm-find-all needs a better name (as does fm-collect) and they
+;; should be modified to go through 'gather', which should be the real fm-find-all
+;;
+
+(defun fm-do-up (self &optional (fn 'identity))
+ (when self
+ (funcall fn self)
+ (if .parent (fm-do-up .parent fn) self))
+ (values))
+
+(defun fm-gather (family &key (test #'true-that))
+ (packed-flat!
+ (cons (when (funcall test family) family)
+ (mapcar (lambda (fm)
+ (fm-gather fm :test test))
+ (kids family)))))
+
+(defun fm-find-all (family md-name &key (must-find t) (global-search t))
+ (let ((matches (catch 'fm-find-all
+ (with-dynamic-fn
+ (traveller (family)
+ (with-dynamic-fn
+ (filter (kid) (eql md-name (md-name kid)))
+ (let ((matches (remove-if-not filter (kids family))))
+ (when matches
+ (throw 'fm-find-all matches)))))
+ (fm-traverse family traveller :global-search global-search)))))
+ (when (and must-find (null matches))
+ (setf *stop* t)
+ (fm-traverse family (lambda (node)
+ (trc "known node" (md-name node))) :global-search global-search)
+ (break "fm-find-all > *stop*ping...did not find ~a ~a ~a" family md-name global-search)
+ ;; (error 'fm-not-found (list md-name family global-search))
+ )
+ matches))
+
+(defun fm-find-next (fm test-fn)
+ (fm-find-next-within fm test-fn))
+
+(defun fm-find-next-within (fm test-fn &optional upperbound &aux (fm-parent (unless (eql upperbound fm)
+ (fm-parent fm))))
+ (let ((sibs (and fm-parent (rest (member fm (kids fm-parent))))))
+ (or (dolist (s sibs)
+ (let ((winner (fm-find-if s test-fn)))
+ (when winner (return winner))))
+ (if fm-parent
+ (fm-find-next-within fm-parent test-fn upperbound)
+ (fm-find-if fm test-fn)))))
+
+(defun fm-find-prior (fm test-fn)
+ (fm-find-prior-within fm test-fn))
+
+(defun fm-find-prior-within (fm test-fn &optional upperbound &aux (fm-parent (unless (eql upperbound fm)
+ (fm-parent fm))))
+ (let ((sibs (and fm-parent (kids fm-parent))))
+ (or (loop with next-ok
+ for s on sibs
+ for last-ok = nil then (or next-ok last-ok)
+ when (eql fm (first s)) do (loop-finish)
+ finally (return last-ok)
+ do (setf next-ok (fm-find-last-if (car s) test-fn)))
+ (if fm-parent
+ (fm-find-prior-within fm-parent test-fn upperbound)
+ (fm-find-last-if fm test-fn)))))
+
+ (defun fm-find-last-if (family test-fn)
+ (let ((last))
+ (or (and (kids family)
+ (dolist (k (kids family) last)
+ (setf last (or (fm-find-last-if k test-fn) last))))
+ (when (funcall test-fn family)
+ family))))
+
+(defun fm-prior-sib (self &optional (test-fn #'true-that))
+ "Find nearest preceding sibling passing TEST-FN"
+ (chk self 'psib)
+ (let ((kids (kids (fm-parent self))))
+ (find-if test-fn kids :end (position self kids) :from-end t)))
+
+(defun fm-next-sib-if (self test-fn)
+ (some test-fn (cdr (member self (kids (fm-parent self))))))
+
+(defun fm-next-sib (self)
+ (car (cdr (member self (kids (fm-parent self))))))
+
+(defmacro ^fm-next-sib (&optional (self 'self))
+ (let ((s (gensym)))
+ `(let ((,s ,self))
+ (car (cdr (member ,s (kids (fm-parent ,s))))))))
+
+(defun find-prior (self sibs &key (test #'true-that))
+ (c-assert (member self sibs) () "find-prior of ~a does not find it in sibs arg ~a" self sibs)
+ (unless (eql self (car sibs))
+ (labels
+ ((fpsib (rsibs &aux (psib (car rsibs)))
+ (c-assert rsibs () "find-prior > fpsib > self ~s not found to prior off" self)
+ (if (eql self (cadr rsibs))
+ (when (funcall test psib) psib)
+ (or (fpsib (cdr rsibs))
+ (when (funcall test psib) psib)))))
+ (fpsib sibs))))
+
+(defun fm-find-if (family test-fn &key skip-top-p) ;; 99-03 kt why is thsi depth-first?
+ (c-assert test-fn)
+ (when family
+ (or (dolist (b (sub-nodes family))
+ (let ((match (fm-find-if b test-fn)))
+ (when match (return match))))
+ (when (and (not skip-top-p)
+ (funcall test-fn family))
+ family))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; family ordering
+;;;;
+(defun fm-kid-add (fm-parent kid &optional before)
+ (c-assert (or (null (fm-parent kid)) (eql fm-parent (fm-parent kid))))
+ (c-assert (typep fm-parent 'family))
+ (setf (fm-parent kid) fm-parent)
+ (fm-kid-insert kid before))
+
+(defun fm-kid-insert-last (goal &aux (fm-parent (fm-parent goal)))
+ (setf (kids fm-parent) (nconc (kids fm-parent) (list goal))))
+
+(defun fm-kid-insert-first (goal &aux (fm-parent (fm-parent goal)))
+ (setf (kids fm-parent) (cons goal (kids fm-parent))))
+
+(defun fm-kid-insert (kid &optional before &aux (da-kids (kids (fm-parent kid))))
+ (c-assert (or (null before) (eql (fm-parent kid) (fm-parent before))))
+ (setf (kids (fm-parent kid))
+ (if before
+ (if (eql before (car da-kids))
+ (cons kid da-kids)
+ (let ((cell (member before da-kids)))
+ (rplaca cell kid)
+ (rplacd cell (cons before (cdr cell)))
+ (cons (car da-kids) (rest da-kids))))
+ (if da-kids
+ (progn
+ (rplacd (last da-kids) (cons kid nil))
+ (cons (car da-kids) (rest da-kids)))
+ (cons kid da-kids)))))
+
+(defun fm-kid-remove (kid &key (quiesce t) &aux (parent (fm-parent kid)))
+ (when quiesce
+ (fm-quiesce-all kid))
+ (when parent
+ (setf (kids parent) (remove kid (kids parent)))
+ ;; (setf (fm-parent kid) nil) gratuitous housekeeping caused ensuing focus output
+ ;; image-invalidate to fail since no access to containing window via fm-parent chain
+ ))
+
+(defun fm-quiesce-all (md)
+ (md-quiesce md)
+ (dolist (kid (kids md))
+ (fm-quiesce-all kid)))
+
+(defun fm-kid-replace (old-kid new-kid &aux (fm-parent (fm-parent old-kid)))
+ (c-assert (member old-kid (kids fm-parent)) ()
+ "~&oldkid ~s not amongst kids of its fm-parent ~s"
+ old-kid fm-parent)
+ (when fm-parent ;; silly test given above assert--which is right?
+ (c-assert (typep fm-parent 'family))
+ (setf (fm-parent new-kid) fm-parent)
+ (setf (kids fm-parent) (substitute new-kid old-kid (kids fm-parent)))
+ ;;(rplaca (member oldkid (kids fm-parent)) newkid)
+ new-kid))
+
+;----------------------------------------------------------
+;;
+;; h i g h - o r d e r f a m i l y o p s
+;;
+;; currently not in use...someday?
+;;
+
+
+(defun fm-min-kid (self slot-name)
+ (or (loop for k in (^kids)
+ minimizing (funcall slot-name k))
+ 0))
+(defun fm-max-kid (self slot-name)
+ (or (loop for k in (^kids)
+ maximizing (funcall slot-name k))
+ 0))
+
+(defmacro fm-other (md-name &key (starting 'self) skip-tree (test '#'true-that))
+ `(fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find t
+ :skip-tree ,skip-tree
+ :global-search t
+ :test ,test))
+
+(defmacro fmv (name)
+ `(value (fm-other ,name)))
+
+(defmacro fm-otherx (md-name &key (starting 'self) skip-tree)
+ (if (eql starting 'self)
+ `(or (fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find t
+ :skip-tree ,skip-tree
+ :global-search t))
+ `(fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find t
+ :skip-tree ,skip-tree
+ :global-search t)))
+
+(defun fm-other-v (md-name starting &optional (global-search t))
+ (fm-find-one starting md-name
+ :must-find nil
+ :global-search global-search))
+
+(defmacro fm-otherv? (md-name &optional (starting 'self) (global-search t))
+ `(fm-other-v ,md-name ,starting ,global-search))
+
+(defmacro fm-other? (md-name &optional (starting 'self) (global-search t))
+ `(fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find nil
+ :global-search ,global-search))
+
+(defun fm-other! (starting md-name &optional (global-search t))
+ (fm-find-one starting md-name
+ :must-find t
+ :global-search global-search))
+
+(defmacro fm^ (md-name &key (skip-tree 'self) (must-find t))
+ `(without-c-dependency
+ (fm-find-one (fm-parent self) ,md-name
+ :skip-tree ,skip-tree
+ :must-find ,must-find
+ :global-search t)))
+
+
+(export! fm^v)
+(defmacro fm^v (id)
+ `(value (fm^ ,id)))
+
+(defmacro fm? (md-name &optional (starting 'self) (global-search t))
+ `(fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find nil
+ :global-search ,global-search))
+
+(defmacro fm! (md-name &optional (starting 'self))
+ `(without-c-dependency
+ (fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find t
+ :global-search nil)))
+
+(defmacro fm!v (id)
+ `(value (fm! ,id)))
+
+(defmacro fm-other?! (md-name &optional (starting 'self))
+ `(fm-find-one ,starting ,(if (consp md-name)
+ `(list ',(car md-name) ,(cadr md-name))
+ `',md-name)
+ :must-find nil
+ :global-search nil))
+
+(defmacro fm-collect (md-name &key (must-find t))
+ `(fm-find-all self ',md-name :must-find ,must-find)) ;deliberate capture
+
+(defmacro fm-map (fn md-name)
+ `(mapcar ,fn (fm-find-all self ',md-name))) ;deliberate capture
+
+(defmacro fm-mapc (fn md-name)
+ `(mapc ,fn (fm-find-all self ',md-name))) ;deliberate capture
+
+(defun fm-pos (goal &aux (fm-parent (fm-parent goal)))
+ (when fm-parent
+ (or (position goal (kids fm-parent))
+ (length (kids fm-parent))))) ;; ?!!
+
+(defmacro fm-count-named (family md-name &key (global-search t))
+ `(length (fm-find-all ,family ,md-name
+ :must-find nil
+ :global-search ,global-search)))
+;---------------------------------------------------------------
+(defun fm-top (fm &optional (test #'true-that) &aux (fm-parent (fm-parent fm)))
+ (cond ((null fm-parent) fm)
+ ((not (funcall test fm-parent)) fm)
+ (t (fm-top fm-parent test))))
+
+(defun fm-first-above (fm &key (test #'true-that) &aux (fm-parent (fm-parent fm)))
+ (cond ((null fm-parent) nil)
+ ((funcall test fm-parent) fm-parent)
+ (t (fm-first-above fm-parent :test test))))
+
+(defun fm-nearest-if (test fm)
+ (when fm
+ (if (funcall test fm)
+ fm
+ (fm-nearest-if test (fm-parent fm)))))
+
+(defun fm-includes (fm sought)
+ (fm-ancestor-p fm sought))
+
+(defun fm-ancestor-p (fm sought)
+ (c-assert fm)
+ (when sought
+ (or (eql fm sought)
+ (fm-includes fm (fm-parent sought)))))
+
+(defun fm-kid-containing (fm-parent descendant)
+ (with-dynamic-fn (finder (node) (not (eql fm-parent node)))
+ (fm-top descendant finder)))
+
+;;; above looks confused, let's try again
+
+(defun fm-ascendant-p (older younger)
+ (cond
+ ((null (fm-parent younger)) nil)
+ ((eq older (fm-parent younger)) t)
+ (t (fm-ascendant-p older (fm-parent younger)))))
+
+(defun make-name (root &optional subscript)
+ (if subscript (list root subscript) root))
+
+(defun name-root (md-name)
+ (if (atom md-name) md-name (car md-name)))
+
+(defun name-subscript (md-name)
+ (when (consp md-name) (cadr md-name)))
+
+(defun fm-find-one (family md-name &key (must-find t)
+ (global-search t) skip-tree (test #'true-that)
+ &aux diag)
+ (count-it :fm-find-one)
+ (flet ((matcher (fm)
+ (when diag
+ (trc nil
+ "fm-find-one matcher sees name" (md-name fm) :ofthing (type-of fm) :seeking md-name global-search))
+ (when (and (eql (name-root md-name)(md-name fm))
+ (or (null (name-subscript md-name))
+ (eql (name-subscript md-name) (fm-pos fm)))
+ (progn
+ (when diag
+ (trc "fm-find-one testing" fm))
+ (funcall test fm)))
+ (throw 'fm-find-one fm))))
+ #-lispworks (declare (dynamic-extent matcher))
+ (trc nil "fm-find-one> entry " md-name family)
+ (let ((match (catch 'fm-find-one
+ (fm-traverse family #'matcher
+ :skip-tree skip-tree
+ :global-search global-search))))
+ (when (and must-find (null match))
+ (trc "fm-find-one > erroring fm-not-found, in family: " family :seeking md-name :global? global-search)
+ (setq diag t must-find nil)
+ (fm-traverse family #'matcher
+ :skip-tree skip-tree
+ :global-search global-search)
+ (c-break "fm-find-one > *stop*ping...did not find ~a ~a ~a" family md-name global-search)
+ )
+ match)))
+
+(defun fm-find-kid (self name)
+ (find name (kids self) :key #'md-name))
+
+(defun fm-kid-typed (self type)
+ (c-assert self)
+ (find type (kids self) :key #'type-of))
+
+(defun kid-no (self)
+ (unless (typep self 'model-object)
+ (break "not a model object ~a" self))
+ (when (and self (fm-parent self))
+ (c-assert (member self (kids (fm-parent self))))
+ (position self (kids (fm-parent self)))))
Added: dependencies/trunk/cells/gui-geometry/coordinate-xform.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/coordinate-xform.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,287 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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 :gui-geometry)
+
+(defconstant *reference-dpi* 1440)
+
+(let (
+ (logical-dpi 96) ;;1440)
+ ; This is cello's internal dots per inch. This value is germane only if size references are unqualified by a function call.
+ ; Size references should always be qualified, as in (:pts 6), except when specifying pen widths.
+ ; (Pen widths may pose a special case -- we may need to match screen pens to print pens.)
+
+ (scan-resolution 300)
+ ; This is the desired scan resolution, and the assumed resolution of all scans.
+ ; Hypothetically, a scanner not capable of scanning at 300 dpi could make a big hash of this scheme.
+ ; Rather than even pretend to support multiple resolutions within a study, for now we'll enforce 300 across the board.
+ ; Dependencies on this spec can be identified by searching on scan-resolution.
+
+ (logical-screen-resolution 96)
+ ; This is the internal logical screen resolution, which does _not_ have to equal the current LOGPIXELSX (LOGPIXELSY) value
+ ; reported by GetDeviceCaps. The original thought was that we could use this to rescale _all_ drawing on the fly. Now that
+ ; idea is being superseded by targetRes, but this functions (1) as a tacit targetRes for the outer window and (2) as a magic
+ ; number to complicate debugging [we need to root out a few references in .bmp drawing, I think].
+
+ ;;(printer-resolution 600) ; /// improve #'cs-printer-resolution to bypass this.
+
+ ;;(emf-resolution 600)
+
+ )
+
+ (declare (ignorable logical-dpi scan-resolution logical-screen-resolution printer-resolution))
+
+ ; Notice the somewhat nonstandard naming convention:
+ ; #'uInches takes logical inches and returns logical units (DPI)
+ ; so, for instance, if logical-dpi = 1440, then (uInches 0.5) = 720.
+ (defun u-round (number &optional (divisor 1))
+ (multiple-value-bind (quotient remainder)
+ (round number divisor)
+ (declare (ignorable remainder))
+ ;(assert (zerop remainder))
+ ;(assert (zerop (mod quotient 15))) ;96ths
+ quotient))
+
+
+ (defun udots (dots dpi)
+ (u-round (* dots logical-dpi) dpi)) ;only the first value will be used.
+
+ (defun uinches (logical-inches)
+ (u-round (* logical-inches logical-dpi))) ;only the first value will be used.
+
+ (defun uin (logical-inches)
+ (uinches logical-inches))
+
+ (defun upoints (logical-points)
+ (udots logical-points 72))
+
+ (defun upts (logical-points)
+ (upoints logical-points))
+
+ (defun u96ths (logical-96ths)
+ (udots logical-96ths 96))
+
+ (defun u8ths (logical-8ths)
+ (udots logical-8ths 8))
+
+ (defun u16ths (logical-16ths)
+ (udots logical-16ths 16))
+
+ (defun u32nds (logical-32nds)
+ (udots logical-32nds 32))
+
+ (defun u120ths (logical-120ths)
+ (udots logical-120ths 120))
+
+ (defun cs-logical-dpi ()
+ logical-dpi)
+
+ (defsetf cs-logical-dpi cs-logical-dpi-setf)
+
+ (defun cs-logical-dpi-setf (new-value)
+ (setf logical-dpi new-value))
+
+ (defun cs-scan-resolution ()
+ scan-resolution)
+
+ (defun cs-logical-screen-resolution ()
+ logical-screen-resolution)
+
+ )
+
+
+
+
+(defmethod u-cvt ((nn number) (units (eql :96ths)) )
+ (u96ths nn))
+
+(defmethod u-cvt ((nn number) (units (eql :8ths)) )
+ (u8ths nn))
+
+(defmethod u-cvt ((nn number) (units (eql :16ths)) )
+ (u16ths nn))
+
+(defmethod u-cvt ((nn number) (units (eql :32nds)) )
+ (u32nds nn))
+
+(defmethod u-cvt ((nn number) (units (eql :inches)) )
+ (uinches nn))
+
+(defmethod u-cvt ((nn number) (units (eql :points)) )
+ (upoints nn))
+
+(defmethod u-cvt (other units)
+ (declare (ignore units))
+ other)
+
+(defmethod u-cvt ((nns cons) units)
+ (cons (u-cvt (car nns) units)
+ (u-cvt (cdr nns) units)))
+
+(defmacro u-cvt! (nn units)
+ `(u-cvt ,nn ,units))
+
+(defun uv2 (x y u-key) (apply #'mkv2 (u-cvt (list x y) u-key)))
+
+;-----------------
+
+(defun os-logical-screen-dpi ()
+ (break "need (win:GetDeviceCaps (device-context (screen *cg-system*)) win:LOGPIXELSX))"))
+
+#+no(defun browser-target-resolution ()
+ (target-resolution (find-window :clinisys)))
+
+; set to 96 because the code is trying to do rect-frames for the header before the window is init'ed.
+
+(let ((current-target-resolution 96)) ;initialize when main window is created
+
+ (defun set-current-target-resolution (resolution)
+ #+shh(trc "setting current-target-resolution to" resolution)
+ (setf current-target-resolution resolution))
+
+ (defun cs-current-target-resolution ()
+ current-target-resolution)
+
+ (defun cs-target-res ()
+ current-target-resolution)
+
+ (defmacro with-target-resolution ((new-resolution) &rest body)
+ (let ((old-resolution (gensym))
+ )
+ `(let ((,old-resolution (cs-current-target-resolution))
+ )
+ (prog2
+ (set-current-target-resolution ,new-resolution)
+ (progn , at body)
+ (set-current-target-resolution ,old-resolution)
+ ))))
+ )
+
+
+;converts screen pixels to logical pixels given the current target resolution OR OPTIONAL OTHER RES
+(defun scr2log (dots &optional (target-res (cs-target-res)))
+ (round (* dots (cs-logical-dpi))
+ target-res))
+
+(defun log2scr (logv &optional (target-res (cs-target-res)))
+ (floor-round (* logv target-res )
+ (cs-logical-dpi)))
+
+(defun cs-archos-dpi ()
+ (cs-logical-dpi))
+
+(defun floor-round (x &optional (divisor 1))
+ (ceiling (- (/ x divisor) 1/2)))
+
+;converts logical pixels to screen pixels given the current target resolution OR OPTIONAL OTHER RES
+(defun logical-to-screen-vector (dots &optional target-res)
+ (let ((convert-res (or target-res (cs-target-res))))
+ (floor-round (* dots convert-res) (cs-logical-dpi))))
+
+(defun logical-to-screen-point (point &optional target-res)
+ (mkv2
+ (log2scr (v2-h point) target-res)
+ (log2scr (v2-v point) target-res)))
+
+(defun screen-to-logical-v2 (point &optional target-res)
+ (mkv2
+ (scr2log (v2-h point) target-res)
+ (scr2log (v2-v point) target-res)))
+
+(defun nr-screen-to-logical (logical-rect screen-rect &optional target-res)
+ (nr-make logical-rect
+ (scr2log (r-left screen-rect) target-res)
+ (scr2log (r-top screen-rect) target-res)
+ (scr2log (r-right screen-rect) target-res)
+ (scr2log (r-bottom screen-rect) target-res)))
+
+; logical-to-target is a more sensible name throughout
+
+(defun logical-to-target-vector (dots &optional target-res)
+ (log2scr dots target-res))
+;--------------------------------------------------------------------------------------------
+
+(defun r-logical-to-screen (logical-rect &optional target-res)
+ (count-it :r-logical-to-screen)
+ (nr-logical-to-screen (mkr 0 0 0 0) logical-rect target-res))
+
+(defun nr-logical-to-screen (screen-rect logical-rect &optional target-res)
+ (nr-make screen-rect
+ (log2scr (r-left logical-rect) target-res)
+ (log2scr (r-top logical-rect) target-res)
+ (log2scr (r-right logical-rect) target-res)
+ (log2scr (r-bottom logical-rect) target-res)))
+
+;------------------------------------------------------------------------------------------------
+
+;;;(defun set-scaling (window)
+;;; #+shh(trc "targetResolution" (targetRes window))
+;;;
+;;; (set-current-target-resolution (cs-logical-screen-resolution)) ;here and below, we'll probably make scalable
+;;; ;(set-current-target-resolution (cs-logical-dpi))
+;;; (let ((dc (device-context window))
+;;; (display-dpi (cs-logical-screen-resolution)) ;... and use (targetRes window)
+;;; (logical-dpi (cs-logical-dpi)))
+;;; (os-SetMapMode dc win:MM_ISOTROPIC)
+;;; (os-SetWindowExtEx dc logical-dpi logical-dpi ct:hnull)
+;;; (os-SetViewportExtEx dc display-dpi display-dpi ct:hnull)))
+
+
+(defun move-v2-x-y (v2 x y)
+ (incf (v2-h v2) x)
+ (incf (v2-v v2) y)
+ v2)
+
+(defmethod ncanvas-to-screen-point (self point)
+ (ncanvas-to-screen-point (fm-parent self)
+ (move-v2-x-y point (px self) (py self))))
+
+(defmethod res-to-res ((amount number) from-res to-res)
+ (if to-res
+ (round (* amount from-res) to-res)
+ from-res))
+
+(defmethod res-to-res ((point v2) from-res to-res)
+ (nres-to-res (copy-v2 point) from-res to-res))
+
+#+no-2e-h
+(defmethod nres-to-res ((point v2) from-res to-res)
+ (setf (v2-h point) (res-to-res (v2-h point) from-res to-res))
+ (setf (v2-v point) (res-to-res (v2-v point) from-res to-res))
+ point)
+
+(defmethod res-to-res ((box rect) from-res to-res)
+ (count-it :res-to-res)
+ (nres-to-res (nr-copy (mkr 0 0 0 0) box) from-res to-res))
+
+(defmethod nres-to-res :around (geo-thing from-res (to-res null))
+ (declare (ignore from-res))
+ geo-thing)
+
+(defmethod nres-to-res ((box rect) from-res to-res)
+ (setf (r-left box) (res-to-res (r-left box) from-res to-res))
+ (setf (r-top box) (res-to-res (r-top box) from-res to-res))
+ (setf (r-right box) (res-to-res (r-right box) from-res to-res))
+ (setf (r-bottom box) (res-to-res (r-bottom box) from-res to-res))
+ box)
+
+(defun canvas-to-screen-box (self box)
+ (count-it :canvas-to-screen-box)
+ (nr-make-from-corners
+ (mkr 0 0 0 0)
+ (ncanvas-to-screen-point self (r-top-left box))
+ (ncanvas-to-screen-point self (r-bottom-right box))))
+
Added: dependencies/trunk/cells/gui-geometry/defpackage.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/defpackage.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,53 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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.
+
+|#
+
+(defpackage #:gui-geometry
+ (:nicknames #:geo)
+ (:use #:common-lisp #:excl #:utils-kt #:cells)
+ (:export #:geometer #:geo-zero-tl #:geo-inline #:a-stack #:a-row
+ #:px #:py #:ll #:lt #:lr #:lb #:pl #:pt #:pr #:pb
+ #:^px #:^py #:^ll #:^lt #:^lr #:^lb #:^lb-height
+ #:^fill-parent-down
+ #:u96ths #:udots #:uinches #:uin #:upoints #:upts #:u8ths #:u16ths #:u32nds
+ #:mkr #:v2-nmove #:l-height #:mkv2 #:^offset-within #:inset-lr #:v2-v #:v2-h
+ #:r-bounds #:l-box
+ #:lb
+ #:cs-target-res
+ #:nr-make
+ #:r-contains
+ #:collapsed
+ #:g-box
+ #:v2-in-rect-ratio
+ #:v2-xlate #:v2-in-rect #:v2-add #:v2-subtract
+ #:log2scr
+ #:^lr-width
+ #:px-maintain-pr
+ #:outset
+ #:py-maintain-pb
+ #:cs-logical-dpi
+ #:px-maintain-pl #:py-maintain-pt
+ #:scr2log
+ #:inset-width #:inset-height
+ #:res-to-res
+ #:logical-to-screen-point
+ #:nres-to-res
+ #:cs-logical-screen-resolution
+ #:outl
+ #:with-r-bounds #:r-inset
+ #:ncopy-rect
+ #:l
+ #:r-height #:r-width #:r-top #:r-right #:r-bottom #:r-left
+ #:l-width ))
Added: dependencies/trunk/cells/gui-geometry/geo-data-structures.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/geo-data-structures.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,342 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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 :gui-geometry)
+
+(eval-now!
+ (export '(v2 mkv2 v2=)))
+;-----------------------------
+
+(defstruct v2
+ (h 0 ) ;; horizontal coordinate
+ (v 0 ) ;; vertical coordinate
+ )
+
+(defmethod print-object ((self v2) s)
+ (format s "~a|~a" (v2-h self)(v2-v self)))
+
+(defun mkv2 (h v) (make-v2 :h h :v v))
+
+(defun v2= (a b)
+ (and a b
+ (= (v2-h a)(v2-h b))
+ (= (v2-v a)(v2-v b))))
+
+(defun v2-add (p1 p2-or-x &optional y-or-p2-or-x-is-p2)
+ (if y-or-p2-or-x-is-p2
+ (make-v2 :h (+ (v2-h p1) p2-or-x)
+ :v (+ (v2-v p1) y-or-p2-or-x-is-p2))
+ (make-v2 :h (+ (v2-h p1) (v2-h p2-or-x))
+ :v (+ (v2-v p1) (v2-v p2-or-x)))))
+
+(defun v2-subtract (p1 p2-or-x &optional y-or-p2-or-x-is-p2)
+ (if y-or-p2-or-x-is-p2
+ (make-v2 :h (- (v2-h p1) p2-or-x)
+ :v (- (v2-v p1) y-or-p2-or-x-is-p2))
+ (make-v2 :h (- (v2-h p1) (v2-h p2-or-x))
+ :v (- (v2-v p1) (v2-v p2-or-x)))))
+
+(defun v2-nmove (p1 x &optional y)
+ (if y
+ (progn
+ (incf (v2-h p1) x)
+ (incf (v2-v p1) y))
+ (v2-nmove p1 (v2-h x)(v2-v x)))
+ p1)
+
+(defun v2-in-rect (v2 r)
+ (mkv2 (min (r-right r) (max (r-left r) (v2-h v2)))
+ (min (r-top r) (max (r-bottom r) (v2-v v2)))))
+
+(defun v2-in-rect-ratio (v2 r)
+ (assert (<= (r-left r) (v2-h v2) (r-right r)))
+ (assert (<= (r-bottom r) (v2-v v2) (r-top r)))
+ (mkv2 (div-safe (- (v2-h v2) (r-left r)) (r-width r))
+ (div-safe (- (v2-v v2) (r-bottom r)) (r-height r))))
+
+(defun div-safe (n d &optional (zero-div-return-value 1))
+ (if (zerop d) zero-div-return-value (/ n d)))
+
+(defmethod c-value-incf (c (base v2) (delta number))
+ (declare (ignore c))
+ (mkv2 (+ (v2-h base) delta)
+ (+ (v2-v base) delta)))
+
+(defmethod c-value-incf (c (base v2) (delta v2))
+ (declare (ignore c))
+ (v2-add base delta))
+
+; synapse support
+;
+(defmethod delta-diff ((new v2) (old v2) (subtypename (eql 'v2)))
+ (v2-subtract new old))
+
+(defmethod delta-identity ((dispatcher number) (subtypename (eql 'v2)))
+ (mkv2 0 0))
+
+(defun long-v2 (long-hv)
+ (c-assert (numberp long-hv))
+ (multiple-value-bind (fv fh)
+ (floor long-hv 65536)
+ (mkv2 fh fv)))
+
+(defun long-x (long-hv)
+ (c-assert (numberp long-hv))
+ (mod long-hv 65536))
+
+(defun long-y (long-hv)
+ (c-assert (numberp long-hv))
+ (floor long-hv 65536))
+
+(defun v2-long (v2)
+ (c-assert (typep v2 'v2))
+ (xy-long (v2-h v2) (v2-v v2)))
+
+(defun xy-long (x y)
+ (+ (* 65536 y) x))
+
+(defun v2-to-vector (v2)
+ (vector (v2-h v2) (v2-v v2)))
+
+(defun v2-negative (v2)
+ (c-assert (typep v2 'v2))
+ (mkv2 (- (v2-h v2)) (- (v2-v v2))))
+
+(defun vector-v2 (vc) (mkv2 (elt vc 0) (elt vc 1)))
+
+(defmethod delta-exceeds ((d1 v2) (d2 v2) (subtypename (eql 'v2)))
+ (c-assert (and (typep d1 'v2) (typep d2 'v2)))
+ (> (v2-distance-to d1) (v2-distance-to d2)))
+
+(defun v2-distance (from to)
+ (sqrt (+ (expt (v2-dv from to) 2)
+ (expt (v2-dh from to) 2))))
+
+(defun v2-area (v2)
+ "Treat point as length & width and calc area"
+ (abs (* (v2-h v2)(v2-v v2))))
+
+(defun v2-dh (p1 p2)
+ (- (v2-h p2) (v2-h p1)))
+
+(defun v2-dv (p1 p2)
+ (- (v2-v p2) (v2-v p1)))
+
+(defun v2-angle-between (from to)
+ (atan (v2-dv from to) (v2-dh from to)))
+
+(defun v2-distance-to (to)
+ (sqrt (+ (expt (v2-h to) 2)
+ (expt (v2-v to) 2))))
+;-------------------------------------------------
+
+(export! rect)
+(defstruct (rect (:conc-name r-))
+ (left 0 )
+ (top 0 )
+ (right 0 )
+ (bottom 0 ))
+
+(defmethod print-object ((self rect) s)
+ (format s "(rect (~a,~a) (~a,~a))" (r-left self)(r-top self)(r-right self)(r-bottom self)))
+
+(defun r-top-left (r)
+ (mkv2 (r-left r) (r-top r)))
+
+(export! r-center)
+
+(defun r-center (r)
+ (mkv2 (/ (+ (r-left r)(r-right r)) 2)
+ (/ (+ (r-top r)(r-bottom r)) 2)))
+
+(defun r-bottom-right (r)
+ (mkv2 (r-bottom r) (r-right r)))
+
+(defun mkr (left top right bottom)
+ (count-it :mkrect)
+ (make-rect :left left :top top :right right :bottom bottom))
+
+(defun nr-make (r left top right bottom)
+ (setf (r-left r) left (r-top r) top (r-right r) right (r-bottom r) bottom)
+ r)
+
+(defmacro with-r-bounds ((lv tv rv bv) r-form &body body)
+ (let ((r (gensym)))
+ `(let* ((,r ,r-form)
+ (,lv (r-left ,r))
+ (,tv (r-top ,r))
+ (,rv (r-right ,r))
+ (,bv (r-bottom ,r)))
+ , at body)))
+
+(defun ncopy-rect (old &optional new)
+ (if new
+ (progn
+ (setf (r-left new)(r-left old)
+ (r-top new)(r-top old)
+ (r-right new)(r-right old)
+ (r-bottom new)(r-bottom old))
+ new)
+ (copy-rect old)))
+
+(defun r-inset (r in &optional (destr (mkr 0 0 0 0)))
+ (nr-make destr
+ (+ (r-left r) in)
+ (+ (r-top r) (downs in))
+ (- (r-right r) in)
+ (+ (r-bottom r) (ups in))))
+
+(defun nr-make-from-corners (r tl br)
+ (nr-make r (v2-h tl)(v2-v tl)(v2-h br)(v2-v br)))
+
+(defun nr-copy (r copied-r)
+ (setf (r-left r) (r-left copied-r)
+ (r-top r) (r-top copied-r)
+ (r-right r) (r-right copied-r)
+ (r-bottom r) (r-bottom copied-r))
+ r)
+
+(defun r-contains (r v2)
+ (and (<= (r-left r)(v2-h v2)(r-right r))
+ (<= (r-top r)(v2-v v2)(r-bottom r))))
+
+(defun nr-intersect (r sr)
+ (let ((r-min-v (min (r-top r) (r-bottom r)))
+ (r-max-v (max (r-top r) (r-bottom r)))
+ (r-min-h (min (r-left r) (r-right r)))
+ (r-max-h (max (r-left r) (r-right r)))
+ ;
+ (sr-min-v (min (r-top sr) (r-bottom sr)))
+ (sr-max-v (max (r-top sr) (r-bottom sr)))
+ (sr-min-h (min (r-left sr) (r-right sr)))
+ (sr-max-h (max (r-left sr) (r-right sr)))
+ )
+ (let ((min-v (max r-min-v sr-min-v))
+ (max-v (min r-max-v sr-max-v))
+ (min-h (max r-min-h sr-min-h))
+ (max-h (min r-max-h sr-max-h)))
+ (when (or (>= min-v max-v)(>= min-h max-h))
+ (setf min-h 0 min-v 0 max-h 0 max-v 0))
+ (nr-make r min-h min-v max-h max-v))))
+
+(defun nr-union (r sr) ;; unlike other code, this is assuming opengl's up==plus, and proper rectangles
+ (nr-make r (min (r-left r) (r-left sr))
+ (max (r-top r) (r-top sr))
+ (max (r-right r) (r-right sr))
+ (min (r-bottom r) (r-bottom sr))))
+
+(defun nr-move-to (r h v)
+ (setf (r-left r) h
+ (r-top r) (+ v (r-width r))
+ (r-right r) (+ h (r-width r))
+ (r-bottom r) (+ v (r-height r))))
+
+
+(defun nr-scale (r factor)
+ (nr-make r
+ (round (* (r-left r) factor))
+ (round (* (r-top r) factor))
+ (round (* (r-right r) factor))
+ (round (* (r-bottom r) factor))))
+
+(defun r-empty (r)
+ (or (zerop (r-width r))
+ (zerop (r-height r))))
+
+(defun r-width (r) (abs (- (r-right r)(r-left r))))
+(defun r-height (r) (abs (- (r-top r)(r-bottom r))))
+(defun r-area (r) (* (r-width r)(r-height r)))
+
+(defun nr-offset (r dh dv)
+;;; (declare (optimize (speed 3) (safety 0) (debug 0)))
+ ;; (declare (type fixnum dh dv))
+ (incf (r-left r) dh)
+ (incf (r-right r) dh)
+ (incf (r-top r) dv)
+ (incf (r-bottom r) dv)
+ r)
+
+(defun nr-outset (box dh &optional (dv dh))
+;;; (declare (optimize (speed 3) (safety 0) (debug 0)))
+ (declare (type fixnum dh dv))
+ (decf (r-left box) dh)
+ (incf (r-right box) dh)
+ (decf (r-top box) dv)
+ (incf (r-bottom box) dv)
+ box)
+
+(defun r-bounds (box)
+ (list (r-left box)(r-top box)(r-right box)(r-bottom box)))
+
+(defun pt-in-bounds (point bounds-left bounds-top bounds-right boundsbottom)
+;;; (declare (optimize (speed 3) (safety 0) (debug 0)))
+ (declare (type fixnum bounds-left bounds-top bounds-right boundsbottom))
+ (and (<= bounds-left (progn (v2-h point)) bounds-right)
+ (<= bounds-top (progn (v2-v point)) boundsbottom)))
+
+
+(defun r-in-bounds (box bounds-left bounds-top bounds-right boundsbottom)
+;;; (declare (optimize (speed 3) (safety 0) (debug 0)))
+ (declare (type fixnum bounds-left bounds-top bounds-right boundsbottom))
+ (and (<= bounds-left (progn (r-left box)) (progn (r-right box)) bounds-right)
+ (<= bounds-top (progn (r-top box)) (progn (r-bottom box)) boundsbottom)))
+
+(defun r-unitize (object-r unit-r &aux (ww (r-width unit-r))(wh (r-height unit-r)))
+ (flet ((cf (i) (coerce i 'float)))
+ (mkr (cf (/ (- (r-left object-r)(r-left unit-r)) ww))
+ (cf (/ (- (r-top unit-r)(r-top object-r)) wh))
+ (cf (/ (- (r-right object-r)(r-left unit-r)) ww))
+ (cf (/ (- (r-top unit-r)(r-bottom object-r)) wh)))))
+
+(defun r-scale (r x y)
+ (mkr (* (r-left r) x)
+ (* (r-top r) y)
+ (* (r-right r) x)
+ (* (r-bottom r) x)))
+
+(defun r-analog (this1 that1 this2)
+ (mkr (* (r-left this2) (/ (r-left that1)(r-left this1)))
+ (* (r-top this2) (/ (r-top that1)(r-top this1)))
+ (* (r-right this2) (/ (r-right that1)(r-right this1)))
+ (* (r-bottom this2) (/ (r-bottom that1)(r-bottom this1)))))
+
+
+;;; --- Up / Down variability management ---
+
+(eval-now!
+ (export '(*up-is-positive* ups ups-more ups-most downs downs-most downs-more)))
+
+(defparameter *up-is-positive* t
+ "You should set this to NIL for most GUIs, but not OpenGl")
+
+(defun ups (&rest values)
+ (apply (if *up-is-positive* '+ '-) values))
+
+(defun ups-more (&rest values)
+ (apply (if *up-is-positive* '> '<) values))
+
+(defun ups-most (&rest values)
+ (apply (if *up-is-positive* 'max 'min) values))
+
+(defun downs (&rest values)
+ (apply (if *up-is-positive* '- '+) values))
+
+(defun downs-most (&rest values)
+ (apply (if *up-is-positive* 'min 'max) values))
+
+(defun downs-more (&rest values)
+ (apply (if *up-is-positive* '< '>) values))
+
Added: dependencies/trunk/cells/gui-geometry/geo-family.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/geo-family.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,171 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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 :gui-geometry)
+
+(export! geo-inline-lazy ^px-self-centered justify py-maintain-pt
+ ^prior-sib-pb spacing lr-maintain-pr orientation)
+
+;--------------- geo-inline -----------------------------
+;
+(defmodel geo-inline (geo-zero-tl)
+ ((orientation :initarg :orientation :initform nil :accessor orientation
+ :documentation ":vertical (for a column) or :horizontal (row)")
+ (justify :initarg :justify :accessor justify
+ :initform (c? (ecase (orientation self)
+ (:vertical :left)
+ (:horizontal :top))))
+ (spacing :initarg :spacing :initform 0 :accessor spacing))
+ (:default-initargs
+ :lr (c? (if (^collapsed)
+ (^lr-width 0)
+ (+ (^outset)
+ (ecase (orientation self)
+ (:vertical (loop for k in (^kids)
+ maximizing (l-width k)))
+ (:horizontal (bif (lk (last1 (^kids)))
+ (pr lk) 0))))))
+ :lb (c? (if (^collapsed)
+ (^lb-height 0)
+ (+ (- (^outset))
+ (ecase (orientation self)
+ (:vertical (loop for k in (^kids)
+ unless (collapsed k)
+ minimizing (pb k)))
+ (:horizontal (downs (loop for k in (^kids)
+ maximizing (l-height k))))))))
+ :kid-slots (lambda (self)
+ (ecase (orientation .parent)
+ (:vertical (list
+ (mk-kid-slot (px :if-missing t)
+ (c? (^px-self-centered (justify .parent))))
+ (mk-kid-slot (py)
+ (c? (py-maintain-pt
+ (^prior-sib-pb self (spacing .parent)))))))
+ (:horizontal (list
+ (mk-kid-slot (py :if-missing t)
+ (c? (py-self-centered self (justify .parent))))
+ (mk-kid-slot (px :if-missing t)
+ (c? (px-maintain-pl
+ (^prior-sib-pr self (spacing .parent)))))))))
+ ))
+
+(defmodel geo-inline-lazy (geo-zero-tl)
+ ((orientation :initarg :orientation :initform nil :accessor orientation
+ :documentation ":vertical (for a column) or :horizontal (row)")
+ (justify :initarg :justify :accessor justify
+ :initform (c_? (ecase (orientation self)
+ (:vertical :left)
+ (:horizontal :top))))
+ (spacing :initarg :spacing :initform 0 :accessor spacing))
+ (:default-initargs
+ :lr (c_? (+ (^outset)
+ (ecase (orientation self)
+ (:vertical (loop for k in (^kids)
+ maximizing (l-width k)))
+ (:horizontal (bif (lk (last1 (^kids)))
+ (pr lk) 0)))))
+ :lb (c_? (+ (- (^outset))
+ (ecase (orientation self)
+ (:vertical (bif (lk (last1 (^kids)))
+ (pb lk) 0))
+ (:horizontal (downs (loop for k in (^kids)
+ maximizing (l-height k)))))))
+ :kid-slots (lambda (self)
+ (ecase (orientation .parent)
+ (:vertical (list
+ (mk-kid-slot (px :if-missing t)
+ (c_? (^px-self-centered (justify .parent))))
+ (mk-kid-slot (py)
+ (c_? (eko (nil "py" self (^lt) (l-height self)(psib))
+ (py-maintain-pt
+ (eko (nil "psib-pb")
+ (^prior-sib-pb self (spacing .parent)))))))))
+ (:horizontal (list
+ (mk-kid-slot (py :if-missing t)
+ (c_? (py-self-centered self (justify .parent))))
+ (mk-kid-slot (px)
+ (c_? (px-maintain-pl
+ (^prior-sib-pr self (spacing .parent)))))))))))
+
+
+
+(defun ^prior-sib-pb (self &optional (spacing 0)) ;; just keeping with -pt variant till both converted to defun
+ (bif (psib (find-prior self (kids .parent)
+ :test (lambda (sib)
+ (not (collapsed sib)))))
+ (eko (nil "^prior-sib-pb spc pb-psib -lt" (- (abs spacing)) (pb psib) (- (^lt)))
+ (+ (- (abs spacing)) ;; force spacing to minus(= down for OpenGL)
+ (pb psib)))
+ 0))
+
+(defun centered-h? ()
+ (c? (px-maintain-pl (round (- (inset-width .parent) (l-width self)) 2))))
+
+(defun centered-v? ()
+ (c? (py-maintain-pt (round (- (l-height .parent) (l-height self)) -2))))
+
+;--------------- geo.row.flow ----------------------------
+(export! geo-row-flow fixed-col-width ^fixed-col-width ^spacing-hz spacing-hz
+ max-per-row ^max-per-row)
+
+(defmd geo-row-flow (geo-inline)
+ (spacing-hz 0)
+ (spacing-vt 0)
+ (aligned :cell nil)
+ fixed-col-width
+ max-per-row
+ (row-flow-layout
+ (c? (loop with max-pb = 0 and pl = 0 and pt = 0
+ for k in (^kids)
+ for kn upfrom 0
+ for kw = (or (^fixed-col-width) (l-width k))
+ for kpr = (+ pl kw)
+ when (unless (= pl 0)
+ (if (^max-per-row)
+ (zerop (mod kn (^max-per-row)))
+ (> kpr (- (l-width self) (outset self)))))
+ do
+ (when (> kpr (- (l-width self) (outset self)))
+ (trc nil "LR overflow break" kpr :gt (- (l-width self) (outset self))))
+ (when (zerop (mod kn (^max-per-row)))
+ (trc nil "max/row break" kn (^max-per-row) (mod kn (^max-per-row))))
+ (setf pl 0
+ pt (+ max-pb (downs (^spacing-vt))))
+
+ collect (cons (+ pl (case (justify self)
+ (:center (/ (- kw (l-width k)) 2))
+ (:right (- kw (l-width k)))
+ (otherwise 0))) pt) into pxys
+ do (incf pl (+ kw (^spacing-hz)))
+ (setf max-pb (min max-pb (+ pt (downs (l-height k)))))
+ finally (return (cons max-pb pxys)))))
+ :lb (c? (+ (bif (xys (^row-flow-layout))
+ (car xys) 0)
+ (downs (outset self))))
+ :kid-slots (lambda (self)
+ (declare (ignore self))
+ (list
+ (mk-kid-slot (px)
+ (c? (px-maintain-pl (car (nth (kid-no self) (cdr (row-flow-layout .parent)))))))
+ (mk-kid-slot (py)
+ (c? (py-maintain-pt (cdr (nth (kid-no self) (cdr (row-flow-layout .parent))))))))))
+
+
+
+
+
+
Added: dependencies/trunk/cells/gui-geometry/geo-macros.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/geo-macros.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,142 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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 #:gui-geometry)
+
+(defmacro ^offset-within (inner outer)
+ (let ((offset-h (gensym)) (offset-v (gensym)) (from (gensym)))
+ `(let ((,offset-h 0)
+ (,offset-v 0))
+ (do ((,from ,inner (fm-parent ,from)))
+ ((or (null ,from)
+ (eql ,from ,outer))
+ ;
+ (mkv2 ,offset-h ,offset-v))
+
+ (incf ,offset-h (px ,from))
+ (incf ,offset-v (py ,from))))))
+
+(defmacro ^ll-width (width)
+ `(- (lr self) ,width))
+
+(defmacro ^lr-width (width)
+ `(+ (ll self) ,width))
+
+(defmacro ^lt-height (height)
+ `(- (lb self) ,height))
+
+(defmacro ^lb-height (height)
+ `(+ (lt self) ,height))
+
+(defmacro ll-maintain-pL (pl)
+ `(- ,pL (^px)))
+
+(defmacro lr-maintain-pr (pr)
+ `(- ,pr (^px)))
+
+(defmacro ^fill-right (upperType &optional (padding 0))
+ `(call-^fillRight self (upper self ,upperType) ,padding))
+
+;recalc local top based on pT and offset
+(defmacro lt-maintain-pT (pT)
+ `(- ,pT (^py)))
+
+;recalc local bottom based on pB and offset
+(defmacro lb-maintain-pB (pB)
+ `(- ,pB (^py)))
+
+;------------------------------------
+; recalc offset based on p and local
+;
+
+(defmacro px-maintain-pL (pL)
+ (let ((lL (gensym)))
+ `(- ,pL (let ((,lL (^lL)))
+ (c-assert ,lL () "^px-maintain-pL sees nil lL for ~a" self)
+ ,lL))))
+
+(defmacro px-maintain-pR (pR)
+ `(- ,pR (^lR)))
+
+(defmacro py-maintain-pT (pT)
+ `(- ,pT (^lT)))
+
+(defmacro py-maintain-pB (pB)
+ `(- ,pB (^lB)))
+
+(export! centered-h? centered-v? lb-maintain-pB)
+
+(defmacro ^fill-down (upper-type &optional (padding 0))
+ (let ((filled (gensym)))
+ `(let ((,filled (upper self ,upper-type)))
+ #+shhh (trc "^fillDown sees filledLR less offH"
+ (lb ,filled)
+ ,padding
+ (v2-v (offset-within self ,filled)))
+ (- (lb ,filled)
+ ,padding
+ (v2-v (offset-within self ,filled))))))
+
+(defmacro ^lbmax? (&optional (padding 0))
+ `(c? (lb-maintain-pb (- (inset-lb .parent)
+ ,padding))))
+
+(defmacro ^lrmax? (&optional (padding 0))
+ `(c? (lr-maintain-pr (- (inset-lr .parent)
+ ,padding))))
+
+; "...return the sib's pL [if ,alignment is :left] or pR, plus optional spacing"
+
+(defmacro ^prior-sib-pr (self &optional (spacing 0) alignment)
+ (let ((kid (gensym))
+ (psib (gensym)))
+ `(let* ((,kid ,self)
+ (,psib (find-prior ,kid (kids (fm-parent ,kid)) :test (lambda (k) (not (collapsed k))))))
+ (if ,psib
+ (case ,alignment
+ (:left (+ ,spacing (pl ,psib)))
+ (otherwise (+ ,spacing (pr ,psib))))
+ 0))))
+
+(defmacro ^px-stay-right-of (other &key (by '0))
+ `(px-maintain-pl (+ (pr (fm-other ,other)) ,by)))
+
+; in use; adjust offset to maintain pL based on ,justify
+(defmacro ^px-self-centered (justify)
+ `(px-maintain-pl
+ (ecase ,justify
+ (:left 0)
+ (:center (floor (- (inset-width .parent) (l-width self)) 2))
+ (:right (- (inset-lr .parent) (l-width self))))))
+
+(defmacro ^fill-parent-right (&optional (inset 0))
+ `(lr-maintain-pr (- (inset-lr .parent) ,inset)))
+
+(defmacro ^fill-parent-down ()
+ `(lb-maintain-pb (inset-lb .parent)))
+
+(defmacro ^prior-sib-pt (self &optional (spacing 0))
+ (let ((kid (gensym))
+ (psib (gensym)))
+ `(let* ((,kid ,self)
+ (,psib (find-prior ,kid (kids (fm-parent ,kid)))))
+ ;(trc "^priorSib-pb > kid, sib" ,kid ,pSib)
+ (if ,psib
+ (+ (- (abs ,spacing)) (pt ,psib))
+ 0))))
+
+
+
Added: dependencies/trunk/cells/gui-geometry/geometer.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/geometer.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,241 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: gui-geometry; -*-
+#|
+
+Copyright (C) 2004 by Kenneth William Tilton
+
+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 #:gui-geometry)
+
+(eval-now!
+ (export '(outset ^outset mkv2 g-offset g-offset-h g-offset-v collapsed ^collapsed inset ^inset)))
+
+(defmd geometer ()
+ px py ll lt lr lb
+ collapsed
+ (inset (mkv2 0 0) :unchanged-if 'v2=)
+ (outset 0)
+ (w-box (mkr 0 0 0 0) :cell nil :accessor w-box
+ :documentation "bbox in window coordinate system"))
+
+(defmethod collapsed (other)
+ (declare (ignore other))
+ nil)
+
+;;-------- Zero-zero Top Left ----------------------------
+;;
+(defmodel geo-zero-tl (family)
+ ()
+ (:default-initargs
+ :ll (c? (- (outset self)))
+ :lt (c? (+ (outset self)))
+ :lr (c? (geo-kid-wrap self 'pr))
+ :lb (c? (geo-kid-wrap self 'pb))
+ :kid-slots (def-kid-slots
+ (mk-kid-slot (px :if-missing t)
+ (c? (px-maintain-pl 0)))
+ (mk-kid-slot (py :if-missing t)
+ (c? (py-maintain-pt 0))))))
+
+(export! geo-kid-sized)
+(defmodel geo-kid-sized (family)
+ ()
+ (:default-initargs
+ :ll (c? (geo-kid-wrap self 'pl))
+ :lt (c? (geo-kid-wrap self 'pt))
+ :lr (c? (geo-kid-wrap self 'pr))
+ :lb (c? (geo-kid-wrap self 'pb))))
+
+(defun l-box (geo)
+ (count-it :l-box)
+ (mkr (ll geo) (lt geo) (lr geo) (lb geo)))
+
+;---------- gOffset -------------------
+
+(export! offset-within inset-lb)
+;
+(defun offset-within (inner outer &optional dbg)
+ (declare (ignorable dbg))
+ (trc nil "offset-within inner outer" inner outer)
+ (do (
+ (offset-h 0 (progn
+ (trc nil "offset-within delta-h, from" from (px from))
+ (incf offset-h (px from))))
+ (offset-v 0 (incf offset-v (py from)))
+ (from inner (fm-parent from)))
+ ((or (null from)
+ (null outer)
+ (eql from outer)) (eko (nil "offset-within returns")
+ (mkv2 offset-h offset-v)))))
+
+(defun offset-within2 (inner outer)
+ (do (
+ (offset-h 0 (incf offset-h (px from)))
+ (offset-v 0 (incf offset-v (py from)))
+ (from inner (fm-parent from)))
+ ((or (null from)
+ (null outer)
+ (eql from outer)) (mkv2 offset-h offset-v))
+ ;(trc "inner outer" inner outer)
+ ))
+
+
+
+;----------- OfKids -----------------------
+;
+
+(defun v2-in-subframe (super h v sub)
+ (if (eql super sub) ;; bingo
+ (values h v)
+ (dolist (kid (kids super))
+ (multiple-value-bind (subh sub-v)
+ (v2-in-subframe kid h v sub)
+ (when subh
+ (return-from v2-in-subframe (values (- subh (px kid))
+ (- sub-v (py kid)))))))))
+(defun mk-gr (geo)
+ (c-assert geo)
+ (count-it :mk-gr)
+ (let ((g-offset (g-offset geo))) ;; /// wastes a v2
+ (nr-offset (mkr (ll geo) (lt geo) (lr geo) (lb geo)) (v2-h g-offset) (v2-v g-offset))))
+
+;sum pXYs up the family tree ;gave an odd result for cursor display....
+
+(defun v2-xlate (outer inner outer-v2)
+ (if (eq outer inner)
+ outer-v2
+ (v2-xlate outer (fm-parent inner)
+ (v2-subtract outer-v2
+ (mkv2 (px inner) (py inner))))))
+
+(defun v2-xlate-out (inner outer inner-v2)
+ (if (eq outer inner)
+ inner-v2
+ (v2-xlate (fm-parent inner) outer
+ (v2-add inner-v2
+ (mkv2 (px inner) (py inner))))))
+
+(defun v2-xlate-between (from-v2 from to)
+ (cond
+ ((fm-includes from to)(v2-xlate from to from-v2))
+ ((fm-includes to from)(v2-xlate-out from to from-v2))
+ (t (break "time to extend v2-xlate-between"))))
+
+(export! h-xlate v-xlate v2-xlate-between)
+
+(defun h-xlate (outer inner outer-h)
+ (if (eql outer inner)
+ outer-h
+ (h-xlate outer (fm-parent inner)
+ (- outer-h (px inner)))))
+
+(defun v-xlate (outer inner outer-v)
+ (if (eql outer inner)
+ outer-v
+ (v-xlate outer (fm-parent inner)
+ (- outer-v (py inner)))))
+
+(defmethod g-offset (self &optional (accum-h 0) (accum-v 0) within)
+ (declare (ignorable self within))
+ (mkv2 accum-h accum-v))
+
+(defun g-offset-h (geo)
+ (v2-h (g-offset geo)))
+
+(defun g-offset-v (geo)
+ (v2-v (g-offset geo)))
+
+(defun g-box (geo)
+ (count-it :g-box)
+ (if (c-stopped)
+ (trc "gbox sees stop" geo)
+ (progn
+ (c-assert geo)
+ (let* ((g-offset (g-offset geo))
+ (oh (v2-h g-offset)))
+ (c-assert (typep g-offset 'v2))
+ (c-assert (numberp oh))
+ (c-assert (numberp (lr geo)))
+ (let ((r (nr-offset
+ (nr-make (w-box geo) (ll geo) (lt geo) (lr geo) (lb geo))
+ oh (v2-v g-offset))))
+ (c-assert (numberp (r-left r)))
+ (c-assert (numberp (r-top r)))
+ (c-assert (numberp (r-right r)))
+ (c-assert (numberp (r-bottom r)))
+ r)))))
+
+;____________________________________________
+
+(defun pl (self) (+ (px self) (ll self)))
+(defun pr (self)
+ (c-assert (px self))
+ (c-assert (lr self))
+ (+ (px self) (lr self)))
+(defun pt (self) (+ (py self) (lt self)))
+(defun pb (self)
+ (c-assert (lb self))
+ (c-assert (py self))
+ (+ (py self) (lb self)))
+
+(defun pxy (self)
+ (mkv2 (px self) (py self)))
+
+;--------------------------------------------------------
+
+
+(defun l-width (i)
+ (c-assert (lr i))
+ (c-assert (ll i))
+ (- (lr i) (ll i)))
+
+(defun l-height (i)
+ (abs (- (lb i) (lt i))))
+
+;;-----------------------------------------------
+
+(defun inset-width (self)
+ (- (l-width self) (outset self) (outset self)))
+
+(defun inset-lr (self)
+ (- (lr self) (outset self)))
+
+(defun inset-lb (self)
+ (+ (lb self) (outset self)))
+
+(defun inset-lt (self)
+ (downs (lt self) (outset self)))
+
+(defun inset-height (self)
+ (- (l-height self) (outset self) (outset self)))
+
+;---------------------------------
+
+;----------------------------------
+
+(export! geo-kid-wrap inset-lt)
+
+(defun geo-kid-wrap (self bound)
+ (funcall (ecase bound ((pl pb) '-)((pr pt) '+))
+ (funcall (ecase bound
+ ((pl pb) 'fm-min-kid)
+ ((pr pt) 'fm-max-kid)) self bound)
+ (outset self)))
+
+; in use; same idea for pT
+(defun py-self-centered (self justify)
+ (py-maintain-pt
+ (ecase justify
+ (:top 0)
+ (:center (floor (- (inset-height .parent) (l-height self)) -2))
+ (:bottom (downs (- (inset-height .parent) (l-height self)))))))
+
Added: dependencies/trunk/cells/gui-geometry/gui-geometry.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/gui-geometry.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,15 @@
+;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
+
+(asdf:defsystem :gui-geometry
+ :author "Kenny Tilton <kentilton at gmail.com>"
+ :maintainer "Kenny Tilton <kentilton at gmail.com>"
+ :licence "Lisp LGPL"
+ :depends-on (:cells)
+ :serial t
+ :components
+ ((:file "defpackage")
+ (:file "geo-macros")
+ (:file "geo-data-structures")
+ (:file "coordinate-xform")
+ (:file "geometer")
+ (:file "geo-family")))
Added: dependencies/trunk/cells/gui-geometry/gui-geometry.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/gui-geometry/gui-geometry.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,88 @@
+;; -*- lisp-version: "8.0 [Windows] (Jan 29, 2007 18:02)"; cg: "1.81"; -*-
+
+(in-package :cg-user)
+
+(defpackage :COMMON-GRAPHICS-USER)
+
+(define-project :name :gui-geometry
+ :modules (list (make-instance 'module :name "defpackage.lisp")
+ (make-instance 'module :name "geo-macros.lisp")
+ (make-instance 'module :name
+ "geo-data-structures.lisp")
+ (make-instance 'module :name "coordinate-xform.lisp")
+ (make-instance 'module :name "geometer.lisp")
+ (make-instance 'module :name "geo-family.lisp"))
+ :projects (list (make-instance 'project-module :name
+ "..\\..\\Cells\\cells"))
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :common-graphics-user
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules '(:cg-dde-utils :cg.base :cg.bitmap-pane
+ :cg.bitmap-pane.clipboard :cg.bitmap-stream
+ :cg.button :cg.caret :cg.check-box :cg.choice-list
+ :cg.choose-printer :cg.clipboard
+ :cg.clipboard-stack :cg.clipboard.pixmap
+ :cg.color-dialog :cg.combo-box :cg.common-control
+ :cg.comtab :cg.cursor-pixmap :cg.curve
+ :cg.dialog-item :cg.directory-dialog
+ :cg.directory-dialog-os :cg.drag-and-drop
+ :cg.drag-and-drop-image :cg.drawable
+ :cg.drawable.clipboard :cg.dropping-outline
+ :cg.edit-in-place :cg.editable-text
+ :cg.file-dialog :cg.fill-texture
+ :cg.find-string-dialog :cg.font-dialog
+ :cg.gesture-emulation :cg.get-pixmap
+ :cg.get-position :cg.graphics-context
+ :cg.grid-widget :cg.grid-widget.drag-and-drop
+ :cg.group-box :cg.header-control :cg.hotspot
+ :cg.html-dialog :cg.html-widget :cg.icon
+ :cg.icon-pixmap :cg.ie :cg.item-list
+ :cg.keyboard-shortcuts :cg.lamp :cg.lettered-menu
+ :cg.lisp-edit-pane :cg.lisp-text :cg.lisp-widget
+ :cg.list-view :cg.mci :cg.menu :cg.menu.tooltip
+ :cg.message-dialog :cg.multi-line-editable-text
+ :cg.multi-line-lisp-text :cg.multi-picture-button
+ :cg.multi-picture-button.drag-and-drop
+ :cg.multi-picture-button.tooltip :cg.ocx
+ :cg.os-widget :cg.os-window :cg.outline
+ :cg.outline.drag-and-drop
+ :cg.outline.edit-in-place :cg.palette
+ :cg.paren-matching :cg.picture-widget
+ :cg.picture-widget.palette :cg.pixmap
+ :cg.pixmap-widget :cg.pixmap.file-io
+ :cg.pixmap.printing :cg.pixmap.rotate :cg.printing
+ :cg.progress-indicator :cg.project-window
+ :cg.property :cg.radio-button :cg.rich-edit
+ :cg.rich-edit-pane :cg.rich-edit-pane.clipboard
+ :cg.rich-edit-pane.printing :cg.sample-file-menu
+ :cg.scaling-stream :cg.scroll-bar
+ :cg.scroll-bar-mixin :cg.selected-object
+ :cg.shortcut-menu :cg.static-text :cg.status-bar
+ :cg.string-dialog :cg.tab-control
+ :cg.template-string :cg.text-edit-pane
+ :cg.text-edit-pane.file-io :cg.text-edit-pane.mark
+ :cg.text-or-combo :cg.text-widget :cg.timer
+ :cg.toggling-widget :cg.toolbar :cg.tooltip
+ :cg.trackbar :cg.tray :cg.up-down-control
+ :cg.utility-dialog :cg.web-browser
+ :cg.web-browser.dde :cg.wrap-string
+ :cg.yes-no-list :cg.yes-no-string :dde)
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags '(:top-level :debugger)
+ :build-flags '(:allow-runtime-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :default-command-line-arguments "+M +t \"Console for Debugging\""
+ :additional-build-lisp-image-arguments '(:read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :on-initialization 'default-init-function
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/initialize.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/initialize.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,63 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(eval-when (compile eval load)
+ (export '(c-envalue)))
+
+(defstruct (c-envaluer (:conc-name nil))
+ envalue-rule)
+
+(defmethod awaken-cell (c)
+ (declare (ignorable c)))
+
+(defmethod awaken-cell ((c cell))
+ (assert (c-inputp c))
+ ;
+ ; nothing to calculate, but every cellular slot should be output
+ ;
+ (trc nil "awaken cell observing" c)
+ (when (> *data-pulse-id* (c-pulse-observed c))
+ (setf (c-pulse-observed c) *data-pulse-id*)
+ (slot-value-observe (c-slot-name c) (c-model c) (c-value c) nil nil c)
+ (ephemeral-reset c)))
+
+(defmethod awaken-cell ((c c-ruled))
+ (let (*depender*)
+ (calculate-and-set c :fn-awaken-cell nil)))
+
+#+cormanlisp ; satisfy CormanCL bug
+(defmethod awaken-cell ((c c-dependent))
+ (let (*depender*)
+ (trc nil "awaken-cell c-dependent clearing *depender*" c)
+ (calculate-and-set c :fn-awaken-cell nil)))
+
+(defmethod awaken-cell ((c c-drifter))
+ ;
+ ; drifters *begin* valid, so the derived version's test for unbounditude
+ ; would keep (drift) rule ever from being evaluated. correct solution
+ ; (for another day) is to separate awakening (ie, linking to independent
+ ; cs) from evaluation, tho also evaluating if necessary during
+ ; awakening, because awakening's other role is to get an instance up to speed
+ ; at once upon instantiation
+ ;
+ (calculate-and-set c :fn-awaken-cell nil)
+ (cond ((c-validp c) (c-value c))
+ ((c-unboundp c) nil)
+ (t "illegal state!!!")))
Added: dependencies/trunk/cells/integrity.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/integrity.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,234 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(define-constant *ufb-opcodes* '(:tell-dependents
+ :awaken
+ :client
+ :ephemeral-reset
+ :change))
+
+(defmacro with-integrity ((&optional opcode defer-info debug) &rest body)
+ (declare (ignorable debug))
+ (when opcode
+ (assert (find opcode *ufb-opcodes*) ()
+ "Invalid opcode for with-integrity: ~a. Allowed values: ~a" opcode *ufb-opcodes*))
+ `(call-with-integrity ,opcode ,defer-info
+ (lambda (opcode defer-info)
+ (declare (ignorable opcode defer-info))
+ ;;; ,(when debug
+ ;;; `(trc "integrity action entry" opcode defer-info ',body))
+ ;;; (when *c-debug*
+ ;;; (when (eq opcode :change)
+ ;;; (trc "-------w/integ :change go--------------->:" defer-info)))
+ , at body)
+ nil
+ #+noway (when *c-debug* ',body)))
+
+(export! with-cc)
+
+(defmacro with-cc (id &body body)
+ `(with-integrity (:change ,id)
+ , at body))
+
+(defun integrity-managed-p ()
+ *within-integrity*)
+
+(defun call-with-integrity (opcode defer-info action code)
+ (declare (ignorable code))
+ (when *stop*
+ (return-from call-with-integrity))
+ (if *within-integrity*
+ (if opcode
+ (prog1
+ :deferred-to-ufb-1 ; SETF is supposed to return the value being installed
+ ; in the place, but if the SETF is deferred we return
+ ; something that will help someone who tries to use
+ ; the setf'ed value figure out what is going on:
+ (ufb-add opcode (cons defer-info action)))
+
+ ; thus by not supplying an opcode one can get something
+ ; executed immediately, potentially breaking data integrity
+ ; but signifying by having coded the with-integrity macro
+ ; that one is aware of this. If you read this comment.
+ (funcall action opcode defer-info))
+
+ (flet ((go-go ()
+ (let ((*within-integrity* t)
+ *unfinished-business*
+ *defer-changes*)
+ (trc nil "initiating new UFB!!!!!!!!!!!!" opcode defer-info)
+ ;(when *c-debug* (assert (boundp '*istack*)))
+ (when (or (zerop *data-pulse-id*)
+ (eq opcode :change))
+ (eko (nil "!!! New pulse, event" *data-pulse-id* defer-info)
+ (data-pulse-next (cons opcode defer-info))))
+ (prog1
+ (funcall action opcode defer-info)
+ (setf *finbiz-id* 0)
+ (finish-business)))))
+ (if nil ;; *c-debug*
+ (let ((*istack* (list (list opcode defer-info)
+ (list :trigger code)
+ (list :start-dp *data-pulse-id*))))
+ (trc "*istack* bound")
+ (handler-case
+ (go-go)
+ (xcell (c)
+ (if (functionp *c-debug*)
+ (funcall *c-debug* c (nreverse *istack*))
+ (loop for f in (nreverse *istack*)
+ do (format t "~&istk> ~(~a~) " f)
+ finally (describe c)
+ (break "integ backtrace: see listener for deets")))))
+ (trc "*istack* unbinding"))
+ (go-go)))))
+
+(defun ufb-queue (opcode)
+ (cdr (assoc opcode *unfinished-business*)))
+
+(defun ufb-queue-ensure (opcode)
+ (or (ufb-queue opcode)
+ (cdr (car (push (cons opcode (make-fifo-queue)) *unfinished-business*)))))
+
+(defparameter *no-tell* nil)
+
+(defun ufb-add (opcode continuation)
+ #+trythis (when (and *no-tell* (eq opcode :tell-dependents))
+ (break "truly queueing tell under no-tell"))
+ (trc nil "ufb-add deferring" opcode (when (eql opcode :client)(car continuation)))
+ (fifo-add (ufb-queue-ensure opcode) continuation))
+
+(defun just-do-it (op-or-q &optional (op-code op-or-q) ;; [mb]
+ &aux (q (if (keywordp op-or-q)
+ (ufb-queue op-or-q)
+ op-or-q)))
+ (declare (ignorable op-code))
+ (trc nil "----------------------------just do it doing---------------------" op-or-q)
+ (loop for (defer-info . task) = (fifo-pop q)
+ while task
+ do (trc nil "unfin task is" opcode task)
+ #+chill (when *c-debug*
+ (push (list op-code defer-info) *istack*))
+ (funcall task op-or-q defer-info)))
+
+(defun finish-business ()
+ (when *stop* (return-from finish-business))
+ (incf *finbiz-id*)
+ (tagbody
+ tell-dependents
+ (just-do-it :tell-dependents)
+ ;
+ ; while the next step looks separate from the prior, they are closely bound.
+ ; during :tell-dependents, any number of new model instances can be spawned.
+ ; as they are spawned, shared-initialize queues them for awakening, which
+ ; you will recall forces the calculation of ruled cells and observer notification
+ ; for all cell slots. These latter may enqueue :change or :client tasks, in which
+ ; case note that they become appended to :change or :client tasks enqueued
+ ; during :tell-dependents. How come? Because the birth itself of model instances during
+ ; a datapulse is considered part of that datapulse, so we do want tasks enqueued
+ ; during their awakening to be handled along with those enqueued by cells of
+ ; existing model instances.
+ ;
+ #-its-alive!
+ (bwhen (uqp (fifo-peek (ufb-queue :tell-dependents)))
+ (trcx fin-business uqp)
+ (dolist (b (fifo-data (ufb-queue :tell-dependents)))
+ (trc "unhandled :tell-dependents" (car b) (c-callers (car b))))
+ (break "unexpected 1> ufb needs to tell dependnents after telling dependents"))
+ (let ((*no-tell* t))
+ (just-do-it :awaken) ;--- md-awaken new instances ---
+ )
+ ;
+ ; OLD THINKING, preserved for the record, but NO LONGER TRUE:
+ ; we do not go back to check for a need to :tell-dependents because (a) the original propagation
+ ; and processing of the :tell-dependents queue is a full propagation; no rule can ask for a cell that
+ ; then decides it needs to recompute and possibly propagate; and (b) the only rules forced awake during
+ ; awakening need that precisely because no one asked for their values, so there can be no dependents
+ ; to "tell". I think. :) So...
+ ; END OF OLD THINKING
+ ;
+ ; We now allow :awaken to change things so more dependents need to be told. The problem is the implicit
+ ; dependence on the /life/ of a model whenever there is a dependence on any /cell/ of that model.
+ ; md-quiesce currently just flags such slots as uncurrent -- maybe /that/ should change and those should
+ ; recalculate at once -- and then an /observer/ can run and ask for a new value from such an uncurrent cell,
+ ; which now knows it must recalculate. And that recalculation of course can and likely will come up with a new value
+ ; and perforce need to tell its dependents. So...
+ ;
+ ; I /could/ explore something other than the "uncurrent" kludge, but NCTM 2007 is coming up and
+ ; to be honest the idea of not allowing nested tells was enforcing a /guess/ that that should not
+ ; arise, and there was not even any perceived integrity whole being closed, it was just a gratuitous
+ ; QA trick, and indeed for a long time many nested tells were avoidable. But the case of the quiesced
+ ; dependent reverses the arrow and puts the burden on the prosecution to prove nested tells are a problem.
+
+ (bwhen (uqp (fifo-peek (ufb-queue :tell-dependents)))
+ #+xxx (trc "retelling dependenst, one new one being" uqp)
+ (go tell-dependents))
+
+ ;--- process client queue ------------------------------
+ ;
+ (when *stop* (return-from finish-business))
+
+ handle-clients
+ (bwhen (clientq (ufb-queue :client))
+ (if *client-queue-handler*
+ (funcall *client-queue-handler* clientq) ;; might be empty/not exist, so handlers must check
+ (just-do-it clientq :client))
+ (when (fifo-peek (ufb-queue :client))
+ #+shhh (ukt::fifo-browse (ufb-queue :client) (lambda (entry)
+ (trc "surprise client" entry)))
+ (go handle-clients)))
+ ;--- now we can reset ephemerals --------------------
+ ;
+ ; one might be wondering when the observers got notified. That happens right during
+ ; slot.value.assume, via c-propagate.
+ ;
+ ; Nice historical note: by accident, in the deep-cells test to exercise the new behavior
+ ; of cells3, I coded an ephemeral cell and initialized it to non-nil, hitting a runtime
+ ; error (now gone) saying I had no idea what a non-nil ephemeral would mean. That had been
+ ; my conclusion when the idea occurred to me the first time, so I stuck in an assertion
+ ; to warn off callers.
+ ;
+ ; But the new
+ ; datachange progression defined by Cells3 had already forced me to manage ephemeral resets
+ ; more predictably (something in the test suite failed). By the time I got the runtime
+ ; error on deep-cells I was able to confidently take out the error and just let the thing
+ ; run. deep-cells looks to behave just right, but maybe a tougher test will present a problem?
+ ;
+ (just-do-it :ephemeral-reset)
+
+ ;--- do deferred state changes -----------------------
+ ;
+ (bwhen (task-info (fifo-pop (ufb-queue :change)))
+ (trc nil "!!! finbiz --- CHANGE ---- (first of)" (fifo-length (ufb-queue :change)))
+ (destructuring-bind (defer-info . task-fn) task-info
+ #+xxx (trc "fbz: dfrd chg" defer-info (fifo-length (ufb-queue :change)))
+ (data-pulse-next (list :finbiz defer-info))
+ (funcall task-fn :change defer-info)
+ ;
+ ; to finish this state change we could recursively call (finish-business), but
+ ; a goto let's us not use the stack. Someday I envision code that keeps on
+ ; setf-ing, polling the OS for events, in which case we cannot very well use
+ ; recursion. But as a debugger someone might want to change the next form
+ ; to (finish-business) if they are having trouble with a chain of setf's and
+ ; want to inspect the history on the stack.
+ ;
+ (go tell-dependents)))))
+
+
Added: dependencies/trunk/cells/link.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/link.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,152 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defun record-caller (used)
+ (when (c-optimized-away-p used) ;; 2005-05-21 removed slow type check that used is cell
+ (trc nil "depender not being recorded because used optimized away" *depender* (c-value used) :used used)
+ (return-from record-caller nil))
+ #+shhh (trc *depender* "record-caller depender entry: used=" used :caller *depender*)
+ (assert *depender*)
+ #+shhh (trc used "record-caller caller entry: used=" (qci used)
+ :caller *depender*)
+
+ (multiple-value-bind (used-pos useds-len)
+ (loop with u-pos
+ for known in (cd-useds *depender*)
+ counting known into length
+ when (eq used known)
+ do
+ (count-it :known-used)
+ (setf u-pos length)
+ finally (return (values (when u-pos (- length u-pos)) length)))
+
+ (when (null used-pos)
+ (trc nil "c-link > new caller,used " *depender* used)
+ (count-it :new-used)
+ (setf used-pos useds-len)
+ (push used (cd-useds *depender*))
+ (caller-ensure used *depender*) ;; 060604 experiment was in unlink
+ )
+ (let ((cd-usage (cd-usage *depender*)))
+ (when (>= used-pos (array-dimension cd-usage 0))
+ (setf cd-usage
+ (setf (cd-usage *depender*)
+ (adjust-array (cd-usage *depender*)
+ (+ used-pos 16)
+ :initial-element 0))))
+ (setf (sbit cd-usage used-pos) 1))
+ #+nonportable
+ (handler-case
+ (setf (sbit (cd-usage *depender*) used-pos) 1)
+ (type-error (error)
+ (declare (ignorable error))
+ (setf (cd-usage *depender*)
+ (adjust-array (cd-usage *depender*) (+ used-pos 16) :initial-element 0))
+ (setf (sbit (cd-usage *depender*) used-pos) 1))))
+ used)
+
+
+;--- unlink unused --------------------------------
+
+(defun c-unlink-unused (c &aux (usage (cd-usage c))
+ (usage-size (array-dimension (cd-usage c) 0))
+ (dbg nil))
+ (declare (ignorable dbg usage-size))
+ (when (cd-useds c)
+ (let (rev-pos)
+ (labels ((nail-unused (useds)
+ (flet ((handle-used (rpos)
+ (if (or (>= rpos usage-size)
+ (zerop (sbit usage rpos)))
+ (progn
+ (count-it :unlink-unused)
+ (trc nil "c-unlink-unused" c :dropping-used (car useds))
+ (c-unlink-caller (car useds) c)
+ (rplaca useds nil))
+ (progn
+ ;; moved into record-caller 060604 (caller-ensure (car useds) c)
+ )
+ )))
+ (if (cdr useds)
+ (progn
+ (nail-unused (cdr useds))
+ (handle-used (incf rev-pos)))
+ (handle-used (setf rev-pos 0))))))
+ (trc nil "cd-useds length" (length (cd-useds c)) c)
+ (nail-unused (cd-useds c))
+ (setf (cd-useds c) (delete nil (cd-useds c)))
+ (trc nil "useds of" c :now (mapcar 'qci (cd-useds c)))))))
+
+(defun c-caller-path-exists-p (from-used to-caller)
+ (count-it :caller-path-exists-p)
+ (or (find to-caller (c-callers from-used))
+ (find-if (lambda (from-used-caller)
+ (c-caller-path-exists-p from-used-caller to-caller))
+ (c-callers from-used))))
+
+; ---------------------------------------------
+
+(defun cd-usage-clear-all (c)
+ (setf (cd-usage c) (blank-usage-mask))
+ #+wowo (loop with mask = (cd-usage c)
+ for n fixnum below (array-dimension mask 0)
+ do (setf (sbit mask n) 0)
+ finally (return mask))
+ )
+
+
+;--- unlink from used ----------------------
+
+(defmethod c-unlink-from-used ((caller c-dependent))
+ (dolist (used (cd-useds caller))
+ (trc nil "unlinking from used" caller used)
+ (c-unlink-caller used caller))
+ ;; shouldn't be necessary (setf (cd-useds caller) nil)
+ )
+
+(defmethod c-unlink-from-used (other)
+ (declare (ignore other)))
+
+;----------------------------------------------------------
+
+(defun c-unlink-caller (used caller)
+ (trc nil "(1) caller unlinking from (2) used" caller used)
+ (caller-drop used caller)
+ (c-unlink-used caller used))
+
+(defun c-unlink-used (caller used)
+ (setf (cd-useds caller) (remove used (cd-useds caller))))
+
+;----------------- link debugging ---------------------
+
+(defun dump-callers (c &optional (depth 0))
+ (format t "~&~v,4t~s" depth c)
+ (dolist (caller (c-callers c))
+ (dump-callers caller (+ 1 depth))))
+
+(defun dump-useds (c &optional (depth 0))
+ ;(c.trc "dump-useds> entry " c (+ 1 depth))
+ (when (zerop depth)
+ (format t "x~&"))
+ (format t "~&|usd> ~v,8t~s" depth c)
+ (when (typep c 'c-ruled)
+ ;(c.trc "its ruled" c)
+ (dolist (used (cd-useds c))
+ (dump-useds used (+ 1 depth)))))
Added: dependencies/trunk/cells/load.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/load.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,7 @@
+(require 'asdf)
+(push "/home/alessio/libs/lisp/cells/" asdf:*central-registry*)
+(push "/home/alessio/libs/lisp/cells/utils-kt/" asdf:*central-registry*)
+(asdf:oos 'asdf:load-op :cells)
+
+(push "/home/alessio/libs/lisp/cells/cells-test/" asdf:*central-registry*)
+(asdf:oos 'asdf:load-op :cells-test)
Added: dependencies/trunk/cells/md-slot-value.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/md-slot-value.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,407 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defparameter *ide-app-hard-to-kill* t)
+
+(defun md-slot-value (self slot-name &aux (c (md-slot-cell self slot-name)))
+ (when (and (not *not-to-be*) (mdead self))
+ ;#-its-alive!
+ (unless *stop*
+ (trc nil "md-slot-value passed dead self:" self :asked4slot slot-name :cell c)
+ ;#-sbcl (inspect self)
+ ;(setf *stop* t)
+ ;(break "md-slot-value sees dead ~a" self)
+ )
+ (return-from md-slot-value (slot-value self slot-name))) ;; we can dream
+ (tagbody
+ retry
+ (when *stop*
+ (if *ide-app-hard-to-kill*
+ (progn
+ (princ #\.)
+ (princ "stopped")
+ (return-from md-slot-value))
+ (restart-case
+ (error "Cells is stopped due to a prior error.")
+ (continue ()
+ :report "Return a slot value of nil."
+ (return-from md-slot-value nil))
+ (reset-cells ()
+ :report "Reset cells and retry getting the slot value."
+ (cells-reset)
+ (go retry))))))
+
+ ;; (count-it :md-slot-value slot-name)
+ (if c
+ (cell-read c)
+ (values (slot-value self slot-name) nil)))
+
+(defun cell-read (c)
+ (assert (typep c 'cell))
+ (prog1
+ (with-integrity ()
+ (ensure-value-is-current c :c-read nil))
+ (when *depender*
+ (record-caller c))))
+
+(defun chk (s &optional (key 'anon))
+ (when (mdead s)
+ (break "model ~a is dead at ~a" s key)))
+
+(defvar *trc-ensure* nil)
+
+(defun qci (c)
+ (when c
+ (cons (md-name (c-model c)) (c-slot-name c))))
+
+
+(defun ensure-value-is-current (c debug-id ensurer)
+ ;
+ ; ensurer can be used cell propagating to callers, or an existing caller who wants to make sure
+ ; dependencies are up-to-date before deciding if it itself is up-to-date
+ ;
+ (declare (ignorable debug-id ensurer))
+ ;(count-it! :ensure.value-is-current)
+ ;(trc "evic entry" (qci c))
+ (wtrcx (:on? nil) ("evic>" (qci c) debug-id (qci ensurer))
+ ;(count-it! :ensure.value-is-current )
+ #+chill
+ (when ensurer ; (trcp c)
+ (count-it! :ensure.value-is-current (c-slot-name c) (md-name (c-model c))(c-slot-name ensurer) (md-name (c-model ensurer))))
+ #+chill
+ (when (and *c-debug* (trcp c)
+ (> *data-pulse-id* 650))
+ (bgo ens-high))
+
+ (trc nil ; c ;; (and *c-debug* (> *data-pulse-id* 495)(trcp c))
+ "ensure.value-is-current > entry1" debug-id (qci c) :st (c-state c) :vst (c-value-state c)
+ :my/the-pulse (c-pulse c) *data-pulse-id*
+ :current (c-currentp c) :valid (c-validp c))
+
+ #+nahhh
+ (when ensurer
+ (trc (and *c-debug* (> *data-pulse-id* 495)(trcp c))
+ "ensure.value-is-current > entry2"
+ :ensurer (qci ensurer)))
+
+ (when *not-to-be*
+ (when (c-unboundp c)
+ (error 'unbound-cell :cell c :instance (c-model c) :name (c-slot-name c)))
+ (return-from ensure-value-is-current
+ (when (c-validp c) ;; probably accomplishes nothing
+ (c-value c))))
+
+ (when (and (not (symbolp (c-model c))) ;; damn, just here because of playing around with global vars and cells
+ (eq :eternal-rest (md-state (c-model c))))
+ (break "model ~a of cell ~a is dead" (c-model c) c))
+
+ (cond
+ ((c-currentp c)
+ (count-it! :ensvc-is-indeed-currentp)
+ (trc nil "EVIC yep: c-currentp" c)
+ ) ;; used to follow c-inputp, but I am toying with letting ephemerals (inputs) fall obsolete
+ ;; and then get reset here (ie, ((c-input-p c) (ephemeral-reset c))). ie, do not assume inputs are never obsolete
+ ;;
+ ((and (c-inputp c)
+ (c-validp c) ;; a c?n (ruled-then-input) cell will not be valid at first
+ (not (and (typep c 'c-dependent)
+ (eq (cd-optimize c) :when-value-t)
+ (null (c-value c)))))
+ (trc nil "evic: cool: inputp" (qci c)))
+
+ ((or (bwhen (nv (not (c-validp c)))
+ (count-it! :ens-val-not-valid)
+ (trc nil "not c-validp, gonna run regardless!!!!!!" c)
+ nv)
+ ;;
+ ;; new for 2006-09-21: a cell ended up checking slots of a dead instance, which would have been
+ ;; refreshed when checked, but was going to be checked last because it was the first used, useds
+ ;; being simply pushed onto a list as they come up. We may need fancier handling of dead instance/cells
+ ;; still being encountered by consulting the prior useds list, but checking now in same order as
+ ;; accessed seems Deeply Correct (and fixed the immediate problem nicely, always a Good Sign).
+ ;;
+ (labels ((check-reversed (useds)
+ (when useds
+ (or (check-reversed (cdr useds))
+ (let ((used (car useds)))
+ (ensure-value-is-current used :nested c)
+ #+slow (trc nil "comparing pulses (ensurer, used, used-changed): " c debug-id used (c-pulse-last-changed used))
+ (when (> (c-pulse-last-changed used)(c-pulse c))
+ (count-it! :ens-val-someused-newer)
+ (trc nil "used changed and newer !!!!######!!!!!! used" (qci used) :oldpulse (c-pulse used)
+ :lastchg (c-pulse-last-changed used))
+ #+shhh (when (trcp c)
+ (describe used))
+ t))))))
+ (assert (typep c 'c-dependent))
+ (check-reversed (cd-useds c))))
+ (trc nil "kicking off calc-set of!!!!" (c-state c) (c-validp c) (qci c) :vstate (c-value-state c)
+ :stamped (c-pulse c) :current-pulse *data-pulse-id*)
+ (calculate-and-set c :evic ensurer)
+ (trc nil "kicked off calc-set of!!!!" (c-state c) (c-validp c) (qci c) :vstate (c-value-state c)
+ :stamped (c-pulse c) :current-pulse *data-pulse-id*))
+
+ ((mdead (c-value c))
+ (trc nil "ensure.value-is-current> trying recalc of ~a with current but dead value ~a" c (c-value c))
+ (let ((new-v (calculate-and-set c :evic-mdead ensurer)))
+ (trc nil "ensure.value-is-current> GOT new value ~a to replace dead!!" new-v)
+ new-v))
+
+ (t (trc nil "ensure.current decided current, updating pulse" (c-slot-name c) debug-id)
+ (c-pulse-update c :valid-uninfluenced)))
+
+ (when (c-unboundp c)
+ (error 'unbound-cell :cell c :instance (c-model c) :name (c-slot-name c)))
+
+ (bwhen (v (c-value c))
+ (if (mdead v)
+ (progn
+ #-its-alive!
+ (progn
+ (format t "~&on pulse ~a ensure.value still got and still not returning ~a dead value ~a" *data-pulse-id* c v)
+ (inspect v))
+ nil)
+ v))))
+
+
+(defun calculate-and-set (c dbgid dbgdata)
+ (declare (ignorable dbgid dbgdata)) ;; just there for inspection of the stack during debugging
+ (flet ((body ()
+ (when (c-stopped)
+ (princ #\.)
+ (return-from calculate-and-set))
+
+ #-its-alive!
+ (bwhen (x (find c *call-stack*)) ;; circularity
+ (unless nil ;; *stop*
+ (let ()
+ (inspect c)
+ (trc "calculating cell:" c (cr-code c))
+ (trc "appears-in-call-stack (newest first): " (length *call-stack*))
+ (loop for caller in (copy-list *call-stack*)
+ for n below (length *call-stack*)
+ do (trc "caller> " caller #+shhh (cr-code caller))
+ when (eq caller c) do (loop-finish))))
+ (setf *stop* t)
+ (c-break ;; break is problem when testing cells on some CLs
+ "cell ~a midst askers (see above)" c)
+ (error 'asker-midst-askers :cell c))
+
+ (multiple-value-bind (raw-value propagation-code)
+ (calculate-and-link c)
+
+ (when (and *c-debug* (typep raw-value 'cell))
+ (c-break "new value for cell ~s is itself a cell: ~s. probably nested (c? ... (c? ))"
+ c raw-value))
+
+ (unless (c-optimized-away-p c)
+ ; this check for optimized-away-p arose because a rule using without-c-dependency
+ ; can be re-entered unnoticed since that clears *call-stack*. If re-entered, a subsequent
+ ; re-exit will be of an optimized away cell, which we need not sv-assume on... a better
+ ; fix might be a less cutesy way of doing without-c-dependency, and I think anyway
+ ; it would be good to lose the re-entrance.
+ (md-slot-value-assume c raw-value propagation-code)))))
+ (if (trcp c) ;; *dbg*
+ (wtrc (0 100 "calcnset" c) (body))
+ (body))))
+
+(defun calculate-and-link (c)
+ (let ((*call-stack* (cons c *call-stack*))
+ (*depender* c)
+ (*defer-changes* t))
+ (assert (typep c 'c-ruled))
+ (trc nil "calculate-and-link" c)
+ (cd-usage-clear-all c)
+ (multiple-value-prog1
+ (funcall (cr-rule c) c)
+ (c-unlink-unused c))))
+
+
+;-------------------------------------------------------------
+
+(defun md-slot-makunbound (self slot-name
+ &aux (c (md-slot-cell self slot-name)))
+ (unless c
+ (c-break ":md-slot-makunbound > cellular slot ~a of ~a cannot be unbound unless initialized as inputp"
+ slot-name self))
+
+ (when (c-unboundp c)
+ (return-from md-slot-makunbound nil))
+
+ (when *within-integrity* ;; 2006-02 oops, bad name
+ (c-break "md-slot-makunbound of ~a must be deffered by wrapping code in with-integrity" c))
+
+ ;
+ ; Big change here for Cells III: before, only the propagation was deferred. Man that seems
+ ; wrong. So now the full makunbound processing gets deferred. Less controversially,
+ ; by contrast the without-c-dependency wrapped everything, and while that is harmless,
+ ; it is also unnecessary and could confuse people trying to follow the logic.
+ ;
+ (let ((causation *causation*))
+ (with-integrity (:change c)
+ (let ((*causation* causation))
+ ; --- cell & slot maintenance ---
+ (let ((prior-value (c-value c)))
+ (setf (c-value-state c) :unbound
+ (c-value c) nil
+ (c-state c) :awake)
+ (bd-slot-makunbound self slot-name)
+ ;
+ ; --- data flow propagation -----------
+ ;
+ (without-c-dependency
+ (c-propagate c prior-value t)))))))
+
+;;; --- setf md.slot.value --------------------------------------------------------
+;;;
+
+(defun (setf md-slot-value) (new-value self slot-name
+ &aux (c (md-slot-cell self slot-name)))
+ #+shhh (when *within-integrity*
+ (trc "mdsetf>" self (type-of self) slot-name :new new-value))
+ (when *c-debug*
+ (c-setting-debug self slot-name c new-value))
+
+ (unless c
+ (c-break "cellular slot ~a of ~a cannot be SETFed because it is not
+mediated by a Cell with :inputp t. To achieve this, the initial value ~s -- whether
+supplied as an :initform, :default-initarg, or at make-instance time via
+an :initarg -- should be wrapped in either macro C-IN or C-INPUT.
+In brief, initialize ~0@*~a to (c-in ~2@*~s) instead of plain ~:*~s"
+ slot-name self (slot-value self slot-name)))
+
+ (cond
+ ((find (c-lazy c) '(:once-asked :always t))
+ (md-slot-value-assume c new-value nil))
+
+ (*defer-changes*
+ (c-break "SETF of ~a must be deferred by wrapping code in WITH-INTEGRITY" c))
+
+ (t
+ (with-integrity (:change slot-name)
+ (md-slot-value-assume c new-value nil))))
+
+ ;; new-value
+ ;; above line commented out 2006-05-01. It seems to me we want the value assumed by the slot
+ ;; not the value setf'ed (on rare occasions they diverge, or at least used to for delta slots)
+ ;; anyway, if they no longer diverge the question of which to return is moot
+ )
+
+(defun md-slot-value-assume (c raw-value propagation-code)
+ (assert c)
+ (trc nil "md-slot-value-assume entry" (qci c)(c-state c))
+ (without-c-dependency
+ (let ((prior-state (c-value-state c))
+ (prior-value (c-value c))
+ (absorbed-value (c-absorb-value c raw-value)))
+
+ (c-pulse-update c :slotv-assume)
+
+ ; --- head off unchanged; this got moved earlier on 2006-06-10 ---
+ (when (and (not (eq propagation-code :propagate))
+ (find prior-state '(:valid :uncurrent))
+ (c-no-news c absorbed-value prior-value))
+ (setf (c-value-state c) :valid) ;; new for 2008-07-15
+ (trc nil "(setf md-slot-value) > early no news" propagation-code prior-state prior-value absorbed-value)
+ (count-it :nonews)
+ (return-from md-slot-value-assume absorbed-value))
+
+ ; --- slot maintenance ---
+
+ (unless (c-synaptic c)
+ (md-slot-value-store (c-model c) (c-slot-name c) absorbed-value))
+
+ ; --- cell maintenance ---
+ (setf
+ (c-value c) absorbed-value
+ (c-value-state c) :valid
+ (c-state c) :awake)
+
+ (case (and (typep c 'c-dependent)
+ (cd-optimize c))
+ ((t) (c-optimize-away?! c)) ;;; put optimize test here to avoid needless linking
+ (:when-value-t (when (c-value c)
+ (c-unlink-from-used c))))
+
+ ; --- data flow propagation -----------
+ (unless (eq propagation-code :no-propagate)
+ (trc nil "md-slot-value-assume flagging as changed: prior state, value:" prior-state prior-value )
+ (c-propagate c prior-value (cache-state-bound-p prior-state))) ;; until 06-02-13 was (not (eq prior-state :unbound))
+ (trc nil "exiting md-slot-val-assume" (c-state c) (c-value-state c))
+ absorbed-value)))
+
+(defun cache-bound-p (c)
+ (cache-state-bound-p (c-value-state c)))
+
+(defun cache-state-bound-p (value-state)
+ (or (eq value-state :valid)
+ (eq value-state :uncurrent)))
+
+;---------- optimizing away cells whose dependents all turn out to be constant ----------------
+;
+
+(defun flushed? (c)
+ (rassoc c (cells-flushed (c-model c))))
+
+(defun c-optimize-away?! (c)
+ #+shhh (trc nil "c-optimize-away?! entry" (c-state c) c)
+ (when (and (typep c 'c-dependent)
+ (null (cd-useds c))
+ (cd-optimize c)
+ (not (c-optimized-away-p c)) ;; c-streams (FNYI) may come this way repeatedly even if optimized away
+ (c-validp c) ;; /// when would this not be the case? and who cares?
+ (not (c-synaptic c)) ;; no slot to cache invariant result, so they have to stay around)
+ (not (c-inputp c)) ;; yes, dependent cells can be inputp
+ )
+ ;; (when (trcp c) (break "go optimizing ~a" c))
+
+ (when (trcp c)
+ (trc "optimizing away" c (c-state c) (rassoc c (cells (c-model c)))(rassoc c (cells-flushed (c-model c))))
+ )
+
+ (count-it :c-optimized)
+
+ (setf (c-state c) :optimized-away)
+
+ (let ((entry (rassoc c (cells (c-model c)))))
+ (unless entry
+ (describe c)
+ (bwhen (fe (rassoc c (cells-flushed (c-model c))))
+ (trc "got in flushed thoi!" fe)))
+ (c-assert entry)
+ ;(trc (eq (c-slot-name c) 'cgtk::id) "c-optimize-away?! moving cell to flushed list" c)
+ (setf (cells (c-model c)) (delete entry (cells (c-model c))))
+ #-its-alive! (push entry (cells-flushed (c-model c)))
+ )
+
+ (dolist (caller (c-callers c) )
+ ;
+ ; example: on window shutdown with a tool-tip displayed, the tool-tip generator got
+ ; kicked off and asked about the value of a dead instance. That returns nil, and
+ ; there was no other dependency, so the Cell then decided to optimize itself away.
+ ; of course, before that time it had a normal value on which other things depended,
+ ; so we ended up here. where there used to be a break.
+ ;
+ (setf (cd-useds caller) (delete c (cd-useds caller)))
+ ;;; (trc "nested opti" c caller)
+ (c-optimize-away?! caller) ;; rare but it happens when rule says (or .cache ...)
+ )))
+
+
Added: dependencies/trunk/cells/md-utilities.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/md-utilities.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,245 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defun md-awake (self) (eql :awake (md-state self)))
+
+(defun fm-grandparent (md)
+ (fm-parent (fm-parent md)))
+
+
+(defmethod md-release (other)
+ (declare (ignorable other)))
+
+(export! mdead)
+;___________________ birth / death__________________________________
+
+(defgeneric mdead (self)
+ (:method ((self model-object))
+ (unless *not-to-be* ;; weird
+ (eq :eternal-rest (md-state self))))
+
+ (:method (self)
+ (declare (ignore self))
+ nil))
+
+
+
+(defgeneric not-to-be (self)
+ (:method (other)
+ (declare (ignore other)))
+ (:method ((self cons))
+ (not-to-be (car self))
+ (not-to-be (cdr self)))
+ (:method ((self array))
+ (loop for s across self
+ do (not-to-be s)))
+ (:method ((self hash-table))
+ (maphash (lambda (k v)
+ (declare (ignorable k))
+ (not-to-be v)) self))
+
+ (:method ((self model-object))
+ (setf (md-census-count self) -1)
+ (md-quiesce self))
+
+ (:method :before ((self model-object))
+ (loop for slot-name in (md-owning-slots self)
+ do (not-to-be (slot-value self slot-name))))
+
+ (:method :around ((self model-object))
+ (declare (ignorable self))
+ (let ((*not-to-be* t)
+ (dbg nil))
+
+ (flet ((gok ()
+ (if (eq (md-state self) :eternal-rest)
+ (trc nil "n2be already dead" self)
+ (progn
+ (call-next-method)
+ (setf (fm-parent self) nil
+ (md-state self) :eternal-rest)
+;;; (bif (a (assoc (type-of self) *awake-ct*))
+;;; (decf (cdr a))
+;;; (break "no awake for" (type-of self) *awake-ct*))
+;;; (setf *awake* (delete self *awake*))
+ (md-map-cells self nil
+ (lambda (c)
+ (c-assert (eq :quiesced (c-state c)) ()
+ "Cell ~a of dead model ~a not quiesced. Was not-to-be shadowed by
+ a primary method? Use :before instead." c self))) ;; fails if user obstructs not.to-be with primary method (use :before etc)
+
+ ))))
+ (if (not dbg)
+ (gok)
+ (wtrc (0 100 "not.to-be nailing" self (when (typep self 'family)
+ (mapcar 'type-of (slot-value self '.kids))))
+ (gok)
+ (when dbg (trc "finished nailing" self))))))))
+
+
+
+(defun md-quiesce (self)
+ (trc nil "md-quiesce nailing cells" self (type-of self))
+ (md-map-cells self nil (lambda (c)
+ (trc nil "quiescing" c)
+ (c-assert (not (find c *call-stack*)))
+ (c-quiesce c)))
+ (when (register? self)
+ (fm-check-out self)))
+
+(defun c-quiesce (c)
+ (typecase c
+ (cell
+ (trc nil "c-quiesce unlinking" c)
+ (c-unlink-from-used c)
+ (dolist (caller (c-callers c))
+ (setf (c-value-state caller) :uncurrent)
+ (trc nil "c-quiesce totlalaly unlinking caller and making uncurrent" .dpid :q c :caller caller)
+ (c-unlink-caller c caller))
+ (setf (c-state c) :quiesced) ;; 20061024 for debugging for now, might break some code tho
+ )))
+
+(defparameter *to-be-dbg* nil)
+
+(defmacro make-kid (class &rest initargs)
+ `(make-instance ,class
+ , at initargs
+ :fm-parent (progn (assert self) self)))
+
+(defvar *c-d-d*)
+(defvar *max-d-d*)
+
+(defparameter *model-pop* nil)
+
+(export! md-census-start md-census-report md-census-count)
+
+(defun md-census-start ()
+ (setf *model-pop* (make-hash-table :test 'eq)))
+
+(defun (setf md-census-count) (delta self)
+ (when *model-pop*
+ (incf (gethash (type-of self) *model-pop* 0) delta)))
+
+(defun md-census-report ()
+ (when *model-pop*
+ (loop for (ct . type)
+ in (sort (let (raw)
+ (maphash (lambda (k v)
+ (push (cons v k) raw))
+ *model-pop*)
+ raw) '< :key 'car)
+ unless (zerop ct)
+ do (trc "pop" ct type))))
+
+#+test
+(md-census-report)
+
+#+test
+(md-census-count)
+
+(defun md-census-count (&optional type)
+ (when *model-pop*
+ (if type
+ (gethash type *model-pop* 0)
+ (loop for v being the hash-values of *model-pop*
+ summing v))))
+
+
+(defun count-model (self &key count-cells &aux (ccc 0))
+
+ (setf *c-d-d* (make-hash-table :test 'eq) *max-d-d* 0)
+ (let ((*counted* (make-hash-table :test 'eq :size 5000)))
+ (with-metrics (t nil "cells statistics for" self)
+ (labels ((cc (self from)
+ (unless (gethash self *counted*)
+ (setf (gethash self *counted*) t)
+ (typecase self
+ (cons (cc (car self) from)
+ (cc (cdr self) from))
+ #+nahhhh (mathx::box (count-it! :mathx-box-struct)
+ (cc (mathx::bx-mx self) from))
+ (model
+ (when (zerop (mod (incf ccc) 100))
+ (trc "cc" (md-name self) (type-of self)))
+ (count-it! :thing)
+ (count-it! :thing (type-of self))
+ #+nahhhh (when (typep self 'mathx::problem)
+ (count-it! :thing-from (type-of self) (type-of from)))
+ (when count-cells
+ (loop for (nil . c) in (cells self)
+ do (count-it! :live-cell)
+ ;(count-it! :live-cell id)
+ (when (c-lazy c)
+ (count-it! :lazy)
+ (count-it! :lazy (c-value-state c)))
+ (typecase c
+ (c-dependent
+ (count-it! :dependent-cell)
+ #+chill (loop repeat (length (c-useds c))
+ do (count-it! :cell-useds)
+ (count-it! :dep-depth (c-depend-depth c))))
+ (otherwise (if (c-inputp c)
+ (progn
+ (count-it! :c-input-altogether)
+ ;(count-it! :c-input id)
+ )
+ (count-it! :c-unknown))))
+
+ (loop repeat (length (c-callers c))
+ do (count-it! :cell-callers)))
+
+ (loop repeat (length (cells-flushed self))
+ do (count-it! :flushed-cell #+toomuchinfo id)))
+
+ (loop for slot in (md-owning-slots self) do
+ (loop for k in (let ((sv (SLOT-VALUE self slot)))
+ (if (listp sv) sv (list sv)))
+ do (cc k self)))
+ #+nahhh
+ (progn
+ (when (typep self 'mathx::mx-optr)
+ (cc (mathx::opnds self) from))
+ (when (typep self 'mathx::math-expression)
+ (count-it! :math-expression))))
+ (otherwise
+ (count-it (type-of self)))))))
+ (cc self nil)))))
+
+(defun c-depend-depth (ctop)
+ (if (null (c-useds ctop))
+ 0
+ (or (gethash ctop *c-d-d*)
+ (labels ((cdd (c &optional (depth 1) chain)
+ (when (and (not (c-useds c))
+ (> depth *max-d-d*))
+ (setf *max-d-d* depth)
+ (trc "new dd champ from user" depth :down-to c)
+ (when (= depth 41)
+ (trc "end at" (c-slot-name c) :of (type-of (c-model c)))
+ (loop for c in chain do
+ (trc "called by" (c-slot-name c) :of (type-of (c-model c))))))
+ (setf (gethash c *c-d-d*)
+ ;(break "c-depend-depth ~a" c)
+ (progn
+ ;(trc "dd" c)
+ (1+ (loop for u in (c-useds c)
+ maximizing (cdd u (1+ depth) (cons c chain))))))))
+ (cdd ctop)))))
+
\ No newline at end of file
Added: dependencies/trunk/cells/model-object.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/model-object.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,331 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+;;; --- model-object ----------------------
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(md-name fm-parent .parent )))
+
+(defclass model-object ()
+ ((.md-state :initform :nascent :accessor md-state) ; [nil | :nascent | :alive | :doomed]
+ (.awaken-on-init-p :initform nil :initarg :awaken-on-init-p :accessor awaken-on-init-p)
+ (.cells :initform nil :accessor cells)
+ (.cells-flushed :initform nil :accessor cells-flushed
+ :documentation "cells supplied but un-whenned or optimized-away")
+ (adopt-ct :initform 0 :accessor adopt-ct)))
+
+(defmethod register? ((self model-object)))
+
+(defmethod md-state ((self symbol))
+ :alive)
+;;; --- md obj initialization ------------------
+
+(defmethod shared-initialize :after ((self model-object) slotnames
+ &rest initargs &key fm-parent)
+ (declare (ignorable initargs slotnames fm-parent))
+ (setf (md-census-count self) 1) ;; bad idea if we get into reinitializing
+ ;
+ ; for convenience and transparency of mechanism we allow client code
+ ; to intialize a slot to a cell, but we want the slot to hold the functional
+ ; value, partly for ease of inspection, partly for performance, mostly
+ ; because sometimes we are a slave to other libraries, such as a persistence
+ ; library that does interesting things automatically based on the slot value.
+ ;
+ ; here we shuttle cells out of the slots and into a per-instance dictionary of cells,
+ ; as well as tell the cells what slot and instance they are mediating.
+ ;
+
+ (when (slot-boundp self '.md-state)
+ (loop for esd in (class-slots (class-of self))
+ for sn = (slot-definition-name esd)
+ for sv = (when (slot-boundp self sn)
+ (slot-value self sn))
+ ;; do (print (list (type-of self) sn sv (typep sv 'cell)))
+ when (typep sv 'cell)
+ do (if (md-slot-cell-type (type-of self) sn)
+ (md-install-cell self sn sv)
+ (when *c-debug*
+ (break "warning: cell ~a offered for non-cellular model/slot ~a/~a" sv sn (type-of self)))))
+ ;
+ ; queue up for awakening
+ ;
+ (if (awaken-on-init-p self)
+ (md-awaken self)
+ (with-integrity (:awaken self)
+ (md-awaken self)))
+ ))
+
+(defun md-install-cell (self slot-name c &aux (c-isa-cell (typep c 'cell)))
+ ;
+ ; iff cell, init and move into dictionary
+ ;
+ (when c-isa-cell
+ (count-it :md-install-cell)
+ (setf
+ (c-model c) self
+ (c-slot-name c) slot-name
+ (md-slot-cell self slot-name) c))
+ ;
+ ; now have the slot really be the slot
+ ;
+ (if c-isa-cell
+ (if (c-unboundp c)
+ (bd-slot-makunbound self slot-name)
+ (if self
+ (setf (slot-value self slot-name)
+ (when (c-inputp c) (c-value c)))
+ (setf (symbol-value slot-name)
+ (when (c-inputp c) (c-value c)))))
+ ;; note that in this else branch "c" is a misnomer since
+ ;; the value is not actually a cell
+ (if self
+ (setf (slot-value self slot-name) c)
+ (setf (symbol-value slot-name) c))))
+
+
+;;; --- awaken --------
+;
+; -- do initial evaluation of all ruled slots
+; -- call observers of all slots
+
+
+
+(export! md-awake-ct md-awake-ct-ct)
+(defun md-awake-ct ()
+ *awake-ct*)
+
+(defun md-awake-ct-ct ()
+ (reduce '+ *awake-ct* :key 'cdr))
+
+
+(defmethod md-awaken :around ((self model-object))
+ (when (eql :nascent (md-state self))
+ #+nahh (bif (a (assoc (type-of self) *awake-ct*))
+ (incf (cdr a))
+ (push (cons (type-of self) 1) *awake-ct*))
+ ;(trc "awake" (type-of self))
+ #+chya (push self *awake*)
+ (call-next-method))
+ self)
+
+#+test
+(md-slot-cell-type 'cgtk::label 'cgtk::container)
+
+(defmethod md-awaken ((self model-object))
+ ;
+ ; --- debug stuff
+ ;
+ (when *stop*
+ (princ #\.)
+ (return-from md-awaken))
+ (trc nil "md-awaken entry" self (md-state self))
+ (c-assert (eql :nascent (md-state self)))
+ (count-it :md-awaken)
+ ;(count-it 'mdawaken (type-of self))
+
+ ; ---
+
+ (setf (md-state self) :awakening)
+
+ (dolist (esd (class-slots (class-of self)))
+ (bwhen (sct (md-slot-cell-type (type-of self) (slot-definition-name esd)))
+ (let* ((slot-name (slot-definition-name esd))
+ (c (md-slot-cell self slot-name)))
+ (when *c-debug*
+ (bwhen (sv (and (slot-boundp self slot-name)
+ (slot-value self slot-name)))
+ (when (typep sv 'cell)
+ (c-break "md-awaken ~a found cell ~a in slot ~a" self sv esd))))
+
+ (cond
+ ((not c)
+ ;; all slots must hit any change handlers as instances come into existence to get
+ ;; models fully connected to the outside world they are controlling. that
+ ;; happens in awaken-cell for slots in fact mediated by cells, but as an
+ ;; optimization we allow raw literal values to be specified for a slot, in
+ ;; which case heroic measures are needed to get the slot to the change handler
+ ;;
+ ;; next is an indirect and brittle way to determine that a slot has already been output,
+ ;; but I think anything better creates a run-time hit.
+ ;;
+ ;; until 2007-10 (unless (cdr (assoc slot-name (cells-flushed self))) ;; make sure not flushed
+ ;; but first I worried about it being slow keeping the flushed list /and/ searching, then
+ ;; I wondered why a flushed cell should not be observed, constant cells are. So Just Observe It
+
+ (let ((flushed (md-slot-cell-flushed self slot-name)))
+ (when (or (null flushed) ;; constant, ie, never any cell provided for this slot
+ (> *data-pulse-id* (c-pulse-observed flushed))) ;; unfrickinlikely
+ (when flushed
+ (setf (c-pulse-observed flushed) *data-pulse-id*)) ;; probably unnecessary
+ (slot-value-observe slot-name self (bd-slot-value self slot-name) nil nil flushed))))
+
+ ((find (c-lazy c) '(:until-asked :always t))
+ (trc nil "md-awaken deferring c-awaken since lazy"
+ self esd))
+
+ ((eq :nascent (c-state c))
+ (c-assert (c-model c) () "c-awaken sees uninstalled cell" c)
+ (c-assert (eq :nascent (c-state c)))
+ (trc nil "c-awaken > awakening" c)
+ (count-it :c-awaken)
+
+ (setf (c-state c) :awake)
+ (awaken-cell c))))))
+
+ (setf (md-state self) :awake)
+ self)
+
+;;; --- utilities, accessors, etc --------------------------------------
+
+(defmethod c-slot-value ((self model-object) slot)
+ (slot-value self slot))
+
+(defmethod md-slot-cell (self slot-name)
+ (if self
+ (cdr (assoc slot-name (cells self)))
+ (get slot-name 'cell)))
+
+(defmethod md-slot-cell-flushed (self slot-name)
+ (if self
+ (cdr (assoc slot-name (cells-flushed self)))
+ (get slot-name 'cell)))
+
+#+test
+(get 'cgtk::label :cell-types)
+
+(defun md-slot-cell-type (class-name slot-name)
+ (assert class-name)
+ (if (eq class-name 'null)
+ (get slot-name :cell-type)
+ (bif (entry (assoc slot-name (get class-name :cell-types)))
+ (cdr entry)
+ (dolist (super (class-precedence-list (find-class class-name))
+ (setf (md-slot-cell-type class-name slot-name) nil))
+ (bwhen (entry (assoc slot-name (get (c-class-name super) :cell-types)))
+ (return-from md-slot-cell-type
+ (setf (md-slot-cell-type class-name slot-name) (cdr entry))))))))
+
+(defun (setf md-slot-cell-type) (new-type class-name slot-name)
+ (assert class-name)
+ (if (eq class-name 'null) ;; not def-c-variable
+ (setf (get slot-name :cell-type) new-type)
+ (let ((entry (assoc slot-name (get class-name :cell-types))))
+ (if entry
+ (prog1
+ (setf (cdr entry) new-type)
+ (loop for c in (class-direct-subclasses (find-class class-name))
+ do (setf (md-slot-cell-type (class-name c) slot-name) new-type)))
+ (cdar (push (cons slot-name new-type) (get class-name :cell-types)))))))
+
+#+test
+(md-slot-owning? 'm-index '.value)
+
+(defun md-slot-owning? (class-name slot-name)
+ (assert class-name)
+ (if (eq class-name 'null)
+ (get slot-name :owning) ;; might be wrong -- support for specials is unfinished w.i.p.
+ (bif (entry (assoc slot-name (get class-name :direct-ownings)))
+ (cdr entry)
+ (bif (entry (assoc slot-name (get class-name :indirect-ownings)))
+ (cdr entry)
+ (cdar
+ (push (cons slot-name
+ (cdr (loop for super in (cdr (class-precedence-list (find-class class-name)))
+ thereis (assoc slot-name (get (c-class-name super) :direct-ownings)))))
+ (get class-name :indirect-ownings)))))))
+
+(defun (setf md-slot-owning-direct?) (value class-name slot-name)
+ (assert class-name)
+ (if (eq class-name 'null) ;; global variables
+ (setf (get slot-name :owning) value)
+ (progn
+ (bif (entry (assoc slot-name (get class-name :direct-ownings)))
+ (setf (cdr entry) value)
+ (push (cons slot-name value) (get class-name :direct-ownings)))
+ ; -- propagate to derivatives ...
+ (labels ((clear-subclass-ownings (c)
+ (loop for sub-c in (class-direct-subclasses c)
+ for sub-c-name = (c-class-name sub-c)
+ do (setf (get sub-c-name :indirect-ownings)
+ (delete slot-name (get sub-c-name :indirect-ownings) :key 'car)) ;; forces redecide
+ (setf (get sub-c-name :model-ownings) nil) ;; too much forcing full recalc like this?
+ (clear-subclass-ownings sub-c))))
+ (clear-subclass-ownings (find-class class-name))))))
+
+(defun md-owning-slots (self &aux (st (type-of self)))
+ (or (get st :model-ownings)
+ (setf (get st :model-ownings)
+ (loop for s in (class-slots (class-of self))
+ for sn = (slot-definition-name s)
+ when (and (md-slot-cell-type st sn)
+ (md-slot-owning? st sn))
+ collect sn))))
+
+#+test
+(md-slot-owning? 'cells::family '.kids)
+
+(defun md-slot-value-store (self slot-name new-value)
+ (trc nil "md-slot-value-store" self slot-name new-value)
+ (if self
+ (setf (slot-value self slot-name) new-value)
+ (setf (symbol-value slot-name) new-value)))
+
+;----------------- navigation: slot <> initarg <> esd <> cell -----------------
+
+#+cmu
+(defmethod c-class-name ((class pcl::standard-class))
+ (pcl::class-name class))
+
+(defmethod c-class-name (other) (declare (ignore other)) nil)
+
+;; why not #-cmu?
+(defmethod c-class-name ((class standard-class))
+ (class-name class))
+
+(defmethod cell-when (other) (declare (ignorable other)) nil)
+
+(defun (setf md-slot-cell) (new-cell self slot-name)
+ (if self ;; not on def-c-variables
+ (bif (entry (assoc slot-name (cells self)))
+ ; this next branch guessed it would only occur during kid-slotting,
+ ; before any dependency-ing could have happened, but a math-editor
+ ; is silently switching between implied-multiplication and mixed numbers
+ ; while they type and it
+ (progn
+ (trc nil "second cell same slot:" slot-name :old entry :new new-cell)
+ (let ((old (cdr entry))) ;; s/b being supplanted by kid-slotter
+ (declare (ignorable old))
+ (c-assert (null (c-callers old)))
+ (when (typep entry 'c-dependent)
+ (c-assert (null (cd-useds old))))
+ (trc nil "replacing in model .cells" old new-cell self)
+ (rplacd entry new-cell)))
+ (progn
+ (trc nil "adding to model .cells" new-cell self)
+ (push (cons slot-name new-cell)
+ (cells self))))
+ (setf (get slot-name 'cell) new-cell)))
+
+(defun md-map-cells (self type celldo)
+ (map type (lambda (cell-entry)
+ (bwhen (cell (cdr cell-entry))
+ (unless (listp cell)
+ (funcall celldo cell))))
+ (cells self)))
Added: dependencies/trunk/cells/propagate.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/propagate.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,291 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+;----------------- change detection ---------------------------------
+
+(defun c-no-news (c new-value old-value)
+ ;;; (trc nil "c-no-news > checking news between" newvalue oldvalue)
+ (bif (test (c-unchanged-test (c-model c) (c-slot-name c)))
+ (funcall test new-value old-value)
+ (eql new-value old-value)))
+
+(defmacro def-c-unchanged-test ((class slotname) &body test)
+ `(defmethod c-unchanged-test ((self ,class) (slotname (eql ',slotname)))
+ , at test))
+
+(defmethod c-unchanged-test (self slotname)
+ (declare (ignore self slotname))
+ nil)
+
+; --- data pulse (change ID) management -------------------------------------
+
+(defparameter *one-pulse?* nil)
+
+(defun data-pulse-next (pulse-info)
+ (declare (ignorable pulse-info))
+ (unless *one-pulse?*
+ ;(trc "dp-next> " (1+ *data-pulse-id*) pulse-info)
+ #+chill (when *c-debug*
+ (push (list :data-pulse-next pulse-info) *istack*))
+ (incf *data-pulse-id*)))
+
+(defun c-currentp (c)
+ (eql (c-pulse c) *data-pulse-id*))
+
+(defun c-pulse-update (c key)
+ (declare (ignorable key))
+ (unless (find key '(:valid-uninfluenced))
+ (trc nil "!!!!!!! c-pulse-update updating !!!!!!!!!!" *data-pulse-id* c key :prior-pulse (c-pulse c)))
+ (assert (>= *data-pulse-id* (c-pulse c)) ()
+ "Current DP ~a not GE pulse ~a of cell ~a" *data-pulse-id* (c-pulse c) c)
+ (setf (c-pulse c) *data-pulse-id*))
+
+;--------------- propagate ----------------------------
+; n.b. the cell argument may have been optimized away,
+; though it is still receiving final processing here.
+
+(defparameter *per-cell-handler* nil)
+
+(defun c-propagate (c prior-value prior-value-supplied)
+ (when *one-pulse?*
+ (when *per-cell-handler*
+ (funcall *per-cell-handler* c prior-value prior-value-supplied)
+ (return-from c-propagate)))
+
+ (count-it :cpropagate)
+ (setf (c-pulse-last-changed c) *data-pulse-id*)
+
+ (when prior-value
+ (assert prior-value-supplied () "How can prior-value-supplied be nil if prior-value is not?!! ~a" c))
+ (let (*depender* *call-stack* ;; I think both need clearing, cuz we are neither depending nor calling when we prop to callers
+ (*c-prop-depth* (1+ *c-prop-depth*))
+ (*defer-changes* t))
+ (trc nil "c.propagate clearing *depender*" c)
+
+ ;------ debug stuff ---------
+ ;
+ (when *stop*
+ (princ #\.)(princ #\!)
+ (return-from c-propagate))
+ (trc nil "c.propagate> !!!!!!! propping" c (c-value c) :caller-ct (length (c-callers c)))
+ #+slow (trc nil "c.propagate> !!!! new value" (c-value c) :prior-value prior-value :caller-ct (length (c-callers c)) c)
+ (when *c-debug*
+ (when (> *c-prop-depth* 250)
+ (trc nil "c.propagate deep" *c-prop-depth* (c-model c) (c-slot-name c) #+nah c))
+ (when (> *c-prop-depth* 300)
+ (c-break "c.propagate looping ~c" c)))
+
+ ; --- manifest new value as needed ---
+ ;
+ ; 20061030 Trying not.to.be first because doomed instances may be interested in callers
+ ; who will decide to propagate. If a family instance kids slot is changing, a doomed kid
+ ; will be out of the kids but not yet quiesced. If the propagation to this rule asks the kid
+ ; to look at its siblings (say a view instance being deleted from a stack who looks to the psib
+ ; pb to decide its own pt), the doomed kid will still have a parent but not be in its kids slot
+ ; when it goes looking for a sibling relative to its position.
+ ;
+ (when (and prior-value-supplied
+ prior-value
+ (md-slot-owning? (type-of (c-model c)) (c-slot-name c)))
+ (trc nil "c.propagate> contemplating lost" (qci c))
+ (flet ((listify (x) (if (listp x) x (list x))))
+ (bif (lost (set-difference (listify prior-value) (listify (c-value c))))
+ (progn
+ (trc nil "prop nailing owned!!!!!!!!!!!" (qci c) :lost (length lost)) ;; :leaving (c-value c))
+ (loop for l in lost
+ when (numberp l)
+ do (break "got num ~a" (list l (type-of (c-model c))(c-slot-name c)
+ (md-slot-owning? (type-of (c-model c)) (c-slot-name c)))))
+ (mapcar 'not-to-be lost))
+ (trc nil "no owned lost!!!!!"))))
+
+ ; propagation to callers jumps back in front of client slot-value-observe handling in cells3
+ ; because model adopting (once done by the kids change handler) can now be done in
+ ; shared-initialize (since one is now forced to supply the parent to make-instance).
+ ;
+ ; we wnat it here to support (eventually) state change rollback. change handlers are
+ ; expected to have side-effects, so we want to propagate fully and be sure no rule
+ ; wants a rollback before starting with the side effects.
+ ;
+ (progn ;; unless (member (c-lazy c) '(t :always :once-asked)) ;; 2006-09-26 still fuzzy on this
+ (c-propagate-to-callers c))
+
+ (trc nil "c.propagate observing" c)
+
+ ; this next assertion is just to see if we can ever come this way twice. If so, just
+ ; make it a condition on whether to observe
+ (when t ; breaks algebra (> *data-pulse-id* (c-pulse-observed c))
+ (setf (c-pulse-observed c) *data-pulse-id*)
+ (slot-value-observe (c-slot-name c) (c-model c)
+ (c-value c) prior-value prior-value-supplied c))
+
+
+ ;
+ ; with propagation done, ephemerals can be reset. we also do this in c-awaken, so
+ ; let the fn decide if C really is ephemeral. Note that it might be possible to leave
+ ; this out and use the datapulse to identify obsolete ephemerals and clear them
+ ; when read. That would avoid ever making again bug I had in which I had the reset inside slot-value-observe,
+ ; thinking that that always followed propagation to callers. It would also make
+ ; debugging easier in that I could find the last ephemeral value in the inspector.
+ ; would this be bad for persistent CLOS, in which a DB would think there was still a link
+ ; between two records until the value actually got cleared?
+ ;
+ (ephemeral-reset c)))
+
+; --- slot change -----------------------------------------------------------
+
+(defmacro defobserver (slotname &rest args &aux (aroundp (eq :around (first args))))
+ (when aroundp (setf args (cdr args)))
+ (when (find slotname '(value kids))
+ (break "d: did you mean .value or .kids when you coded ~a?" slotname))
+ (destructuring-bind ((&optional (self-arg 'self) (new-varg 'new-value)
+ (oldvarg 'old-value) (oldvargboundp 'old-value-boundp) (cell-arg 'c))
+ &body output-body) args
+ `(progn
+ (eval-when (:compile-toplevel :load-toplevel :execute)
+ (setf (get ',slotname :output-defined) t))
+ ,(if (eql (last1 output-body) :test)
+ (let ((temp1 (gensym))
+ (loc-self (gensym)))
+ `(defmethod slot-value-observe #-(or cormanlisp) ,(if aroundp :around 'progn)
+ ((slotname (eql ',slotname)) ,self-arg ,new-varg ,oldvarg ,oldvargboundp ,cell-arg)
+ (let ((,temp1 (bump-output-count ,slotname))
+ (,loc-self ,(if (listp self-arg)
+ (car self-arg)
+ self-arg)))
+ (when (and ,oldvargboundp ,oldvarg)
+ (format t "~&output ~d (~a ~a) old: ~a" ,temp1 ',slotname ,loc-self ,oldvarg ,cell-arg))
+ (format t "~&output ~d (~a ~a) new: ~a" ,temp1 ',slotname ,loc-self ,new-varg ,cell-arg))))
+ `(defmethod slot-value-observe
+ #-(or cormanlisp) ,(if aroundp :around 'progn)
+ ((slotname (eql ',slotname)) ,self-arg ,new-varg ,oldvarg ,oldvargboundp ,cell-arg)
+ (declare (ignorable
+ ,@(flet ((arg-name (arg-spec)
+ (etypecase arg-spec
+ (list (car arg-spec))
+ (atom arg-spec))))
+ (list (arg-name self-arg)(arg-name new-varg)
+ (arg-name oldvarg)(arg-name oldvargboundp) (arg-name cell-arg)))))
+ , at output-body)))))
+
+(defmacro bump-output-count (slotname) ;; pure test func
+ `(if (get ',slotname :outputs)
+ (incf (get ',slotname :outputs))
+ (setf (get ',slotname :outputs) 1)))
+
+; --- recalculate dependents ----------------------------------------------------
+
+
+(defmacro cll-outer (val &body body)
+ `(let ((outer-val ,val))
+ , at body))
+
+(defmacro cll-inner (expr)
+ `(,expr outer-val))
+
+(export! cll-outer cll-inner)
+
+(defun c-propagate-to-callers (c)
+ ;
+ ; We must defer propagation to callers because of an edge case in which:
+ ; - X tells A to recalculate
+ ; - A asks B for its current value
+ ; - B must recalculate because it too uses X
+ ; - if B propagates to its callers after recalculating instead of deferring it
+ ; - B might tell H to reclaculate, where H decides this time to use A
+ ; - but A is in the midst of recalculating, and cannot complete until B returns.
+ ; but B is busy eagerly propagating. "This time" is important because it means
+ ; there is no way one can reliably be sure H will not ask for A
+ ;
+ (when (find-if-not (lambda (caller)
+ (and (c-lazy caller) ;; slight optimization
+ (member (c-lazy caller) '(t :always :once-asked))))
+ (c-callers c))
+ (let ((causation (cons c *causation*))) ;; in case deferred
+ #+slow (trc nil "c.propagate-to-callers > queueing notifying callers" (c-callers c))
+ (with-integrity (:tell-dependents c)
+ (assert (null *call-stack*))
+ (assert (null *depender*))
+ ;
+ (if (mdead (c-model c))
+ (trc nil "WHOAA!!!! dead by time :tell-deps dispatched; bailing" c)
+ (let ((*causation* causation))
+ (trc nil "c.propagate-to-callers > actually notifying callers of" c (c-callers c))
+ #+c-debug (dolist (caller (c-callers c))
+ (assert (find c (cd-useds caller)) () "test 1 failed ~a ~a" c caller))
+ #+c-debug (dolist (caller (copy-list (c-callers c))) ;; following code may modify c-callers list...
+ (trc nil "PRE-prop-CHECK " c :caller caller (c-state caller) (c-lazy caller))
+ (unless (or (eq (c-state caller) :quiesced) ;; ..so watch for quiesced
+ (member (c-lazy caller) '(t :always :once-asked)))
+ (assert (find c (cd-useds caller))() "Precheck Caller ~a of ~a does not have it as used" caller c)
+ ))
+ (dolist (caller (c-callers c))
+ (trc nil "propagating to caller iterates" c :caller caller (c-state caller) (c-lazy caller))
+ (block do-a-caller
+ (unless (or (eq (c-state caller) :quiesced) ;; ..so watch for quiesced
+ (member (c-lazy caller) '(t :always :once-asked)))
+ (unless (find c (cd-useds caller))
+ (trc "WHOA!!!! Bailing on Known caller:" caller :does-not-in-its-used c)
+ (return-from do-a-caller))
+ #+slow (trc nil "propagating to caller is used" c :caller caller (c-currentp c))
+ (let ((*trc-ensure* (trcp c)))
+ ;
+ ; we just calculate-and-set at the first level of dependency because
+ ; we do not need to check the next level (as ensure-value-is-current does)
+ ; because we already know /this/ notifying dependency has changed, so yeah,
+ ; any first-level cell /has to/ recalculate. (As for ensuring other dependents
+ ; of the first level guy are current, that happens automatically anyway JIT on
+ ; any read.) This is a minor efficiency enhancement since ensure-value-is-current would
+ ; very quickly decide it has to re-run, but maybe it makes the logic clearer.
+ ;
+ ;(ensure-value-is-current caller :prop-from c) <-- next was this, but see above change reason
+ ;
+ (unless (c-currentp caller) ; happens if I changed when caller used me in current pulse
+ (calculate-and-set caller :propagate c))))))))))))
+
+(defparameter *the-unpropagated* nil)
+
+(defmacro with-one-datapulse ((&key (per-cell nil per-cell?) (finally nil finally?)) &body body)
+ `(call-with-one-datapulse (lambda () , at body)
+ ,@(when per-cell? `(:per-cell (lambda (c prior-value prior-value-boundp)
+ (declare (ignorable c prior-value prior-value-boundp))
+ ,per-cell)))
+ ,@(when finally? `(:finally (lambda (cs) (declare (ignorable cs)) ,finally)))))
+
+(defun call-with-one-datapulse
+ (f &key
+ (per-cell (lambda (c prior-value prior-value?)
+ (unless (find c *the-unpropagated* :key 'car)
+ (pushnew (list c prior-value prior-value?) *the-unpropagated*))))
+ (finally (lambda (cs)
+ (print `(finally sees ,*data-pulse-id* ,cs))
+ ;(trace c-propagate ensure-value-is-current)
+ (loop for (c prior-value prior-value?) in (nreverse cs) do
+ (c-propagate c prior-value prior-value?)))))
+ (assert (not *one-pulse?*))
+ (data-pulse-next :client-prop)
+ (trc "call-with-one-datapulse bumps pulse" *data-pulse-id*)
+ (funcall finally
+ (let ((*one-pulse?* t)
+ (*per-cell-handler* per-cell)
+ (*the-unpropagated* nil))
+ (funcall f)
+ *the-unpropagated*)))
+
Added: dependencies/trunk/cells/slot-utilities.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/slot-utilities.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,97 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defun c-setting-debug (self slot-name c new-value)
+ (declare (ignorable new-value))
+ (cond
+ ((null c)
+ (format t "c-setting-debug > constant ~a in ~a may not be altered..init to (c-in nil)"
+ slot-name self)
+
+ (c-break "setting-const-cell")
+ (error "setting-const-cell"))
+ ((c-inputp c))
+ (t
+ (let ((self (c-model c))
+ (slot-name (c-slot-name c)))
+ ;(trc "c-setting-debug sees" c newvalue self slot-name)
+ (when (and c (not (and slot-name self)))
+ ;; cv-test handles errors, so don't set *stop* (c-stop)
+ (c-break "unadopted ~a for self ~a spec ~a" c self slot-name)
+ (error 'c-unadopted :cell c))
+ #+whocares (typecase c
+ (c-dependent
+ ;(trc "setting c-dependent" c newvalue)
+ (format t "c-setting-debug > ruled ~a in ~a may not be setf'ed"
+ (c-slot-name c) self)
+
+ (c-break "setting-ruled-cell")
+ (error "setting-ruled-cell"))
+ )))))
+
+(defun c-absorb-value (c value)
+ (typecase c
+ (c-drifter-absolute (c-value-incf c value 0)) ;; strange but true
+ (c-drifter (c-value-incf c (c-value c) value))
+ (t value)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(c-value-incf)))
+
+(defmethod c-value-incf (c (envaluer c-envaluer) delta)
+ (c-assert (c-model c))
+ (c-value-incf c (funcall (envalue-rule envaluer) c)
+ delta))
+
+(defmethod c-value-incf (c (base number) delta)
+ (declare (ignore c))
+ (if delta
+ (+ base delta)
+ base))
+
+
+;----------------------------------------------------------------------
+
+(defun bd-slot-value (self slot-name)
+ (slot-value self slot-name))
+
+(defun (setf bd-slot-value) (new-value self slot-name)
+ (setf (slot-value self slot-name) new-value))
+
+(defun bd-bound-slot-value (self slot-name caller-id)
+ (declare (ignorable caller-id))
+ (when (bd-slot-boundp self slot-name)
+ (bd-slot-value self slot-name)))
+
+(defun bd-slot-boundp (self slot-name)
+ (slot-boundp self slot-name))
+
+(defun bd-slot-makunbound (self slot-name)
+ (if slot-name ;; not in def-c-variable
+ (slot-makunbound self slot-name)
+ (makunbound self)))
+
+#| sample incf
+(defmethod c-value-incf ((base fpoint) delta)
+ (declare (ignore model))
+ (if delta
+ (fp-add base delta)
+ base))
+|#
Added: dependencies/trunk/cells/synapse-types.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/synapse-types.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,152 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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! f-find)
+
+(defmacro f-find (synapse-id sought where)
+ `(call-f-find ,synapse-id ,sought ,where))
+
+(defun call-f-find (synapse-id sought where)
+ (with-synapse synapse-id ()
+ (bif (k (progn
+ (find sought where)))
+ (values k :propagate)
+ (values nil :no-propagate))))
+
+(defmacro f-sensitivity (synapse-id (sensitivity &optional subtypename) &body body)
+ `(call-f-sensitivity ,synapse-id ,sensitivity ,subtypename (lambda () , at body)))
+
+(defun call-f-sensitivity (synapse-id sensitivity subtypename body-fn)
+ (with-synapse synapse-id (prior-fire-value)
+ (let ((new-value (funcall body-fn)))
+ ;(trc "f-sensitivity fire-p decides new" new-value :from-prior prior-fire-value :sensi sensitivity)
+ (let ((prop-code (if (or (xor prior-fire-value new-value)
+ (eko (nil "sens fire-p decides" new-value prior-fire-value sensitivity)
+ (delta-greater-or-equal
+ (delta-abs (delta-diff new-value prior-fire-value subtypename)
+ subtypename)
+ (delta-abs sensitivity subtypename)
+ subtypename)))
+ :propagate
+ :no-propagate)))
+ (values (if (eq prop-code :propagate)
+ (progn
+ (trc nil "sense prior fire value now" new-value)
+ (setf prior-fire-value new-value))
+ new-value) prop-code)))))
+
+(defmacro f-delta (synapse-id (&key sensitivity (type 'number)) &body body)
+ `(call-f-delta ,synapse-id ,sensitivity ',type (lambda () , at body)))
+
+(defun call-f-delta (synapse-id sensitivity type body-fn)
+ (with-synapse synapse-id (last-relay-basis last-bound-p delta-cum)
+ (let* ((new-basis (funcall body-fn))
+ (threshold sensitivity)
+ (tdelta (delta-diff new-basis
+ (if last-bound-p
+ last-relay-basis
+ (delta-identity new-basis type))
+ type)))
+ (trc nil "tdelta, threshhold" tdelta threshold)
+ (setf delta-cum tdelta)
+ (let ((propagation-code
+ (when threshold
+ (if (delta-exceeds tdelta threshold type)
+ (progn
+ (setf last-bound-p t)
+ (setf last-relay-basis new-basis)
+ :propagate)
+ :no-propagate))))
+ (trc nil "f-delta returns values" delta-cum propagation-code)
+ (values delta-cum propagation-code)))))
+
+(defmacro f-plusp (key &rest body)
+ `(with-synapse ,key (prior-fire-value)
+ (let ((new-basis (progn , at body)))
+ (values new-basis (if (xor prior-fire-value (plusp new-basis))
+ (progn
+ (setf prior-fire-value (plusp new-basis))
+ :propagate)
+ :no-propagate)))))
+
+(defmacro f-zerop (key &rest body)
+ `(with-synapse ,key (prior-fire-value)
+ (let ((new-basis (progn , at body)))
+ (values new-basis (if (xor prior-fire-value (zerop new-basis))
+ (progn
+ (setf prior-fire-value (zerop new-basis))
+ :propagate)
+ :no-propagate)))))
+
+
+
+;;;(defun f-delta-list (&key (test #'true))
+;;; (with-synapse (prior-list)
+;;; :fire-p (lambda (syn new-list)
+;;; (declare (ignorable syn))
+;;; (or (find-if (lambda (new)
+;;; ;--- gaining one? ----
+;;; (and (not (member new prior-list))
+;;; (funcall test new)))
+;;; new-list)
+;;; (find-if (lambda (old)
+;;; ;--- losing one? ----
+;;; (not (member old new-list))) ;; all olds have passed test, so skip test here
+;;; prior-list)))
+;;;
+;;; :fire-value (lambda (syn new-list)
+;;; (declare (ignorable syn))
+;;; ;/// excess consing on long lists
+;;; (setf prior-list (remove-if-not test new-list)))))
+
+;;;(defun f-find-once (finder-fn)
+;;; (mk-synapse (bingo bingobound)
+;;;
+;;; :fire-p (lambda (syn new-list)
+;;; (declare (ignorable syn))
+;;; (unless bingo ;; once found, yer done
+;;; (setf bingobound t
+;;; bingo (find-if finder-fn new-list))))
+;;;
+;;; :fire-value (lambda (syn new-list)
+;;; (declare (ignorable syn))
+;;; (or bingo
+;;; (and (not bingobound) ;; don't bother if fire? already looked
+;;; (find-if finder-fn new-list))))))
+
+;;;(defun fdifferent ()
+;;; (mk-synapse (prior-object)
+;;; :fire-p (lambda (syn new-object)
+;;; (declare (ignorable syn))
+;;; (trc nil "fDiff: prior,new" (not (eql new-object prior-object))
+;;; prior-object new-object)
+;;; (not (eql new-object prior-object)))
+;;;
+;;; :fire-value (lambda (syn new-object)
+;;; (declare (ignorable syn))
+;;; (unless (eql new-object prior-object)
+;;; (setf prior-object new-object)))
+;;; ))
+
+
+;;;(defun f-boolean (&optional (sensitivity 't))
+;;; (f-delta :sensitivity sensitivity :type 'boolean))
+
+
Added: dependencies/trunk/cells/synapse.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/synapse.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,89 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(mk-synapse f-delta f-sensitivity f-plusp f-zerop fdifferent with-synapse)))
+
+(defmacro with-synapse (synapse-id (&rest closure-vars) &body body)
+ (let ((syn-id (gensym)))
+ `(let* ((,syn-id ,synapse-id)
+ (synapse (or (find ,syn-id (cd-useds *depender*) :key 'c-slot-name)
+ (let ((new-syn
+ (let (, at closure-vars)
+ (make-c-dependent
+ :model (c-model *depender*)
+ :slot-name ,syn-id
+ :code ',body
+ :synaptic t
+ :rule (c-lambda , at body)))))
+ (record-caller new-syn)
+ new-syn))))
+ (prog1
+ (multiple-value-bind (v p)
+ (with-integrity ()
+ (ensure-value-is-current synapse :synapse *depender*))
+ (values v p))
+ (record-caller synapse)))))
+
+
+;__________________________________________________________________________________
+;
+
+(defmethod delta-exceeds (bool-delta sensitivity (subtypename (eql 'boolean)))
+ (unless (eql bool-delta :unchanged)
+ (or (eq sensitivity t)
+ (eq sensitivity bool-delta))))
+
+(defmethod delta-diff ((new number) (old number) subtypename)
+ (declare (ignore subtypename))
+ (- new old))
+
+(defmethod delta-identity ((dispatcher number) subtypename)
+ (declare (ignore subtypename))
+ 0)
+
+(defmethod delta-abs ((n number) subtypename)
+ (declare (ignore subtypename))
+ (abs n))
+
+(defmethod delta-exceeds ((d1 number) (d2 number) subtypename)
+ (declare (ignore subtypename))
+ (> d1 d2))
+
+(defmethod delta-greater-or-equal ((d1 number) (d2 number) subtypename)
+ (declare (ignore subtypename))
+ (>= d1 d2))
+
+;_________________________________________________________________________________
+;
+(defmethod delta-diff (new old (subtypename (eql 'boolean)))
+ (if new
+ (if old
+ :unchanged
+ :on)
+ (if old
+ :off
+ :unchanged)))
+
+
+(defmethod delta-identity (dispatcher (subtypename (eql 'boolean)))
+ (declare (ignore dispatcher))
+ :unchanged)
+
Added: dependencies/trunk/cells/test-cc.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test-cc.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,40 @@
+(in-package :cells)
+
+(defmd tcc ()
+ (tccversion 1)
+ (tcc-a (c-in nil))
+ (tcc-2a (c-in nil)))
+
+(defobserver tcc-a ()
+ (case (^tccversion)
+ (1 (when new-value
+ (with-cc :tcc-a-obs
+ (setf (tcc-2a self) (* 2 new-value))
+ (with-cc :aha!2
+ (assert (eql (tcc-2a self) (* 2 new-value))
+ () "one")
+ (trc "one happy")))
+ (with-cc :aha!
+ (assert (eql (tcc-2a self) (* 2 new-value))
+ () "two"))))
+ (2 (when new-value
+ (with-cc :tcc-a-obs
+ (setf (tcc-2a self) (* 2 new-value))
+ (with-cc :aha!2
+ (assert (eql (tcc-2a self) (* 2 new-value))
+ () "one")
+ (trc "one happy")))))))
+
+
+(defun test-with-cc ()
+ (let ((self (make-instance 'tcc
+ :tccversion 2 ;:tcc-2a
+ )))
+ (trcx cool 42)
+ (setf (tcc-a self) 42)
+ (assert (and (numberp (tcc-2a self))
+ (= (tcc-2a self) 84)))))
+
+#+test
+(test-with-cc)
+
Added: dependencies/trunk/cells/test-cycle.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test-cycle.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,77 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+
+
+(defmodel m-cyc ()
+ ((m-cyc-a :initform (c-in nil) :initarg :m-cyc-a :accessor m-cyc-a)
+ (m-cyc-b :initform (c-in nil) :initarg :m-cyc-b :accessor m-cyc-b)))
+
+(def-c-output m-cyc-a ()
+ (print `(output m-cyc-a ,self ,new-value ,old-value))
+ (setf (m-cyc-b self) new-value))
+
+(def-c-output m-cyc-b ()
+ (print `(output m-cyc-b ,self ,new-value ,old-value))
+ (setf (m-cyc-a self) new-value))
+
+(defun m-cyc () ;;def-cell-test m-cyc
+ (let ((m (make-be 'm-cyc)))
+ (print `(start ,(m-cyc-a m)))
+ (setf (m-cyc-a m) 42)
+ (assert (= (m-cyc-a m) 42))
+ (assert (= (m-cyc-b m) 42))))
+
+#+(or)
+(m-cyc)
+
+(defmodel m-cyc2 ()
+ ((m-cyc2-a :initform (c-in 0) :initarg :m-cyc2-a :accessor m-cyc2-a)
+ (m-cyc2-b :initform (c? (1+ (^m-cyc2-a)))
+ :initarg :m-cyc2-b :accessor m-cyc2-b)))
+
+(def-c-output m-cyc2-a ()
+ (print `(output m-cyc2-a ,self ,new-value ,old-value))
+ #+(or) (when (< new-value 45)
+ (setf (m-cyc2-b self) (1+ new-value))))
+
+(def-c-output m-cyc2-b ()
+ (print `(output m-cyc2-b ,self ,new-value ,old-value))
+ (when (< new-value 45)
+ (setf (m-cyc2-a self) (1+ new-value))))
+
+(def-cell-test m-cyc2
+ (cell-reset)
+ (let ((m (make-be 'm-cyc2)))
+ (print '(start))
+ (setf (m-cyc2-a m) 42)
+ (describe m)
+ (assert (= (m-cyc2-a m) 44))
+ (assert (= (m-cyc2-b m) 45))
+ ))
+
+#+(or)
+(m-cyc2)
+
+
Added: dependencies/trunk/cells/test-ephemeral.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test-ephemeral.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,57 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+
+(defmodel m-ephem ()
+ ((m-ephem-a :cell :ephemeral :initform nil :initarg :m-ephem-a :accessor m-ephem-a)
+ (m-test-a :cell nil :initform nil :initarg :m-test-a :accessor m-test-a)
+ (m-ephem-b :cell :ephemeral :initform nil :initarg :m-ephem-b :accessor m-ephem-b)
+ (m-test-b :cell nil :initform nil :initarg :m-test-b :accessor m-test-b)))
+
+(def-c-output m-ephem-a ()
+ (setf (m-test-a self) new-value))
+
+(def-c-output m-ephem-b ()
+ (setf (m-test-b self) new-value))
+
+(def-cell-test m-ephem
+ (let ((m (make-be 'm-ephem :m-ephem-a (c-in nil) :m-ephem-b (c? (* 2 (or (^m-ephem-a) 0))))))
+ (ct-assert (null (slot-value m 'm-ephem-a)))
+ (ct-assert (null (m-ephem-a m)))
+ (ct-assert (null (m-test-a m)))
+ (ct-assert (null (slot-value m 'm-ephem-b)))
+ (ct-assert (null (m-ephem-b m)))
+ (ct-assert (zerop (m-test-b m)))
+ (setf (m-ephem-a m) 3)
+ (ct-assert (null (slot-value m 'm-ephem-a)))
+ (ct-assert (null (m-ephem-a m)))
+ (ct-assert (eql 3 (m-test-a m)))
+ ;
+ (ct-assert (null (slot-value m 'm-ephem-b)))
+ (ct-assert (null (m-ephem-b m)))
+ (ct-assert (eql 6 (m-test-b m)))
+ ))
+
+
+
Added: dependencies/trunk/cells/test-propagation.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test-propagation.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,45 @@
+(in-package :cells)
+
+(defmd tcp ()
+ (left (c-in 0))
+ (top (c-in 0))
+ (right (c-in 0))
+ (bottom (c-in 0))
+ (area (c? (trc "area running")
+ (* (- (^right)(^left))
+ (- (^top)(^bottom))))))
+
+(defobserver area ()
+ (TRC "new area" new-value old-value old-value-boundp :pulse *data-pulse-id*))
+
+(defobserver bottom ()
+ (TRC "new bottom" new-value old-value old-value-boundp :pulse *data-pulse-id*)
+ (with-integrity (:change 'bottom-tells-left)
+ (setf (^left) new-value)))
+
+(defobserver left ()
+ (TRC "new left" new-value old-value old-value-boundp :pulse *data-pulse-id*))
+
+(defun tcprop ()
+ (untrace)
+ (ukt:test-prep)
+ (LET ((box (make-instance 'tcp)))
+ (trc "changing top to 10" *data-pulse-id*)
+ (setf (top box) 10)
+ (trc "not changing top" *data-pulse-id*)
+ (setf (top box) 10)
+ (trc "changing right to 10" *data-pulse-id*)
+ (setf (right box) 10)
+ (trc "not changing right" *data-pulse-id*)
+ (setf (right box) 10)
+ (trc "changing bottom to -1" *data-pulse-id*)
+ (decf (bottom box))
+ (with-one-datapulse ()
+ (loop repeat 5 do
+ (trc "changing bottom by -1" *data-pulse-id*)
+ (decf (bottom box))))))
+
+
+
+
+
Added: dependencies/trunk/cells/test-synapse.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test-synapse.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,102 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(in-package :cells)
+
+
+(defmodel m-syn ()
+ ((m-syn-a :initform nil :initarg :m-syn-a :accessor m-syn-a)
+ (m-syn-b :initform nil :initarg :m-syn-b :accessor m-syn-b)
+ (m-syn-factor :initform nil :initarg :m-syn-factor :accessor m-syn-factor)
+ (m-sens :initform nil :initarg :m-sens :accessor m-sens)
+ (m-plus :initform nil :initarg :m-plus :accessor m-plus)
+ ))
+
+(def-c-output m-syn-b ()
+ (print `(output m-syn-b ,self ,new-value ,old-value)))
+
+
+
+(def-cell-test m-syn
+ (progn (cell-reset)
+ (let* ((delta-ct 0)
+ (sens-ct 0)
+ (plus-ct 0)
+ (m (make-be 'm-syn
+ :m-syn-a (c-in 0)
+ :m-syn-b (c? (incf delta-ct)
+ (trc nil "syn-b rule firing!!!!!!!!!!!!!!" delta-ct)
+ (eko (nil "syn-b rule returning")
+ (f-delta :syna-1 (:sensitivity 2)
+ (^m-syn-a))))
+ :m-syn-factor (c-in 1)
+ :m-sens (c? (incf sens-ct)
+ (trc nil "m-sens rule firing ~d !!!!!!!!!!!!!!" sens-ct)
+ (* (^m-syn-factor)
+ (f-sensitivity :sensa (3) (^m-syn-a))))
+ :m-plus (c? (incf plus-ct)
+ (trc nil "m-plus rule firing!!!!!!!!!!!!!!" plus-ct)
+ (f-plusp :syna-2 (- 2 (^m-syn-a)))))))
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (= 1 sens-ct))
+ (ct-assert (= 1 plus-ct))
+ (ct-assert (= 0 (m-sens m)))
+ (trc "make-be complete. about to incf m-syn-a")
+ (incf (m-syn-a m))
+ (ct-assert (= 1 delta-ct))
+ (ct-assert (= 1 sens-ct))
+ (ct-assert (= 1 plus-ct))
+ (ct-assert (= 0 (m-sens m)))
+ (trc "about to incf m-syn-a 2")
+ (incf (m-syn-a m) 2)
+ (trc nil "syn-b now" (m-syn-b m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 2 sens-ct))
+ (ct-assert (= 2 plus-ct))
+
+ (ct-assert (= 3 (m-sens m)))
+ (trc "about to incf m-syn-a")
+ (incf (m-syn-a m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 2 sens-ct))
+ (trc "about to incf m-syn-factor")
+ (incf (m-syn-factor m))
+ (ct-assert (= 3 sens-ct))
+ (ct-assert (= (m-sens m) (* (m-syn-factor m) (m-syn-a m))))
+ (trc "about to incf m-syn-a xxx")
+ (incf (m-syn-a m))
+ (ct-assert (= 2 delta-ct))
+ (ct-assert (= 3 sens-ct))
+ (trc "about to incf m-syn-a yyyy")
+ (incf (m-syn-a m))
+ (ct-assert (= 3 delta-ct))
+ (ct-assert (= 4 sens-ct))
+ (ct-assert (= 2 plus-ct))
+ (describe m)
+ (print '(start)))))
+
+(Def-c-output m-syn-a ()
+ (trc "!!! M-SYN-A now =" new-value))
+
+#+(or)
+(m-syn)
+
Added: dependencies/trunk/cells/test.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/test.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,228 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+#| Synapse Cell Unification Notes
+
+- start by making Cells synapse-y
+
+- make sure outputs show right old and new values
+- make sure outputs fire when they should
+
+- wow: test the Cells II dictates: no output callback sees stale data, no rule
+sees stale data, etc etc
+
+- test a lot of different synapses
+
+- make sure they fire when they should, and do not when they should not
+
+- make sure they survive an evaluation by the caller which does not branch to
+them (ie, does not access them)
+
+- make sure they optimize away
+
+- test with forms which access multiple other cells
+
+- look at direct alteration of a caller
+
+- does SETF honor not propagating, as well as a c-ruled after re-calcing
+
+- do diff unchanged tests such as string-equal work
+
+|#
+
+#| do list
+
+-- can we lose the special handling of the .kids slot?
+
+-- test drifters (and can they be handled without creating a special
+subclass for them?)
+
+|#
+
+(eval-when (compile load)
+ (proclaim '(optimize (speed 2) (safety 3) (space 1) (debug 3))))
+
+(in-package :cells)
+
+(defvar *cell-tests* nil)
+
+
+#+go
+(test-cells)
+
+(defun test-cells ()
+ (loop for test in (reverse *cell-tests*)
+ do (cell-test-init test)
+ (funcall test)))
+
+(defun cell-test-init (name)
+ (print (make-string 40 :initial-element #\!))
+ (print `(starting test ,name))
+ (print (make-string 40 :initial-element #\!))
+ (cell-reset))
+
+(defmacro def-cell-test (name &rest body)
+ `(progn
+ (pushnew ',name *cell-tests*)
+ (defun ,name ()
+ (cell-reset)
+ , at body)))
+
+(defmacro ct-assert (form &rest stuff)
+ `(progn
+ (print `(attempting ,',form))
+ (assert ,form () "Error with ~a >> ~a" ',form (list , at stuff))))
+
+;; test huge number of useds by one rule
+
+(defmodel m-index (family)
+ ()
+ (:default-initargs
+ :value (c? (bwhen (ks (^kids))
+ (apply '+ (mapcar 'value ks))))))
+
+(def-cell-test many-useds
+ (let ((i (make-instance 'm-index)))
+ (loop for n below 100
+ do (push (make-instance 'model
+ :value (c-in n))
+ (kids i)))
+ (trc "index total" (value i))))
+
+(defmodel m-null ()
+ ((aa :initform nil :cell nil :initarg :aa :accessor aa)))
+
+(def-cell-test m-null
+ (let ((m (make-be 'm-null :aa 42)))
+ (ct-assert (= 42 (aa m)))
+ (ct-assert (= 21 (decf (aa m) 21)))
+ :okay-m-null))
+
+(defmodel m-solo ()
+ ((m-solo-a :initform nil :initarg :m-solo-a :accessor m-solo-a)
+ (m-solo-b :initform nil :initarg :m-solo-b :accessor m-solo-b)))
+
+(def-cell-test m-solo
+ (let ((m (make-be 'm-solo
+ :m-solo-a (c-in 42)
+ :m-solo-b (c? (* 2 (^m-solo-a))))))
+ (ct-assert (= 42 (m-solo-a m)))
+ (ct-assert (= 84 (m-solo-b m)))
+ (decf (m-solo-a m))
+ (ct-assert (= 41 (m-solo-a m)))
+ (ct-assert (= 82 (m-solo-b m)))
+ :okay-m-null))
+
+(defmodel m-var ()
+ ((m-var-a :initform nil :initarg :m-var-a :accessor m-var-a)
+ (m-var-b :initform nil :initarg :m-var-b :accessor m-var-b)))
+
+(def-c-output m-var-b ()
+ (print `(output m-var-b ,self ,new-value ,old-value)))
+
+(def-cell-test m-var
+ (let ((m (make-be 'm-var :m-var-a (c-in 42) :m-var-b 1951)))
+ (ct-assert (= 42 (m-var-a m)))
+ (ct-assert (= 21 (decf (m-var-a m) 21)))
+ (ct-assert (= 21 (m-var-a m)))
+ :okay-m-var))
+
+(defmodel m-var-output ()
+ ((cbb :initform nil :initarg :cbb :accessor cbb)
+ (aa :cell nil :initform nil :initarg :aa :accessor aa)))
+
+(def-c-output cbb ()
+ (trc "output cbb" self)
+ (setf (aa self) (- new-value (if old-value-boundp
+ old-value 0))))
+
+(def-cell-test m-var-output
+ (let ((m (make-be 'm-var-output :cbb (c-in 42))))
+ (ct-assert (eql 42 (cbb m)))
+ (ct-assert (eql 42 (aa m)))
+ (ct-assert (eql 27 (decf (cbb m) 15)))
+ (ct-assert (eql 27 (cbb m)))
+ (ct-assert (eql -15 (aa m)))
+ (list :okay-m-var (aa m))))
+
+(defmodel m-var-linearize-setf ()
+ ((ccc :initform nil :initarg :ccc :accessor ccc)
+ (ddd :initform nil :initarg :ddd :accessor ddd)))
+
+(def-c-output ccc ()
+ (with-deference
+ (setf (ddd self) (- new-value (if old-value-boundp
+ old-value 0)))))
+
+(def-cell-test m-var-linearize-setf
+ (let ((m (make-be 'm-var-linearize-setf
+ :ccc (c-in 42)
+ :ddd (c-in 1951))))
+
+ (ct-assert (= 42 (ccc m)))
+ (ct-assert (= 42 (ddd m)))
+ (ct-assert (= 27 (decf (ccc m) 15)))
+ (ct-assert (= 27 (ccc m)))
+ (ct-assert (= -15 (ddd m)))
+ :okay-m-var))
+
+;;; -------------------------------------------------------
+
+(defmodel m-ruled ()
+ ((eee :initform nil :initarg :eee :accessor eee)
+ (fff :initform (c? (floor (^ccc) 2)) :initarg :fff :accessor fff)))
+
+(def-c-output eee ()
+ (print `(output> eee ,new-value old ,old-value)))
+
+(def-c-output fff ()
+ (print `(output> eee ,new-value old ,old-value)))
+
+(def-cell-test m-ruled
+ (let ((m (make-be 'm-ruled
+ :eee (c-in 42)
+ :fff (c? (floor (^eee) 2)))))
+ (trc "___Initial TOBE done____________________")
+ (print `(pulse ,*data-pulse-id*))
+ (ct-assert (= 42 (eee m)))
+ (ct-assert (= 21 (fff m)))
+ (ct-assert (= 36 (decf (eee m) 6)))
+ (print `(pulse ,*data-pulse-id*))
+ (ct-assert (= 36 (eee m)))
+ (ct-assert (= 18 (fff m)) m)
+ :okay-m-ruled))
+
+(defmodel m-worst-case ()
+ ((wc-x :accessor wc-x :initform (c-input () 2))
+ (wc-a :accessor wc-a :initform (c? (when (oddp (wc-x self))
+ (wc-c self))))
+ (wc-c :accessor wc-c :initform (c? (evenp (wc-x self))))
+ (wc-h :accessor wc-h :initform (c? (or (wc-c self)(wc-a self))))))
+
+(def-cell-test m-worst-case
+ (let ((m (make-be 'm-worst-case)))
+ (trc "___Initial TOBE done____________________")
+ (ct-assert (eql t (wc-c m)))
+ (ct-assert (eql nil (wc-a m)))
+ (ct-assert (eql t (wc-h m)))
+ (ct-assert (eql 3 (incf (wc-x m))))))
+
Added: dependencies/trunk/cells/trc-eko.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/trc-eko.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,170 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ The Newly Cells-aware TRC trace and EKO value echo facilities
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+;----------- trc -------------------------------------------
+(defparameter *last-trc* (get-internal-real-time))
+(defparameter *trcdepth* 0)
+
+(defun trcdepth-reset ()
+ (setf *trcdepth* 0))
+
+(defmacro trc (tgt-form &rest os)
+ (if (eql tgt-form 'nil)
+ '(progn)
+ (if (stringp tgt-form)
+ `(without-c-dependency
+ (call-trc t ,tgt-form , at os))
+ (let ((tgt (gensym)))
+ ;(break "slowww? ~a" tgt-form)
+ `(without-c-dependency
+ (bif (,tgt ,tgt-form)
+ (if (trcp ,tgt)
+ (progn
+ (assert (stringp ,(car os)) () "trc with test expected string second, got ~a" ,(car os))
+ (call-trc t , at os)) ;;,(car os) ,tgt ,@(cdr os)))
+ (progn
+ ;(trc "trcfailed")
+ (count-it :trcfailed)))
+ (count-it :tgtnileval)))))))
+
+(defun call-trc (stream s &rest os)
+ ;(break)
+ (if #+cormanlisp nil #-cormanlisp (and (boundp '*trcdepth*)
+ *trcdepth*)
+ (format stream "~&~v,,,'.<~d~>> " (mod *trcdepth* 100) *trcdepth*)
+ (format stream "~&"))
+ ;;(format stream " ~a " (round (- (get-internal-real-time) *last-trc*) 10))
+ (setf *last-trc* (get-internal-real-time))
+ (format stream "~a" s)
+ (let (pkwp)
+ (dolist (o os)
+ (format stream (if pkwp " ~(~s~)" " ~(~s~)") o) ;; save, used to insert divider, trcx dont like
+ (setf pkwp (keywordp o))))
+ (force-output stream)
+ (values))
+
+(export! brk brkx .bgo bgo)
+
+(define-symbol-macro .bgo
+ #+gimme-a-break (break "go")
+ #-gimme-a-break nil)
+
+(defmacro bgo (msg)
+ (declare (ignorable msg))
+ #+gimme-a-break `(break "BGO ~a" ',msg)
+ #-gimme-a-break `(progn))
+
+(defmacro brkx (msg)
+ (declare (ignorable msg))
+ #+gimme-a-break `(break "At ~a: OK?" ',msg)
+ #-gimme-a-break `(progn))
+
+(defmacro trcx (tgt-form &rest os)
+ (if (eql tgt-form 'nil)
+ '(progn)
+ `(without-c-dependency
+ (call-trc t ,(format nil "TX> ~(~s~)" tgt-form)
+ ,@(loop for obj in (or os (list tgt-form))
+ nconcing (list (intern (format nil "~a" obj) :keyword) obj))))))
+
+(defun call-trc-to-string (fmt$ &rest fmt-args)
+ (let ((o$ (make-array '(0) :element-type 'base-char
+ :fill-pointer 0 :adjustable t)))
+ (with-output-to-string (os-stream o$)
+ (apply 'call-trc os-stream fmt$ fmt-args))
+ o$))
+
+#+findtrcevalnils
+(defmethod trcp :around (other)
+ (unless (call-next-method other)(break)))
+
+(defmethod trcp (other)
+ (eq other t))
+
+(defmethod trcp (($ string))
+ t)
+
+(defun trcdepth-incf ()
+ (incf *trcdepth*))
+
+(defun trcdepth-decf ()
+ (format t "decrementing trc depth ~d" *trcdepth*)
+ (decf *trcdepth*))
+
+(defmacro wtrc ((&optional (min 1) (max 50) &rest banner) &body body )
+ `(let ((*trcdepth* (if *trcdepth*
+ (1+ *trcdepth*)
+ 0)))
+ ,(when banner `(when (>= *trcdepth* ,min)
+ (if (< *trcdepth* ,max)
+ (trc , at banner)
+ (progn
+ (break "excess trace notttt!!! ~d" *trcdepth*) ;; , at banner)
+ nil))))
+ (when (< *trcdepth* ,max)
+ , at body)))
+
+(defmacro wtrcx ((&key (min 1) (max 50) (on? t))(&rest banner) &body body )
+ `(let ((*trcdepth* (if *trcdepth*
+ (1+ *trcdepth*)
+ 0)))
+ ,(when banner `(when (and ,on? (>= *trcdepth* ,min))
+ (if (< *trcdepth* ,max)
+ (trc , at banner)
+ (progn
+ (break "excess trace notttt!!! ~d" *trcdepth*) ;; , at banner)
+ nil))))
+ (when (< *trcdepth* ,max)
+ , at body)))
+
+(defmacro wnotrc ((&optional (min 1) (max 50) &rest banner) &body body )
+ (declare (ignore min max banner))
+ `(progn , at body))
+
+;------ eko --------------------------------------
+
+(defmacro eko ((&rest trcargs) &rest body)
+ (let ((result (gensym)))
+ `(let ((,result , at body))
+ ,(if (stringp (car trcargs))
+ `(trc ,(car trcargs) :=> ,result ,@(cdr trcargs))
+ `(trc ,(car trcargs) ,(cadr trcargs) :=> ,result ,@(cddr trcargs)))
+ ,result)))
+
+(defmacro ekx (ekx-id &rest body)
+ (let ((result (gensym)))
+ `(let ((,result (, at body)))
+ (trc ,(string-downcase (symbol-name ekx-id)) :=> ,result)
+ ,result)))
+
+(defmacro eko-if ((&rest trcargs) &rest body)
+ (let ((result (gensym)))
+ `(let ((,result , at body))
+ (when ,result
+ (trc ,(car trcargs) :res ,result ,@(cdr trcargs)))
+ ,result)))
+
+(defmacro ek (label &rest body)
+ (let ((result (gensym)))
+ `(let ((,result (, at body)))
+ (when ,label
+ (trc ,label ,result))
+ ,result)))
+
Added: dependencies/trunk/cells/tutorial/01-lesson.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/01-lesson.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,40 @@
+(defmacro cells::ct-assert (form &rest stuff)
+ `(progn
+ (print `(attempting ,',form))
+ (assert ,form () "Error with ~a >> ~a" ',form (list , at stuff))))
+
+(defpackage #:tu-selfinit (:use :cl :cells))
+
+;;
+;; We will keep making new packages so we can incrementally develop the
+;; same class without newer versions stomping on earlier versions (by
+;; being in the same package and effectively redefining earlier versions).
+;;
+(in-package #:tu-selfinit)
+
+(defmodel rectangle ()
+ ((len :initarg :len :accessor len
+ :initform (c? (* 2 (width self))))
+ (width :initarg :width :initform nil :accessor width))
+ (:default-initargs
+ :width (c? (/ (len self) 2))))
+
+#+test
+(cells::ct-assert (eql 21 (width (make-instance 'rectangle :len 42))))
+
+;;; The first thing we see is that we are not creating something new, we are
+;;; merely /extending/ CLOS. defmodel works like defclass in all ways, except for
+;;; extensions to provide the behavior of Cells. We see both :initform
+;;; and :default-initarg used to provide rules for a slot. We also see
+;;; the initarg :len used to override the default initform.
+;;;
+;;; By extending defclass we (a) retain its expressiveness, and (b) produce
+;;; something hopefully easier to learn by developers already familiar with CLOS.
+;;;
+;;; The first extension we see is that the len initform refers to the
+;;; Smalltalk-like anaphoric variable self, to which will be bound
+;;; the rectangle instance being initialized. Normally an initform is evaluated
+;;; without being able to see the instance, and any initialization requiring
+;;; that must be done in the class initializer.
+
+
Added: dependencies/trunk/cells/tutorial/01a-dataflow.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/01a-dataflow.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,17 @@
+(defpackage #:tu-dataflow (:use :cl :cells))
+(in-package #:tu-dataflow)
+
+(defmodel rectangle ()
+ ((len :initarg :len :accessor len
+ :initform (c? (* 2 (width self))))
+ (width :initarg :width :initform nil :accessor width))
+ (:default-initargs
+ :width (c? (/ (len self) 2))))
+
+#+test
+(let ((r (make-instance 'rectangle :len (c-in 42))))
+ (cells::ct-assert (eql 21 (width r)))
+ (cells::ct-assert (= 1000 (setf (len r) 1000))) ;; make sure we did not break SETF, which must return the value set
+ (cells::ct-assert (eql 500 (width r)))) ;; make sure new value propagated
+
+
Added: dependencies/trunk/cells/tutorial/01b-change-handling.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/01b-change-handling.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,36 @@
+#| There is the fun part: automatic state management. Not only can a slot get its value from
+a self-aware rule, but that value will stay current with other values as they change.
+
+But often changes to a value must be reflected outside the automatic dataflow model. See next.
+
+|#
+
+(defpackage #:tu-change-handling (:use :cl :cells))
+(in-package #:tu-change-handling)
+
+(defmodel rectangle ()
+ ((len :initarg :len :accessor len
+ :initform (c? (* 2 (width self))))
+ (width :initarg :width :initform nil :accessor width))
+ (:default-initargs
+ :width (c? (/ (len self) 2))))
+
+(defvar *gui-told*)
+
+(defobserver len ((self rectangle) new-value old-value old-value-bound-p)
+ ;; Where rectangle is a GUI element, we need to tell the GUI framework
+ ;; to update this area of the screen
+ (setf *gui-told* t)
+ (print (list "tell GUI about" self new-value old-value old-value-bound-p)))
+
+#+test
+(let* ((*gui-told* nil)
+ (r (make-instance 'rectangle :len (c-in 42))))
+ (cells::ct-assert *gui-told*)
+ (setf *gui-told* nil)
+ (cells::ct-assert (eql 21 (width r)))
+
+ (cells::ct-assert (= 1000 (setf (len r) 1000)))
+ (cells::ct-assert *gui-told*)
+ (cells::ct-assert (eql 500 (width r))))
+
Added: dependencies/trunk/cells/tutorial/01c-cascade.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/01c-cascade.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,94 @@
+#|
+
+Now we have automatic state management (including change propagation)
+outside the Cells model as well as in. Now lets look at cascading change
+by adding another level of computation, so A->B->C.
+
+In this case: len->area->brightness
+Also: len->width->area->brightness
+
+That leads to some complications I will discuss, but no assertions here
+enforce correct behavior in re those complications. Soon. :)
+
+|#
+
+(defpackage #:tu-depth (:use :cl :cells))
+(in-package #:tu-depth)
+
+(defmacro start-finish (key rule)
+ `(progn
+ (print (list :start ,key))
+ (prog1
+ (progn ,rule)
+ (print (list :finish ,key)))))
+
+(defmodel rectangle ()
+ ((lumens :initform 1000000 :reader lumens)
+ (len :initarg :len :accessor len
+ :initform (c? (start-finish :len
+ (* 2 (width self)))))
+ (area :initarg :area :accessor area
+ :initform (c? (start-finish :area
+ (* (len self)(width self)))))
+ (width :initarg :width :accessor width
+ :initform (c? (start-finish :width
+ (floor (len self) 2))))
+ (brightness :reader brightness
+ :initform (c? (start-finish :brightness
+ (/ (^lumens) (^area)))))
+ ))
+
+#+test
+(let ((r (make-instance 'rectangle :len (c-in 100))))
+ (cells::ct-assert (eql 50 (width r)))
+ (cells::ct-assert (eql 5000 (area r)))
+ (cells::ct-assert (eql 200 (brightness r)))
+ (cells::ct-assert (= 1000 (setf (len r) 1000)))
+ (cells::ct-assert (eql 500000 (area r)))
+ (cells::ct-assert (eql 2 (brightness r))))
+
+#| --- discussion ----------------------------
+
+The output in Cells is:
+
+(:START :AREA)
+(:START :WIDTH)
+(:finish :WIDTH)
+(:finish :AREA)
+(:START :BRIGHTNESS)
+(:finish :BRIGHTNESS)
+(CELTK::ATTEMPTING (EQL 50 (WIDTH R)))
+(CELTK::ATTEMPTING (EQL 5000 (AREA R)))
+(CELTK::ATTEMPTING (EQL 200 (BRIGHTNESS R)))
+(CELTK::ATTEMPTING (= 1000 (SETF (LEN R) 1000)))
+0> c-propagate-to-users > notifying users of | [i :=[24]LEN/#<RECTANGLE>] | (AREA WIDTH)
+
+Notice here that the LEN cell is about to tell both the width and area to recalculate,
+since area depends (of course) on len and (rather artificially) width also derives
+from LEN.
+
+ie, This example has accidentally deviated into more complexity than intended. But we are
+approaching these issues anyay, so I will leave it for now. We can always break it up
+later.
+
+Let's continue:
+
+(:START :WIDTH)
+(:finish :WIDTH)
+(:START :AREA)
+(:finish :AREA)
+
+Fine, now here comes the challenge. Width is also going to tell area to recalculate:
+
+0> c-propagate-to-users > notifying users of | [? :<vld>=[24]WIDTH/#<RECTANGLE>] | (AREA)
+0> c-propagate-to-users > notifying users of | [? :<vld>=[24]AREA/#<RECTANGLE>] | (BRIGHTNESS)
+
+Correct: Area does not actually run its rule since it already did so when notified by LEN,
+ but it does propagate to brightness.
+
+(:START :BRIGHTNESS)
+(:finish :BRIGHTNESS)
+(CELTK::ATTEMPTING (EQL 500000 (AREA R)))
+(CELTK::ATTEMPTING (EQL 2 (BRIGHTNESS R)))
+
+|#
\ No newline at end of file
Added: dependencies/trunk/cells/tutorial/02-lesson.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/02-lesson.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,63 @@
+#| A->B->C works. For efficiency, let's have propagation stop if some rule
+computes the same value as last time.
+|#
+
+(defpackage #:tu-smart-propagation (:use :cl :cells :utils-kt :tu-cells))
+(in-package #:tu-smart-propagation)
+
+
+;;; -----------------------------------------------
+
+(defmodel rectangle ()
+ ((padded-width :initarg :padded-width :accessor padded-width
+ :initform (c? (compute-log :padded-width)
+ (+ 10 (width self))))
+ (len :initarg :len :accessor len
+ :initform (c? (compute-log :len)
+ (* 2 (width self))))
+ (width :initarg :width :accessor width
+ :initform (c? (compute-log :width)
+ (floor (len self) 2)))))
+
+(defobserver width ()
+ (assert (not (eql new-value old-value)))
+ (TRC "observing width" new-value old-value)
+ (compute-log :width-observer))
+
+(defobserver len ()
+ (compute-log :len-observer))
+
+#+test
+(let* ((r (progn
+ (CELLS-RESET)
+ (clear-computed)
+ (make-instance 'rectangle :len (c-in 42)))))
+ (cells::ct-assert (eql 21 (width r)))
+
+ ;; first check that setting an input cell does not
+ ;; propagate needlessly...
+
+ (clear-computed)
+ (verify-not-computed :len-observer :width :width-observer :padded-width)
+ (setf (len r) 42) ;; n.b. same as existing value, no change
+ (cells::ct-assert (eql 21 (width r))) ;; floor truncates
+ (verify-not-computed :len-observer :width :width-observer :padded-width)
+
+ ;; now check that intermediate computations, when unchanged
+ ;; from the preceding computation, does not propagate needlessly...
+
+ (clear-computed)
+ (setf (len r) 43)
+ (cells::ct-assert (eql 21 (width r))) ;; floor truncates
+ (verify-computed :len-observer :width)
+ (verify-not-computed :width-observer :padded-width)
+
+ #| Ok, so the engine runs the width rule, sees that it computes
+the same value as before, so does not invoke either the width
+observer or recalculation of are. Very efficient. The sanity check
+reconfirms that the engine does do that work when necessary.
+|#
+
+ (clear-computed)
+ (setf (len r) 44)
+ (verify-computed :len-observer :width :width-observer :padded-width))
Added: dependencies/trunk/cells/tutorial/03-ephemeral.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/03-ephemeral.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,85 @@
+
+
+(defpackage #:tu-ephemeral (:use :cl :utils-kt :cells :tu-cells))
+(in-package #:tu-ephemeral)
+
+
+#|
+
+Events present a problem for spreadsheet models. Suppose we have a clicked rule for a button
+which says:
+
+ :clicked (c? (point-in-rect
+ (screen-location (mouse-event *window*))
+ (bounding-box self)))
+
+Now suppose we get a mouse-event outside the bounding box of widget X, and then in the
+next application event something happens that makes the bounding box grow such that it
+includes the location of the old mouse event. We need the mouse-event not to be there any more,
+because, well, events are events. It is relevant only in the moment of its creation and propagation.
+
+Note, btw, that this must happen not as bang-bang:
+
+ (setf (mouse-event *window*) (get-next-event)
+ (setf (mouse-event *window*) nil)
+
+...because observers can kick off state change, and anyway SETF has interesting Cell semantics,
+including observers firing. So setf-nil is a kludge, better that the Cells engine acknowledge that
+events are different and accomodate them by silently reverting an event to nil as soon as it finishes
+propagating.
+
+Finally, so far this has worked out well as a slot attribute as defined at the class level, not
+instance by instance, by specifying :cell :ephemeral
+
+|#
+
+(defmodel rectangle ()
+ ((click :cell :ephemeral :initform (c-in nil) :accessor click)
+ (bbox :initarg :bbox :initform (c-in nil) :accessor bbox)
+ (clicked :cell :ephemeral :accessor clicked
+ :initform (c? (point-in-rect (^click)(^bbox))))))
+
+(defun point-in-rect (p r)
+ (when (and p r)
+ (destructuring-bind (x y) p
+ (destructuring-bind (l top r b) r
+ (and (<= l x r)
+ (<= b y top))))))
+
+(defobserver click ((self rectangle) new-value old-value old-value-bound-p)
+ (when new-value
+ (with-integrity (:change)
+ (TRC "setting bbox!!!")
+ (setf (bbox self) (list -1000 1000 1000 -1000)))))
+
+(defobserver clicked ((self rectangle) new-value old-value old-value-bound-p)
+ (when new-value
+ (TRC "clicked!!!!" self new-value)
+ (compute-log :clicked)))
+
+#+test
+(progn
+ (cells-reset)
+ (let* ((starting-bbox (list 10 10 20 20))
+ (r (make-instance 'rectangle
+ :bbox (c-in (list 10 10 20 20)))))
+ (clear-computed)
+ (setf (click r) (list 0 0))
+ (assert (and (not (point-in-rect (list 0 0) starting-bbox))
+ (point-in-rect (list 0 0)(bbox r))
+ (verify-not-computed :clicked)))))
+
+#|
+The assertion demonstrates... well, it is complicated. Point 0-0 is
+in the current bbox, but the system correctly determines that it
+was not clicked. The click event at 0-0 happened when the bbox
+was elsewhwer. When the bbox moved, the Cells engine had already cleared
+the "ephemeral" click.
+
+Note that now we have less transparency: if one wants to perturb the data model
+from with an observer of some ongoing perturbation, one needs to arrange for
+that nested perturbation to wait until the ongoing one completes. That
+explains the "with-integrity" macro.
+
+|#
+
\ No newline at end of file
Added: dependencies/trunk/cells/tutorial/04-formula-once-then-input.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/04-formula-once-then-input.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,48 @@
+
+
+(defpackage #:tu-rule-once-then-input (:use :cl :utils-kt :cells :tu-cells))
+(in-package #:tu-rule-once-then-input)
+
+
+#|
+
+Often in interactive applications one needs to do interesting things to come up
+with an initial value for a field which then is to be edited by a user, or
+for some other reason regularly fed as a C-INPUT.
+
+|#
+
+(defvar *db-entry*)
+
+(defun get-age (id)
+ (bwhen (props (cdr (assoc id *db-entry* :test 'string=)))
+ (getf props :age)))
+
+(defmodel kenny-view ()
+ ((age :accessor age :initform (c-formula (:inputp t)
+ (- (get-age "555-55-5555")
+ (^grecian-formula-amt))))
+ (grecian-formula-amt :accessor grecian-formula-amt
+ :initform (c-in 5))))
+
+(defobserver age ((self kenny-view))
+ (setf (getf (cdr (assoc "555-55-5555" *db-entry* :test 'string=)) :age) new-value))
+
+#+test
+(let ((*db-entry* (copy-list '(("555-55-5555" . (:name "ken" :age 54))
+ ("666-66-6666" . (:name "satan" :age most-positive-fixnum))))))
+ (cells-reset)
+ (let ((kv (make-instance 'kenny-view)))
+ (print `(:age-init ,(age kv)))
+ (assert (= 49 (age kv)))
+
+ (incf (grecian-formula-amt kv) 10) ;; try looking younger
+ (assert (= 15 (grecian-formula-amt kv)))
+
+ (assert (= 49 (age kv))) ;; unchanged -- the age rule is gone
+
+ (print `(:happy-birthday ,(incf (age kv))))
+ (assert (= 50 (age kv)(get-age "555-55-5555")))
+ ;
+ ; just showin' off...
+ (assert (= 51 (1+ (age kv))(incf (age kv))(get-age "555-55-5555")))))
\ No newline at end of file
Added: dependencies/trunk/cells/tutorial/test.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/test.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,52 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+;;;
+;;; Copyright (c) 1995,2003 by Kenneth William Tilton.
+;;;
+;;; Permission is hereby granted, free of charge, to any person obtaining a copy
+;;; of this software and associated documentation files (the "Software"), to deal
+;;; in the Software without restriction, including without limitation the rights
+;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+;;; copies of the Software, and to permit persons to whom the Software is furnished
+;;; to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be included in
+;;; all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+;;; IN THE SOFTWARE.
+
+(eval-when (compile load)
+ (proclaim '(optimize (speed 2) (safety 3) (space 1) (debug 3))))
+
+(defpackage #:tu-cells
+ (:use :cl :utils-kt)
+ (:export #:clear-computed #:verify-computed #:verify-not-computed #:compute-log))
+
+(in-package :tu-cells)
+
+(defmacro ct-assert (form &rest stuff)
+ `(progn
+ (print `(attempting ,',form))
+ (assert ,form () "Error with ~a >> ~a" ',form (list , at stuff))))
+
+(defvar *computed*)
+(defun clear-computed ()
+ (setf *computed* nil))
+
+(defun compute-log (&rest keys)
+ (loop for k in keys
+ do (pushnew k *computed*)))
+
+(defun verify-computed (&rest keys)
+ (loop for k in keys
+ do (assert (find k *computed*)() "Unable verify ~a computed: ~a" k *computed*)))
+
+(defun verify-not-computed (&rest keys)
+ (loop for k in keys
+ do (assert (not (find k *computed*)) () "Unable verify ~a NOT computed: ~a" k *computed*)
+ finally (return t)))
\ No newline at end of file
Added: dependencies/trunk/cells/tutorial/tutorial.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/tutorial/tutorial.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,95 @@
+;; -*- lisp-version: "8.0 [Windows] (May 22, 2006 0:51)"; cg: "1.81"; -*-
+
+(in-package :cg-user)
+
+(defpackage :TU-CELLS)
+
+(define-project :name :tutorial
+ :modules (list (make-instance 'module :name "test.lisp")
+ (make-instance 'module :name "01-lesson.lisp")
+ (make-instance 'module :name "01a-dataflow.lisp")
+ (make-instance 'module :name
+ "01b-change-handling.lisp")
+ (make-instance 'module :name "01c-cascade.lisp")
+ (make-instance 'module :name "02-lesson.lisp")
+ (make-instance 'module :name "03-ephemeral.lisp")
+ (make-instance 'module :name
+ "04-formula-once-then-input.lisp")
+ (make-instance 'module :name "05-class-cell.lisp")
+ (make-instance 'module :name
+ "..\\gotchas\\lost-ephemeral-init.lisp")
+ (make-instance 'module :name "chat-cells.lisp")
+ (make-instance 'module :name "df-interference.lisp"))
+ :projects (list (make-instance 'project-module :name "..\\cells"))
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :tu-cells
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules '(:cg-dde-utils :cg.base :cg.bitmap-pane
+ :cg.bitmap-pane.clipboard :cg.bitmap-stream
+ :cg.button :cg.caret :cg.check-box :cg.choice-list
+ :cg.choose-printer :cg.clipboard
+ :cg.clipboard-stack :cg.clipboard.pixmap
+ :cg.color-dialog :cg.combo-box :cg.common-control
+ :cg.comtab :cg.cursor-pixmap :cg.curve
+ :cg.dialog-item :cg.directory-dialog
+ :cg.directory-dialog-os :cg.drag-and-drop
+ :cg.drag-and-drop-image :cg.drawable
+ :cg.drawable.clipboard :cg.dropping-outline
+ :cg.edit-in-place :cg.editable-text
+ :cg.file-dialog :cg.fill-texture
+ :cg.find-string-dialog :cg.font-dialog
+ :cg.gesture-emulation :cg.get-pixmap
+ :cg.get-position :cg.graphics-context
+ :cg.grid-widget :cg.grid-widget.drag-and-drop
+ :cg.group-box :cg.header-control :cg.hotspot
+ :cg.html-dialog :cg.html-widget :cg.icon
+ :cg.icon-pixmap :cg.ie :cg.item-list
+ :cg.keyboard-shortcuts :cg.lamp :cg.lettered-menu
+ :cg.lisp-edit-pane :cg.lisp-text :cg.lisp-widget
+ :cg.list-view :cg.mci :cg.menu :cg.menu.tooltip
+ :cg.message-dialog :cg.multi-line-editable-text
+ :cg.multi-line-lisp-text :cg.multi-picture-button
+ :cg.multi-picture-button.drag-and-drop
+ :cg.multi-picture-button.tooltip :cg.ocx
+ :cg.os-widget :cg.os-window :cg.outline
+ :cg.outline.drag-and-drop
+ :cg.outline.edit-in-place :cg.palette
+ :cg.paren-matching :cg.picture-widget
+ :cg.picture-widget.palette :cg.pixmap
+ :cg.pixmap-widget :cg.pixmap.file-io
+ :cg.pixmap.printing :cg.pixmap.rotate :cg.printing
+ :cg.progress-indicator :cg.project-window
+ :cg.property :cg.radio-button :cg.rich-edit
+ :cg.rich-edit-pane :cg.rich-edit-pane.clipboard
+ :cg.rich-edit-pane.printing :cg.sample-file-menu
+ :cg.scaling-stream :cg.scroll-bar
+ :cg.scroll-bar-mixin :cg.selected-object
+ :cg.shortcut-menu :cg.static-text :cg.status-bar
+ :cg.string-dialog :cg.tab-control
+ :cg.template-string :cg.text-edit-pane
+ :cg.text-edit-pane.file-io :cg.text-edit-pane.mark
+ :cg.text-or-combo :cg.text-widget :cg.timer
+ :cg.toggling-widget :cg.toolbar :cg.tooltip
+ :cg.trackbar :cg.tray :cg.up-down-control
+ :cg.utility-dialog :cg.web-browser
+ :cg.web-browser.dde :cg.wrap-string
+ :cg.yes-no-list :cg.yes-no-string :dde)
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags '(:top-level :debugger)
+ :build-flags '(:allow-runtime-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :default-command-line-arguments "+M +t \"Console for Debugging\""
+ :additional-build-lisp-image-arguments '(:read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :on-initialization 'tu-cells::tu-chat-2
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/utils-kt/core.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/core.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,74 @@
+#|
+
+ Utils-kt core
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+
+
+(defmacro with-gensyms ((&rest symbols) &body body)
+ `(let ,(loop for sym in symbols
+ collecting `(,sym (gensym ,(string sym))))
+ , at body))
+
+(defmacro eval-now! (&body body)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ , at body))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defmacro export! (&rest symbols)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export ',symbols))))
+
+(eval-now!
+ (defmacro define-constant (name value &optional docstring)
+ "Define a constant properly. If NAME is unbound, DEFCONSTANT
+it to VALUE. If it is already bound, and it is EQUAL to VALUE,
+reuse the SYMBOL-VALUE of NAME. Otherwise, DEFCONSTANT it again,
+resulting in implementation-specific behavior."
+ `(defconstant ,name
+ (if (not (boundp ',name))
+ ,value
+ (let ((value ,value))
+ (if (equal value (symbol-value ',name))
+ (symbol-value ',name)
+ value)))
+ ,@(when docstring (list docstring)))))
+
+(defun test-setup (&optional drib)
+ #+(and allegro ide (or (not its-alive!) debugging-alive!))
+ (ide.base::find-new-prompt-command
+ (cg.base::find-window :listener-frame))
+ (when drib
+ (dribble (merge-pathnames
+ (make-pathname :name drib :type "TXT")
+ (project-path)))))
+
+(export! test-setup test-prep test-init)
+(export! project-path)
+(defun project-path ()
+ #+(and allegro ide (not its-alive!))
+ (excl:path-pathname (ide.base::project-file ide.base:*current-project*))
+ )
+
+#+test
+(test-setup)
+
+(defun test-prep (&optional drib)
+ (test-setup drib))
+
+(defun test-init (&optional drib)
+ (test-setup drib))
\ No newline at end of file
Added: dependencies/trunk/cells/utils-kt/datetime.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/datetime.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,205 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cello; -*-
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(os-tickcount time-of-day now hour-min-of-day
+ time-in-zone dd-mmm-yy mmm-dd-yyyy)))
+
+(defun os-tickcount ()
+ (cl:get-internal-real-time))
+
+(defun now ()
+ (/ (get-internal-real-time)
+ internal-time-units-per-second))
+
+(defun time-of-day (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~A:~2,,,'0 at A:~2,,,'0 at A" hours minutes seconds)))
+
+(defun hour-min-of-day (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~2,,,'0 at A:~2,,,'0 at A" hours minutes)))
+
+(defun time-in-zone (inzone &optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylightsavingsp this-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable this-zone day-of-week daylightsavingsp))
+ (encode-universal-time seconds minutes hours date month year (- inzone (if daylightsavingsp 1 0)))))
+
+(defun dd-mmm-yy (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~A-~A-~2,,,'0 at A" date (month-abbreviation month)
+ (mod year 100))))
+
+(defun mmm-dd-yyyy (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~A ~A, ~A" (month-abbreviation month)
+ date year)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(month-abbreviation weekday-abbreviation week-time
+ mdyy-yymd u-time u-date)))
+
+(defun month-abbreviation (month)
+ (elt '("Jan" "Feb" "Mar" "Apr" "May" "June"
+ "July" "Aug" "Sept" "Oct" "Nov" "Dec") (1- month)))
+
+(defun weekday-abbreviation (day)
+ (elt '("Mon" "Tue" "Wed" "Thur" "Fri" "Sat" "Sun") day))
+
+(defun week-time (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~A ~A ~A, ~A ~a:~2,'0d ~a"
+ (weekday-abbreviation day-of-week)
+ (month-abbreviation month)
+
+ date
+ year
+ (if (= 12 hours) hours (mod hours 12)) ; JP 010911 since (mod 12 12) = 0, treat 12 as a special case.
+ minutes (if (>= hours 12) "PM" "AM"))))
+
+
+(defun mdyy-yymd (d)
+ (assert (eql 8 (length d)))
+ (conc$ (right$ d 4) (left$ d 4)))
+
+(defun u-time (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~2,d:~2,'0d ~a"
+ ;; /// time-zone, really Naggum's stuff
+ (mod hours 12) minutes
+ (if (>= hours 12) "PM" "AM"))))
+
+(defun u-date (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~A-~A-~A"
+ date
+ (elt '("Jan" "Feb" "Mar" "Apr" "May" "June"
+ "July" "Aug" "Sept" "Oct" "Nov" "Dec") (1- month))
+ year
+ )))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(u-day multiple-value-bind m/d/y mm/dd yyyy-mm-dd)))
+
+(defun u-day (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (elt '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday") day-of-week)))
+
+(defun u-day3 (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (elt '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun") day-of-week)))
+
+(defun m/d/y (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~2,,,'0 at A/~2,,,'0 at A/~2,,,'0 at A" month date (mod year 100))))
+
+(defun mm/dd (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~2,,,'0 at A/~2,,,'0 at A" month date)))
+
+(defun yyyy-mm-dd (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~4,,,'0 at A~2,,,'0 at A~2,,,'0 at A"
+ year month date)))
+
+(eval-now!
+ (export '(ymdhmsh)))
+
+(defun ymdhmsh (&optional (i-time (get-universal-time)))
+ (multiple-value-bind
+ (seconds minutes hours date month year day-of-week daylight-saving-time-p time-zone)
+ (decode-universal-time i-time)
+ (declare (ignorable seconds minutes hours date
+ month year day-of-week
+ daylight-saving-time-p time-zone))
+ (format nil "~4,,,'0 at A:~2,,,'0 at A:~2,,,'0 at A:~2,,,'0 at A:~2,,,'0 at A:~2,,,'0 at A:~2,,,'0 at A"
+ year month date hours minutes seconds (floor (* 10 (mod (now) 1.0))))))
+
+(defun hyphenated-time-string ()
+ (substitute #\- #\: (ymdhmsh)))
+
+#+test
+(hyphenated-time-string)
+
+#+test
+(ymdhmsh)
\ No newline at end of file
Added: dependencies/trunk/cells/utils-kt/debug.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/debug.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,150 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: utils-kt; -*-
+;;;
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+
+(defvar *count* nil)
+(defvar *counting* nil)
+(defvar *dbg*)
+(defvar *stop* nil)
+
+(defun utils-kt-reset ()
+ (clock-off :ukt-reset)
+ (setf *count* nil
+ *stop* nil
+ *dbg* nil)
+
+ (print "----------UTILSRESET----------------------------------"))
+
+;------------- counting ---------------------------
+
+(defmacro with-counts ((onp &rest msg) &body body)
+ `(if ,onp
+ (let ((*counting* (cons t *counting*)))
+ (prog2
+ (count-clear nil , at msg)
+ (progn , at body)
+ (show-count t , at msg)))
+ (progn , at body)))
+
+(defun count-of (key)
+ (cdr (assoc key *count* :key 'car)))
+
+(defun count-clear (announce &rest msg)
+ (declare (ignorable msg))
+ (when announce (format t "~&count-clear > ~a" msg))
+ (setf *count* nil))
+
+(defmacro count-it (&rest keys)
+ (declare (ignorable keys))
+ #+nahhh
+ `(progn)
+ `(when (car *counting*)
+ (call-count-it , at keys)))
+
+(export! count-it!)
+(defmacro count-it! (&rest keys)
+ (declare (ignorable keys))
+ #+(and its-alive! (not debugging-alive!))
+ `(progn)
+ #-(and its-alive! (not debugging-alive!))
+ `(when (car *counting*)
+ (call-count-it , at keys)))
+
+(defun call-count-it (&rest keys)
+ (declare (ignorable keys))
+ #+nahh (when (find (car keys) '(:trcfailed :TGTNILEVAL))
+ (break "clean up time ~a" keys))
+ (let ((entry (assoc keys *count* :test #'equal)))
+ (if entry
+ (setf (cdr entry) (1+ (cdr entry)))
+ (push (cons keys 1) *count*))))
+
+(defun show-count (clearp &rest msg &aux announced)
+
+ (let ((res (sort (copy-list *count*) (lambda (v1 v2)
+ (let ((v1$ (symbol-name (caar v1)))
+ (v2$ (symbol-name (caar v2))))
+ (if (string= v1$ v2$)
+ (< (cdr v1) (cdr v2))
+ (string< v1$ v2$))))))
+ )
+ (loop for entry in res
+ for occs = (cdr entry)
+ when (plusp occs)
+ sum occs into running
+ and do (unless announced
+ (setf announced t)
+ (format t "~&Counts after: clearp ~a, length ~d: ~s" clearp (length *count*) msg))
+ (format t "~&~4d ... ~2d ... ~(~{~a ~}~)" running occs (car entry))))
+ (when clearp (count-clear announced "show-count" )))
+
+;-------------------- timex ---------------------------------
+
+(export! timex)
+
+(defmacro timex ((onp &rest trcargs) &body body)
+ `(if ,onp
+ (prog2
+ (format t "~&Starting timing run of ~{ ~a~}" (list , at trcargs))
+ (time (progn , at body))
+ (format t "~&Above timing was of ~{ ~a~}" (list , at trcargs)))
+ (progn , at body)))
+
+#+save
+(defun dbg-time-report (cpu-gc-user cpu-gc-sys cpu-tot-user cpu-tot-sys real-time conses other-bytes static-bytes)
+ (format t "~&cpu-gc-user ~a" cpu-gc-user)
+ (format t "~&cpu-gc-sys ~a" cpu-gc-sys)
+ (format t "~&cpu-tot-user ~a" cpu-tot-user)
+ (format t "~&cpu-tot-sys ~a" cpu-tot-sys)
+ (format t "~&<non-gc user cpu> ~a" (- cpu-tot-user cpu-gc-user))
+ (format t "~&<non-gc sys cpu> ~a" (- cpu-tot-sys cpu-gc-sys))
+ (format t "~&conses ~a" conses)
+ (format t "~&other-bytes ~a" other-bytes)
+ (format t "~&static-bytes ~a" static-bytes)
+ (excl::time-report cpu-gc-user cpu-gc-sys cpu-tot-user cpu-tot-sys real-time conses other-bytes static-bytes))
+
+;---------------- Metrics -------------------
+
+(defmacro with-metrics ((countp timep &rest trcargs) form-measured &body postlude)
+ `(with-counts (,countp , at trcargs)
+ (timex (,timep , at trcargs)
+ ,form-measured)
+ , at postlude))
+
+(defvar *clock*)
+
+(export! clock clock-0 clock-off)
+
+(defun clock-off (key)
+ (when (boundp '*clock*)
+ (print (list :clock-off key))
+ (makunbound '*clock*)))
+
+(defun clock-0 (key &aux (now (get-internal-real-time)))
+ (setf *clock* (cons now now))
+ (print (list :clock-initialized-by key)))
+
+(defun clock (&rest keys &aux (now (get-internal-real-time)))
+ (when (boundp '*clock*)
+ (print (list* :clock (- now (cdr *clock*)) :tot (- now (car *clock*)) :at keys))
+ (setf (cdr *clock*) now)))
+
Added: dependencies/trunk/cells/utils-kt/defpackage.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/defpackage.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,61 @@
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :cl-user)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (setf *features* (remove :its-alive! *features*)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (setf *features* (pushnew :gimme-a-break *features*)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (setf *features* (remove :debugging-alive! *features*)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ ;;; #+(and its-alive! (not debugging-alive!))
+ ;;; (proclaim '(optimize (speed 3) (safety 1) (space 1) (debug 0)))
+ ;;; #-(and its-alive! (not debugging-alive!))
+ (proclaim '(optimize (speed 2) (safety 1) (space 1) (debug 3))))
+
+(defpackage :utils-kt
+ (:nicknames #:ukt)
+ (:use #:common-lisp
+ #+(or allegro lispworks clisp) #:clos
+ #+cmu #:mop
+ #+sbcl #:sb-mop
+ #+openmcl-partial-mop #:openmcl-mop
+ #+(and mcl (not openmcl-partial-mop)) #:ccl)
+ (:export
+ #:export!
+ #:utils-kt-reset
+ #:count-it #:count-of #:with-counts
+ #:wdbg #:maptimes #:bwhen #:bif #:xor
+ #:with-dynamic-fn #:last1 #:packed-flat! #:with-metrics
+ #:shortc
+ #:intern$
+ #:define-constant #:*count* #:*stop*
+ #:*dbg*
+ #:with-gensyms
+ #:make-fifo-queue #:fifo-queue #:fifo-add #:fifo-delete
+ #:fifo-empty #:fifo-pop #:fifo-clear
+ #:fifo-map #:fifo-peek #:fifo-data #:with-fifo-map #:fifo-length
+
+ #-(or lispworks mcl) #:true
+ #+(and mcl (not openmcl-partial-mop)) #:class-slots
+ ))
Added: dependencies/trunk/cells/utils-kt/detritus.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/detritus.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,230 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: utils-kt; -*-
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(eval-now! export! assocd rassoca class-proto brk)))
+
+(defmacro wdbg (&body body)
+ `(let ((*dbg* t))
+ , at body))
+
+(defun assocd (x y) (cdr (assoc x y)))
+(defun rassoca (x y) (car (assoc x y)))
+
+(defun class-proto (c)
+ (let ((cc (find-class c)))
+ (when cc
+ (finalize-inheritance cc))
+ (mop::class-prototype cc)))
+
+
+(defun brk (&rest args)
+ #+its-alive! (apply 'error args)
+ #-its-alive! (progn
+ ;;(setf *ctk-dbg* t)
+ (apply 'break args)))
+
+(defun find-after (x l)
+ (bIf (xm (member x l))
+ (cadr xm)
+ (brk "find-after ~a not member of ~a" x l)))
+
+(defun find-before (x l)
+ (loop with prior = nil
+ for i in l
+ if (eql i x)
+ return prior
+ else do (setf prior i)
+ finally (brk "find-before ~a not member of ~a" x l)))
+
+(defun list-insert-after (list after new )
+ (let* ((new-list (copy-list list))
+ (m (member after new-list)))
+ (rplacd m (cons new (cdr m)))
+ new-list))
+
+#+(and mcl (not openmcl-partial-mop))
+(defun class-slots (c)
+ (nconc (copy-list (class-class-slots c))
+ (copy-list (class-instance-slots c))))
+
+
+#-(or lispworks mcl)
+(progn
+ (defun true (it) (declare (ignore it)) t)
+ (defun false (it) (declare (ignore it))))
+
+(defun xor (c1 c2)
+ (if c1 (not c2) c2))
+
+(export! collect collect-if find-after find-before list-insert-after)
+
+(defun collect (x list &key (key 'identity) (test 'eql))
+ (loop for i in list
+ when (funcall test x (funcall key i))
+ collect i))
+
+(defun collect-if (test list)
+ (remove-if-not test list))
+
+;;; --- FIFO Queue -----------------------------
+
+(defun make-fifo-queue (&rest init-data)
+ (let ((q (cons nil nil)))
+ (prog1 q
+ (loop for id in init-data
+ do (fifo-add q id)))))
+
+(deftype fifo-queue () 'cons)
+
+(defun fifo-data (q) (car q))
+(defun fifo-clear (q) (rplaca q nil))
+(defun fifo-empty (q) (not (fifo-data q)))
+(defun fifo-length (q) (length (fifo-data q)))
+(defun fifo-peek (q) (car (fifo-data q)))
+
+(defun fifo-browse (q fn)
+ (map nil fn (fifo-data q)))
+
+(defun fifo-add (q new)
+ (if (car q)
+ (let ((last (cdr q))
+ (newlast (list new)))
+ (rplacd last newlast)
+ (rplacd q newlast))
+ (let ((newlist (list new)))
+ (rplaca q newlist)
+ (rplacd q newlist))))
+
+(defun fifo-delete (q dead)
+ (let ((c (member dead (fifo-data q))))
+ (assert c)
+ (rplaca q (delete dead (fifo-data q)))
+ (when (eq c (cdr q))
+ (rplacd q (last (fifo-data q))))))
+
+(defun fifo-pop (q)
+ (unless (fifo-empty q)
+ (prog1
+ (fifo-peek q)
+ (rplaca q (cdar q)))))
+
+(defun fifo-map (q fn)
+ (loop until (fifo-empty q)
+ do (funcall fn (fifo-pop q))))
+
+(defmacro with-fifo-map ((pop-var q) &body body)
+ (let ((qc (gensym)))
+ `(loop with ,qc = ,q
+ while (not (fifo-empty ,qc))
+ do (let ((,pop-var (fifo-pop ,qc)))
+ , at body))))
+
+#+(or)
+(let ((*print-circle* t))
+ (let ((q (make-fifo-queue)))
+ (loop for n below 3
+ do (fifo-add q n))
+ (fifo-delete q 1)
+ (loop until (fifo-empty q)
+ do (print (fifo-pop q)))))
+
+#+test
+(line-count "/openair" t 10 t)
+
+#+allegro
+(defun line-count (path &optional show-files (max-depth most-positive-fixnum) no-semis (depth 0))
+ (cond
+ ((excl:file-directory-p path)
+ (if (>= depth max-depth)
+ (progn
+ (format t "~&~v,8t~a dir too deep:" depth (pathname-directory path))
+ 0)
+ (progn
+ (when show-files
+ (format t "~&~v,8t~a counts:" depth (pathname-directory path)))
+ (let ((directory-lines
+ (loop for file in (directory path :directories-are-files nil)
+ for lines = (line-count file show-files max-depth no-semis (1+ depth))
+ when (and show-files (plusp lines))
+ do (bwhen (fname (pathname-name file))
+ (format t "~&~v,8t~a ~,40t~d" (1+ depth) fname lines))
+ summing lines)))
+ (unless (zerop directory-lines)
+ (format t "~&~v,8t~a ~,50t~d" depth (pathname-directory path) directory-lines))
+ directory-lines))))
+
+ ((find (pathname-type path) '("cl" "lisp" "c" "h" "java")
+ :test 'string-equal)
+ (source-line-count path no-semis))
+ (t 0)))
+
+(defun source-line-count (path no-semis)
+ (with-open-file (s path)
+ (loop with block-rem = 0
+ for line = (read-line s nil nil)
+ for trim = (when line (string-trim '(#\space #\tab) line))
+ while line
+ when (> (length trim) 1)
+ do (cond
+ ((string= "#|" (subseq trim 0 2))(incf block-rem))
+ ((string= "|#" (subseq trim 0 2))(decf block-rem)))
+ unless (or (string= trim "")
+ (and no-semis (or (plusp block-rem)
+ (char= #\; (schar trim 0)))))
+ count 1)))
+
+#+(or)
+(line-count (make-pathname
+ :device "c"
+ :directory `(:absolute "0algcount" ))
+ nil 5 t)
+
+#+(or)
+(loop for d1 in '("cl-s3" "kpax" "puri-1.5.1" "s-base64" "s-http-client" "s-http-server" "s-sysdeps" "s-utils" "s-xml")
+ summing (line-count (make-pathname
+ :device "c"
+ :directory `(:absolute "0Algebra" "1-devtools" ,d1))))
+
+
+(export! tree-includes tree-traverse tree-intersect)
+
+(defun tree-includes (sought tree &key (test 'eql))
+ (typecase tree
+ (null)
+ (atom (funcall test sought tree))
+ (cons (or (tree-includes sought (car tree) :test test)
+ (tree-includes sought (cdr tree) :test test)))))
+
+(defun tree-traverse (tree fn)
+ (typecase tree
+ (null)
+ (atom (funcall fn tree))
+ (cons (tree-traverse (car tree) fn)
+ (tree-traverse (cdr tree) fn)))
+ (values))
+
+(defun tree-intersect (t1 t2 &key (test 'eql))
+ (tree-traverse t1
+ (lambda (t1-node)
+ (when (tree-includes t1-node t2 :test test)
+ (return-from tree-intersect t1-node)))))
+
Added: dependencies/trunk/cells/utils-kt/flow-control.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/flow-control.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,254 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: utils-kt; -*-
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+(defun last1 (thing)
+ (car (last thing)))
+
+(defun max-if (&rest values)
+ (loop for x in values when x maximize x))
+
+(defun min-max-of (v1 v2)
+ (values (min-if v1 v2) (max-if v1 v2)))
+
+(defun min-if (v1 v2)
+ (if v1 (if v2 (min v1 v2) v1) v2))
+
+(export! list-flatten! tree-flatten list-insertf subseq-contiguous-p pair-off)
+
+(defun list-flatten! (&rest list)
+ (if (consp list)
+ (let (head work visited)
+ (labels ((link (cell)
+ ;;(format t "~&Link > cons: ~s . ~s" (car cell) (cdr cell))
+ (when (and (consp cell)
+ (member cell visited))
+ (break "list-flatten! detects infinite list: cell ~a, visited ~a" cell visited))
+ (push cell visited)
+
+ (when cell
+ (if (consp (car cell))
+ (link (car cell))
+ (progn
+ (setf head (or head cell))
+ (when work
+ (rplacd work cell))
+ (setf work cell)))
+ (link (rest cell)))))
+ (link list))
+ head)
+ list))
+
+(defun tree-flatten (tree)
+ (list-flatten! (copy-tree tree)))
+
+(export! push-end)
+(defmacro push-end (item place )
+ `(setf ,place (nconc ,place (list ,item))))
+
+(defun pair-off (list &optional (test 'eql))
+ (loop with pairs and copy = (copy-list list)
+ while (cdr copy)
+ do (let ((pair (find (car copy) (cdr copy) :test test)))
+ (if pair
+ (progn
+ (push-end (cons (car copy) pair) pairs)
+ (setf copy (delete pair (cdr copy) :count 1)))
+ (setf copy (cdr copy))))
+ finally (return pairs)))
+
+(defun packed-flat! (&rest u-nameit)
+ (delete nil (list-flatten! u-nameit)))
+
+(defmacro with-dynamic-fn ((fn-name (&rest fn-args) &body fn-body) &body body)
+ `(let ((,fn-name (lambda ,fn-args , at fn-body)))
+ (declare (dynamic-extent ,fn-name))
+ , at body))
+
+(defmacro list-insertf (place item &key after)
+ (let ((list (gensym))
+ (afterv (gensym))
+ (afters (gensym)))
+ `(let* ((,list ,place)
+ (,afterv ,after)
+ (,afters (when ,afterv (member ,after ,list))))
+ (assert (or (null ,afterv) ,afters) () "list-insertf after ~a not in list ~a" ,afterv ,list)
+ (setf ,place
+ (if ,afterv
+ (append (ldiff ,list ,afters)
+ (list ,afterv)
+ (list ,item)
+ (cdr ,afters))
+ (append ,list (list ,item)))))))
+
+(defun intern$ (&rest strings)
+ (intern (apply #'concatenate 'string strings)))
+
+#-allegro
+(defmacro until (test &body body)
+ `(loop (when ,test (return)) , at body))
+
+#-allegro
+(defmacro while (test &body body)
+ `(loop (unless ,test (return)) , at body))
+
+(defmacro bwhen ((bindvar boundform) &body body)
+ `(let ((,bindvar ,boundform))
+ (when ,bindvar
+ , at body)))
+
+(defmacro b-when (bindvar boundform &body body)
+ `(let ((,bindvar ,boundform))
+ (when ,bindvar
+ , at body)))
+
+(defmacro bif ((bindvar boundform) yup &optional nope)
+ `(let ((,bindvar ,boundform))
+ (if ,bindvar
+ ,yup
+ ,nope)))
+
+(defmacro b-if (bindvar boundform yup &optional nope)
+ `(let ((,bindvar ,boundform))
+ (if ,bindvar
+ ,yup
+ ,nope)))
+
+(defmacro b1 ((bindvar boundform) &body body)
+ `(let ((,bindvar ,boundform))
+ , at body))
+
+(defmacro maptimes ((nvar count) &body body)
+ `(loop for ,nvar below ,count
+ collecting (progn , at body)))
+
+(export! b1 maphash* hashtable-assoc -1?1 -1?1 prime? b-if b-when)
+
+(defun maphash* (f h)
+ (loop for k being the hash-keys of h
+ using (hash-value v)
+ collecting (funcall f k v)))
+
+(defun hashtable-assoc (h)
+ (maphash* (lambda (k v) (cons k v)) h))
+
+(define-symbol-macro -1?1 (expt -1 (random 2)))
+
+(defun -1?1 (x) (* -1?1 x))
+
+(defun prime? (n)
+ (when (> n 1)
+ (cond
+ ((= 2 n) t)
+ ((evenp n) (values nil 2))
+ (t (loop for d upfrom 3 by 2 to (sqrt n)
+ when (zerop (mod n d)) do (return-from prime? (values nil d))
+ finally (return t))))))
+
+
+
+; --- cloucell support for struct access of slots ------------------------
+
+(eval-when (:compile-toplevel :execute :load-toplevel)
+ (export '(cc-defstruct instance-slots)))
+
+(defmacro cc-defstruct (header &rest slots)
+ (let (name conc-name (cache (gensym)))
+ (if (consp header)
+ (destructuring-bind (hname &rest options)
+ header
+ (setf name hname)
+ (setf conc-name (bif (conc-option (find :conc-name options :key #'car))
+ (unless (eql (second conc-option) 'nil)
+ (second conc-option))
+ (intern (concatenate 'string
+ (symbol-name hname)
+ "-")))))
+ (progn
+ (setf name header)
+ (setf conc-name (intern (concatenate 'string
+ (symbol-name header) "-")))))
+
+ (let ((cc-info (mapcar (lambda (s)
+ (let ((sn (if (consp s)
+ (car s) s)))
+ (cons sn
+ (intern (concatenate 'string
+ (when conc-name (symbol-name conc-name))
+ (symbol-name sn))))))
+ slots)))
+ `(progn
+ (defstruct ,header , at slots)
+ (let (,cache)
+ (defmethod instance-slots ((self ,name))
+ (or ,cache (setf ,cache (append (call-next-method) ',cc-info)))))
+ ))))
+
+(defmethod instance-slots (self)
+ (class-slots (class-of self))) ;; acl has this for structs
+
+;;; ---- without-repeating ----------------------------------------------
+
+;; Returns a function that generates an elements from ALL each time it
+;; is called. When a certain element is generated it will take at
+;; least DECENT-INTERVAL calls before it is generated again.
+;;
+;; note: order of ALL is important for first few calls, could be fixed
+
+(defun without-repeating-generator (decent-interval all)
+ (let ((len (length all))
+ (head (let ((v (shuffle all)))
+ (nconc v v))))
+ (lambda ()
+ ;(print (list "without-repeating-generator sees len all =" len :decent-interval decent-interval))
+ (if (< len 2)
+ (car all)
+ (prog2
+ (rotatef (car head)
+ (car (nthcdr (random (- len decent-interval))
+ head)))
+ (car head)
+ (setf head (cdr head)))))))
+
+(defun shuffle (list &key (test 'identity))
+ (if (cdr list)
+ (loop thereis
+ (funcall test
+ (mapcar 'cdr
+ (sort (loop for e in list collecting (cons (random most-positive-fixnum) e))
+ '< :key 'car))))
+ (copy-list list)))
+
+(export! without-repeating shuffle)
+
+(defparameter *without-repeating-generators* nil)
+
+(defun reset-without-repeating ()
+ (if *without-repeating-generators*
+ (clrhash *without-repeating-generators*)
+ (setf *without-repeating-generators* (make-hash-table :test 'equalp))))
+
+(defun without-repeating (key all &optional (decent-interval (floor (length all) 2)))
+ (funcall (or (gethash key *without-repeating-generators*)
+ (progn
+ ;(print (list "without-repeating makes new gen" key :all-len (length all) :int decent-interval))
+ (setf (gethash key *without-repeating-generators*)
+ (without-repeating-generator decent-interval all))))))
+
Added: dependencies/trunk/cells/utils-kt/quad.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/quad.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,199 @@
+
+#|
+
+From: Erik Naggum (erik at naggum.no)
+Subject: Re: XML->sexpr ideas
+Newsgroups: comp.lang.lisp
+Date: 2004-01-19 04:24:43 PST
+
+* Kenny Tilton
+| Of course it is easy enough for me to come up with a sexpr format off
+| the top of my head, but I seem to recall someone (Erik? Tim? Other?)
+| saying they had done some work on a formal approach to an alternative
+| to XML/HTML/whatever.
+|
+| True that? If so, I am all ears.
+
+ Really? You are? Maybe I didn't survive 2003 and this is some Hell
+ where people have to do eternal penance, and now I get to do SGML all
+ over again.
+
+ Much processing of SGML-like data appears to be stream-like and will
+ therefore appear to be equivalent to an in-order traversal of a tree,
+ which can therefore be represented with cons cells while the traverser
+ maintains its own backward links elsewhere, but this is misleading.
+
+ The amount of work and memory required to maintain the proper backward
+ links and to make the right decisions is found in real applications to
+ balloon and to cause random hacks; the query languages reflect this
+ complexity. Ease of access to the parent element is crucial to the
+ decision-making process, so if one wants to use a simple list to keep
+ track of this, the most natural thing is to create a list of the
+ element type, the parent, and the contents, such that each element has
+ the form (type parent . contents), but this has the annoying property
+ that moving from a particular element to the next can only be done by
+ remembering the position of the current element in a list, just as one
+ cannot move to the next element in a list unless you keep the cons
+ cell around. However, the whole point of this exercise is to be able
+ to keep only one pointer around. So the contents of an element must
+ have the form (type parent contents . tail) if it has element contents
+ or simply a list of objects, or just the object if simple enough.
+
+ Example: <foo>123</foo> would thus be represented by (foo nil "123"),
+ <foo>123</foo><bar>456</bar> by (foo nil "123" bar nil "456"), and
+ <zot><foo>123</foo><bar>456</bar></zot> by #1=(zot nil (foo #1# "123"
+ bar #1# "456")).
+
+ Navigation inside this kind of structure is easy: When the contents in
+ CADDR is exhausted, the CDDDR is the next element, or if NIL, we have
+ exhausted the contents of the parent and move up to the CADR and look
+ for its next element, etc. All the important edges of the containers
+ that make up the *ML document are easily detectible and the operations
+ that are usually found at the edges are normally tied to the element
+ type (or as modified by its parents), are easily computable. However,
+ using a list for this is cumbersome, so I cooked up the «quad». The
+ «quad» is devoid of any intrinsic meaning because it is intended to be
+ a general data structure, so I looked for the best meaningless names
+ for the slots/accessors, and decided on QAR, QBR, QCR, and QDR. The
+ quad points to the element type (like the operator in a sexpr) in the
+ QAR, the parent (or back) quad in the QBR, the contents of the element
+ in the QCR, and the usual pointer to the next quad in the QDR.
+
+ Since the intent with this model is to «load» SGML/XML/SALT documents
+ into memory, one important issue is how to represent long stretches of
+ character content or binary content. The quad can easily be used to
+ represent a (sequence of) entity fragments, with the source in QAR,
+ the start position in QBR, and the end position in QCR, thereby using
+ a minimum of memory for the contents. Since very large documents are
+ intended to be loaded into memory, this property is central to the
+ ability to search only selected elements for their contents -- most
+ searching processors today parse the entire entity structure and do
+ very little to maintain the parsed element structure.
+
+ Speaking of memory, one simple and efficient way to implement the quad
+ on systems that lack the ability to add native types without overhead,
+ is to use a two-dimensional array with a second dimension of 4 and let
+ quad pointers be integers, which is friendly to garbage collection and
+ is unambiguous when the quad is used in the way explained above.
+
+ Maybe I'll talk about SALT some other day.
+
+--
+Erik Naggum | Oslo, Norway
+
+Act from reason, and failure makes you rethink and study harder.
+Act from faith, and failure makes you blame someone and push harder.
+
+|#
+
+(in-package :ukt)
+
+;;;(defstruct (juad jar jbr jcr jdr)
+
+
+
+(defun qar (q) (car q))
+(defun (setf qar) (v q) (setf (car q) v))
+
+(defun qbr (q) (cadr q))
+(defun (setf qbr) (v q) (setf (cadr q) v))
+
+(defun qcr (q) (caddr q))
+(defun (setf qcr) (v q) (setf (caddr q) v))
+
+(defun qdr (q) (cdddr q))
+(defun (setf qdr) (v q) (setf (cdddr q) v))
+
+(defun sub-quads (q)
+ (loop for childq on (qcr q) by #'qdr
+ collecting childq))
+
+(defun sub-quads-do (q fn)
+ (loop for childq on (qcr q) by #'qdr
+ do (funcall fn childq)))
+
+(defun quad-traverse (q fn &optional (depth 0))
+ (funcall fn q depth)
+ (sub-quads-do q
+ (lambda (subq)
+ (quad-traverse subq fn (1+ depth)))))
+
+(defun quad (operator parent contents next)
+ (list operator parent contents next))
+
+(defun quad* (operator parent contents next)
+ (list operator parent contents next))
+
+(defun qups (q)
+ (loop for up = (qbr q) then (qbr up)
+ unless up do (loop-finish)
+ collecting up))
+
+(defun quad-tree (q)
+ (list* (qar q)
+ (loop for childq on (qcr q) by #'qdr
+ while childq
+ collecting (quad-tree childq))))
+
+(defun tree-quad (tree &optional parent)
+ (let* ((q (quad (car tree) parent nil nil))
+ (kids (loop for k in (cdr tree)
+ collecting (tree-quad k q))))
+ (loop for (k n) on kids
+ do (setf (qdr k) n))
+ (setf (qcr q) (car kids))
+ q))
+
+#+test
+(test-qt)
+
+(defun test-qt ()
+ (print (quad-tree #1='(zot nil (foo #1# ("123" "abc")
+ . #2=(bar #1# (ding #2# "456"
+ dong #2# "789")))))))
+
+(print #1='(zot nil (foo #1# ("123" "abc")
+ . #2=(bar #1# (ding #2# "456"
+ dong #2# "789")))))
+#+xxxx
+(test-tq)
+
+(defun test-tq ()
+ (let ((*print-circle* t)
+ (tree '(zot (foo ("123")) (bar (ding) (dong)))))
+ (assert (equal tree (quad-tree (tree-quad tree))))))
+
+(defun testq ()
+ (let ((*print-circle* t))
+ (let ((q #1='(zot nil (foo #1# ("123" "abc")
+ . #2=(bar #1# (ding #2# "456"
+ dong #2# "789"))))))
+ (print '(traverse showing each type and data preceded by its depth))
+
+ (quad-traverse q (lambda (q depth)
+ (print (list depth (qar q)(qcr q)))))
+ (print `(listify same ,(quad-tree q))))
+ (let ((q #2='(zot nil (ding #2# "456"
+ dong #2# "789"))))
+ (print '(traverse showing each "car" and itd parentage preceded by its depth))
+ (print '(of data (zot (ding (dong)))))
+ (quad-traverse q (lambda (q depth)
+ (print (list depth (qar q)
+ (mapcar 'qar (qups q)))))))))
+
+;;;(defun tree-quad (tree)
+
+
+(defun testq2 ()
+ (let ((*print-circle* t))
+ (let ((q #2='(zot nil (ding #2# "456"
+ dong #2# "789"))))
+ (print '(traverse showing each "car" and itd parentage preceded by its depth))
+ (print '(of data (zot (ding (dong)))))
+ (quad-traverse q (lambda (q depth)
+ (print (list depth (qar q)
+ (mapcar 'qar (qups q)))))))))
+
+
+
+
\ No newline at end of file
Added: dependencies/trunk/cells/utils-kt/split-sequence.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/split-sequence.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,223 @@
+;;;; SPLIT-SEQUENCE
+;;;
+;;; This code was based on Arthur Lemmens' in
+;;; <URL:http://groups.google.com/groups?as_umsgid=39F36F1A.B8F19D20%40simplex.nl>;
+;;;
+;;; changes include:
+;;;
+;;; * altering the behaviour of the :from-end keyword argument to
+;;; return the subsequences in original order, for consistency with
+;;; CL:REMOVE, CL:SUBSTITUTE et al. (:from-end being non-NIL only
+;;; affects the answer if :count is less than the number of
+;;; subsequences, by analogy with the above-referenced functions).
+;;;
+;;; * changing the :maximum keyword argument to :count, by analogy
+;;; with CL:REMOVE, CL:SUBSTITUTE, and so on.
+;;;
+;;; * naming the function SPLIT-SEQUENCE rather than PARTITION rather
+;;; than SPLIT.
+;;;
+;;; * adding SPLIT-SEQUENCE-IF and SPLIT-SEQUENCE-IF-NOT.
+;;;
+;;; * The second return value is now an index rather than a copy of a
+;;; portion of the sequence; this index is the `right' one to feed to
+;;; CL:SUBSEQ for continued processing.
+
+;;; There's a certain amount of code duplication here, which is kept
+;;; to illustrate the relationship between the SPLIT-SEQUENCE
+;;; functions and the CL:POSITION functions.
+
+;;; Examples:
+;;;
+;;; * (split-sequence #\; "a;;b;c")
+;;; -> ("a" "" "b" "c"), 6
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t)
+;;; -> ("a" "" "b" "c"), 0
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t :count 1)
+;;; -> ("c"), 4
+;;;
+;;; * (split-sequence #\; "a;;b;c" :remove-empty-subseqs t)
+;;; -> ("a" "b" "c"), 6
+;;;
+;;; * (split-sequence-if (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("" "" "r" "c" "d" "" "r" ""), 11
+;;;
+;;; * (split-sequence-if-not (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("ab" "a" "a" "ab" "a"), 11
+;;;
+;;; * (split-sequence #\; ";oo;bar;ba;" :start 1 :end 9)
+;;; -> ("oo" "bar" "b"), 9
+
+;; cl-utilities note: the license of this file is unclear, and I don't
+;; even know whom to contact to clarify it. If anybody objects to my
+;; assumption that it is public domain, please contact me so I can do
+;; something about it. Previously I required the split-sequence
+ ; package as a dependency, but that was so unwieldy that it was *the*
+;; sore spot sticking out in the design of cl-utilities. -Peter Scott
+
+(in-package :utils-kt)
+
+(export! split-sequence)
+
+(defun split-sequence (delimiter seq &key (count nil) (remove-empty-subseqs nil) (from-end nil)
+ (start 0) (end nil) (test nil test-supplied) (test-not nil test-not-supplied) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by delimiter.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE. In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+ (let ((len (length seq))
+ (other-keys (nconc (when test-supplied
+ (list :test test))
+ (when test-not-supplied
+ (list :test-not test-not))
+ (when key-supplied
+ (list :key key)))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position delimiter seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position delimiter seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+(defun split-sequence-if (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by items satisfying
+predicate.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE-IF. In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+ (let ((len (length seq))
+ (other-keys (when key-supplied
+ (list :key key))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position-if predicate seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position-if predicate seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+(defun split-sequence-if-not (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by items satisfying
+(CL:COMPLEMENT predicate).
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE-IF-NOT. In particular,
+the behaviour of :from-end is possibly different from other versions
+of this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped." ; Emacs syntax highlighting is broken, and this helps: "
+ (let ((len (length seq))
+ (other-keys (when key-supplied
+ (list :key key))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position-if-not predicate seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position-if-not predicate seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+
+
+(pushnew :split-sequence *features*)
Added: dependencies/trunk/cells/utils-kt/strings.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/strings.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,221 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: utils-kt; -*-
+#|
+
+ Utils-kt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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 :utils-kt)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (export '(case$ strloc$ make$ space$ char$ conc-list$ conc$
+ left$ mid$ seg$ right$ insert$ remove$
+ trim$ trunc$ abbrev$ empty$ find$ num$
+ normalize$ down$ lower$ up$ upper$ equal$
+ min$ numeric$ alpha$ assoc$ member$ starts$
+ +return$+ +lf$+ case-string-equal)))
+
+(defmacro case$ (string-form &rest cases)
+ (let ((v$ (gensym))
+ (default (or (find 'otherwise cases :key #'car)
+ (find 'otherwise cases :key #'car))))
+ (when default
+ (setf cases (delete default cases)))
+ `(let ((,v$ ,string-form))
+ (cond
+ ,@(mapcar (lambda (case-forms)
+ `((string-equal ,v$ ,(car case-forms)) ,@(rest case-forms)))
+ cases)
+ (t ,@(or (cdr default) `(nil)))))))
+
+(defmacro case-string-equal (string-form &rest cases)
+ (let ((v$ (gensym))
+ (default (or (find 'otherwise cases :key #'car)
+ (find 'otherwise cases :key #'car))))
+ (when default
+ (setf cases (delete default cases)))
+ `(let ((,v$ ,string-form))
+ (cond
+ ,@(mapcar (lambda (case-forms)
+ `((string-equal ,v$ ,(string (car case-forms))) ,@(rest case-forms)))
+ cases)
+ (t ,@(or (cdr default) `(nil)))))))
+
+;--------
+
+(defmethod shortc (other)
+ (declare (ignorable other))
+ (concatenate 'string "noshortc" (symbol-name (class-name (class-of other)))))
+
+(defmethod longc (other) (shortc other))
+
+(defmethod shortc ((nada null)) nil)
+(defmethod shortc ((many list))
+ (if (consp (cdr many))
+ (mapcar #'shortc many)
+ (conc$ (shortc (car many)) " " (shortc (cdr many)))))
+(defmethod shortc ((self string)) self)
+(defmethod shortc ((self symbol)) (string self))
+(defmethod shortc ((self number)) (num$ self))
+(defmethod shortc ((self character)) (string self))
+
+;-----------------------
+
+(defun strloc$ (substr str)
+ (when (and substr str (not (string= substr "")))
+ (search substr str)))
+
+(defun make$ (&optional (size 0) (char #\space))
+ (make-string size :initial-element (etypecase char
+ (character char)
+ (number (code-char char)))))
+(defun basic$ ()
+ (make-array 0 :element-type 'character :adjustable t :fill-pointer 0))
+
+(defun space$ (size)
+ (make$ size))
+
+(defun char$ (char)
+ (make$ 1 char))
+
+(defun conc-list$ (ss)
+ (when ss
+ (reduce (lambda (s1 s2) (concatenate 'string s1 s2)) ss)))
+
+(defun conc$ (&rest ss)
+ (with-output-to-string (stream)
+ (dolist (s ss)
+ (when s
+ (princ (shortc s) stream)))))
+
+(defun left$ (s n)
+ (subseq s 0 (max (min n (length s)) 0)))
+
+(export! cc$)
+(defun cc$ (code) (string (code-char code)))
+
+(defun mid$ (s offset length)
+ (let* ((slen (length s))
+ (start (min slen (max offset 0)))
+ (end (max start (min (+ offset length) slen))))
+ (subseq s start end)))
+
+(defun seg$ (s offset end)
+ (let* ((slen (length s))
+ (start (min slen (max offset 0)))
+ (end (max start (min end slen))))
+ (subseq s start end)))
+
+(defun right$ (s n)
+ (subseq s (min n (length s))))
+
+(defun insert$ (s c &optional (offset (length s)))
+ (conc$ (subseq s 0 offset)
+ (string c)
+ (subseq s offset)))
+
+(defun remove$ (s offset)
+ (conc$ (subseq s 0 (1- offset))
+ (subseq s offset)))
+
+(defun trim$ (s)
+ (assert (or (null s) (stringp s)))
+ (string-trim '(#\space) s))
+
+(defun trunc$ (s char)
+ (let ((pos (position char s)))
+ (if pos
+ (subseq s 0 pos)
+ s)))
+
+(defun abbrev$ (long$ max)
+ (if (<= (length long$) max)
+ long$
+ (conc$ (left$ long$ (- max 3)) "...")))
+
+(defmethod empty ((nada null)) t)
+(defmethod empty ((c cons))
+ (and (empty (car c))
+ (empty (cdr c))))
+(defmethod empty ((s string)) (empty$ s))
+(defmethod empty (other) (declare (ignorable other)) nil)
+
+(defun empty$ (s)
+ (or (null s)
+ (if (stringp s)
+ (string-equal "" (trim$ s))
+ #+(or) (format t "empty$> sees non-string ~a" (type-of s)))))
+
+(defmacro find$ (it where &rest args)
+ `(find ,it ,where , at args :test #'string-equal))
+
+(defmethod num$ ((n number))
+ (format nil "~d" n))
+
+(defmethod num$ (n)
+ (format nil "~d" n))
+
+(defun normalize$ (s)
+ (down$ s))
+
+(defun down$ (s)
+ (etypecase s
+ (null "")
+ (string (string-downcase s))
+ (number (format nil "~a" s))
+ (symbol (string-downcase (symbol-name s)))
+ (cons (format nil "~{~(~a~)~^ ~}" s))))
+
+(defun lower$ (s)
+ (string-downcase s))
+
+(defun up$ (s)
+ (string-upcase s))
+
+(defun upper$ (s)
+ (string-upcase s))
+
+(defun equal$ (s1 s2)
+ (if (empty$ s1)
+ (empty$ s2)
+ (when s2
+ (string-equal s1 s2))))
+
+(defun min$ (&rest ss)
+ (cond
+ ((null ss) nil)
+ ((null (cdr ss)) (car ss))
+ (t (let ((rmin$ (apply #'min$ (cdr ss))))
+ (if (string< (car ss) rmin$)
+ (car ss) rmin$)))))
+
+(defun numeric$ (s &optional trimmed)
+ (every (lambda (c) (digit-char-p c)) (if trimmed (trim$ s) s)))
+
+(defun alpha$ (s)
+ (every (lambda (c) (alpha-char-p c)) s))
+
+(defmacro assoc$ (item alist &rest kws)
+ `(assoc ,item ,alist :test #'equal , at kws))
+
+(defmacro member$ (item list &rest kws)
+ `(member ,item ,list :test #'string= , at kws))
+
+(defun starts$ (a b)
+ (bwhen (s (search b a))
+ (zerop s)))
+
+(defparameter *return$* (conc$ (char$ #\return) (char$ #\linefeed)))
+(defparameter *lf$* (string #\linefeed))
Added: dependencies/trunk/cells/utils-kt/utils-kt.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/utils-kt.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,30 @@
+;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+
+;(declaim (optimize (debug 2) (speed 1) (safety 1) (compilation-speed 1)))
+(declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))
+
+;;;(operate 'load-op :asdf-aclproj)
+;;;(use-package :asdf-aclproj)
+
+#+(or allegro lispworks cmu mcl clisp cormanlisp sbcl scl abcl)
+
+(asdf:defsystem :utils-kt
+ :name "utils-kt"
+ :author "Kenny Tilton <ktilton at nyc.rr.com>"
+ :version "2007-12-02"
+ :maintainer "Kenny Tilton <ktilton at nyc.rr.com>"
+ :licence "MIT Style"
+ :description "Kenny's Utilities"
+ :long-description "Low-level utilities used by all of Kenny's projects"
+ :components ((:file "defpackage")
+ (:file "core" :depends-on ("defpackage"))
+ (:file "debug" :depends-on ("core"))
+ (:file "flow-control" :depends-on ("core" "debug"))
+ (:file "detritus" :depends-on ("core" "debug"))
+ (:file "strings" :depends-on ("core" "debug"))
+ (:file "datetime" :depends-on ("core" "debug"))
+ (:file "split-sequence" :depends-on ("core" "debug"))))
+
+(defmethod perform ((o load-op) (c (eql (find-system :utils-kt))))
+ ; (pushnew "CELLS" *modules* :test #'string=)
+ (pushnew :utils-kt *features*))
Added: dependencies/trunk/cells/utils-kt/utils-kt.lpr
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/utils-kt/utils-kt.lpr Tue Jan 26 15:20:07 2010
@@ -0,0 +1,39 @@
+;; -*- lisp-version: "8.1 [Windows] (Oct 11, 2008 17:00)"; cg: "1.103.2.10"; -*-
+
+(in-package :cg-user)
+
+(define-project :name :utils-kt
+ :modules (list (make-instance 'module :name "defpackage.lisp")
+ (make-instance 'module :name "core.lisp")
+ (make-instance 'module :name "debug.lisp")
+ (make-instance 'module :name "flow-control.lisp")
+ (make-instance 'module :name "detritus.lisp")
+ (make-instance 'module :name "strings.lisp")
+ (make-instance 'module :name "datetime.lisp")
+ (make-instance 'module :name "split-sequence.lisp"))
+ :projects nil
+ :libraries nil
+ :distributed-files nil
+ :internally-loaded-files nil
+ :project-package-name :common-lisp
+ :main-form nil
+ :compilation-unit t
+ :verbose nil
+ :runtime-modules nil
+ :splash-file-module (make-instance 'build-module :name "")
+ :icon-file-module (make-instance 'build-module :name "")
+ :include-flags (list :local-name-info)
+ :build-flags (list :allow-debug :purify)
+ :autoload-warning t
+ :full-recompile-for-runtime-conditionalizations nil
+ :include-manifest-file-for-visual-styles t
+ :default-command-line-arguments "+cx +t \"Initializing\""
+ :additional-build-lisp-image-arguments (list :read-init-files nil)
+ :old-space-size 256000
+ :new-space-size 6144
+ :runtime-build-option :standard
+ :build-number 0
+ :on-initialization 'default-init-function
+ :on-restart 'do-default-restart)
+
+;; End of Project Definition
Added: dependencies/trunk/cells/variables.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cells/variables.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,118 @@
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
+#|
+
+ Cells -- Automatic Dataflow Managememnt
+
+Copyright (C) 1995, 2006 by Kenneth Tilton
+
+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)
+
+(defun c-variable-accessor (symbol)
+ (assert (symbolp symbol))
+ (c-variable-reader symbol))
+
+(defun (setf c-variable-accessor) (value symbol)
+ (assert (symbolp symbol))
+ (c-variable-writer value symbol))
+
+(defun c-variable-reader (symbol)
+ (assert (symbolp symbol))
+ (assert (get symbol 'cell))
+ (cell-read (get symbol 'cell)))
+
+(defun c-variable-writer (value symbol)
+ (assert (symbolp symbol))
+ (setf (md-slot-value nil symbol) value)
+ (setf (symbol-value symbol) value))
+
+(export! def-c-variable)
+
+(defmacro def-c-variable (v-name cell &key ephemeral owning unchanged-if)
+ (declare (ignore unchanged-if))
+ (let ((c 'whathef)) ;;(gensym)))
+ `(progn
+ (eval-when (:compile-toplevel :load-toplevel)
+ (define-symbol-macro ,v-name (c-variable-accessor ',v-name))
+ (setf (md-slot-cell-type 'null ',v-name) (when ,ephemeral :ephemeral))
+ (when ,owning
+ (setf (md-slot-owning 'null ',v-name) t)))
+ (eval-when (:load-toplevel)
+ (let ((,c ,cell))
+ (md-install-cell nil ',v-name ,c)
+ (awaken-cell ,c)))
+ ',v-name)))
+
+
+(defobserver *kenny* ()
+ (trcx kenny-obs new-value old-value old-value-boundp))
+
+#+test
+(def-c-variable *kenny* (c-in nil))
+
+
+#+test
+(defmd kenny-watcher ()
+ (twice (c? (bwhen (k *kenny*)
+ (* 2 k)))))
+
+(defobserver twice ()
+ (trc "twice kenny is:" new-value self old-value old-value-boundp))
+
+#+test-ephem
+(progn
+ (cells-reset)
+ (let ((tvw (make-instance 'kenny-watcher)))
+ (trcx twice-read (twice tvw))
+ (setf *c-debug* nil)
+ (setf *kenny* 42)
+ (setf *kenny* 42)
+ (trcx post-setf-kenny *kenny*)
+ (trcx print-twice (twice tvw))
+ ))
+
+#+test
+(let ((*kenny* 13)) (print *kenny*))
+
+#+test
+(let ((c (c-in 42)))
+ (md-install-cell '*test-c-variable* '*test-c-variable* c)
+ (awaken-cell c)
+ (let ((tvw (make-instance 'test-var-watcher)))
+ (trcx twice-read (twice tvw))
+ (setf *test-c-variable* 69)
+ (trcx print-testvar *test-c-variable*)
+ (trcx print-twice (twice tvw))
+ (unless (eql (twice tvw) 138)
+ (inspect (md-slot-cell tvw 'twice))
+ (inspect c)
+ ))
+ )
+
+#+test2
+(let ((tvw (make-instance 'test-var-watcher :twice (c-in 42))))
+ (let ((c (c? (trcx joggggggggging!!!!!!!!!!!!!!!)
+ (floor (twice tvw) 2))))
+ (md-install-cell '*test-c-variable* '*test-c-variable* c)
+ (awaken-cell c)
+ (trcx print-testvar *test-c-variable*)
+ (trcx twice-read (twice tvw))
+ (setf (twice tvw) 138)
+ (trcx print-twice (twice tvw))
+ (trcx print-testvar *test-c-variable*)
+ (unless (eql *test-c-variable* 69)
+ (inspect (md-slot-cell tvw 'twice))
+ (inspect c)
+ ))
+ )
+
Added: dependencies/trunk/cl-utilities-1.2.4/README
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/README Tue Jan 26 15:20:07 2010
@@ -0,0 +1,59 @@
+CL-UTILITIES Collection
+=======================
+
+On Cliki.net <http://www.cliki.net/Common%20Lisp%20Utilities>, there
+is a collection of Common Lisp Utilities, things that everybody writes
+since they're not part of the official standard. There are some very
+useful things there; the only problems are that they aren't
+implemented as well as you'd like (some aren't implemented at all) and
+they aren't conveniently packaged and maintained. It takes quite a bit
+of work to carefully implement utilities for common use, commented
+and documented, with error checking placed everywhere some dumb user
+might make a mistake.
+
+The CLRFI process <http://clrfi.alu.org/> is a lot better thought out,
+and will probably produce better standards than informal discussion on
+a Wiki, but it has one problem: at the time of this writing, it's not
+doing anything yet. Until the CLRFI process gets going, I think that a
+high-quality collection of the informal standards on Cliki is a
+valuable thing to have. It's here, and it's called cl-utilities.
+
+The home page is <http://common-lisp.net/project/cl-utilities/>.
+
+Documentation
+-------------
+
+Right now, documentation is at
+<http://www.cliki.net/Common%20Lisp%20Utilities>. There are a few
+differences, though:
+
+* The READ-DELIMITED function takes :start and :end keyword args.
+* A WITH-GENSYMS function is provided for compatibility.
+* COPY-ARRAY is not called SHALLOW-COPY-ARRAY.
+* The ONCE-ONLY macro is included.
+
+Installation
+------------
+
+To install cl-utilities, you'll need to do one of two things:
+
+* Download cl-utilities into a place where asdf can find it, then
+ load it via asdf. You will also need to get the split-sequence
+ package, which cl-utilities depends on.
+
+-or-
+
+* Use asdf-install: (asdf-install:install :cl-utilities)
+
+Feedback
+--------
+
+The current maintainer is Peter Scott. If you have questions, bugs,
+comments, or contributions, please send them to the cl-utilities-devel
+mailing list, <cl-utilities-devel at common-lisp.net>.
+
+License
+-------
+
+The code in cl-utilities is in the public domain. Do whatever you want
+with it.
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/cl-utilities.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/cl-utilities.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,33 @@
+;; -*- Lisp -*-
+
+(defpackage #:cl-utilities-system
+ (:use #:common-lisp #:asdf))
+
+(in-package #:cl-utilities-system)
+
+(defsystem cl-utilities
+ :author "Maintained by Peter Scott"
+ :components ((:file "package")
+ (:file "split-sequence" :depends-on ("package"))
+ (:file "extremum" :depends-on ("package"
+ "with-unique-names"
+ "once-only"))
+ (:file "read-delimited" :depends-on ("package"))
+ (:file "expt-mod" :depends-on ("package"))
+ (:file "with-unique-names" :depends-on ("package"))
+ (:file "collecting" :depends-on ("package"
+ "with-unique-names"
+ "compose"))
+ (:file "once-only" :depends-on ("package"))
+ (:file "rotate-byte" :depends-on ("package"))
+ (:file "copy-array" :depends-on ("package"))
+ (:file "compose" :depends-on ("package"))))
+
+;; Sometimes we can accelerate byte rotation on SBCL by using the
+;; SB-ROTATE-BYTE extension. This loads it.
+#+sbcl
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (handler-case (progn
+ (require :sb-rotate-byte)
+ (pushnew :sbcl-uses-sb-rotate-byte *features*))
+ (error () (delete :sbcl-uses-sb-rotate-byte *features*))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/collecting.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/collecting.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,84 @@
+;; Opinions differ on how a collection macro should work. There are
+;; two major points for discussion: multiple collection variables and
+;; implementation method.
+;;
+;; There are two main ways of implementing collection: sticking
+;; successive elements onto the end of the list with tail-collection,
+;; and using the PUSH/NREVERSE idiom. Tail-collection is usually
+;; faster, except on CLISP, where PUSH/NREVERSE is a little faster.
+;;
+;; The COLLECTING macro only allows collection into one list, and you
+;; can't nest them to get the same effect as multiple collection since
+;; it always uses the COLLECT function. If you want to collect into
+;; multiple lists, use the WITH-COLLECT macro.
+
+(in-package :cl-utilities)
+
+;; This should only be called inside of COLLECTING macros, but we
+;; define it here to provide an informative error message and to make
+;; it easier for SLIME (et al.) to get documentation for the COLLECT
+;; function when it's used in the COLLECTING macro.
+(defun collect (thing)
+ "Collect THING in the context established by the COLLECTING macro"
+ (error "Can't collect ~S outside the context of the COLLECTING macro"
+ thing))
+
+(defmacro collecting (&body body)
+ "Collect things into a list forwards. Within the body of this macro,
+the COLLECT function will collect its argument into the list returned
+by COLLECTING."
+ (with-unique-names (collector tail)
+ `(let (,collector ,tail)
+ (labels ((collect (thing)
+ (if ,collector
+ (setf (cdr ,tail)
+ (setf ,tail (list thing)))
+ (setf ,collector
+ (setf ,tail (list thing))))))
+ , at body)
+ ,collector)))
+
+(defmacro with-collectors ((&rest collectors) &body body)
+ "Collect some things into lists forwards. The names in COLLECTORS
+are defined as local functions which each collect into a separate
+list. Returns as many values as there are collectors, in the order
+they were given."
+ (%with-collectors-check-collectors collectors)
+ (let ((gensyms-alist (%with-collectors-gensyms-alist collectors)))
+ `(let ,(loop for collector in collectors
+ for tail = (cdr (assoc collector gensyms-alist))
+ nconc (list collector tail))
+ (labels ,(loop for collector in collectors
+ for tail = (cdr (assoc collector gensyms-alist))
+ collect `(,collector (thing)
+ (if ,collector
+ (setf (cdr ,tail)
+ (setf ,tail (list thing)))
+ (setf ,collector
+ (setf ,tail (list thing))))))
+ , at body)
+ (values , at collectors))))
+
+(defun %with-collectors-check-collectors (collectors)
+ "Check that all of the COLLECTORS are symbols. If not, raise an error."
+ (let ((bad-collector (find-if-not #'symbolp collectors)))
+ (when bad-collector
+ (error 'type-error
+ :datum bad-collector
+ :expected-type 'symbol))))
+
+(defun %with-collectors-gensyms-alist (collectors)
+ "Return an alist mapping the symbols in COLLECTORS to gensyms"
+ (mapcar #'cons collectors
+ (mapcar (compose #'gensym
+ #'(lambda (x)
+ (format nil "~A-TAIL-" x)))
+ collectors)))
+
+;; Some test code which would be too hard to move to the test suite.
+#+nil (with-collectors (one-through-nine abc)
+ (mapcar #'abc '(a b c))
+ (dotimes (x 10)
+ (one-through-nine x)
+ (print one-through-nine))
+ (terpri) (terpri))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/compose.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/compose.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,51 @@
+;; This version of COMPOSE can only handle functions which take one
+;; value and return one value. There are other ways of writing
+;; COMPOSE, but this is the most commonly used.
+
+(in-package :cl-utilities)
+
+;; This is really slow and conses a lot. Fortunately we can speed it
+;; up immensely with a compiler macro.
+(defun compose (&rest functions)
+ "Compose FUNCTIONS right-associatively, returning a function"
+ #'(lambda (x)
+ (reduce #'funcall functions
+ :initial-value x
+ :from-end t)))
+
+;; Here's some benchmarking code that compares various methods of
+;; doing the same thing. If the first method, using COMPOSE, is
+;; notably slower than the rest, the compiler macro probably isn't
+;; being run.
+#+nil
+(labels ((2* (x) (* 2 x)))
+ (macrolet ((repeat ((x) &body body)
+ (with-unique-names (counter)
+ `(dotimes (,counter ,x)
+ (declare (type (integer 0 ,x) ,counter)
+ (ignorable ,counter))
+ , at body))))
+ ;; Make sure the compiler macro gets run
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (time (repeat (30000000) (funcall (compose #'1+ #'2* #'1+) 6)))
+ (time (repeat (30000000) (funcall (lambda (x) (1+ (2* (1+ x)))) 6)))
+ (time (repeat (30000000)
+ (funcall (lambda (x)
+ (funcall #'1+ (funcall #'2* (funcall #'1+ x))))
+ 6)))))
+
+;; Converts calls to COMPOSE to lambda forms with everything written
+;; out and some things written as direct function calls.
+;; Example: (compose #'1+ #'2* #'1+) => (LAMBDA (X) (1+ (2* (1+ X))))
+(define-compiler-macro compose (&rest functions)
+ (labels ((sharp-quoted-p (x)
+ (and (listp x)
+ (eql (first x) 'function)
+ (symbolp (second x)))))
+ `(lambda (x) ,(reduce #'(lambda (fun arg)
+ (if (sharp-quoted-p fun)
+ (list (second fun) arg)
+ (list 'funcall fun arg)))
+ functions
+ :initial-value 'x
+ :from-end t))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/copy-array.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/copy-array.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,29 @@
+(in-package :cl-utilities)
+
+(defun copy-array (array &key (undisplace nil))
+ "Shallow copies the contents of any array into another array with
+equivalent properties. If array is displaced, then this function will
+normally create another displaced array with similar properties,
+unless UNDISPLACE is non-NIL, in which case the contents of the array
+will be copied into a completely new, not displaced, array."
+ (declare (type array array))
+ (let ((copy (%make-array-with-same-properties array undisplace)))
+ (unless (array-displacement copy)
+ (dotimes (n (array-total-size copy))
+ (setf (row-major-aref copy n) (row-major-aref array n))))
+ copy))
+
+(defun %make-array-with-same-properties (array undisplace)
+ "Make an array with the same properties (size, adjustability, etc.)
+as another array, optionally undisplacing the array."
+ (apply #'make-array
+ (list* (array-dimensions array)
+ :element-type (array-element-type array)
+ :adjustable (adjustable-array-p array)
+ :fill-pointer (when (array-has-fill-pointer-p array)
+ (fill-pointer array))
+ (multiple-value-bind (displacement offset)
+ (array-displacement array)
+ (when (and displacement (not undisplace))
+ (list :displaced-to displacement
+ :displaced-index-offset offset))))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/doc/collecting.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/collecting.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Macro COLLECTING, WITH-COLLECTORS</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Macro</i> <b>COLLECTING</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p>
+
+<p><b>collecting</b> <i>form*</i> => <i>result</i><p>
+
+<p><b>with-collectors</b> <i>(collector*) form*</i> => <i>result</i>*<p>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>forms</i>---an <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/glo_i.html#implicit_progn">implicit
+progn</a>.
+
+<p><i>collector</i>---a symbol which will have a collection function bound to it.
+
+<p><i>result</i>---a collected list.
+
+<p>
+<p><b>Description:</b><p>
+<p>
+<b>collecting</b> collects things into a list. Within the
+body of this macro, the <b>collect</b> function will collect its
+argument into <i>result</i>.
+
+<p><b>with-collectors</b> collects some things into lists. The
+<i>collector</i> names are defined as local functions which each
+collect into a separate list. Returns as many values as there are
+collectors, in the order they were given.
+
+<p><b>Exceptional situations:</b><p>
+<p>
+
+<p>If the <i>collector</i> names are not all symbols, a
+<b>type-error</b> will be signalled.
+
+<p><b>Examples:</b>
+
+<pre>
+(collecting (dotimes (x 10) (collect x))) => (0 1 2 3 4 5 6 7 8 9)
+
+(multiple-value-bind (a b)
+ (with-collectors (x y)
+ (x 1)
+ (y 2)
+ (x 3))
+ (append a b)) => (1 2 3)
+</pre>
+
+<p><p><b>Implementation notes:</b></p>
+
+<p>Opinions differ on how a collection macro should work. There are
+two major points for discussion: multiple collection variables and
+implementation method.</b>
+
+<p>There are two main ways of implementing collection: sticking
+successive elements onto the end of the list with tail-collection, or
+using the PUSH/NREVERSE idiom. Tail-collection is usually faster,
+except on CLISP, where PUSH/NREVERSE is a little faster because it's
+implemented in C which is always faster than Lisp bytecode.</p>
+
+<p>The <b>collecting</b> macro only allows collection into one list,
+and you can't nest them to get the same effect as multiple collection
+since it always uses the <b>collect</b> function. If you want to
+collect into multiple lists, use the <b>with-collect</b> macro.</p>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+ </body></html>
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/doc/compose.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/compose.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function COMPOSE</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>COMPOSE</b></p>
+
+<p><p><b>Syntax:</b></p>
+
+<p><p><b>compose</b> <i>function* <tt>=></tt> composite-function</i></p>
+
+<p><p><b>Arguments and Values:</b></p>
+
+<p><p><i>function</i>---a <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/glo_f.html#function_designator">function designator</a></i>.</p>
+
+<p><i>composite-function</i>---a <i>function</i>.
+
+<p><p><b>Description:</b></p>
+
+<p>Composes its arguments into a single composite function. All its
+arguments are assumed to designate functions which take one argument
+and return one argument.
+
+<p><tt>(funcall (compose f g) 42)</tt> is equivalent to <tt>(f (g
+42))</tt>. Composition is right-associative.
+
+<p><b>Examples:</b>
+
+<pre>
+;; Just to illustrate order of operations
+(defun 2* (x) (* 2 x))
+
+
+(funcall (compose #'1+ #'1+) 1) => 3
+(funcall (compose '1+ '2*) 5) => 11
+(funcall (compose #'1+ '2* '1+) 6) => 15
+</pre>
+
+<p><b>Notes:</b>
+<p>If you're dealing with multiple arguments and return values, the
+same concept can be used. Here is some code that could be useful:
+
+<pre>
+(defun mv-compose2 (f1 f2)
+ (lambda (&rest args)
+ (multiple-value-call f1 (apply f2 args))))
+
+(defun mv-compose (&rest functions)
+ (if functions
+ (reduce #'mv-compose2 functions)
+ #'values))
+</pre>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/copy-array.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/copy-array.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function COPY-ARRAY</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>COPY-ARRAY</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p>
+
+<p><b>copy-array</b> <i>array <tt>&key</tt> undisplace</i> => <i>new-array</i>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>array</i>---an <i>array</i>. <p>
+
+<i>undisplace</i>---a <i>generalized boolean</i>. The default is <i>false</i>.<p>
+
+<i>new-array</i>---an <i>array</i></a>. <p>
+
+<p>
+<p><b>Description:</b><p>
+
+<p>Shallow copies the contents of <i>array</i> into another array with
+equivalent properties. If <i>array</i> is displaced, then this
+function will normally create another displaced array with similar
+properties, unless <i>undisplace</i> is <i>true</i>, in which case the
+contents of <i>array</i> will be copied into a completely new, not
+displaced, array.</p>
+
+<p><p><b>Examples:</b></p>
+<pre>
+(copy-array #(1 2 3)) => #(1 2 3)
+
+(let ((array #(1 2 3)))
+ (eq (copy-array array) array)) => NIL
+</pre>
+
+<p><p><b>Side Effects:</b> None.</p>
+
+<p><p><b>Affected By:</b> None.</p>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/expt-mod.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/expt-mod.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function EXPT-MOD</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>EXPT-MOD</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p><b>expt-mod</b> <i>n exponent divisor</i> => <i>result</i>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>n</i>---a <i>number</i></a>. <p>
+
+<i>exponent</i>---a <i>number</i></a>. <p>
+
+<i>divisor</i>---a <i>number</i></a>. <p>
+
+<i>result</i>---a <i>number</i></a>. <p>
+
+<p>
+<p><b>Description:</b><p>
+<p>
+<b>expt-mod</b> returns <i>n</i> raised to the <i>exponent</i> power,
+modulo <i>divisor</i>. <tt>(expt-mod n exponent divisor)</tt> is
+equivalent to <tt>(mod (expt n exponent) divisor)</tt>.
+
+<p>
+<p><b>Exceptional situations:</b><p>
+<p>
+
+<p>The exceptional situations are the same as those for <tt>(mod (expt
+n exponent) divisor)</tt>.
+
+<p><p><b>Notes:</b></p>
+
+<p>One might wonder why we shouldn't simply write <tt>(mod (expt n
+exponent) divisor)</tt>. This function exists because the naïve
+way of evaluating <tt>(mod (expt n exponent) divisor)</tt> produces a
+gigantic intermediate result, which kills performance in applications
+which use this operation heavily. The operation can be done much more
+efficiently. Usually the compiler does this optimization
+automatically, producing very fast code. However, we can't
+<i>depend</i> on this behavior if we want to produce code that is
+guaranteed not to perform abysmally on some Lisp implementations.
+
+<p>Therefore cl-utilities provides a standard interface to this
+composite operation which uses mediocre code by default. Specific
+implementations can usually do much better, but some do much
+worse. We can get the best of both by simply using the same interface
+and doing read-time conditionalization within cl-utilities to get
+better performance on compilers like SBCL and Allegro CL which
+optimize this operation.
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/extremum.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/extremum.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,155 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function EXTREMUM, EXTREMA, N-MOST-EXTREME</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>EXTREMUM, EXTREMA, N-MOST-EXTREME</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p>
+
+<p><b>extremum</b> <i>sequence predicate <tt>&key</tt> key (start 0) end</i> => <i>morally-smallest-element</i><p>
+<p><b>extrema</b> <i>sequence predicate <tt>&key</tt> key (start 0) end</i> => <i>morally-smallest-elements</i><p>
+<p><b>n-most-extreme</b> <i>n sequence predicate <tt>&key</tt> key (start 0) end</i> => <i>n-smallest-elements</i><p>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>sequence</i>---a <i>proper sequence</i></a>. <p>
+
+<i>predicate</i>---a <i>designator</i> for a <i>function</i> of two
+arguments that returns a <i>generalized boolean</i>. <p>
+
+<i>key</i>---a <i>designator</i> for a <i>function</i> of one
+argument, or <b>nil</b>. <p>
+
+<i>start, end</i>---bounding index designators of <i>sequence</i>. The
+defaults for start and end are 0 and <b>nil</b>, respectively.<p>
+
+<i>morally-smallest-element</i>---the element of <i>sequence</i> that
+would appear first if the sequence were ordered according to <a
+class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_sortcm_stable-sort.html"><b>sort</b></a>
+using <i>predicate</i> and <i>key</i>
+
+<p><i>morally-smallest-elements</i>---the identical elements of
+<i>sequence</i> that would appear first if the sequence were ordered
+according to <a class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_sortcm_stable-sort.html"><b>sort</b></a>
+using <i>predicate</i> and <i>key</i>. If <i>predicate</i> states that
+neither of two objects is before the other, they are considered
+identical.
+
+<i>n</i>---a positive integer<p>
+
+<i>n-smallest-elements</i>---the <i>n</i> elements of <i>sequence</i> that
+would appear first if the sequence were ordered according to <a
+class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_sortcm_stable-sort.html"><b>sort</b></a>
+using <i>predicate</i> and <i>key</i>
+
+<p>
+<p><b>Description:</b><p>
+<p>
+<b>extremum</b> returns the element of <i>sequence</i> that would
+appear first if the subsequence of <i>sequence</i> specified by
+<i>start</i> and <i>end</i> were ordered according to <a
+class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_sortcm_stable-sort.html"><b>sort</b></a>
+using <i>predicate</i> and <i>key</i>.
+
+
+<p><p><b>extremum</b> determines the relationship between two elements
+by giving keys extracted from the elements to the
+<i>predicate</i>. The first argument to the <i>predicate</i> function
+is the part of one element of <i>sequence</i> extracted by the
+<i>key</i> function (if supplied); the second argument is the part of
+another element of <i>sequence</i> extracted by the <i>key</i>
+function (if supplied). <i>Predicate</i> should return <i>true</i> if
+and only if the first argument is strictly less than the second (in
+some appropriate sense). If the first argument is greater than or
+equal to the second (in the appropriate sense), then the
+<i>predicate</i> should return <i>false</i>. <p>
+
+<p>The argument to the <i>key</i> function is the <i>sequence</i>
+element. The return value of the <i>key</i> function becomes an
+argument to <i>predicate</i>. If <i>key</i> is not supplied or
+<b>nil</b>, the <i>sequence</i> element itself is used. There is no
+guarantee on the number of times the <i>key</i> will be called. <p>
+
+<p>If the <i>key</i> and <i>predicate</i> always return, then the
+operation will always terminate. This is guaranteed even if the
+<i>predicate</i> does not really consistently represent a total order
+(in which case the answer may be wrong). If the <i>key</i>
+consistently returns meaningful keys, and the <i>predicate</i> does
+reflect some total ordering criterion on those keys, then the answer
+will be right <p>
+
+<p>The <i>predicate</i> is assumed to consider two elements <tt>x</tt>
+and <tt>y</tt> to be equal if <tt>(funcall </tt><i>predicate</i><tt>
+</tt><i>x</i><tt> </tt><i>y</i><tt>)</tt> and <tt>(funcall
+</tt><i>predicate</i><tt> </tt><i>y</i><tt> </tt><i>x</i><tt>)</tt>
+are both <i>false</i>.
+
+
+<p>The return value of <tt>(extremum predicate sequence :key key)</tt>
+can be defined as <tt>(elt (<a class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_sortcm_stable-sort.html"><b>sort</b></a>
+predicate (subseq sequence start end) :key key) 0)</tt> except when
+<i>sequence</i> is empty (see Exceptional Situations), but may use
+faster (less asymptotically complex) algorithms to find this answer.
+
+<p><b>extrema</b> is similar to <b>extremum</b>, but it returns a list
+of values. There can be more than one extremum, as determined by
+<i>predicate</i>, and with <b>extremum</b> the choice of which
+extremum to return is arbitrary. <b>extrema</b> returns all the
+possible values which <i>predicate</i> determines to be equal.
+
+<p><b>n-most-extreme</b> returns a list of <i>n</i> values without
+testing for equality. It orders <i>sequence</i> in the same way as
+<b>extremum</b> and <b>extrema</b>, then returns the first <i>n</i>
+elements of the sorted sequence.
+
+<p>
+<p><b>Exceptional situations:</b><p>
+<p>
+
+<p>If <i>sequence</i> is empty, then the error <i>no-extremum</i> is
+signalled. Invoking the <b>continue</b> restart will cause
+<b>extremum</b> to return <b>nil</b>.
+
+
+<p>Should be prepared to signal an error of type <b>type-error</b> if
+<i>sequence</i> is not a proper sequence.
+
+<p>If there are fewer than <i>n</i> values in the part of
+<i>sequence</i> that <b>n-most-extreme</b> may operate on, it returns
+all the values it can in sorted order and signals the warning
+<b>n-most-extreme-not-enough-elements</b>. This warning stores the
+given values for <i>n</i> and the relevant subsequence, and they may
+be accessed with <b>n-most-extreme-not-enough-elements-n</b> and
+<b>n-most-extreme-not-enough-elements-subsequence</b>, respectively.
+
+<p><p><b>Implementation notes:</b></p>
+
+<p>There are two implementations of this function included in
+cl-utilities, which should only concern you if you want to squeeze out
+more efficiency, since the versions perform differently on different
+inputs.
+
+<p>The function <b>extremum-fastkey</b> is used exactly like
+<b>extremum</b>, but it calls <i>key</i> fewer times. If <i>key</i> is
+fast, <b>extremum-fastkey</b> is slower than regular <b>extremum</b>,
+but if <i>key</i> is hard to compute you can get significant gains in
+speed. The <b>extremum-fastkey</b> function is more complicated than
+<b>extremum</b>, and therefore may be more likely to contain
+bugs. That said, it doesn't seem buggy.</p>
+
+<p>Don't worry about the performance of passing <tt>#'identity</tt> as
+<i>key</i>. This is optimized by a compiler macro.</p>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/index.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/index.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,58 @@
+<html>
+<head>
+ <title>cl-utilities manual</title>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+</head>
+<body>
+
+<h1>cl-utilities manual</h1>
+
+<p>Everybody writes some utilities because they're not part of the
+standard but they're so broadly useful. This results in a lot of wheel
+reinvention, and most reinventions are not as good as they should
+be. The cl-utilities project is an actively maintained collection of
+some of these utilities, with high-quality public-domain
+implementations and decent documentation.
+
+<h2>Table of contents:</h2>
+
+<ul style="list-style-type: none;">
+
+<li><a href="split-sequence.html">SPLIT-SEQUENCE, SPLIT-SEQUENCE-IF,
+SPLIT-SEQUENCE-IF-NOT</a>. Used for splitting sequences.</li>
+
+<li><a href="extremum.html">EXTREMUM, EXTREMA,
+N-MOST-EXTREME</a>: Finding extreme values in sequences based on
+user-defined criteria.</li>
+
+<li><a href="read-delimited.html">READ-DELIMITED</a> reads from a
+sequence delimited somehow, in a somewhat inconvenient but hopefully
+efficient way.</li>
+
+<li><a href="expt-mod.html">EXPT-MOD</a>, an interface for calculating
+<tt>(mod (expt n e) m)</tt> efficiently across implementations.</li>
+
+<li><a href="with-unique-names.html">WITH-UNIQUE-NAMES, née
+WITH-GENSYMS</a>. A classic macro-writing macro for preventing
+variable capture.</li>
+
+<li><a href="collecting.html">COLLECTING, WITH-COLLECTORS</a>. Some
+macros for clearly and efficiently collecting items into lists.</li>
+
+<li><a href="once-only.html">ONCE-ONLY</a>, a classic macro-writing
+macro for preventing multiple evaluation.</li>
+
+<li><a href="rotate-byte.html">ROTATE-BYTE</a> rotates bits in a byte</li>
+
+<li><a href="copy-array.html">COPY-ARRAY</a> shallow copies arrays.</li>
+
+<li><a href="compose.html">COMPOSE</a>. Composes functions.</li>
+
+</ul>
+
+<p><hr>Public domain, maintained by <a
+href="mailto:sketerpot at gmail.com">Peter Scott</a>. For more information, see
+the <a href="http://common-lisp.net/project/cl-utilities/">home page</a>.
+
+</body>
+</html>
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/doc/once-only.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/once-only.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/transitional.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Macro ONCE-ONLY</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Macro</i> <b>ONCE-ONLY</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p>
+
+<p><b>once-only</b> <i>(name*) form*</i>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>name</i>---a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/glo_s.html#symbol"><i>symbol</i></a></a>. <p>
+
+<i>form</i>---a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/glo_f.html#form"><i>form</i></a></a>. <p>
+
+<p>
+<p><b>Description:</b><p>
+<p>Meant to be used in macro code, <b>once-only</b> guards against
+multiple evaluation of its arguments in macroexpansion code. Any
+concise description would be far too vague to grasp, but <a
+href="http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/1783554653afad7f/f6357129c8c1c002?rnum=1&_done=%2Fgroup%2Fcomp.lang.lisp%2Fbrowse_frm%2Fthread%2F1783554653afad7f%2F940b6ebd2d1757f4%3F#doc_f6357129c8c1c002">this
+thread on comp.lang.lisp</a> does a decent job of explaining what
+<b>once-only</b> does.
+
+<p><p><b>Notes:</b></p>
+
+<p>The description here is frustratingly non-descriptive, and I
+apologize for that. If you understand <b>once-only</b> and can give a
+better explanation, I would be very grateful—not to mention
+completely awed.
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/read-delimited.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/read-delimited.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function READ-DELIMITED</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>READ-DELIMITED</b></p>
+
+<p><p><b>Syntax:</b></p>
+
+<p><p><b>read-delimited</b> <i>sequence stream <tt>&key </tt> start end delimiter test key</i> => <i>position, delimited-p</i></p>
+
+<p><p><b>Arguments and Values:</b></p>
+
+<p><p><i>sequence</i>---a <i>sequence</i>.</p>
+
+<p><i>stream</i>---an <i>input stream</i>.</p>
+<p><i>start, end</i>---<i>bounding index designators</i> of
+<i>sequence</i>. The defaults for <i>start</i> and <i>end</i> are 0
+and <b>nil</b>, respectively.
+
+<p><i>delimiter</i>---a <i>character</i>. It defaults to #\newline.</p>
+<p><i>test</i>---a <i>designator</i> for a <i>function</i> of two
+<i>arguments</i> that returns a <i>generalized boolean</i>.</p>
+
+<p><i>key</i>---a <i>designator</i> for a <i>function</i> of one
+argument, or <b>nil</b>.</p>
+<p><i>position</i>---an <i>integer</i> greater than or equal to zero,
+and less than or equal to the <i>length</i> of the sequence.</p>
+
+<p><i>delimited-p</i>---the result of the last invokation of <i>test</i></p>
+
+<p><p><b>Description:</b></p>
+
+<p><p>Destructively modifies <i>sequence</i> by replacing
+<i>elements</i> of <i>sequence</i> <i>bounded</i> by <i>start</i> and
+<i>end</i> with <i>elements</i> read from <i>stream</i>.</p>
+
+<p><p><i>Test</i> is called with the actual read character, converted
+by applying <i>key</i> to it, as the first and <i>delimiter</i> as the
+second argument.</p>
+
+<p><p>If a character is read for which (funcall <i>test</i> (funcall
+<i>key</i> <b>char</b>) <i>delimiter</i>) is non-nil,
+<b>read-delimited</b> terminates the copying even before reaching
+<i>end of file</i> or the <i>end</i> of the <i>bounding
+designator</i>.</p>
+
+<p><p><b>read-delimited</b> returns the index of the first
+<i>element</i> of <i>sequence</i> that was not updated as the first
+and the result of the last invokation of <i>test</i> as the second
+value.</p>
+
+<p><p><i>Sequence</i> is destructively modified by copying successive
+<i>elements</i> into it from <i>stream</i>. If the <i>end of file</i>
+for <i>stream</i> is reached before copying all <i>elements</i> of the
+subsequence, then the extra <i>elements</i> near the end of
+<i>sequence</i> are not updated.</p>
+
+<p><b>Exceptional situations:</b>
+
+<p>If <i>start</i> and/or <i>end</i> are out of bounds, or if
+<i>start</i> > <i>end</i>, then a
+<b>read-delimited-bounds-error</b> error is signalled. This error is
+passed the values of <i>start</i>, <i>end</i>, and <i>sequence</i>,
+which can be read with <b>read-delimited-bounds-error-start</b>,
+<b>read-delimited-bounds-error-end</b>, and
+<b>read-delimited-bounds-error-sequence</b>,
+respectively.
+
+<p><p><b>Implementation notes:</b></p>
+
+<p>This is one of the more complex utilities, and the amount of
+argument checking needed to do it properly is daunting. An amazing 76%
+of the code is spent on making sure that the bounds are valid and in
+order, and on what to do if they aren't. Once you remove all that, the
+actual function which does all the work is quite simple, and unlikely
+to contain bugs.</p>
+
+<p>The design of this function makes it a little annoying to use, but
+it is more efficient. If you need something more high-level, this
+could be built on top of <b>read-delimited</b> fairly easily.</p>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/rotate-byte.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/rotate-byte.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function ROTATE-BYTE</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>ROTATE-BYTE</b></a></a> <p>
+<p><b>Syntax:</b><p>
+
+<p>
+
+<p><b>rotate-byte</b> <i>count bytespec integer</i> => <i>result</i>
+<p>
+<p><b>Arguments and Values:</b><p>
+<p>
+<i>count</i>---an <i>integer</i></a>. <p>
+
+<i>bytespec</i>---a <i>byte specifier</i></a>. <p>
+
+<i>integer</i>---an <i>integer</i></a>. <p>
+
+<i>result</i>---an <i>integer</i></a>. <p>
+
+<p>
+<p><b>Description:</b><p>
+
+<p>Rotates a field of bits within <i>integer</i>; specifically, returns an
+integer that contains the bits of <i>integer</i> rotated <i>count</i> times
+leftwards within the byte specified by <i>bytespec</i>, and elsewhere
+contains the bits of <i>integer</i>.</p>
+
+<p><p><b>Examples:</b></p>
+<pre>
+(rotate-byte 3 (byte 32 0) 3) => 24
+(rotate-byte 3 (byte 5 5) 3) => 3
+(rotate-byte 6 (byte 8 0) -3) => -129
+</pre>
+
+<p><p><b>Side Effects:</b> None.</p>
+
+<p><p><b>Affected By:</b> None.</p>
+
+<p><p><b>Exceptional Situations:</b> None.</p>
+
+<p><p><b>See Also:</b></p>
+
+<p><a class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_bytecm_by_yte-position.html"><b>byte</b></a>,
+<a class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/fun_dpb.html"><b>dpb</b></a>, <a
+class="hyperspec" href ="
+http://www.lispworks.com/documentation/HyperSpec/Body/acc_ldb.html"><b>ldb</b></a>
+
+<p><b>Implementation notes</b>
+
+<p>SBCL provides the sb-rotate-byte extension to do this
+efficiently. On SBCL, cl-utilities uses this extension
+automatically. On other implementations, portable Common Lisp code is
+used instead.
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/split-sequence.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/split-sequence.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Function SPLIT-SEQUENCE, SPLIT-SEQUENCE-IF, SPLIT-SEQUENCE-IF-NOT</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><p><i>Function</i> <b>SPLIT-SEQUENCE, SPLIT-SEQUENCE-IF, SPLIT-SEQUENCE-IF-NOT</b></p>
+
+<p><p><b>Syntax:</b></p>
+
+<p><p><b>split-sequence</b> <i>delimiter sequence <tt>&key</tt> count remove-empty-subseqs from-end start end test test-not key</i> => <i>list, index</i></p>
+<p><p><b>split-sequence-if</b> <i>predicate sequence <tt>&key</tt> count remove-empty-subseqs from-end start end key</i> => <i>list, index</i></p>
+
+<p><p><b>split-sequence-if-not</b> <i>predicate sequence <tt>&key</tt> count remove-empty-subseqs from-end start end key</i> => <i>list, index</i></p>
+
+<p><p><b>Arguments and Values:</b></p>
+
+<p><p><i>delimiter</i>---an <i>object</i>.</p>
+
+<p><i>predicate</i>---a <i>designator</i> for a <i>function</i> of one <i>argument</i> that returns a <i>generalized boolean</i>.</p>
+<p><i>sequence</i>---a <i>proper sequence</i>.</p>
+
+<p><i>count</i>---an <i>integer</i> or <b>nil</b>. The default is <b>nil</b>.</p>
+<p><i>remove-empty-subseqs</i>---a <i>generalized boolean</i>. The default is <i>false</i>.</p>
+
+<p><i>from-end</i>---a <i>generalized boolean</i>. The default is <i>false</i>.</p>
+<p><i>start, end</i>---<i>bounding index designators</i> of <i>sequence</i>. The defaults for </i>start</i> and <i>end</i> are <tt>0</tt> and <b>nil</b>, respectively.</p>
+
+<p><i>test</i>---a <i>designator</i> for a <i>function</i> of two <i>arguments</i> that returns a <i>generalized boolean</i>.</p>
+<p><i>test-not</i>---a <i>designator</i> for a <i>function</i> of two <i>arguments</i> that returns a <i>generalized boolean</i>.</p>
+
+<p><i>key</i>---a <i>designator</i> for a <i>function</i> of one <i>argument</i>, or <b>nil</b>.</p>
+<p><i>list</i>---a <i>proper sequence</i>.</p>
+
+<p><i>index</i>---an <i>integer</i> greater than or equal to zero, and less than or equal to the <i>length</i> of the <i>sequence</i>.</p>
+
+<p><p><b>Description:</b></p>
+
+<p><p>Splits <i>sequence</i> into a list of subsequences delimited by objects <i>satisfying the test</i>.
+
+
+<p><i>List</i> is a list of sequences of the same kind as <i>sequence</i> that has elements consisting of subsequences of <i>sequence</i> that were delimited in the argument by elements <i>satisfying the test</i>. <i>Index</i> is an index into <i>sequence</i> indicating the unprocessed region, suitable as an argument to <a class="hyperspec" href =" http://www.lispworks.com/documentation/HyperSpec/Body/acc_subseq.html"><b>subseq</b></a> to continue processing in the same manner if desired.
+
+
+<p>The <i>count</i> argument, if supplied, limits the number of subsequences in the first return value; if more than <i>count</i> delimited subsequences exist in <i>sequence</i>, the <i>count</i> leftmost delimited subsequences will be in order in the first return value, and the second return value will be the index into <i>sequence</i> at which processing stopped.
+
+<p>If <i>from-end</i> is non-null, <i>sequence</i> is conceptually processed from right to left, accumulating the subsequences in reverse order; <i>from-end</i> only makes a difference in the case of a non-null <i>count</i> argument. In the presence of <i>from-end</i>, the <i>count</i> rightmost delimited subsequences will be in the order that they are in <i>sequence</i> in the first return value, and the second is the index indicating the end of the unprocessed region.
+
+
+<p>The <i>start</i> and <i>end</i> keyword arguments permit a certain subsequence of the <i>sequence</i> to be processed without the need for a copying stage; their use is conceptually equivalent to partitioning the subsequence delimited by <i>start</i> and <i>end</i>, only without the need for copying.
+
+<p>If <i>remove-empty-subseqs</i> is null (the default), then empty subsequences will be included in the result.
+
+
+<p>In all cases, the subsequences in the first return value will be in the order that they appeared in <i>sequence</i>.
+
+<p><p><b>Examples:</b></p>
+
+<p><pre>
+ (split-sequence:SPLIT-SEQUENCE #\Space "A stitch in time saves nine.")
+=> ("A" "stitch" "in" "time" "saves" "nine.")
+ 28
+ (split-sequence:SPLIT-SEQUENCE #\, "foo,bar ,baz, foobar , barbaz,")
+=> ("foo" "bar " "baz" " foobar " " barbaz" "")
+ 30
+</pre>
+
+<p><p><b>Implementation notes:</b></p>
+
+<p>This code was written various people, and the license is
+unknown. Since multiple people worked on it collaboratively and none
+of them seem interested in keeping their intellectual property rights
+to it, I'll assume that it is in the public domain (since the process
+that produced it seems like the very essence of public domain). If
+this is incorrect, please <a href="mailto:sketerpot at gmail.com">contact
+me</a> so we can get it straightened out.</p>
+
+<p>The implementation itself is mature and well tested, and it is
+widely used. The code should be fast enough for most people, but be
+warned: it was written with vectors in mind, with list manipulation as
+an afterthought. It does a lot of things that are quick on vectors but
+slow on lists, and this can result in many orders of magnitude
+slowdown in list benchmarks versus code written for lists. If this is
+a problem for you, it should be straightforward to write your own,
+such as the (more limited, not API compatible) example function given
+by Szymon in <a
+href="http://common-lisp.net/pipermail/cl-utilities-devel/2006-May/000011.html">this
+mailing list post</a>:</p>
+
+<p><pre>
+(defun split-list-if (test list &aux (start list) (end list))
+ (loop while (and end (setq start (member-if-not test end)))
+ collect (ldiff start (setq end (member-if test start)))))
+</pre></p>
+
+<p>If this is an issue for enough people, I could optimize the code
+and fix this problem. I'm reluctant to do that, however, since the
+code works and is tested. It's usually more important to be correct
+and non-buggy than to be fast, and I have been known to introduce
+bugs.</p>
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</body></html>
Added: dependencies/trunk/cl-utilities-1.2.4/doc/style.css
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/style.css Tue Jan 26 15:20:07 2010
@@ -0,0 +1,16 @@
+pre {
+ margin-right: 0.5cm;
+ border: thin black solid;
+ background: #F3EEEE;
+ padding: 0.5em;
+}
+
+h1 {
+ font-family: sans-serif;
+ font-variant: small-caps;
+}
+
+h2 {
+ font-family: sans-serif;
+ font-size: medium;
+}
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/doc/with-unique-names.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/doc/with-unique-names.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+<HEAD>
+ <TITLE>Macro WITH-UNIQUE-NAMES</TITLE>
+ <LINK REL="stylesheet" HREF="style.css" type="text/css">
+</HEAD>
+<BODY>
+
+<p><i>Macro</i> <b>WITH-UNIQUE-NAMES</b></p><p><b>Syntax:</b></p><p>
+
+<b>with-unique-names</b> <i>({<i>var</i> | (<i>var</i>
+ <i>prefix</i>)}<b>*</b>) <i>declaration</i><b>*</b>
+ <i>form</i><b>*</b></i> => <i><i>result</i><b>*</b></i>
+
+
+ </p><p><b>Arguments and Values:</b></p><p>
+ <p><i>var</i>---a <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#symbol"><i>symbol</i></a>;
+ not <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#evaluate"><i>evaluate</i></a>d.</p>
+ <p><i>prefix</i>---a <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string_designator"><i>string designator</i></a>; not
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#evaluate"><i>evaluate</i></a>d. The default is <i>var</i>.</p>
+
+ <p><i>declaration</i>---a <a href ="
+ http://www.lispworks.com/documentation/HyperSpec/Body/sym_declare.html"><b>declare</b></a>
+ <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#expression"><i>expression</i></a>;
+ not <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#evaluate"><i>evaluate</i></a>d.</p>
+ <p><i>form</i>---a <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#form"><i>form</i></a>.</p>
+ <p><i>results</i>---the <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_v.htm#value"><i>value</i></a>s
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_r.htm#return"><i>return</i></a>ed by the <i>form</i>s.</p>
+
+ </p><p><b>Description:</b></p><p> <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#Execute"><i>Execute</i></a>s
+ a series of <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#form"><i>form</i></a>s
+ with each
+ <i>var</i> <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#bound"><i>bound</i></a> to a <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh"><i>fresh</i></a>,
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#uninterned"><i>uninterned</i></a> <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#symbol"><i>symbol</i></a>. The
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#uninterned"><i>uninterned</i></a> <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#symbol"><i>symbol</i></a> is created as if by
+ a <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#call"><i>call</i></a> to <a href =" http://www.lispworks.com/documentation/HyperSpec/Body/fun_gensym.html"><b>gensym</b></a> with the
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string"><i>string</i></a> denoted by <i>prefix</i>---or, if
+ <i>prefix</i> is not supplied, the <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string"><i>string</i></a>
+
+ denoted by <i>var</i>---as <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#argument"><i>argument</i></a>.
+ <p></p> The <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_v.htm#variable"><i>variable</i></a>
+ <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binding"><i>binding</i></a>s
+ created are <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lexical"><i>lexical</i></a>
+ unless <a
+ HREF="http://www.lispworks.com/documentation/HyperSpec/Body/dec_specia.htm#special"><b>special</b></a>
+
+ <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_d.htm#declaration"><i>declaration</i></a>s are specified.
+ <p></p>
+ The <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#form"><i>form</i></a>s are <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#evaluate"><i>evaluate</i></a>d in order, and
+ the <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_v.htm#value"><i>value</i></a>s of all but the last are discarded (that
+ is, the body is an <a HREF="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#implicit_progn"><i>implicit progn</i></a>).
+ </p><p><b>Examples:</b></p><p>
+<pre>
+
+ (with-unique-names (sym1) sym1) => #:SYM13142
+ (with-unique-names ((sym1 "SYM1-")) sym1) => #:SYM1-3143
+ (find-symbol "SYM1-3143") => NIL, NIL
+ (with-unique-names ((sym #\Q)) sym) => #:Q3144
+ (with-unique-names ((sym1 :sym1-)) sym1) => #:SYM1-3145
+ (with-unique-names (sym1) (symbol-package sym1)) => NIL
+ (with-unique-names (sym8) (eq sym8 sym8)) => T
+ (with-unique-names (sym9) (set sym9 42) (symbol-value sym9)) => 42
+</pre>
+
+ </p><p><b>Side Effects:</b></p><p>
+ Might increment <a href =" http://www.lispworks.com/documentation/HyperSpec/Body/var_stgensym-counterst.html"><b>*gensym-counter*</b></a> once for each
+ <i>var</i>.
+ </p><p><b>Affected by:</b></p><p> <a href ="
+ http://www.lispworks.com/documentation/HyperSpec/Body/var_stgensym-counterst.html"><b>*gensym-counter*</b></a>
+
+ </p><p><b>Exceptional Situations:</b></p><p>
+ None.
+ </p><p><b>See Also:</b></p><p>
+<a href =" http://www.lispworks.com/documentation/HyperSpec/Body/fun_gensym.html"><b>gensym</b></a>, <a href =" http://www.lispworks.com/documentation/HyperSpec/Body/speope_letcm_letst.html"><b>let</b></a></b>
+ </p>
+ </p>
+
+<p><b>Notes:</b>
+<p>This is an extension of the classic macro <b>with-gensyms</b>. In
+fact, cl-utilities also exports <b>with-gensyms</b>, and it can be
+used as usual. The exported <b>with-gensyms</b> is actually just an
+alias for <b>with-unique-names</b> which gives a warning at
+compile-time if the extensions of <b>with-unique-names</b> are used.
+
+<p>You are encouraged to use <b>with-unique-names</b> instead of
+<b>with-gensyms</b> because it is a little more flexible and because
+it tells what is going on rather than how it works. This is a somewhat
+controversial point, so go ahead and use whichever you like if you
+have an opinion on it. But if you're a newbie who honestly doesn't
+care, please use <b>with-unique-names</b>.
+
+<p class="footer"><hr><a href="index.html">Manual Index</a></p>
+
+</BODY>
+</HTML>
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/expt-mod.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/expt-mod.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,38 @@
+(in-package :cl-utilities)
+
+;; This is portable Common Lisp, but implementation-specific code may
+;; improve performance considerably.
+(defun expt-mod (n exponent modulus)
+ "As (mod (expt n exponent) modulus), but more efficient."
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ ;; It's much faster on SBCL and ACL to use the simple method, and
+ ;; trust the compiler to optimize it. This may be the case on other
+ ;; Lisp implementations as well.
+ #+(or sbcl allegro) (mod (expt n exponent) modulus)
+ #-(or sbcl allegro)
+ (if (some (complement #'integerp) (list n exponent modulus))
+ (mod (expt n exponent) modulus)
+ (loop with result = 1
+ for i of-type fixnum from 0 below (integer-length exponent)
+ for sqr = n then (mod (* sqr sqr) modulus)
+ when (logbitp i exponent) do
+ (setf result (mod (* result sqr) modulus))
+ finally (return result))))
+
+;; If the compiler is going to expand compiler macros, we should
+;; directly inline the simple expansion; this lets the compiler do all
+;; sorts of fancy optimizations based on type information that
+;; wouldn't be used to optimize the normal EXPT-MOD function.
+#+(or sbcl allegro)
+(define-compiler-macro expt-mod (n exponent modulus)
+ `(mod (expt ,n ,exponent) ,modulus))
+
+
+;; Here's some benchmarking code that may be useful. I probably
+;; completely wasted my time declaring ITERATIONS to be a fixnum.
+#+nil
+(defun test (&optional (iterations 50000000))
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1))
+ (fixnum iterations))
+ (time (loop repeat iterations do (mod (expt 12 34) 235)))
+ (time (loop repeat iterations do (expt-mod 12 34 235))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/extremum.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/extremum.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,167 @@
+(in-package :cl-utilities)
+
+(define-condition no-extremum (error) ()
+ (:report "Cannot find extremum of empty sequence")
+ (:documentation "Raised when EXTREMUM is called on an empty
+sequence, since there is no morally smallest element"))
+
+(defun comparator (test &optional (key #'identity))
+ "Comparison operator: auxilliary function used by EXTREMUM"
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (lambda (a b) (if (funcall test
+ (funcall key a)
+ (funcall key b))
+ a
+ b)))
+
+;; This optimizes the case where KEY is #'identity
+(define-compiler-macro comparator (&whole whole test
+ &optional (key #'identity))
+ (if (eql key #'identity)
+ `(lambda (a b)
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (if (funcall ,test a b) a b))
+ whole))
+
+;; The normal way of testing the if length of a proper sequence equals
+;; zero is to just use (zerop (length sequence)). And, while some
+;; implementations may optimize this, it's probably a good idea to
+;; just write an optimized version and use it. This method can speed
+;; up list length testing.
+(defun zero-length-p (sequence)
+ "Is the length of SEQUENCE equal to zero?"
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (or (null sequence)
+ (when (vectorp sequence)
+ (zerop (length sequence)))))
+
+(declaim (inline zero-length-p))
+
+;; Checks the length of the subsequence of SEQUENCE specified by START
+;; and END, and if it's 0 then a NO-EXTREMUM error is signalled. This
+;; should only be used in EXTREMUM functions.
+(defmacro with-check-length ((sequence start end) &body body)
+ (once-only (sequence start end)
+ `(if (or (zero-length-p ,sequence)
+ (>= ,start (or ,end (length ,sequence))))
+ (restart-case (error 'no-extremum)
+ (continue ()
+ :report "Return NIL instead"
+ nil))
+ (progn , at body))))
+
+;; This is an extended version which takes START and END keyword
+;; arguments. Any spec-compliant use of EXTREMUM will also work with
+;; this extended version.
+(defun extremum (sequence predicate
+ &key (key #'identity) (start 0) end)
+ "Returns the element of SEQUENCE that would appear first if the
+sequence were ordered according to SORT using PREDICATE and KEY using
+an unstable sorting algorithm. See http://www.cliki.net/EXTREMUM for
+the full specification."
+ (with-check-length (sequence start end)
+ (reduce (comparator predicate key) sequence
+ :start start :end end)))
+
+;; This optimizes the case where KEY is #'identity
+(define-compiler-macro extremum (&whole whole sequence predicate
+ &key (key #'identity) (start 0) end)
+ (if (eql key #'identity)
+ (once-only (sequence predicate start end)
+ `(with-check-length (,sequence ,start ,end)
+ (locally (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (reduce (comparator ,predicate) ,sequence
+ :start ,start :end ,end))))
+ whole))
+
+;; This is an "optimized" version which calls KEY less. REDUCE is
+;; already so optimized that this will actually be slower unless KEY
+;; is expensive. And on CLISP, of course, the regular version will be
+;; much faster since built-in functions are ridiculously faster than
+;; ones implemented in Lisp. Be warned, this isn't as carefully tested
+;; as regular EXTREMUM and there's more that could go wrong.
+(defun extremum-fastkey (sequence predicate
+ &key (key #'identity) (start 0) end)
+ "EXTREMUM implemented so that it calls KEY less. This is only faster
+if the KEY function is so slow that calling it less often would be a
+significant improvement; ordinarily it's slower."
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ (with-check-length (sequence start end)
+ (let* ((smallest (elt sequence 0))
+ (smallest-key (funcall key smallest))
+ (current-index 0)
+ (real-end (or end (1- most-positive-fixnum))))
+ (declare (type (integer 0) current-index real-end start)
+ (fixnum current-index real-end start))
+ (map nil #'(lambda (x)
+ (when (<= start current-index real-end)
+ (let ((x-key (funcall key x)))
+ (when (funcall predicate
+ x-key
+ smallest-key)
+ (setf smallest x)
+ (setf smallest-key x-key))))
+ (incf current-index))
+ sequence)
+ smallest)))
+
+;; EXTREMA and N-MOST-EXTREME are based on code and ideas from Tobias
+;; C. Rittweiler. They deal with the cases in which you are not
+;; looking for a single extreme element, but for the extreme identical
+;; elements or the N most extreme elements.
+
+(defun extrema (sequence predicate &key (key #'identity) (start 0) end)
+ (with-check-length (sequence start end)
+ (let* ((sequence (subseq sequence start end))
+ (smallest-elements (list (elt sequence 0)))
+ (smallest-key (funcall key (elt smallest-elements 0))))
+ (map nil
+ #'(lambda (x)
+ (let ((x-key (funcall key x)))
+ (cond ((funcall predicate x-key smallest-key)
+ (setq smallest-elements (list x))
+ (setq smallest-key x-key))
+ ;; both elements are considered equal if the predicate
+ ;; returns false for (PRED A B) and (PRED B A)
+ ((not (funcall predicate smallest-key x-key))
+ (push x smallest-elements)))))
+ (subseq sequence 1))
+ ;; We use NREVERSE to make this stable (in the sorting algorithm
+ ;; sense of the word 'stable').
+ (nreverse smallest-elements))))
+
+
+
+(define-condition n-most-extreme-not-enough-elements (warning)
+ ((n :initarg :n :reader n-most-extreme-not-enough-elements-n
+ :documentation "The number of elements that need to be returned")
+ (subsequence :initarg :subsequence
+ :reader n-most-extreme-not-enough-elements-subsequence
+ :documentation "The subsequence from which elements
+must be taken. This is determined by the sequence and the :start and
+:end arguments to N-MOST-EXTREME."))
+ (:report (lambda (condition stream)
+ (with-slots (n subsequence) condition
+ (format stream "There are not enough elements in the sequence ~S~% to return the ~D most extreme elements"
+ subsequence n))))
+ (:documentation "There are not enough elements in the sequence given
+to N-MOST-EXTREME to return the N most extreme elements."))
+
+(defun n-most-extreme (n sequence predicate &key (key #'identity) (start 0) end)
+ "Returns a list of the N elements of SEQUENCE that would appear
+first if the sequence were ordered according to SORT using PREDICATE
+and KEY with a stable sorting algorithm. If there are less than N
+elements in the relevant part of the sequence, this will return all
+the elements it can and signal the warning
+N-MOST-EXTREME-NOT-ENOUGH-ELEMENTS"
+ (check-type n (integer 0))
+ (with-check-length (sequence start end)
+ ;; This is faster on vectors than on lists.
+ (let ((sequence (subseq sequence start end)))
+ (if (> n (length sequence))
+ (progn
+ (warn 'n-most-extreme-not-enough-elements
+ :n n :subsequence sequence)
+ (stable-sort (copy-seq sequence) predicate :key key))
+ (subseq (stable-sort (copy-seq sequence) predicate :key key)
+ 0 n)))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/once-only.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/once-only.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,31 @@
+;; The ONCE-ONLY macro is hard to explain, hard to understand, hard to
+;; write, hard to modify, and hard to live without once you figure out
+;; how to use it. It's used in macros to guard against multiple
+;; evaluation of arguments. My version is longer than most, but it
+;; does some error checking and it gives gensym'd variables more
+;; meaningful names than usual.
+
+(in-package :cl-utilities)
+
+(defun %check-once-only-names (names)
+ "Check that all of the NAMES are symbols. If not, raise an error."
+ ;; This only raises an error for the first non-symbol argument
+ ;; found. While this won't report multiple errors, it is probably
+ ;; more convenient to only report one.
+ (let ((bad-name (find-if-not #'symbolp names)))
+ (when bad-name
+ (error "ONCE-ONLY expected a symbol but got ~S" bad-name))))
+
+(defmacro once-only (names &body body)
+ ;; Check the NAMES list for validity.
+ (%check-once-only-names names)
+ ;; Do not touch this code unless you really know what you're doing.
+ (let ((gensyms (loop for name in names collect (gensym (string name)))))
+ `(let (,@(loop for g in gensyms
+ for name in names
+ collect `(,g (gensym ,(string name)))))
+ `(let (,,@(loop for g in gensyms for n in names
+ collect ``(,,g ,,n)))
+ ,(let (,@(loop for n in names for g in gensyms
+ collect `(,n ,g)))
+ , at body)))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/package.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/package.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,49 @@
+(defpackage :cl-utilities
+ (:use :common-lisp)
+ (:export #:split-sequence
+ #:split-sequence-if
+ #:split-sequence-if-not
+ #:partition
+ #:partition-if
+ #:partition-if-not
+
+ #:extremum
+ #:no-extremum
+ #:extremum-fastkey
+ #:extrema
+ #:n-most-extreme
+ #:n-most-extreme-not-enough-elements
+ #:n-most-extreme-not-enough-elements-n
+ #:n-most-extreme-not-enough-elements-subsequence
+
+ #:read-delimited
+ #:read-delimited-bounds-error
+ #:read-delimited-bounds-error-start
+ #:read-delimited-bounds-error-end
+ #:read-delimited-bounds-error-sequence
+
+ #:expt-mod
+
+ #:collecting
+ #:collect
+ #:with-collectors
+
+ #:with-unique-names
+ #:with-gensyms
+ #:list-binding-not-supported
+ #:list-binding-not-supported-binding
+
+ #:once-only
+
+ #:rotate-byte
+
+ #:copy-array
+
+ #:compose))
+
+#+split-sequence-deprecated
+(defpackage :split-sequence
+ (:documentation "This package mimics SPLIT-SEQUENCE for compatibility with
+packages that expect that system.")
+ (:use :cl-utilities)
+ (:export #:split-sequence #:split-sequence-if #:split-sequence-if-not))
Added: dependencies/trunk/cl-utilities-1.2.4/package.sh
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/package.sh Tue Jan 26 15:20:07 2010
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+mkdir cl-utilities-1.2.4
+mkdir cl-utilities-1.2.4/doc
+cp cl-utilities.asd package.sh collecting.lisp split-sequence.lisp expt-mod.lisp package.lisp compose.lisp extremum.lisp read-delimited.lisp test.lisp copy-array.lisp once-only.lisp rotate-byte.lisp with-unique-names.lisp README cl-utilities-1.2.4/
+cp doc/collecting.html doc/expt-mod.html doc/read-delimited.html doc/with-unique-names.html doc/compose.html doc/extremum.html doc/rotate-byte.html doc/copy-array.html doc/index.html doc/split-sequence.html doc/once-only.html doc/style.css cl-utilities-1.2.4/doc/
+
+rm -f cl-utilities-latest.tar.gz cl-utilities-latest.tar.gz.asc
+
+tar -czvf cl-utilities-1.2.4.tar.gz cl-utilities-1.2.4/
+ln -s ~/hacking/lisp/cl-utilities/cl-utilities-1.2.4.tar.gz ~/hacking/lisp/cl-utilities/cl-utilities-latest.tar.gz
+gpg -b -a ~/hacking/lisp/cl-utilities/cl-utilities-1.2.4.tar.gz
+ln -s ~/hacking/lisp/cl-utilities/cl-utilities-1.2.4.tar.gz.asc ~/hacking/lisp/cl-utilities/cl-utilities-latest.tar.gz.asc
+rm -Rf cl-utilities-1.2.4/
+
+scp cl-utilities-1.2.4.tar.gz pscott at common-lisp.net:/project/cl-utilities/public_html/cl-utilities-1.2.4.tar.gz
+scp cl-utilities-1.2.4.tar.gz.asc pscott at common-lisp.net:/project/cl-utilities/public_html/cl-utilities-1.2.4.tar.gz.asc
+scp cl-utilities-latest.tar.gz pscott at common-lisp.net:/project/cl-utilities/ftp/cl-utilities-1.2.4.tar.gz
+scp cl-utilities-latest.tar.gz.asc pscott at common-lisp.net:/project/cl-utilities/ftp/cl-utilities-1.2.4.tar.gz.asc
+scp cl-utilities-latest.tar.gz pscott at common-lisp.net:/project/cl-utilities/public_html/cl-utilities-latest.tar.gz
+scp cl-utilities-latest.tar.gz.asc pscott at common-lisp.net:/project/cl-utilities/public_html/cl-utilities-latest.tar.gz.asc
Added: dependencies/trunk/cl-utilities-1.2.4/read-delimited.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/read-delimited.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,78 @@
+(in-package :cl-utilities)
+
+(defun read-delimited (sequence stream &key (start 0) end
+ (delimiter #\Newline) (test #'eql) (key #'identity))
+ ;; Check bounds on SEQUENCE
+ (multiple-value-setq (start end)
+ (%read-delimited-bounds-check sequence start end))
+ ;; Loop until we run out of input characters or places to put them,
+ ;; or until we encounter the delimiter.
+ (loop for index from start
+ for char = (read-char stream nil nil)
+ for test-result = (funcall test (funcall key char) delimiter)
+ while (and char
+ (< index end)
+ (not test-result))
+ do (setf (elt sequence index) char)
+ finally (return-from read-delimited
+ (values index test-result))))
+
+;; Conditions
+;;;;;;;;;;;;;
+
+(define-condition read-delimited-bounds-error (error)
+ ((start :initarg :start :reader read-delimited-bounds-error-start)
+ (end :initarg :end :reader read-delimited-bounds-error-end)
+ (sequence :initarg :sequence :reader read-delimited-bounds-error-sequence))
+ (:report (lambda (condition stream)
+ (with-slots (start end sequence) condition
+ (format stream "The bounding indices ~S and ~S are bad for a sequence of length ~S"
+ start end (length sequence)))))
+ (:documentation "There's a problem with the indices START and END
+for SEQUENCE. See CLHS SUBSEQ-OUT-OF-BOUNDS:IS-AN-ERROR issue."))
+
+;; Error checking for bounds
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun %read-delimited-bounds-check (sequence start end)
+ "Check to make sure START and END are in bounds when calling
+READ-DELIMITED with SEQUENCE"
+ (check-type start (or integer null))
+ (check-type end (or integer null))
+ (let ((start (%read-delimited-bounds-check-start sequence start end))
+ (end (%read-delimited-bounds-check-end sequence start end)))
+ ;; Returns (values start end)
+ (%read-delimited-bounds-check-order sequence start end)))
+
+(defun %read-delimited-bounds-check-order (sequence start end)
+ "Check the order of START and END bounds, and return them in the
+correct order."
+ (when (< end start)
+ (restart-case (error 'read-delimited-bounds-error
+ :start start :end end :sequence sequence)
+ (continue ()
+ :report "Switch start and end"
+ (rotatef start end))))
+ (values start end))
+
+(defun %read-delimited-bounds-check-start (sequence start end)
+ "Check to make sure START is in bounds when calling READ-DELIMITED
+with SEQUENCE"
+ (when (and start (< start 0))
+ (restart-case (error 'read-delimited-bounds-error
+ :start start :end end :sequence sequence)
+ (continue ()
+ :report "Use default for START instead"
+ (setf start 0))))
+ start)
+
+(defun %read-delimited-bounds-check-end (sequence start end)
+ "Check to make sure END is in bounds when calling READ-DELIMITED
+with SEQUENCE"
+ (when (and end (> end (length sequence)))
+ (restart-case (error 'read-delimited-bounds-error
+ :start start :end end :sequence sequence)
+ (continue ()
+ :report "Use default for END instead"
+ (setf end nil))))
+ (or end (length sequence)))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/rotate-byte.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/rotate-byte.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,29 @@
+(in-package :cl-utilities)
+
+(defun rotate-byte (count bytespec integer)
+ "Rotates a field of bits within INTEGER; specifically, returns an
+integer that contains the bits of INTEGER rotated COUNT times
+leftwards within the byte specified by BYTESPEC, and elsewhere
+contains the bits of INTEGER. See http://www.cliki.net/ROTATE-BYTE"
+ (declare (optimize (speed 3) (safety 0) (space 0) (debug 1)))
+ #-sbcl
+ (let ((size (byte-size bytespec)))
+ (when (= size 0)
+ (return-from rotate-byte integer))
+ (let ((count (mod count size)))
+ (labels ((rotate-byte-from-0 (count size integer)
+ (let ((bytespec (byte size 0)))
+ (if (> count 0)
+ (logior (ldb bytespec (ash integer count))
+ (ldb bytespec (ash integer (- count size))))
+ (logior (ldb bytespec (ash integer count))
+ (ldb bytespec (ash integer (+ count size))))))))
+ (dpb (rotate-byte-from-0 count size (ldb bytespec integer))
+ bytespec
+ integer))))
+ ;; On SBCL, we use the SB-ROTATE-BYTE extension.
+ #+sbcl-uses-sb-rotate-byte (sb-rotate-byte:rotate-byte count bytespec integer))
+
+;; If we're using the SB-ROTATE-BYTE extension, we should inline our
+;; call and let SBCL handle optimization from there.
+#+sbcl-uses-sb-rotate-byte (declaim (inline rotate-byte))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/split-sequence.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/split-sequence.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,244 @@
+;;;; SPLIT-SEQUENCE
+;;;
+;;; This code was based on Arthur Lemmens' in
+;;; <URL:http://groups.google.com/groups?as_umsgid=39F36F1A.B8F19D20%40simplex.nl>;
+;;;
+;;; changes include:
+;;;
+;;; * altering the behaviour of the :from-end keyword argument to
+;;; return the subsequences in original order, for consistency with
+;;; CL:REMOVE, CL:SUBSTITUTE et al. (:from-end being non-NIL only
+;;; affects the answer if :count is less than the number of
+;;; subsequences, by analogy with the above-referenced functions).
+;;;
+;;; * changing the :maximum keyword argument to :count, by analogy
+;;; with CL:REMOVE, CL:SUBSTITUTE, and so on.
+;;;
+;;; * naming the function SPLIT-SEQUENCE rather than PARTITION rather
+;;; than SPLIT.
+;;;
+;;; * adding SPLIT-SEQUENCE-IF and SPLIT-SEQUENCE-IF-NOT.
+;;;
+;;; * The second return value is now an index rather than a copy of a
+;;; portion of the sequence; this index is the `right' one to feed to
+;;; CL:SUBSEQ for continued processing.
+
+;;; There's a certain amount of code duplication here, which is kept
+;;; to illustrate the relationship between the SPLIT-SEQUENCE
+;;; functions and the CL:POSITION functions.
+
+;;; Examples:
+;;;
+;;; * (split-sequence #\; "a;;b;c")
+;;; -> ("a" "" "b" "c"), 6
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t)
+;;; -> ("a" "" "b" "c"), 0
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t :count 1)
+;;; -> ("c"), 4
+;;;
+;;; * (split-sequence #\; "a;;b;c" :remove-empty-subseqs t)
+;;; -> ("a" "b" "c"), 6
+;;;
+;;; * (split-sequence-if (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("" "" "r" "c" "d" "" "r" ""), 11
+;;;
+;;; * (split-sequence-if-not (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("ab" "a" "a" "ab" "a"), 11
+;;;
+;;; * (split-sequence #\; ";oo;bar;ba;" :start 1 :end 9)
+;;; -> ("oo" "bar" "b"), 9
+
+;; cl-utilities note: the license of this file is unclear, and I don't
+;; even know whom to contact to clarify it. If anybody objects to my
+;; assumption that it is public domain, please contact me so I can do
+;; something about it. Previously I required the split-sequence
+ ; package as a dependency, but that was so unwieldy that it was *the*
+;; sore spot sticking out in the design of cl-utilities. -Peter Scott
+
+(in-package :cl-utilities)
+
+(defun split-sequence (delimiter seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (test nil test-supplied) (test-not nil test-not-supplied) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by delimiter.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE. In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+ (let ((len (length seq))
+ (other-keys (nconc (when test-supplied
+ (list :test test))
+ (when test-not-supplied
+ (list :test-not test-not))
+ (when key-supplied
+ (list :key key)))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position delimiter seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position delimiter seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+(defun split-sequence-if (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by items satisfying
+predicate.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE-IF. In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+ (let ((len (length seq))
+ (other-keys (when key-supplied
+ (list :key key))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position-if predicate seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position-if predicate seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+(defun split-sequence-if-not (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+ "Return a list of subsequences in seq delimited by items satisfying
+(CL:COMPLEMENT predicate).
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded. All other keywords
+work analogously to those for CL:SUBSTITUTE-IF-NOT. In particular,
+the behaviour of :from-end is possibly different from other versions
+of this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped." ; Emacs syntax highlighting is broken, and this helps: "
+ (let ((len (length seq))
+ (other-keys (when key-supplied
+ (list :key key))))
+ (unless end (setq end len))
+ (if from-end
+ (loop for right = end then left
+ for left = (max (or (apply #'position-if-not predicate seq
+ :end right
+ :from-end t
+ other-keys)
+ -1)
+ (1- start))
+ unless (and (= right (1+ left))
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values (nreverse subseqs) right)
+ else
+ collect (subseq seq (1+ left) right) into subseqs
+ and sum 1 into nr-elts
+ until (< left start)
+ finally (return (values (nreverse subseqs) (1+ left))))
+ (loop for left = start then (+ right 1)
+ for right = (min (or (apply #'position-if-not predicate seq
+ :start left
+ other-keys)
+ len)
+ end)
+ unless (and (= right left)
+ remove-empty-subseqs) ; empty subseq we don't want
+ if (and count (>= nr-elts count))
+ ;; We can't take any more. Return now.
+ return (values subseqs left)
+ else
+ collect (subseq seq left right) into subseqs
+ and sum 1 into nr-elts
+ until (>= right end)
+ finally (return (values subseqs right))))))
+
+;;; clean deprecation
+
+(defun partition (&rest args)
+ (apply #'split-sequence args))
+
+(defun partition-if (&rest args)
+ (apply #'split-sequence-if args))
+
+(defun partition-if-not (&rest args)
+ (apply #'split-sequence-if-not args))
+
+(define-compiler-macro partition (&whole form &rest args)
+ (declare (ignore args))
+ (warn "PARTITION is deprecated; use SPLIT-SEQUENCE instead.")
+ form)
+
+(define-compiler-macro partition-if (&whole form &rest args)
+ (declare (ignore args))
+ (warn "PARTITION-IF is deprecated; use SPLIT-SEQUENCE-IF instead.")
+ form)
+
+(define-compiler-macro partition-if-not (&whole form &rest args)
+ (declare (ignore args))
+ (warn "PARTITION-IF-NOT is deprecated; use SPLIT-SEQUENCE-IF-NOT instead")
+ form)
+
+(pushnew :split-sequence *features*)
Added: dependencies/trunk/cl-utilities-1.2.4/test.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/test.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,178 @@
+;; This file requires the FiveAM unit testing framework.
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (asdf:oos 'asdf:load-op :fiveam)
+ (asdf:oos 'asdf:load-op :cl-utilities))
+
+;; To run all the tests:
+;; (5am:run! 'cl-utilities-tests::cl-utilities-suite)
+
+(defpackage :cl-utilities-tests
+ (:use :common-lisp :cl-utilities :5am))
+
+(in-package :cl-utilities-tests)
+
+(def-suite cl-utilities-suite :description "Test suite for cl-utilities")
+(in-suite cl-utilities-suite)
+
+;; These tests were taken directly from the comments at the top of
+;; split-sequence.lisp
+(test split-sequence
+ (is (tree-equal (values (split-sequence #\; "a;;b;c"))
+ '("a" "" "b" "c") :test #'equal))
+ (is (tree-equal (values (split-sequence #\; "a;;b;c" :from-end t))
+ '("a" "" "b" "c") :test #'equal))
+ (is (tree-equal (values (split-sequence #\; "a;;b;c" :from-end t :count 1))
+ '("c") :test #'equal))
+ (is (tree-equal (values (split-sequence #\; "a;;b;c" :remove-empty-subseqs t))
+ '("a" "b" "c") :test #'equal))
+ (is (tree-equal (values (split-sequence-if (lambda (x)
+ (member x '(#\a #\b)))
+ "abracadabra"))
+ '("" "" "r" "c" "d" "" "r" "") :test #'equal))
+ (is (tree-equal (values (split-sequence-if-not (lambda (x)
+ (member x '(#\a #\b)))
+ "abracadabra"))
+ '("ab" "a" "a" "ab" "a") :test #'equal))
+ (is (tree-equal (values (split-sequence #\; ";oo;bar;ba;" :start 1 :end 9))
+ '("oo" "bar" "b") :test #'equal)))
+
+(test extremum
+ (is (= (extremum '(1 23 3 4 5 0) #'< :start 1 :end 4) 3))
+ (signals no-extremum (extremum '() #'<))
+ (is-false (handler-bind ((no-extremum #'continue))
+ (extremum '() #'<)))
+ (is (= (extremum '(2/3 2 3 4) #'> :key (lambda (x) (/ 1 x))) 2/3))
+ (is (= (locally (declare (optimize (speed 3) (safety 0)))
+ (extremum #(1 23 3 4 5 0) #'>))
+ 23))
+ (is (= (extremum-fastkey '(2/3 2 3 4) #'> :key (lambda (x) (/ 1 x))) 2/3)))
+
+(test extrema
+ (is (tree-equal (extrema '(3 2 1 1 2 1) #'<)
+ '(1 1 1)))
+ (is (tree-equal (extrema #(3 2 1 1 2 1) #'<)
+ '(1 1 1)))
+ (is (tree-equal (extrema #(3 2 1 1 2 1) #'< :end 4)
+ '(1 1)))
+ (is (tree-equal (extrema '(3 2 1 1 2 1) #'< :end 4)
+ '(1 1)))
+ (is (tree-equal (extrema #(3 2 1 1 2 1) #'< :start 3 :end 4)
+ '(1)))
+ (is (tree-equal (extrema '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr)
+ '((B . 1) (D . 1)))))
+
+(defmacro quietly (&body body)
+ "Perform BODY quietly, muffling any warnings that may arise"
+ `(handler-bind ((warning #'muffle-warning))
+ , at body))
+
+(test n-most-extreme
+ (is (tree-equal (n-most-extreme 1 '(3 1 2 1) #'>)
+ '(3)))
+ (is (tree-equal (n-most-extreme 2 '(3 1 2 1) #'>)
+ '(3 2)))
+ (is (tree-equal (n-most-extreme 2 '(3 1 2 1) #'<)
+ '(1 1)))
+ (is (tree-equal (n-most-extreme 1 '((A . 3) (B . 1) (C . 2) (D . 1)) #'> :key #'cdr)
+ '((A . 3))))
+ (is (tree-equal (n-most-extreme 2 '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr)
+ '((B . 1) (D . 1))))
+ (is (tree-equal (quietly (n-most-extreme 20 '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr))
+ '((B . 1) (D . 1) (C . 2) (A . 3))))
+ (is (tree-equal (quietly (n-most-extreme 2 '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr :start 1 :end 2))
+ '((B . 1))))
+ (signals n-most-extreme-not-enough-elements (n-most-extreme 2 '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr :start 1 :end 2)))
+
+(defun delimited-test (&key (delimiter #\|) (start 0) end
+ (string "foogo|ogreogrjejgierjijri|bar|baz"))
+ (with-input-from-string (str string)
+ (let ((buffer (copy-seq " ")))
+ (multiple-value-bind (position delimited-p)
+ (read-delimited buffer str
+ :delimiter delimiter :start start :end end)
+ (declare (ignore delimited-p))
+ (subseq buffer 0 position)))))
+
+(test read-delimited
+ (is (string= (delimited-test) "foogo"))
+ (is (string= (delimited-test :delimiter #\t) "foogo|ogreog"))
+ (is (string= (delimited-test :delimiter #\t :start 3) " foogo|ogr"))
+ (is (string= (delimited-test :start 3) " foogo"))
+ (is (string= (delimited-test :end 3) "foo"))
+ (is (string= (delimited-test :start 1 :end 3) " fo"))
+ (is (string= (delimited-test :string "Hello") "Hello"))
+ (is (string= (delimited-test :string "Hello" :start 3) " Hello"))
+ (is (string= (handler-bind ((read-delimited-bounds-error #'continue))
+ (delimited-test :start 3 :end 1))
+ " fo"))
+ (signals type-error (delimited-test :start 3/2))
+ (signals read-delimited-bounds-error (delimited-test :start -3))
+ (signals read-delimited-bounds-error (delimited-test :end 30))
+ (signals read-delimited-bounds-error (delimited-test :start 3 :end 1)))
+
+;; Random testing would probably work better here.
+(test expt-mod
+ (is (= (expt-mod 2 34 54) (mod (expt 2 34) 54)))
+ (is (= (expt-mod 20 3 54) (mod (expt 20 3) 54)))
+ (is (= (expt-mod 2.5 3.8 34.9) (mod (expt 2.5 3.8) 34.9)))
+ (is (= (expt-mod 2/5 3/8 34/9) (mod (expt 2/5 3/8) 34/9))))
+
+(test collecting
+ (is (tree-equal (collecting (dotimes (x 10) (collect x)))
+ '(0 1 2 3 4 5 6 7 8 9)))
+ (is (tree-equal (collecting
+ (labels ((collect-it (x) (collect x)))
+ (mapcar #'collect-it (reverse '(c b a)))))
+ '(a b c)))
+ (is (tree-equal (multiple-value-bind (a b)
+ (with-collectors (x y)
+ (x 1)
+ (y 2)
+ (x 3))
+ (append a b))
+ '(1 3 2))))
+
+(test with-unique-names
+ (is (equalp (subseq (with-unique-names (foo)
+ (string foo))
+ 0 3)
+ "foo"))
+ (is (equalp (subseq (with-unique-names ((foo "bar"))
+ (string foo))
+ 0 3)
+ "bar"))
+ (is (equalp (subseq (with-unique-names ((foo baz))
+ (string foo))
+ 0 3)
+ "baz"))
+ (is (equalp (subseq (with-unique-names ((foo #\y))
+ (string foo))
+ 0 1)
+ "y"))
+ (is (equalp (subseq (with-gensyms (foo)
+ (string foo))
+ 0 3)
+ "foo")))
+
+;; Taken from spec
+(test rotate-byte
+ (is (= (rotate-byte 3 (byte 32 0) 3) 24))
+ (is (= (rotate-byte 3 (byte 5 5) 3) 3))
+ (is (= (rotate-byte 6 (byte 8 0) -3) -129)))
+
+(test copy-array
+ (let ((test-array (make-array '(10 10) :initial-element 5)))
+ (is (not (eq (copy-array test-array) test-array)))
+ (is (equalp (copy-array test-array) test-array))))
+
+(test compose
+ (labels ((2* (x) (* 2 x)))
+ (is (= (funcall (compose #'1+ #'1+) 1) 3))
+ (is (= (funcall (compose '1+ #'2*) 5) 11))
+ (is (= (funcall (compose #'1+ #'2* '1+) 6) 15))
+ ;; This should signal an undefined function error, since we're
+ ;; using '2* rather than #'2*, which means that COMPOSE will use
+ ;; the dynamic binding at the time it is called rather than the
+ ;; lexical binding here.
+ (signals undefined-function
+ (= (funcall (compose #'1+ '2* '1+) 6) 15))))
\ No newline at end of file
Added: dependencies/trunk/cl-utilities-1.2.4/with-unique-names.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/cl-utilities-1.2.4/with-unique-names.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,46 @@
+(in-package :cl-utilities)
+
+;; Defined at http://www.cliki.net/WITH-UNIQUE-NAMES
+
+(defmacro with-unique-names ((&rest bindings) &body body)
+ "Executes a series of forms with each var bound to a fresh,
+uninterned symbol. See http://www.cliki.net/WITH-UNIQUE-NAMES"
+ `(let ,(mapcar #'(lambda (binding)
+ (multiple-value-bind (var prefix)
+ (%with-unique-names-binding-parts binding)
+ (check-type var symbol)
+ `(,var (gensym ,(format nil "~A"
+ (or prefix var))))))
+ bindings)
+ , at body))
+
+(defun %with-unique-names-binding-parts (binding)
+ "Return (values var prefix) from a WITH-UNIQUE-NAMES binding
+form. If PREFIX is not given in the binding, NIL is returned to
+indicate that the default should be used."
+ (if (consp binding)
+ (values (first binding) (second binding))
+ (values binding nil)))
+
+(define-condition list-binding-not-supported (warning)
+ ((binding :initarg :binding :reader list-binding-not-supported-binding))
+ (:report (lambda (condition stream)
+ (format stream "List binding ~S not supported by WITH-GENSYMS.
+It will work, but you should use WITH-UNIQUE-NAMES instead."
+ (list-binding-not-supported-binding condition))))
+ (:documentation "List bindings aren't supported by WITH-GENSYMS, and
+if you want to use them you should use WITH-UNIQUE-NAMES instead. That
+said, they will work; they'll just signal this warning to complain
+about it."))
+
+
+(defmacro with-gensyms ((&rest bindings) &body body)
+ "Synonym for WITH-UNIQUE-NAMES, but BINDINGS should only consist of
+atoms; lists are not supported. If you try to give list bindings, a
+LIST-BINDING-NOT-SUPPORTED warning will be signalled, but it will work
+the same way as WITH-UNIQUE-NAMES. Don't do it, though."
+ ;; Signal a warning for each list binding, if there are any
+ (dolist (binding (remove-if-not #'listp bindings))
+ (warn 'list-binding-not-supported :binding binding))
+ ;; Otherwise, this is a synonym for WITH-UNIQUE-NAMES
+ `(with-unique-names ,bindings , at body))
\ No newline at end of file
Added: dependencies/trunk/commons-logging.jar
==============================================================================
Binary file. No diff available.
Added: dependencies/trunk/miglayout-3.7.1.jar
==============================================================================
Binary file. No diff available.
Added: dependencies/trunk/named-readtables/LICENSE
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/LICENSE Tue Jan 26 15:20:07 2010
@@ -0,0 +1,36 @@
+
+Copyright (c) 2007 - 2009 Tobias C. Rittweiler <tcr at freebits.de>
+Copyright (c) 2007, Robert P. Goldman <rpgoldman at sift.info> and SIFT, LLC
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the names of Tobias C. Rittweiler, Robert P. Goldman,
+ SIFT, LLC nor the names of its contributors may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY Tobias C. Rittweiler, Robert
+P. Goldman and SIFT, LLC ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL Tobias C. Rittweiler, Robert
+P. Goldman or SIFT, LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Added: dependencies/trunk/named-readtables/cruft.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/cruft.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,375 @@
+;;;;
+;;;; Copyright (c) 2008 - 2009 Tobias C. Rittweiler <tcr at freebits.de>
+;;;;
+;;;; All rights reserved.
+;;;;
+;;;; See LICENSE for details.
+;;;;
+
+(in-package :editor-hints.named-readtables)
+
+(defmacro define-cruft (name lambda-list &body (docstring . alternatives))
+ (assert (typep docstring 'string) (docstring) "Docstring missing!")
+ (assert (not (null alternatives)))
+ `(progn
+ (declaim (inline ,name))
+ (defun ,name ,lambda-list ,docstring ,(first alternatives))))
+
+(eval-when (:compile-toplevel :execute)
+ #+sbcl (when (find-symbol "ASSERT-NOT-STANDARD-READTABLE"
+ (find-package "SB-IMPL"))
+ (pushnew :sbcl+safe-standard-readtable *features*)))
+
+
+;;;;; Implementation-dependent cruft
+
+;;;; Mapping between a readtable object and its readtable-name.
+
+(defvar *readtable-names* (make-hash-table :test 'eq))
+
+(define-cruft %associate-readtable-with-name (name readtable)
+ "Associate READTABLE with NAME for READTABLE-NAME to work."
+ #+ :common-lisp (setf (gethash readtable *readtable-names*) name))
+
+(define-cruft %unassociate-readtable-from-name (name readtable)
+ "Remove the association between READTABLE and NAME."
+ #+ :common-lisp (progn (assert (eq name (gethash readtable *readtable-names*)))
+ (remhash readtable *readtable-names*)))
+
+(define-cruft %readtable-name (readtable)
+ "Return the name associated with READTABLE."
+ #+ :common-lisp (values (gethash readtable *readtable-names*)))
+
+(define-cruft %list-all-readtable-names ()
+ "Return a list of all available readtable names."
+ #+ :common-lisp (list* :standard :current
+ (loop for name being each hash-value of *readtable-names*
+ collect name)))
+
+
+;;;; Mapping between a readtable-name and the actual readtable object.
+
+;;; On Allegro we reuse their named-readtable support so we work
+;;; nicely on their infrastructure.
+
+#-allegro
+(defvar *named-readtables* (make-hash-table :test 'eq))
+
+#+allegro
+(defun readtable-name-for-allegro (symbol)
+ (multiple-value-bind (kwd status)
+ (if (keywordp symbol)
+ (values symbol nil)
+ ;; Kludge: ACL uses keywords to name readtables, we allow
+ ;; arbitrary symbols.
+ (intern (format nil "~A.~A"
+ (package-name (symbol-package symbol))
+ (symbol-name symbol))
+ :keyword))
+ (prog1 kwd
+ (assert (or (not status) (get kwd 'named-readtable-designator)))
+ (setf (get kwd 'named-readtable-designator) t))))
+
+(define-cruft %associate-name-with-readtable (name readtable)
+ "Associate NAME with READTABLE for FIND-READTABLE to work."
+ #+ :allegro (setf (excl:named-readtable (readtable-name-for-allegro name)) readtable)
+ #+ :common-lisp (setf (gethash name *named-readtables*) readtable))
+
+(define-cruft %unassociate-name-from-readtable (name readtable)
+ "Remove the association between NAME and READTABLE"
+ #+ :allegro (let ((n (readtable-name-for-allegro name)))
+ (assert (eq readtable (excl:named-readtable n)))
+ (setf (excl:named-readtable n) nil))
+ #+ :common-lisp (progn (assert (eq readtable (gethash name *named-readtables*)))
+ (remhash name *named-readtables*)))
+
+(define-cruft %find-readtable (name)
+ "Return the readtable named NAME."
+ #+ :allegro (excl:named-readtable (readtable-name-for-allegro name))
+ #+ :common-lisp (values (gethash name *named-readtables* nil)))
+
+
+;;;; Reader-macro related predicates
+
+;;; CLISP creates new function objects for standard reader macros on
+;;; each readtable copy.
+(define-cruft function= (fn1 fn2)
+ "Are reader-macro function-designators FN1 and FN2 the same?"
+ #+ :clisp
+ (let* ((fn1 (ensure-function fn1))
+ (fn2 (ensure-function fn2))
+ (n1 (system::function-name fn1))
+ (n2 (system::function-name fn2)))
+ (if (and (eq n1 :lambda) (eq n2 :lambda))
+ (eq fn1 fn2)
+ (equal n1 n2)))
+ #+ :common-lisp
+ (eq (ensure-function fn1) (ensure-function fn2)))
+
+;;; CCL has a bug that prevents the portable form below from working
+;;; (Ticket 601). CLISP will incorrectly fold the call to G-D-M-C away
+;;; if not declared inline.
+(define-cruft dispatch-macro-char-p (char rt)
+ "Is CHAR a dispatch macro character in RT?"
+ #+ :ccl
+ (let ((def (cdr (nth-value 1 (ccl::%get-readtable-char char rt)))))
+ (or (consp (cdr def))
+ (eq (car def) #'ccl::read-dispatch)))
+ #+ :common-lisp
+ (handler-case (locally
+ #+clisp (declare (notinline get-dispatch-macro-character))
+ (get-dispatch-macro-character char #\x rt)
+ t)
+ (error () nil)))
+
+;; (defun macro-char-p (char rt)
+;; (let ((reader-fn (%get-macro-character char rt)))
+;; (and reader-fn t)))
+
+;; (defun standard-macro-char-p (char rt)
+;; (multiple-value-bind (rt-fn rt-flag) (get-macro-character char rt)
+;; (multiple-value-bind (std-fn std-flag) (get-macro-character char *standard-readtable*)
+;; (and (eq rt-fn std-fn)
+;; (eq rt-flag std-flag)))))
+
+;; (defun standard-dispatch-macro-char-p (disp-char sub-char rt)
+;; (flet ((non-terminating-p (ch rt) (nth-value 1 (get-macro-character ch rt))))
+;; (and (eq (non-terminating-p disp-char rt)
+;; (non-terminating-p disp-char *standard-readtable*))
+;; (eq (get-dispatch-macro-character disp-char sub-char rt)
+;; (get-dispatch-macro-character disp-char sub-char *standard-readtable*)))))
+
+
+;;;; Readtables Iterators
+
+(defmacro with-readtable-iterator ((name readtable) &body body)
+ (let ((it (gensym)))
+ `(let ((,it (%make-readtable-iterator ,readtable)))
+ (macrolet ((,name () `(funcall ,',it)))
+ , at body))))
+
+#+sbcl
+(defun %make-readtable-iterator (readtable)
+ (let ((char-macro-array (sb-impl::character-macro-array readtable))
+ (char-macro-ht (sb-impl::character-macro-hash-table readtable))
+ (dispatch-tables (sb-impl::dispatch-tables readtable))
+ (char-code 0))
+ (with-hash-table-iterator (ht-iterator char-macro-ht)
+ (labels ((grovel-base-chars ()
+ (declare (optimize sb-c::merge-tail-calls))
+ (if (>= char-code sb-int:base-char-code-limit)
+ (grovel-unicode-chars)
+ (let ((reader-fn (svref char-macro-array char-code))
+ (char (code-char (shiftf char-code (1+ char-code)))))
+ (if reader-fn
+ (yield char reader-fn)
+ (grovel-base-chars)))))
+ (grovel-unicode-chars ()
+ (multiple-value-bind (more? char reader-fn) (ht-iterator)
+ (if (not more?)
+ (values nil nil nil nil nil)
+ (yield char reader-fn))))
+ (yield (char reader-fn)
+ (let ((disp-ht))
+ (cond
+ ((setq disp-ht (cdr (assoc char dispatch-tables)))
+ (let* ((disp-fn (get-macro-character char readtable))
+ (sub-char-alist))
+ (maphash (lambda (k v)
+ (push (cons k v) sub-char-alist))
+ disp-ht)
+ (values t char disp-fn t sub-char-alist)))
+ (t
+ (values t char reader-fn nil nil))))))
+ #'grovel-base-chars))))
+
+#+clozure
+(defun %make-readtable-iterator (readtable)
+ (let ((char-macro-alist (ccl::rdtab.alist readtable)))
+ (lambda ()
+ (if char-macro-alist
+ (destructuring-bind (char . defn) (pop char-macro-alist)
+ (if (consp defn)
+ (values t char (car defn) t (cdr defn))
+ (values t char defn nil nil)))
+ (values nil nil nil nil nil)))))
+
+;;; Written on ACL 8.0.
+#+allegro
+(defun %make-readtable-iterator (readtable)
+ (declare (optimize speed)) ; for TCO
+ (check-type readtable readtable)
+ (let* ((macro-table (first (excl::readtable-macro-table readtable)))
+ (dispatch-tables (excl::readtable-dispatch-tables readtable))
+ (table-length (length macro-table))
+ (idx 0))
+ (labels ((grovel-macro-chars ()
+ (if (>= idx table-length)
+ (grovel-dispatch-chars)
+ (let ((read-fn (svref macro-table idx))
+ (oidx idx))
+ (incf idx)
+ (if (or (eq read-fn #'excl::read-token)
+ (eq read-fn #'excl::read-dispatch-char)
+ (eq read-fn #'excl::undefined-macro-char))
+ (grovel-macro-chars)
+ (values t (code-char oidx) read-fn nil nil)))))
+ (grovel-dispatch-chars ()
+ (if (null dispatch-tables)
+ (values nil nil nil nil nil)
+ (destructuring-bind (disp-char sub-char-table)
+ (first dispatch-tables)
+ (setf dispatch-tables (rest dispatch-tables))
+ ;;; Kludge. We can't fully clear dispatch tables
+ ;;; in %CLEAR-READTABLE.
+ (when (eq (svref macro-table (char-code disp-char))
+ #'excl::read-dispatch-char)
+ (values t
+ disp-char
+ (svref macro-table (char-code disp-char))
+ t
+ (loop for subch-fn across sub-char-table
+ for subch-code from 0
+ when subch-fn
+ collect (cons (code-char subch-code)
+ subch-fn))))))))
+ #'grovel-macro-chars)))
+
+
+#-(or sbcl clozure allegro)
+(eval-when (:compile-toplevel)
+ (let ((*print-pretty* t))
+ (simple-style-warn
+ "~&~@< ~@;~A has not been ported to ~A. ~
+ We fall back to a portable implementation of readtable iterators. ~
+ This implementation has to grovel through all available characters. ~
+ On Unicode-aware implementations this may come with some costs.~@:>"
+ (package-name '#.*package*) (lisp-implementation-type))))
+
+#-(or sbcl clozure allegro)
+(defun %make-readtable-iterator (readtable)
+ (check-type readtable readtable)
+ (let ((char-code 0))
+ #'(lambda ()
+ (prog ()
+ :GROVEL
+ (when (< char-code char-code-limit)
+ (let* ((char (code-char char-code))
+ (fn (get-macro-character char readtable)))
+ (incf char-code)
+ (when (not fn) (go :GROVEL))
+ (multiple-value-bind (disp? alist)
+ (handler-case ; grovel dispatch macro characters.
+ (values t
+ ;; Only grovel upper case characters to
+ ;; avoid duplicates.
+ (loop for code from 0 below char-code-limit
+ for subchar = (let ((ch (code-char code)))
+ (when (or (not (alpha-char-p ch))
+ (upper-case-p ch))
+ ch))
+ for disp-fn = (and subchar
+ (get-dispatch-macro-character
+ char subchar readtable))
+ when disp-fn
+ collect (cons subchar disp-fn)))
+ (error () nil))
+ (return (values t char fn disp? alist)))))))))
+
+(defmacro do-readtable ((entry-designator readtable &optional result)
+ &body body)
+ "Iterate through a readtable's macro characters, and dispatch macro characters."
+ (destructuring-bind (char &optional reader-fn non-terminating-p disp? table)
+ (if (symbolp entry-designator)
+ (list entry-designator)
+ entry-designator)
+ (let ((iter (gensym "ITER+"))
+ (more? (gensym "MORE?+"))
+ (rt (gensym "READTABLE+")))
+ `(let ((,rt ,readtable))
+ (with-readtable-iterator (,iter ,rt)
+ (loop
+ (multiple-value-bind (,more?
+ ,char
+ ,@(when reader-fn (list reader-fn))
+ ,@(when disp? (list disp?))
+ ,@(when table (list table)))
+ (,iter)
+ (unless ,more? (return ,result))
+ (let ,(when non-terminating-p
+ ;; FIXME: N-T-P should be incorporated in iterators.
+ `((,non-terminating-p
+ (nth-value 1 (get-macro-character ,char ,rt)))))
+ , at body))))))))
+
+;;;; Misc
+
+;;; This should return an implementation's actual standard readtable
+;;; object only if the implementation makes the effort to guard against
+;;; modification of that object. Otherwise it should better return a
+;;; copy.
+(define-cruft %standard-readtable ()
+ "Return the standard readtable."
+ #+ :sbcl+safe-standard-readtable sb-impl::*standard-readtable*
+ #+ :common-lisp (copy-readtable nil))
+
+;;; On SBCL, SET-SYNTAX-FROM-CHAR does not get rid of a
+;;; readtable's dispatch table properly.
+;;; Same goes for Allegro but that does not seem to provide a
+;;; setter for their readtable's dispatch tables. Hence this ugly
+;;; workaround.
+(define-cruft %clear-readtable (readtable)
+ "Make all macro characters in READTABLE be constituents."
+ #+ :sbcl
+ (prog1 readtable
+ (do-readtable (char readtable)
+ (set-syntax-from-char char #\A readtable))
+ (setf (sb-impl::dispatch-tables readtable) nil))
+ #+ :allegro
+ (prog1 readtable
+ (do-readtable (char readtable)
+ (set-syntax-from-char char #\A readtable))
+ (let ((dispatch-tables (excl::readtable-dispatch-tables readtable)))
+ (setf (cdr dispatch-tables) nil)
+ (setf (caar dispatch-tables) #\Backspace)
+ (setf (cadar dispatch-tables) (fill (cadar dispatch-tables) nil))))
+ #+ :common-lisp
+ (do-readtable (char readtable readtable)
+ (set-syntax-from-char char #\A readtable)))
+
+;;; See Clozure Trac Ticket 601. This is supposed to be removed at
+;;; some point in the future.
+(define-cruft %get-dispatch-macro-character (char subchar rt)
+ "Ensure ANSI behaviour for GET-DISPATCH-MACRO-CHARACTER."
+ #+ :ccl (ignore-errors
+ (get-dispatch-macro-character char subchar rt))
+ #+ :common-lisp (get-dispatch-macro-character char subchar rt))
+
+;;; Allegro stores READ-TOKEN as reader macro function of each
+;;; constituent character.
+(define-cruft %get-macro-character (char rt)
+ "Ensure ANSI behaviour for GET-MACRO-CHARACTER."
+ #+ :allegro (let ((fn (get-macro-character char rt)))
+ (cond ((not fn) nil)
+ ((function= fn #'excl::read-token) nil)
+ (t fn)))
+ #+ :common-lisp (get-macro-character char rt))
+
+
+;;;; Specialized PRINT-OBJECT for named readtables.
+
+;;; As per #19 in CLHS 11.1.2.1.2 defining a method for PRINT-OBJECT
+;;; that specializes on READTABLE is actually forbidden. It's quite
+;;; likely to work (modulo package-locks) on most implementations,
+;;; though.
+
+;;; We don't need this on Allegro CL's as we hook into their
+;;; named-readtable facility, and they provide such a method already.
+#-allegro
+(without-package-lock (:common-lisp)
+ (defmethod print-object :around ((rt readtable) stream)
+ (let ((name (readtable-name rt)))
+ (if name
+ (print-unreadable-object (rt stream :type nil :identity t)
+ (format stream "~A ~S" :named-readtable name))
+ (call-next-method)))))
\ No newline at end of file
Added: dependencies/trunk/named-readtables/define-api.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/define-api.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,63 @@
+
+(in-package :named-readtables)
+
+(defmacro define-api (name lambda-list type-list &body body)
+ (flet ((parse-type-list (type-list)
+ (let ((pos (position '=> type-list)))
+ (assert pos () "You forgot to specify return type (`=>' missing.)")
+ (values (subseq type-list 0 pos)
+ `(values ,@(nthcdr (1+ pos) type-list) &optional)))))
+ (multiple-value-bind (body decls docstring)
+ (parse-body body :documentation t :whole `(define-api ,name))
+ (multiple-value-bind (arg-typespec value-typespec)
+ (parse-type-list type-list)
+ (multiple-value-bind (reqs opts rest keys)
+ (parse-ordinary-lambda-list lambda-list)
+ (declare (ignorable reqs opts rest keys))
+ `(progn
+ (declaim (ftype (function ,arg-typespec ,value-typespec) ,name))
+ (locally
+ ;;; Muffle the annoying "&OPTIONAL and &KEY found in
+ ;;; the same lambda list" style-warning
+ #+sbcl (declare (sb-ext:muffle-conditions style-warning))
+ (defun ,name ,lambda-list
+ ,docstring
+
+ #+sbcl (declare (sb-ext:unmuffle-conditions style-warning))
+
+ , at decls
+
+ ;; SBCL will interpret the ftype declaration as
+ ;; assertion and will insert type checks for us.
+ #-sbcl
+ (progn
+ ;; CHECK-TYPE required parameters
+ ,@(loop for req-arg in reqs
+ for req-type = (pop type-list)
+ do (assert req-type)
+ collect `(check-type ,req-arg ,req-type))
+
+ ;; CHECK-TYPE optional parameters
+ ,@(loop initially (assert (or (null opts)
+ (eq (pop type-list) '&optional)))
+ for (opt-arg . nil) in opts
+ for opt-type = (pop type-list)
+ do (assert opt-type)
+ collect `(check-type ,opt-arg ,opt-type))
+
+ ;; CHECK-TYPE rest parameter
+ ,@(when rest
+ (assert (eq (pop type-list) '&rest))
+ (let ((rest-type (pop type-list)))
+ (assert rest-type)
+ `((dolist (x ,rest)
+ (check-type x ,rest-type)))))
+
+ ;; CHECK-TYPE key parameters
+ ,@(loop initially (assert (or (null keys)
+ (eq (pop type-list) '&key)))
+ for ((keyword key-arg) . nil) in keys
+ for (nil key-type) = (find keyword type-list :key #'car)
+ collect `(check-type ,key-arg ,key-type)))
+
+ , at body))))))))
\ No newline at end of file
Added: dependencies/trunk/named-readtables/doc/named-readtables.html
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/doc/named-readtables.html Tue Jan 26 15:20:07 2010
@@ -0,0 +1,463 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>EDITOR-HINTS.NAMED-READTABLES</title>
+ <style type="text/css">
+ pre { padding:5px; background-color:#e0e0e0 }
+ h3, h4 { text-decoration: underline; }
+ a { text-decoration: none; padding: 1px 2px 1px 2px; }
+ a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+ a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; }
+ a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+ a.none { text-decoration: none; padding: 0; }
+ a.none:visited { text-decoration: none; padding: 0; }
+ a.none:hover { text-decoration: none; border: none; padding: 0; }
+ a.none:focus { text-decoration: none; border: none; padding: 0; }
+ a.noborder { text-decoration: none; padding: 0; }
+ a.noborder:visited { text-decoration: none; padding: 0; }
+ a.noborder:hover { text-decoration: none; border: none; padding: 0; }
+ a.noborder:focus { text-decoration: none; border: none; padding: 0; }
+ pre.none { padding:5px; background-color:#ffffff }
+ </style>
+</head>
+
+<body bgcolor=white>
+
+<h2> EDITOR-HINTS.NAMED-READTABLES</h2>
+
+<h5> by Tobias C Rittweiler </h5>
+
+<font color=red>Download:</font> <br> <br>
+
+
+ <code>darcs get http://common-lisp.net/~trittweiler/darcs/editor-hints/named-readtables/</code> (to be changed)
+
+<br> <br><h3><a class=none name="contents">Contents</a></h3>
+<ol>
+ <li> <a href="#what_are_named-readtables?">What are Named-Readtables?</a>
+ <li> <a href="#notes_on_the_api">Notes on the API</a>
+ <li> <a href="#important_api_idiosyncrasies">Important API idiosyncrasies</a>
+ <li> <a href="#preregistered_readtables">Preregistered Readtables</a>
+ <li> <a href="#examples">Examples</a>
+ <li> <a href="#acknowledgements">Acknowledgements</a>
+
+
+ <li><a href="#dictionary">Dictionary</a>
+ <ol>
+ <li><a href="#COPY-NAMED-READTABLE"><code>COPY-NAMED-READTABLE</code></a>
+ <li><a href="#DEFREADTABLE"><code>DEFREADTABLE</code></a>
+ <li><a href="#ENSURE-READTABLE"><code>ENSURE-READTABLE</code></a>
+ <li><a href="#FIND-READTABLE"><code>FIND-READTABLE</code></a>
+ <li><a href="#IN-READTABLE"><code>IN-READTABLE</code></a>
+ <li><a href="#LIST-ALL-NAMED-READTABLES"><code>LIST-ALL-NAMED-READTABLES</code></a>
+ <li><a href="#MAKE-READTABLE"><code>MAKE-READTABLE</code></a>
+ <li><a href="#MERGE-READTABLES-INTO"><code>MERGE-READTABLES-INTO</code></a>
+ <li><a href="#NAMED-READTABLE-DESIGNATOR"><code>NAMED-READTABLE-DESIGNATOR</code></a>
+ <li><a href="#READER-MACRO-CONFLICT"><code>READER-MACRO-CONFLICT</code></a>
+ <li><a href="#READTABLE-DOES-ALREADY-EXIST"><code>READTABLE-DOES-ALREADY-EXIST</code></a>
+ <li><a href="#READTABLE-DOES-NOT-EXIST"><code>READTABLE-DOES-NOT-EXIST</code></a>
+ <li><a href="#READTABLE-NAME"><code>READTABLE-NAME</code></a>
+ <li><a href="#REGISTER-READTABLE"><code>REGISTER-READTABLE</code></a>
+ <li><a href="#RENAME-READTABLE"><code>RENAME-READTABLE</code></a>
+ <li><a href="#UNREGISTER-READTABLE"><code>UNREGISTER-READTABLE</code></a>
+
+ </ol>
+</ol> <br> <br><h3><a class=none name="what_are_named-readtables?">What are Named-Readtables?</a></h3>
+ Named-Readtables is a library that provides a namespace for readtables akin to the <br> already-existing namespace of packages. In particular:
+<ul>
+ <li>you can associate readtables with names, and retrieve readtables by names;</li>
+ <li>you can associate source files with readtable names, and be sure that the <br> right readtable is active when compiling/loading the file;</li>
+ <li>similiarly, your development environment now has a chance to automatically <br> determine what readtable should be active while processing source forms on <br> interactive commands. (E.g. think of `C-c C-c' in Slime [yet to be done])</li>
+</ul>
+ Additionally, it also attempts to become a facility for using readtables in a <br> <u>modular</u> way. In particular:
+<ul>
+ <li>it provides a macro to specify the content of a readtable at a glance;</li>
+ <li>it makes it possible to use multiple inheritance between readtables.</li>
+</ul>
+<br> <br><h3><a class=none name="notes_on_the_api">Notes on the API</a></h3>
+ The <code>API</code> heavily imitates the <code>API</code> of packages. This has the nice property that any <br> experienced Common Lisper will take it up without effort.
+<br><br>
+ <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/m_defpkg.htm"><code>DEFPACKAGE</code></a></code>
+<br><br>
+ <code><a href="#In-Readtable"><code>IN-READTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/m_in_pkg.htm"><code>IN-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Merge-Readtables-Into"><code>MERGE-READTABLES-INTO</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_use_pk.htm"><code>USE-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Make-Readtable"><code>MAKE-READTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_mk_pkg.htm"><code>MAKE-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Unregister-Readtable"><code>UNREGISTER-READTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_del_pk.htm"><code>DELETE-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Rename-Readtable"><code>RENAME-READTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_rn_pkg.htm"><code>RENAME-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Find-Readtable"><code>FIND-READTABLE</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_find_p.htm"><code>FIND-PACKAGE</code></a></code>
+<br><br>
+ <code><a href="#Readtable-Name"><code>READTABLE-NAME</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_pkg_na.htm"><code>PACKAGE-NAME</code></a></code>
+<br><br>
+ <code><a href="#List-All-Named-Readtables"><code>LIST-ALL-NAMED-READTABLES</code></a></code> - <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_list_a.htm"><code>LIST-ALL-PACKAGES</code></a></code>
+<br> <br><h3><a class=none name="important_api_idiosyncrasies">Important API idiosyncrasies</a></h3>
+ There are three major differences between the <code>API</code> of Named-Readtables, and the <code>API</code> <br> of packages.
+<br><br>
+ <code>1.</code> Readtable names are symbols not strings.
+<br><br>
+ Time has shown that the fact that packages are named by strings causes severe <br> headache because of the potential of package names colliding with each other.
+<br><br>
+ Hence, readtables are named by symbols lest to make the situation worse than it <br> already is. Consequently, readtables named <code>CL-ORACLE:SQL-SYNTAX</code> and <br> <code>CL-MYSQL:SQL-SYNTAX</code> can happily coexist next to each other. Or, taken to an extreme, <br> <code>SCHEME:SYNTAX</code> and <code>ELISP:SYNTAX.</code>
+<br><br>
+ If, for example to duly signify the importance of your cool readtable hack, you <br> really think it deserves a global name, you can always resort to keywords.
+<br><br>
+ <code>2.</code> The inheritance is resolved statically, not dynamically.
+<br><br>
+ A package that uses another package will have access to all the other <br> package's exported symbols, even to those that will be added after its <br> definition. I.e. the inheritance is resolved at run-time, that is dynamically.
+<br><br>
+ Unfortunately, we cannot do the same for readtables in a portable manner.
+<br><br>
+ Therefore, we do not talk about "using" another readtable but about <br> "merging" the other readtable's definition into the readtable we are <br> going to define. I.e. the inheritance is resolved once at definition time, that is <br> statically.
+<br><br>
+ (Such merging can more or less be implemented portably albeit at a certain cost. <br> Most of the time, this cost manifests itself at the time a readtable is defined, <br> i.e. once at compile-time, so it may not bother you. Nonetheless, we provide extra <br> support for Sbcl, ClozureCL, and AllegroCL at the moment. Patches for your <br> implementation of choice are welcome, of course.)
+<br><br>
+ <code>3.</code> <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> does not have compile-time effects.
+<br><br>
+ If you define a package via <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/m_defpkg.htm"><code>DEFPACKAGE</code></a>,</code> you can make that package the currently <br> active package for the subsequent compilation of the same file via <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/m_in_pkg.htm"><code>IN-PACKAGE</code></a>.</code> The <br> same is, however, not true for <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> and <code><a href="#In-Readtable"><code>IN-READTABLE</code></a></code> for the following <br> reason:
+<br><br>
+ It's unlikely that the need for special reader-macros arises for a problem <br> which can be solved in just one file. Most often, you're going to define the <br> reader macro functions, and set up the corresponding readtable in an extra file.
+<br><br>
+ If <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> had compile-time effects, you'd have to wrap each definition <br> of a reader-macro function in an <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/s_eval_w.htm"><code>EVAL-WHEN</code></a></code> to make its definition available at <br> compile-time. Because that's simply not the common case, <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> does not <br> have a compile-time effect.
+<br><br>
+ If you want to use a readtable within the same file as its definition, wrap the <br> <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> and the reader-macro function definitions in an explicit <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/s_eval_w.htm"><code>EVAL-WHEN</code></a>.</code>
+<br> <br><h3><a class=none name="preregistered_readtables">Preregistered Readtables</a></h3>
+ - <code>NIL,</code> <code>:STANDARD,</code> and <code>:COMMON-LISP</code> designate the <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#standard_readtable">standard readtable</a></i>.
+<br><br>
+ - <code>:MODERN</code> designates a <u>case-preserving</u> <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#standard-readtable">standard-readtable</a></i>.
+<br><br>
+ - <code>:CURRENT</code> designates the <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#current_readtable">current readtable</a></i>.
+<br> <br><h3><a class=none name="examples">Examples</a></h3>
+<pre>
+ (defreadtable elisp:syntax
+ (:merge :standard)
+ (:macro-char #\? #'elisp::read-character-literal t)
+ (:macro-char #\[ #'elisp::read-vector-literal t)
+ ...
+ (:case :preserve))
+
+ (defreadtable scheme:syntax
+ (:merge :standard)
+ (:macro-char #\[ #'(lambda (stream char)
+ (read-delimited-list #\] stream)))
+ (:macro-char #\# :dispatch)
+ (:dispatch-macro-char #\# #\t #'scheme::read-#t)
+ (:dispatch-macro-char #\# #\f #'scheme::read-#f)
+ ...
+ (:case :preserve))
+
+ (in-readtable elisp:syntax)
+
+ ...
+
+ (in-readtable scheme:syntax)
+
+ ...
+</pre>
+
+<br> <br><h3><a class=none name="acknowledgements">Acknowledgements</a></h3>
+ Thanks to Robert Goldman for making me want to write this library.
+<br><br>
+ Thanks to Stephen Compall, Ariel Badichi, David Lichteblau, Bart Botta, David <br> Crawford, and Pascal Costanza for being early adopters, providing comments and <br> bugfixes.
+<br> <br>
+<br> <br><h3><a class=none name="dictionary">Dictionary</a></h3>
+
+
+<!-- Entry for COPY-NAMED-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='COPY-NAMED-READTABLE'><b>copy-named-readtable</b> <i>named-readtable</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>named-readtable</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Like <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_cp_rdt.htm"><code>COPY-READTABLE</code></a></code> but takes a <code><a href="#Named-Readtable-Designator"><code>NAMED-READTABLE-DESIGNATOR</code></a></code> as argument.
+
+
+</blockquote>
+
+<!-- End of entry for COPY-NAMED-READTABLE -->
+
+
+<!-- Entry for DEFREADTABLE -->
+
+<p><br>[Macro]<br><a class=none name='DEFREADTABLE'><b>defreadtable</b> <i>name &body options</i> => <i>result</i></a><br><br> Description:
+<blockquote>
+
+Define a new named readtable, whose name is given by the symbol <i>name</i>. Or, if <br> a readtable is already registered under that name, redefine that one.
+<br><br>
+The readtable can be populated using the following <i>options</i>:
+<br><br>
+ <code>(:MERGE</code> <i>readtable-designators</i>+)
+<br><br>
+ Merge the readtables designated into the new readtable being defined as per <br> <code><a href="#Merge-Readtables-Into"><code>MERGE-READTABLES-INTO</code></a>.</code>
+<br><br>
+ If no <code>:MERGE</code> clause is given, an empty readtable is used. See <code><a href="#Make-Readtable"><code>MAKE-READTABLE</code></a>.</code>
+<br><br>
+ <code>(:FUZE</code> <i>readtable-designators</i>+)
+<br><br>
+ Like <code>:MERGE</code> except:
+<br><br>
+ Error conditions of type <code><a href="#Reader-Macro-Conflict"><code>READER-MACRO-CONFLICT</code></a></code> that are signaled during the merge <br> operation will be silently <u>continued</u>. It follows that reader macros in earlier <br> entries will be overwritten by later ones.
+<br><br>
+ <code>(:DISPATCH-MACRO-CHAR</code> <i>macro-char</i> <i>sub-char</i> <i>function</i>)
+<br><br>
+ Define a new sub character <i>sub-char</i> for the dispatching macro character <br> <i>macro-char</i>, per <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_set__1.htm"><code>SET-DISPATCH-MACRO-CHARACTER</code></a>.</code> You probably have to define <br> <i>macro-char</i> as a dispatching macro character by the following option first.
+<br><br>
+ <code>(:MACRO-CHAR</code> <i>macro-char</i> <i>function</i> [<i>non-terminating-p</i>])
+<br><br>
+ Define a new macro character in the readtable, per <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_set_ma.htm"><code>SET-MACRO-CHARACTER</code></a>.</code> If <br> <i>function</i> is the keyword <code>:DISPATCH,</code> <i>macro-char</i> is made a dispatching <br> macro character, per <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_mk_dis.htm"><code>MAKE-DISPATCH-MACRO-CHARACTER</code></a>.</code>
+<br><br>
+ <code>(:SYNTAX-FROM</code> <i>from-readtable-designator</i> <i>from-char</i> <i>to-char</i>)
+<br><br>
+ Set the character syntax of <i>to-char</i> in the readtable being defined to the <br> same syntax as <i>from-char</i> as per <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/f_set_sy.htm"><code>SET-SYNTAX-FROM-CHAR</code></a>.</code>
+<br><br>
+ <code>(:CASE</code> <i>case-mode</i>)
+<br><br>
+ Defines the <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#case_sensitivity_mode">case sensitivity mode</a></i> of the resulting readtable.
+<br><br>
+Any number of option clauses may appear. The options are grouped by their type, but <br> in each group the order the options appeared textually is preserved. The following <br> groups exist and are executed in the following order: <code>:MERGE</code> and <code>:FUZE</code> (one group), <br> <code>:CASE,</code> <code>:MACRO-CHAR</code> and <code>:DISPATCH-MACRO-CHAR</code> (one group), finally <code>:SYNTAX-FROM.</code>
+<br><br>
+Notes:
+<br><br>
+ The readtable is defined at load-time. If you want to have it available at <br> compilation time <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/a__.htm"><code>-</code></a>-</code> say to use its reader-macros in the same file as its definition <br> <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/a__.htm"><code>-</code></a>-</code> you have to wrap the <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> form in an explicit <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/s_eval_w.htm"><code>EVAL-WHEN</code></a>.</code>
+<br><br>
+ On redefinition, the target readtable is made empty first before it's refilled <br> according to the clauses.
+<br><br>
+ <code>NIL,</code> <code>:STANDARD,</code> <code>:COMMON-LISP,</code> <code>:MODERN,</code> and <code>:CURRENT</code> are preregistered readtable <br> names.
+
+
+</blockquote>
+
+<!-- End of entry for DEFREADTABLE -->
+
+
+<!-- Entry for ENSURE-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='ENSURE-READTABLE'><b>ensure-readtable</b> <i>name <tt>&optional</tt> default</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>name</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>default</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Looks up the readtable specified by <i>name</i> and returns it if it's found. <br> If it is not found, it registers the readtable designated by <i>default</i> under <br> the name represented by <i>name</i>; or if no default argument is given, it signals <br> an error of type <code><a href="#Readtable-Does-Not-Exist"><code>READTABLE-DOES-NOT-EXIST</code></a></code> instead.
+
+
+</blockquote>
+
+<!-- End of entry for ENSURE-READTABLE -->
+
+
+<!-- Entry for FIND-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='FIND-READTABLE'><b>find-readtable</b> <i>name</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>name</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>(OR
+ READTABLE
+ NULL)</code></blockquote> Description:
+<blockquote>
+
+Looks for the readtable specified by <i>name</i> and returns it if it is found. <br> Returns <code>NIL</code> otherwise.
+
+
+</blockquote>
+
+<!-- End of entry for FIND-READTABLE -->
+
+
+<!-- Entry for IN-READTABLE -->
+
+<p><br>[Macro]<br><a class=none name='IN-READTABLE'><b>in-readtable</b> <i>name</i> => <i>result</i></a><br><br> Description:
+<blockquote>
+
+Set <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/v_rdtabl.htm"><code>*READTABLE*</code></a></code> to the readtable referred to by the symbol <i>name</i>.
+
+
+</blockquote>
+
+<!-- End of entry for IN-READTABLE -->
+
+
+<!-- Entry for LIST-ALL-NAMED-READTABLES -->
+
+<p><br>[Function]<br><a class=none name='LIST-ALL-NAMED-READTABLES'><b>list-all-named-readtables</b> <i></i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>result</i>: <code>LIST</code></blockquote> Description:
+<blockquote>
+
+Returns a list of all registered readtables. The returned list is guaranteed to be <br> fresh, but may contain duplicates.
+
+
+</blockquote>
+
+<!-- End of entry for LIST-ALL-NAMED-READTABLES -->
+
+
+<!-- Entry for MAKE-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='MAKE-READTABLE'><b>make-readtable</b> <i><tt>&optional</tt> name <tt>&key</tt> merge</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>name</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>merge</i>: <code>LIST</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Creates and returns a new readtable under the specified <i>name</i>.
+<br><br>
+<i>merge</i> takes a list of <code><a href="#Named-Readtable-Designators"><code>NAMED-READTABLE-DESIGNATORS</code></a></code> and specifies the <br> readtables the new readtable is created from. (See the <code>:MERGE</code> clause of <code><a href="#Defreadtable"><code>DEFREADTABLE</code></a></code> <br> for details.)
+<br><br>
+If <i>merge</i> is <code>NIL,</code> an empty readtable is used instead.
+<br><br>
+If <i>name</i> is not given, an anonymous empty readtable is returned.
+<br><br>
+Notes:
+<br><br>
+ An empty readtable is a readtable where each character's syntax is the same as <br> in the <i><a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#standard_readtable">standard readtable</a></i> except that each macro character has been made a <br> constituent. Basically: whitespace stays whitespace, everything else is constituent.
+
+
+</blockquote>
+
+<!-- End of entry for MAKE-READTABLE -->
+
+
+<!-- Entry for MERGE-READTABLES-INTO -->
+
+<p><br>[Function]<br><a class=none name='MERGE-READTABLES-INTO'><b>merge-readtables-into</b> <i>result-readtable <tt>&rest</tt> named-readtables</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>result-readtable</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>named-readtables</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Copy the contents of each readtable in <i>named-readtables</i> into <br> <i>result-table</i>.
+<br><br>
+If a macro character appears in more than one of the readtables, i.e. if a conflict <br> is discovered during the merge, an error of type <code><a href="#Reader-Macro-Conflict"><code>READER-MACRO-CONFLICT</code></a></code> is signaled.
+
+
+</blockquote>
+
+<!-- End of entry for MERGE-READTABLES-INTO -->
+
+
+<!-- Entry for NAMED-READTABLE-DESIGNATOR -->
+
+<p><br>[Type]<br><a class=none name='NAMED-READTABLE-DESIGNATOR'><b>named-readtable-designator</b></a><br><br> Description:
+<blockquote>
+
+Either a symbol or a readtable itself.
+
+
+</blockquote>
+
+<!-- End of entry for NAMED-READTABLE-DESIGNATOR -->
+
+
+<!-- Entry for READER-MACRO-CONFLICT -->
+
+<p><br>[Condition type]<br><a class=none name='READER-MACRO-CONFLICT'><b>reader-macro-conflict</b></a><br><br> Description:
+<blockquote>
+
+Continuable.
+<br><br>
+This condition is signaled during the merge process if a) a reader macro (be it a <br> macro character or the sub character of a dispatch macro character) is both present <br> in the source as well as the target readtable, and b) if and only if the two <br> respective reader macro functions differ.
+
+
+</blockquote>
+
+<!-- End of entry for READER-MACRO-CONFLICT -->
+
+
+<!-- Entry for READTABLE-DOES-ALREADY-EXIST -->
+
+<p><br>[Condition type]<br><a class=none name='READTABLE-DOES-ALREADY-EXIST'><b>readtable-does-already-exist</b></a><br><br> Description:
+<blockquote>
+
+Continuable.
+
+
+</blockquote>
+
+<!-- End of entry for READTABLE-DOES-ALREADY-EXIST -->
+
+
+<!-- Entry for READTABLE-DOES-NOT-EXIST -->
+
+<p><br>[Condition type]<br><a class=none name='READTABLE-DOES-NOT-EXIST'><b>readtable-does-not-exist</b></a><br><br>
+<blockquote>
+
+
+
+</blockquote>
+
+<!-- End of entry for READTABLE-DOES-NOT-EXIST -->
+
+
+<!-- Entry for READTABLE-NAME -->
+
+<p><br>[Function]<br><a class=none name='READTABLE-NAME'><b>readtable-name</b> <i>named-readtable</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>named-readtable</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>SYMBOL</code></blockquote> Description:
+<blockquote>
+
+Returns the name of the readtable designated by <i>named-readtable</i>, or <code>NIL.</code>
+
+
+</blockquote>
+
+<!-- End of entry for READTABLE-NAME -->
+
+
+<!-- Entry for REGISTER-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='REGISTER-READTABLE'><b>register-readtable</b> <i>name readtable</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>name</i>: <code>SYMBOL</code></blockquote><blockquote><i>readtable</i>: <code>READTABLE</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Associate <i>readtable</i> with <i>name</i>. Returns the readtable.
+
+
+</blockquote>
+
+<!-- End of entry for REGISTER-READTABLE -->
+
+
+<!-- Entry for RENAME-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='RENAME-READTABLE'><b>rename-readtable</b> <i>old-name new-name</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>old-name</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>new-name</i>: <code>SYMBOL</code></blockquote><blockquote><i>result</i>: <code>READTABLE</code></blockquote> Description:
+<blockquote>
+
+Replaces the associated name of the readtable designated by <i>old-name</i> with <br> <i>new-name</i>. If a readtable is already registered under <i>new-name</i>, an <br> error of type <code><a href="#Readtable-Does-Already-Exist"><code>READTABLE-DOES-ALREADY-EXIST</code></a></code> is signaled.
+
+
+</blockquote>
+
+<!-- End of entry for RENAME-READTABLE -->
+
+
+<!-- Entry for UNREGISTER-READTABLE -->
+
+<p><br>[Function]<br><a class=none name='UNREGISTER-READTABLE'><b>unregister-readtable</b> <i>named-readtable</i> => <i>result</i></a><br><br> Argument and Values:<blockquote><i>named-readtable</i>: <code>(OR
+ READTABLE
+ SYMBOL)</code></blockquote><blockquote><i>result</i>: <code>(MEMBER T
+ NIL)</code></blockquote> Description:
+<blockquote>
+
+Remove the association of <i>named-readtable</i>. Returns <code><a href="http://www.lispworks.com/reference/HyperSpec/Body/a_t.htm"><code>T</code></a></code> if successfull, <code>NIL</code> <br> otherwise.
+
+
+</blockquote>
+
+<!-- End of entry for UNREGISTER-READTABLE -->
+
+
+<hr>
+<p>
+This documentation was generated on 2009-9-29 from a Lisp image using some home-brewn,
+duct-taped, <br> evolutionary hacked extension of Edi Weitz'
+<a href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>.
+</p>
+
+</body>
+</html>
\ No newline at end of file
Added: dependencies/trunk/named-readtables/named-readtables.asd
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/named-readtables.asd Tue Jan 26 15:20:07 2010
@@ -0,0 +1,50 @@
+;;; -*- Mode:Lisp -*-
+
+(in-package :cl-user)
+
+(defclass asdf::named-readtables-source-file (asdf:cl-source-file) ())
+
+#+sbcl
+(defmethod asdf:perform :around ((o asdf:compile-op)
+ (c asdf::named-readtables-source-file))
+ (let ((sb-ext:*derive-function-types* t))
+ (call-next-method)))
+
+
+(asdf:defsystem :named-readtables
+ :description "Library that creates a namespace for named readtable akin to the namespace of packages."
+ :author "Tobias C. Rittweiler <trittweiler at common-lisp.net>"
+ :version "1.0 (unpublished so far)"
+ :licence "BSD"
+ :default-component-class asdf::named-readtables-source-file
+ :components
+ ((:file "package")
+ (:file "utils" :depends-on ("package"))
+ (:file "define-api" :depends-on ("package" "utils"))
+ (:file "cruft" :depends-on ("package" "utils"))
+ (:file "named-readtables" :depends-on ("package" "utils" "cruft" "define-api"))))
+
+(defmethod asdf:perform ((o asdf:test-op)
+ (c (eql (asdf:find-system :named-readtables))))
+ (asdf:operate 'asdf:load-op :named-readtables-test)
+ (asdf:operate 'asdf:test-op :named-readtables-test))
+
+
+(asdf:defsystem :named-readtables-test
+ :description "Test suite for the Named-Readtables library."
+ :author "Tobias C. Rittweiler <trittweiler at common-lisp.net>"
+ :depends-on (:named-readtables)
+ :components
+ ((:module tests
+ :default-component-class asdf::named-readtables-source-file
+ :serial t
+ :components
+ ((:file "package")
+ (:file "rt" :depends-on ("package"))
+ (:file "tests" :depends-on ("package" "rt"))))))
+
+(defmethod asdf:perform ((o asdf:test-op)
+ (c (eql (asdf:find-system
+ :named-readtables-test))))
+ (let ((*package* (find-package :named-readtables-test)))
+ (funcall (intern (string '#:do-tests) *package*))))
\ No newline at end of file
Added: dependencies/trunk/named-readtables/named-readtables.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/named-readtables.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,527 @@
+;;;; -*- Mode:Lisp -*-
+;;;;
+;;;; Copyright (c) 2007 - 2009 Tobias C. Rittweiler <tcr at freebits.de>
+;;;; Copyright (c) 2007, Robert P. Goldman <rpgoldman at sift.info> and SIFT, LLC
+;;;;
+;;;; All rights reserved.
+;;;;
+;;;; See LICENSE for details.
+;;;;
+
+(in-package :editor-hints.named-readtables)
+
+;;;
+;;; ``This is enough of a foothold to implement a more elaborate
+;;; facility for using readtables in a localized way.''
+;;;
+;;; (X3J13 Cleanup Issue IN-SYNTAX)
+;;;
+
+;;;;;; DEFREADTABLE &c.
+
+(defmacro defreadtable (name &body options)
+ "Define a new named readtable, whose name is given by the symbol `name'.
+Or, if a readtable is already registered under that name, redefine that
+one.
+
+The readtable can be populated using the following `options':
+
+ (:MERGE `readtable-designators'+)
+
+ Merge the readtables designated into the new readtable being defined
+ as per MERGE-READTABLES-INTO.
+
+ If no :MERGE clause is given, an empty readtable is used. See
+ MAKE-READTABLE.
+
+ (:FUZE `readtable-designators'+)
+
+ Like :MERGE except:
+
+ Error conditions of type READER-MACRO-CONFLICT that are signaled
+ during the merge operation will be silently _continued_. It follows
+ that reader macros in earlier entries will be overwritten by later
+ ones.
+
+ (:DISPATCH-MACRO-CHAR `macro-char' `sub-char' `function')
+
+ Define a new sub character `sub-char' for the dispatching macro
+ character `macro-char', per SET-DISPATCH-MACRO-CHARACTER. You
+ probably have to define `macro-char' as a dispatching macro character
+ by the following option first.
+
+ (:MACRO-CHAR `macro-char' `function' [`non-terminating-p'])
+
+ Define a new macro character in the readtable, per SET-MACRO-CHARACTER.
+ If `function' is the keyword :DISPATCH, `macro-char' is made a
+ dispatching macro character, per MAKE-DISPATCH-MACRO-CHARACTER.
+
+ (:SYNTAX-FROM `from-readtable-designator' `from-char' `to-char')
+
+ Set the character syntax of `to-char' in the readtable being defined
+ to the same syntax as `from-char' as per SET-SYNTAX-FROM-CHAR.
+
+ (:CASE `case-mode')
+
+ Defines the /case sensitivity mode/ of the resulting readtable.
+
+Any number of option clauses may appear. The options are grouped by their
+type, but in each group the order the options appeared textually is
+preserved. The following groups exist and are executed in the following
+order: :MERGE and :FUZE (one group), :CASE, :MACRO-CHAR
+and :DISPATCH-MACRO-CHAR (one group), finally :SYNTAX-FROM.
+
+Notes:
+
+ The readtable is defined at load-time. If you want to have it available
+ at compilation time -- say to use its reader-macros in the same file as
+ its definition -- you have to wrap the DEFREADTABLE form in an explicit
+ EVAL-WHEN.
+
+ On redefinition, the target readtable is made empty first before it's
+ refilled according to the clauses.
+
+ NIL, :STANDARD, :COMMON-LISP, :MODERN, and :CURRENT are
+ preregistered readtable names.
+"
+ (check-type name symbol)
+ (when (reserved-readtable-name-p name)
+ (error "~A is the designator for a predefined readtable. ~
+ Not acceptable as a user-specified readtable name." name))
+ (flet ((process-option (option var)
+ (destructure-case option
+ ((:merge &rest readtable-designators)
+ `(merge-readtables-into ,var
+ ,@(mapcar #'(lambda (x) `',x) readtable-designators))) ; quotify
+ ((:fuze &rest readtable-designators)
+ `(handler-bind ((reader-macro-conflict #'continue))
+ (merge-readtables-into ,var
+ ,@(mapcar #'(lambda (x) `',x) readtable-designators))))
+ ((:dispatch-macro-char disp-char sub-char function)
+ `(set-dispatch-macro-character ,disp-char ,sub-char ,function ,var))
+ ((:macro-char char function &optional non-terminating-p)
+ (if (eq function :dispatch)
+ `(make-dispatch-macro-character ,char ,non-terminating-p ,var)
+ `(set-macro-character ,char ,function ,non-terminating-p ,var)))
+ ((:syntax-from from-rt-designator from-char to-char)
+ `(set-syntax-from-char ,to-char ,from-char
+ ,var (find-readtable ,from-rt-designator)))
+ ((:case mode)
+ `(setf (readtable-case ,var) ,mode))))
+ (remove-clauses (clauses options)
+ (setq clauses (if (listp clauses) clauses (list clauses)))
+ (remove-if-not #'(lambda (x) (member x clauses))
+ options :key #'first)))
+ (let* ((merge-clauses (remove-clauses '(:merge :fuze) options))
+ (case-clauses (remove-clauses :case options))
+ (macro-clauses (remove-clauses '(:macro-char :dispatch-macro-char)
+ options))
+ (syntax-clauses (remove-clauses :syntax-from options))
+ (other-clauses (set-difference options
+ (append merge-clauses case-clauses
+ macro-clauses syntax-clauses))))
+ (cond
+ ((not (null other-clauses))
+ (error "Bogus DEFREADTABLE clauses: ~/PPRINT-LINEAR/" other-clauses))
+ (t
+ `(eval-when (:load-toplevel :execute)
+ ;; The (FIND-READTABLE ...) isqrt important for proper
+ ;; redefinition semantics, as redefining has to modify the
+ ;; already existing readtable object.
+ (let ((readtable (find-readtable ',name)))
+ (cond ((not readtable)
+ (setq readtable (make-readtable ',name)))
+ (t
+ (setq readtable (%clear-readtable readtable))
+ (simple-style-warn "Overwriting already existing readtable ~S."
+ readtable)))
+ ,@(loop for option in merge-clauses
+ collect (process-option option 'readtable))
+ ,@(loop for option in case-clauses
+ collect (process-option option 'readtable))
+ ,@(loop for option in macro-clauses
+ collect (process-option option 'readtable))
+ ,@(loop for option in syntax-clauses
+ collect (process-option option 'readtable))
+ readtable)))))))
+
+(defmacro in-readtable (name)
+ "Set *READTABLE* to the readtable referred to by the symbol `name'."
+ (check-type name symbol)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ ;; NB. The :LOAD-TOPLEVEL is needed for cases like (DEFVAR *FOO*
+ ;; (GET-MACRO-CHARACTER #\"))
+ (setf *readtable* (ensure-readtable ',name))
+ (when (find-package :swank)
+ (%frob-swank-readtable-alist *package* *readtable*))
+ ))
+
+;;; KLUDGE: [interim solution]
+;;;
+;;; We need support for this in Slime itself, because we want IN-READTABLE
+;;; to work on a per-file basis, and not on a per-package basis.
+;;;
+(defun %frob-swank-readtable-alist (package readtable)
+ (let ((readtable-alist (find-symbol (string '#:*readtable-alist*)
+ (find-package :swank))))
+ (when (boundp readtable-alist)
+ (pushnew (cons (package-name package) readtable)
+ (symbol-value readtable-alist)
+ :test #'(lambda (entry1 entry2)
+ (destructuring-bind (pkg-name1 . rt1) entry1
+ (destructuring-bind (pkg-name2 . rt2) entry2
+ (and (string= pkg-name1 pkg-name2)
+ (eq rt1 rt2)))))))))
+
+(deftype readtable-designator ()
+ `(or null readtable))
+
+(deftype named-readtable-designator ()
+ "Either a symbol or a readtable itself."
+ `(or readtable-designator symbol))
+
+
+(declaim (special *standard-readtable* *empty-readtable*))
+
+(define-api make-readtable
+ (&optional (name nil name-supplied-p) &key merge)
+ (&optional named-readtable-designator &key (:merge list) => readtable)
+ "Creates and returns a new readtable under the specified `name'.
+
+`merge' takes a list of NAMED-READTABLE-DESIGNATORS and specifies the
+readtables the new readtable is created from. (See the :MERGE clause of
+DEFREADTABLE for details.)
+
+If `merge' is NIL, an empty readtable is used instead.
+
+If `name' is not given, an anonymous empty readtable is returned.
+
+Notes:
+
+ An empty readtable is a readtable where each character's syntax is the
+ same as in the /standard readtable/ except that each macro character has
+ been made a constituent. Basically: whitespace stays whitespace,
+ everything else is constituent."
+ (cond ((not name-supplied-p)
+ (copy-readtable *empty-readtable*))
+ ((reserved-readtable-name-p name)
+ (error "~A is the designator for a predefined readtable. ~
+ Not acceptable as a user-specified readtable name." name))
+ ((let ((rt (find-readtable name)))
+ (and rt (prog1 nil
+ (cerror "Overwrite existing entry."
+ 'readtable-does-already-exist :readtable-name name)
+ ;; Explicitly unregister to make sure that we do not hold on
+ ;; of any reference to RT.
+ (unregister-readtable rt)))))
+ (t (let ((result (apply #'merge-readtables-into
+ ;; The first readtable specified in the :merge list is
+ ;; taken as the basis for all subsequent (destructive!)
+ ;; modifications (and hence it's copied.)
+ (copy-readtable (if merge
+ (ensure-readtable (first merge))
+ *empty-readtable*))
+ (rest merge))))
+
+ (register-readtable name result)))))
+
+(define-api rename-readtable
+ (old-name new-name)
+ (named-readtable-designator symbol => readtable)
+ "Replaces the associated name of the readtable designated by `old-name'
+with `new-name'. If a readtable is already registered under `new-name', an
+error of type READTABLE-DOES-ALREADY-EXIST is signaled."
+ (when (find-readtable new-name)
+ (cerror "Overwrite existing entry."
+ 'readtable-does-already-exist :readtable-name new-name))
+ (let* ((readtable (ensure-readtable old-name))
+ (readtable-name (readtable-name readtable)))
+ ;; We use the internal functions directly to omit repeated
+ ;; type-checking.
+ (%unassociate-name-from-readtable readtable-name readtable)
+ (%unassociate-readtable-from-name readtable-name readtable)
+ (%associate-name-with-readtable new-name readtable)
+ (%associate-readtable-with-name new-name readtable)
+ readtable))
+
+(define-api merge-readtables-into
+ (result-readtable &rest named-readtables)
+ (named-readtable-designator &rest named-readtable-designator => readtable)
+ "Copy the contents of each readtable in `named-readtables' into
+`result-table'.
+
+If a macro character appears in more than one of the readtables, i.e. if a
+conflict is discovered during the merge, an error of type
+READER-MACRO-CONFLICT is signaled."
+ (flet ((merge-into (to from)
+ (do-readtable ((char reader-fn non-terminating-p disp? table) from)
+ (check-reader-macro-conflict from to char)
+ (cond ((not disp?)
+ (set-macro-character char reader-fn non-terminating-p to))
+ (t
+ (ensure-dispatch-macro-character char non-terminating-p to)
+ (loop for (subchar . subfn) in table do
+ (check-reader-macro-conflict from to char subchar)
+ (set-dispatch-macro-character char subchar subfn to)))))
+ to))
+ (let ((result-table (ensure-readtable result-readtable)))
+ (dolist (table (mapcar #'ensure-readtable named-readtables))
+ (merge-into result-table table))
+ result-table)))
+
+(defun ensure-dispatch-macro-character (char &optional non-terminating-p
+ (readtable *readtable*))
+ (if (dispatch-macro-char-p char readtable)
+ t
+ (make-dispatch-macro-character char non-terminating-p readtable)))
+
+(define-api copy-named-readtable
+ (named-readtable)
+ (named-readtable-designator => readtable)
+ "Like COPY-READTABLE but takes a NAMED-READTABLE-DESIGNATOR as argument."
+ (copy-readtable (ensure-readtable named-readtable)))
+
+(define-api list-all-named-readtables () (=> list)
+ "Returns a list of all registered readtables. The returned list is
+guaranteed to be fresh, but may contain duplicates."
+ (mapcar #'ensure-readtable (%list-all-readtable-names)))
+
+
+(define-condition readtable-error (error) ())
+
+(define-condition readtable-does-not-exist (readtable-error)
+ ((readtable-name :initarg :readtable-name
+ :initform (required-argument)
+ :accessor missing-readtable-name
+ :type named-readtable-designator))
+ (:report (lambda (condition stream)
+ (format stream "A readtable named ~S does not exist."
+ (missing-readtable-name condition)))))
+
+(define-condition readtable-does-already-exist (readtable-error)
+ ((readtable-name :initarg :readtable-name
+ :initform (required-argument)
+ :accessor existing-readtable-name
+ :type named-readtable-designator))
+ (:report (lambda (condition stream)
+ (format stream "A readtable named ~S already exists."
+ (existing-readtable-name condition))))
+ (:documentation "Continuable."))
+
+(define-condition reader-macro-conflict (readtable-error)
+ ((macro-char
+ :initarg :macro-char
+ :initform (required-argument)
+ :accessor conflicting-macro-char
+ :type character)
+ (sub-char
+ :initarg :sub-char
+ :initform nil
+ :accessor conflicting-dispatch-sub-char
+ :type (or null character))
+ (from-readtable
+ :initarg :from-readtable
+ :initform (required-argument)
+ :accessor from-readtable
+ :type readtable)
+ (to-readtable
+ :initarg :to-readtable
+ :initform (required-argument)
+ :accessor to-readtable
+ :type readtable))
+ (:report
+ (lambda (condition stream)
+ (format stream "~@<Reader macro conflict while trying to merge the ~
+ ~:[macro character~;dispatch macro characters~] ~
+ ~@C~@[ ~@C~] from ~A into ~A.~@:>"
+ (conflicting-dispatch-sub-char condition)
+ (conflicting-macro-char condition)
+ (conflicting-dispatch-sub-char condition)
+ (from-readtable condition)
+ (to-readtable condition))))
+ (:documentation "Continuable.
+
+This condition is signaled during the merge process if a) a reader macro
+\(be it a macro character or the sub character of a dispatch macro
+character\) is both present in the source as well as the target readtable,
+and b) if and only if the two respective reader macro functions differ."))
+
+(defun check-reader-macro-conflict (from to char &optional subchar)
+ (flet ((conflictp (from-fn to-fn)
+ (assert from-fn) ; if this fails, there's a bug in readtable iterators.
+ (and to-fn (not (function= to-fn from-fn)))))
+ (when (if subchar
+ (conflictp (%get-dispatch-macro-character char subchar from)
+ (%get-dispatch-macro-character char subchar to))
+ (conflictp (%get-macro-character char from)
+ (%get-macro-character char to)))
+ (cerror (format nil "Overwrite ~@C in ~A." char to)
+ 'reader-macro-conflict
+ :from-readtable from
+ :to-readtable to
+ :macro-char char
+ :sub-char subchar))))
+
+
+;;; Although there is no way to get at the standard readtable in
+;;; Common Lisp (cf. /standard readtable/, CLHS glossary), we make
+;;; up the perception of its existence by interning a copy of it.
+;;;
+;;; We do this for reverse lookup (cf. READTABLE-NAME), i.e. for
+;;;
+;;; (equal (readtable-name (find-readtable :standard)) "STANDARD")
+;;;
+;;; holding true.
+;;;
+;;; We, however, inherit the restriction that the :STANDARD
+;;; readtable _must not be modified_ (cf. CLHS 2.1.1.2), although it'd
+;;; technically be feasible (as *STANDARD-READTABLE* will contain a
+;;; mutable copy of the implementation-internal standard readtable.)
+;;; We cannot enforce this restriction without shadowing
+;;; CL:SET-MACRO-CHARACTER and CL:SET-DISPATCH-MACRO-FUNCTION which
+;;; is out of scope of this library, though. So we just threaten
+;;; with nasal demons.
+;;;
+(defvar *standard-readtable*
+ (%standard-readtable))
+
+(defvar *empty-readtable*
+ (%clear-readtable (copy-readtable nil)))
+
+(defvar *case-preserving-standard-readtable*
+ (let ((readtable (copy-readtable nil)))
+ (setf (readtable-case readtable) :preserve)
+ readtable))
+
+(defparameter *reserved-readtable-names*
+ '(nil :standard :common-lisp :modern :current))
+
+(defun reserved-readtable-name-p (name)
+ (and (member name *reserved-readtable-names*) t))
+
+;;; In principle, we could DEFREADTABLE some of these. But we do
+;;; reserved readtable lookup seperately, since we can't register a
+;;; readtable for :CURRENT anyway.
+
+(defun find-reserved-readtable (reserved-name)
+ (cond ((eq reserved-name nil) *standard-readtable*)
+ ((eq reserved-name :standard) *standard-readtable*)
+ ((eq reserved-name :common-lisp) *standard-readtable*)
+ ((eq reserved-name :modern) *case-preserving-standard-readtable*)
+ ((eq reserved-name :current) *readtable*)
+ (t (error "Bug: no such reserved readtable: ~S" reserved-name))))
+
+(define-api find-readtable
+ (name)
+ (named-readtable-designator => (or readtable null))
+ "Looks for the readtable specified by `name' and returns it if it is
+found. Returns NIL otherwise."
+ (cond ((readtablep name) name)
+ ((reserved-readtable-name-p name)
+ (find-reserved-readtable name))
+ ((%find-readtable name))))
+
+;;; FIXME: This doesn't take a NAMED-READTABLE-DESIGNATOR, but only a
+;;; STRING-DESIGNATOR. (When fixing, heed interplay with compiler
+;;; macros below.)
+(defsetf find-readtable register-readtable)
+
+(define-api ensure-readtable
+ (name &optional (default nil default-p))
+ (named-readtable-designator &optional (or named-readtable-designator null)
+ => readtable)
+ "Looks up the readtable specified by `name' and returns it if it's found.
+If it is not found, it registers the readtable designated by `default'
+under the name represented by `name'; or if no default argument is given,
+it signals an error of type READTABLE-DOES-NOT-EXIST instead."
+ (cond ((find-readtable name))
+ ((not default-p)
+ (error 'readtable-does-not-exist :readtable-name name))
+ (t (setf (find-readtable name) (ensure-readtable default)))))
+
+
+(define-api register-readtable
+ (name readtable)
+ (symbol readtable => readtable)
+ "Associate `readtable' with `name'. Returns the readtable."
+ (assert (typep name '(not (satisfies reserved-readtable-name-p))))
+ (%associate-readtable-with-name name readtable)
+ (%associate-name-with-readtable name readtable)
+ readtable)
+
+(define-api unregister-readtable
+ (named-readtable)
+ (named-readtable-designator => boolean)
+ "Remove the association of `named-readtable'. Returns T if successfull,
+NIL otherwise."
+ (let* ((readtable (find-readtable named-readtable))
+ (readtable-name (and readtable (readtable-name readtable))))
+ (if (not readtable-name)
+ nil
+ (prog1 t
+ (check-type readtable-name (not (satisfies reserved-readtable-name-p)))
+ (%unassociate-readtable-from-name readtable-name readtable)
+ (%unassociate-name-from-readtable readtable-name readtable)))))
+
+(define-api readtable-name
+ (named-readtable)
+ (named-readtable-designator => symbol)
+ "Returns the name of the readtable designated by `named-readtable', or
+NIL."
+ (let ((readtable (ensure-readtable named-readtable)))
+ (cond ((%readtable-name readtable))
+ ((eq readtable *readtable*) :current)
+ ((eq readtable *standard-readtable*) :common-lisp)
+ ((eq readtable *case-preserving-standard-readtable*) :modern)
+ (t nil))))
+
+
+;;;;; Compiler macros
+
+;;; Since the :STANDARD readtable is interned, and we can't enforce
+;;; its immutability, we signal a style-warning for suspicious uses
+;;; that may result in strange behaviour:
+
+;;; Modifying the standard readtable would, obviously, lead to a
+;;; propagation of this change to all places which use the :STANDARD
+;;; readtable (and thus rendering this readtable to be non-standard,
+;;; in fact.)
+
+
+(defun constant-standard-readtable-expression-p (thing)
+ (cond ((symbolp thing) (or (eq thing 'nil) (eq thing :standard)))
+ ((consp thing) (some (lambda (x) (equal thing x))
+ '((find-readtable nil)
+ (find-readtable :standard)
+ (ensure-readtable nil)
+ (ensure-readtable :standard))))
+ (t nil)))
+
+(defun signal-suspicious-registration-warning (name-expr readtable-expr)
+ (simple-style-warn
+ "Caution: ~<You're trying to register the :STANDARD readtable ~
+ under a new name ~S. As modification of the :STANDARD readtable ~
+ is not permitted, subsequent modification of ~S won't be ~
+ permitted either. You probably want to wrap COPY-READTABLE ~
+ around~@:>~% ~S"
+ (list name-expr name-expr) readtable-expr))
+
+(let ()
+ ;; Defer to runtime because compiler-macros are made available already
+ ;; at compilation time. So without this two subsequent invocations of
+ ;; COMPILE-FILE on this file would result in an undefined function
+ ;; error because the two above functions are not yet available.
+ ;; (This does not use EVAL-WHEN because of Fig 3.7, CLHS 3.2.3.1;
+ ;; cf. last example in CLHS "EVAL-WHEN" entry.)
+
+ (define-compiler-macro register-readtable (&whole form name readtable)
+ (when (constant-standard-readtable-expression-p readtable)
+ (signal-suspicious-registration-warning name readtable))
+ form)
+
+ (define-compiler-macro ensure-readtable (&whole form name &optional (default nil default-p))
+ (when (and default-p (constant-standard-readtable-expression-p default))
+ (signal-suspicious-registration-warning name default))
+ form))
+
+
Added: dependencies/trunk/named-readtables/package.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/package.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,193 @@
+
+(in-package :common-lisp-user)
+
+(defpackage :editor-hints.named-readtables
+ (:use :common-lisp)
+ (:nicknames :named-readtables)
+ (:export
+ #:defreadtable
+ #:in-readtable
+ #:make-readtable
+ #:merge-readtables-into
+ #:find-readtable
+ #:ensure-readtable
+ #:rename-readtable
+ #:readtable-name
+ #:register-readtable
+ #:unregister-readtable
+ #:copy-named-readtable
+ #:list-all-named-readtables
+ ;; Types
+ #:named-readtable-designator
+ ;; Conditions
+ #:reader-macro-conflict
+ #:readtable-does-already-exist
+ #:readtable-does-not-exist
+ )
+ (:documentation
+ "
+* What are Named-Readtables?
+
+ Named-Readtables is a library that provides a namespace for
+ readtables akin to the already-existing namespace of packages. In
+ particular:
+
+ * you can associate readtables with names, and retrieve
+ readtables by names;
+
+ * you can associate source files with readtable names, and be
+ sure that the right readtable is active when compiling/loading
+ the file;
+
+ * similiarly, your development environment now has a chance to
+ automatically determine what readtable should be active while
+ processing source forms on interactive commands. (E.g. think of
+ `C-c C-c' in Slime [yet to be done])
+
+ It follows that Named-Readtables is a facility for using readtables in
+ a localized way.
+
+ Additionally, it also attempts to become a facility for using
+ readtables in a _modular_ way. In particular:
+
+ * it provides a macro to specify the content of a readtable at a
+ glance;
+
+ * it makes it possible to use multiple inheritance between readtables.
+
+* Notes on the API
+
+ The API heavily imitates the API of packages. This has the nice
+ property that any experienced Common Lisper will take it up without
+ effort.
+
+ DEFREADTABLE - DEFPACKAGE
+
+ IN-READTABLE - IN-PACKAGE
+
+ MERGE-READTABLES-INTO - USE-PACKAGE
+
+ MAKE-READTABLE - MAKE-PACKAGE
+
+ UNREGISTER-READTABLE - DELETE-PACKAGE
+
+ RENAME-READTABLE - RENAME-PACKAGE
+
+ FIND-READTABLE - FIND-PACKAGE
+
+ READTABLE-NAME - PACKAGE-NAME
+
+ LIST-ALL-NAMED-READTABLES - LIST-ALL-PACKAGES
+
+* Important API idiosyncrasies
+
+ There are three major differences between the API of Named-Readtables,
+ and the API of packages.
+
+ 1. Readtable names are symbols not strings.
+
+ Time has shown that the fact that packages are named by
+ strings causes severe headache because of the potential of
+ package names colliding with each other.
+
+ Hence, readtables are named by symbols lest to make the
+ situation worse than it already is. Consequently, readtables
+ named CL-ORACLE:SQL-SYNTAX and CL-MYSQL:SQL-SYNTAX can
+ happily coexist next to each other. Or, taken to an extreme,
+ SCHEME:SYNTAX and ELISP:SYNTAX.
+
+ If, for example to duly signify the importance of your cool
+ readtable hack, you really think it deserves a global name,
+ you can always resort to keywords.
+
+ 2. The inheritance is resolved statically, not dynamically.
+
+ A package that uses another package will have access to all
+ the other package's exported symbols, even to those that will
+ be added after its definition. I.e. the inheritance is
+ resolved at run-time, that is dynamically.
+
+ Unfortunately, we cannot do the same for readtables in a
+ portable manner.
+
+ Therefore, we do not talk about \"using\" another readtable
+ but about \"merging\" the other readtable's definition into
+ the readtable we are going to define. I.e. the inheritance is
+ resolved once at definition time, that is statically.
+
+ (Such merging can more or less be implemented portably albeit
+ at a certain cost. Most of the time, this cost manifests
+ itself at the time a readtable is defined, i.e. once at
+ compile-time, so it may not bother you. Nonetheless, we
+ provide extra support for Sbcl, ClozureCL, and AllegroCL at
+ the moment. Patches for your implementation of choice are
+ welcome, of course.)
+
+ 3. DEFREADTABLE does not have compile-time effects.
+
+ If you define a package via DEFPACKAGE, you can make that
+ package the currently active package for the subsequent
+ compilation of the same file via IN-PACKAGE. The same is,
+ however, not true for DEFREADTABLE and IN-READTABLE for the
+ following reason:
+
+ It's unlikely that the need for special reader-macros arises
+ for a problem which can be solved in just one file. Most
+ often, you're going to define the reader macro functions, and
+ set up the corresponding readtable in an extra file.
+
+ If DEFREADTABLE had compile-time effects, you'd have to wrap
+ each definition of a reader-macro function in an EVAL-WHEN to
+ make its definition available at compile-time. Because that's
+ simply not the common case, DEFREADTABLE does not have a
+ compile-time effect.
+
+ If you want to use a readtable within the same file as its
+ definition, wrap the DEFREADTABLE and the reader-macro
+ function definitions in an explicit EVAL-WHEN.
+
+* Preregistered Readtables
+
+ - NIL, :STANDARD, and :COMMON-LISP designate the /standard readtable/.
+
+ - :MODERN designates a _case-preserving_ /standard-readtable/.
+
+ - :CURRENT designates the /current readtable/.
+
+* Examples
+
+ > (defreadtable elisp:syntax
+ > (:merge :standard)
+ > (:macro-char #\\? #'elisp::read-character-literal t)
+ > (:macro-char #\\[ #'elisp::read-vector-literal t)
+ > ...
+ > (:case :preserve))
+ >
+ > (defreadtable scheme:syntax
+ > (:merge :standard)
+ > (:macro-char #\\[ #'(lambda (stream char)
+ > (read-delimited-list #\\] stream)))
+ > (:macro-char #\\# :dispatch)
+ > (:dispatch-macro-char #\\# #\\t #'scheme::read-#t)
+ > (:dispatch-macro-char #\\# #\\f #'scheme::read-#f)
+ > ...
+ > (:case :preserve))
+ >
+ > (in-readtable elisp:syntax)
+ >
+ > ...
+ >
+ > (in-readtable scheme:syntax)
+ >
+ > ...
+
+* Acknowledgements
+
+ Thanks to Robert Goldman for making me want to write this library.
+
+ Thanks to Stephen Compall, Ariel Badichi, David Lichteblau, Bart
+ Botta, David Crawford, and Pascal Costanza for being early adopters,
+ providing comments and bugfixes.
+"))
+
+(pushnew :named-readtables *features*)
\ No newline at end of file
Added: dependencies/trunk/named-readtables/tests/package.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/tests/package.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,12 @@
+;;; -*- Mode:Lisp -*-
+
+(in-package :cl-user)
+
+(defpackage :named-readtables-test
+ (:use :cl :named-readtables)
+ (:import-from :named-readtables
+ #:dispatch-macro-char-p
+ #:do-readtable
+ #:ensure-function
+ #:ensure-dispatch-macro-character
+ #:function=))
\ No newline at end of file
Added: dependencies/trunk/named-readtables/tests/rt.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/tests/rt.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,256 @@
+#|----------------------------------------------------------------------------|
+ | Copyright 1990 by the Massachusetts Institute of Technology, Cambridge MA. |
+ | |
+ | Permission to use, copy, modify, and distribute this software and its |
+ | documentation for any purpose and without fee is hereby granted, provided |
+ | that this copyright and permission notice appear in all copies and |
+ | supporting documentation, and that the name of M.I.T. not be used in |
+ | advertising or publicity pertaining to distribution of the software |
+ | without specific, written prior permission. M.I.T. makes no |
+ | representations about the suitability of this software for any purpose. |
+ | It is provided "as is" without express or implied warranty. |
+ | |
+ | M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
+ | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
+ | M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
+ | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
+ | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
+ | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
+ | SOFTWARE. |
+ |----------------------------------------------------------------------------|#
+
+;; (defpackage :rt
+;; (:use #:cl)
+;; (:export #:*do-tests-when-defined* #:*test* #:continue-testing
+;; #:deftest #:do-test #:do-tests #:get-test #:pending-tests
+;; #:rem-all-tests #:rem-test)
+;; (:documentation "The MIT regression tester"))
+
+;; (in-package :rt)
+
+(in-package :named-readtables-test)
+
+(defvar *test* nil "Current test name")
+(defvar *do-tests-when-defined* nil)
+(defvar *entries* '(nil) "Test database")
+(defvar *in-test* nil "Used by TEST")
+(defvar *debug* nil "For debugging")
+(defvar *catch-errors* t
+ "When true, causes errors in a test to be caught.")
+(defvar *print-circle-on-failure* nil
+ "Failure reports are printed with *PRINT-CIRCLE* bound to this value.")
+(defvar *compile-tests* nil
+ "When true, compile the tests before running them.")
+(defvar *optimization-settings* '((safety 3)))
+(defvar *expected-failures* nil
+ "A list of test names that are expected to fail.")
+
+(defstruct (entry (:conc-name nil)
+ (:type list))
+ pend name form)
+
+(defmacro vals (entry) `(cdddr ,entry))
+
+(defmacro defn (entry) `(cdr ,entry))
+
+(defun pending-tests ()
+ (do ((l (cdr *entries*) (cdr l))
+ (r nil))
+ ((null l) (nreverse r))
+ (when (pend (car l))
+ (push (name (car l)) r))))
+
+(defun rem-all-tests ()
+ (setq *entries* (list nil))
+ nil)
+
+(defun rem-test (&optional (name *test*))
+ (do ((l *entries* (cdr l)))
+ ((null (cdr l)) nil)
+ (when (equal (name (cadr l)) name)
+ (setf (cdr l) (cddr l))
+ (return name))))
+
+(defun get-test (&optional (name *test*))
+ (defn (get-entry name)))
+
+(defun get-entry (name)
+ (let ((entry (find name (cdr *entries*)
+ :key #'name
+ :test #'equal)))
+ (when (null entry)
+ (report-error t
+ "~%No test with name ~:@(~S~)."
+ name))
+ entry))
+
+(defmacro deftest (name form &rest values)
+ `(add-entry '(t ,name ,form .,values)))
+
+(defun add-entry (entry)
+ (setq entry (copy-list entry))
+ (do ((l *entries* (cdr l))) (nil)
+ (when (null (cdr l))
+ (setf (cdr l) (list entry))
+ (return nil))
+ (when (equal (name (cadr l))
+ (name entry))
+ (setf (cadr l) entry)
+ (report-error nil
+ "Redefining test ~:@(~S~)"
+ (name entry))
+ (return nil)))
+ (when *do-tests-when-defined*
+ (do-entry entry))
+ (setq *test* (name entry)))
+
+(defun report-error (error? &rest args)
+ (cond (*debug*
+ (apply #'format t args)
+ (if error? (throw '*debug* nil)))
+ (error? (apply #'error args))
+ (t (apply #'warn args))))
+
+(defun do-test (&optional (name *test*))
+ (do-entry (get-entry name)))
+
+(defun equalp-with-case (x y)
+ "Like EQUALP, but doesn't do case conversion of characters."
+ (cond
+ ((eq x y) t)
+ ((consp x)
+ (and (consp y)
+ (equalp-with-case (car x) (car y))
+ (equalp-with-case (cdr x) (cdr y))))
+ ((and (typep x 'array)
+ (= (array-rank x) 0))
+ (equalp-with-case (aref x) (aref y)))
+ ((typep x 'vector)
+ (and (typep y 'vector)
+ (let ((x-len (length x))
+ (y-len (length y)))
+ (and (eql x-len y-len)
+ (loop
+ for e1 across x
+ for e2 across y
+ always (equalp-with-case e1 e2))))))
+ ((and (typep x 'array)
+ (typep y 'array)
+ (not (equal (array-dimensions x)
+ (array-dimensions y))))
+ nil)
+ ((typep x 'array)
+ (and (typep y 'array)
+ (let ((size (array-total-size x)))
+ (loop for i from 0 below size
+ always (equalp-with-case (row-major-aref x i)
+ (row-major-aref y i))))))
+ (t (eql x y))))
+
+(defun do-entry (entry &optional
+ (s *standard-output*))
+ (catch '*in-test*
+ (setq *test* (name entry))
+ (setf (pend entry) t)
+ (let* ((*in-test* t)
+ ;; (*break-on-warnings* t)
+ (aborted nil)
+ r)
+ ;; (declare (special *break-on-warnings*))
+
+ (block aborted
+ (setf r
+ (flet ((%do
+ ()
+ (if *compile-tests*
+ (multiple-value-list
+ (funcall (compile
+ nil
+ `(lambda ()
+ (declare
+ (optimize ,@*optimization-settings*))
+ ,(form entry)))))
+ (multiple-value-list
+ (eval (form entry))))))
+ (if *catch-errors*
+ (handler-bind
+ ((style-warning #'muffle-warning)
+ (error #'(lambda (c)
+ (setf aborted t)
+ (setf r (list c))
+ (return-from aborted nil))))
+ (%do))
+ (%do)))))
+
+ (setf (pend entry)
+ (or aborted
+ (not (equalp-with-case r (vals entry)))))
+
+ (when (pend entry)
+ (let ((*print-circle* *print-circle-on-failure*))
+ (format s "~&Test ~:@(~S~) failed~
+ ~%Form: ~S~
+ ~%Expected value~P: ~
+ ~{~S~^~%~17t~}~%"
+ *test* (form entry)
+ (length (vals entry))
+ (vals entry))
+ (format s "Actual value~P: ~
+ ~{~S~^~%~15t~}.~%"
+ (length r) r)))))
+ (when (not (pend entry)) *test*))
+
+(defun continue-testing ()
+ (if *in-test*
+ (throw '*in-test* nil)
+ (do-entries *standard-output*)))
+
+(defun do-tests (&optional
+ (out *standard-output*))
+ (dolist (entry (cdr *entries*))
+ (setf (pend entry) t))
+ (if (streamp out)
+ (do-entries out)
+ (with-open-file
+ (stream out :direction :output)
+ (do-entries stream))))
+
+(defun do-entries (s)
+ (format s "~&Doing ~A pending test~:P ~
+ of ~A tests total.~%"
+ (count t (cdr *entries*)
+ :key #'pend)
+ (length (cdr *entries*)))
+ (dolist (entry (cdr *entries*))
+ (when (pend entry)
+ (format s "~@[~<~%~:; ~:@(~S~)~>~]"
+ (do-entry entry s))))
+ (let ((pending (pending-tests))
+ (expected-table (make-hash-table :test #'equal)))
+ (dolist (ex *expected-failures*)
+ (setf (gethash ex expected-table) t))
+ (let ((new-failures
+ (loop for pend in pending
+ unless (gethash pend expected-table)
+ collect pend)))
+ (if (null pending)
+ (format s "~&No tests failed.")
+ (progn
+ (format s "~&~A out of ~A ~
+ total tests failed: ~
+ ~:@(~{~<~% ~1:;~S~>~
+ ~^, ~}~)."
+ (length pending)
+ (length (cdr *entries*))
+ pending)
+ (if (null new-failures)
+ (format s "~&No unexpected failures.")
+ (when *expected-failures*
+ (format s "~&~A unexpected failures: ~
+ ~:@(~{~<~% ~1:;~S~>~
+ ~^, ~}~)."
+ (length new-failures)
+ new-failures)))
+ ))
+ (finish-output s)
+ (null pending))))
Added: dependencies/trunk/named-readtables/tests/tests.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/tests/tests.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,322 @@
+;;; -*- Mode:Lisp -*-
+
+(in-package :named-readtables-test)
+
+(defun map-alist (car-fn cdr-fn alist)
+ (mapcar #'(lambda (entry)
+ (cons (funcall car-fn (car entry))
+ (funcall cdr-fn (cdr entry))))
+ alist))
+
+(defun length=1 (list)
+ (and list (null (cdr list))))
+
+(defmacro signals-condition-p (name &body body)
+ `(handler-case (prog1 nil , at body)
+ (,(second name) () t)))
+
+(defmacro continue-condition (name &body body)
+ `(handler-bind ((,(second name) #'continue))
+ , at body))
+
+(defun read-with-readtable (name string)
+ (let ((*package* '#.*package*)
+ (*readtable* (find-readtable name)))
+ (values (read-from-string string))))
+
+(defun random-named-readtable ()
+ (let ((readtables (list-all-named-readtables)))
+ (nth (random (length readtables)) readtables)))
+
+
+(defun readtable-content (named-readtable-designator)
+ (let ((readtable (ensure-readtable named-readtable-designator))
+ (result '()))
+ ;; Make sure to canonicalize the order and function designators so
+ ;; we can compare easily.
+ (do-readtable ((char reader-fn ntp disp? table) readtable)
+ (setq table (sort (copy-list table) #'char< :key #'car))
+ (push (list* char
+ (ensure-function reader-fn)
+ ntp
+ (and disp? (list (map-alist #'identity
+ #'ensure-function
+ table))))
+ result))
+ (sort result #'char< :key #'car)))
+
+(defun readtable= (rt1 rt2)
+ (tree-equal (readtable-content rt1) (readtable-content rt2)
+ :test #'(lambda (x y)
+ (if (and (functionp x) (functionp y))
+ (function= x y)
+ (eql x y)))))
+
+
+(defun read-A (stream c)
+ (declare (ignore stream c))
+ :a)
+
+(defun read-A-as-X (stream c)
+ (declare (ignore stream c))
+ :x)
+
+(defun read-B (stream c)
+ (declare (ignore stream c))
+ :b)
+
+(defun read-sharp-paren (stream c n)
+ (declare (ignore stream c n))
+ 'sharp-paren)
+
+(defun read-C (stream c)
+ (declare (ignore stream c))
+ :c)
+
+(defreadtable A
+ (:macro-char #\A #'read-A))
+
+(defreadtable A-as-X
+ (:macro-char #\A #'read-A-as-X))
+
+(defreadtable B
+ (:macro-char #\B #'read-B))
+
+(defreadtable C
+ (:macro-char #\C #'read-C))
+
+(defreadtable A+B+C
+ (:merge A B C))
+
+(defreadtable standard+A+B+C
+ (:merge :standard A+B+C))
+
+(defreadtable sharp-paren
+ (:macro-char #\# :dispatch)
+ (:dispatch-macro-char #\# #\( #'read-sharp-paren))
+
+
+(deftest cruft.1
+ (function= (get-macro-character #\" (copy-readtable nil))
+ (get-macro-character #\" (copy-readtable nil)))
+ t)
+
+(deftest cruft.2
+ (dispatch-macro-char-p #\# (find-readtable :standard))
+ t)
+
+(deftest cruft.3
+ (dispatch-macro-char-p #\# (make-readtable))
+ nil)
+
+(deftest cruft.4
+ (let ((rt (copy-named-readtable :standard)))
+ (ensure-dispatch-macro-character #\# t rt)
+ (dispatch-macro-char-p #\# rt))
+ t)
+
+(deftest cruft.5
+ (let ((rt (make-readtable)))
+ (values
+ (dispatch-macro-char-p #\$ rt)
+ (ensure-dispatch-macro-character #\$ t rt)
+ (dispatch-macro-char-p #\$ rt)))
+ nil t t)
+
+(deftest cruft.6
+ (let ((rt (make-readtable))
+ (fn (constantly nil)))
+ (ensure-dispatch-macro-character #\$ t rt)
+ (set-dispatch-macro-character #\$ #\# fn rt)
+ (values
+ (eq fn (get-dispatch-macro-character #\$ #\# rt))
+ (length=1 (readtable-content rt))))
+ t t)
+
+(deftest cruft.7
+ (let ((rt (make-readtable))
+ (fn (constantly nil)))
+ (set-macro-character #\$ fn t rt)
+ (values
+ (eq fn (get-macro-character #\$ rt))
+ (length=1 (readtable-content rt))))
+ t t)
+
+
+(deftest standard.1
+ (read-with-readtable :standard "ABC")
+ ABC)
+
+(deftest standard.2
+ (read-with-readtable :standard "(A B C)")
+ (A B C))
+
+(deftest standard.3
+ (let ((x (find-readtable nil))
+ (y (find-readtable :standard))
+ (z (find-readtable :common-lisp)))
+ (and (eq x y) (eq y z)))
+ t)
+
+
+(deftest modern.1
+ (read-with-readtable :modern "FooF")
+ |FooF|)
+
+
+(deftest empty.1
+ (null (readtable-content (make-readtable)))
+ t)
+
+(deftest empty.2
+ (readtable= (merge-readtables-into (make-readtable) :standard)
+ (find-readtable :standard))
+ t)
+
+(deftest empty.3
+ (let ((rt (copy-named-readtable :standard)))
+ (readtable= (merge-readtables-into (make-readtable) rt)
+ (merge-readtables-into rt (make-readtable))))
+ t)
+
+
+(deftest basics.1
+ (read-with-readtable 'A "A")
+ :a)
+
+(deftest basics.2
+ (read-with-readtable 'A-as-X "A")
+ :x)
+
+(deftest basics.3
+ (read-with-readtable 'A "B")
+ B)
+
+(deftest basics.4
+ (read-with-readtable 'A "(A B C)")
+ |(|)
+
+
+(deftest unregister.1
+ (let ((rt (find-readtable 'A)))
+ (register-readtable 'does-not-exist rt)
+ (values
+ (and (find-readtable 'does-not-exist) t)
+ (unregister-readtable 'does-not-exist)
+ (and (find-readtable 'does-not-exist) t)))
+ t t nil)
+
+
+(deftest name.1
+ (let ((rt (random-named-readtable)))
+ (eq rt (find-readtable (readtable-name rt))))
+ t)
+
+(deftest ensure.1
+ (unwind-protect
+ (let* ((x (ensure-readtable 'does-not-exist (find-readtable 'A)))
+ (y (find-readtable 'A))
+ (z (find-readtable 'does-not-exist)))
+ (and (eq x y) (eq y z)))
+ (unregister-readtable 'does-not-exist))
+ t)
+
+
+(deftest merge.1
+ (values
+ (read-with-readtable 'A+B+C "A")
+ (read-with-readtable 'A+B+C "B")
+ (read-with-readtable 'A+B+C "C"))
+ :a :b :c)
+
+(deftest merge.2
+ (read-with-readtable 'standard+A+B+C "(A B C)")
+ (:a :b :c))
+
+(deftest merge.3
+ (read-with-readtable 'standard+A+B+C "#(A B C)")
+ #(:a :b :c))
+
+(deftest merge.4
+ (let ((A+B+C+standard (merge-readtables-into (copy-named-readtable 'A+B+C)
+ :standard)))
+ (readtable= 'standard+A+B+C A+B+C+standard))
+ t)
+
+
+(deftest rename.1
+ (unwind-protect
+ (progn (make-readtable 'A* :merge '(A))
+ (rename-readtable 'A* 'A**)
+ (values (and (find-readtable 'A*) t)
+ (and (find-readtable 'A**) t)))
+ (unregister-readtable 'A*)
+ (unregister-readtable 'A**))
+ nil
+ t)
+
+
+(deftest reader-macro-conflict.1
+ (signals-condition-p 'reader-macro-conflict
+ (merge-readtables-into (make-readtable) 'A 'A-as-X))
+ t)
+
+(deftest reader-macro-conflict.2
+ (signals-condition-p 'reader-macro-conflict
+ (merge-readtables-into (make-readtable) :standard :standard))
+ nil)
+
+(deftest reader-macro-conflict.3
+ (signals-condition-p 'reader-macro-conflict
+ (merge-readtables-into (make-readtable) 'A+B+C 'A))
+ nil)
+
+(deftest reader-macro-conflict.4
+ (signals-condition-p 'reader-macro-conflict
+ (merge-readtables-into (make-readtable) :standard 'sharp-paren))
+ t)
+
+
+(deftest readtable-does-not-exist.1
+ (signals-condition-p 'readtable-does-not-exist
+ (ensure-readtable 'does-not-exist))
+ t)
+
+
+(deftest readtable-does-already-exist.1
+ (signals-condition-p 'readtable-does-already-exist
+ (make-readtable 'A))
+ t)
+
+(deftest readtable-does-already-exist.2
+ (signals-condition-p 'readtable-does-already-exist
+ (make-readtable 'A))
+ t)
+
+(deftest readtable-does-already-exist.3
+ (let ((rt (make-readtable 'does-not-exist :merge '(:standard A B))))
+ (declare (ignore rt))
+ (unwind-protect
+ (read-with-readtable (continue-condition 'readtable-does-already-exist
+ (make-readtable 'does-not-exist
+ :merge '(:standard A C)))
+
+ "(A B C)")
+ (unregister-readtable 'does-not-exist)))
+ (:a B :c))
+
+
+(deftest defreadtable.1
+ (unwind-protect
+ (signals-condition-p 'reader-macro-conflict
+ (eval `(defreadtable does-not-exist (:merge A A-as-X))))
+ (unregister-readtable 'does-not-exist))
+ t)
+
+(deftest defreadtable.2
+ (unwind-protect
+ (signals-condition-p 't
+ (eval `(defreadtable does-not-exist (:fuze A A-as-X))))
+ (unregister-readtable 'does-not-exist))
+ nil)
+
Added: dependencies/trunk/named-readtables/utils.lisp
==============================================================================
--- (empty file)
+++ dependencies/trunk/named-readtables/utils.lisp Tue Jan 26 15:20:07 2010
@@ -0,0 +1,245 @@
+;;;;
+;;;; Copyright (c) 2008 - 2009 Tobias C. Rittweiler <tcr at freebits.de>
+;;;;
+;;;; All rights reserved.
+;;;;
+;;;; See LICENSE for details.
+;;;;
+
+(in-package :editor-hints.named-readtables)
+
+(defmacro without-package-lock ((&rest package-names) &body body)
+ (declare (ignorable package-names))
+ #+clisp
+ (return-from without-package-lock
+ `(ext:without-package-lock (, at package-names) , at body))
+ #+lispworks
+ (return-from without-package-lock
+ `(let ((hcl:*packages-for-warn-on-redefinition*
+ (set-difference hcl:*packages-for-warn-on-redefinition*
+ '(, at package-names)
+ :key (lambda (package-designator)
+ (if (packagep package-designator)
+ (package-name package-designator)
+ package-designator))
+ :test #'string=)))
+ , at body))
+ `(progn , at body))
+
+;;; Taken from SWANK (which is Public Domain.)
+
+(defmacro destructure-case (value &rest patterns)
+ "Dispatch VALUE to one of PATTERNS.
+A cross between `case' and `destructuring-bind'.
+The pattern syntax is:
+ ((HEAD . ARGS) . BODY)
+The list of patterns is searched for a HEAD `eq' to the car of
+VALUE. If one is found, the BODY is executed with ARGS bound to the
+corresponding values in the CDR of VALUE."
+ (let ((operator (gensym "op-"))
+ (operands (gensym "rand-"))
+ (tmp (gensym "tmp-")))
+ `(let* ((,tmp ,value)
+ (,operator (car ,tmp))
+ (,operands (cdr ,tmp)))
+ (case ,operator
+ ,@(loop for (pattern . body) in patterns collect
+ (if (eq pattern t)
+ `(t , at body)
+ (destructuring-bind (op &rest rands) pattern
+ `(,op (destructuring-bind ,rands ,operands
+ , at body)))))
+ ,@(if (eq (caar (last patterns)) t)
+ '()
+ `((t (error "destructure-case failed: ~S" ,tmp))))))))
+
+;;; Taken from Alexandria (which is Public Domain, or BSD.)
+
+(define-condition simple-style-warning (simple-warning style-warning)
+ ())
+
+(defun simple-style-warn (format-control &rest format-args)
+ (warn 'simple-style-warning
+ :format-control format-control
+ :format-arguments format-args))
+
+(define-condition simple-program-error (simple-error program-error)
+ ())
+
+(defun simple-program-error (message &rest args)
+ (error 'simple-program-error
+ :format-control message
+ :format-arguments args))
+
+(defun required-argument (&optional name)
+ "Signals an error for a missing argument of NAME. Intended for
+use as an initialization form for structure and class-slots, and
+a default value for required keyword arguments."
+ (error "Required argument ~@[~S ~]missing." name))
+
+(defun ensure-list (list)
+ "If LIST is a list, it is returned. Otherwise returns the list
+designated by LIST."
+ (if (listp list)
+ list
+ (list list)))
+
+(declaim (inline ensure-function)) ; to propagate return type.
+(declaim (ftype (function (t) (values function &optional))
+ ensure-function))
+(defun ensure-function (function-designator)
+ "Returns the function designated by FUNCTION-DESIGNATOR:
+if FUNCTION-DESIGNATOR is a function, it is returned, otherwise
+it must be a function name and its FDEFINITION is returned."
+ (if (functionp function-designator)
+ function-designator
+ (fdefinition function-designator)))
+
+(defun parse-body (body &key documentation whole)
+ "Parses BODY into (values remaining-forms declarations doc-string).
+Documentation strings are recognized only if DOCUMENTATION is true.
+Syntax errors in body are signalled and WHOLE is used in the signal
+arguments when given."
+ (let ((doc nil)
+ (decls nil)
+ (current nil))
+ (tagbody
+ :declarations
+ (setf current (car body))
+ (when (and documentation (stringp current) (cdr body))
+ (if doc
+ (error "Too many documentation strings in ~S." (or whole body))
+ (setf doc (pop body)))
+ (go :declarations))
+ (when (and (listp current) (eql (first current) 'declare))
+ (push (pop body) decls)
+ (go :declarations)))
+ (values body (nreverse decls) doc)))
+
+(defun parse-ordinary-lambda-list (lambda-list)
+ "Parses an ordinary lambda-list, returning as multiple values:
+
+ 1. Required parameters.
+ 2. Optional parameter specifications, normalized into form (NAME INIT SUPPLIEDP)
+ where SUPPLIEDP is NIL if not present.
+ 3. Name of the rest parameter, or NIL.
+ 4. Keyword parameter specifications, normalized into form ((KEYWORD-NAME NAME) INIT SUPPLIEDP)
+ where SUPPLIEDP is NIL if not present.
+ 5. Boolean indicating &ALLOW-OTHER-KEYS presence.
+ 6. &AUX parameter specifications, normalized into form (NAME INIT).
+
+Signals a PROGRAM-ERROR is the lambda-list is malformed."
+ (let ((state :required)
+ (allow-other-keys nil)
+ (auxp nil)
+ (required nil)
+ (optional nil)
+ (rest nil)
+ (keys nil)
+ (aux nil))
+ (labels ((simple-program-error (format-string &rest format-args)
+ (error 'simple-program-error
+ :format-control format-string
+ :format-arguments format-args))
+ (fail (elt)
+ (simple-program-error "Misplaced ~S in ordinary lambda-list:~% ~S"
+ elt lambda-list))
+ (check-variable (elt what)
+ (unless (and (symbolp elt) (not (constantp elt)))
+ (simple-program-error "Invalid ~A ~S in ordinary lambda-list:~% ~S"
+ what elt lambda-list)))
+ (check-spec (spec what)
+ (destructuring-bind (init suppliedp) spec
+ (declare (ignore init))
+ (check-variable suppliedp what)))
+ (make-keyword (name)
+ "Interns the string designated by NAME in the KEYWORD package."
+ (intern (string name) :keyword)))
+ (dolist (elt lambda-list)
+ (case elt
+ (&optional
+ (if (eq state :required)
+ (setf state elt)
+ (fail elt)))
+ (&rest
+ (if (member state '(:required &optional))
+ (setf state elt)
+ (progn
+ (break "state=~S" state)
+ (fail elt))))
+ (&key
+ (if (member state '(:required &optional :after-rest))
+ (setf state elt)
+ (fail elt)))
+ (&allow-other-keys
+ (if (eq state '&key)
+ (setf allow-other-keys t
+ state elt)
+ (fail elt)))
+ (&aux
+ (cond ((eq state '&rest)
+ (fail elt))
+ (auxp
+ (simple-program-error "Multiple ~S in ordinary lambda-list:~% ~S"
+ elt lambda-list))
+ (t
+ (setf auxp t
+ state elt))
+ ))
+ (otherwise
+ (when (member elt '#.(set-difference lambda-list-keywords
+ '(&optional &rest &key &allow-other-keys &aux)))
+ (simple-program-error
+ "Bad lambda-list keyword ~S in ordinary lambda-list:~% ~S"
+ elt lambda-list))
+ (case state
+ (:required
+ (check-variable elt "required parameter")
+ (push elt required))
+ (&optional
+ (cond ((consp elt)
+ (destructuring-bind (name &rest tail) elt
+ (check-variable name "optional parameter")
+ (if (cdr tail)
+ (check-spec tail "optional-supplied-p parameter")
+ (setf elt (append elt '(nil))))))
+ (t
+ (check-variable elt "optional parameter")
+ (setf elt (cons elt '(nil nil)))))
+ (push elt optional))
+ (&rest
+ (check-variable elt "rest parameter")
+ (setf rest elt
+ state :after-rest))
+ (&key
+ (cond ((consp elt)
+ (destructuring-bind (var-or-kv &rest tail) elt
+ (cond ((consp var-or-kv)
+ (destructuring-bind (keyword var) var-or-kv
+ (unless (symbolp keyword)
+ (simple-program-error "Invalid keyword name ~S in ordinary ~
+ lambda-list:~% ~S"
+ keyword lambda-list))
+ (check-variable var "keyword parameter")))
+ (t
+ (check-variable var-or-kv "keyword parameter")
+ (setf var-or-kv (list (make-keyword var-or-kv) var-or-kv))))
+ (if (cdr tail)
+ (check-spec tail "keyword-supplied-p parameter")
+ (setf tail (append tail '(nil))))
+ (setf elt (cons var-or-kv tail))))
+ (t
+ (check-variable elt "keyword parameter")
+ (setf elt (list (list (make-keyword elt) elt) nil nil))))
+ (push elt keys))
+ (&aux
+ (if (consp elt)
+ (destructuring-bind (var &optional init) elt
+ (declare (ignore init))
+ (check-variable var "&aux parameter"))
+ (check-variable elt "&aux parameter"))
+ (push elt aux))
+ (t
+ (simple-program-error "Invalid ordinary lambda-list:~% ~S" lambda-list)))))))
+ (values (nreverse required) (nreverse optional) rest (nreverse keys)
+ allow-other-keys (nreverse aux))))
\ No newline at end of file
More information about the snow-cvs
mailing list