Mercurial > hg > xemacs-beta
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 |