emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scratch/oclosure cd2a5037fa 1/2: oclosure.el: Add anonymous classes


From: Stefan Monnier
Subject: scratch/oclosure cd2a5037fa 1/2: oclosure.el: Add anonymous classes
Date: Sun, 9 Jan 2022 17:12:47 -0500 (EST)

branch: scratch/oclosure
commit cd2a5037fa9ae4f49909fff3de20328411e8772e
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    oclosure.el: Add anonymous classes
    
    Allow listing several types in `oclosure-lambda` and build an
    anonymous class for it on the fly.
    
    * lisp/emacs-lisp/oclosure.el (oclosure--lambda): Change `type` arg to
    take an expression rather than a value.
    (oclosure--anonymous-name, oclosure--anonymous-define): New functions.
    (oclosure-lambda): Allow multiple type symbols and generate an
    anonymous class on the fly in this case.
    
    * lisp/files.el (file-relative-name): Avoid errors if used early during
    the preload.
    
    * src/eval.c (Fcommandp): Fix the detection of docstring in CONSP case.
    
    * test/lisp/emacs-lisp/oclosure-tests.el (oclosure-test-anonymous):
    New test.
---
 lisp/emacs-lisp/oclosure.el            | 32 ++++++++++++++++++++++++--------
 lisp/files.el                          |  3 ++-
 src/eval.c                             |  8 ++++----
 test/lisp/emacs-lisp/oclosure-tests.el | 25 +++++++++++++++++++++----
 4 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index 1bb136326e..1efaf207bb 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -115,7 +115,6 @@
 ;; - `oclosure-(cl-)defun', `oclosure-(cl-)defsubst', `oclosure-define-inline'?
 ;; - Use accessor in cl-defstruct.
 ;; - Add pcase patterns for OClosures.
-;; - anonymous OClosure types.
 ;; - copiers for mixins
 ;; - class-allocated slots?
 ;; - code-allocated slots?
@@ -412,7 +411,7 @@
 
 (defmacro oclosure--lambda (type bindings mutables args &rest body)
   "Low level construction of an OClosure object.
-TYPE should be be a symbol that is (or will be) defined as an OClosure type.
+TYPE should be be an form returning an OClosure type (a symbol)
 BINDINGS should list all the slots expected by this type, in the proper order.
 MUTABLE is a list of symbols indicating which of the BINDINGS
 should be mutable.
@@ -425,9 +424,6 @@ No checking is performed,"
   ;; likely mishandle such a new special form (e.g. `generator.el').
   ;; But don't be fooled: this macro is tightly bound to `cconv.el'.
   (pcase-let*
-      ;; FIXME: Since we use the docstring internally to store the
-      ;; type we can't handle actual docstrings.  We could fix this by adding
-      ;; a docstring slot to OClosures.
       ((`(,prebody . ,body) (macroexp-parse-body body))
        (rovars (mapcar #'car bindings)))
     (dolist (mutable mutables)
@@ -448,13 +444,22 @@ No checking is performed,"
         ;; store-conversion (i.e. if a variable/slot is mutated).
         (ignore ,@rovars)
         (lambda ,args
-          (:documentation ',type)
+          (:documentation ,type)
           ,@prebody
           ;; Add dummy code which accesses the field's vars to make sure
           ;; they're captured in the closure.
           (if t nil ,@rovars ,@(mapcar (lambda (m) `(setq ,m ,m)) mutables))
           ,@body)))))
 
