changeset 5226:7789ae555c45

Add Common Lisp's #'complement to cl-extra.el. 2010-06-02 Aidan Kehoe <kehoea@parhasard.net> * cl-macs.el (complement): * cl-extra.el (complement): Add an implementation and a compiler macro for #'complement, as specified by CL. For discussion; the compiler macro may be a little too aggressive about taking the compile time argument lists of the functions it is inverting.
author Aidan Kehoe <kehoea@parhasard.net>
date Wed, 02 Jun 2010 16:18:50 +0100
parents 1086297242fe
children fbd1485af104
files lisp/ChangeLog lisp/cl-extra.el lisp/cl-macs.el
diffstat 3 files changed, 56 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Wed Jun 02 15:31:15 2010 +0100
+++ b/lisp/ChangeLog	Wed Jun 02 16:18:50 2010 +0100
@@ -1,3 +1,12 @@
+2010-06-02  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* cl-macs.el (complement):
+	* cl-extra.el (complement):
+	Add an implementation and a compiler macro for #'complement, as
+	specified by CL. For discussion; the compiler macro may be a
+	little too aggressive about taking the compile time argument lists
+	of the functions it is inverting.
+
 2010-05-31  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* specifier.el (current-display-table):
--- a/lisp/cl-extra.el	Wed Jun 02 15:31:15 2010 +0100
+++ b/lisp/cl-extra.el	Wed Jun 02 16:18:50 2010 +0100
@@ -100,6 +100,14 @@
 ;; XEmacs; #'map, #'mapc, #'mapl, #'maplist, #'mapcon, #'some and #'every
 ;; are now in C, together with #'map-into, which was never in this file.
 
+;; The compiler macro for this in cl-macs.el means if #'complement is handed
+;; a constant expression, byte-compiled code will see a byte-compiled
+;; function.
+(defun complement (function &optional documentation)
+  "Return a function which gives the logical inverse of what FUNCTION would."
+  `(lambda (&rest arguments) ,@(if documentation (list documentation))
+     (not (apply ',function arguments))))
+
 (defun notany (cl-pred cl-seq &rest cl-rest)
   "Return true if PREDICATE is false of every element of SEQ or SEQs."
   (not (apply 'some cl-pred cl-seq cl-rest)))
--- a/lisp/cl-macs.el	Wed Jun 02 15:31:15 2010 +0100
+++ b/lisp/cl-macs.el	Wed Jun 02 16:18:50 2010 +0100
@@ -3712,6 +3712,45 @@
 (define-compiler-macro pairlis (a b &optional c)
   `(nconc (mapcar* #'cons ,a ,b) ,c))
 
+(define-compiler-macro complement (&whole form fn)
+  (if (or (eq (car-safe fn) 'function) (eq (car-safe fn) 'quote))
+      (cond
+       ((and (symbolp (second fn)) (get (second fn) 'byte-compile-negated-op))
+        (list 'function (get (second fn) 'byte-compile-negated-op)))
+       ((and (symbolp (second fn)) (fboundp (second fn))
+             (compiled-function-p (indirect-function (second fn))))
+        (let* ((cf (indirect-function (second fn)))
+               (cfa (compiled-function-arglist cf))
+               (do-apply (memq '&rest cfa)))
+          `#'(lambda ,cfa
+               (not (,@(if do-apply `(apply ',(second fn)) (list (second fn)))
+                       ,@(remq '&optional
+                               (remq '&rest cfa)))))))
+       (t
+        `#'(lambda (&rest arguments)
+             (not (apply ,fn arguments)))))
+    ;; Determine the function to call at runtime.
+    (destructuring-bind
+        (arglist instructions constants stack-depth)
+        (let ((compiled-lambda
+               (byte-compile-sexp
+                #'(lambda (&rest arguments)
+                    (not (apply 'placeholder arguments))))))
+          (list
+           (compiled-function-arglist compiled-lambda)
+           (compiled-function-instructions compiled-lambda)
+           (append (compiled-function-constants compiled-lambda) nil)
+           (compiled-function-stack-depth compiled-lambda)))
+      `(make-byte-code
+        ',arglist ,instructions (vector
+                                 ,@(nsublis
+                                    (list (cons (quote-maybe
+                                                 'placeholder)
+                                                fn))
+                                    (mapcar #'quote-maybe constants)
+                                    :test #'equal))
+        ,stack-depth))))
+
 (mapc
  #'(lambda (y)
      (put (car y) 'side-effect-free t)