;;; installed-scm-file
-;;;; Copyright (C) 1998,1999,2000,2001,2002, 2003, 2006 Free Software Foundation, Inc.
+;;;; Copyright (C) 1998,1999,2000,2001,2002, 2003, 2006, 2009 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
;;;; License as published by the Free Software Foundation; either
-;;;; version 2.1 of the License, or (at your option) any later version.
+;;;; version 3 of the License, or (at your option) any later version.
;;;;
;;;; This library is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
make-generic ensure-generic
make-extended-generic
make-accessor ensure-accessor
- make-method add-method!
- object-eqv? object-equal?
+ add-method!
class-slot-ref class-slot-set! slot-unbound slot-missing
slot-definition-name slot-definition-options
slot-definition-allocation
class-direct-methods class-direct-slots class-precedence-list
class-slots class-environment
generic-function-name
- generic-function-methods method-generic-function method-specializers
+ generic-function-methods method-generic-function
+ method-specializers method-formals
primitive-generic-generic enable-primitive-generic!
method-procedure accessor-method-slot-definition
slot-exists? make find-method get-keyword)
- :replace (<class> <operator-class> <entity-class> <entity>)
+ :replace (<class> <entity-class> <entity>)
:no-backtrace)
(define *goops-module* (current-module))
;; First initialize the builtin part of GOOPS
-(eval-case
- ((load-toplevel compile-toplevel)
- (%init-goops-builtins)))
+(eval-when (eval load compile)
+ (%init-goops-builtins))
+
+(eval-when (eval load compile)
+ (use-modules ((language tree-il primitives) :select (add-interesting-primitive!)))
+ (add-interesting-primitive! 'class-of)
+ (add-interesting-primitive! '@slot-ref)
+ (add-interesting-primitive! '@slot-set!))
;; Then load the rest of GOOPS
(use-modules (oop goops util)
(oop goops compile))
\f
-(define min-fixnum (- (expt 2 29)))
-
-(define max-fixnum (- (expt 2 29) 1))
+(eval-when (eval load compile)
+ (define min-fixnum (- (expt 2 29)))
+ (define max-fixnum (- (expt 2 29) 1)))
;;
;; goops-error
(if (null? supers)
<class>
(let* ((all-metas (map (lambda (x) (class-of x)) supers))
- (all-cpls (apply append
- (map (lambda (m)
- (cdr (class-precedence-list m)))
- all-metas)))
+ (all-cpls (append-map (lambda (m)
+ (cdr (class-precedence-list m)))
+ all-metas))
(needed-metas '()))
;; Find the most specific metaclasses. The new metaclass will be
;; a subclass of these.
;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
;;; OPTION ::= KEYWORD VALUE
;;;
-(define (define-class-pre-definition kw val)
- (case kw
- ((#:getter #:setter)
- `(if (or (not (defined? ',val))
- (not (is-a? ,val <generic>)))
- (define-generic ,val)))
- ((#:accessor)
- `(if (or (not (defined? ',val))
- (not (is-a? ,val <accessor>)))
- (define-accessor ,val)))
- (else #f)))
(define (kw-do-map mapper f kwargs)
(define (keywords l)
(a (args kwargs)))
(mapper f k a)))
-;;; This code should be implemented in C.
-;;;
-(define-macro (define-class name supers . slots)
- ;; Some slot options require extra definitions to be made. In
- ;; particular, we want to make sure that the generic function objects
- ;; which represent accessors exist before `make-class' tries to add
- ;; methods to them.
- ;;
- ;; Postpone some error handling to class macro.
- ;;
- `(begin
- ;; define accessors
- ,@(append-map (lambda (slot)
- (kw-do-map filter-map
- define-class-pre-definition
- (if (pair? slot) (cdr slot) '())))
- (take-while (lambda (x) (not (keyword? x))) slots))
- (if (and (defined? ',name)
- (is-a? ,name <class>)
- (memq <object> (class-precedence-list ,name)))
- (class-redefinition ,name
- (class ,supers ,@slots #:name ',name))
- (define ,name (class ,supers ,@slots #:name ',name)))))
-
-(define standard-define-class define-class)
+(define (make-class supers slots . options)
+ (let ((env (or (get-keyword #:environment options #f)
+ (top-level-env))))
+ (let* ((name (get-keyword #:name options (make-unbound)))
+ (supers (if (not (or-map (lambda (class)
+ (memq <object>
+ (class-precedence-list class)))
+ supers))
+ (append supers (list <object>))
+ supers))
+ (metaclass (or (get-keyword #:metaclass options #f)
+ (ensure-metaclass supers env))))
+
+ ;; Verify that all direct slots are different and that we don't inherit
+ ;; several time from the same class
+ (let ((tmp1 (find-duplicate supers))
+ (tmp2 (find-duplicate (map slot-definition-name slots))))
+ (if tmp1
+ (goops-error "make-class: super class ~S is duplicate in class ~S"
+ tmp1 name))
+ (if tmp2
+ (goops-error "make-class: slot ~S is duplicate in class ~S"
+ tmp2 name)))
+
+ ;; Everything seems correct, build the class
+ (apply make metaclass
+ #:dsupers supers
+ #:slots slots
+ #:name name
+ #:environment env
+ options))))
;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
;;;
(else
`(list ',def))))
slots))
-
(if (not (list? supers))
(goops-error "malformed superclass list: ~S" supers))
- (let ((slot-defs (cons #f '()))
- (slots (take-while (lambda (x) (not (keyword? x))) slots))
+ (let ((slots (take-while (lambda (x) (not (keyword? x))) slots))
(options (or (find-tail keyword? slots) '())))
`(make-class
;; evaluate super class variables
;; evaluate class options
,@options)))
-(define (make-class supers slots . options)
- (let ((env (or (get-keyword #:environment options #f)
- (top-level-env))))
- (let* ((name (get-keyword #:name options (make-unbound)))
- (supers (if (not (or-map (lambda (class)
- (memq <object>
- (class-precedence-list class)))
- supers))
- (append supers (list <object>))
- supers))
- (metaclass (or (get-keyword #:metaclass options #f)
- (ensure-metaclass supers env))))
-
- ;; Verify that all direct slots are different and that we don't inherit
- ;; several time from the same class
- (let ((tmp1 (find-duplicate supers))
- (tmp2 (find-duplicate (map slot-definition-name slots))))
- (if tmp1
- (goops-error "make-class: super class ~S is duplicate in class ~S"
- tmp1 name))
- (if tmp2
- (goops-error "make-class: slot ~S is duplicate in class ~S"
- tmp2 name)))
-
- ;; Everything seems correct, build the class
- (apply make metaclass
- #:dsupers supers
- #:slots slots
- #:name name
- #:environment env
- options))))
+(define-syntax define-class-pre-definition
+ (lambda (x)
+ (syntax-case x ()
+ ((_ (k arg rest ...) out ...)
+ (keyword? (syntax->datum (syntax k)))
+ (case (syntax->datum (syntax k))
+ ((#:getter #:setter)
+ (syntax
+ (define-class-pre-definition (rest ...)
+ out ...
+ (if (or (not (defined? 'arg))
+ (not (is-a? arg <generic>)))
+ (toplevel-define!
+ 'arg
+ (ensure-generic (if (defined? 'arg) arg #f) 'arg))))))
+ ((#:accessor)
+ (syntax
+ (define-class-pre-definition (rest ...)
+ out ...
+ (if (or (not (defined? 'arg))
+ (not (is-a? arg <accessor>)))
+ (toplevel-define!
+ 'arg
+ (ensure-accessor (if (defined? 'arg) arg #f) 'arg))))))
+ (else
+ (syntax
+ (define-class-pre-definition (rest ...) out ...)))))
+ ((_ () out ...)
+ (syntax (begin out ...))))))
+
+;; Some slot options require extra definitions to be made. In
+;; particular, we want to make sure that the generic function objects
+;; which represent accessors exist before `make-class' tries to add
+;; methods to them.
+(define-syntax define-class-pre-definitions
+ (lambda (x)
+ (syntax-case x ()
+ ((_ () out ...)
+ (syntax (begin out ...)))
+ ((_ (slot rest ...) out ...)
+ (keyword? (syntax->datum (syntax slot)))
+ (syntax (begin out ...)))
+ ((_ (slot rest ...) out ...)
+ (identifier? (syntax slot))
+ (syntax (define-class-pre-definitions (rest ...)
+ out ...)))
+ ((_ ((slotname slotopt ...) rest ...) out ...)
+ (syntax (define-class-pre-definitions (rest ...)
+ out ... (define-class-pre-definition (slotopt ...))))))))
+
+(define-syntax define-class
+ (syntax-rules ()
+ ((_ name supers slot ...)
+ (begin
+ (define-class-pre-definitions (slot ...))
+ (if (and (defined? 'name)
+ (is-a? name <class>)
+ (memq <object> (class-precedence-list name)))
+ (class-redefinition name
+ (class supers slot ... #:name 'name))
+ (toplevel-define! 'name (class supers slot ... #:name 'name)))))))
+
+(define-syntax standard-define-class
+ (syntax-rules ()
+ ((_ arg ...) (define-class arg ...))))
;;;
;;; {Generic functions and accessors}
(let ((ans (if gws?
(let* ((sname (and name (make-setter-name name)))
(setters
- (apply append
- (map (lambda (gf)
+ (append-map (lambda (gf)
(if (is-a? gf <generic-with-setter>)
(list (ensure-generic (setter gf)
sname))
'()))
- gfs)))
+ gfs))
(es (make <extended-generic-with-setter>
#:name name
#:extends gfs
(else (make <generic> #:name name)))))
;; same semantics as <generic>
-(define-macro (define-accessor name)
- (if (not (symbol? name))
- (goops-error "bad accessor name: ~S" name))
- `(define ,name
- (if (and (defined? ',name) (is-a? ,name <accessor>))
- (make <accessor> #:name ',name)
- (ensure-accessor (if (defined? ',name) ,name #f) ',name))))
+(define-syntax define-accessor
+ (syntax-rules ()
+ ((_ name)
+ (define name
+ (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
+ ((is-a? name <accessor>) (make <accessor> #:name 'name))
+ (else (ensure-accessor name 'name)))))))
(define (make-setter-name name)
(string->symbol (string-append "setter:" (symbol->string name))))
;;; {Methods}
;;;
-(define-macro (define-method head . body)
- (if (not (pair? head))
- (goops-error "bad method head: ~S" head))
- (let ((gf (car head)))
- (cond ((and (pair? gf)
- (eq? (car gf) 'setter)
- (pair? (cdr gf))
- (symbol? (cadr gf))
- (null? (cddr gf)))
- ;; named setter method
- (let ((name (cadr gf)))
- (cond ((not (symbol? name))
- `(add-method! (setter ,name)
- (method ,(cdr head) ,@body)))
- (else
- `(begin
- (if (or (not (defined? ',name))
- (not (is-a? ,name <accessor>)))
- (define-accessor ,name))
- (add-method! (setter ,name)
- (method ,(cdr head) ,@body)))))))
- ((not (symbol? gf))
- `(add-method! ,gf (method ,(cdr head) ,@body)))
- (else
- `(begin
- ;; FIXME: this code is how it always was, but it's quite
- ;; cracky: it will only define the generic function if it
- ;; was undefined before (ok), or *was defined to #f*. The
- ;; latter is crack. But there are bootstrap issues about
- ;; fixing this -- change it to (is-a? ,gf <generic>) and
- ;; see.
- (if (or (not (defined? ',gf))
- (not ,gf))
- (define-generic ,gf))
- (add-method! ,gf
- (method ,(cdr head) ,@body)))))))
-
-(define (make-method specializers procedure)
- (make <method>
- #:specializers specializers
- #:procedure procedure))
-
-(define-macro (method args . body)
- (letrec ((specializers
- (lambda (ls)
- (cond ((null? ls) (list (list 'quote '())))
- ((pair? ls) (cons (if (pair? (car ls))
- (cadar ls)
- '<top>)
- (specializers (cdr ls))))
- (else '(<top>)))))
- (formals
- (lambda (ls)
- (if (pair? ls)
- (cons (if (pair? (car ls)) (caar ls) (car ls))
- (formals (cdr ls)))
- ls))))
- `(make <method>
- #:specializers (cons* ,@(specializers args))
- #:formals ',(formals args)
- #:body ',body
- #:compile-env (compile-time-environment)
- #:procedure (lambda ,(formals args)
- ,@(if (null? body)
- '(begin)
- body)))))
+(define (toplevel-define! name val)
+ (module-define! (current-module) name val))
+
+(define-syntax define-method
+ (syntax-rules (setter)
+ ((_ ((setter name) . args) body ...)
+ (begin
+ (if (or (not (defined? 'name))
+ (not (is-a? name <accessor>)))
+ (toplevel-define! 'name
+ (ensure-accessor
+ (if (defined? 'name) name #f) 'name)))
+ (add-method! (setter name) (method args body ...))))
+ ((_ (name . args) body ...)
+ (begin
+ ;; FIXME: this code is how it always was, but it's quite cracky:
+ ;; it will only define the generic function if it was undefined
+ ;; before (ok), or *was defined to #f*. The latter is crack. But
+ ;; there are bootstrap issues about fixing this -- change it to
+ ;; (is-a? name <generic>) and see.
+ (if (or (not (defined? 'name))
+ (not name))
+ (toplevel-define! 'name (make <generic> #:name 'name)))
+ (add-method! name (method args body ...))))))
+
+(define-syntax method
+ (lambda (x)
+ (define (parse-args args)
+ (let lp ((ls args) (formals '()) (specializers '()))
+ (syntax-case ls ()
+ (((f s) . rest)
+ (and (identifier? (syntax f)) (identifier? (syntax s)))
+ (lp (syntax rest)
+ (cons (syntax f) formals)
+ (cons (syntax s) specializers)))
+ ((f . rest)
+ (identifier? (syntax f))
+ (lp (syntax rest)
+ (cons (syntax f) formals)
+ (cons (syntax <top>) specializers)))
+ (()
+ (list (reverse formals)
+ (reverse (cons (syntax '()) specializers))))
+ (tail
+ (identifier? (syntax tail))
+ (list (append (reverse formals) (syntax tail))
+ (reverse (cons (syntax <top>) specializers)))))))
+
+ (define (find-free-id exp referent)
+ (syntax-case exp ()
+ ((x . y)
+ (or (find-free-id (syntax x) referent)
+ (find-free-id (syntax y) referent)))
+ (x
+ (identifier? (syntax x))
+ (let ((id (datum->syntax (syntax x) referent)))
+ (and (free-identifier=? (syntax x) id) id)))
+ (_ #f)))
+
+ (define (compute-procedure formals body)
+ (syntax-case body ()
+ ((body0 ...)
+ (with-syntax ((formals formals))
+ (syntax (lambda formals body0 ...))))))
+
+ (define (->proper args)
+ (let lp ((ls args) (out '()))
+ (syntax-case ls ()
+ ((x . xs) (lp (syntax xs) (cons (syntax x) out)))
+ (() (reverse out))
+ (tail (reverse (cons (syntax tail) out))))))
+
+ (define (compute-make-procedure formals body next-method)
+ (syntax-case body ()
+ ((body ...)
+ (with-syntax ((next-method next-method))
+ (syntax-case formals ()
+ ((formal ...)
+ (syntax
+ (lambda (real-next-method)
+ (lambda (formal ...)
+ (let ((next-method (lambda args
+ (if (null? args)
+ (real-next-method formal ...)
+ (apply real-next-method args)))))
+ body ...)))))
+ (formals
+ (with-syntax (((formal ...) (->proper (syntax formals))))
+ (syntax
+ (lambda (real-next-method)
+ (lambda formals
+ (let ((next-method (lambda args
+ (if (null? args)
+ (apply real-next-method formal ...)
+ (apply real-next-method args)))))
+ body ...)))))))))))
+
+ (define (compute-procedures formals body)
+ ;; So, our use of this is broken, because it operates on the
+ ;; pre-expansion source code. It's equivalent to just searching
+ ;; for referent in the datums. Ah well.
+ (let ((id (find-free-id body 'next-method)))
+ (if id
+ ;; return a make-procedure
+ (values (syntax #f)
+ (compute-make-procedure formals body id))
+ (values (compute-procedure formals body)
+ (syntax #f)))))
+
+ (syntax-case x ()
+ ((_ args) (syntax (method args (if #f #f))))
+ ((_ args body0 body1 ...)
+ (with-syntax (((formals (specializer ...)) (parse-args (syntax args))))
+ (call-with-values
+ (lambda ()
+ (compute-procedures (syntax formals) (syntax (body0 body1 ...))))
+ (lambda (procedure make-procedure)
+ (with-syntax ((procedure procedure)
+ (make-procedure make-procedure))
+ (syntax
+ (make <method>
+ #:specializers (cons* specializer ...)
+ #:formals 'formals
+ #:body '(body0 body1 ...)
+ #:make-procedure make-procedure
+ #:procedure procedure))))))))))
;;;
;;; {add-method!}
;; Add method in all the classes which appears in its specializers list
(for-each* (lambda (x)
(let ((dm (class-direct-methods x)))
- (if (not (memv m dm))
+ (if (not (memq m dm))
(slot-set! x 'direct-methods (cons m dm)))))
(method-specializers m)))
;;;
(define-method (method-source (m <method>))
(let* ((spec (map* class-name (slot-ref m 'specializers)))
- (proc (procedure-source (slot-ref m 'procedure)))
- (args (cadr proc))
- (body (cddr proc)))
- (cons 'method
- (cons (map* list args spec)
- body))))
+ (src (procedure-source (slot-ref m 'procedure))))
+ (and src
+ (let ((args (cadr src))
+ (body (cddr src)))
+ (cons 'method
+ (cons (map* list args spec)
+ body))))))
+
+(define-method (method-formals (m <method>))
+ (slot-ref m 'formals))
;;;
;;; Slots
(define-method (eqv? x y) #f)
(define-method (equal? x y) (eqv? x y))
-;;; These following two methods are for backward compatibility only.
-;;; They are not called by the Guile interpreter.
-;;;
-(define-method (object-eqv? x y) #f)
-(define-method (object-equal? x y) (eqv? x y))
-
;;;
;;; methods to display/write an object
;;;
(define (compute-slot-accessors class slots env)
(for-each
(lambda (s g-n-s)
- (let ((name (slot-definition-name s))
- (getter-function (slot-definition-getter s))
+ (let ((getter-function (slot-definition-getter s))
(setter-function (slot-definition-setter s))
(accessor (slot-definition-accessor s)))
(if getter-function
(procedure-environment proc)))
(lambda (o) (assert-bound (proc o) o)))))
-(define n-standard-accessor-methods 10)
-
-(define bound-check-get-methods (make-vector n-standard-accessor-methods #f))
-(define standard-get-methods (make-vector n-standard-accessor-methods #f))
-(define standard-set-methods (make-vector n-standard-accessor-methods #f))
-
-(define (standard-accessor-method make methods)
- (lambda (index)
- (cond ((>= index n-standard-accessor-methods) (make index))
- ((vector-ref methods index))
- (else (let ((m (make index)))
- (vector-set! methods index m)
- m)))))
-
;; the idea is to compile the index into the procedure, for fastest
;; lookup. Also, @slot-ref and @slot-set! have their own bytecodes.
-(eval-case
- ((load-toplevel compile-toplevel)
- (use-modules ((language scheme compile-ghil) :select (define-scheme-translator))
- ((language ghil) :select (make-ghil-inline))
- (system base pmatch))
-
- ;; unfortunately, can't use define-inline because these are primitive
- ;; syntaxen.
- (define-scheme-translator @slot-ref
- ((,obj ,index) (guard (integer? index)
- (>= index 0) (< index max-fixnum))
- (make-ghil-inline #f #f 'slot-ref
- (list (retrans obj) (retrans index)))))
-
- (define-scheme-translator @slot-set!
- ((,obj ,index ,val) (guard (integer? index)
- (>= index 0) (< index max-fixnum))
- (make-ghil-inline #f #f 'slot-set
- (list (retrans obj) (retrans index) (retrans val)))))))
-
-;; Irritatingly, we can't use `compile' here, as the module shadows
-;; the binding.
-(define (make-bound-check-get index)
- ((@ (system base compile) compile)
- `(lambda (o) (let ((x (@slot-ref o ,index)))
- (if (unbound? x)
- (slot-unbound obj)
- x)))
- #:env *goops-module*))
-
-(define (make-get index)
- ((@ (system base compile) compile)
- `(lambda (o) (@slot-ref o ,index))
- #:env *goops-module*))
-
-(define (make-set index)
- ((@ (system base compile) compile)
- `(lambda (o v) (@slot-set! o ,index v))
- #:env *goops-module*))
-
-(define bound-check-get
- (standard-accessor-method make-bound-check-get bound-check-get-methods))
-(define standard-get (standard-accessor-method make-get standard-get-methods))
-(define standard-set (standard-accessor-method make-set standard-set-methods))
+(eval-when (eval load compile)
+ (define num-standard-pre-cache 20))
+
+(define-macro (define-standard-accessor-method form . body)
+ (let ((name (caar form))
+ (n-var (cadar form))
+ (args (cdr form)))
+ (define (make-one x)
+ (define (body-trans form)
+ (cond ((not (pair? form)) form)
+ ((eq? (car form) '@slot-ref)
+ `(,(car form) ,(cadr form) ,x))
+ ((eq? (car form) '@slot-set!)
+ `(,(car form) ,(cadr form) ,x ,(cadddr form)))
+ (else
+ (map body-trans form))))
+ `(lambda ,args ,@(map body-trans body)))
+ `(define ,name
+ (let ((cache (vector ,@(map make-one (iota num-standard-pre-cache)))))
+ (lambda (n)
+ (if (< n ,num-standard-pre-cache)
+ (vector-ref cache n)
+ ((lambda (,n-var) (lambda ,args ,@body)) n)))))))
+
+(define-standard-accessor-method ((bound-check-get n) o)
+ (let ((x (@slot-ref o n)))
+ (if (unbound? x)
+ (slot-unbound o)
+ x)))
+
+(define-standard-accessor-method ((standard-get n) o)
+ (@slot-ref o n))
+
+(define-standard-accessor-method ((standard-set n) o v)
+ (@slot-set! o n v))
;;; compute-getters-n-setters
;;;
((#:virtual) ;; No allocation
;; slot-ref and slot-set! function must be given by the user
(let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
- (set (get-keyword #:slot-set! (slot-definition-options s) #f))
- (env (class-environment class)))
+ (set (get-keyword #:slot-set! (slot-definition-options s) #f)))
(if (not (and get set))
(goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
s))
(set-object-procedure! object
(lambda args (apply proc args)))))))
-(define-method (initialize (class <operator-class>) initargs)
- (next-method)
- (initialize-object-procedure class initargs))
-
-(define-method (initialize (owsc <operator-with-setter-class>) initargs)
- (next-method)
- (%set-object-setter! owsc (get-keyword #:setter initargs #f)))
-
(define-method (initialize (entity <entity>) initargs)
(next-method)
(initialize-object-procedure entity initargs))
(slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
(slot-set! method 'specializers (get-keyword #:specializers initargs '()))
(slot-set! method 'procedure
- (get-keyword #:procedure initargs dummy-procedure))
- (slot-set! method 'code-table '())
+ (get-keyword #:procedure initargs #f))
(slot-set! method 'formals (get-keyword #:formals initargs '()))
(slot-set! method 'body (get-keyword #:body initargs '()))
- (slot-set! method 'compile-env (get-keyword #:compile-env initargs #f)))
+ (slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
(define-method (initialize (obj <foreign-object>) initargs))