+(defun oclosure--anonymous-name (names)
+  (intern (format "%S" (vconcat names))))
+
+(defun oclosure--anonymous-define (name names)
+  (unless (cl--find-class name)
+    (setf (cl--find-class name)
+          (oclosure--merge-classes names)))
+  name)
+
 (defmacro oclosure-lambda (type-and-slots args &rest body)
   "Define anonymous OClosure function.
 TYPE-AND-SLOTS should be of the form (TYPE . SLOTS)
@@ -467,6 +472,15 @@ ARGS and BODY are the same as for `lambda'."
   ;; from `fields'?
   (pcase-let*
       ((`(,type . ,fields) type-and-slots)
+       (types (named-let loop ((types (list type)))
+                (if (listp (car fields))
+                    (nreverse types)
+                  (loop (cons (pop fields) types)))))
+       (type-exp (if (null (cdr types))
+                     `',(car types)
+                   `(oclosure--anonymous-define
+                     ',(oclosure--anonymous-name types) ',types)))
+       (type (eval type-exp t))
        (class (cl--find-class type))
        (slots (oclosure--class-slots class))
        (mutables '())
@@ -481,6 +495,7 @@ ARGS and BODY are the same as for `lambda'."
                      (let* ((name (car field))
                             (bind (assq name slotbinds)))
                        (cond
+                        ;; FIXME: Should we also warn about missing slots?
                         ((not bind)
                          (error "Unknown slot: %S" name))
                         ((cdr bind)
@@ -492,7 +507,7 @@ ARGS and BODY are the same as for `lambda'."
                    fields)))
     ;; FIXME: Optimize temps away when they're provided in the right order?
     `(let ,tempbinds
-       (oclosure--lambda ,type ,slotbinds ,mutables ,args ,@body))))
+       (oclosure--lambda ,type-exp ,slotbinds ,mutables ,args ,@body))))
 
 (defun oclosure--fix-type (_ignore oclosure)
   (if (byte-code-function-p oclosure)
@@ -563,7 +578,7 @@ ARGS and BODY are the same as for `lambda'."
   ;; Use `oclosure--lambda' to circumvent a bootstrapping problem:
   ;; `oclosure-accessor' is not yet defined at this point but
   ;; `oclosure--accessor-prototype' is needed when defining 
`oclosure-accessor'.
-  (oclosure--lambda oclosure-accessor ((type) (slot) (index)) nil
+  (oclosure--lambda 'oclosure-accessor ((type) (slot) (index)) nil
     (oclosure) (oclosure--get oclosure index nil)))
 
 (oclosure-define accessor
@@ -635,5 +650,6 @@ ARGS and BODY are the same as for `lambda'."
   (oclosure-lambda (oclosure-accessor (type) (slot) (index)) (val oclosure)
     (oclosure--set val oclosure
                    (oclosure--mixin-slot-index oclosure slot))))
+
 (provide 'oclosure)
 ;;; oclosure.el ends here
diff --git a/lisp/files.el b/lisp/files.el
index 94ac213a99..c4587e9c20 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -5343,7 +5343,8 @@ on a DOS/Windows machine, it returns FILENAME in expanded 
form."
     (let ((fremote (file-remote-p filename))
          (dremote (file-remote-p directory))
          (fold-case (or (file-name-case-insensitive-p filename)
-                        read-file-name-completion-ignore-case)))
+                        (bound-and-true-p
+                         read-file-name-completion-ignore-case))))
       (if ;; Conditions for separate trees
          (or
           ;; Test for different filesystems on DOS/Windows
diff --git a/src/eval.c b/src/eval.c
index 29243b1d54..8eccba9a11 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2233,10 +2233,10 @@ then strings and vectors are not accepted.  */)
            return Qt;
          else
            {
-             body = CAR_SAFE (body);
+             Lisp_Object first = CAR_SAFE (body);
              if (!NILP (CDR_SAFE (body))
-                 && (STRINGP (body) || FIXNUMP (body) ||
-                     FIXNUMP (CDR_SAFE (body))))
+                 && (STRINGP (first) || FIXNUMP (first) ||
+                     FIXNUMP (CDR_SAFE (first))))
                genfun = true;
            }
        }
@@ -2255,7 +2255,7 @@ then strings and vectors are not accepted.  */)
       fun = Fsymbol_function (fun);
     }
 
-  /* If there's no immdiate interactive form but there's a docstring,
+  /* If there's no immediate interactive form but there's a docstring,
      then delegate to the generic-function in case it's an FCR with
      a type-specific interactive-form.  */
   if (genfun
diff --git a/test/lisp/emacs-lisp/oclosure-tests.el 
b/test/lisp/emacs-lisp/oclosure-tests.el
index 3f433d8c87..243921a734 100644
--- a/test/lisp/emacs-lisp/oclosure-tests.el
+++ b/test/lisp/emacs-lisp/oclosure-tests.el
@@ -39,7 +39,7 @@
 (cl-defmethod oclosure-test-gen ((_x oclosure-test))
   (format "#<oclosure-test:%s>" (cl-call-next-method)))
 
-(ert-deftest oclosure-tests ()
+(ert-deftest oclosure-test ()
   (let* ((i 42)
          (ocl1 (oclosure-lambda (oclosure-test (fst 1) (snd 2) (name "hi"))
                    ()
@@ -66,7 +66,7 @@
                       "#<oclosure-test:#<oclosure:#<bytecode>>>")))
     ))
 
-(ert-deftest oclosure-tests--limits ()
+(ert-deftest oclosure-test-limits ()
   (should
    (condition-case err
        (let ((lexical-binding t)
@@ -109,7 +109,7 @@
   "Simple OClosure with a mutable field."
   (mut :mutable t))
 
-(ert-deftest oclosure-test--mutate ()
+(ert-deftest oclosure-test-mutate ()
   (let* ((f (oclosure-lambda (oclosure-test-mut (fst 0) (mut 3))
                 (x)
               (+ x fst mut)))
@@ -155,7 +155,7 @@
     (should (equal 'cm3 (oclosure-test-mixin2--cm ocl3)))
     (should (equal 'd3 (oclosure-test-mixin3--d ocl3)))))
 
-(ert-deftest oclosure-tests-slot-value ()
+(ert-deftest oclosure-test-slot-value ()
   (require 'eieio)
   (let ((ocl1 (oclosure-lambda (oclosure-test-mixin1 (a 'a1) (b 'b1) (bm 'bm1))
                   (x) (list a b x)))
@@ -183,4 +183,21 @@
     (should (equal 'c3 (slot-value ocl3 'c)))
     ))
 
+(ert-deftest oclosure-test-anonymous ()
+  (let ((ocl1 (oclosure-lambda (oclosure-test
+                                oclosure-test-mixin1
+                                (fst 'fst) (snd 'snd)
+                                (a 'a))
+                  (x)
+                (list x fst))))
+
+    (should (equal (oclosure-test-mixin1--a ocl1)
+                   'a))
+    (should (equal (oclosure-test--snd ocl1)
+                   'snd))
+    (should (equal (funcall ocl1 'x) '(x fst)))
+    (should (cl-typep ocl1 'oclosure-test))
+    (should (cl-typep ocl1 'oclosure-test-mixin1))
+    (should (cl-typep ocl1 '(and oclosure-test oclosure-test-mixin1)))))
+
 ;;; oclosure-tests.el ends here.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]