-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathdomain-support.lisp
400 lines (349 loc) · 13.9 KB
/
domain-support.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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
;;; -*- Mode:Lisp; Package:Weyli; Base:10; Lowercase:T; Syntax:Common-Lisp -*-
;;; ===========================================================================
;;; Domains
;;; ===========================================================================
;;; (c) Copyright 1989, 1993 Cornell University
;;; domain-support.lisp,v 1.7 1995/05/24 17:41:59 rz Exp
(in-package "WEYLI")
;;; DELETE (make::adjust-version-numbers Weyl "1.7")
(defclass has-property-list ()
((property-list :initform nil
:accessor property-list-of)))
(defmethod getf ((obj has-property-list) key &optional (default nil))
(common-lisp:getf (property-list-of obj) key default))
(defmethod putf ((obj has-property-list) key value)
(setf (common-lisp:getf (property-list-of obj) key) value))
(defun domain-print-object (d stream)
(format stream "#<Domain: ~A>" (class-name (class-of d))))
(defclass domain (has-property-list)
((operation-table :initform (make-hash-table))
(super-domains :initform nil
:initarg :super-domains
:accessor super-domains-of)
(morphisms-from :initform nil
:accessor domain-morphisms-from)
(morphisms-to :initform nil
:accessor domain-morphisms-to)
(print-function :initform #'domain-print-object
:initarg :print-function)))
;;; FIXME : Merge with domain-print-object.
(defmethod print-object ((d domain) stream)
(with-slots (print-function) d
;; This is so that you can pretty print objects in lucid. It
;; appears, that you are not supposed to use PRINC inside these
;; methods.
#+Lucid
(let ((*print-pretty* nil))
(funcall print-function d stream))
#-Lucid
(funcall print-function d stream)))
(defmacro define-operations (domain &body operations)
`(defmethod parse-operations :after ((d ,domain))
(parse-operation-list d ',operations)))
(defgeneric parse-operation-list (domain operation-list)
(:documentation
"The purpose of this method is not known."))
(defmethod parse-operation-list ((d domain) operation-list)
(with-slots (operation-table) d
(loop for ((operation . arguments) nil values) on operation-list by #'cdddr
do (setf (gethash operation operation-table)
(list operation arguments values)))))
;;; Need a dummy primary method to hang all the :after methods on.
;;; FIXME : Organize so that the primary method is not useless.
(defgeneric parse-operations (domain)
(:method ((domain domain))
nil)
(:documentation
"The purpose of this method is not known."))
;;; FIXME : Audit for merging with parse-operations.
(defmethod initialize-instance :after ((d domain) &rest plist)
(declare (ignore plist))
(parse-operations d))
(defgeneric list-operations (domain)
(:documentation
"Return a list of operations for the domain."))
;;; FIXME : Convert the maphash to a LOOP.
(defmethod list-operations ((d domain))
(with-slots (operation-table) d
(let (ops)
(maphash #'(lambda (key value)
(declare (ignore value))
(push key ops))
operation-table)
ops)))
(defgeneric operation-arguments (domain operation)
(:documentation
"The purpose of this method is not known."))
(defmethod operation-arguments ((d domain) operation)
(with-slots (operation-table) d
(subst (class-name (class-of d)) 'self
(second (gethash operation operation-table)))))
(defgeneric operation-values (domain operation)
(:documentation
"The purpose of this method is not known."))
(defmethod operation-values ((d domain) operation)
(with-slots (operation-table) d
(subst (class-name (class-of d)) 'self
(third (gethash operation operation-table)))))
(defgeneric describe-operations (domain &optional no-complaints)
(:documentation
"The purpose of this method is not known."))
#+Genera
(defmethod describe-operations ((d domain) &optional no-complaints)
(declare (ignore no-complaints))
(let* ((class-name (class-name (class-of d)))
(domain-element (cond ((null (rest (get class-name 'domain-elements)))
(first (get class-name 'domain-elements)))
(t (format nil "~A element" class-name)))))
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(format t "~&~S is a ~A~%" d class-name)
(fresh-line)
(with-slots (operation-table) d
(scl:formatting-table ()
(scl:with-character-style ('(nil :italic nil))
(scl:formatting-row ()
(scl:formatting-cell ()
(princ "Operation"))
(scl:formatting-cell ()
(princ "Arguments"))
(scl:formatting-cell ()
(princ "Values"))))
(maphash #'(lambda (key value)
(declare (ignore key))
(scl:formatting-row ()
(scl:formatting-cell ()
(princ (first value)))
(scl:formatting-cell ()
(format t "~A~{, ~A~}"
(canonicalize-class (first (second value)))
(mapcar #'canonicalize-class
(rest (second value)))))
(scl:formatting-cell ()
(princ (canonicalize-class (third value))))))
operation-table))))))
#-Genera
(defmethod describe-operations ((d domain) &optional no-complaints)
(declare (ignore no-complaints))
(let* ((class-name (class-name (class-of d)))
(element-classes (get class-name 'element-classes))
(domain-element (cond ((and element-classes
(null (rest element-classes)))
(first element-classes))
(t (format nil "~A element" class-name)))))
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(format t "~&~S is a ~A~%" d class-name)
(fresh-line)
(with-slots (operation-table) d
(format t "Operation Arguments Values")
(maphash #'(lambda (key value)
(declare (ignore key))
(format t "~&(~A ~A~{, ~A~}) -> ~A~%"
(first value)
(canonicalize-class (first (second value)))
(mapcar #'canonicalize-class
(rest (second value)))
(canonicalize-class (third value))))
operation-table)))))
(defgeneric required-operations (domain &optional fun)
(:documentation
"The purpose of this method is not known."))
(defmethod required-operations ((d domain) &optional fun)
(let* ((class-name (class-name (class-of d)))
(element-classes (get class-name 'element-classes))
(domain-element (cond ((and element-classes
(null (rest element-classes)))
(first element-classes))
(t (cons 'or element-classes))))
list)
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(unless fun
(setq fun #'(lambda (form)
(push (cons (first form)
(mapcar #'canonicalize-class (second form)))
list))))
(with-slots (operation-table) d
(maphash #'(lambda (key value)
(declare (ignore key))
(%funcall fun value))
operation-table))
list)))
(defun map-over-arglist-combinations (self arglist fun)
(labels ((recur (arglist types)
(cond ((null arglist)
(%funcall fun (reverse types)))
((atom (first arglist))
(recur (rest arglist) (cons (first arglist) types)))
((eql (first (first arglist)) 'or)
(loop for type in (rest (first arglist))
do (recur (cons type (rest arglist)) types)))
((eql (first (first arglist)) 'element)
(loop for type in (get self 'element-classes)
do (recur (cons type (rest arglist)) types)))
(t (error "Don't understand arglist entry: ~S"
(first arglist))))))
(recur (first arglist) ())))
;;; DELETE : The method does not appear to be used anywhere.
(defgeneric check-domain (domain)
(:documentation
"The purspose of this method is not known."))
;;; FIXME : SBCL specific. Need to abstract for other implementations.
#+SB-MOP
(defmethod check-domain ((d domain))
(required-operations
d
(lambda (form)
(let ((operation (first form))
(args (rest form)))
(map-over-arglist-combinations
(class-name (class-of d)) args
#'(lambda (arg-names)
(let ((args (loop for type in arg-names
collect (find-class type nil))))
(loop for method in (sb-mop:generic-function-methods
(symbol-function operation))
do (when (equal args
(sb-mop::method-specializers method))
(return t))
finally (format t "No method for ~S~%"
(cons operation arg-names))))))))))
;; Domain creators
;;; FIXME : Need to make creating domains atomic so that domains are
;;; not added to the list unless they are actually created.
(defvar *domains* ()
"List of domains currently in use")
(defvar *general* ()
"The general representation domain")
(defun reset-domains ()
(setq *domains* nil)
(setf (domain-morphisms-from *general*) nil)
(setf (domain-morphisms-to *general*) nil))
(defmacro add-domain (predicate &body body)
`(add-domain-internal ,predicate #'(lambda () ,@body)))
(defun add-domain-internal (predicate body)
(let ((domain (find nil *domains*
:test #'(lambda (a b)
(declare (ignore a))
(%funcall predicate b)))))
(when (null domain)
(setq domain (%funcall body))
(push domain *domains*))
domain))
(defun false (&rest args)
(declare (ignore args))
nil)
(defun true (&rest args)
(declare (ignore args))
t)
;;; FIXME : Need to ensure that the generic function is defined prior
;;; to the methods. The exact semantics depend on how this is used. It
;;; either needs to test for the existing of a generic function and
;;; create one if it doesn't exist or just create one if there should
;;; not already be one.
(defmacro define-domain-creator (name args creator &key predicate body)
(labels ((parse-args (args)
(cond ((null args)
args)
((member (first args) '(&optional &key))
(parse-args (rest args)))
((eql (first args) '&rest)
(error "Can't handle &rest args here"))
((atom (first args))
(cons (first args) (parse-args (rest args))))
(t (cons (first (first args))
(parse-args (rest args)))))))
(let ((internal-fun (intern (format nil "MAKE-~A*" name)))
(true-args (parse-args args)))
`(progn
(defmethod ,internal-fun ,args ,creator)
(defmethod ,(intern (format nil "MAKE-~A" name)) ,args
(add-domain #'false (,internal-fun ,@true-args)))
,@(when predicate
`((defmethod ,(intern (format nil "GET-~A" name)) ,args
(add-domain ,predicate (,internal-fun ,@true-args)))))
,@(when body
`((defmethod ,(intern (format nil "GET-~A" name)) ,args
,body)))))))
(defmacro with-new-weyl-context ((plist) &body body)
`(let ((*domains* nil)
(*allow-coercions*
,(or (%getf plist :allow-coercions) '*allow-coercions*)))
,@body))
;; All elements of a domain should include this class
(defclass domain-element ()
((domain :initarg :domain
:reader domain-of)))
(defmacro define-domain-element-classes (domain &body element-classes)
`(progn
;; At one time we thought there would be a one to one
;; correspondence between classes of domains and the classes of
;; their elements. This isn't the case. In addition, no uses
;; the element-class to domain-class correspondence, as you would
;; expect, so I'm not bothering to keep track of it. --RZ 7/12/94
#+ignore
,@(loop for element-class in element-classes
collect
`(cond ((eql (get ',element-class 'domain-class) ',domain))
(t
(when (get ',element-class 'domain-class)
(format t "WARNING: Reset domain-class of ~S~%"
',element-class))
(setf (get ',element-class 'domain-class) ',domain))))
(setf (get ',domain 'element-classes) ',element-classes)))
(defgeneric domain-element-classes (domain)
(:method ((domain domain))
(get (class-name (class-of domain)) 'element-classes))
(:documentation
"The purpose of this method is not known."))
;; This is so that you can pretty print objects in lucid. It appears,
;; that you are not supposed to use PRINC inside these methods.
#+Lucid
;; This must be an :around method since it must come before all the
;; primary methods.
(defmethod print-object :around ((object domain-element) stream)
(let ((*print-pretty* nil))
(call-next-method)))
(defgeneric coerce (elt domain)
(:documentation
"Coerce the element into the domain."))
(defgeneric coercible? (elt domain)
(:documentation
"Return true if the element is coercible into the domain."))
(defmacro defmethod-sd (op (x-spec y-spec) &body body)
#+Genera
(declare (zwei:indentation . wei:indent-for-clos-defmethod))
(let ((x (if (atom x-spec) x-spec (first x-spec)))
(y (if (atom y-spec) y-spec (first y-spec))))
`(defmethod ,op (,x-spec ,y-spec)
(let ((domain (domain-of ,x)))
(cond ((eql domain (domain-of ,y))
,@body)
(t (call-next-method)))))))
;; These are often of use when defining generic operations for domains.
(defvar *domain* ()
"Within the context of an operation, the current domain")
(defgeneric %bind-dynamic-domain-context (domain function)
(:documentation
"The purpose of this method is not known.")
(:method ((domain domain) function)
(let ((*domain* domain))
(%funcall function))))
(defmacro bind-domain-context (domain &body body)
`(%bind-dynamic-domain-context ,domain
(lambda ()
#+Genera (declare (sys:downward-function))
,@body)))