2 ;;;; Additional Common Lisp Functions for XLISP-STAT 2.0
3 ;;;; XLISP-STAT 2.1 Copyright (c) 1990-95, by Luke Tierney
4 ;;;; Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz
5 ;;;; You may give out copies of this software; for conditions see the file
6 ;;;; COPYING included with this distribution.
12 ;;;;;; New DEFSTRUCT System
13 ;;;;;; Replaces the internal special form
16 ;;;;;; Limitations: Error checking is poor.
17 ;;;;;; Multiple :constructor options are not allowed.
18 ;;;;;; Typed structures do not support :initial-offset's.
19 ;;;;;; The :type slot option is ignored.
20 ;;;;;; Probably lots more.
23 ;;;; Some Compiler Support Functions
26 (defvar *cmp-structs
*)
28 (defun cmp-get-slotinfo (structname)
29 (if (boundp '*cmp-structs
*)
30 (assoc structname
*cmp-structs
*)))
32 (defun cmp-register-slotinfo (structname slotinfo
)
33 (if (boundp '*cmp-structs
*)
34 (push (cons structname slotinfo
) *cmp-structs
*)))
38 ;;;; Some Runtime Support Functions
41 (defun get-structure-slot-default (type slot
)
42 (second (assoc slot
(get type
'*struct-slots
*))))
44 (defun set-structure-slot-default (type slot new
)
45 (let* ((slotinfo (get type
'*struct-slots
*))
46 (entry (assoc slot slotinfo
)))
48 (let ((new-entry (copy-list entry
)))
49 (setf (second new-entry
) new
)
50 (setf (get type
'*struct-slots
*)
51 (subst new-entry entry slotinfo
))))))
54 ;;**** This hash table based method may be slightly better for large
56 (let ((default-table (make-hash-table :test
'equal
))
57 (lookup-cell (cons nil nil
)))
59 (defun set-structure-slot-default (type slot new
)
60 (setf (gethash (cons type slot
) default-table
) new
))
62 (defun get-structure-slot-default (type slot
)
63 (setf (car lookup-cell
) type
)
64 (setf (cdr lookup-cell
) slot
)
65 (gethash lookup-cell default-table
)))
68 (defun default-structure-slot-value (type slot
)
69 (let ((init (get-structure-slot-default type slot
)))
70 (if init
(funcall init
))))
72 (defun install-sharp-s-constructor (structname f
)
74 (setf (get structname
'*struct-constructor
*) f
)
75 (let* ((symname (concatenate 'string
"MAKE-" (symbol-name structname
)))
76 (sym (make-symbol symname
)))
77 (setf (get structname
'*struct-constructor
*) sym
)
78 (setf (symbol-function sym
) f
))))
80 (defun install-structure-slots (structname include slots
)
81 (let* ((parent (first include
))
82 (parent-info (if parent
(get (first include
) '*struct-slots
*)))
83 (slotinfo (append parent-info slots
))
84 (overrides (rest include
)))
85 (setf (get structname
'*struct-slots
*) slotinfo
)
87 (set-structure-slot-default structname
(first s
) (second s
)))
89 (dolist (i parent-info
)
90 (let* ((name (structure-slotinfo-name i
))
91 (default (get-structure-slot-default parent name
)))
92 (set-structure-slot-default structname name default
)))
93 (dolist (new overrides
)
94 (set-structure-slot-default structname
(first new
)
99 ;;;; Slot Info Representation
102 (defun make-structure-slotinfo (name form readonly
) (list name form readonly
))
103 (defun structure-slotinfo-name (x) (first x
))
104 (defun structure-slotinfo-form (x) (second x
))
105 (defun structure-slotinfo-read-only (x) (third x
))
109 ;;;; Slot Name Comparison Function
112 (defun structure-slot-eql (x y
) (string= (symbol-name x
) (symbol-name y
)))
116 ;;;; Slot Option Extractors
119 (defun convert-structure-slot-options (slots)
120 (mapcar #'(lambda (x)
122 (make-structure-slotinfo (first x
)
124 (getf (rest (rest x
)) :read-only
))
125 (make-structure-slotinfo x nil nil
)))
128 (defun get-structure-parent-slotinfo (p)
129 (let ((si (get p
'*struct-slots
* 'none
)))
131 (let ((cmpinfo (cmp-get-slotinfo p
)))
132 (unless cmpinfo
(error "no slot info available for structure ~s" p
))
133 (copy-list (cdr cmpinfo
)))
136 (defun get-structure-slotinfo (include slots
)
137 (let ((parent (first include
)))
138 (append (if parent
(get-structure-parent-slotinfo parent
)) slots
)))
142 ;;;; Slot Option Expanders
145 (defun check-structure-slots (structspec slotspecs
)
146 (let* ((structname (if (consp structspec
) (first structspec
) structspec
))
147 (options (if (consp structspec
) (rest structspec
)))
148 (include (get-structure-include options
))
149 (parent (first include
))
150 (overrides (rest include
))
151 (owninfo (convert-structure-slot-options slotspecs
))
152 (incinfo (if parent
(get-structure-parent-slotinfo parent
)))
153 (info (append incinfo owninfo
)))
155 (structure-slot-eql (structure-slotinfo-name x
)
156 (structure-slotinfo-name y
))))
157 ;; check include slot options for existenc and consistent read-only state
158 (dolist (new overrides
)
159 (let ((old (find new incinfo
:test
#'same
)))
161 (error "no inherited slot named ~s"
162 (symbol-name (structure-slotinfo-name new
))))
163 (when (and (structure-slotinfo-read-only old
)
164 (not (structure-slotinfo-read-only new
)))
165 (error "inherited slot ~s must be read-only"
166 (structure-slotinfo-name new
)))))
167 ;; check slots for uniqueness
168 (dolist (own owninfo
)
169 (when (< 1 (count own info
:test
#'same
))
170 (error "only one slot named ~s allowed"
171 (symbol-name (structure-slotinfo-name own
))))))))
173 (defun make-structure-slot-forms (structname include slots
)
175 (let ((name (structure-slotinfo-name x
))
176 (form (structure-slotinfo-form x
))
177 (readonly (structure-slotinfo-read-only x
)))
178 `(list ',name
,(if form
`#'(lambda () ,form
)) ,readonly
))))
179 (let ((incname (first include
))
180 (incslots (mapcar #'fix-info
(rest include
)))
181 (ownslots (mapcar #'fix-info slots
)))
182 `(install-structure-slots ',structname
183 ,(if incname
`(list ',incname
,@incslots
))
184 ,(if ownslots
`(list ,@ownslots
))))))
186 (defun make-structure-slot-accessor-forms (conc-name slotinfo typed
)
189 (i (if (and typed
(not named
)) 0 1))
190 (ref-fun (if typed
'elt
'%struct-ref
)))
191 (dolist (sk slotinfo
)
192 (let* ((sn (structure-slotinfo-name sk
))
193 (name (intern (concatenate 'string conc-name
(symbol-name sn
))))
194 (ro (structure-slotinfo-read-only sk
)))
195 (push `(defun ,name
(x) (,ref-fun x
,i
)) forms
)
196 ;;**** change this to inlining later?
197 (push `(define-compiler-macro ,name
(x) (list ',ref-fun x
,i
)) forms
)
199 `(defsetf ,name
(x) (v) (error "slot ~s is read-only" ',sn
))
200 `(defsetf ,name
(x) (v)
202 `(list 'setf
(list 'elt x
,i
) v
)
203 `(list '%struct-set x
,i v
))))
206 (if forms
`(progn ,@(nreverse forms
)))))
210 ;;;; Structure Option Extractors
213 (defconstant *structure-options
*
214 '(:conc-name
:copier
:constructor
:include
:named
215 :print-function
:predicate
:type
))
217 (defun check-structure-specification (structspec)
218 (let ((structname (if (consp structspec
) (first structspec
) structspec
))
219 (options (if (consp structspec
) (rest structspec
))))
220 (unless (symbolp structname
) (error "bad structure name - ~s" structname
))
221 (flet ((check (x s
) (when x
(error "~a - ~s" s x
)))
222 (is-opt (x) (or (eq x
:named
) (consp x
)))
223 (optname (x) (if (symbolp x
) x
(first x
))))
224 (check (find-if-not #'is-opt options
) "bad structure option")
225 (check (find-if-not #'(lambda (x) (member x
*structure-options
*))
228 "unknown structure option")
229 (dolist (opt *structure-options
*)
230 (check (if (< 1 (count opt options
:key
#'optname
)) opt
)
231 "structure option used more than once")))))
233 (defun find-structure-option (name options
)
234 (find name options
:key
#'(lambda (x) (if (symbolp x
) x
(first x
)))))
236 (defun get-structure-option-symbol (name options optname s1 s2
)
237 (let ((option (find-structure-option optname options
)))
239 (let ((sym (second option
)))
240 (unless (symbolp sym
) (error "~s is not a symbol"))
242 (intern (concatenate 'string s1
(string name
) s2
)))))
244 (defun get-structure-conc-name (structname options
)
245 (let ((option (find-structure-option :conc-name options
)))
247 (let ((name (second option
)))
248 (if name
(string name
) ""))
249 (concatenate 'string
(symbol-name structname
) "-"))))
251 (defun get-structure-copier (structname options
)
252 (get-structure-option-symbol structname options
:copier
"COPY-" ""))
254 (defun get-structure-constructor (structname options
)
255 (let ((option (find-structure-option :constructor options
)))
258 ((null (second option
)) nil
)
259 ((consp (rest (rest option
))) (list (second option
) (third option
)))
261 (intern (concatenate 'string
"MAKE-" (symbol-name structname
))))))
263 (defun get-structure-include (options)
264 (let ((option (find-structure-option :include options
)))
266 (cons (second option
)
267 (convert-structure-slot-options (rest (rest option
)))))))
269 (defun get-structure-predicate (structname options
)
270 (get-structure-option-symbol structname options
:predicate
"" "-P"))
272 (defun get-structure-print-function (options)
273 (second (find-structure-option :print-function options
)))
275 (defun get-structure-type (options)
276 (let ((type (second (find-structure-option :type options
))))
277 (when type
(cons type
(find-structure-option :named options
)))))
281 ;;;; Structure Option Expanders
284 (defun make-structure-copier-form (copier)
285 (when copier
`(defun ,copier
(x) (%copy-struct x
))))
287 (defun make-structure-predicate-form (structname predicate type
)
288 (when (and predicate
(not type
))
290 (defun ,predicate
(x) (%struct-type-p
',structname x
))
291 (define-compiler-macro ,predicate
(x)
292 (list '%struct-type-p
'',structname x
)))))
294 (defun make-structure-print-function-form (structname printfun type
)
295 (if (and printfun
(not type
))
296 `(setf (get ',structname
'*struct-print-function
*)
297 ,(if (symbolp printfun
)
298 (list 'quote printfun
)
300 `(remprop ',structname
'*struct-print-function
*)))
303 (defun make-structure-constructor-form-body (structname slotnames tn
)
304 (let ((type (first tn
))
307 ((eq type
'list
) `(list ,@(if named
`(',structname
)) ,@slotnames
))
308 ((eq type
'vector
) `(vector ,@(if named
`(',structname
)) ,@slotnames
))
309 ((and (consp type
) (eq (first type
) 'vector
))
310 (let* ((slen (length slotnames
))
311 (n (if named
(+ slen
1) slen
))
312 (args (if named
`(',structname
,@slotnames
) slotnames
))
313 (etype (second type
)))
315 :element-type
',etype
316 :initial-contents
(list ,@args
))))
317 (t `(%make-struct
',structname
,@slotnames
)))))
319 (defun make-standard-structure-constructor-form (structname slotinfo tn
)
321 (slotnames (mapcar #'structure-slotinfo-name slotinfo
)))
322 (dolist (s slotnames
)
323 (push `(,s
(default-structure-slot-value ',structname
',s
)) alist
))
324 (when alist
(setf alist
`(&key
,@(nreverse alist
))))
326 ,(make-structure-constructor-form-body structname slotnames tn
))))
328 (defun fixup-structure-constructor-argform (name a
)
329 (flet ((new-form (a) `(,a
(default-structure-slot-value ',name
',a
))))
331 ((symbolp a
) (new-form a
))
332 ((and (consp a
) (null (rest a
)))
333 (let* ((syment (first a
))
334 (sym (if (symbolp syment
) syment
(second syment
))))
338 (defun remove-structure-constructor-slot (a slots
)
340 ((symbolp a
) (remove a slots
))
341 ((symbolp (first a
)) (remove (first a
) slots
))
342 ((consp (first a
)) (remove (second (first a
)) slots
))
345 (defun structure-constructor-arglist (name alist slots
)
346 (let ((new-alist nil
)
350 ((member a lambda-list-keywords
) (setf key a
))
352 (when (member key
'(&optional
&key
))
353 (setf a
(fixup-structure-constructor-argform name a
)))))
354 (setf slots
(remove-structure-constructor-slot a slots
))
357 (pushnew '&aux new-alist
)
359 (push (fixup-structure-constructor-argform name s
) new-alist
)))
360 (nreverse new-alist
)))
362 (defun make-boa-structure-constructor-form (structname slotinfo alist tn
)
363 (let* ((slots (mapcar #'structure-slotinfo-name slotinfo
))
364 (args (structure-constructor-arglist structname alist slots
)))
366 ,(make-structure-constructor-form-body structname slots tn
))))
368 (defun make-structure-constructor-form (structname slotinfo constructor tn
)
370 ((symbolp constructor
)
372 ,@(make-standard-structure-constructor-form structname slotinfo tn
)))
374 `(defun ,(first constructor
)
375 ,@(make-boa-structure-constructor-form structname
380 (defun make-sharp-s-structure-constructor-form (structname slotinfo tn
)
381 `(install-sharp-s-constructor
384 ,@(make-standard-structure-constructor-form structname slotinfo tn
))))
386 (defun make-structure-include-form (structname include
)
388 `(setf (get ',structname
'*struct-include
*) ',(first include
))))
395 (defmacro defstruct
(structspec &rest slotspecs
)
396 (check-structure-specification structspec
)
397 ;;**** drop doc string for now
398 (when (stringp (first slotspecs
)) (pop slotspecs
))
399 (check-structure-slots structspec slotspecs
)
400 (let* ((structname (if (consp structspec
) (first structspec
) structspec
))
401 (options (if (consp structspec
) (rest structspec
)))
402 (slots (convert-structure-slot-options slotspecs
))
403 (conc-name (get-structure-conc-name structname options
))
404 (copier (get-structure-copier structname options
))
405 (constructor (get-structure-constructor structname options
))
406 (include (get-structure-include options
))
407 (printfun (get-structure-print-function options
))
408 (predicate (get-structure-predicate structname options
))
409 (type (get-structure-type options
))
410 (slotinfo (get-structure-slotinfo include slots
)))
411 (flet ((list-if (x) (if x
(list x
))))
413 (eval-when (:compile-toplevel
)
414 (cmp-register-slotinfo ',structname
',slotinfo
))
415 ,(make-structure-slot-forms structname include slots
)
416 ,@(list-if (make-structure-slot-accessor-forms conc-name
419 ,@(list-if (make-structure-copier-form copier
))
420 ,@(list-if (make-structure-predicate-form structname predicate type
))
421 ,@(list-if (make-structure-print-function-form structname
424 ,@(list-if (make-structure-constructor-form structname
428 ,(make-sharp-s-structure-constructor-form structname slotinfo type
)
429 ,@(list-if (make-structure-include-form structname include
))