(mime-library-product): Update to 1.13.4.
[elisp/flim.git] / luna.el
diff --git a/luna.el b/luna.el
index 8f2c64f..f3504a6 100644 (file)
--- a/luna.el
+++ b/luna.el
 
 (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))
+  (` (get (, name) 'luna-class)))
 
 (defmacro luna-set-class (name class)
-  `(put ,name 'luna-class ,class))
+  (` (put (, name) 'luna-class (, class))))
 
 (defmacro luna-class-obarray (class)
-  `(aref ,class 1))
+  (` (aref (, class) 1)))
 
 (defmacro luna-class-parents (class)
-  `(aref ,class 2))
+  (` (aref (, class) 2)))
 
 (defmacro luna-class-number-of-slots (class)
-  `(aref ,class 3))
+  (` (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))
-                              ',slots))
+  (` (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
@@ -60,13 +77,14 @@ If SLOTS is specified, TYPE will be defined to have them."
     (while rest
       (setq parent (pop rest)
            b (- i 2))
-      (mapatoms (lambda (sym)
-                 (when (setq j (get sym 'luna-slot-index))
-                   (setq name (symbol-name sym))
-                   (unless (intern-soft name oa)
-                     (put (intern name oa) 'luna-slot-index (+ j b))
-                     (setq i (1+ i))
-                     )))
+      (mapatoms (function
+                (lambda (sym)
+                  (when (setq j (get sym 'luna-slot-index))
+                    (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 rest slots)
@@ -98,11 +116,11 @@ If SLOTS is specified, TYPE will be defined to have them."
   (intern member-name (luna-class-obarray class)))
 
 (defmacro luna-class-slot-index (class slot-name)
-  `(get (luna-class-find-member ,class ,slot-name) 'luna-slot-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))
-                         ,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."
@@ -121,18 +139,19 @@ Usage of this macro follows:
 
 NAME is the name of method.
 
-Optional argument METHOD-QUALIFIER must be :before or :after.  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 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.
 
 BODY is the body of method."
   (let ((method-qualifier (pop definition))
        args specializer class self)
-    (if (memq method-qualifier '(:before :after))
+    (if (memq method-qualifier '(:before :after :around))
        (setq args (pop definition))
       (setq args method-qualifier
            method-qualifier nil)
@@ -140,20 +159,22 @@ BODY is the body of method."
     (setq specializer (car args)
          class (nth 1 specializer)
          self (car specializer))
-    `(let ((func (lambda ,(if self
-                             (cons self (cdr args))
-                           (cdr args))
-                  ,@definition))
-          (sym (luna-class-find-or-make-member
-                (luna-find-class ',class) ',name)))
-       (fset sym func)
-       (put sym 'luna-method-qualifier ,method-qualifier)
-       )))
+    (` (let ((func (function
+                   (lambda (, (if self
+                                  (cons self (cdr args))
+                                (cdr args)))
+                     (,@ 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)
 
 (def-edebug-spec luna-define-method
-  (&define name [&optional &or ":before" ":after"]
+  (&define name [&optional &or ":before" ":after" ":around"]
           ((arg symbolp)
            [&rest arg]
            [&optional ["&optional" arg &rest arg]]
@@ -182,6 +203,9 @@ BODY is the body of method."
               (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))
               ))
@@ -189,31 +213,60 @@ BODY is the body of method."
       )))
 
 (defmacro luna-find-functions (entity service)
-  `(luna-class-find-functions (luna-find-class (luna-class-name ,entity))
-                             ,service))
-
-(defsubst luna-send (entity message &rest args)
-  "Send MESSAGE to ENTITY with ARGS, and return the result."
-  (let ((functions (luna-find-functions entity message))
-       ret)
-    (while functions
-      (setq ret (apply (car functions) args)
-           functions (cdr functions))
-      )
-    ret))
+  (` (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))
+  (` (aref (, entity) 0)))
 
 (defmacro luna-set-class-name (entity name)
-  `(aset ,entity 0 ,name))
+  (` (aset (, entity) 0 (, name))))
 
 (defmacro luna-get-obarray (entity)
-  `(aref ,entity 1))
+  (` (aref (, entity) 1)))
 
 (defmacro luna-set-obarray (entity obarray)
-  `(aset ,entity 1 ,obarray))
+  (` (aset (, entity) 1 (, obarray))))
 
 (defun luna-make-entity (type &rest init-args)
   "Make instance of luna-class TYPE and return it.
@@ -223,7 +276,7 @@ It must be plist and each slot name must have prefix `:'."
         (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)
+    (apply (function luna-send) v 'initialize-instance v init-args)
     ))
 
 (defsubst luna-arglist-to-arguments (arglist)
@@ -240,15 +293,15 @@ It must be plist and each slot name must have prefix `:'."
   "Define generic-function NAME.
 ARGS is argument of and DOC is DOC-string."
   (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))
-       )))
+      (` (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)))
+        ))))
 
 (put 'luna-define-generic 'lisp-indent-function 'defun)
 
@@ -257,34 +310,35 @@ ARGS is argument of and DOC is DOC-string."
   (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))
-                ))
-            )))
+     (function
+      (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)