File: extensions.lisp

package info (click to toggle)
gcl 2.6.7%2Bdfsga-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 84,796 kB
  • sloc: ansic: 452,686; lisp: 156,133; asm: 111,405; sh: 29,299; cpp: 18,599; perl: 5,602; makefile: 5,201; tcl: 3,181; sed: 469; yacc: 378; lex: 174; fortran: 48; awk: 30; csh: 23
file content (496 lines) | stat: -rw-r--r-- 22,421 bytes parent folder | download | duplicates (14)
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
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp; -*-

;;;
;;; *************************************************************************
;;;
;;;   File: extensions.lisp.
;;;
;;;     by Trent E. Lange, Effective Date 04-23-92
;;;
;;;
;;;  This file contains a small set of useful extensions to PCL. 
;;;
;;; Permission is granted to any individual or institution to use, copy,
;;; modify and distribute this document.
;;;
;;; Suggestions, bugs, criticism and questions to lange@cs.ucla.edu
;;; *************************************************************************
;;;

(in-package 'pcl)

(eval-when (compile load eval)

(defvar *extensions-exports*
        '(set-standard-instance-access
          set-funcallable-instance-access

          funcallable-instance-slot-value
          set-funcallable-instance-slot-value
          funcallable-instance-slot-boundp
          standard-instance-slot-value
          set-standard-instance-slot-value
          standard-instance-slot-boundp
          structure-instance-slot-value
          set-structure-instance-slot-value
          structure-instance-slot-boundp

          #+pcl-user-instances
          user-instance-slot-value
          #+pcl-user-instances
          set-user-instance-slot-value
          #+pcl-user-instances
          user-instance-slot-boundp

          with-optimized-slots
          with-standard-instance-slots

          method-needs-next-methods-p
          map-all-classes
          finalize-all-classes

          updater
          record-updater))
)

(defclass updater ()
  ((dependent :initarg :dependent :reader dependent)))

