Article View: comp.lang.lisp.x
Article #29Re: xscheme oop package (fixed)
From: Mikael Pettersson
Date: Sun, 17 Sep 1989 14:47
Date: Sun, 17 Sep 1989 14:47
409 lines
13459 bytes
13459 bytes
In article <1343@senilix.ida.liu.se> I wrote:
>In article <1341@majestix.ida.liu.se> I wrote:
>>... The following two files implement, in Scheme, an object mechanism
>>that is supposed to be near 100% compatible with those in xscheme and xlisp.
>
>Oops! Except for some bugs in the handling of class variables that is :-(
>(methods could execute in the wrong class context and class variables weren't
>inherited properly). The following context diff to "obj.scm" should fix this.
Arrgh! No it didn't. Class variables *still* weren't handled properly, as
it *copied* the superclass's cvars, rather than *sharing* them. Sigh.
Anyway, 'tis fixed now. The context diff being almost as big as the
file to apply it to, I post the file rather than the diff.
;; OBJ.SCM -- A simple class mechanism for XScheme
;; by: Mikael Pettersson, mpe@ida.liu.se
;; This software is in the public domain
;; (no warranty, use at your own risk etc)
;;
;; Version 1.0 Sep 15, 1989 (mpe@ida.liu.se)
;; Released to comp.lang.lisp.x
;;
;; Version 1.1 Sep 16, 1989 (mpe@ida.liu.se)
;; Fixed some bugs in class variable handling
;;
;; Version 1.2 Sep 17, 1989 (mpe@ida.liu.se)
;; Fixed more bugs in the handling of class variables
;;
;;
;; Implementation notes
;; ====================
;; An object is represented by a closure with bindings for the
;; state vectors of itself and its class.
;;
;; Instances have the following state structure:
;; pos contents
;; --- --------
;; 0 self (the closure)
;; 1..(# of inst vars) instance variable values
;;
;; Classes have the following state structure:
;; pos contents
;; --- --------
;; 0 self (the closure)
;; 1 list of (message . method) pairs
;; 2 list of instance variable names
;; 3 a pair whose car is the list of class
;; variable names, and cdr references the
;; same field in the superclass
;; 4 a vector where slot #0 references the
;; same field in the superclass, and slots
;; #1..<number of class vars> contain the
;; class variable values
;; 5 the superclass' state
;; 6 number of class instance variables
;; 7 total number of instance variables
;;
;; Fields (3) and (4) together make up a "class variable environment".
;;
;; Methods execute in a pseudo-environment (see cl-answer) with bindings
;; for SELF and the instance and class variables. Free variables are
;; evaluated in the global environment.
;;
;; Sending a message to the superclass (from inside a method) is done with
;; (self 'send-super msg ...). For backwards compability, the old form
;; (send-super msg ...) is supplied as a macro.
;;
;; Class variables can be given initial values with
;; (Class 'new '(ivar1 .. ivari) '((cvar1 val1) .. (cvarj valj)) ..).
;; An omitted initial value defaults to '().
;;
;; Limitations
;; ===========
;; Object? is not implemented
;; print et al prints objects as unnamed procedures, which is counterintuitive
;; performance(?)
;; instance and class variables can't be rebound in a method's body
;;
;; exported objects, procedures and macros
;;
(define Object)
(define Class)
(define (object? x) #f)
(macro send-super (lambda (form) `(self 'send-super ,@(cdr form))))
;;
;; internal state access/update macros
;;
(macro mkvref
(lambda (form)
(let ((i (cadr form)))
(lambda (form)
`(vector-ref ,(cadr form) ,i)))))
(macro mkvset
(lambda (form)
(let ((i (cadr form)))
(lambda (form)
`(vector-set! ,(cadr form) ,i ,(caddr form))))))
(macro get-self (mkvref 0))
(macro set-self! (mkvset 0))
(macro get-messages (mkvref 1))
(macro set-messages! (mkvset 1))
(macro get-ivars (mkvref 2))
(macro set-ivars! (mkvset 2))
(macro get-cvars (mkvref 3))
(macro set-cvars! (mkvset 3))
(macro get-cvals (mkvref 4))
(macro set-cvals! (mkvset 4))
(macro get-super-state (mkvref 5))
(macro set-super-state! (mkvset 5))
(macro get-ivarcnt (mkvref 6))
(macro set-ivarcnt! (mkvset 6))
(macro get-ivartotal (mkvref 7))
(macro set-ivartotal! (mkvset 7))
;;
;; make a package to hide the implementation details
;;
(let ()
;;
;; misc. stuff
;;
(define class-size 7)
(define (new-object-state slots)
(make-vector (1+ slots))) ; add one for SELF
(define (make-object obj-state cls-state)
(lambda (msg . argl)
(send cls-state cls-state obj-state msg argl)))
(define (send cls-state o-cls-state obj-state msg argl)
;; cls-state is where we begin searching for the method
;; o-cls-state is the original class-state for 'self'
;; obj-state is the state for 'self'
(let (m)
(while (and cls-state
(begin (set! m (assq msg (get-messages cls-state)))
(null? m)))
(set! cls-state (get-super-state cls-state)))
(if m
((cdr m) obj-state o-cls-state (get-cvals cls-state) argl)
(error "no method for this message" msg (get-self obj-state)))))
(define (assert-null! argl who)
(and argl (error "too many arguments" who argl)))
;;
;; default methods for Object
;;
;; 'send-super -- send to the super class
(define (ob-send-super self-state cls-state cvals argl)
(send (get-super-state cls-state) cls-state self-state (car argl) (cdr argl)))
;; 'isnew -- default 'isnew method
(define (ob-isnew self-state cls-state cvals argl)
(assert-null! argl 'ISNEW)
(get-self self-state))
;; 'class -- get the class of an object
(define (ob-class self-state cls-state cvals argl)
(assert-null! argl 'CLASS)
(get-self cls-state))
;; 'show -- show the instance variables of an object
(define (ob-show self-state cls-state cvals argl)
(let ((port (if argl
(begin
(assert-null! (cdr argl) 'SHOW)
(car argl))
(current-output-port))))
;; print the object and class
(princ "Object is " port)
(princ (get-self self-state) port)
(princ ", Class is " port)
(princ (get-self cls-state) port)
(newline port)
;; print the object's instance variables
(ob-show-vars port (get-ivars cls-state) self-state 1)
;; return the object
(get-self self-state)))
(define (ob-show-vars port ivars-list obj-state pos)
(while ivars-list
(princ #\space port)
(princ (car ivars-list) port)
(princ " = " port)
(princ (vector-ref obj-state pos) port)
(newline port)
(set! pos (1+ pos))
(set! ivars-list (cdr ivars-list))))
;; '%state -- get the state vector for an object (used by cl-isnew)
(define (ob-state self-state cls-state cvals argl)
(assert-null! argl '%STATE)
self-state)
;;
;; default methods for Class
;;
;; 'new -- create a new object instance
(define (cl-new self-state cls-state cvals argl)
(let ((obj-state (new-object-state (get-ivartotal self-state))))
(set-self! obj-state (make-object obj-state self-state))
(send self-state self-state obj-state 'isnew argl)))
;; 'isnew -- initialize a new class
(define (cl-isnew self-state cls-state cvals argl)
(let (ivars cvars super-state n)
(set! ivars (car argl))
(set! argl (cdr argl))
(set! super-state Object-state)
(if argl
(begin
(set! cvars (car argl))
(set! argl (cdr argl))
(if argl
(begin
(set! super-state ((car argl) '%state)) ; invoke ob-state
(assert-null! (cdr argl) 'ISNEW)))))
;; store the instance variable list and the superclass
(set-ivars! self-state (append (get-ivars super-state) (append ivars '())))
(set-super-state! self-state super-state)
;; construct the class variable environment
(set-cvars! self-state ; method compile-time lookup structure
(cons (map (lambda (x) (if (symbol? x) x (car x))) cvars)
(get-cvars super-state)))
(set-cvals! self-state ; method run-time storage structure
(list->vector (cons (get-cvals super-state)
(map (lambda (x)
(if (symbol? x) '() (cadr x)))
cvars))))
;; compute the instance variable count
(set! n (length ivars))
(set-ivarcnt! self-state n)
(set-ivartotal! self-state (+ n (get-ivartotal super-state)))
;; return the new class object
(get-self self-state)))
;; 'answer -- define a method for answering a message
(define (cl-answer self-state cls-state cvals argl)
(let (msg fargs code)
;; message symbol, formal argument list and code
(set! msg (car argl))
(set! argl (cdr argl))
(set! fargs (car argl))
(set! argl (cdr argl))
(set! code (car argl))
(assert-null! (cdr argl) 'ANSWER)
;; hack the code: expand all macro calls and change references to
;; instance or class variables into references to the state vectors
;; passed as arguments by send. SELF is the implicit ivar #0.
(set! code (hack-list (%expand-macros code)
(get-cvars self-state)
(cons 'self (get-ivars self-state))))
;; make it look like a method
(set! code `(lambda (%ivals %cls-state %cvals %argl)
(apply (lambda ,fargs ,@code) %argl)))
;; compile and store the method
;; (could use `eval' instead, but this saves us from copying the entire
;; s-expr again while trying to expand the non-existant macro calls)
(entermsg! self-state msg ((%compile code)))
;; return the object
(get-self self-state)))
(define (hack-list lst cvars ivars)
(map (lambda (item) (hack-item item cvars ivars)) lst))
;; bugs: doesn't handle the binding forms (lambda, let et al).
(define (hack-item item cvars ivars)
(if (pair? item)
(let ((func (car item))
(args (cdr item)))
(cond ((eq? func 'quote)
item)
((eq? func 'set!)
(let ((nam (car args))
(val (hack-item (cadr args) cvars ivars)))
(or (hack-ivar-set nam val ivars)
(hack-cvar #f val nam cvars)
`(set! ,nam ,val))))
(else
(hack-list item cvars ivars))))
(or (and (symbol? item)
(or (hack-ivar-ref item ivars)
(hack-cvar #t '() item cvars)))
item)))
(define (find-pos-in-list sym lst)
(let ((pos 0))
(while (and lst
(not (eq? sym (car lst))))
(set! pos (1+ pos))
(set! lst (cdr lst)))
(and lst pos)))
(define (hack-ivar-ref sym lst)
(let ((pos (find-pos-in-list sym lst)))
(and pos
`(vector-ref %ivals ,pos))))
(define (hack-ivar-set sym val lst)
(let ((pos (find-pos-in-list sym lst)))
(and pos
`(vector-set! %ivals ,pos ,val))))
(define (hack-cvar isref val sym cvars)
(let ((frame 0) pos)
(while (and cvars
(begin (set! pos (find-pos-in-list sym (car cvars)))
(null? pos)))
(set! frame (1+ frame))
(set! cvars (cdr cvars)))
(and cvars
(let ((name '%cvals))
(while (> frame 0)
(set! name `(vector-ref ,name 0))
(set! frame (-1+ frame)))
(if isref
`(vector-ref ,name ,(1+ pos))
`(vector-set! ,name ,(1+ pos) ,val))))))
(define (entermsg! state msg val)
(let* ((mlist (get-messages state))
(pair (assq msg mlist)))
(if pair
(set-cdr! pair val)
(set-messages! state (cons (cons msg val) mlist)))))
;;
;; create and initialize the Object and Class objects
;;
(define Class-state)
(define Object-state)
(set! Class-state (new-object-state class-size))
(set! Class (make-object Class-state Class-state))
(set-self! Class-state Class)
(set! Object-state (new-object-state class-size))
(set! Object (make-object Object-state Class-state))
(set-self! Object-state Object)
(set-ivarcnt! Object-state 0)
(set-ivartotal! Object-state 0)
(entermsg! Object-state 'send-super ob-send-super)
(entermsg! Object-state 'isnew ob-isnew)
(entermsg! Object-state 'class ob-class)
(entermsg! Object-state 'show ob-show)
(entermsg! Object-state '%state ob-state) ; used by cl-isnew
(set-ivars! Class-state '(messages ivars cvars cvals super-state ivarcnt ivartotal))
(set-ivarcnt! Class-state class-size)
(set-ivartotal! Class-state class-size)
(set-super-state! Class-state Object-state)
(entermsg! Class-state 'new cl-new)
(entermsg! Class-state 'isnew cl-isnew)
(entermsg! Class-state 'answer cl-answer)
;; close the package
)
;;
;; remove our macros (it's a pity macros aren't statically scoped)
;;
(put 'mkvref '%macro '())
(put 'mkvset '%macro '())
(put 'get-self '%macro '())
(put 'set-self! '%macro '())
(put 'get-messages '%macro '())
(put 'set-messages! '%macro '())
(put 'get-ivars '%macro '())
(put 'set-ivars! '%macro '())
(put 'get-cvars '%macro '())
(put 'set-cvars! '%macro '())
(put 'get-cvals '%macro '())
(put 'set-cvals! '%macro '())
(put 'get-super-state '%macro '())
(put 'set-super-state! '%macro '())
(put 'get-ivarcnt '%macro '())
(put 'set-ivarcnt! '%macro '())
(put 'get-ivartotal '%macro '())
(put 'set-ivartotal! '%macro '())
--
Mikael Pettersson, Dept of Comp & Info Sci, University of Linkoping, Sweden
email: mpe@ida.liu.se or ..!{mcvax,munnari,uunet}!enea!liuida!mpe
Technical Details
Message-ID:
<1344@majestix.ida.liu.se>Path:
arc1-ca.pugleaf.net!utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!cs.utexas.edu!uunet!mcsun!sunic!liuida!mikpeReferences:
<1341@majestix.ida.liu.se> <1343@senilix.ida.liu.se>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!cs.utexas.edu!uunet!mcsun!sunic!liuida!mikpe From: mikpe@majestix.ida.liu.se (Mikael Pettersson) Newsgroups: comp.lang.lisp.x Subject: Re: xscheme oop package (fixed) Summary: fix #2 Message-ID: <1344@majestix.ida.liu.se> Date: 17 Sep 89 14:47:13 GMT References: <1341@majestix.ida.liu.se> <1343@senilix.ida.liu.se> Organization: CIS Dept, Univ of Linkoping, Sweden Lines: 409