comparison lisp/cl-macs.el @ 4792:95b04754ea8c

Make #'equalp more compatible with CL; add a compiler macro, test & doc it. lisp/ChangeLog addition: 2009-11-08 Aidan Kehoe <kehoea@parhasard.net> * cl-extra.el (cl-string-vector-equalp) (cl-bit-vector-vector-equalp, cl-vector-array-equalp) (cl-hash-table-contents-equalp): New functions, to implement equalp treating arrays with identical contents as equivalent, as specified by Common Lisp. (equalp): Revise this function to implement array equivalence, and the hash-table equalp behaviour specified by CL. * cl-macs.el (equalp): Add a compiler macro for this function, used when one of the arguments is constant, and as such, its type is known at compile time. man/ChangeLog addition: 2009-11-08 Aidan Kehoe <kehoea@parhasard.net> * lispref/objects.texi (Equality Predicates): Document #'equalp here, as well as #'equal and #'eq. tests/ChangeLog addition: 2009-12-31 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-tests.el: Test much of the functionality of equalp; add a pointer to Paul Dietz' ANSI test suite for this function, converted to Emacs Lisp. Not including the tests themselves in XEmacs because who owns the copyright on the files is unclear and the GCL people didn't respond to my queries.
author Aidan Kehoe <kehoea@parhasard.net>
date Thu, 31 Dec 2009 15:09:41 +0000
parents e29fcfd8df5f
children 8484c6c76837
comparison
equal deleted inserted replaced
4791:ea07b60c097f 4792:95b04754ea8c
3346 (define-compiler-macro regexp-quote (&whole form string) 3346 (define-compiler-macro regexp-quote (&whole form string)
3347 (if (stringp string) 3347 (if (stringp string)
3348 (regexp-quote string) 3348 (regexp-quote string)
3349 form)) 3349 form))
3350 3350
3351 (define-compiler-macro equalp (&whole form x y)
3352 "Expand calls to `equalp' where X or Y is a constant expression.
3353
3354 Much of the processing that `equalp' does is dependent on the types of both
3355 of its arguments, and with type information for one of them, we can
3356 eliminate much of the body of the function at compile time.
3357
3358 Where both X and Y are constant expressions, `equalp' is evaluated at
3359 compile time by byte-optimize.el--this compiler macro passes FORM through to
3360 the byte optimizer in those cases."
3361 ;; Cases where both arguments are constant are handled in
3362 ;; byte-optimize.el, we only need to handle those cases where one is
3363 ;; constant here.
3364 (let* ((equalp-sym (eval-when-compile (gensym)))
3365 (let-form '(progn))
3366 (check-bit-vector t)
3367 (check-string t)
3368 (original-y y)
3369 equalp-temp checked)
3370 (macrolet
3371 ((unordered-check (check)
3372 `(prog1
3373 (setq checked
3374 (or ,check
3375 (prog1 ,(sublis '((x . y) (y . x)) check :test #'eq)
3376 (setq equalp-temp x x y y equalp-temp))))
3377 (when checked
3378 (unless (symbolp y)
3379 (setq let-form `(let ((,equalp-sym ,y))) y equalp-sym))))))
3380 ;; In the bodies of the below clauses, x is always a constant expression
3381 ;; of the type we're interested in, and y is always a symbol that refers
3382 ;; to the result non-constant side of the comparison.
3383 (cond ((unordered-check (and (arrayp x) (not (cl-const-expr-p y))))
3384 ;; Strings and other arrays. A vector containing the same
3385 ;; character elements as a given string is equalp to that string;
3386 ;; a bit-vector can only be equalp to a string if both are
3387 ;; zero-length.
3388 (cond
3389 ((member x '("" #* []))
3390 ;; No need to protect against multiple evaluation here:
3391 `(and (member ,original-y '("" #* [])) t))
3392 ((stringp x)
3393 `(,@let-form
3394 (if (stringp ,y)
3395 (eq t (compare-strings ,x nil nil
3396 ,y nil nil t))
3397 (if (vectorp ,y)
3398 (cl-string-vector-equalp ,x ,y)))))
3399 ((bit-vector-p x)
3400 `(,@let-form
3401 (if (bit-vector-p ,y)
3402 ;; No need to call equalp on each element here:
3403 (equal ,x ,y)
3404 (if (vectorp ,y)
3405 (cl-bit-vector-vector-equalp ,x ,y)))))
3406 (t
3407 (loop
3408 for elt across x
3409 ;; We may not need to check the other argument if it's a
3410 ;; string or bit vector, depending on the contents of x:
3411 always (progn
3412 (unless (characterp elt) (setq check-string nil))
3413 (unless (and (numberp elt) (or (= elt 0) (= elt 1)))
3414 (setq check-bit-vector nil))
3415 (or check-string check-bit-vector)))
3416 `(,@let-form
3417 (cond
3418 ,@(if check-string
3419 `(((stringp ,y)
3420 (cl-string-vector-equalp ,y ,x))))
3421 ,@(if check-bit-vector
3422 `(((bit-vector-p ,y)
3423 (cl-bit-vector-vector-equalp ,y ,x))))
3424 ((vectorp ,y)
3425 (cl-vector-array-equalp ,x ,y)))))))
3426 ((unordered-check (and (characterp x) (not (cl-const-expr-p y))))
3427 `(,@let-form
3428 (or (eq ,x ,y)
3429 ;; eq has a bytecode, char-equal doesn't.
3430 (and (characterp ,y)
3431 (eq (downcase ,x) (downcase ,y))))))
3432 ((unordered-check (and (numberp x) (not (cl-const-expr-p y))))
3433 `(,@let-form
3434 (and (numberp ,y)
3435 (= ,x ,y))))
3436 ((unordered-check (and (hash-table-p x) (not (cl-const-expr-p y))))
3437 ;; Hash tables; follow the CL spec.
3438 `(,@let-form
3439 (and (hash-table-p ,y)
3440 (eq ',(hash-table-test x) (hash-table-test ,y))
3441 (= ,(hash-table-count x) (hash-table-count ,y))
3442 (cl-hash-table-contents-equalp ,x ,y))))
3443 ((unordered-check
3444 ;; Symbols; eq.
3445 (and (not (cl-const-expr-p y))
3446 (or (memq x '(nil t))
3447 (and (eq (car-safe x) 'quote) (symbolp (second x))))))
3448 (cons 'eq (cdr form)))
3449 ((unordered-check
3450 ;; Compare conses at runtime, there's no real upside to
3451 ;; unrolling the function -> they fall through to the next
3452 ;; clause in this function.
3453 (and (cl-const-expr-p x) (not (consp x))
3454 (not (cl-const-expr-p y))))
3455 ;; All other types; use equal.
3456 (cons 'equal (cdr form)))
3457 ;; Neither side is a constant expression, do all our evaluation at
3458 ;; runtime (or both are, and equalp will be called from
3459 ;; byte-optimize.el).
3460 (t form)))))
3461
3351 (mapc 3462 (mapc
3352 #'(lambda (y) 3463 #'(lambda (y)
3353 (put (car y) 'side-effect-free t) 3464 (put (car y) 'side-effect-free t)
3354 (put (car y) 'byte-compile 'cl-byte-compile-compiler-macro) 3465 (put (car y) 'byte-compile 'cl-byte-compile-compiler-macro)
3355 (put (car y) 'cl-compiler-macro 3466 (put (car y) 'cl-compiler-macro