(defun record-updater (class dependee dependent &rest initargs)
  (let ((updater
         (apply #'make-instance class :dependent dependent initargs)))
    (add-dependent dependee updater)
    updater))


(defun finalize-all-classes (&optional (root-name 't))
  "Makes sure that all classes are finalized.  If Root-Name is supplied,
   then finalizes Root-Name and all of its subclasses and their subclasses."
  (map-all-classes #'(lambda (class)
                       (unless (class-finalized-p class)
                         (finalize-inheritance class)))
                   root-name))


;;;
;;;
;;;


(defmacro slot-value-from-index (instance wrapper slot-name slots index)
  "Returns instance's slot-value given slot-name's index."
  (once-only (index)
    `(if ,index
         (let ((val (%svref ,slots ,index)))
           (if (eq val ',*slot-unbound*)
               (slot-unbound (wrapper-class ,wrapper) ,instance ,slot-name)
             val))
         (if *safe-to-use-slot-value-wrapper-optimizations-p*
             (get-class-slot-value-1 ,instance ,wrapper ,slot-name)
             (accessor-slot-value ,instance ,slot-name)))))

(defmacro set-slot-value-from-index
          (instance wrapper slot-name slots index new-value)
  "Sets instance's slot-value to new-value given slot-name's index."
  (once-only (index)
    `(if ,index
          (setf (%svref ,slots ,index) ,new-value)
          (if *safe-to-use-set-slot-value-wrapper-optimizations-p*
              (set-class-slot-value-1 ,instance ,wrapper ,slot-name ,new-value)
              (setf (accessor-slot-value ,instance ,slot-name) ,new-value)))))

(defsetf slot-value-from-index set-slot-value-from-index)

(defmacro with-slots-slot-value-from-index
          (instance wrapper slot-name slots index variable-instance)
  "Returns instance's slot-value given slot-name's index."
  (cond
   ((consp wrapper)
    `(let ((wrapper ,wrapper))
       (unless (eq (wrapper-state wrapper) 't)
         (setf wrapper (wrapper-state-trap wrapper ,instance)))
       (with-slots-slot-value-from-index
         ,instance wrapper ,slot-name ,slots ,index ,variable-instance)))
   (variable-instance
    `(let ((,instance ,variable-instance))
       (with-slots-slot-value-from-index
         ,instance ,wrapper ,slot-name ,slots ,index NIL)))
   (T `(slot-value-from-index ,instance ,wrapper ,slot-name ,slots ,index))))

(defmacro set-with-slots-slot-value-from-index
          (instance wrapper slot-name slots index variable-instance new-value)
  "Sets instance's slot-value to new-value given slot-name's index."
  (cond
   ((consp wrapper)
    `(let ((wrapper ,wrapper))
       (unless (eq (wrapper-state wrapper) 't)
         (setf wrapper (wrapper-state-trap wrapper ,instance)))
       (set-with-slots-slot-value-from-index
         ,instance wrapper ,slot-name ,slots ,index ,variable-instance
         ,new-value)))
   (variable-instance
    `(let ((,instance ,variable-instance))
       (set-with-slot-slots-value-from-index
         ,instance ,wrapper ,slot-name ,slots ,index NIL ,new-value)))
   (T
    `(setf (slot-value-from-index ,instance ,wrapper ,slot-name ,slots ,index)
           ,new-value))))

(defsetf with-slots-slot-value-from-index
         set-with-slots-slot-value-from-index)

(defmacro with-slots-slot-value-from-wrapper-and-slots
    (instance slot-name wrapper slots-layout slots variable-instance)
  (cond
   (variable-instance
    `(let ((,instance ,variable-instance))
       (with-slots-slot-value-from-wrapper-and-slots
         ,instance ,slot-name ,wrapper ,slots-layout ,slots NIL)))
   ((consp wrapper)
    `(if *safe-to-use-slot-value-wrapper-optimizations-p*
         (let ((wrapper ,wrapper))
           (unless (eq (wrapper-state wrapper) 't)
             (setf wrapper (wrapper-state-trap wrapper ,instance)))
           (slot-value-from-wrapper-and-slots ,instance ,slot-name
             wrapper ,slots-layout ,slots NIL))
         (accessor-slot-value ,instance ,slot-name)))
   (T
    `(if *safe-to-use-slot-value-wrapper-optimizations-p*
         (slot-value-from-wrapper-and-slots
           ,instance ,slot-name ,wrapper ,slots-layout ,slots NIL)
         (accessor-slot-value ,instance ,slot-name)))))

(defmacro set-with-slots-slot-value-from-wrapper-and-slots
    (instance slot-name wrapper slots-layout slots variable-instance new-value)
  (cond
   (variable-instance
    `(let ((,instance ,variable-instance))
       (set-with-slots-slot-value-from-wrapper-and-slots
         ,instance ,slot-name ,wrapper ,slots-layout ,slots NIL ,new-value)))
   ((consp wrapper)
    `(if *safe-to-use-set-slot-value-wrapper-optimizations-p*
         (let ((wrapper ,wrapper))
           (unless (eq (wrapper-state wrapper) 't)
             (setf wrapper (wrapper-state-trap wrapper ,instance)))
           (setf (slot-value-from-wrapper-and-slots ,instance ,slot-name
                    wrapper ,slots-layout ,slots NIL)
                 ,new-value))
         (setf (accessor-slot-value ,instance ,slot-name) ,new-value)))
   (T
    `(if *safe-to-use-set-slot-value-wrapper-optimizations-p*
         (setf (slot-value-from-wrapper-and-slots
                 ,instance ,slot-name ,wrapper ,slots-layout ,slots NIL)
               ,new-value)
         (setf (accessor-slot-value ,instance ,slot-name) ,new-value)))))

(defsetf with-slots-slot-value-from-wrapper-and-slots
         set-with-slots-slot-value-from-wrapper-and-slots)

(defun tree-memq-p (item form)
  (cond ((consp form)
         (or (tree-memq-p item (car form))
             (tree-memq-p item (cdr form))))
        (T (eq item form)))) 

(defmacro with-optimized-slots (slot-entries instance-form &body body)
  "Optimized version of With-Slots that is faster because it factors out
   functions common to all slot accesses on the instance.  It has two
   extensions to With-Slots: (1) the second value of slot-entries are
   evaluated as forms rather than considered to be hard slot-names, allowing
   access of variable slot-names.  (2) if a :variable-instance keyword is
   the first part of the body, then the instance-form is treated as a variable
   form, which is always expected to return an instance of the same class.
   The value of the keyword must be an instance that is the same class as
   instance-form will always return."
  ;;  E.g. (with-optimized-slots (foo-slot
  ;;                         (foo-slot-accessor     'foo-slot)
  ;;                         (variable-slot-accessor variable-slot))
  ;;                        instance
  ;;                        :instance-form (car instances-of-same-class)
  ;;         (loop for instance in objects-of-same-class
  ;;               as  variable-slot in variable-slots
  ;;               collect (list foo-slot
  ;;                             foo-slot-accessor
  ;;                             variable-slot-accessor)))
  ;;   ==> (loop for instance in objects-of-same-class
  ;;             as  variable-slot in variable-slots
  ;;             collect (list (slot-value instance 'foo-slot)
  ;;                           (slot-value instance 'foo-slot)
  ;;                           (slot-value instance variable-slot)))
  (build-with-optimized-slots-form slot-entries instance-form body))

(defmacro with-standard-instance-slots (slot-entries instance-form &body body)
  "Optimized version of With-Slots that assumes that the instance-form
   evaluates to a standard-instance.  The result is undefined if it does not.
   With-standard-instance-slots is faster than With-Slots because it factors
   out functions common to all slot accesses on the instance.  It has two
   extensions to With-Slots: (1) the second value of slot-entries are
   evaluated as forms rather than considered to be hard slot-names, allowing
   access of variable slot-names.  (2) if a :variable-instance keyword is
   the first part of the body, then the instance-form is treated as a variable
   form, which is always expected to return an instance of the same class.
   The value of the keyword must be an instance that is the same class as
   instance-form will always return."
  (build-with-optimized-slots-form slot-entries instance-form body 'std-instance))

(defun build-with-optimized-slots-form (slot-entries instance-form body
                                        &optional instance-type)
  (let* ((variable-instance
           (if (eq (car body) :variable-instance)
               (prog1
                 (cadr body)
                 (setf body (cddr body)))))
         (hard-accessors
           (let ((collect NIL))
             (dolist (slot-entry slot-entries (nreverse collect))
               (when (and (symbolp slot-entry)
                          (tree-memq-p slot-entry body))
                 (push (cons slot-entry slot-entry) collect))
               (when (and (consp slot-entry)
                          (constantp   (second slot-entry))
                          (tree-memq-p (car slot-entry) body))
                 (push (cons (car slot-entry) (second (second slot-entry)))
                       collect)))))
         (variable-accessors
           (let ((collect NIL))
             (dolist (slot-entry slot-entries (nreverse collect))
               (when (and (consp slot-entry)
                          (not (constantp (second slot-entry)))
                          (tree-memq-p (car slot-entry) body))
                 (push slot-entry collect))))))
    (if *safe-to-use-slot-wrapper-optimizations-p*
        (build-maybe-safe-w-o-s-v hard-accessors variable-accessors
                                  instance-form body variable-instance
                                  instance-type)
        (build-with-accessor-s-v  hard-accessors variable-accessors
                                  instance-form body variable-instance))))

(defun build-maybe-safe-w-o-s-v (hard-accessors variable-accessors
                                 instance-form body variable-instance
                                 instance-type)
  (let* ((instance-string
           (if (symbolp instance-form) (symbol-name instance-form) ""))
         (instance-form-var
           (if (and variable-instance (simple-eval-access-p instance-form))
               instance-form
             (gensym
               (concatenate 'simple-string instance-string "-INSTANCE-FORM"))))
         (prototype-form
           (if variable-instance
               (if (simple-eval-access-p variable-instance)
                   variable-instance
                 (gensym (concatenate 'simple-string "VARIABLE-INSTANCE"
                                                     instance-string)))
               instance-form-var))
         (wrapper-var
           (gensym (concatenate 'simple-string instance-string "-WRAPPER")))
         (slots-var
           (unless variable-instance
             (gensym (concatenate 'simple-string instance-string "-SLOTS"))))
         (type-var
           (when (and variable-instance (not instance-type))
             (gensym (concatenate 'simple-string instance-string "-TYPE"))))
         (type-var-std 1)
         (type-var-fsc 2)
         #+pcl-user-instances
         (type-var-user 3)
         (slot-index-vars
           (mapcar #'(lambda (slot-entry)
                         (list (car slot-entry)
                               (cdr slot-entry)
                               (gensym (concatenate
                                         'simple-string
                                         (if (string= instance-string "")
                                             "INSTANCE-FORM-"
                                           instance-string)
                                         (symbol-name (cdr slot-entry))
                                         "-INDEX"))))
                   (remove-duplicates hard-accessors :key #'cdr)))
         (slots-layout-var
           (gensym (concatenate 'simple-string "SLOTS-LAYOUT-" instance-string)))
         (runtime-slots-form
           (if variable-instance
               (ecase instance-type
                 (std-instance  `(std-instance-slots ,instance-form-var))
                 (fsc-instance  `(fsc-instance-slots ,instance-form-var))
                 #+pcl-user-instances
                 (user-instance `(get-user-instance-slots ,instance-form-var))
                 ((nil)
                  `(case ,type-var
                     (,type-var-std  (std-instance-slots ,instance-form-var))
                     (,type-var-fsc  (fsc-instance-slots ,instance-form-var))
                     #+pcl-user-instances
                     (,type-var-user (get-user-instance-slots ,instance-form-var)))))
                slots-var))
         (runtime-wrapper-form
           (if variable-instance
               (ecase instance-type
                 (std-instance  `(std-instance-wrapper ,instance-form-var))
                 (fsc-instance  `(fsc-instance-wrapper ,instance-form-var))
                 #+pcl-user-instances
                 (user-instance `(get-user-instance-wrapper ,instance-form-var))
                 ((nil)
                  `(case ,type-var
                     (,type-var-std  (std-instance-wrapper ,instance-form-var))
                     (,type-var-fsc  (fsc-instance-wrapper ,instance-form-var))
                     #+pcl-user-instances
                     (,type-var-user (get-user-instance-wrapper ,instance-form-var)))))
               wrapper-var)))
    (declare (type simple-string instance-string)
             (type list          slot-index-vars))
    `(let (,@(unless variable-instance
              `((,instance-form-var ,instance-form)))
           ,@(when (and variable-instance
                        (not (eq prototype-form variable-instance)))
               `((,prototype-form ,variable-instance)))
           ,wrapper-var ,slots-layout-var
           ,@(if variable-instance
                 (if type-var `((type-var 0)))
                 (list slots-var))
           ,@(mapcar #'third slot-index-vars))
       ,@(when type-var `((declare (type index ,type-var))))
       (when *safe-to-use-slot-wrapper-optimizations-p*
         ,@(ecase instance-type
             (std-instance
              `((setf ,wrapper-var (std-instance-wrapper ,prototype-form))
                ,@(unless variable-instance
                   `((setf ,slots-var (std-instance-slots ,prototype-form))))))
             (fsc-instance
              `((setf ,wrapper-var (fsc-instance-wrapper ,prototype-form))
                ,@(unless variable-instance
                   `((setf ,slots-var (fsc-instance-slots ,prototype-form))))))
             #+pcl-user-instances
             (user-instance
              `((setf ,wrapper-var (get-user-instance-wrapper ,prototype-form))
                ,@(unless variable-instance
                   `((setf ,slots-var (get-user-instance-slots ,prototype-form))))))
             ((nil)
             `((cond
                ((std-instance-p ,prototype-form)
                 (setf ,wrapper-var (std-instance-wrapper ,prototype-form))
                 ,(if variable-instance
                      `(setf ,type-var ,type-var-std)
                    `(setf ,slots-var (std-instance-slots ,prototype-form))))
                ((fsc-instance-p ,prototype-form)
                 (setf ,wrapper-var (fsc-instance-wrapper ,prototype-form))
                 ,(if variable-instance
                      `(setf ,type-var ,type-var-fsc)
                    `(setf ,slots-var (fsc-instance-slots ,prototype-form))))
                #+pcl-user-instances
                ((get-user-instance-p ,prototype-form)
                 (setf ,wrapper-var (get-user-instance-wrapper ,prototype-form))
                 ,(if variable-instance
                      `(setf ,type-var ,type-var-user)
                    `(setf ,slots-var (get-user-instance-slots ,prototype-form))))))))
         ,@(if instance-type
               (build-w-s-v-find-slot-indices wrapper-var slots-layout-var
                  prototype-form slot-index-vars)
               `((when ,wrapper-var
                  ,@(build-w-s-v-find-slot-indices wrapper-var slots-layout-var
                       prototype-form slot-index-vars)))))
       (symbol-macrolet
         (,@(mapcar
              #'(lambda (slot-cons)
                  `(,(car slot-cons)
                     (with-slots-slot-value-from-index
                        ,instance-form-var
                        ,runtime-wrapper-form
                        ',(cdr slot-cons)
                        ,runtime-slots-form
                        ,(third (assoc (car slot-cons) slot-index-vars
                                       :test #'eq))
                        ,(when (and variable-instance
                                    (not (eq variable-instance
                                             instance-form-var)))
                           variable-instance))))
              hard-accessors)
          ,@(mapcar
              #'(lambda (variable-cons)
                  `(,(car variable-cons)
                    (with-slots-slot-value-from-wrapper-and-slots
                      ,instance-form-var
                      ,(second variable-cons)
                      ,runtime-wrapper-form
                      ,slots-layout-var
                      ,runtime-slots-form
                      ,(when (and variable-instance
                                  (not (eq variable-instance
                                           instance-form-var)))
                         variable-instance))))
              variable-accessors))
         ,@body))))

(defun build-w-s-v-find-slot-indices (wrapper-var slots-layout-var
                                      prototype-form
                                      slot-index-vars)
  (declare (type list slot-index-vars))
  `((unless (eq (wrapper-state ,wrapper-var) 't)
      (setf ,wrapper-var
            (wrapper-state-trap ,wrapper-var ,prototype-form)))
    (setf ,slots-layout-var (wrapper-instance-slots-layout ,wrapper-var))
    ,@(if (<= (length slot-index-vars) 2)
          (mapcar
            #'(lambda (slot-cons)
                `(setf ,(third slot-cons)
                       (instance-slot-index-from-slots-layout
                         ,slots-layout-var ',(second slot-cons))))
            slot-index-vars)
          ;; More than two slots, so more efficient to search slots-layout-var
          ;; only once, rather than once for each with instance-slot-index.
          (labels
            ((build-comps (slot-vars index)
               (if slot-vars
                   `(if (eq slot-name ',(second (car slot-vars)))
                        (progn
                          (setf ,(third (car slot-vars)) ,index)
                          (if (= matches ,(1- (length slot-index-vars)))
                              (go end-loop)
                            (setf matches (the fixnum (1+ matches)))))
                     ,(build-comps (cdr slot-vars) index)))))
            `((block nil
                (let ((slots-left ,slots-layout-var)
                      (slot-name  NIL)
                      (index      0)
                      (matches    0))
                  (declare (type fixnum index matches))
                  (when slots-left
                    (tagbody
                      begin-instance-slots-loop
                        (setf slot-name (car slots-left))
                        ,(build-comps slot-index-vars 'index)
                        (setf index (the fixnum (1+ index)))
                        (if (null (setf slots-left (cdr slots-left)))
                            (go end-loop))
                        (go begin-instance-slots-loop)
                      end-loop)))))))))

(defun build-with-accessor-s-v (hard-accessors variable-accessors
                                instance-form body variable-instance)
  ;; Build the body for with-optimized-slot-value when it is unsafe
  ;; and accessor-slot-value must be used.
  (let ((instance-form-var
          (if variable-instance instance-form (gensym "INSTANCE-FORM"))))
  `(let (,@(unless variable-instance
            `((,instance-form-var ,instance-form))))
     (symbol-macrolet
       (,@(mapcar
            #'(lambda (slot-cons)
                `(,(car slot-cons)
                  (accessor-slot-value ,instance-form-var
                                       ',(cdr slot-cons))))
            hard-accessors)
        ,@(mapcar
            #'(lambda (variable-cons)
                `(,(car variable-cons)
                  (accessor-slot-value ,instance-form-var
                                       ,(second variable-cons))))
            variable-accessors))
       ,@body))))


#-(or KCL IBCL)
(export *extensions-exports* *the-pcl-package*)

#+(or KCL IBCL)
(mapc 'export (list *extensions-exports*) (list *the-pcl-package*))