OSDN Git Service

*** empty log message ***
[pf3gnuchains/sourceware.git] / cgen / cos.scm
1 ;; Cgen's Object System.
2 ;; Copyright (C) 2000, 2009, 2010 Red Hat, Inc.
3 ;; This file is part of CGEN.
4 ;; See file COPYING.CGEN for details.
5 ;;
6 ;; Scheme implementations don't agree on a lot of things beyond the basics.
7 ;; This is a simple object system for cgen's needs.
8 ;; I thought at the start that when Guile had an official object system
9 ;; we'd switch over, but the higher order bit now is to be usable on
10 ;; multiple Scheme implementations: Guile isn't fast enough.
11 ;;
12 ;; NOTE: The original COS supported multiple inheritance.  This does not.
13 ;;
14 ;; Classes look like:
15 ;;
16 ;; #(class-tag
17 ;;   class-name
18 ;;   class-uid ;; unique id of class, index into /class-table
19 ;;   parent-name
20 ;;   elm-alist ;; not including parent classes
21 ;;   method-alist ;; not including parent classes
22 ;;   full-elm-initial-list ;; including parent classes
23 ;;   method-cache ;; ??? not currently used
24 ;;   class-descriptor)
25 ;;
26 ;; PARENT-NAME is the name of the parent class, if any.
27 ;; If a subclasses b which subclasses c, then parent-name for a is b,
28 ;; the parent-name for b is c, and the parent-name for c is #f.
29 ;;
30 ;; ELM-ALIST is an alist of (symbol vector-offset-with-class . initial-value)
31 ;; for this class only.
32 ;; Values can be looked up by name, via elm-make-[gs]etter routines.
33 ;; Various Lisp (or Lisp-like) OOP systems (e.g. CLOS, Dylan) call these
34 ;; "slots".  Maybe for consistency "slot" would be a better name.  Some might
35 ;; confuse that with intentions at directions though.
36 ;;
37 ;; METHOD-ALIST is an alist of (symbol . procedure) for this class only.
38 ;;
39 ;; FULL-ELM-INITIAL-LIST is the elements of the flattened inheritance tree.
40 ;; Initially it is #f meaning it hasn't been computed yet.
41 ;; It is computed when the class is first instantiated.  During development,
42 ;; it can be reset to #f after some module has been reloaded (as long as no
43 ;; elements have been deleted/added/moved/etc., existing objects are ok).
44 ;;
45 ;; METHOD-CACHE is an alist of the methods of the flattened inheritance
46 ;; tree.  Each element is (symbol . (parent-list-entry . method)).
47 ;; Initially it is #f meaning it hasn't been computed yet.
48 ;; It is computed when the class is first instantiated.  During development,
49 ;; it can be reset to #f after some module has been reloaded (requires all
50 ;; object instantiation to happen later of course).
51 ;; FIXME: We don't yet implement the method cache.
52 ;;
53 ;; CLASS-DESCRIPTOR is the processed form of parent-name-list.
54 ;; There is an entry for the class and one for each parent (recursively):
55 ;; (class offset child-backpointer [parent-descriptor]).
56 ;; offset is the offset in the element vector of the class's elements.
57 ;; child-backpointer is #f in the top level object.
58 ;; ??? child->subclass, parent->superclass?
59 ;; Initially the class-descriptor is #f meaning it hasn't been computed yet.
60 ;; It is computed when the class is first instantiated.  During development,
61 ;; it can be reset to #f after some module has been reloaded (requires all
62 ;; object instantiation to happen later of course).
63 ;;
64 ;; An object is a vector: #(object-tag class-name class-uid elm1 elm2 ...)
65 ;; Vectors are nice in that they're self-evaluating.
66 ;; Both class name and uid are stored here for a better developer experience.
67 ;; It might be better to store the class-descriptor instead, but it's big and
68 ;; vastly reduces the S/N ratio when displaying objects.
69 ;;
70 ;; -----------------------------------------------------------------------------
71 ;;
72 ;; User visible procs/macros:
73 ;;
74 ;; (define-class name prefix parents members)
75 ;;
76 ;; This is a macro that defines several things:
77 ;; - the class object with the specified class members
78 ;; - a predicate to identify instances of this class, named "class?"
79 ;; - getters and setters for each member
80 ;; NAME is the name of the class.
81 ;; Convention requires class names to be decorated as <class-name>.
82 ;; ??? This might change to require the actual class object, but not yet.
83 ;; PREFIX is prepended to member getters/setters.
84 ;; PARENTS is a list of parent class names.
85 ;; It must contain at most one element, multiple inheritance isn't supported.
86 ;; Each element of MEMBERS is either member-name (for uninitialized
87 ;;  elements) or (member-name . initial-value).
88 ;; MEMBER-NAME may begin with modifiers / and !:
89 ;; / - member is private: getter/setter begins with /
90 ;; ! - member is writable: readonly members do not get a setter
91 ;; / and ! may not appear elsewhere in MEMBER-NAME.
92 ;; / and ! may appear in either order.
93 ;;
94 ;; (class-make name parents members unused) -> class
95 ;;
96 ;; Create a class.  The result is then passed back by procedures requiring
97 ;; a class argument.
98 ;; NAME is the name of the class.
99 ;; Convention requires class names to be decorated as <class-name>.
100 ;; PARENTS is a list of parent class names.
101 ;; It must contain at most one element, multiple inheritance isn't supported.
102 ;; ??? This might change to require the actual class object, but not yet.
103 ;; MEMBERS is a list of members, each list member is either a name (for
104 ;; uninitialized elements) or (name . initial-value).
105 ;; UNUSED must be the empty list, it will eventually be deleted.
106 ;;
107 ;; (class-list) -> list of all defined classes
108 ;;
109 ;; (class-name class) -> name of CLASS
110 ;;
111 ;; (class-lookup class-name) -> class
112 ;;
113 ;; (class-instance? class object) -> #t if OBJECT is an instance of CLASS
114 ;;
115 ;; (object-class object) -> class of OBJECT
116 ;;
117 ;; (object-class-name object) -> class name of OBJECT
118 ;;
119 ;; (send object method-name . args) -> result of invoking METHOD-NAME
120 ;;
121 ;; (send-next object class-name method-name . args) -> result of invoking next METHOD-NAME
122 ;;
123 ;; (new class) -> instantiate CLASS
124 ;;
125 ;; The object is initialized with values specified when CLASS
126 ;; (and its parent classes) was defined.
127 ;;
128 ;; (vmake class . args) -> instantiate class and initialize it with 'vmake!
129 ;;
130 ;; This is shorthand for (send (new class) 'vmake! args).
131 ;; ARGS is a list of option names and arguments (a la CLOS).
132 ;; ??? Not implemented yet.
133 ;;
134 ;; (method-vmake! object . args) -> modify OBJECT from ARGS
135 ;;
136 ;; This is the standard 'vmake! method, available for use by user-written
137 ;; 'vmake! methods.
138 ;; ??? Not implemented yet.
139 ;;
140 ;; (make class . args) -> instantiate CLASS and initialize it with 'make!
141 ;;
142 ;; This is shorthand for (send (new class) 'make! arg1 ...).
143 ;; This is a positional form of `new'.
144 ;;
145 ;; (method-make-make! class elm1-name elm2-name ...) -> unspecified
146 ;;
147 ;; Create a 'make! method that sets the specified elements.
148 ;;
149 ;; (object-copy object) -> copy of OBJECT
150 ;;
151 ;; Return a copy of OBJECT.
152 ;; NOTE: This does a shallow copy.
153 ;;
154 ;; (object-assign! dstsrc) -> unspecified
155 ;;
156 ;; Assign the contents of SRC to DST.
157 ;; Both must be objects of the same class.
158 ;;
159 ;; (class? foo) -> return #t if FOO is a class
160 ;;
161 ;; (object? foo) -> return #t if FOO is an object
162 ;;
163 ;; (method-make! class name lambda) -> unspecified
164 ;;
165 ;; Add method NAME to CLASS.
166 ;;
167 ;; (method-make-forward! class elm-name methods) -> unspecified
168 ;;
169 ;; Add METHODS to CLASS that pass the "message" onto the object in element
170 ;; ELM-NAME.
171 ;;
172 ;; (elm-get object elm-name) -> value of element ELM-NAME in OBJ
173 ;;
174 ;; Can only be used in methods.
175 ;;
176 ;; (elm-set! object elm-name new-value) -> unspecified
177 ;;
178 ;; Set element ELM-NAME in OBJECT to NEW-VALUE.
179 ;; Can only be used in methods.
180 ;;
181 ;; (elm-make-getter class elm-name) -> lambda
182 ;;
183 ;; Return efficient lambda to get the value of ELM-NAME in CLASS.
184 ;;
185 ;; (elm-make-setter class elm-name) -> lambda
186 ;;
187 ;; Return efficient lambda to set the value of ELM-NAME in CLASS.
188 ;;
189 ;; Conventions used in this file:
190 ;; - procs/vars internal to this file are prefixed with "/"
191 ;; - except for a few exceptions, public procs/macros begin with one of
192 ;;   define-, class-, object-, elm-, method-.
193 ;;   The exceptions are make, vmake, new, send, send-next.
194 ;;
195 ;; NOTES:
196 ;; - "send" as a public interface is deprecated
197 \f
198 (define /class-tag "class")
199 (define /object-tag "object")
200
201 ;; Alist of all classes.
202 ;; Each element is (class-name class?-object).
203 ;; Note that classes are consed unto the front.
204
205 (define /class-list '())
206
207 ;; Table of all classes, indexed by class-uid.
208 ;; Note that classes are appended to the end.
209
210 (define /class-table '#())
211
212 ;; Internal variables to mark their respective properties.
213 (define /object-unspecified #:unspecified)
214 (define /object-unbound #:unbound)
215
216 ;; True if error messages are verbose and debugging messages are printed.
217
218 (define /object-verbose? #f)
219
220 ;; Cover fn to set verbosity.
221
222 (define (object-set-verbose! verbose?)
223   (set! /object-verbose? verbose?)
224 )
225
226 ;; Signal error if not class/object.
227
228 (define (/class-check maybe-class proc-name . extra-text)
229   (if (not (class? maybe-class))
230       (apply /object-error
231              (append! (list proc-name maybe-class "not a class")
232                       extra-text)))
233   /object-unspecified
234 )
235
236 (define (/object-check-name maybe-name proc-name . extra-text)
237   (if (not (symbol? maybe-name))
238       (apply /object-error
239              (append! (list proc-name maybe-name) extra-text)))
240   /object-unspecified
241 )
242
243 (define (/object-check maybe-object proc-name . extra-text)
244   (if (not (object? maybe-object))
245       (apply /object-error
246              (append! (list proc-name maybe-object "not an object")
247                       extra-text)))
248   /object-unspecified
249 )
250
251 ;; Main routine to flag a cos error.
252 ;; X is any arbitrary Scheme data.
253
254 (define (/object-error proc-name x . text)
255   (error (string-append proc-name ": "
256                         (apply string-append (map /object->string text))
257                         (if (object? x)
258                             (string-append
259                              " (class: " (/object->string (/object-class-name x))
260                              (if (method-present? x 'get-name)
261                                  (string-append ", name: "
262                                                 (/object->string (send x 'get-name)))
263                                  "")
264                              ")")
265                             "")
266                         "")
267          x)
268 )
269
270 ;; Utility to count the number of non-#f elements in FLAGS.
271
272 (define (/object-count-true flags)
273   (let loop ((result 0) (flags flags))
274     (if (null? flags)
275         result
276         (loop (+ result (if (car flags) 1 0))
277               (cdr flags))))
278 )
279
280 ;; If S is a symbol, convert it to a string.
281 ;; Otherwise S must be a string, returned unchanged.
282
283 (define (/object->string s)
284   (cond ((symbol? s) (symbol->string s))
285         ((string? s) s)
286         (else (error "not a symbol or string" s)))
287 )
288 \f
289 ;; Low level class operations.
290
291 ;; Return boolean indicating if X is a class.
292
293 (define (class? class)
294   (and (vector? class) (eq? /class-tag (vector-ref class 0)))
295 )
296
297 ;; Accessors.
298
299 (define (/class-name class) (vector-ref class 1))
300 (define (/class-uid class) (vector-ref class 2))
301 (define (/class-parent-name class) (vector-ref class 3))
302 (define (/class-elements class) (vector-ref class 4))
303 (define (/class-methods class) (vector-ref class 5))
304 (define (/class-all-initial-values class) (vector-ref class 6))
305 (define (/class-method-cache class) (vector-ref class 7))
306 (define (/class-class-desc class) (vector-ref class 8))
307
308 (define (/class-set-uid! class uid)
309   (vector-set! class 2 uid)
310 )
311
312 (define (/class-set-methods! class method-alist)
313   (vector-set! class 5 method-alist)
314 )
315
316 (define (/class-set-all-initial-values! class init-list)
317   (vector-set! class 6 init-list)
318 )
319
320 (define (/class-set-method-cache! class all-meth-list)
321   (vector-set! class 7 all-meth-list)
322 )
323
324 (define (/class-set-class-desc! class parent-list)
325   (vector-set! class 8 parent-list)
326 )
327
328 ;; Make a class.
329 ;; The new definition overrides any existing definition.
330
331 (define (/class-make! name parent-name elements)
332   (let ((class (vector /class-tag name
333                        #f ;; uid filled in later
334                        parent-name elements
335                        '() ;; methods, none yet
336                        #f #f #f))
337         (list-entry (assq name /class-list)))
338     (if list-entry
339         (let ((uid (/class-uid (cdr list-entry))))
340           (/class-set-uid! class uid)
341           (set-cdr! list-entry class))
342         (let ((uid (vector-length /class-table)))
343           (/class-set-uid! class uid)
344           (set! /class-table (list->vector
345                               (append (vector->list /class-table)
346                                       (list class))))
347           (set! /class-list (acons name class /class-list))))
348     class)
349 )
350
351 ;; Lookup a class given its name.
352 ;; The result is the class or #f if not found.
353
354 (define (class-lookup name) (assq-ref /class-list name))
355
356 ;; Lookup a class given its uid.
357
358 (define (/class-lookup-uid uid) (vector-ref /class-table uid))
359
360 ;; Return a list of all direct parent classes of CLASS.
361 ;; The list can have at most one element.
362 ;; this is for callers that prefer a list result.
363
364 (define (/class-parent-classes class)
365   (if (/class-parent-name class)
366       (let ((parent (class-lookup (/class-parent-name class))))
367         (if parent
368             (list parent)
369             ;; The proc name we pass here is made up as we don't
370             ;; want it to be the name of an internal proc.
371             (/object-error "class" parent "not a class")))
372       '())
373 )
374
375 ;; Cover proc of /class-name for the outside world to use.
376 ;; The result is the name of the class or #f if CLASS is not a class.
377 ;; We could issue an error here, but to be consistent with object-class-name
378 ;; we don't.
379
380 (define (class-name class)
381   (if (class? class)
382       (/class-name class)
383       #f)
384 )
385 \f
386 ;; Class descriptor utilities.
387 ;; A class-descriptor is:
388 ;; (class offset child-backpointer [parent-descriptor])
389
390 (define (/class-desc? maybe-class-desc)
391   (and (pair? maybe-class-desc)
392        (class? (car maybe-class-desc)))
393 )
394 (define /class-desc-class car)
395 (define /class-desc-offset cadr)
396 (define /class-desc-child caddr)
397 (define /class-desc-parents cdddr) ;; nil or list of one element
398
399 ;; Compute the class descriptor of CLASS.
400 ;; OFFSET is the beginning offset in the element vector.
401 ;; We can assume the parents of CLASS have already been initialized.
402 ;;
403 ;; A class-descriptor is:
404 ;; (class offset child (parent-entry))
405 ;; CLASS is the class? data structure of the class.
406 ;; OFFSET is the offset into the object vector of the baseclass's elements.
407 ;; CHILD is the backlink to the direct child class or #f if no subclass.
408 ;; PARENT-ENTRY is the class descriptor of the parent class.
409
410 (define (/class-compute-class-desc class offset child)
411
412   ;; OFFSET must be global to the calculation because it is continually
413   ;; incremented as we recurse down through the hierarchy (actually, as we
414   ;; traverse back up).  At any point in time it is the offset from the start
415   ;; of the element vector of the next class's elements.
416   ;; Object elements are laid out using a depth first traversal of the
417   ;; inheritance tree.
418
419   (define (compute1 class child)
420
421     ;; Build the result first, then build our parents so that our parents have
422     ;; the right value for the CHILD-BACKPOINTER field.
423     ;; FIXME: Can't assume append! works that way.
424     ;; Use a bogus value (999) for offset for the moment.
425     ;; The correct value is set later.
426
427     (let ((result (list class 999 child)))
428
429       ;; Recurse on the parent.
430
431       (if (/class-parent-name class)
432           (let ((parent (class-lookup (/class-parent-name class))))
433             (if (not parent)
434                 ;; The proc name we pass here is made up as we don't
435                 ;; want it to be the name of an internal proc.
436                 (/object-error "class" (car parents) "not a class"))
437
438             (let ((parent-desc (compute1 parent result)))
439
440               ;; We use `append!' here as the location of `result' is now fixed
441               ;; so that our parent's child-backpointer remains stable.
442               (append! result (list parent-desc)))))
443
444       (list-set! result 1 offset)
445       (set! offset (+ offset (length (/class-elements class))))
446       result))
447
448   (compute1 class child)
449 )
450
451 ;; Return the top level class-descriptor of CLASS-DESC.
452
453 (define (/class-desc-top class-desc)
454   (if (/class-desc-child class-desc)
455       (/class-desc-top (/class-desc-child class-desc))
456       class-desc)
457 )
458
459 ;; Pretty print a class descriptor.
460
461 (define (class-desc-dump class-desc)
462   (let* ((cep (current-error-port))
463          (top-desc (/class-desc-top class-desc))
464          (spaces (lambda (n port)
465                    (display (make-string n #\space) port)))
466          (writeln (lambda (indent port . args)
467                     (spaces indent port)
468                     (for-each (lambda (arg) (display arg port))
469                               args)
470                     (newline port)))
471          )
472     (letrec ((dump (lambda (cd indent)
473                      (writeln indent cep "Class: "
474                               (/class-name (/class-desc-class cd)))
475                      (writeln indent cep "  offset: "
476                               (/class-desc-offset cd))
477                      (writeln indent cep "  child:       "
478                               (if (/class-desc-child cd)
479                                   (/class-name (/class-desc-class
480                                                 (/class-desc-child cd)))
481                                   "-top-"))
482                      (for-each (lambda (parent-cd) (dump parent-cd (+ indent 4)))
483                                (/class-desc-parents cd))
484                      )))
485       (display "Top level class: " cep)
486       (display (/class-name (/class-desc-class top-desc)) cep)
487       (newline cep)
488       (dump class-desc 0)
489       ))
490 )
491 \f
492 ;; Low level object utilities.
493
494 ;; Make an object.
495 ;; All elements get initial (or unbound) values.
496
497 (define (/object-make! class)
498   (/class-check-init! class)
499   (apply vector (append! (list /object-tag
500                                (/class-name class)
501                                (/class-uid class))
502                          (/class-all-initial-values class)))
503 )
504
505 ;; Make an object using VALUES.
506 ;; VALUES must specify all elements in the class (and parent classes).
507
508 (define (/object-make-with-values! class values)
509   (/class-check-init! class)
510   (apply vector (append! (list /object-tag
511                                (/class-name class)
512                                (/class-uid class))
513                          values))
514 )
515
516 ;; Copy an object.
517 ;; WARNING: A shallow copy is currently done on the elements!
518
519 (define (/object-copy obj)
520   (/object-vector-copy obj)
521 )
522
523 ;; Accessors.
524
525 (define (/object-class-name obj) (vector-ref obj 1))
526 (define (/object-class-uid obj) (vector-ref obj 2))
527
528 (define (/object-class-desc obj)
529   (/class-class-desc (/object-class obj))
530 )
531
532 (define (/object-class obj)
533   (/class-lookup-uid (/object-class-uid obj))
534 )
535
536 (define (/object-elm-get obj elm-offset)
537   (vector-ref obj elm-offset)
538 )
539
540 (define (/object-elm-set! obj elm-offset new-val)
541   (vector-set! obj elm-offset new-val)
542   /object-unspecified
543 )
544
545 ;; Return boolean indicating if X is an object.
546
547 (define (object? obj)
548   (and (vector? obj)
549        (>= (vector-length obj) 3)
550        (eq? /object-tag (vector-ref obj 0)))
551 )
552
553 ;; Return the class of an object.
554
555 (define (object-class obj)
556   (/object-check obj "object-class")
557   (/object-class obj)
558 )
559
560 ;; Cover proc of /object-class-name for the outside world to use.
561 ;; The result is the name of the class or #f if OBJ is not an object.
562
563 (define (object-class-name obj)
564   (if (object? obj)
565       (/object-class-name obj)
566       #f)
567 )
568 \f
569 ;; Class operations.
570
571 ;; Return the list of initial values for CLASS.
572 ;; The result does not include parent classes.
573
574 (define (/class-my-initial-values class)
575   (map cadr (/class-elements class))
576 )
577
578 ;; Initialize class if not already done.
579 ;; FIXME: Need circularity check.  Later.
580
581 (define (/class-check-init! class)
582   ;; This should be fast the second time through, so don't do any
583   ;; computation until we know it's necessary.
584
585   (if (/class-all-initial-values class)
586
587       #t ;; nothing to do
588
589       (begin
590
591         ;; First pass ensures all parents are initialized.
592         (for-each /class-check-init!
593                   (/class-parent-classes class))
594
595         ;; Next pass initializes the initial value list.
596         (letrec ((get-inits
597                   (lambda (class)
598                     (let ((parents (/class-parent-classes class)))
599                       (append (apply append (map get-inits parents))
600                               (/class-my-initial-values class))))))
601
602           (let* ((parents (/class-parent-classes class))
603                  (inits (append (apply append (map get-inits parents))
604                                 (/class-my-initial-values class))))
605             (/class-set-all-initial-values! class inits)))
606
607         ;; Next pass initializes the class's class-descriptor.
608         ;; Object elements begin at offset 3 in the element vector.
609         (/class-set-class-desc! class
610                                 (/class-compute-class-desc class 3 #f))
611         ))
612
613   /object-unspecified
614 )
615
616 ;; Make a class.
617 ;;
618 ;; PARENTS is the name of parent class as a list, i.e. () or (<parent>).
619 ;; It's a list just in case multiple-inheritance is added one day.
620 ;; The parent need not exist yet, though it must exist when the class
621 ;; is first instantiated.
622 ;; ELMS is a either a list of either element names or name/value pairs.
623 ;; Elements without initial values are marked as "unbound".
624 ;; UNUSED must be the empty list, it will eventually be deleted.
625
626 (define (class-make name parents elms unused)
627   (if (> (length parents) 1)
628       (/object-error "class-make" parents "multiple-inheritance is not supported"))
629   (if (not (null? unused))
630       (/object-error "class-make" methods "unused parameter must be ()"))
631
632   (let ((elm-list #f))
633
634     ;; Mark elements without initial values as unbound, and
635     ;; compute indices into the element vector (relative to the class's
636     ;; offset).
637     ;; Elements are recorded as (symbol initial-value . vector-index)
638     (let loop ((elm-list-tmp '()) (index 0) (elms elms))
639       (if (null? elms)
640           (set! elm-list (reverse! elm-list-tmp)) ;; done
641           (if (pair? (car elms))
642               (loop (acons (caar elms)
643                            (cons (cdar elms) index)
644                            elm-list-tmp)
645                     (+ index 1)
646                     (cdr elms))
647               (loop (acons (car elms)
648                            (cons /object-unbound index)
649                            elm-list-tmp)
650                     (+ index 1)
651                     (cdr elms)))))
652
653     (let ((result (/class-make! name
654                                 (if (null? parents) #f (car parents))
655                                 elm-list)))
656
657       ;; Create the standard `make!' method.
658       ;; The caller can override afterwards if desired.
659       ;; Note that if there are any parent classes then we don't know the names
660       ;; of all of the elements yet, that is only known after the class has been
661       ;; initialized which only happens when the class is first instantiated.
662       ;; This method won't be called until that happens though so we're safe.
663       ;; This is written without knowledge of the names, it just initializes
664       ;; all elements.
665       (method-make! result 'make!
666                     (lambda args
667                       (let ((self (car args)))
668                         ;; Ensure exactly all of the elements are provided.
669                         (if (not (= (length args)
670                                     (- (vector-length self) 2)))
671                             (/object-error "make!" "" "wrong number of arguments to method `make!'"))
672                         (/object-make-with-values! (/object-class self)
673                                                    (cdr args)))))
674
675       result))
676 )
677
678 ;; Create an object of a class CLASS.
679
680 (define (new class)
681   (/class-check class "new")
682
683   (if /object-verbose?
684       (display (string-append "Instantiating class " (/class-name class) ".\n")
685                (current-error-port)))
686
687   (/object-make! class)
688 )
689
690 ;; Make a copy of OBJ.
691 ;; WARNING: A shallow copy is done on the elements!
692
693 (define (object-copy obj)
694   (/object-check obj "object-copy")
695   (/object-copy obj)
696 )
697
698 ;; Assign object SRC to object DST.
699 ;; They must have the same class.
700
701 (define (object-assign! dst src)
702   (/object-check dst "object-assign!")
703   (/object-check src "object-assign!")
704   (if (not (eq? (/object-class-name dst) (/object-class-name src)))
705       (/object-error "object-assign" (list dst src) "not same class"))
706
707   (let ((n (vector-length dst)))
708     (let loop ((i 0))
709       (if (< i n)
710           (begin
711             (vector-set! dst i (vector-ref src i))
712             (loop (+ i 1))))))
713   /object-unspecified
714 )
715
716 ;; Utility to define a standard `make!' method.
717 ;; A standard make! method is one in which all it does is initialize
718 ;; fields from args.
719
720 (define (method-make-make! class args)
721   (let ((lambda-expr
722          (append (list 'lambda (cons 'self args))
723                  (map (lambda (elm) (list 'elm-set! 'self
724                                           (list 'quote elm) elm))
725                       args)
726                  '(self))))
727     (method-make! class 'make! (eval1 lambda-expr)))
728 )
729
730 ;; The "standard" way to invoke `make!' is (send (new class) 'make! ...).
731 ;; This puts all that in a cover function.
732
733 (define (make class . operands)
734   (apply send (append (cons (new class) '()) '(make!) operands))
735 )
736
737 ;; Return #t if class X is a subclass of BASE-NAME.
738
739 (define (/class-subclass? base-name x)
740   (if (eq? base-name (/class-name x))
741       #t
742       (let ((parent-name (/class-parent-name x)))
743         (if parent-name
744             (/class-subclass? base-name (class-lookup parent-name))
745             #f)))
746 )
747
748 ;; Return #t if OBJECT is an instance of CLASS.
749 ;; This does not signal an error if OBJECT is not an object as this is
750 ;; intended to be used in class predicates.
751
752 (define (class-instance? class object)
753   (/class-check class "class-instance?")
754   (if (object? object)
755       (/class-subclass? (/class-name class) (/object-class object))
756       #f)
757 )
758
759 ;; Subroutine of define-class.
760 ;; Parse a define-class member list and return a list of five elements:
761 ;; - list of all members
762 ;; - list of public readable members
763 ;; - list of public writable members
764 ;; - list of private readable members
765 ;; - list of private writable members
766 ;; MEMBER-SPEC is a list of members, with private members prefixed with '/',
767 ;; and writable members prefixed with '!'.  / and ! may appear in any order.
768 ;; Each element is either member-name or (member-name . initial-value).
769
770 (define (/parse-member-list member-spec)
771   (let loop ((member-spec member-spec)
772              (members nil)
773              (public-readable nil)
774              (public-writable nil)
775              (private-readable nil)
776              (private-writable nil))
777     (if (null? member-spec)
778         (list (reverse! members)
779               (reverse! public-readable)
780               (reverse! public-writable)
781               (reverse! private-readable)
782               (reverse! private-writable))
783         (let* ((spec (car member-spec))
784                (sym (if (pair? spec) (car spec) spec))
785                (str (symbol->string sym)))
786           (let ((private? (string-index str #\/))
787                 (writable? (string-index str #\!)))
788             ;; ??? Assumes /,! are first characters.
789             (let* ((stripped-str (substring str (/object-count-true (list private? writable?))))
790                    (stripped-sym (string->symbol stripped-str)))
791               (loop (cdr member-spec)
792                     ;; Combine initial value if present.
793                     (cons (if (pair? spec)
794                               (cons stripped-sym (cdr spec))
795                               stripped-sym)
796                           members)
797                     (if (not private?)
798                         (cons stripped-sym public-readable)
799                         public-readable)
800                     (if (and (not private?) writable?)
801                         (cons stripped-sym public-writable)
802                         public-writable)
803                     (if private?
804                         (cons stripped-sym private-readable)
805                         private-readable)
806                     (if (and private? writable?)
807                         (cons stripped-sym private-writable)
808                         private-writable)))))))
809 )
810
811 ;; Subroutine of define-class.
812 ;; Return a list of definitions of member getters.
813
814 (define (/build-getter-defs class prefix members private?)
815   (let ((str-prefix (symbol->string prefix)))
816     (cons 'begin
817           (map (lambda (m)
818                  (let* ((elm-name (if (pair? m) (car m) m))
819                         (name (string-append (if private? "/" "")
820                                              str-prefix
821                                              (symbol->string elm-name)))
822                         (getter-name (string->symbol name)))
823                    `(define ,getter-name
824                       (elm-make-getter ,class (quote ,elm-name)))))
825                members)))
826 )
827
828 ;; Subroutine of define-class.
829 ;; Return a list of definitions of member getters.
830
831 (define (/build-setter-defs class prefix members private?)
832   (let ((str-prefix (symbol->string prefix)))
833     (cons 'begin
834           (map (lambda (m)
835                  (let* ((elm-name (if (pair? m) (car m) m))
836                         (name (string-append (if private? "/" "")
837                                              str-prefix
838                                              "set-"
839                                              (symbol->string elm-name)
840                                              "!"))
841                         (getter-name (string->symbol name)))
842                    `(define ,getter-name
843                       (elm-make-setter ,class (quote ,elm-name)))))
844                members)))
845 )
846
847 ;; Main routine to define a class.
848 ;;
849 ;; This defines several things:
850 ;; - the class object with the specified class members
851 ;; - a predicate to identify instances of this class, named "class?"
852 ;; - getters and setters for each member
853 ;;
854 ;; Private members are specified as /member.
855 ;; Writable members are specified as !member.
856 ;; / and ! may be combined in any order.
857 ;;
858 ;; By convention name is formatted as <class-name>.
859
860 (defmacro define-class (name prefix parents members)
861   (let* ((parsed-members (/parse-member-list members))
862          (str-name (symbol->string name))
863          (str-name-len (string-length str-name))
864          (name-sans-decorations (substring str-name 1 (- str-name-len 1))))
865     ;; Enforce the <class> naming convention.
866     (if (or (not (eq? (string-ref str-name 0) #\<))
867             (not (eq? (string-ref str-name (- str-name-len 1)) #\>)))
868         (/object-error "define-class" name " not formatted as <class>: "))
869     `(begin
870        (define ,name (class-make (quote ,name) (quote ,parents) (quote ,(car parsed-members)) nil))
871        ,(/build-getter-defs name prefix (list-ref parsed-members 1) #f)
872        ,(/build-setter-defs name prefix (list-ref parsed-members 2) #f)
873        ,(/build-getter-defs name prefix (list-ref parsed-members 3) #t)
874        ,(/build-setter-defs name prefix (list-ref parsed-members 4) #t)
875        (define ,(string->symbol (string-append name-sans-decorations "?"))
876          (lambda (obj) (class-instance? ,name obj)))))
877 )
878 \f
879 ;; Element operations.
880
881 ;; Lookup an element in a class-desc.
882 ;; The result is elm-index or #f if not found.
883
884 (define (/class-lookup-element class-desc elm-name)
885   (let* ((class (/class-desc-class class-desc))
886          (elm (assq elm-name (/class-elements class))))
887     (if elm
888         (+ (cddr elm) ;; elm is (name init-value . index)
889            (/class-desc-offset class-desc))
890         (let ((parents (/class-desc-parents class-desc)))
891           (if (null? parents)
892               #f
893               (/class-lookup-element (car parents) elm-name)))))
894 )
895
896 ;; Return a boolean indicating if ELM-NAME is bound in OBJ.
897
898 (define (elm-bound? obj elm-name)
899   (/object-check obj "elm-bound?")
900   (let ((index (/class-lookup-element (/object-class-desc obj) elm-name)))
901     (if index
902         (not (eq? (/object-elm-get obj index) /object-unbound))
903         (/object-error "elm-bound?" obj "element not present: " elm-name)))
904 )
905
906 ;; Subroutine of elm-get.
907
908 (define (/elm-make-method-getter self elm-name)
909   (/object-check self "elm-get")
910   (let ((index (/class-lookup-element (/object-class-desc self) elm-name)))
911     (if index
912         (procedure->memoizing-macro
913          (lambda (exp env)
914            `(lambda (obj)
915               (/object-elm-get obj ,index))))
916         (/object-error "elm-get" self "element not present: " elm-name)))
917 )
918
919 ;; Get an element from an object.
920 ;; If OBJ is `self' then the caller is required to be a method and we emit
921 ;; memoized code.  Otherwise we do things the slow way.
922 ;; ??? There must be a better way.
923 ;; What this does is turn
924 ;; (elm-get self 'foo)
925 ;; into
926 ;; ((/elm-make-method-get self 'foo) self)
927 ;; Note the extra set of parens.  /elm-make-method-get then does the lookup of
928 ;; foo and returns a memoizing macro that returns the code to perform the
929 ;; operation with O(1).  Cute, but I'm hoping there's an easier/better way.
930
931 (defmacro elm-get (self elm-name)
932   (if (eq? self 'self)
933       `(((/elm-make-method-getter ,self ,elm-name)) ,self)
934       `(elm-xget ,self ,elm-name))
935 )
936
937 ;; Subroutine of elm-set!.
938
939 (define (/elm-make-method-setter self elm-name)
940   (/object-check self "elm-set!")
941   (let ((index (/class-lookup-element (/object-class-desc self) elm-name)))
942     (if index
943         (procedure->memoizing-macro
944          (lambda (exp env)
945            `(lambda (obj new-val)
946               (/object-elm-set! obj ,index new-val))))
947         (/object-error "elm-set!" self "element not present: " elm-name)))
948 )
949
950 ;; Set an element in an object.
951 ;; This can only be used by methods.
952 ;; See the comments for `elm-get'!
953
954 (defmacro elm-set! (self elm-name new-val)
955   (if (eq? self 'self)
956       `(((/elm-make-method-setter ,self ,elm-name)) ,self ,new-val)
957       `(elm-xset! ,self ,elm-name ,new-val))
958 )
959
960 ;; Get an element from an object.
961 ;; This is for invoking from outside a method, and without having to
962 ;; use elm-make-getter.  It should be used sparingly.
963
964 (define (elm-xget obj elm-name)
965   (/object-check obj "elm-xget")
966   (let ((index (/class-lookup-element (/object-class-desc obj) elm-name)))
967     (if index
968         (/object-elm-get obj index)
969         (/object-error "elm-xget" obj "element not present: " elm-name)))
970 )
971
972 ;; Set an element in an object.
973 ;; This is for invoking from outside a method, and without having to
974 ;; use elm-make-setter.  It should be used sparingly.
975
976 (define (elm-xset! obj elm-name new-val)
977   (/object-check obj "elm-xset!")
978   (let ((index (/class-lookup-element (/object-class-desc obj) elm-name)))
979     (if index
980         (/object-elm-set! obj index new-val)
981         (/object-error "elm-xset!" obj "element not present: " elm-name)))
982 )
983
984 ;; Return a boolean indicating if object OBJ has element ELM-NAME.
985
986 (define (elm-present? obj elm-name)
987   (/object-check obj "elm-present?")
988   (->bool (/class-lookup-element (/object-class-desc obj) elm-name))
989 )
990
991 ;; Return lambda to get element ELM-NAME in CLASS.
992 ;; FIXME: validate elm-name.
993
994 (define (elm-make-getter class elm-name)
995   (/class-check class "elm-make-getter")
996   ;; We use delay here as we can't assume parent classes have been
997   ;; initialized yet.
998   (let ((fast-index (delay (/class-lookup-element
999                             (/class-class-desc class) elm-name))))
1000     (lambda (obj)
1001       (let ((index (force fast-index)))
1002         (/object-elm-get obj index))))
1003 )
1004
1005 ;; Return lambda to set element ELM-NAME in CLASS.
1006 ;; FIXME: validate elm-name.
1007
1008 (define (elm-make-setter class elm-name)
1009   (/class-check class "elm-make-setter")
1010   ;; We use delay here as we can't assume parent classes have been
1011   ;; initialized yet.
1012   (let ((fast-index (delay (/class-lookup-element
1013                             (/class-class-desc class) elm-name))))
1014     (lambda (obj newval)
1015       (let ((index (force fast-index)))
1016         (/object-elm-set! obj index newval))))
1017 )
1018 \f
1019 ;; Method operations.
1020
1021 ;; Lookup the next method in a class.
1022 ;; This means begin the search in the parent.
1023
1024 (define (/method-lookup-next class-desc method-name)
1025   (let ((parent-descs (/class-desc-parents class-desc)))
1026     (if (null? parent-descs)
1027         #f
1028         (let ((parent-desc (car parent-descs)))
1029           (/method-lookup parent-desc method-name))))
1030 )
1031
1032 ;; Lookup a method in a class.
1033 ;; The result is (class-desc . method).  If the method is found in a parent
1034 ;; class, the associated parent class descriptor is returned.
1035
1036 (define (/method-lookup class-desc method-name)
1037   (if /object-verbose?
1038       (display (string-append "Looking up method " method-name " in "
1039                               (/class-name (/class-desc-class class-desc)) ".\n")
1040                (current-error-port)))
1041
1042   (let ((meth (assq method-name (/class-methods (/class-desc-class class-desc)))))
1043     (if meth
1044         ;; Found.
1045         (cons class-desc (cdr meth))
1046         ;; Method not found, search parents.
1047         (/method-lookup-next class-desc method-name)))
1048 )
1049
1050 ;; Return a boolean indicating if object OBJ has method NAME.
1051
1052 (define (method-present? obj name)
1053   (/object-check obj "method-present?")
1054   (->bool (/method-lookup (/object-class-desc obj) name))
1055 )
1056
1057 ;; Add a method to a class.
1058
1059 (define (method-make! class method-name method)
1060   (/class-check class "method-make!")
1061   (/object-check-name method-name "method-make!" "method-name must be a symbol")
1062   (if (not (procedure? method))
1063       (/object-error "method-make!" method "method must be a procedure"))
1064   (/class-set-methods! class (acons method-name method
1065                                     (/class-methods class)))
1066   /object-unspecified
1067 )
1068
1069 ;; Utility to create "forwarding" methods.
1070 ;; METHODS are forwarded to class member ELM-NAME, assumed to be an object.
1071 ;; The created methods take a variable number of arguments.
1072 ;; Argument length checking will be done by the receiving method.
1073 ;; FIXME: ensure elm-name is a symbol
1074
1075 (define (method-make-forward! class elm-name methods)
1076   (for-each (lambda (method-name)
1077               (method-make!
1078                class method-name
1079                (eval1 `(lambda args
1080                          (apply send
1081                                 (cons (elm-get (car args)
1082                                                (quote ,elm-name))
1083                                       (cons (quote ,method-name)
1084                                             (cdr args))))))))
1085             methods)
1086   /object-unspecified
1087 )
1088
1089 ;; Utility of send, send-next.
1090
1091 (define (/object-method-notify obj method-name maybe-next)
1092   (set! /object-verbose? #f)
1093   (display (string-append "Sending " maybe-next method-name " to"
1094                           (if (method-present? obj 'get-name)
1095                               (let ((name (send obj 'get-name)))
1096                                 (if (or (symbol? name) (string? name))
1097                                     (string-append " object " name)
1098                                     ""))
1099                               "")
1100                           " class " (object-class-name obj) ".\n")
1101            (current-error-port))
1102   (set! /object-verbose? #t)
1103 )
1104
1105 ;; Invoke a method in an object.
1106 ;; When the method is invoked, the (possible parent class) object in which the
1107 ;; method is found is passed to the method.
1108 ;; ??? The word `send' comes from "sending messages".  Perhaps should pick
1109 ;; a better name for this operation, except this is deprecated as a public API.
1110
1111 (define (send obj method-name . args)
1112   (/object-check obj "send")
1113   (if /object-verbose? (/object-method-notify obj method-name ""))
1114
1115   (let ((class-desc.meth (/method-lookup (/object-class-desc obj)
1116                                          method-name)))
1117     (if class-desc.meth
1118         (apply (cdr class-desc.meth)
1119                (cons obj args))
1120         (/object-error "send" obj "method not supported: " method-name)))
1121 )
1122
1123 ;; Invoke the next method named METHOD-NAME in the heirarchy of OBJ.
1124 ;; i.e. the method that would have been invoked if the calling method
1125 ;; didn't exist.
1126 ;; CLASS-NAME is the class of the invoking method.
1127 ;; It is present to simplify things: otherwise we have to either include in
1128 ;; objects the notion a current class or specialization, or include the class
1129 ;; as an argument to methods.
1130 ;; This may only be called by a method.
1131 ;; ??? Ideally we shouldn't need either CLASS-NAME or METHOD-NAME arguments.
1132 ;; They could be removed with a bit of effort, but is it worth it?
1133 ;; One possibility is if method-make! was a macro, then maybe send-next could
1134 ;; work with method-make! and get the values from it.
1135 ;;
1136 ;; While `send' is deprecated, this is not, yet anyway.
1137
1138 (define (send-next obj class-name method-name . args)
1139   (/object-check obj "send-next")
1140   (if /object-verbose? (/object-method-notify obj method-name "next "))
1141
1142   (let* ((class (class-lookup class-name)) ;; FIXME: slow
1143          (class-desc.meth (/method-lookup-next (/class-class-desc class)
1144                                                method-name)))
1145     (if class-desc.meth
1146         (apply (cdr class-desc.meth)
1147                (cons obj args))
1148         (/object-error "send-next" obj "method not supported: " method-name)))
1149 )
1150
1151 ;; Create an interface.
1152 ;; This defines a function named NAME that invokes METHOD-NAME.
1153
1154 (defmacro define-interface (name method-name . arg-list)
1155   `(define (,name object ,@arg-list)
1156      (send object (quote ,method-name) ,@arg-list))
1157 )
1158
1159 ;; Wrapper to define a method.
1160 ;; `self' must be the first argument.
1161
1162 (defmacro define-method (class name args . body)
1163   `(method-make! ,class (quote ,name) ,(cons 'lambda (cons args body)))
1164 )
1165 \f
1166 ;; Miscellaneous publically accessible utilities.
1167
1168 ;; Return list of all classes.
1169
1170 (define (class-list) (map cdr /class-list))
1171
1172 ;; Utility to map over a class and all its parent classes, recursively.
1173
1174 (define (class-map-over-class proc class)
1175   (cons (proc class)
1176         (map (lambda (class) (class-map-over-class proc class))
1177              (/class-parent-classes class)))
1178 )
1179
1180 ;; Return class tree of a class or object.
1181
1182 (define (class-tree class-or-object)
1183   (cond ((class? class-or-object)
1184          (class-map-over-class class-name class-or-object))
1185         ((object? class-or-object)
1186          (class-map-over-class class-name (/object-class class-or-object)))
1187         (else (/object-error "class-tree" class-or-object
1188                              "not a class or object")))
1189 )
1190
1191 ;; Return names of each alist.
1192
1193 (define (/class-alist-names class)
1194   (list (/class-name class)
1195         (map car (/class-elements class))
1196         (map car (/class-methods class)))
1197 )
1198
1199 ;; Return complete layout of class-or-object.
1200
1201 (define (class-layout class-or-object)
1202   (cond ((class? class-or-object)
1203          (class-map-over-class /class-alist-names class-or-object))
1204         ((object? class-or-object)
1205          (class-map-over-class /class-alist-names (/object-class class-or-object)))
1206         (else (/object-error "class-layout" class-or-object
1207                              "not a class or object")))
1208 )
1209
1210 ;; Define the getter for a list of elements of a class.
1211
1212 (defmacro define-getters (class class-prefix elm-names)
1213   (cons 'begin
1214         (map (lambda (elm-name)
1215                (if (pair? elm-name)
1216                    `(define ,(symbol-append class-prefix '- (cdr elm-name))
1217                       (elm-make-getter ,class (quote ,(car elm-name))))
1218                    `(define ,(symbol-append class-prefix '- elm-name)
1219                       (elm-make-getter ,class (quote ,elm-name)))))
1220              elm-names))
1221 )
1222
1223 ;; Define the setter for a list of elements of a class.
1224
1225 (defmacro define-setters (class class-prefix elm-names)
1226   (cons 'begin
1227         (map (lambda (elm-name)
1228                (if (pair? elm-name)
1229                    `(define ,(symbol-append class-prefix '-set- (cdr elm-name) '!)
1230                       (elm-make-setter ,class (quote ,(car elm-name))))
1231                    `(define ,(symbol-append class-prefix '-set- elm-name '!)
1232                       (elm-make-setter ,class (quote ,elm-name)))))
1233              elm-names))
1234 )
1235
1236 ;; Make an object, specifying values for particular elements.
1237
1238 (define (vmake class . args)
1239   (let ((obj (new class)))
1240     (let ((unrecognized (send obj 'vmake! args)))
1241       (if (null? unrecognized)
1242           obj
1243           (error "vmake: unknown options:" unrecognized))))
1244 )
1245
1246 ;; Like assq but based on the `name' element.
1247 ;; WARNING: Slow.
1248
1249 (define (object-assq name obj-list)
1250   (find-first (lambda (o) (eq? (elm-xget o 'name) name))
1251               obj-list)
1252 )
1253
1254 ;; Like memq but based on the `name' element.
1255 ;; WARNING: Slow.
1256
1257 (define (object-memq name obj-list)
1258   (let loop ((r obj-list))
1259     (cond ((null? r) #f)
1260           ((eq? name (elm-xget (car r) 'name)) r)
1261           (else (loop (cdr r)))))
1262 )
1263 \f
1264 ;; Misc. internal utilities.
1265
1266 ;; We need a fast vector copy operation.
1267 ;; If `vector-copy' doesn't exist (which is assumed to be the fast one),
1268 ;; provide a simple version.
1269
1270 (if (defined? 'vector-copy)
1271     (define /object-vector-copy vector-copy)
1272     (define (/object-vector-copy v) (list->vector (vector->list v)))
1273 )