;;; Copyright (c) 2002 Oscar Waddell and R. Kent Dybvig ;;; Permission is hereby granted, free of charge, to any person obtaining a ;;; copy of this software and associated documentation files (the "Software"), ;;; to deal in the Software without restriction, including without limitation ;;; the rights to use, copy, modify, merge, publish, distribute, sublicense, ;;; and/or sell copies of the Software, and to permit persons to whom the ;;; Software is furnished to do so, subject to the following conditions: ;;; The above copyright notice and this permission notice shall be included in ;;; all copies or substantial portions of the Software. ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;;; DEALINGS IN THE SOFTWARE. ;;; Last modified: October 2010 ;;; Acknowledgements ;;; - Michael Lenaghan of frogware, Inc., contributed to the define-class ;;; interface handling code. ;;; Possible names: ;;; Chez Scheme OOP System (Chez SOOP) #| define-class: definition -> (define-class (class-name class-formal*) (base-name base-actual*) clause*) clause -> (implements interface*) | (ivars ivar*) | (init init-expr*) | (methods method*) | (constructor id) | (predicate id) | (prefix string) ivar -> (modifier* ivar-name ivar-expression) modifier -> mutability | visibility mutability -> mutable | immutable visibility -> public | private method -> (method-name formals method-body+) formals -> (var*) | var | (var+ . var) notes: - at most one of each kind of clause may be present - at most one of each kind of modifier may be present - multiple methods of the same name but different formals can be present products: - class-name is bound to class information in the expand-time environment - make-class-name (or specified constructor name) is bound to creation procedure - class-name? (or specified predicate name) is bound to predicate procedure - new (not inherited) method names are bound to method-dispatch procedures - for each public ivar, -ivar is bound to an accessor procedure, where is the specified prefix or "class-name-" - for each public, mutable ivar, -ivar-set! is bound to a mutator procedure, where is the specified prefix or "class-name-" define-interface: definition -> (define-interface interface-name method*) | (define-interface interface-name base-name method*) method -> (method-name formals) products: - interface-name is bound to interface information in the expand-time environment - new (not inherited) method names are bound to method-dispatch procedures |# #| Todo: - try using record-constructor, record-accessor, and record-mutator! instead of $record, $object-ref, and $object-set! - add a nongenerative clause ala define-record-type - squawk if any methods or interfaces - default to generative - need more tests: - init-expr checks - profile to see if all paths are covered by test suite - consider: don't degenerate to record definition facility - flush implicit nongenerative option - flush visibility keywords - flush getter/setter => less complex but less useful - consider: alternative version - and method definitions are only products - (make-class-name arg ...) replaced by (make arg ...) - (class-name? arg) replaced by (isa? arg) - accessors and mutators replaced by (open-instance (ivar ...) expr expr ...) - upside: prettier, less namespace clutter - downside: can't use module system directly to control visibility of makers, predicates, accessors, and mutators separately - downside: maker, predicate, accessors, mutators aren't first-class procedures - consider: inherited ivars - (inherited-ivar ivar ...) - inheritable / noninheritable ivar modifiers - (frogware) Error messages for incorrect syntax should be better. (You often get a generic "invalid syntax" message and have to hunt to figure out what caused it.) - consider exposing query-interface somehow - (interface I x) => version of x to which I's methods are applicable - (interface->instance (interface I x)) => x - interfaces and inheritance - either require parent for define-interface or hide - consider allowing multiple inheritance (including zero parents) for interfaces |# #| reaching into Chez Scheme's internals for: #!base-rtd $make-record-type $record-type-field-offsets $record-type-interfaces $record $object-ref $object-set! |# #!chezscheme (library (oop helpers) (export $make-class $class? $class-formals $class-formal-bindings $class-ivar-bindings $class-minfos $class-vtable-rtd $class-ctrtd $class-vtable-expr $class-interfaces $class-init-proc $make-interface $interface? $interface-rtd $interface-minfos $instance $make-instance $instance? root-vtable-rtd make-minfo minfo-mname minfo-hidden-mname minfo-arity minfo-formals minfo-flat-formals construct-name parse-formals build-generic make-ivar-defn free-id-member ) (import (chezscheme)) (define-record-type ($class $make-class $class?) (nongenerative) (fields (immutable formals) ; (formal ...) (immutable formal-bindings) ; (((base-formal base-arg) ...) ...) (immutable ivar-bindings) ; (((ivar init) ...) ...) (immutable minfos) ; ((mname hidden-mname arity) ...) ; same mname may appear more than once (immutable vtable-rtd) (immutable ctrtd) (immutable vtable-expr) (immutable interfaces) (immutable init-proc))) (define-record-type ($interface $make-interface $interface?) (nongenerative) (fields (immutable rtd) (immutable minfos))) (define-record-type ($instance $make-instance $instance?) (nongenerative)) ;; minfos cannot be records since we insert minfos into the output of ;; define-class and define-interface and the identifiers contained within ;; these minfos must be marked/unmarked as appropriate by the expander, ;; which delves into vectors but not records (define make-minfo (lambda (mname hidden-name arity formals flat-formals) (vector mname hidden-name arity formals flat-formals))) (define minfo-mname (lambda (x) (vector-ref x 0))) (define minfo-hidden-mname (lambda (x) (vector-ref x 1))) (define minfo-arity (lambda (x) (vector-ref x 2))) (define minfo-formals (lambda (x) (vector-ref x 3))) (define minfo-flat-formals (lambda (x) (vector-ref x 4))) (define root-vtable-rtd (#%$make-record-type #!base-rtd #!base-rtd "root-vtable-rtd" '((immutable ptr interfaces)) #f #f)) (define construct-name (lambda (template-identifier . args) (datum->syntax template-identifier (string->symbol (apply string-append (map (lambda (x) (if (string? x) x (symbol->string (syntax->datum x)))) args)))))) (define parse-formals (lambda (fmls) (let f ([ids fmls] [n 0]) (syntax-case ids () [(car . cdr) (if (identifier? #'car) (f #'cdr (fx+ n 1)) (syntax-error fmls "invalid method formals"))] [() (values n fmls)] [else (if (identifier? #'ids) (values (fx- -1 n) (let f ([ids fmls]) (syntax-case ids () [(x . d) (cons #'x (f #'d))] [x #'(x)]))) (syntax-error #'fmls "invalid method formals"))])))) (define build-generic (lambda (minfos offsets) (define cull-method (lambda (mname minfos offsets) (if (null? minfos) (values '() '() '() '()) (let ([offset (car offsets)] [minfo (car minfos)]) (let-values ([(gminfos goffsets new-minfos new-offsets) (cull-method mname (cdr minfos) (cdr offsets))]) (if (bound-identifier=? (minfo-mname minfo) mname) (values (cons minfo gminfos) (cons offset goffsets) new-minfos new-offsets) (values gminfos goffsets (cons (car minfos) new-minfos) (cons (car offsets) new-offsets)))))))) (if (null? minfos) '() (let ([generic-name (minfo-mname (car minfos))]) (let-values ([(gminfos goffsets minfos offsets) (cull-method generic-name minfos offsets)]) (cons `(,generic-name ,@(map (lambda (minfo offset) `(,(minfo-formals minfo) ,(minfo-flat-formals minfo) ,offset)) gminfos goffsets)) (build-generic minfos offsets))))))) (define (make-ivar-defn ivar mutable? ivar-offset) (with-syntax ([ivar ivar] [ivar-offset ivar-offset]) (if mutable? #'(define-syntax ivar (identifier-syntax [id (#3%$object-ref 'scheme-object ego ivar-offset)] [(set! var val) (#3%$object-set! 'scheme-object ego ivar-offset val)])) #'(define-syntax ivar (make-variable-transformer (lambda (x) (syntax-case x (set!) [id (identifier? #'id) #'(#3%$object-ref 'scheme-object ego ivar-offset)] [(set! var val) (syntax-error x "invalid assignment of immutable ivar")]))))))) (define free-id-member (lambda (id ls2) (and (not (null? ls2)) (if (free-identifier=? (car ls2) id) ls2 (free-id-member id (cdr ls2)))))) (record-writer (type-descriptor $instance) (lambda (x p wr) (fprintf p "#" (let ([rtd (record-rtd x)]) (if (eq? rtd (type-descriptor $instance)) "" (record-type-name rtd)))))) ; these aren't evaluated if placed in (oop), since (oop) has no ; run-time (variable) exports (pretty-format 'define-class '(_ (fill 0 x ...) 13 (fill 0 x ...) #f clause ...)) (pretty-format 'methods '(_ #f (bracket x (fill 0 x ...) #f e ...) ...)) (pretty-format 'ivars '(_ (bracket fill #f x ...) 6 (bracket fill #f x ...) ...)) (pretty-format 'define-interface '(alt (_ var var #f (bracket x x) ...) (_ var #f (bracket x x) ...))) ) ;;; supplies define-class, define-interface, , and ;;; and aux keywords (library (oop) (export define-interface define-class ivars public private methods self implements init constructor predicate prefix) (import (chezscheme) (oop helpers)) (define-syntax (make-compile-time-value ($make-class '() '() '() '() ; formals formal-bindings ivar-bindings minfos root-vtable-rtd (type-descriptor $instance) ; ctrtd #'(type-descriptor $instance) ; vtable-expr '() ; interfaces #'values))) ; init-expr ... (define-syntax (make-compile-time-value ($make-interface (make-record-type "base interface rtd" '()) '()))) (define-syntax define-interface (lambda (x) (define build-minfo (lambda (mname formals) (let-values ([(arity flat-formals) (parse-formals formals)]) ; discard source information (make-minfo mname "ignored" arity formals flat-formals)))) (syntax-case x () [(_ iname [method-name method-formals] ...) (and (identifier? #'iname) (andmap identifier? #'(method-name ...))) #'(define-interface iname [method-name method-formals] ...)] [(_ iname base-iname [method-name method-formals] ...) (and (identifier? #'iname) (identifier? #'base-iname) (andmap identifier? #'(method-name ...))) (lambda (r) (let ([bi (r #'base-iname)]) (unless ($interface? bi) (syntax-error #'base-iname "define-interface: unrecognized base interface")) (let ([base-mnames (with-syntax ([(#(base-mname base-hidden-name base-arity base-formals base-flat-formals) ...) ($interface-minfos bi)]) #'(base-mname ...))]) (let f ([ls #'(method-name ...)]) (unless (null? ls) (when (free-id-member (car ls) base-mnames) (syntax-error (car ls) "conflict with inherited interface method")) (f (cdr ls))))) (with-syntax ([(base-minfo ...) ($interface-minfos bi)] [(minfo ...) (map build-minfo #'(method-name ...) #'(method-formals ...))]) (with-syntax ([iface-rtd (make-record-type ($interface-rtd bi) (symbol->string (syntax->datum #'iname)) (syntax->datum (map minfo-mname #'(minfo ...))))]) (with-syntax ([((generic-name (generic-formals generic-flat-formals generic-offset) ...) ...) (build-generic #'(minfo ...) (let ([ls (#%$record-type-field-offsets #'iface-rtd)]) (list-tail ls (- (length ls) (length #'(minfo ...))))))] [opt3 (= (optimize-level) 3)]) #`(begin (define-syntax iname (make-compile-time-value ($make-interface 'iface-rtd #'(base-minfo ... minfo ...)))) (define (qi who ego) (define get-interfaces (#3%record-accessor '#,root-vtable-rtd 0)) (or (and (or opt3 (#3%record? ego)) (let ([rtd (#3%record-rtd ego)]) (and (or opt3 (#3%record? rtd '#,root-vtable-rtd)) (#3%ormap (lambda (i) (and (#3%record? i 'iface-rtd) i)) (get-interfaces rtd))))) (errorf who "not applicable to ~s" ego))) (define generic-name (let ([who 'generic-name]) ; can't ref generic-name pattern vble inside ... below (case-lambda [(ego . generic-formals) ((#3%$object-ref 'scheme-object (qi who ego) generic-offset) ego . generic-flat-formals)] ...))) ...))))))]))) (define-syntax define-class (lambda (x) (define build-minfo (lambda (mname formals hidden) (let-values ([(arity flat-formals) (parse-formals formals)]) (make-minfo mname hidden arity formals flat-formals)))) (define build-method (lambda (minfo class-name body base-minfos) (define cull-super (lambda (mname minfos) (if (null? minfos) '() (let ([minfo (car minfos)]) (if (free-identifier=? (minfo-mname minfo) mname) (cons minfo (cull-super mname (cdr minfos))) (cull-super mname (cdr minfos))))))) (with-syntax ([(formal ...) (minfo-flat-formals minfo)] [body body] [super-definition (with-syntax ([super (datum->syntax class-name 'super)]) (if (not (null? base-minfos)) (with-syntax ([(#(mname hidden-name arity formals flat-formals) ...) (cull-super (minfo-mname minfo) base-minfos)]) #'(define super (case-lambda [formals (hidden-name ego . flat-formals)] ...))) (with-syntax ([mname (minfo-mname minfo)] [class-name class-name]) #'(define-syntax super (lambda (x) (syntax-error x (format "no inherited ~s method for ~s in" 'mname 'class-name)))))))]) #'(lambda (i formal ...) (fluid-let-syntax ([ego (identifier-syntax i)]) super-definition body))))) (define unwrap-minfos (lambda (minfos) ; need list/vector structure, with arity unwrapped (with-syntax ([(#(mname hidden-name arity formals flat-formals) ...) minfos]) (with-syntax ([(arity ...) (syntax->datum #'(arity ...))]) #'(#(mname hidden-name arity formals flat-formals) ...))))) (define build-interface-vtable (lambda (minfos) (lambda (iface) (with-syntax ([irtd ($interface-rtd iface)] [(hidden ...) (map (lambda (iminfo) (let ([mname (minfo-mname iminfo)] [arity (minfo-arity iminfo)]) (let f ([minfos minfos]) (cond [(null? minfos) (syntax-error (minfo-mname iminfo) "no suitable implementation for interface method")] [(minfo-match? (car minfos) mname arity) (minfo-hidden-mname (car minfos))] [else (f (cdr minfos))])))) (unwrap-minfos ($interface-minfos iface)))]) #'(#3%$record 'irtd hidden ...))))) (define minfo-match? (lambda (minfo mname arity) (and (= arity (minfo-arity minfo)) (free-identifier=? mname (minfo-mname minfo))))) (define process-methods (lambda (class-name interfaces all-base-minfos minfos bodies) (let f ([minfos minfos]) (unless (null? minfos) (let ([mname (minfo-mname (car minfos))] [arity (minfo-arity (car minfos))]) (let f ([ls (cdr minfos)]) (unless (null? ls) (if (minfo-match? (car ls) mname arity) (syntax-error (minfo-mname (car ls)) "duplicate arity for method") (f (cdr ls)))))) (f (cdr minfos)))) (let ([base-mnames (map minfo-mname all-base-minfos)]) (let f ([base-minfos all-base-minfos] [minfos minfos] [bodies bodies] [mlambdas '()] [all-minfos '()]) (if (null? base-minfos) (let f ([minfos minfos] [bodies bodies] [mlambdas mlambdas] [all-minfos all-minfos] [generics '()]) (if (null? minfos) (begin ; We should never create a new generic method that matches the name of ; an interface method. (If the generic method made it this far then we ; know it didn't match the arity of the interface method. If it did it ; would have been filtered out.) (for-each (lambda (generic) (let ([mname (minfo-mname generic)]) (for-each (lambda (i) (for-each (lambda (m) (let ([interface-mname (minfo-mname m)]) (when (free-identifier=? mname interface-mname) (syntax-error mname "arity not supported by interface method")))) (unwrap-minfos ($interface-minfos i)))) interfaces))) generics) (list (reverse all-minfos) (reverse mlambdas) (reverse generics) ; We need to build a list of minfos that will actually be included in the class vtable-- ; ie, minfos that appear in this class and/or any super-class (but not in any interface). ; That list will match up with the vtable offsets. (let f ([possibly-included-minfos all-minfos] [included-minfos '()]) (if (null? possibly-included-minfos) included-minfos (let ([minfo (car possibly-included-minfos)]) (let ([mname (minfo-mname minfo)] [arity (minfo-arity minfo)]) (f (cdr possibly-included-minfos) (if (ormap (lambda (i) (ormap (lambda (m) (minfo-match? m mname arity)) (unwrap-minfos ($interface-minfos i)))) interfaces) included-minfos (cons minfo included-minfos))))))))) (let ([minfo (car minfos)]) (let ([mname (minfo-mname minfo)] [arity (minfo-arity minfo)]) (when (ormap (lambda (base-mname) (free-identifier=? mname base-mname)) base-mnames) (syntax-error mname "arity not supported by base class method")) (f (cdr minfos) (cdr bodies) (cons (list (minfo-hidden-mname minfo) (build-method minfo class-name (car bodies) '())) mlambdas) (cons minfo all-minfos) (if (ormap (lambda (i) (ormap (lambda (m) (minfo-match? m mname arity)) (unwrap-minfos ($interface-minfos i)))) interfaces) generics (cons minfo generics))))))) (let ([base-minfo (car base-minfos)]) (let ([base-mname (minfo-mname base-minfo)] [base-arity (syntax->datum (minfo-arity base-minfo))]) (let find-method ([xminfos minfos] [xbodies bodies]) (cond [(null? xminfos) (f (cdr base-minfos) minfos bodies mlambdas (cons base-minfo all-minfos))] [(and (= base-arity (minfo-arity (car xminfos))) (free-identifier=? base-mname (minfo-mname (car xminfos)))) (f (cdr base-minfos) (remq (car xminfos) minfos) (remq (car xbodies) bodies) (cons (list (minfo-hidden-mname (car xminfos)) (build-method (car xminfos) class-name (car xbodies) all-base-minfos)) mlambdas) (cons (car xminfos) all-minfos))] [else (find-method (cdr xminfos) (cdr xbodies))]))))))))) (define free-id-union (lambda (ls1 ls2) (if (null? ls1) ls2 (if (free-id-member (car ls1) ls2) (free-id-union (cdr ls1) ls2) (cons (car ls1) (free-id-union (cdr ls1) ls2)))))) (module (parse-clauses) (define-syntax define-option-parser (lambda (x) (syntax-case x (else) [(_ name [desc (kwd ...) ([var default] ...) pattern guard expr] ...) (with-syntax ([(seen? ...) (generate-temporaries #'(desc ...))]) #'(begin (define (option-parser x*) (let f ([x* x*] [seen? #f] ... [var default] ... ...) (if (null? x*) (values var ... ...) (syntax-case (car x*) (kwd ... ...) [pattern guard (begin (when seen? (syntax-error (car x*) (format "extra ~a" desc))) (let ([seen? #t]) (let-values ([(var ...) expr]) (f (cdr x*) seen? ... var ... ...))))] ...)))) (define-syntax name (lambda (x) (syntax-case x () [(k x* b1 b2 (... ...)) (with-implicit (k var ... ...) #'(let-values ([(var ... ...) (option-parser x*)]) b1 b2 (... ...)))])))))]))) (define (valid-type? x) (eq? x 'scheme-object)) (define-option-parser parse-modifiers ["ivar mutability modifier" () ([mutable? #t]) x (or (literal-identifier=? #'x #'mutable) (literal-identifier=? #'x #'immutable)) (literal-identifier=? #'x #'mutable)] ["ivar public/private modifier" () ([public? #f]) x (or (literal-identifier=? #'x #'public) (literal-identifier=? #'x #'private)) (literal-identifier=? #'x #'public)] ["ivar type" () ([type 'scheme-object]) x (valid-type? (datum x)) (datum x)] ["" () () x #t (syntax-error #'x "invalid ivar modifier")]) (define-option-parser parse-clauses ["implements clause" (implements) ([iface* '()]) (implements iface ...) (andmap identifier? (syntax->list #'(iface ...))) (syntax->list #'(iface ...))] ["ivars clause" (ivars) ([public?* '()] [mutable?* '()] [ivar* '()] [ivar-init* '()]) (ivars (modifier ... ivar ivar-init) ...) (andmap identifier? (syntax->list #'(ivar ...))) (let-values ([(public?* mutable?*) (let f ([modifier** (syntax->list #'((modifier ...) ...))]) (if (null? modifier**) (values '() '()) (let-values ([(public?* mutable?*) (f (cdr modifier**))]) (parse-modifiers (syntax->list (car modifier**)) (values (cons public? public?*) (cons mutable? mutable?*))))))]) (values public?* mutable?* (syntax->list #'(ivar ...)) (syntax->list #'(ivar-init ...))))] ["init clause" (init) ([init-expr* '()]) (init init-expr ...) #t (syntax->list #'(init-expr ...))] ["methods clause" (methods) ([method-name* '()] [method-formals* '()] [method-body* '()]) (methods method ...) #t (let f ([method* (syntax->list #'(method ...))]) (if (null? method*) (values '() '() '()) (let-values ([(method) (car method*)] [(method-name* method-formals* method-body*) (f (cdr method*))]) (syntax-case method () [(method-name method-formals method-b1 method-b2 ...) (values (cons #'method-name method-name*) (cons #'method-formals method-formals*) (cons #'(let () method-b1 method-b2 ...) method-body*))] [method (syntax-error #'method "invalid method syntax")]))))] ["constructor clause" (constructor) ([constructor-id #f]) (constructor id) (identifier? #'id) #'id] ["predicate clause" (predicate) ([predicate-id #f]) (predicate id) (identifier? #'id) #'id] ["prefix clause" (prefix) ([prefix-string #f]) (prefix str) (string? (datum str)) (datum str)] ["" () () x #t (syntax-error #'x "invalid define-class clause")])) (syntax-case x () [(_ (class-name class-formal ...) (base-name base-arg ...) clause ...) (parse-clauses (syntax->list #'(clause ...)) (lambda (r) (let ([bc (r #'base-name)]) (unless ($class? bc) (syntax-error #'base-name "define-class: unrecognized base class")) (let f ([ls ivar*]) (define bound-id-member? (lambda (x ls) (and (not (null? ls)) (or (bound-identifier=? (car ls) x) (bound-id-member? x (cdr ls)))))) (unless (null? ls) (if (bound-id-member? (car ls) (cdr ls)) (syntax-error (car ls) "duplicate instance variable") (f (cdr ls))))) (with-syntax ([(iface-name ...) (free-id-union iface* (syntax->list ($class-interfaces bc)))]) (with-syntax ([(interface ...) (map (lambda (x) (let ([iface (r x)]) (unless ($interface? iface) (syntax-error x "unrecognized interface")) iface)) #'(iface-name ...))]) (with-syntax ([(((base-ivar base-ivar-init) ...) ...) ($class-ivar-bindings bc)] [base-init-proc ($class-init-proc bc)] [(base-formal-binding ...) (with-syntax ([(base-formal ...) ($class-formals bc)] [(base-base-formal-binding ...) ($class-formal-bindings bc)]) (unless (= (length #'(base-arg ...)) (length #'(base-formal ...))) (syntax-error #'base-name "incorrect number of arguments to base class")) #'(((base-formal base-arg) ...) base-base-formal-binding ...))] [((all-minfo ...) ((new-hidden-mname mlambda) ...) (generic ...) (included-minfo ...)) (process-methods #'class-name #'(interface ...) (unwrap-minfos ($class-minfos bc)) (map build-minfo method-name* method-formals* (generate-temporaries method-name*)) method-body*)] [self (datum->syntax #'class-name 'self)]) (let ([name (let ([name (datum class-name)]) (if (gensym? name) name (symbol->string name)))] [nongenerative? (and (null? #'(all-minfo ...)) (null? #'(interface ...)))] [flds (map list (map (lambda (x) (if x 'mutable 'immutable)) mutable?*) (map (lambda (x) 'ptr) ivar*) (syntax->datum ivar*))]) (when (gensym? name) (unless nongenerative? (syntax-error #'class-name "cannot specify gensym class-name with methods or interfaces"))) (with-syntax ([ctrtd (#%$make-record-type #!base-rtd ($class-ctrtd bc) name flds #f #f)] [vtable-rtd (#%$make-record-type #!base-rtd ($class-vtable-rtd bc) "compile-time-vtable-rtd" (syntax->datum (map minfo-mname #'(generic ...))) #f #f)]) (with-syntax ([(ivar ...) ivar*] [(ivar-init ...) ivar-init*] [(ivar-defn ...) (map make-ivar-defn ivar* mutable?* (let ([offsets (#%$record-type-field-offsets #'ctrtd)]) (list-tail offsets (- (length offsets) (length ivar*)))))]) (with-syntax ([init-proc (with-syntax ([(init-expr ...) init-expr*]) #'(lambda (ego) (base-init-proc ego) (let () ivar-defn ... (define-syntax self (identifier-syntax ego)) init-expr ... ego)))] [vtable-expr (if nongenerative? #''ctrtd (with-syntax ([(vtable-init ...) (map minfo-hidden-mname #'(included-minfo ...))] [parent-rtd ($class-vtable-expr bc)] [name (datum->syntax #'class-name name)] [flds (datum->syntax #'class-name flds)] [(iface-elt ...) (map (build-interface-vtable #'(all-minfo ...)) #'(interface ...))]) #'(#%$make-record-type 'vtable-rtd parent-rtd name 'flds #f #f (list iface-elt ...) vtable-init ...)))] [((generic-name (generic-formals generic-flat-formals generic-offset) ...) ...) (build-generic #'(generic ...) (let ([offsets (#%$record-type-field-offsets #'vtable-rtd)] [minfos #'(included-minfo ...)]) (let f ([offsets (list-tail offsets (- (length offsets) (length minfos)))] [minfos minfos] [generics #'(generic ...)]) (if (null? generics) '() (if (eq? (car generics) (car minfos)) (cons (car offsets) (f (cdr offsets) (cdr minfos) (cdr generics))) (f (cdr offsets) (cdr minfos) generics))))))] [opt3 (= (optimize-level) 3)]) (with-syntax ([maker-name (or constructor-id (construct-name #'class-name "make-" #'class-name))] [maker-body (let f ([ls #'(base-formal-binding ...)]) (syntax-case ls () [(((lhs rhs) ...)) #'(let ((lhs rhs) ...) (init-proc (let* ([base-ivar base-ivar-init] ... ... [ivar ivar-init] ...) (#3%$record vtable base-ivar ... ... ivar ...))))] [(((lhs rhs) ...) m ...) (with-syntax ([body (f #'(m ...))]) #'(let ((lhs rhs) ...) body))]))] [pred-name (or predicate-id (construct-name #'class-name #'class-name "?"))] [((accessor-name . accessor) ...) (let ([offset* (let ([offset* (#%$record-type-field-offsets #'ctrtd)]) (list-tail offset* (- (length offset*) (length ivar*))))] [prefix-string (or prefix-string (format "~a-" (datum class-name)))]) (let f ([ivar* ivar*] [offset* offset*] [public?* public?*]) (if (null? ivar*) '() (with-syntax ([rest (f (cdr ivar*) (cdr offset*) (cdr public?*))]) (if (car public?*) (with-syntax ([accessor-name (construct-name (car ivar*) prefix-string (car ivar*))] [offset (car offset*)]) #'((accessor-name . (lambda (x) (unless (or opt3 (record? x vtable)) (errorf 'accessor-name "not applicable to ~s" x)) (#3%$object-ref 'scheme-object x offset))) . rest)) #'rest)))))] [((mutator-name . mutator) ...) (let ([offset* (let ([offset* (#%$record-type-field-offsets #'ctrtd)]) (list-tail offset* (- (length offset*) (length ivar*))))] [prefix-string (or prefix-string (format "~a-" (datum class-name)))]) (let f ([ivar* ivar*] [offset* offset*] [public?* public?*] [mutable?* mutable?*]) (if (null? ivar*) '() (with-syntax ([rest (f (cdr ivar*) (cdr offset*) (cdr public?*) (cdr mutable?*))]) (if (and (car public?*) (car mutable?*)) (with-syntax ([mutator-name (construct-name (car ivar*) prefix-string (car ivar*) "-set!")] [offset (car offset*)]) #'((mutator-name . (lambda (x v) (unless (or opt3 (record? x vtable)) (errorf 'mutator-name "not applicable to ~s" x)) (#3%$object-set! 'scheme-object x offset v))) . rest)) #'rest)))))]) #'(begin ; we use a module here (1) to attach necessary ; indirect exports to class-name in case class-name ; is exported from a top-level module and ; (2) so that we can define vtable after ; new-hidden-mname ... yet still have the introduced ; identifier vtable resolve to the correct binding ; in the method bodies. we don't wrap the entire ; define-class output in a module form since we want ; the pred-name, maker-name, and the generic names ; to be ordinary top-level variables at top level. (module ((class-name vtable new-hidden-mname ...) vtable new-hidden-mname ...) (module (new-hidden-mname ...) ; local ego for fluid-let-syntax to whack (define-syntax ego values) ivar-defn ... (define-syntax self (identifier-syntax ego)) (define new-hidden-mname mlambda) ...) ; counting on letrec* semantics below so that ; new-hidden-mname ... are defined before referenced ; in vtable-expr (define vtable vtable-expr) (define-syntax class-name (make-compile-time-value ($make-class #'(class-formal ...) #'(base-formal-binding ...) #'(([base-ivar base-ivar-init] ...) ... ([ivar ivar-init] ...)) #'(all-minfo ...) 'vtable-rtd 'ctrtd #'vtable #'(iface-name ...) #'init-proc)))) (define (pred-name x) (#3%record? x vtable)) (define (maker-name class-formal ...) maker-body) (define accessor-name accessor) ... (define mutator-name mutator) ... (define generic-name (let ([who 'generic-name]) ; can't ref generic-name pattern vble inside ... below (case-lambda [(ego . generic-formals) (unless (or opt3 (#3%record? ego vtable)) (errorf who "not applicable to ~s" ego)) ((#3%$object-ref 'scheme-object (#3%record-rtd ego) generic-offset) ego . generic-flat-formals)] ...))) ...))))))))))))]))) (define-syntax aux (syntax-rules () [(_ kwd) (define-syntax kwd (lambda (x) (syntax-error x "misplaced aux keyword")))])) (aux ivars) (aux public) (aux private) (aux methods) (aux self) (aux implements) (aux init) ; constructor, predicate, and prefix are defined by (chezscheme) #;(aux constructor) #;(aux predicate) #;(aux prefix) )