X-Git-Url: http://git.chise.org/gitweb/?p=elisp%2Fflim.git;a=blobdiff_plain;f=luna.el;h=238fba8e49a66042233a45e703eb7922d2e67dc5;hp=e66d26575e0f9f81864f25ec017c9f9a8fc785de;hb=HEAD;hpb=09986cc02a99ec95288aef3f2e890426ae252b54 diff --git a/luna.el b/luna.el index e66d265..238fba8 100644 --- a/luna.el +++ b/luna.el @@ -1,7 +1,6 @@ ;;; luna.el --- tiny OOP system kernel -;; Copyright (C) 1999 Electrotechnical Laboratory, JAPAN. -;; Licensed to the Free Software Foundation. +;; Copyright (C) 1999,2000,2002 Free Software Foundation, Inc. ;; Author: MORIOKA Tomohiko ;; Keywords: OOP @@ -20,54 +19,54 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. ;;; Code: (eval-when-compile (require 'cl)) -(eval-when-compile (require 'static)) -(static-condition-case nil - :symbol-for-testing-whether-colon-keyword-is-available-or-not - (void-variable - (defconst :before ':before) - (defconst :after ':after) - (defconst :around ':around))) +;;; @ class +;;; (defmacro luna-find-class (name) - "Return the luna-class of the given NAME." + "Return a luna-class that has NAME." `(get ,name 'luna-class)) +;; Give NAME (symbol) the luna-class CLASS. (defmacro luna-set-class (name class) `(put ,name 'luna-class ,class)) +;; Return the obarray of luna-class CLASS. (defmacro luna-class-obarray (class) `(aref ,class 1)) +;; Return the parents of luna-class CLASS. (defmacro luna-class-parents (class) `(aref ,class 2)) +;; Return the number of slots of luna-class CLASS. (defmacro luna-class-number-of-slots (class) `(aref ,class 3)) -(defmacro luna-define-class (type &optional parents slots) - "Define TYPE as a luna-class. -If PARENTS is specified, TYPE inherits PARENTS. -Each parent must be name of luna-class (symbol). -If SLOTS is specified, TYPE will be defined to have them." - `(luna-define-class-function ',type ',(append parents '(standard-object)) +(defmacro luna-define-class (class &optional parents slots) + "Define CLASS as a luna-class. +CLASS always inherits the luna-class `standard-object'. + +The optional 1st arg PARENTS is a list luna-class names. These +luna-classes are also inheritted by CLASS. + +The optional 2nd arg SLOTS is a list of slots CLASS will have." + `(luna-define-class-function ',class ',(append parents '(standard-object)) ',slots)) -(defun luna-define-class-function (type &optional parents slots) - (static-condition-case nil - :symbol-for-testing-whether-colon-keyword-is-available-or-not - (void-variable - (let (key) - (dolist (slot slots) - (setq key (intern (format ":%s" slot))) - (set key key))))) + +;; Define CLASS as a luna-class. PARENTS, if non-nil, is a list of +;; luna-class names inherited by CLASS. SLOTS, if non-nil, is a list +;; of slots belonging to CLASS. + +(defun luna-define-class-function (class &optional parents slots) (let ((oa (make-vector 31 0)) (rest parents) parent name @@ -81,79 +80,80 @@ If SLOTS is specified, TYPE will be defined to have them." (setq name (symbol-name sym)) (unless (intern-soft name oa) (put (intern name oa) 'luna-slot-index (+ j b)) - (setq i (1+ i)) - ))) - (luna-class-obarray (luna-find-class parent))) - ) + (setq i (1+ i))))) + (luna-class-obarray (luna-find-class parent)))) (setq rest slots) (while rest (setq name (symbol-name (pop rest))) (unless (intern-soft name oa) (put (intern name oa) 'luna-slot-index i) - (setq i (1+ i)) - )) - (luna-set-class type (vector 'class oa parents i)) - )) + (setq i (1+ i)))) + (luna-set-class class (vector 'class oa parents i)))) + + +;; Return a member (slot or method) of CLASS that has name +;; MEMBER-NAME. (defun luna-class-find-member (class member-name) (or (stringp member-name) (setq member-name (symbol-name member-name))) - (or (intern-soft member-name (luna-class-obarray class)) - (let ((parents (luna-class-parents class)) - ret) - (while (and parents - (null - (setq ret (luna-class-find-member - (luna-find-class (pop parents)) - member-name))))) - ret))) + (intern-soft member-name (luna-class-obarray class))) + + +;; Return a member (slot or method) of CLASS that has name +;; MEMBER-NAME. If CLASS doesnt' have such a member, make it in +;; CLASS. (defsubst luna-class-find-or-make-member (class member-name) (or (stringp member-name) (setq member-name (symbol-name member-name))) (intern member-name (luna-class-obarray class))) + +;; Return the index number of SLOT-NAME in CLASS. + (defmacro luna-class-slot-index (class slot-name) `(get (luna-class-find-member ,class ,slot-name) 'luna-slot-index)) -(defmacro luna-slot-index (entity slot-name) - `(luna-class-slot-index (luna-find-class (luna-class-name ,entity)) - ,slot-name)) +(defmacro luna-define-method (name &rest definition) + "Define NAME as a method of a luna class. -(defsubst luna-slot-value (entity slot) - "Return the value of SLOT of ENTITY." - (aref entity (luna-slot-index entity slot))) +Usage of this macro follows: -(defsubst luna-set-slot-value (entity slot value) - "Store VALUE into SLOT of ENTITY." - (aset entity (luna-slot-index entity slot) value)) + (luna-define-method NAME [METHOD-QUALIFIER] ARGLIST [DOCSTRING] BODY...) -(defmacro luna-define-method (name &rest definition) - "Define NAME as a method function of a class. +The optional 1st argument METHOD-QUALIFIER specifies when and how the +method is called. -Usage of this macro follows: +If it is :before, call the method before calling the parents' methods. + +If it is :after, call the method after calling the parents' methods. - (luna-define-method NAME [METHOD-QUALIFIER] ARGLIST [DOCSTRING] BODY...) +If it is :around, call the method only. The parents' methods can be +executed by calling the function `luna-call-next-method' in BODY. -NAME is the name of method. +Otherwize, call the method only, and the parents' methods are never +executed. In this case, METHOD-QUALIFIER is treated as ARGLIST. -Optional argument METHOD-QUALIFIER must be :before, :after or :around. -If it is :before / :after, the method is called before / after a -method of parent class is finished. ARGLIST is like an argument list -of lambda, but (car ARGLIST) must be specialized parameter. (car (car -ARGLIST)) is name of variable and \(nth 1 (car ARGLIST)) is name of -class. +ARGLIST has the form ((VAR CLASS) METHOD-ARG ...), where VAR is a +variable name that should be bound to an entity that receives the +message NAME, CLASS is a class name. The first argument to the method +is VAR, and the remaining arguments are METHOD-ARGs. -Optional argument DOCSTRING is the documentation of method. +If VAR is nil, arguments to the method are METHOD-ARGs. This kind of +methods can't be called from generic-function (see +`luna-define-generic'). -BODY is the body of method." +The optional 4th argument DOCSTRING is the documentation of the +method. If it is not string, it is treated as BODY. + +The optional 5th BODY is the body of the method." (let ((method-qualifier (pop definition)) args specializer class self) (if (memq method-qualifier '(:before :after :around)) (setq args (pop definition)) (setq args method-qualifier - method-qualifier nil) - ) + method-qualifier nil)) (setq specializer (car args) class (nth 1 specializer) self (car specializer)) @@ -162,10 +162,17 @@ BODY is the body of method." (cdr args)) ,@definition)) (sym (luna-class-find-or-make-member - (luna-find-class ',class) ',name))) + (luna-find-class ',class) ',name)) + (cache (get ',name 'luna-method-cache))) + (and cache + (fboundp sym) + (mapatoms + (lambda (s) + (if (memq (symbol-function sym) (symbol-value s)) + (unintern s cache))) + cache)) (fset sym func) - (put sym 'luna-method-qualifier ,method-qualifier) - ))) + (put sym 'luna-method-qualifier ,method-qualifier)))) (put 'luna-define-method 'lisp-indent-function 'defun) @@ -174,10 +181,13 @@ BODY is the body of method." ((arg symbolp) [&rest arg] [&optional ["&optional" arg &rest arg]] - &optional ["&rest" arg] - ) + &optional ["&rest" arg]) def-body)) + +;; Return a list of method functions named SERVICE registered in the +;; parents of CLASS. + (defun luna-class-find-parents-functions (class service) (let ((parents (luna-class-parents class)) ret) @@ -188,25 +198,52 @@ BODY is the body of method." service))))) ret)) +;; Return a list of method functions named SERVICE registered in CLASS +;; and the parents.. + (defun luna-class-find-functions (class service) (let ((sym (luna-class-find-member class service))) (if (fboundp sym) (cond ((eq (get sym 'luna-method-qualifier) :before) (cons (symbol-function sym) - (luna-class-find-parents-functions class service)) - ) + (luna-class-find-parents-functions class service))) ((eq (get sym 'luna-method-qualifier) :after) (nconc (luna-class-find-parents-functions class service) - (list (symbol-function sym))) - ) + (list (symbol-function sym)))) ((eq (get sym 'luna-method-qualifier) :around) - (cons sym (luna-class-find-parents-functions class service)) - ) + (cons sym (luna-class-find-parents-functions class service))) (t - (list (symbol-function sym)) - )) - (luna-class-find-parents-functions class service) - ))) + (list (symbol-function sym)))) + (luna-class-find-parents-functions class service)))) + + +;;; @ instance (entity) +;;; + +(defmacro luna-class-name (entity) + "Return class-name of the ENTITY." + `(aref ,entity 0)) + +(defmacro luna-set-class-name (entity name) + `(aset ,entity 0 ,name)) + +(defmacro luna-get-obarray (entity) + `(aref ,entity 1)) + +(defmacro luna-set-obarray (entity obarray) + `(aset ,entity 1 ,obarray)) + +(defmacro luna-slot-index (entity slot-name) + `(luna-class-slot-index (luna-find-class (luna-class-name ,entity)) + ,slot-name)) + +(defsubst luna-slot-value (entity slot) + "Return the value of SLOT of ENTITY." + (aref entity (luna-slot-index entity slot))) + +(defsubst luna-set-slot-value (entity slot value) + "Store VALUE into SLOT of ENTITY." + (aset entity (luna-slot-index entity slot) value)) (defmacro luna-find-functions (entity service) `(luna-class-find-functions (luna-find-class (luna-class-name ,entity)) @@ -214,6 +251,8 @@ BODY is the body of method." (defsubst luna-send (entity message &rest luna-current-method-arguments) "Send MESSAGE to ENTITY, and return the result. +ENTITY is an instance of a luna class, and MESSAGE is a method name of +the luna class. LUNA-CURRENT-METHOD-ARGUMENTS is arguments of the MESSAGE." (let ((luna-next-methods (luna-find-functions entity message)) luna-current-method @@ -232,11 +271,12 @@ LUNA-CURRENT-METHOD-ARGUMENTS is arguments of the MESSAGE." (eval-when-compile (defvar luna-next-methods nil) - (defvar luna-current-method-arguments nil) - ) + (defvar luna-current-method-arguments nil)) (defun luna-call-next-method () - "Call the next method in a method with :around qualifier." + "Call the next method in the current method function. +A method function that has :around qualifier should call this function +to execute the parents' methods." (let (luna-current-method luna-previous-return-value) (while (and luna-next-methods @@ -251,58 +291,91 @@ LUNA-CURRENT-METHOD-ARGUMENTS is arguments of the MESSAGE." t)))) luna-previous-return-value)) -(defmacro luna-class-name (entity) - "Return class-name of the ENTITY." - `(aref ,entity 0)) +(defun luna-make-entity (class &rest init-args) + "Make an entity (instance) of luna-class CLASS and return it. +INIT-ARGS is a plist of the form (:SLOT1 VAL1 :SLOT2 VAL2 ...), +where SLOTs are slots of CLASS and the VALs are initial values of +the corresponding SLOTs." + (let* ((c (get class 'luna-class)) + (v (make-vector (luna-class-number-of-slots c) nil))) + (luna-set-class-name v class) + (luna-set-obarray v (make-vector 7 0)) + (apply #'luna-send v 'initialize-instance v init-args))) -(defmacro luna-set-class-name (entity name) - `(aset ,entity 0 ,name)) -(defmacro luna-get-obarray (entity) - `(aref ,entity 1)) +;;; @ interface (generic function) +;;; -(defmacro luna-set-obarray (entity obarray) - `(aset ,entity 1 ,obarray)) +;; Find a method of ENTITY that handles MESSAGE, and call it with +;; arguments LUNA-CURRENT-METHOD-ARGUMENTS. -(defun luna-make-entity (type &rest init-args) - "Make instance of luna-class TYPE and return it. -If INIT-ARGS is specified, it is used as initial values of the slots. -It must be plist and each slot name must have prefix `:'." - (let* ((c (get type 'luna-class)) - (v (make-vector (luna-class-number-of-slots c) nil))) - (luna-set-class-name v type) - (luna-set-obarray v (make-vector 7 0)) - (apply #'luna-send v 'initialize-instance v init-args) - )) +(defun luna-apply-generic (entity message &rest luna-current-method-arguments) + (let* ((class (luna-class-name entity)) + (cache (get message 'luna-method-cache)) + (sym (intern-soft (symbol-name class) cache)) + luna-next-methods) + (if sym + (setq luna-next-methods (symbol-value sym)) + (setq luna-next-methods + (luna-find-functions entity message)) + (set (intern (symbol-name class) cache) + luna-next-methods)) + (luna-call-next-method))) + + +;; Convert ARGLIST (argument list spec for a method function) to the +;; actual list of arguments. (defsubst luna-arglist-to-arguments (arglist) (let (dest) (while arglist (let ((arg (car arglist))) (or (memq arg '(&optional &rest)) - (setq dest (cons arg dest))) - ) + (setq dest (cons arg dest)))) (setq arglist (cdr arglist))) (nreverse dest))) + (defmacro luna-define-generic (name args &optional doc) - "Define generic-function NAME. -ARGS is argument of and DOC is DOC-string." + "Define a function NAME that provides a generic interface to the method NAME. +ARGS is the argument list for NAME. The first element of ARGS is an +entity. + +The function handles a message sent to the entity by calling the +method with proper arguments. + +The optional 3rd argument DOC is the documentation string for NAME." (if doc - `(defun ,(intern (symbol-name name)) ,args - ,doc - (luna-send ,(car args) ',name - ,@(luna-arglist-to-arguments args)) - ) - `(defun ,(intern (symbol-name name)) ,args - (luna-send ,(car args) ',name - ,@(luna-arglist-to-arguments args)) - ))) + `(progn + (defun ,(intern (symbol-name name)) ,args + ,doc + (luna-apply-generic ,(car args) ',name + ,@(luna-arglist-to-arguments args))) + (put ',name 'luna-method-cache (make-vector 31 0))) + `(progn + (defun ,(intern (symbol-name name)) ,args + (luna-apply-generic ,(car args) ',name + ,@(luna-arglist-to-arguments args))) + (put ',name 'luna-method-cache (make-vector 31 0))))) (put 'luna-define-generic 'lisp-indent-function 'defun) + +;;; @ accessor +;;; + (defun luna-define-internal-accessors (class-name) - "Define internal accessors for an entity of CLASS-NAME." + "Define internal accessors for instances of the luna class CLASS-NAME. + +Internal accessors are macros to refer and set a slot value of the +instances. For instance, if the class has SLOT, macros +CLASS-NAME-SLOT-internal and CLASS-NAME-set-SLOT-internal are defined. + +CLASS-NAME-SLOT-internal accepts one argument INSTANCE, and returns +the value of SLOT. + +CLASS-NAME-set-SLOT-internal accepts two arguemnt INSTANCE and VALUE, +and sets SLOT to VALUE." (let ((entity-class (luna-find-class class-name)) parents parent-class) (mapatoms @@ -314,8 +387,7 @@ ARGS is argument of and DOC is DOC-string." (setq parent-class (luna-find-class (car parents))) (if (luna-class-slot-index parent-class slot) (throw 'derived nil)) - (setq parents (cdr parents)) - ) + (setq parents (cdr parents))) (eval `(progn (defmacro ,(intern (format "%s-%s-internal" @@ -323,23 +395,26 @@ ARGS is argument of and DOC is DOC-string." (entity) (list 'aref entity ,(luna-class-slot-index entity-class - (intern (symbol-name slot))) - )) + (intern (symbol-name slot))))) (defmacro ,(intern (format "%s-set-%s-internal" class-name slot)) (entity value) (list 'aset entity ,(luna-class-slot-index entity-class (intern (symbol-name slot))) - value)) - )) - ))) + value))))))) (luna-class-obarray entity-class)))) + +;;; @ standard object +;;; + +;; Define super class of all luna classes. (luna-define-class-function 'standard-object) (luna-define-method initialize-instance ((entity standard-object) &rest init-args) + "Initialize slots of ENTITY by INIT-ARGS." (let* ((c (luna-find-class (luna-class-name entity))) (oa (luna-class-obarray c)) s i) @@ -347,8 +422,7 @@ ARGS is argument of and DOC is DOC-string." (setq s (intern-soft (substring (symbol-name (pop init-args)) 1) oa) i (pop init-args)) (if s - (aset entity (get s 'luna-slot-index) i) - )) + (aset entity (get s 'luna-slot-index) i))) entity))