Ticket #32: 4.scm

File 4.scm, 61.7 KB (added by Tony Sidaway, 16 years ago)

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