--- /dev/null
+(in-package :lisp-on-lines)
+
+(define-layered-class simple-plist-attribute ()
+ (%property-access-function
+ (description-class :initarg description-class
+ :accessor attribute-description-class))
+ (:documentation "A very simple implementation of ATTRIBUTEs based on
+ simple plists.
+
+To implement layered slot values, we use an anonymous layered function
+with a combination of APPEND. Methods on different layers return a
+plist (which is APPENDed), from which we simply GETF for the slot
+value.
+
+This is ineffecient, of course, but is easy to understand. Caching and
+performance hacks are implemented in subclasses that extend the simple
+protocol we define here."))
+
+(defstruct static-attribute-slot value)
+
+(defmethod ensure-property-access-function ((attribute simple-plist-attribute))
+ "return the PROPERTY-ACCESS-FUNCTION of this attribute. FUNCALLing
+the returned symbol will return the plist of slot values."
+ (if (slot-boundp attribute '%property-access-function)
+ (slot-value attribute '%property-access-function)
+ (let ((fn-name (gensym)))
+ (ensure-layered-function fn-name :lambda-list '(description) :method-combination '(append))
+ (setf (slot-value attribute '%property-access-function) fn-name))))
+
+(defun property-access-value (attribute)
+ (ignore-errors (funcall (ensure-property-access-function attribute) (attribute-description attribute))))
+
+(defconstant +property-not-found+ '=lisp-on-lines-property-not-found-indicator=
+ "A default value for GETF to return.")
+
+(defvar *special-symbol-access* nil)
+
+(defun special-symbol-access-p ()
+ *special-symbol-access*)
+
+(defmacro with-special-symbol-access (&body body)
+ `(let ((*special-symbol-access* t))
+ ,@body))
+
+(defmacro without-special-symbol-access (&body body)
+ `(let ((*special-symbol-access* nil))
+ ,@body))
+
+(define-layered-method
+ contextl:slot-value-using-layer (class (attribute simple-plist-attribute) slotd reader) ()
+ "Only layered slots that are not currently dynamically rebound are looked up via the plist.
+Initial slot values are stored in the PLIST of the symbol ENSURE-PROPERTY-ACCESS-FUNCTION returns."
+
+ (if (or contextl:*symbol-access*
+ (special-symbol-access-p)
+ (not (slot-definition-layeredp slotd)))
+ (call-next-method)
+ (multiple-value-bind (value boundp)
+ (handler-case (values (call-next-method) t)
+ (unbound-slot () (values nil nil)))
+
+ (when (and boundp (not (static-attribute-slot-p value)))
+ (return-from slot-value-using-layer value))
+
+ (let ((dynamic-value
+ (getf (ignore-errors (funcall (ensure-property-access-function attribute)
+ (find-layer (slot-value attribute 'description-class))))
+
+ (slot-definition-name slotd)
+ +property-not-found+)))
+
+ (if (eq dynamic-value +property-not-found+)
+ (if boundp
+ (static-attribute-slot-value value)
+ (call-next-method))
+ dynamic-value)))))
+
+(defun set-property-value-for-layer (attribute property value layer)
+ (let ((vals (property-access-value attribute)))
+ (ensure-layered-method
+ (ensure-property-access-function attribute)
+ `(lambda (description-class)
+ ',(append (list property value) (alexandria:remove-from-plist vals property)))
+ :specializers (list (class-of (attribute-description attribute)))
+ :qualifiers '(append)
+ :in-layer layer)))
+
+(define-layered-method
+ (setf contextl:slot-value-using-layer) :around (value class (attribute simple-plist-attribute) slotd writer)
+"This might not be here"
+ (if (and (not contextl:*symbol-access*)
+ (not (special-symbol-access-p))
+ (slot-definition-layeredp slotd))
+ (with-special-symbol-access (setf (slot-value-using-layer class attribute slotd writer) (make-static-attribute-slot :value value)))
+ (call-next-method))
+)
+
+(defmethod initialize-attribute-for-description (description-class (attribute simple-plist-attribute) layer-name &rest args)
+ "Define a method on the PROPERTY-ACCESS-FUNCTION to associate
+slots (named by their :initarg) with values in layer LAYER-NAME."
+ (let* ((class (class-of attribute))
+ (slotds (class-slots class)))
+ (setf (slot-value attribute 'description-class) description-class)
+ (ensure-layered-method
+ (ensure-property-access-function attribute)
+ `(lambda (description-class)
+ ',(alexandria:remove-from-plist
+ (loop
+ :for (key val) :on args :by #'cddr
+ :nconc (list
+ (loop
+ :for slotd :in slotds
+ :do (when (find key (slot-definition-initargs slotd))
+ (return (slot-definition-name slotd))))
+ val))
+ nil))
+ :specializers (list description-class)
+ :qualifiers '(append)
+ :in-layer layer-name)))
+