-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
data.lisp
329 lines (249 loc) · 12 KB
/
data.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
(in-package #:org.shirakumo.alloy)
(defclass data (observable)
())
(defgeneric refresh (data))
(defgeneric expand-place-data (place))
(defgeneric expand-compound-place-data (place args))
(defgeneric access (data field))
(defgeneric (setf access) (value data field))
(defmethod expand-place-data ((place cons))
(expand-compound-place-data (first place) (rest place)))
(defmacro place-data (place)
(expand-place-data place))
(defmethod access ((object standard-object) field)
(slot-value object field))
(defmethod (setf access) (value (object standard-object) field)
(setf (slot-value object field) value))
(defclass value-data (data)
((value :initarg :value :accessor value)))
(defgeneric value (data))
(defgeneric (setf value) (new-value data))
(make-observable '(setf value) '(new-value observable))
(defmethod value ((string string)) string)
(defmethod access ((data value-data) (field (eql 'value)))
(value data))
(defmethod (setf access) (value (data value-data) (field (eql 'value)))
(setf (value data) value))
(defmethod refresh ((data value-data))
(notify-observers 'value data (value data) data))
(defmethod expand-place-data (atom)
`(make-instance 'value-data :value ,atom))
(defclass object-data (data)
((object :initarg :object :initform (arg! :object) :accessor object)))
(defmethod (setf object) :around (value (data object-data))
(observe NIL (object data) data)
(prog1 (call-next-method)
(observe T (object data) data)))
(defmethod access ((data object-data) field)
(access (object data) field))
(defmethod (setf access) (value (data object-data) field)
(setf (access (object data) field) value))
(defclass delegate-data (object-data)
((observed :initarg :observed :initform () :accessor observed)))
(defmethod reinitialize-instance :before ((data delegate-data) &key)
(observe NIL (object data) data))
(defmethod shared-initialize :after ((data delegate-data) slots &key)
(observe T (object data) data))
(defmethod observe ((nothing (eql NIL)) object (data delegate-data) &optional (name data))
(dolist (function (observed data))
(remove-observers function object name)))
(defmethod observe ((all (eql T)) object (data delegate-data) &optional (name data))
(dolist (function (observed data))
(observe function object (lambda (&rest args) (apply #'notify-observers function data args)) name))
(refresh data))
(defmethod observe :after (function (data delegate-data) observer &optional name)
(declare (ignore name))
(unless (find function (observed data))
(push function (observed data))))
(defmethod (setf observed) :around (value (data delegate-data))
(observe NIL (object data) data)
(prog1 (call-next-method)
(observe T (object data) data)))
(defmethod refresh ((data delegate-data))
;; FIXME: do this. somehow.
)
(defclass remap-data (object-data)
((mapping :initform (make-hash-table :test 'eql) :accessor mapping)))
(defmethod shared-initialize :after ((data remap-data) slots &key (mapping NIL mapping-p))
(when mapping-p (setf (mapping data) mapping)))
(defmethod (setf mapping) :around ((value hash-table) (data remap-data))
(observe NIL (object data) data)
(prog1 (call-next-method)
(observe T (object data) data)))
(defmethod (setf mapping) ((value cons) (data remap-data))
(let ((table (make-hash-table :test 'eql)))
(loop for (k . v) in value
do (setf (gethash k table) v))
(setf (mapping data) table)))
(defmethod (setf mapping) ((value null) (data remap-data))
(let ((table (make-hash-table :test 'eql)))
(setf (mapping data) table)))
(defmethod observe ((nothing (eql NIL)) object (data remap-data) &optional (name data))
(loop for function being the hash-keys of (observed data)
do (remove-observers function object name)))
(defmethod observe ((all (eql T)) object (data remap-data) &optional (name data))
(loop for function being the hash-keys of (observed data) using (hash-value mapped)
do (observe function object (lambda (&rest args) (apply #'notify-observers mapped data args)) name))
(refresh data))
(defmethod refresh ((data remap-data))
;; FIXME: do this. somehow.
)
;;; General case.
(defclass place-data (value-data)
((getter :initarg :getter :initform (arg! :getter) :accessor getter)
(setter :initarg :setter :initform (arg! :setter) :accessor setter)))
(defmethod value ((data place-data))
(funcall (getter data)))
(defmethod (setf value) (new-value (data place-data))
(funcall (setter data) new-value))
(defmethod expand-compound-place-data ((place symbol) args)
(let ((value (gensym "VALUE")))
(if (and (fboundp place)
(fboundp `(setf ,place))
(null (rest args)))
`(make-instance 'accessor-data
:object ,(first args)
:accessor ',place)
`(make-instance 'place-data
:getter (lambda () (,place ,@args))
:setter (lambda (,value) (setf (,place ,@args) ,value))))))
(defmethod expand-place-data ((place symbol))
(let ((value (gensym "VALUE")))
`(make-instance 'place-data
:getter (lambda () ,place)
:setter (lambda (,value) (setf ,place ,value)))))
(defclass accessor-data (value-data object-data)
((accessor :initarg :accessor :initform (arg! :accessor) :accessor accessor)))
(defmethod initialize-instance :after ((data accessor-data) &key)
(when (typep (object data) 'observable)
(observe (accessor data) (object data) (lambda (value object) (notify-observers 'value data value object)) data)))
(defmethod value ((data accessor-data))
(funcall (accessor data) (object data)))
(defmethod (setf value) (new-value (data accessor-data))
(funcall (fdefinition `(setf ,(accessor data))) new-value (object data)))
(defmethod (setf object) :around (value (data accessor-data))
(remove-observers (accessor data) (object data) data)
(call-next-method)
(observe (accessor data) (object data) (lambda (value object) (notify-observers 'value data value object)) data)
(refresh data))
(defmethod (setf accessor) :around (value (data accessor-data))
(remove-observers (accessor data) (object data) data)
(call-next-method)
(observe (accessor data) (object data) (lambda (value object) (notify-observers 'value data value object)) data)
(refresh data))
(defclass slot-data (value-data object-data)
((slot :initarg :slot :initform (arg! :slot) :accessor slot)))
(defmethod initialize-instance :after ((data slot-data) &key)
(when (typep (object data) 'observable)
(observe (slot data) (object data) (lambda (value object) (notify-observers 'value data value object)) data)))
(defmethod value ((data slot-data))
(slot-value (object data) (slot data)))
(defmethod (setf value) (new-value (data slot-data))
(setf (slot-value (object data) (slot data)) new-value))
(defmethod observe ((nothing (eql NIL)) object (data slot-data) &optional (name data))
(remove-observers (slot data) object name))
(defmethod observe ((all (eql T)) object (data slot-data) &optional (name data))
(observe (slot data) object (lambda (value object) (notify-observers 'value data value object)) name)
(refresh data))
(defmethod (setf slot) :around (value (data slot-data))
(observe NIL (object data) data)
(prog1 (call-next-method)
(observe T (object data) data)))
(defmethod expand-compound-place-data ((place (eql 'slot-value)) args)
(destructuring-bind (object slot) args
`(make-instance 'slot-data :object ,object :slot ,slot)))
(defclass aref-data (value-data)
((object :initarg :object :initform (arg! :object) :accessor object)
(index :initarg :index :initform (arg! :index) :accessor index)))
(defmethod value ((data aref-data))
(row-major-aref (object data) (index data)))
(defmethod (setf value) (new-value (data aref-data))
(setf (row-major-aref (object data) (index data)) new-value))
(defmethod expand-compound-place-data ((place (eql 'aref)) args)
(let ((object (gensym "OBJECT")))
`(let ((,object ,(first args)))
(make-instance 'aref-data :object ,object :index (array-row-major-index ,object ,@(rest args))))))
(defmethod (setf index) ((list list) (data aref-data))
(setf (index data) (apply #'array-row-major-index (object data) list)))
(defmethod (setf index) :after (index (data aref-data))
(refresh data))
;;; TODO: This is kinda... not too great.
(defclass computed-data (value-data)
((closure :initarg :closure :accessor closure)))
(defmethod initialize-instance :after ((data computed-data) &key observe)
(flet ((update (&rest _)
(declare (ignore _))
(setf (value data) (apply (closure data)
(loop for (function object) in observe
collect (funcall function object))))))
(loop for (function object) in observe
do (observe function object #'update))
(update)))
(defmethod expand-compound-place-data ((place (eql 'lambda)) args)
(destructuring-bind (args &rest body) args
`(make-instance 'computed-data
:closure (,place ,(mapcar #'first args) ,@body)
:observe (list ,@(loop for (function object) in (mapcar #'second args)
collect `(list ',function ,object))))))
(defclass sequence-data (data)
((value :initarg :sequence :initform (arg! :sequence) :reader value)))
(defgeneric element (data index))
(defgeneric (setf element) (value data index))
(defgeneric count (data))
(defgeneric push-element (value data &optional index))
(defgeneric pop-element (data &optional index))
(make-observable '(setf element) '(value observable index))
(make-observable 'push-element '(value observable &optional index))
(make-observable 'pop-element '(observable &optional index))
;; Defaults for a generic version with support for extensible sequences without having to
;; explicitly depend on that protocol
(defmethod element ((data sequence-data) (index integer))
(elt (value data) index))
(defmethod (setf element) (value (data sequence-data) (index integer))
(setf (elt (value data) index) value))
(defmethod count ((data sequence-data))
(length (value data)))
(defclass list-data (sequence-data)
((value :initarg :list :initform (arg! :list) :reader value)
(count :reader count)))
(defmethod initialize-instance :after ((data list-data) &key list)
(setf (slot-value data 'count) (length list)))
(defmethod shared-initialize :before ((data list-data) slots &key (list NIL list-p))
(when list-p
(check-type list list)))
(defmethod refresh ((data list-data))
(setf (slot-value data 'count) (length (value data))))
(defmethod element ((data list-data) (index integer))
(nth index (value data)))
(defmethod (setf element) (value (data list-data) (index integer))
(setf (nth index (value data)) value))
(defmethod push-element (value (data list-data) &optional index)
(if (and index (< 0 index))
(let ((cons (nthcdr (1- index) (value data))))
(setf (cdr cons) (list* value (cddr cons))))
(push value (value data)))
(incf (slot-value data 'count)))
(defmethod pop-element ((data list-data) &optional index)
(decf (slot-value data 'count))
(if (and index (< 0 index))
(let ((cons (nthcdr (1- index) (value data))))
(prog1 (cadr cons)
(setf (cdr cons) (cddr cons))))
(pop (value data))))
(defclass vector-data (sequence-data)
((value :initarg :vector :initform (arg! :vector) :reader value)))
(defmethod shared-initialize :before ((data vector-data) slots &key (vector NIL vector-p))
(when vector-p
(check-type vector vector)))
(defmethod element ((data vector-data) (index integer))
(aref (value data) index))
(defmethod (setf element) (value (data vector-data) (index integer))
(setf (aref (value data) index) value))
(defmethod push-element (value (data vector-data) &optional index)
(if index
(array-utils:vector-push-extend-position value (value data) index)
(vector-push-extend value (value data))))
(defmethod pop-element ((data vector-data) &optional index)
(if index
(array-utils:vector-pop-position (value data) index)
(vector-pop (value data))))