sasl.el (TopLevel): Autoload `digest-md5' and `scram-md5'.
[elisp/flim.git] / luna.el
diff --git a/luna.el b/luna.el
index 7b348de..e66d265 100644 (file)
--- a/luna.el
+++ b/luna.el
@@ -1,6 +1,7 @@
 ;;; luna.el --- tiny OOP system kernel
 
 ;; Copyright (C) 1999 Electrotechnical Laboratory, JAPAN.
+;; Licensed to the Free Software Foundation.
 
 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
 ;; Keywords: OOP
 
 (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)))
+
 (defmacro luna-find-class (name)
   "Return the luna-class of the given NAME."
   `(get ,name '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))
+                              ',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)))))
   (let ((oa (make-vector 31 0))
        (rest parents)
        parent name
-       (i 2))
+       (i 2)
+       b j)
     (while rest
-      (setq parent (pop rest))
+      (setq parent (pop rest)
+           b (- i 2))
       (mapatoms (lambda (sym)
-                 (when (get sym 'luna-member-index)
+                 (when (setq j (get sym 'luna-slot-index))
                    (setq name (symbol-name sym))
                    (unless (intern-soft name oa)
-                     (put (intern name oa) 'luna-member-index i)
+                     (put (intern name oa) 'luna-slot-index (+ j b))
                      (setq i (1+ i))
                      )))
                (luna-class-obarray (luna-find-class parent)))
@@ -66,48 +89,13 @@ If SLOTS is specified, TYPE will be defined to have them."
     (while rest
       (setq name (symbol-name (pop rest)))
       (unless (intern-soft name oa)
-       (put (intern name oa) 'luna-member-index i)
+       (put (intern name oa) 'luna-slot-index i)
        (setq i (1+ i))
        ))
-    `(luna-set-class ',type
-                    (vector 'class ,oa ',parents ,i))
+    (luna-set-class type (vector 'class oa parents i))
     ))
 
-(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-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 `:'."
-  `(apply #'luna-make-entity-function ',type ',init-args))
-
-(defsubst luna-make-entity-function (type &rest init-args)
-  (let* ((c (get type 'luna-class))
-        (v (make-vector (luna-class-number-of-slots c) nil))
-        (oa (luna-class-obarray c))
-        s i)
-    (luna-set-class-name v type)
-    (luna-set-obarray v (make-vector 7 0))
-    (while init-args
-      (setq s (intern-soft (substring (symbol-name (pop init-args)) 1) oa)
-           i (pop init-args))
-      (if s
-         (aset v (get s 'luna-member-index) i)
-       ))
-    v))
-
-(defsubst luna-class-find-member (class 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))
@@ -126,7 +114,7 @@ It must be plist and each slot name must have prefix `:'."
   (intern member-name (luna-class-obarray class)))
 
 (defmacro luna-class-slot-index (class slot-name)
-  `(get (luna-class-find-member ,class ,slot-name) 'luna-member-index))
+  `(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))
@@ -140,45 +128,152 @@ It must be plist and each slot name must have prefix `:'."
   "Store VALUE into SLOT of ENTITY."
   (aset entity (luna-slot-index entity slot) value))
 
-(defmacro luna-define-method (name args &rest body)
-  "Define NAME as a method function of (nth 1 (car ARGS)) backend.
+(defmacro luna-define-method (name &rest definition)
+  "Define NAME as a method function of a class.
+
+Usage of this macro follows:
+
+  (luna-define-method NAME [METHOD-QUALIFIER] ARGLIST [DOCSTRING] BODY...) 
+
+NAME is the name of method.
+
+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.
+
+Optional argument DOCSTRING is the documentation of method.
 
-ARGS is like an argument list of lambda, but (car ARGS) must be
-specialized parameter.  (car (car ARGS)) is name of variable and (nth
-1 (car ARGS)) is name of backend."
-  (let* ((specializer (car args))
-        (class (nth 1 specializer))
-        (self (car specializer)))
+BODY is the body of 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)
+      )
+    (setq specializer (car args)
+         class (nth 1 specializer)
+         self (car specializer))
     `(let ((func (lambda ,(if self
                              (cons self (cdr args))
                            (cdr args))
-                  ,@body)))
-       (fset (luna-class-find-or-make-member (luna-find-class ',class) ',name)
-            func))))
+                  ,@definition))
+          (sym (luna-class-find-or-make-member
+                (luna-find-class ',class) ',name)))
+       (fset sym func)
+       (put sym 'luna-method-qualifier ,method-qualifier)
+       )))
 
 (put 'luna-define-method 'lisp-indent-function 'defun)
 
-(defsubst luna-class-find-function (class service)
+(def-edebug-spec luna-define-method
+  (&define name [&optional &or ":before" ":after" ":around"]
+          ((arg symbolp)
+           [&rest arg]
+           [&optional ["&optional" arg &rest arg]]
+           &optional ["&rest" arg]
+           )
+          def-body))
+
+(defun luna-class-find-parents-functions (class service)
+  (let ((parents (luna-class-parents class))
+       ret)
+    (while (and parents
+               (null
+                (setq ret (luna-class-find-functions
+                           (luna-find-class (pop parents))
+                           service)))))
+    ret))
+
+(defun luna-class-find-functions (class service)
   (let ((sym (luna-class-find-member class service)))
     (if (fboundp sym)
-       (symbol-function sym)
-      (let ((parents (luna-class-parents class))
-           ret)
-       (while (and parents
-                   (null
-                    (setq ret (luna-class-find-function
-                               (luna-find-class (pop parents))
-                               service)))))
-       ret))))
+       (cond ((eq (get sym 'luna-method-qualifier) :before)
+              (cons (symbol-function sym)
+                    (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)))
+              )
+             ((eq (get sym 'luna-method-qualifier) :around)
+              (cons sym (luna-class-find-parents-functions class service))
+              )
+             (t
+              (list (symbol-function sym))
+              ))
+      (luna-class-find-parents-functions class service)
+      )))
+
+(defmacro luna-find-functions (entity service)
+  `(luna-class-find-functions (luna-find-class (luna-class-name ,entity))
+                             ,service))
+
+(defsubst luna-send (entity message &rest luna-current-method-arguments)
+  "Send MESSAGE to ENTITY, and return the result.
+LUNA-CURRENT-METHOD-ARGUMENTS is arguments of the MESSAGE."
+  (let ((luna-next-methods (luna-find-functions entity message))
+       luna-current-method
+       luna-previous-return-value)
+    (while (and luna-next-methods
+               (progn
+                 (setq luna-current-method (pop luna-next-methods)
+                       luna-previous-return-value
+                       (apply luna-current-method
+                              luna-current-method-arguments))
+                 (if (symbolp luna-current-method)
+                     (not (eq (get luna-current-method
+                                   'luna-method-qualifier) :around))
+                   t))))
+    luna-previous-return-value))
+
+(eval-when-compile
+  (defvar luna-next-methods nil)
+  (defvar luna-current-method-arguments nil)
+  )
+
+(defun luna-call-next-method ()
+  "Call the next method in a method with :around qualifier."
+  (let (luna-current-method
+       luna-previous-return-value)
+    (while (and luna-next-methods
+               (progn
+                 (setq luna-current-method (pop luna-next-methods)
+                       luna-previous-return-value
+                       (apply luna-current-method
+                              luna-current-method-arguments))
+                 (if (symbolp luna-current-method)
+                     (not (eq (get luna-current-method
+                                   'luna-method-qualifier) :around))
+                   t))))
+    luna-previous-return-value))
+
+(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-find-function (entity service)
-  `(luna-class-find-function (luna-find-class (luna-class-name ,entity))
-                            ,service))
+(defmacro luna-set-obarray (entity obarray)
+  `(aset ,entity 1 ,obarray))
 
-(defsubst luna-send (entity message &rest args)
-  "Send MESSAGE to ENTITY with ARGS, and return the result."
-  (apply (luna-find-function entity message)
-        entity args))
+(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)
+    ))
 
 (defsubst luna-arglist-to-arguments (arglist)
   (let (dest)
@@ -197,15 +292,65 @@ ARGS is argument of and DOC is DOC-string."
       `(defun ,(intern (symbol-name name)) ,args
         ,doc
         (luna-send ,(car args) ',name
-                   ,@(luna-arglist-to-arguments (cdr args)))
+                   ,@(luna-arglist-to-arguments args))
         )
     `(defun ,(intern (symbol-name name)) ,args
        (luna-send ,(car args) ',name
-                 ,@(luna-arglist-to-arguments (cdr args)))
+                 ,@(luna-arglist-to-arguments args))
        )))
 
 (put 'luna-define-generic 'lisp-indent-function 'defun)
 
+(defun luna-define-internal-accessors (class-name)
+  "Define internal accessors for an entity of CLASS-NAME."
+  (let ((entity-class (luna-find-class class-name))
+       parents parent-class)
+    (mapatoms
+     (lambda (slot)
+       (if (luna-class-slot-index entity-class slot)
+          (catch 'derived
+            (setq parents (luna-class-parents entity-class))
+            (while parents
+              (setq parent-class (luna-find-class (car parents)))
+              (if (luna-class-slot-index parent-class slot)
+                  (throw 'derived nil))
+              (setq parents (cdr parents))
+              )
+            (eval
+             `(progn
+                (defmacro ,(intern (format "%s-%s-internal"
+                                           class-name slot))
+                  (entity)
+                  (list 'aref entity
+                        ,(luna-class-slot-index entity-class
+                                                (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))
+                ))
+            )))
+     (luna-class-obarray entity-class))))
+
+(luna-define-class-function 'standard-object)
+
+(luna-define-method initialize-instance ((entity standard-object)
+                                        &rest init-args)
+  (let* ((c (luna-find-class (luna-class-name entity)))
+        (oa (luna-class-obarray c))
+        s i)
+    (while init-args
+      (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)
+       ))
+    entity))
+
 
 ;;; @ end
 ;;;