Ticket #32: 3.scm

File 3.scm, 61.4 KB (added by Tony Sidaway, 16 years ago)

Trimmed version of version 3 egg, for comparison using diff

Line 
1; From '@' by Dan Muresan
2(define-syntax slot@
3 (syntax-rules (=)
4 ((_ o) o)
5 ((_ o slot = v) (slot-set! o 'slot v))
6 ((_ o slot . slots) (slot@ (slot-ref o 'slot) . slots))))
7
8(define-syntax define-class
9 (syntax-rules ()
10 [(_ name () slots)
11 (define-class name (<object>) slots) ]
12 [(_ name supers slots)
13 (define-class name supers slots <class>) ]
14 [(_ name () slots meta)
15 (define-class name (<object>) slots meta) ]
16 [(_ cname (supers ...) (slots ...) meta)
17 (define cname (make meta 'name 'cname 'direct-supers (list supers ...) 'direct-slots (list 'slots ...))) ] ) )
18
19(define-syntax define-generic
20 (syntax-rules ()
21 [(_ n class) (define n (make class 'name 'n))]
22 [(_ n) (define n (make-generic 'n))] ) )
23
24(define-macro (define-method head . body)
25 (##sys#check-syntax 'define-method head '(symbol . _))
26 (##sys#check-syntax 'define-method body '#(_ 1))
27 (let gather ([args (##sys#slot head 1)]
28 [specs '()]
29 [vars '()] )
30 (if (or (not (pair? args))
31 (memq (car args) '(#!optional #!key #!rest)) )
32 (let ([name (##sys#slot head 0)])
33 `(add-method
34 ,name
35 (make-method
36 (list ,@(reverse specs))
37 (lambda (call-next-method ,@(reverse vars) ,@args) ,@body) ) ) )
38 (let ([arg (##sys#slot args 0)])
39 (gather (##sys#slot args 1)
40 (cons (if (pair? arg) (cadr arg) '<top>) specs)
41 (cons (if (pair? arg) (car arg) arg) vars) ) ) ) ) )
42
43;; For system use in extending the set of "builtin" classes.
44
45(define-for-syntax (##tinyclos#make-classname-symbol str)
46 (string->symbol (string-append "<" str ">")) )
47
48(define-macro (define-primitive-class name pred . sclasses)
49 `(define ,(##tinyclos#make-classname-symbol name)
50 (##tinyclos#new-primitive-class ,name ,pred ,@sclasses)) )
51
52(define-macro (define-structure-class name tag)
53 `(define ,(##tinyclos#make-classname-symbol name)
54 (##tinyclos#new-structure-class name ',tag)) )
55
56(define-macro (define-tagged-pointer-class name tag)
57 `(define ,(##tinyclos#make-classname-symbol name)
58 (##tinyclos#new-tagged-pointer-class name ',tag)) )
59
60(define-macro (define-extended-procedure-class name pred)
61 `(define ,(##tinyclos#make-classname-symbol name)
62 (##tinyclos#new-extended-procedure-class name pred)) )
63
64;
65; A very simple CLOS-like language, embedded in Scheme, with a simple
66; MOP. The features of the default base language are:
67;
68; * Classes, with instance slots, but no slot options.
69; * Multiple-inheritance.
70; * Generic functions with multi-methods and class specializers only.
71; * Primary methods and call-next-method; no other method combination.
72; * Uses Scheme's lexical scoping facilities as the class and generic
73; function naming mechanism. Another way of saying this is that
74; class, generic function and methods are first-class (meta)objects.
75;
76; While the MOP is simple, it is essentially equal in power to both MOPs
77; in AMOP. This implementation is not at all optimized, but the MOP is
78; designed so that it can be optimized. In fact, this MOP allows better
79; optimization of slot access extenstions than those in AMOP.
80;
81;
82;
83; In addition to calling a generic, the entry points to the default base
84; language are:
85;
86; (MAKE-CLASS list-of-superclasses list-of-slot-names)
87; (MAKE-GENERIC)
88; (MAKE-METHOD list-of-specializers procedure)
89; (ADD-METHOD generic method)
90;
91; (MAKE class . initargs)
92; (INITIALIZE instance initargs) ;Add methods to this,
93; ;don't call it directly.
94;
95; (SLOT-REF object slot-name)
96; (SLOT-SET! object slot-name new-value)
97;
98;
99; So, for example, one might do (but only within this file! 'getl' is not exported):
100;
101; (define <position> (make-class (list <object>) (list 'x 'y)))
102; (add-method initialize
103; (make-method (list <position>)
104; (lambda (call-next-method pos initargs)
105; (for-each (lambda (initarg-name slot-name)
106; (slot-set! pos slot-name (getl initargs initarg-name 0)))
107; '(x y)
108; '(x y)))))
109;
110; (set! p1 (make <position> 'x 1 'y 3))
111;
112;
113;
114; NOTE! Do not use EQUAL? to compare objects! Use EQ? or some hand
115; written procedure. Objects have a pointer to their class,
116; and classes are circular structures, and ...
117;
118;
119;
120; The introspective part of the MOP looks like the following. Note that
121; these are ordinary procedures, not generics.
122;
123; CLASS-OF
124;
125; CLASS-DIRECT-SUPERS
126; CLASS-DIRECT-SLOTS
127; CLASS-CPL
128; CLASS-SLOTS
129;
130; GENERIC-METHODS
131;
132; METHOD-SPECIALIZERS
133; METHOD-PROCEDURE
134;
135;
136; The intercessory protocol looks like (generics in uppercase):
137;
138; make
139; ALLOCATE-INSTANCE
140; INITIALIZE (really a base-level generic)
141;
142; class initialization
143; COMPUTE-CPL
144; COMPUTE-SLOTS
145; COMPUTE-GETTER-AND-SETTER
146;
147; add-method (Notice this is not a generic!)
148; COMPUTE-APPLY-GENERIC
149; COMPUTE-METHODS
150; COMPUTE-METHOD-MORE-SPECIFIC?
151; COMPUTE-APPLY-METHODS
152;
153
154;;; Aliases for sys stuff
155
156(define-inline (%car p) (##sys#slot p 0))
157(define-inline (%cdr p) (##sys#slot p 1))
158(define-inline (%cadr p) (%car (%cdr p)))
159(define-inline (%cddr p) (%cdr (%cdr p)))
160(define-inline (%set-car! p y) (##sys#setslot p 0 y))
161(define-inline (%set-cdr! p y) (##sys#setslot p 1 y))
162
163(define-inline (%string-length s) (##sys#size s))
164
165(define-inline (%vector-ref v i) (##sys#slot v i))
166(define-inline (%vector-set! v i x) (##sys#setslot v i x))
167(define-inline (%vector-length v) (##sys#size v))
168
169(define-macro (%structure? ?x . ?t)
170 (if (null? ?t) `(##sys#generic-structure? ,?x) `(##sys#structure? ,?x ,(car ?t))))
171(define-inline (%structure-ref r i) (##sys#slot r i))
172(define-inline (%structure-set! r i x) (##sys#setslot r i x))
173(define-inline (%structure-length r) (##sys#size r))
174(define-inline (%structure-tag r) (##sys#slot r 0))
175
176(define-inline (%closure-ref c i) (##sys#slot c i))
177(define-inline (%closure-length c) (##sys#size c))
178
179(define-inline (%tagged-pointer-data p) (##sys#slot p 1))
180
181(define-inline (%null-pointer? p) (##sys#null-pointer? p))
182
183(define-inline (%blob? x) (##sys#bytevector? x))
184
185(define-inline (%immediate-value? x) (##sys#immediate? x))
186
187(define-inline (%undefined? x) (##core#inline "C_undefinedp" x))
188
189(define-inline (%unbound? x) (eq? x (##sys#slot '##sys#arbitrary-unbound-symbol 0)))
190
191(define-inline (%pointer? x) (##core#inline "C_pointerp" x))
192
193(define-inline (%tagged-pointer? x) (##core#inline "C_taggedpointerp" x))
194
195(define-inline (%swig-pointer? x) (##core#inline "C_swigpointerp" x))
196
197(define-inline (%locative? x) (##core#inline "C_locativep" x))
198
199(define-inline (%random-fixnum x) (##core#inline "C_random_fixnum" x))
200
201;;; Support code
202
203(define-macro (define-unique-object ?ident)
204 `(define ,?ident (gensym)) )
205
206(define (filter-in f l)
207 (let loop ([l l])
208 (cond [(null? l) '()]
209 [else
210 (let ([h (%car l )]
211 [r (%cdr l)] )
212 (if (f h)
213 ; Unlike SRFI-1 filter this doesn't share the undeleted longest tail
214 #;(let ([t (loop r)]) (if (eq? r t) l (cons h t)))
215 (cons h (loop r))
216 (loop r) ) ) ] ) ) )
217
218(define fast-getl
219 (foreign-lambda* scheme-object ((scheme-object initargs) (scheme-object name) (scheme-object def)) "
220 while(initargs != C_SCHEME_END_OF_LIST) {
221 if(name == C_block_item(initargs, 0)) {
222 if((initargs = C_block_item(initargs, 1)) == C_SCHEME_END_OF_LIST) return(def);
223 else return(C_block_item(initargs, 0));
224 }
225 initargs = C_block_item(initargs, 1);
226 }
227 return(def);") )
228
229(define-unique-object not-found-object)
230
231(define (getl initargs name . def)
232 (let ([value (fast-getl initargs name not-found-object)])
233 (if (eq? value not-found-object)
234 (:optional def (##sys#error 'getl "cannot find item" name initargs))
235 value ) ) )
236
237;
238; A simple topological sort.
239;
240; This is a fairly modified version of code I originally got from Anurag
241; Mendhekar <anurag@moose.cs.indiana.edu>.
242;
243
244(define (compute-std-cpl c get-direct-supers)
245
246 (define-inline (every1 test lst)
247 (let loop ([lst lst])
248 (or (null? lst)
249 (and (test (%car lst))
250 (loop (%cdr lst)) ) ) ) )
251
252 (define (top-sort elements constraints tie-breaker)
253 (let loop ((elements elements)
254 (constraints constraints)
255 (result '()))
256 (if (null? elements)
257 result
258 (let ((can-go-in-now
259 (filter-in
260 (lambda (x)
261 (every1 (lambda (constraint)
262 (or (not (eq? (%cadr constraint) x))
263 (memq (%car constraint) result)))
264 constraints))
265 elements)))
266 (if (null? can-go-in-now)
267 (##sys#error 'top-sort "invalid constraints")
268 (let ((choice (if (null? (%cdr can-go-in-now))
269 (%car can-go-in-now)
270 (tie-breaker result can-go-in-now))))
271 (loop (filter-in (lambda (x) (not (eq? x choice))) elements)
272 ; Include all constraints
273 #;(filter-in (lambda (x) (not (eq? (%cadr x) choice))) constraints)
274 constraints
275 (append result (list choice))))))) ) )
276
277 (define (std-tie-breaker get-supers)
278 (lambda (partial-cpl min-elts)
279 (let loop ((pcpl (reverse partial-cpl)))
280 (let* ((current-elt (%car pcpl))
281 (ds-of-ce (get-supers current-elt))
282 (common (filter-in (cut memq <> ds-of-ce) min-elts)))
283 (if (null? common)
284 (let ([r (%cdr pcpl)])
285 (if (null? r)
286 (##sys#error 'std-tie-breaker "nothing valid")
287 (loop r)) )
288 (%car common)) ) ) ) )
289
290 (define (build-transitive-closure get-follow-ons)
291 (lambda (x)
292 (let track ((result '())
293 (pending (list x)))
294 (if (null? pending)
295 result
296 (let ((next (%car pending)))
297 (if (memq next result)
298 (track result (%cdr pending))
299 (track (cons next result)
300 (append (get-follow-ons next)
301 (%cdr pending)))))) ) ) )
302
303 (define (build-constraints get-follow-ons)
304 (lambda (x)
305 (let loop ((elements ((build-transitive-closure get-follow-ons) x))
306 (this-one '())
307 (result '()))
308 (if (or (null? this-one) (null? (%cdr this-one)))
309 (if (null? elements)
310 result
311 (loop (%cdr elements)
312 (cons (%car elements)
313 (get-follow-ons (%car elements)))
314 result))
315 (loop elements
316 (%cdr this-one)
317 (cons (list (%car this-one) (%cadr this-one))
318 result))) ) ) )
319
320 (top-sort ((build-transitive-closure get-direct-supers) c)
321 ((build-constraints get-direct-supers) c)
322 (std-tie-breaker get-direct-supers) ) )
323
324;;; Method cache support code:
325
326#>
327#define C_METHOD_CACHE_SIZE 8
328<#
329
330(define-foreign-variable method-cache-size int "C_METHOD_CACHE_SIZE")
331
332(define method-caching-enabled #f)
333(define method-cache-tag #f)
334
335(define (make-method-cache)
336 (cons method-cache-tag (make-vector (arithmetic-shift method-cache-size 1) #f)) )
337
338(define method-cache-lookup
339 (foreign-lambda* scheme-object ((scheme-object mcache) (scheme-object hash) (scheme-object classes)) "
340 C_word v = C_block_item(mcache, 1);
341 C_word clist, x, y;
342 int free_index = -1;
343 int i = ((C_unfix(hash) & (C_METHOD_CACHE_SIZE - 1)) << 1) & 0xffff,
344 i0, i2;
345 for(i0 = i;; i = i2) {
346 clist = C_block_item(v, i);
347 if(clist != C_SCHEME_FALSE) {
348 x = classes;
349 y = clist;
350 while(x != C_SCHEME_END_OF_LIST && y != C_SCHEME_END_OF_LIST) {
351 if(C_block_item(x, 0) != C_block_item(y, 0)) goto mismatch;
352 else {
353 x = C_block_item(x, 1);
354 y = C_block_item(y, 1);
355 }
356 }
357 if (x == C_SCHEME_END_OF_LIST && y == C_SCHEME_END_OF_LIST)
358 return(C_block_item(v, i + 1));
359 else
360 goto mismatch;
361 }
362 else if(free_index == -1) free_index = i;
363 mismatch:
364 i2 = (i + 2) & ((C_METHOD_CACHE_SIZE << 1) - 1);
365 if(i2 == i0) return(free_index == -1 ? C_SCHEME_FALSE : C_fix(free_index));
366 }") )
367
368;
369; Then, we need to build what, in a more real implementation, would be
370; the interface to the memory subsystem: instances and entities. The
371; former are used for instances of instances of <class>; the latter
372; are used for instances of instances of <entity-class>. In this MOP,
373; none of this is visible to base- or MOP-level programmers.
374;
375; (One might consider rewriting the definition of instances using
376; define-record or something. It doesn't turn out to make it much
377; simpler, at least not to me. It also breaks the nice parallelism
378; with entities.)
379;
380; %allocate-instance, %allocate-entity, get-field, set-field!, and class-of are
381; the normal interface, from the rest of the code, to the low-level memory
382; system.
383;
384; One thing to take note of is that the protocol does not allow the user
385; to add low-level instance representations. I have never seen a way to make
386; that work.
387;
388
389; Instance
390
391(define the-slots-of-a-class ;
392 '(direct-supers ;(class ...)
393 direct-slots ;((name . options) ...)
394 cpl ;(class ...)
395 slots ;((name . options) ...)
396 nfields ;an integer
397 field-initializers ;(proc ...)
398 getters-n-setters ;((slot-name getter . setter) ...)
399 name) ) ;name
400
401; structure: tag class cache direct-supers ... name
402(define-constant basic-class-instance-size 11)
403
404(define (%allocate-instance class nfields)
405 (let ((instance (make-vector (+ nfields 3) #f)))
406 (##core#inline "C_vector_to_structure" instance)
407 (%structure-set! instance 0 'instance)
408 (%structure-set! instance 1 class)
409 instance))
410
411(define-inline (%instance? x)
412 (%structure? x 'instance))
413
414(define-inline (%instance-class instance)
415 (%structure-ref instance 1))
416
417(define-inline (%set-instance-class! instance new-value)
418 (%structure-set! instance 1 new-value))
419
420(define-inline (%instance-cache-ref instance)
421 (%structure-ref instance 2))
422
423(define-inline (%instance-cache-set! instance x)
424 (%structure-set! instance 2 x))
425
426(define-inline (%instance-ref instance index)
427 (%structure-ref instance (+ index 3)))
428
429(define-inline (%instance-set! instance index new-value)
430 (%structure-set! instance (+ index 3) new-value))
431
432(define-record-printer (instance x out)
433 (print-object x out) )
434
435; Entity
436
437(define-unique-object entity-tag)
438
439(let ([default-proc
440 (lambda args
441 (##sys#error '%allocate-entity "called entity without first setting proc"))] )
442 (set! %allocate-entity
443 (lambda (class nfields name)
444 (letrec ((entity-def (make-vector (+ nfields 5) #f))
445 (closure (lambda args (apply (%structure-ref entity-def 1) args))))
446 (##core#inline "C_vector_to_structure" entity-def)
447 (%structure-set! entity-def 0 'entity)
448 (%structure-set! entity-def 1 default-proc)
449 (%structure-set! entity-def 2 class)
450 (%structure-set! entity-def 3 name)
451 ;; slot #4 is cache (defaults to #f)
452 (let ([len (%closure-length closure)])
453 (let ([entity (make-vector (+ len 2))] )
454 (do ([i 1 (add1 i)])
455 ((>= i len)
456 (%vector-set! entity i entity-def)
457 (%vector-set! entity (add1 i) entity-tag)
458 (##core#inline "C_vector_to_closure" entity)
459 (##core#inline "C_copy_pointer" closure entity)
460 entity)
461 (%vector-set! entity i (%closure-ref closure i)) ) ) ) ) ) ) )
462
463(define-inline (%entity? x)
464 (and (procedure? x)
465 (let ([len (%closure-length x)])
466 (and (> len 3)
467 (eq? entity-tag (%closure-ref x (sub1 len))) ) ) ) )
468
469(define-inline (%entity-def entity)
470 (%closure-ref entity (- (%closure-length entity) 2)) )
471
472(define-inline (%set-entity-proc! entity proc)
473 (%structure-set! (%entity-def entity) 1 proc) )
474
475(define-inline (%entity-class entity)
476 (%structure-ref (%entity-def entity) 2) )
477
478(define-inline (%entity-name entity)
479 (%structure-ref (%entity-def entity) 3) )
480
481(define-inline (%set-entity-name! entity x)
482 (%structure-set! (%entity-def entity) 3 x) )
483
484(define-inline (%entity-cache-ref entity)
485 (%structure-ref (%entity-def entity) 4) )
486
487(define-inline (%entity-cache-set! entity x)
488 (%structure-set! (%entity-def entity) 4 x) )
489
490(define-inline (%entity-ref entity index)
491 (%structure-ref (%entity-def entity) (+ index 5)) )
492
493(define-inline (%entity-set! entity index new-value)
494 (%structure-set! (%entity-def entity) (+ index 5) new-value) )
495
496(define-record-printer (entity x out)
497 (print-object x out) )
498
499; Instance/Entity field accessors
500
501(define get-field
502 (lambda (object field)
503 (cond ((%instance? object) (%instance-ref object field))
504 ((%entity? object) (%entity-ref object field))
505 (else
506 (##sys#signal-hook #:type-error 'get-field "can only get-field of instances and entities" object)))))
507
508(define set-field!
509 (lambda (object field new-value)
510 (cond ((%instance? object) (%instance-set! object field new-value))
511 ((%entity? object) (%entity-set! object field new-value))
512 (else
513 (##sys#signal-hook #:type-error 'set-field! "can only set-field! of instances and entities" object)))))
514
515;
516; Note that this implementation of class-of assumes the name of the
517; primitive classes that are set up later.
518;
519
520; Local namespace - all "exported" procedures are inline so
521; defined in compilation unit top-level scope.
522
523(let ()
524
525 (define (delete1! test lst)
526 (let loop ((cpair lst) (ppair #f))
527 (cond ((null? cpair) lst )
528 ((test (%car cpair))
529 (if ppair
530 (begin (%set-cdr! ppair (%cdr cpair)) lst)
531 (%cdr cpair)) )
532 (else (loop (%cdr cpair) cpair) ) ) ) )
533
534 (define (any1 test lst)
535 (let loop ([lst lst])
536 (and (not (null? lst))
537 (or (test (%car lst))
538 (loop (%cdr lst)) ) ) ) )
539
540 ; Class-of extension helpers
541 ;
542 ; Implemented as a map: "test" <-> "class"
543 ;
544 ; tst - test value; symbol or procedure
545 ; cls - class
546 ; prd - predicate? obj
547 ; eql - symbol eq? obj
548
549 (define-inline (clsmapelm-tst cme)
550 (%car cme) )
551
552 (define-inline (clsmapelm-cls cme)
553 (%cdr cme) )
554
555 (define-inline (clsmapelm-tst-set! cme tst)
556 (%set-car! cme tst) )
557
558 (define-inline (clsmapelm-cls-set! cme cls)
559 (%set-cdr! cme cls) )
560
561 (define-inline (clsmap-tst? tst)
562 (or (procedure? tst) (symbol? tst)) )
563
564 (define-inline (clsmapelm-cls-of/prd? cme tst)
565 ((clsmapelm-tst cme) tst) )
566
567 (define-inline (clsmapelm-cls-of/eql? cme tst)
568 (eq? tst (clsmapelm-tst cme)) )
569
570 (define-inline (clsmapelm-cls-of/prd cme tst)
571 (and (clsmapelm-cls-of/prd? cme tst)
572 (clsmapelm-cls cme)) )
573
574 (define-inline (clsmapelm-cls-of/eql cme tst)
575 (and (clsmapelm-cls-of/eql? cme tst)
576 (clsmapelm-cls cme)) )
577
578 (define-inline (clsmap-add cm tst cls)
579 (cons (cons tst cls) cm) )
580
581 (let (
582 [clsmap-update
583 (lambda (cm tst cls)
584 (let ((cme (any1 (cut clsmapelm-cls-of/eql? <> tst) cm)))
585 (if cme
586 (begin (clsmapelm-tst-set! cme tst) (clsmapelm-cls-set! cme cls) cm)
587 (clsmap-add cm tst cls) ) ) )]
588
589 [clsmap-cls-of/prd
590 (lambda (cm tst)
591 (any1 (cut clsmapelm-cls-of/prd <> tst) cm) )]
592
593 [clsmap-cls-of/eql
594 (lambda (cm tst)
595 (any1 (cut clsmapelm-cls-of/eql <> tst) cm) )]
596
597 [clsmap-del/tst
598 (lambda (cm tst)
599 (delete1! (cut clsmapelm-cls-of/eql? <> tst) cm) )]
600
601 [clsmap-del/cls
602 (lambda (cm cls)
603 (delete1! (lambda (cme) (eq? cls (clsmapelm-cls cme))) cm) )] )
604
605 ; Primitive class-of extensions
606
607 (let ([*primitive-class-map* '()])
608
609 (define-inline (primitive-class-of x)
610 (or (clsmap-cls-of/prd *primitive-class-map* x)
611 #;<object>) )
612
613 (define-inline (delete-primitive-class-of cls-or-tst)
614 (set! *primitive-class-map*
615 (if (clsmap-tst? cls-or-tst)
616 (clsmap-del/tst *primitive-class-map* cls-or-tst)
617 (clsmap-del/cls *primitive-class-map* cls-or-tst))) )
618
619 (define-inline (update-primitive-class-of prd cls)
620 (set! *primitive-class-map* (clsmap-update *primitive-class-map* prd cls)) ) )
621
622 ; Structure class-of extensions
623
624 (let ([*structure-class-map* '()])
625
626 (define-inline (structure-class-of x)
627 (or (clsmap-cls-of/eql *structure-class-map* x)
628 <structure>) )
629
630 (define-inline (delete-structure-class-of cls-or-tst)
631 (set! *structure-class-map*
632 (if (clsmap-tst? cls-or-tst)
633 (clsmap-del/tst *structure-class-map* cls-or-tst)
634 (clsmap-del/cls *structure-class-map* cls-or-tst))) )
635
636 (define-inline (update-structure-class-of tag cls)
637 (set! *structure-class-map* (clsmap-update *structure-class-map* tag cls)) ) )
638
639 ; Tagged-pointer class-of extensions
640
641 (let ([*tagged-pointer-class-map* '()])
642
643 (define-inline (tagged-pointer-class-of x)
644 (or (clsmap-cls-of/eql *tagged-pointer-class-map* (%tagged-pointer-data x))
645 <tagged-pointer>) )
646
647 (define-inline (delete-tagged-pointer-class-of cls-or-tst)
648 (set! *tagged-pointer-class-map*
649 (if (clsmap-tst? cls-or-tst)
650 (clsmap-del/tst *tagged-pointer-class-map* cls-or-tst)
651 (clsmap-del/cls *tagged-pointer-class-map* cls-or-tst))) )
652
653 (define-inline (update-tagged-pointer-class-of tag cls)
654 (set! *tagged-pointer-class-map* (clsmap-update *tagged-pointer-class-map* tag cls)) ) )
655
656 ; Extended-procedure class-of extensions
657
658 (let ([*extended-procedure-class-map* '()]
659 [xproc-tag (vector 'extended)])
660
661 (define-inline (extended-procedure-lambda-decoration x)
662 (##sys#lambda-decoration x (lambda (x) (and (pair? x) (equal? xproc-tag (%car x))))) )
663
664 (define-inline (procedure-class-of x)
665 (or (and-let* ([d (extended-procedure-lambda-decoration x)])
666 (clsmap-cls-of/prd *extended-procedure-class-map* (%cdr d)) )
667 <procedure>) )
668
669 (define-inline (delete-extended-procedure-class-of cls-or-tst)
670 (set! *extended-procedure-class-map*
671 (if (clsmap-tst? cls-or-tst)
672 (clsmap-del/tst *extended-procedure-class-map* cls-or-tst)
673 (clsmap-del/cls *extended-procedure-class-map* cls-or-tst))) )
674
675 (define-inline (update-extended-procedure-class-of prd cls)
676 (set! *extended-procedure-class-map* (clsmap-update *extended-procedure-class-map* prd cls)) ) ) ) )
677
678;
679
680(define (class-of x)
681 (cond [(%unbound? x) (##sys#error 'class-of "unbound object")]
682 [(null? x) <null>]
683 [(fixnum? x) <exact>]
684 [(boolean? x) <boolean>]
685 [(char? x) <char>]
686 [(eof-object? x) <end-of-file>]
687 [(%undefined? x) <void>]
688 [(%immediate-value? x) (##sys#error 'class-of "unidentified immediate object - cannot infer class" x)]
689 [(flonum? x) <inexact>]
690 [(integer? x) <integer>]
691 [(symbol? x) <symbol>]
692 [(%instance? x) (%instance-class x)]
693 [(%entity? x) (%entity-class x)]
694 [(vector? x) <vector>]
695 [(pair? x) <pair>]
696 [(string? x) <string>]
697 [(procedure? x) (procedure-class-of x)]
698 [(port? x) (if (input-port? x) <input-port> <output-port>)]
699 [(%blob? x) <blob>]
700 [(%pointer? x) <pointer>]
701 [(%tagged-pointer? x) (tagged-pointer-class-of x)]
702 [(%swig-pointer? x) <swig-pointer>]
703 [(%locative? x) <locative>]
704 [(%structure? x)
705 (case (%structure-tag x)
706 [(environment) <environment>]
707 [(array) <array>]
708 [(hash-table) <hash-table>]
709 [(queue) <queue>]
710 [(condition) <condition>]
711 [(condition-variable) <condition-variable>]
712 [(char-set) <char-set>]
713 [(time) <time>]
714 [(lock) <lock>]
715 [(mmap) <mmap>]
716 [(promise) <promise>]
717 [(u8vector) <u8vector>]
718 [(s8vector) <s8vector>]
719 [(u16vector) <u16vector>]
720 [(s16vector) <s16vector>]
721 [(u32vector) <u32vector>]
722 [(s32vector) <s32vector>]
723 [(f32vector) <f32vector>]
724 [(f64vector) <f64vector>]
725 [(tcp-listener) <tcp-listener>]
726 [(thread) <thread>]
727 [(mutex) <mutex>]
728 [(continuation) <continuation>]
729 [(read-table) <read-table>]
730 [(regexp) <regexp>]
731 [else (structure-class-of x)] ) ]
732 [(primitive-class-of x)]
733 [else
734 (##sys#error 'class-of "unidentified primitive object - cannot infer class" x) ] ) )
735
736;
737; Now we can get down to business. First, we initialize the braid.
738;
739; For Bootstrapping, we define an early version of MAKE. It will be
740; changed to the real version later on. String search for ``set! make''.
741;
742
743(randomize)
744
745(define (make class . initargs)
746 (cond ((or (eq? class <class>)
747 (eq? class <entity-class>))
748 (let* ((new (%allocate-instance class (length the-slots-of-a-class)))
749 (dsupers (fast-getl initargs 'direct-supers '()))
750 (name (fast-getl initargs 'name "(anonymous)"))
751 (dslots (map list (fast-getl initargs 'direct-slots '())))
752 (cpl (let loop ((sups dsupers) (so-far (list new)))
753 (if (null? sups)
754 (reverse so-far)
755 (let ([cls (%car sups)])
756 (loop (class-direct-supers cls) (cons cls so-far))))))
757 (slots (apply append dslots (map class-direct-slots (%cdr cpl))))
758 (nfields 0)
759 (field-initializers '())
760 (allocator
761 (lambda (init)
762 (let ((f nfields))
763 (set! nfields (add1 nfields))
764 (set! field-initializers
765 (cons init field-initializers))
766 (values (lambda (o) (get-field o f))
767 (lambda (o n) (set-field! o f n))))))
768 (getters-n-setters
769 (map (lambda (s)
770 (cons (%car s)
771 (call-with-values (lambda () (allocator (lambda () (void)))) cons) ) )
772 slots)))
773 (##tinyclos#slot-set! new 'direct-supers dsupers)
774 (##tinyclos#slot-set! new 'direct-slots dslots)
775 (##tinyclos#slot-set! new 'cpl cpl)
776 (##tinyclos#slot-set! new 'slots slots)
777 (##tinyclos#slot-set! new 'nfields nfields)
778 (##tinyclos#slot-set! new 'field-initializers (reverse field-initializers))
779 (##tinyclos#slot-set! new 'getters-n-setters getters-n-setters)
780 (##tinyclos#slot-set! new 'name name)
781 (%instance-cache-set! new (%random-fixnum #x10000))
782 new))
783 ((eq? class <generic>)
784 (let ([new (%allocate-entity class (length (class-slots class))
785 (fast-getl initargs 'name "(unnamed)") ) ] )
786 (##tinyclos#slot-set! new 'methods '())
787 new))
788 ((eq? class <method>)
789 (let ((new (%allocate-instance class (length (class-slots class)))))
790 (##tinyclos#slot-set! new 'specializers (getl initargs 'specializers))
791 (##tinyclos#slot-set! new 'procedure (getl initargs 'procedure))
792 new))
793 (else
794 (##sys#error "bootstrap make: unknown class" class)) ) )
795
796;
797; These are the real versions of slot-ref and slot-set!. Because of the
798; way the new slot access protocol works, with no generic call inline,
799; they can be defined up front like this. Cool eh?
800;
801
802(define (##tinyclos#slot-ref object slot-name)
803 ; if true, then this is an instance of <class>, with no additional slots
804 (if (and (%instance? object) (%instance-cache-ref object))
805 (%instance-ref
806 object
807 (case slot-name
808 [(direct-supers) 0]
809 [(direct-slots) 1]
810 [(cpl) 2]
811 [(slots) 3]
812 [(nfields) 4]
813 [(field-initializers) 5]
814 [(getters-n-setters) 6]
815 [(name) 7]
816 [else (##sys#error "unknown basic slot-name" slot-name)] ) )
817 (let* ((info (lookup-slot-info (class-of object) slot-name))
818 (getter (%car info)))
819 (getter object))) )
820
821(define (##tinyclos#slot-set! object slot-name new-value)
822 (let* ((info (lookup-slot-info (class-of object) slot-name))
823 (setter (%cdr info)) )
824 (setter object new-value)) )
825
826(define slot-ref (getter-with-setter ##tinyclos#slot-ref ##tinyclos#slot-set!))
827(define slot-set! ##tinyclos#slot-set!)
828
829(define (lookup-slot-info class slot-name)
830 (let* ((getters-n-setters
831 (if (eq? class <class>) ;* This grounds out
832 getters-n-setters-for-class ;* the slot-ref tower.
833 (##tinyclos#slot-ref class 'getters-n-setters)))
834 (entry (assq slot-name getters-n-setters)))
835 (if entry
836 (%cdr entry)
837 (##sys#error "no slot in instances of class" slot-name class) ) ) )
838
839;
840; Given that the early version of MAKE is allowed to call accessors on
841; class metaobjects, the definitions for them come here, before the
842; actual class definitions, which are coming up right afterwards.
843;
844
845(define (class-direct-slots class)
846 (##tinyclos#slot-ref class 'direct-slots) )
847(define (class-direct-supers class)
848 (##tinyclos#slot-ref class 'direct-supers))
849(define (class-slots class)
850 (##tinyclos#slot-ref class 'slots) )
851
852(define (class-name class)
853 (##tinyclos#slot-ref class 'name))
854
855(define (generic-methods generic)
856 (##tinyclos#slot-ref generic 'methods) )
857
858(define (method-specializers method)
859 (##tinyclos#slot-ref method 'specializers) )
860(define (method-procedure method)
861 (##tinyclos#slot-ref method 'procedure) )
862
863(define (class-cpl class)
864 (##tinyclos#slot-ref class 'cpl) )
865
866;;; Inline procedures inside this module only:
867
868(eval-when (compile)
869 (define-macro (class-direct-slots class) `(##tinyclos#slot-ref ,class 'direct-slots))
870 (define-macro (class-direct-supers class) `(##tinyclos#slot-ref ,class 'direct-supers))
871 (define-macro (class-slots class) `(##tinyclos#slot-ref ,class 'slots))
872 (define-macro (class-name class) `(##tinyclos#slot-ref ,class 'name))
873 (define-macro (generic-methods generic) `(##tinyclos#slot-ref ,generic 'methods))
874 (define-macro (method-specializers method) `(##tinyclos#slot-ref ,method 'specializers))
875 (define-macro (method-procedure method) `(##tinyclos#slot-ref ,method 'procedure))
876 (define-macro (class-cpl class) `(##tinyclos#slot-ref ,class 'cpl)) )
877
878;
879; The next 7 clusters define the 6 initial classes. It takes 7 to 6
880; because the first and fourth both contribute to <class>.
881;
882
883(define getters-n-setters-for-class ;see lookup-slot-info
884 (let loop ([lst the-slots-of-a-class] [i 0])
885 (if (null? lst)
886 '()
887 (cons (cons (%car lst)
888 (cons (lambda (o) (%instance-ref o i))
889 (lambda (o n) (%instance-set! o i n)) ) )
890 (loop (%cdr lst) (add1 i)) ) ) ) )
891
892(define <class> (%allocate-instance #f (length the-slots-of-a-class)))
893(%set-instance-class! <class> <class>)
894
895(define <top> (make <class>
896 'direct-supers '()
897 'direct-slots '()
898 'name "top"))
899
900(define <object> (make <class>
901 'direct-supers (list <top>)
902 'direct-slots '()
903 'name "object"))
904
905;
906; This cluster, together with the first cluster above that defines
907; <class> and sets its class, have the effect of:
908;
909; (define <class>
910; (make <class>
911; 'direct-supers (list <object>)
912; 'direct-slots (list 'direct-supers ...)))
913;
914
915(%instance-set! <class> 0 (list <object>)) ;d supers
916(%instance-set! <class> 1 (map list the-slots-of-a-class)) ;d slots
917(%instance-set! <class> 2 (list <class> <object> <top>)) ;cpl
918(%instance-set! <class> 3 (map list the-slots-of-a-class)) ;slots
919(%instance-set! <class> 4 (length the-slots-of-a-class)) ;nfields
920(%instance-set! <class> 5 (map (lambda (s) ;field-ini..
921 (lambda () (void)))
922 the-slots-of-a-class))
923(%instance-set! <class> 6 '())
924(%instance-set! <class> 7 'class)
925(%instance-cache-set! <class> (%random-fixnum #x10000))
926
927(define <procedure-class> (make <class>
928 'direct-supers (list <class>)
929 'direct-slots '()
930 'name "procedure-class"))
931
932(define <entity-class> (make <class>
933 'direct-supers (list <procedure-class>)
934 'direct-slots '()
935 'name "entity-class"))
936
937(define <generic> (make <entity-class>
938 'direct-supers (list <object>)
939 'direct-slots (list 'methods)
940 'name "generic"))
941
942(define <method> (make <class>
943 'direct-supers (list <object>)
944 'direct-slots (list 'specializers 'procedure)
945 'name "method"))
946
947;
948; These are the convenient syntax we expose to the base-level user.
949;
950
951(define (make-class direct-supers direct-slots . name)
952 (make <class>
953 'direct-supers direct-supers
954 'direct-slots direct-slots
955 'name (:optional name "(anonymous)")) )
956
957(define (make-generic . name)
958 (make <generic>
959 'name (:optional name "(unnamed)")) )
960
961(define (make-method specializers procedure)
962 (make <method>
963 'specializers specializers
964 'procedure procedure) )
965
966;
967; The initialization protocol
968;
969
970(define initialize (make-generic "initialize"))
971
972;
973; The instance structure protocol.
974;
975
976(define allocate-instance (make-generic "allocate-instance"))
977(define compute-getter-and-setter (make-generic "compute-getter-and-setter"))
978
979;
980; The class initialization protocol.
981;
982
983(define compute-cpl (make-generic "compute-cpl"))
984(define compute-slots (make-generic "compute-slots"))
985
986;
987; The generic invocation protocol.
988;
989
990(define compute-apply-generic (make-generic "compute-apply-generic"))
991(define compute-methods (make-generic "compute-methods"))
992(define compute-method-more-specific? (make-generic "compute-method-more-specific?"))
993(define compute-apply-methods (make-generic "compute-apply-methods"))
994
995;
996; The next thing to do is bootstrap generic functions.
997;
998
999(define generic-invocation-generics (list compute-apply-generic
1000 compute-methods
1001 compute-method-more-specific?
1002 compute-apply-methods))
1003
1004(define (add-method generic method)
1005 (##tinyclos#slot-set!
1006 generic
1007 'methods
1008 (let* ([ms1 (method-specializers method)]
1009 [l1 (length ms1)] )
1010 (let filter-in-method ([methods (##tinyclos#slot-ref generic 'methods)])
1011 (if (null? methods)
1012 (list method)
1013 (let* ([mm (%car methods)]
1014 [ms2 (method-specializers mm)]
1015 [l2 (length ms2)])
1016 (cond ((> l1 l2)
1017 (cons mm (filter-in-method (%cdr methods))))
1018 ((< l1 l2)
1019 (cons method methods))
1020 (else
1021 (let check-method ([ms1 ms1]
1022 [ms2 ms2])
1023 (cond ((and (null? ms1) (null? ms2))
1024 (cons method (%cdr methods))) ;; skip the method already in the generic
1025 ((eq? (%car ms1) (%car ms2))
1026 (check-method (%cdr ms1) (%cdr ms2)))
1027 (else
1028 (cons mm (filter-in-method (%cdr methods)))))))))))))
1029 (if (memq generic generic-invocation-generics)
1030 (set! method-cache-tag (vector))
1031 (%entity-cache-set! generic #f) )
1032 (%set-entity-proc! generic (compute-apply-generic generic)) )
1033
1034;
1035; Adding a method calls COMPUTE-APPLY-GENERIC, the result of which calls
1036; the other generics in the generic invocation protocol. Two, related,
1037; problems come up. A chicken and egg problem and a infinite regress
1038; problem.
1039;
1040; In order to add our first method to COMPUTE-APPLY-GENERIC, we need
1041; something sitting there, so it can be called. The first definition
1042; below does that.
1043;
1044; Then, the second definition solves both the infinite regress and the
1045; not having enough of the protocol around to build itself problem the
1046; same way: it special cases invocation of generics in the invocation
1047; protocol.
1048;
1049;
1050
1051(%set-entity-proc! compute-apply-generic
1052 (lambda (generic) ;The ONE time this is called
1053 ;it doesn't get cnm.
1054 (lambda args
1055 (apply (method-procedure (car (generic-methods generic)))
1056 #f args)))) ;But, the ONE time it is run,
1057 ;it needs to pass a dummy
1058 ;value for cnm!
1059
1060(let ([symbol-vector (vector 'instance entity-tag)])
1061
1062 ; Compute class ID from object
1063 (define hash-arg-list
1064 (foreign-lambda* unsigned-int ((scheme-object args) (scheme-object svector)) "
1065 C_word tag, h, x;
1066 int n, i, j, len = 0;
1067 for(i = 0; args != C_SCHEME_END_OF_LIST; args = C_block_item(args, 1)) {
1068 x = C_block_item(args, 0);
1069 if(C_immediatep(x)) {
1070 switch(x) {
1071 case C_SCHEME_END_OF_LIST: i += 1; break;
1072 case C_SCHEME_TRUE:
1073 case C_SCHEME_FALSE: i += 3; break;
1074 case C_SCHEME_END_OF_FILE: i += 7; break;
1075 case C_SCHEME_UNDEFINED: i += 5; break;
1076 default:
1077 if(x & C_FIXNUM_BIT) i += 2;
1078 else i += 4;
1079 }
1080 }
1081 else {
1082 h = C_header_bits(x);
1083 switch(h) {
1084 case C_STRUCTURE_TYPE:
1085 tag = C_block_item(x, 0);
1086 if(tag == C_block_item(svector, 0)) { /* instance */
1087 if((tag = C_block_item(C_block_item(x, 1), 2)) != C_SCHEME_FALSE) i += C_unfix(tag);
1088 else i += C_header_size(x) << 4;
1089 }
1090 else i += 17;
1091 break;
1092 case C_CLOSURE_TYPE:
1093 n = C_header_size(x);
1094 if(n > 3 && C_block_item(svector, 1) == C_block_item(x, n - 1)) {
1095 if((tag = C_block_item(C_block_item(C_block_item(x, n - 2), 2), 2)) != C_SCHEME_FALSE) i += C_unfix(tag);
1096 else i += 13;
1097 }
1098 break;
1099 case C_SYMBOL_TYPE: i += 8; break;
1100 case C_BYTEVECTOR_TYPE: i += 16; break;
1101 case C_VECTOR_TYPE: i += 9; break;
1102 case C_PAIR_TYPE: i += 10; break;
1103 case C_FLONUM_TYPE: i += 11; break;
1104 case C_STRING_TYPE: i += 12; break;
1105 case C_PORT_TYPE: i += C_block_item(x, 1) ? 15 : 14; break;
1106 default: i += 255;
1107 }
1108 }
1109 ++len;
1110 }
1111 return((i + len) & (C_METHOD_CACHE_SIZE - 1));") )
1112
1113 (add-method compute-apply-generic
1114 (make-method (list <generic>)
1115 (lambda (call-next-method generic)
1116 (lambda args
1117 (let ([mc (%entity-cache-ref generic)])
1118 (when (or (not mc) (not (eq? method-cache-tag (%car mc))))
1119 (set! mc (make-method-cache))
1120 (%entity-cache-set! generic mc) )
1121 (let* ([classes (and method-caching-enabled (map class-of args))]
1122 [key (and classes (hash-arg-list args symbol-vector))]
1123 [e (and classes (method-cache-lookup mc key classes))] )
1124 ;(unless (##sys#immediate? e) (print (%entity-name generic) ": " key))
1125 (if (not (##sys#immediate? e))
1126 (e args)
1127 (let ([cam
1128 (if (and (memq generic generic-invocation-generics)
1129 (memq (car args) generic-invocation-generics))
1130 (let ([proc
1131 (method-procedure
1132 ; select the first method of one argument
1133 (let loop ([lis (generic-methods generic)])
1134 (if (null? lis)
1135 (##sys#error "cannot find original compute-apply-generic")
1136 (let* ([h (%car lis)]
1137 [ms (method-specializers h)])
1138 (if (= 1 (length ms))
1139 h
1140 (loop (%cdr lis))))))) ] )
1141 (lambda (args) (apply proc #f args)) )
1142 (let ([x (compute-apply-methods generic)]
1143 [y ((compute-methods generic) args)] )
1144 (lambda (args) (x y args)) ) ) ] )
1145 (when (and e method-caching-enabled)
1146 (let ([v (%cdr mc)])
1147 (%vector-set! v e classes)
1148 (%vector-set! v (add1 e) cam) ) )
1149 (cam args) ) ) ) ) ) ) ) ) )
1150
1151(add-method compute-methods
1152 (make-method (list <generic>)
1153 (lambda (call-next-method generic)
1154 (lambda (args)
1155 (let ([applicable
1156 (filter-in (lambda (method)
1157 (let check-applicable ([lst1 (method-specializers method)]
1158 [lst2 args])
1159 (cond ((null? lst1) #t)
1160 ((null? lst2) #f)
1161 (else
1162 (and (applicable? (%car lst1) (%car lst2))
1163 (check-applicable (%cdr lst1) (%cdr lst2)))))))
1164 (generic-methods generic) ) ] )
1165 (if (or (null? applicable) (null? (%cdr applicable)))
1166 applicable
1167 (let ([cmms (compute-method-more-specific? generic)])
1168 (sort applicable (lambda (m1 m2) (cmms m1 m2 args))) ) ) ) ) ) ) )
1169
1170(add-method compute-method-more-specific?
1171 (make-method (list <generic>)
1172 (lambda (call-next-method generic)
1173 (lambda (m1 m2 args)
1174 (let loop ((specls1 (method-specializers m1))
1175 (specls2 (method-specializers m2))
1176 (args args))
1177 (cond-expand
1178 [unsafe
1179 (let ((c1 (%car specls1))
1180 (c2 (%car specls2))
1181 (arg (%car args)))
1182 (if (eq? c1 c2)
1183 (loop (%cdr specls1)
1184 (%cdr specls2)
1185 (%cdr args))
1186 (more-specific? c1 c2 arg))) ]
1187 [else
1188 (cond ((and (null? specls1) (null? specls2))
1189 (##sys#error "two methods are equally specific" generic))
1190 #; ; Ok to have diff # of specializers
1191 ((or (null? specls1) (null? specls2))
1192 (##sys#error "two methods have different number of specializers" generic))
1193 ((null? specls1) #f)
1194 ((null? specls2) #t)
1195 ((null? args)
1196 (##sys#error "fewer arguments than specializers" generic))
1197 (else
1198 (let ((c1 (%car specls1))
1199 (c2 (%car specls2))
1200 (arg (%car args)))
1201 (if (eq? c1 c2)
1202 (loop (%cdr specls1)
1203 (%cdr specls2)
1204 (%cdr args))
1205 (more-specific? c1 c2 arg)))) ) ] ) ) ) ) ) )
1206
1207(add-method compute-apply-methods
1208 (make-method (list <generic>)
1209 (lambda (call-next-method generic)
1210 (lambda (methods args)
1211 (letrec ((one-step
1212 (lambda (tail)
1213 (lambda ()
1214 (cond-expand
1215 [unsafe]
1216 [else
1217 (when (null? tail)
1218 (##sys#error "call-next-method: no methods left" generic) ) ] )
1219 (apply (method-procedure (%car tail))
1220 (one-step (%cdr tail)) args)))))
1221 ((one-step methods)))))))
1222
1223(define (applicable? c arg)
1224 (memq c (class-cpl (class-of arg))) )
1225
1226(define (more-specific? c1 c2 arg)
1227 (memq c2 (memq c1 (class-cpl (class-of arg)))) )
1228
1229(add-method initialize
1230 (make-method (list <top>)
1231 (lambda (call-next-method object initargs)
1232 (##sys#error "cannot initialize object" object) ) ) )
1233
1234(add-method initialize
1235 (make-method (list <object>)
1236 (lambda (call-next-method object initargs) object)))
1237
1238(add-method initialize
1239 (make-method (list <class>)
1240 (lambda (call-next-method class initargs)
1241 (call-next-method)
1242 (##tinyclos#slot-set! class 'direct-supers (fast-getl initargs 'direct-supers '()))
1243 (let ([dslots
1244 (map (lambda (s) (if (pair? s) s (list s)))
1245 (fast-getl initargs 'direct-slots '()))])
1246 (let ([namestr
1247 (let ([name (fast-getl initargs 'name "(anonymous)")])
1248 (cond [(symbol? name) (##sys#symbol->string name)]
1249 [(string? name) name]
1250 [else (##sys#signal-hook #:type-error
1251 'initialize "invalid class name" name) ] ) ) ] )
1252 (##tinyclos#slot-set! class 'direct-slots dslots)
1253 (##tinyclos#slot-set! class 'cpl (compute-cpl class))
1254 (##tinyclos#slot-set! class 'name
1255 (let ([namestrlen (%string-length namestr)])
1256 (if (and (> namestrlen 0)
1257 (char=? #\< (string-ref namestr 0))
1258 (char=? #\> (string-ref namestr (sub1 namestrlen))) )
1259 (substring namestr 1 (sub1 namestrlen))
1260 namestr) ))
1261 (let ([slots (compute-slots class)])
1262 (##tinyclos#slot-set! class 'slots slots)
1263 (when (= basic-class-instance-size (%structure-length class))
1264 (%instance-cache-set! class (%random-fixnum #x10000)) )
1265 (let* ([nfields 0]
1266 [field-initializers '()]
1267 [allocator
1268 (lambda (init)
1269 (let ((f nfields))
1270 (set! nfields (add1 nfields))
1271 (set! field-initializers (cons init field-initializers))
1272 (values (lambda (o) (get-field o f))
1273 (lambda (o n) (set-field! o f n)))))]
1274 [getters-n-setters
1275 (map (lambda (slot)
1276 (cons (%car slot)
1277 (call-with-values (lambda ()
1278 (compute-getter-and-setter class slot allocator))
1279 cons) ) )
1280 slots) ] )
1281 (##tinyclos#slot-set! class 'nfields nfields)
1282 (##tinyclos#slot-set! class 'field-initializers (reverse field-initializers))
1283 (##tinyclos#slot-set! class 'getters-n-setters getters-n-setters))))) ) ) )
1284
1285(add-method initialize
1286 (make-method (list <generic>)
1287 (lambda (call-next-method generic initargs)
1288 (call-next-method)
1289 (unless (%entity? generic)
1290 (##sys#error 'initialize "generic is not an entity") )
1291 (##tinyclos#slot-set! generic 'methods '())
1292 (%set-entity-name! generic (fast-getl initargs 'name "(unnamed)"))
1293 (%set-entity-proc! generic (lambda args (##sys#error "has no methods" generic))))))
1294
1295(add-method initialize
1296 (make-method (list <method>)
1297 (lambda (call-next-method method initargs)
1298 (call-next-method)
1299 (##tinyclos#slot-set! method 'specializers (getl initargs 'specializers))
1300 (##tinyclos#slot-set! method 'procedure (getl initargs 'procedure)))))
1301
1302(add-method allocate-instance
1303 (make-method (list <class>)
1304 (lambda (call-next-method class)
1305 (let* ((field-initializers (##tinyclos#slot-ref class 'field-initializers))
1306 (new (%allocate-instance class (length field-initializers))))
1307 (let loop ((n 0) (inits field-initializers))
1308 (if (null? inits)
1309 new
1310 (begin
1311 (%instance-set! new n ((%car inits)))
1312 (loop (add1 n) (%cdr inits)))))))))
1313
1314(add-method allocate-instance
1315 (make-method (list <entity-class>)
1316 (lambda (call-next-method class)
1317 (let* ([field-initializers (##tinyclos#slot-ref class 'field-initializers)]
1318 [new (%allocate-entity class (length field-initializers) "(unnamed)") ] )
1319 (let loop ((n 0) (inits field-initializers))
1320 (if (null? inits)
1321 new
1322 (begin
1323 (%entity-set! new n ((%car inits)))
1324 (loop (add1 n) (%cdr inits)))))))))
1325
1326(add-method compute-cpl
1327 (make-method (list <class>)
1328 (lambda (call-next-method class)
1329 (compute-std-cpl class class-direct-supers))))
1330
1331(add-method compute-slots
1332 (make-method (list <class>)
1333 (lambda (call-next-method class)
1334 (let collect ((to-process (apply append (map class-direct-slots (class-cpl class))))
1335 (result '()))
1336 (if (null? to-process)
1337 (reverse result)
1338 (let* ((current (%car to-process))
1339 (name (%car current))
1340 (others '())
1341 (remaining-to-process
1342 (filter-in (lambda (o)
1343 (if (eq? (%car o) name)
1344 (begin
1345 (set! others (cons o others))
1346 #f)
1347 #t))
1348 (%cdr to-process))))
1349 (collect remaining-to-process
1350 (cons (append current (apply append (map (lambda (x) (%cdr x)) others)))
1351 result))))))))
1352
1353(add-method compute-getter-and-setter
1354 (make-method (list <class>)
1355 (lambda (call-next-method class slot allocator)
1356 (allocator (lambda () (void))))))
1357
1358;
1359; Now everything works, both generic functions and classes, so we can
1360; turn on the real MAKE.
1361;
1362
1363(set! make
1364 (lambda (class . initargs)
1365 (let ((instance (allocate-instance class)))
1366 (initialize instance initargs)
1367 instance)))
1368
1369;
1370; Now define what CLOS calls `built in' classes.
1371;
1372
1373(define <primitive-class>
1374 (make <class>
1375 'direct-supers (list <class>)
1376 'direct-slots '()
1377 'name "primitive-class"))
1378
1379(define <primitive>
1380 (make <class>
1381 'direct-supers (list <top>)
1382 'direct-slots '()
1383 'name "primitive"))
1384
1385(add-method initialize
1386 (make-method (list <primitive>)
1387 (lambda (call-next-method object initargs) object)))
1388
1389(define (make-primitive-class name . sclasses)
1390 (make <primitive-class>
1391 'direct-supers (if (null? sclasses) (list <primitive>) sclasses)
1392 'direct-slots '()
1393 'name name) )
1394
1395(define (make-port-class name)
1396 (make <class>
1397 'direct-supers (list <port>)
1398 'direct-slots '()
1399 'name name) )
1400
1401(define (make-structure-class name)
1402 (make-primitive-class name <structure>) )
1403
1404(define (make-extended-procedure-class name)
1405 (make-primitive-class name <procedure>) )
1406
1407(define (make-tagged-pointer-class name)
1408 (make <class>
1409 'direct-supers (list <tagged-pointer>)
1410 'direct-slots '()
1411 'name name) )
1412
1413(define <void> (make-primitive-class "void"))
1414(define <null> (make-primitive-class "null"))
1415(define <end-of-file> (make-primitive-class "end-of-file"))
1416(define <boolean> (make-primitive-class "boolean"))
1417(define <symbol> (make-primitive-class "symbol"))
1418(define <char> (make-primitive-class "char"))
1419(define <number> (make-primitive-class "number"))
1420(define <integer> (make-primitive-class "integer" <number>))
1421(define <exact> (make-primitive-class "exact" <integer>))
1422(define <inexact> (make-primitive-class "inexact" <number>))
1423(define <vector> (make-primitive-class "vector"))
1424(define <pair> (make-primitive-class "pair"))
1425(define <string> (make-primitive-class "string"))
1426(define <port> (make-primitive-class "port"))
1427(define <input-port> (make-port-class "input-port"))
1428(define <output-port> (make-port-class "output-port"))
1429(define <procedure> (make-primitive-class "procedure" <procedure-class>))
1430(define <blob> (make-primitive-class "blob"))
1431(define <byte-vector> (make-primitive-class "byte-vector")) ; DEPRECATED
1432(define <structure> (make-primitive-class "structure"))
1433(define <environment> (make-structure-class "environment"))
1434(define <hash-table> (make-structure-class "hash-table"))
1435(define <promise> (make-structure-class "promise"))
1436(define <queue> (make-structure-class "queue"))
1437(define <condition> (make-structure-class "condition"))
1438(define <condition-variable> (make-structure-class "condition-variable"))
1439(define <continuation> (make-structure-class "continuation"))
1440(define <char-set> (make-structure-class "char-set"))
1441(define <time> (make-structure-class "time"))
1442(define <lock> (make-structure-class "lock"))
1443(define <mmap> (make-structure-class "mmap"))
1444(define <array> (make-structure-class "array"))
1445(define <tcp-listener> (make-structure-class "tcp-listener"))
1446(define <thread> (make-structure-class "thread"))
1447(define <mutex> (make-structure-class "mutex"))
1448(define <regexp> (make-structure-class "regexp"))
1449(define <read-table> (make-structure-class "read-table"))
1450(define <locative> (make-primitive-class "locative"))
1451(define <pointer> (make-primitive-class "pointer"))
1452(define <swig-pointer> (make-primitive-class "swig-pointer" <pointer>))
1453(define <tagged-pointer> (make-primitive-class "tagged-pointer" <pointer>))
1454(define <u8vector> (make-primitive-class "u8vector" <vector>))
1455(define <s8vector> (make-primitive-class "s8vector" <vector>))
1456(define <u16vector> (make-primitive-class "u16vector" <vector>))
1457(define <s16vector> (make-primitive-class "s16vector" <vector>))
1458(define <u32vector> (make-primitive-class "u32vector" <vector>))
1459(define <s32vector> (make-primitive-class "s32vector" <vector>))
1460(define <f32vector> (make-primitive-class "f32vector" <vector>))
1461(define <f64vector> (make-primitive-class "f64vector" <vector>))
1462
1463(define <c++-object>
1464 (make <class>
1465 'direct-supers (list <object>)
1466 'direct-slots '(this)
1467 'name "c++-object"))
1468
1469(add-method initialize
1470 (make-method (list <c++-object>)
1471 (lambda (call-next-method obj initargs)
1472 (when (and (pair? initargs) (eq? 'this (%car initargs)))
1473 (##tinyclos#slot-set! obj 'this (cadr initargs)) ) ) ) )
1474
1475(set! method-caching-enabled #t)
1476
1477;;; Utilities:
1478
1479(define (initialize-slots object initargs)
1480 (##sys#check-list initargs 'initialize-slots)
1481 (for-each
1482 (lambda (slot)
1483 (let* ([name (car slot)]
1484 [value (fast-getl initargs name not-found-object)] )
1485 (unless (eq? value not-found-object)
1486 (slot-set! object name value))))
1487 (class-slots (class-of object))) )
1488
1489(define print-object (make-generic "print-object"))
1490(define describe-object (make-generic "describe-object"))
1491
1492(add-method print-object
1493 (make-method (list <object>)
1494 (lambda (call-next-method x . port)
1495 (fprintf (:optional port ##sys#standard-output) "#<~A>" (class-name (class-of x))) ) ) )
1496
1497(add-method print-object
1498 (make-method (list <primitive>)
1499 (lambda (call-next-method x . port)
1500 (write x (:optional port ##sys#standard-output)) ) ) )
1501
1502(add-method print-object
1503 (make-method (list <class>)
1504 (lambda (call-next-method x . port)
1505 (fprintf (:optional port ##sys#standard-output) "#<class ~A>" (class-name x)) ) ) )
1506
1507(add-method print-object
1508 (make-method (list <generic>)
1509 (lambda (call-next-method x . port)
1510 (fprintf (:optional port ##sys#standard-output) "#<generic ~A>" (%entity-name x)) ) ) )
1511
1512(add-method describe-object
1513 (make-method (list <object>)
1514 (lambda (call-next-method x . port)
1515 (let ([class (class-of x)]
1516 [port (:optional port ##sys#standard-output)] )
1517 (fprintf port "instance of class ~A:~%" (class-name class))
1518 (for-each
1519 (lambda (s)
1520 (let ([slot (car s)])
1521 (fprintf port " ~S\t-> ~S~%" slot (slot-ref x slot)) ) )
1522 (class-slots class) ) ) ) ) )
1523
1524(add-method describe-object
1525 (make-method (list <class>)
1526 (lambda (call-next-method x . port)
1527 (fprintf (:optional port ##sys#standard-output) "class ~A~%" (class-name x)) ) ) )
1528
1529(add-method describe-object
1530 (make-method (list <generic>)
1531 (lambda (call-next-method x . port)
1532 (fprintf (:optional port ##sys#standard-output) "generic ~A~%" (%entity-name x)) ) ) )
1533
1534(define ensure-generic
1535 (let ([make-generic make-generic])
1536 (lambda (x sym)
1537 (if (%entity? x)
1538 x
1539 (make-generic (##sys#symbol->string sym)) ) ) ) )
1540
1541(define add-global-method
1542 (let ([make-method make-method]
1543 [add-method add-method] )
1544 (lambda (val sym specializers proc)
1545 (let ([g (ensure-generic val sym)])
1546 (add-method g (make-method specializers proc))
1547 g) ) ) )
1548
1549(define (instance? x)
1550 (or (%instance? x) (%entity? x)) )
1551
1552(define (subclass? x y)
1553 (if (memq y (compute-cpl x)) #t #f) )
1554
1555(define (instance-of? x class)
1556 (let ([cl (class-of x)])
1557 (or (eq? cl class)
1558 (subclass? cl class) ) ) )
1559
1560(define (make-instance-from-pointer ptr cls)
1561 (and ptr
1562 (not (%null-pointer? ptr))
1563 (make cls 'this ptr) ) )
1564
1565(define (make/copy x . initargs)
1566 (let ([class (class-of x)])
1567 (apply make class
1568 (let ([initlist initargs]
1569 [inited-slot?
1570 (lambda (nam)
1571 (let loop ([testing? #t] [initargs initargs])
1572 (and (pair? initargs)
1573 (or (and testing? (equal? nam (car initargs)))
1574 (loop (not testing?) (cdr initargs)) ) ) ) )])
1575 (for-each
1576 (lambda (s)
1577 (let ([nam (car s)])
1578 (unless (inited-slot? nam)
1579 (set! initlist (cons nam (cons (slot-ref x nam) initlist))) ) ) )
1580 (class-slots class))
1581 initlist ) ) ) )
1582
1583;; Procedural interface for extending the "builtin" class system
1584
1585; Primitive
1586
1587(define (new-primitive-class name pred . sclasses)
1588 (let ((cls (apply make-primitive-class name sclasses)))
1589 (update-primitive-class-of pred cls)
1590 cls ) )
1591
1592(define (delete-primitive-class cls)
1593 (delete-primitive-class-of cls) )
1594
1595; Structure
1596
1597(define (new-structure-class name tag)
1598 (let ((cls (make-structure-class name)))
1599 (update-structure-class-of tag cls)
1600 cls ) )
1601
1602(define (delete-structure-class cls)
1603 (delete-structure-class-of cls) )
1604
1605; Tagged-pointer
1606
1607(define (new-tagged-pointer-class name tag)
1608 (let ((cls (make-tagged-pointer-class name)))
1609 (update-tagged-pointer-class-of tag cls)
1610 cls ) )
1611
1612(define (delete-tagged-pointer-class cls)
1613 (delete-tagged-pointer-class-of cls) )
1614
1615; Procedure
1616
1617(define (new-extended-procedure-class name pred)
1618 (let ((cls (make-extended-procedure-class name)))
1619 (update-extended-procedure-class-of pred cls)
1620 cls ) )
1621
1622(define (delete-extended-procedure-class cls)
1623 (delete-extended-procedure-class-of cls) )