diff options
| -rw-r--r-- | doc/lispref/lists.texi | 16 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 2 | ||||
| -rw-r--r-- | lisp/emacs-lisp/ert.el | 28 | ||||
| -rw-r--r-- | lisp/format.el | 12 | ||||
| -rw-r--r-- | lisp/org/ob-core.el | 5 | ||||
| -rw-r--r-- | lisp/subr.el | 6 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/ert-tests.el | 42 | ||||
| -rw-r--r-- | test/lisp/subr-tests.el | 18 |
10 files changed, 59 insertions, 78 deletions
diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20c..57cefeac962 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi | |||
| @@ -153,6 +153,22 @@ considered a list and @code{not} when it is considered a truth value | |||
| 153 | @end example | 153 | @end example |
| 154 | @end defun | 154 | @end defun |
| 155 | 155 | ||
| 156 | @defun proper-list-p object | ||
| 157 | This function returns the length of @var{object} if it is a proper | ||
| 158 | list, @code{nil} otherwise (@pxref{Cons Cells}). In addition to | ||
| 159 | satisfying @code{listp}, a proper list is neither circular nor dotted. | ||
| 160 | |||
| 161 | @example | ||
| 162 | @group | ||
| 163 | (proper-list-p '(a b c)) | ||
| 164 | @result{} 3 | ||
| 165 | @end group | ||
| 166 | @group | ||
| 167 | (proper-list-p '(a b . c)) | ||
| 168 | @result{} nil | ||
| 169 | @end group | ||
| 170 | @end example | ||
| 171 | @end defun | ||
| 156 | 172 | ||
| 157 | @node List Elements | 173 | @node List Elements |
| 158 | @section Accessing Elements of Lists | 174 | @section Accessing Elements of Lists |
| @@ -703,6 +703,11 @@ manual for more details. | |||
| 703 | 703 | ||
| 704 | * Lisp Changes in Emacs 27.1 | 704 | * Lisp Changes in Emacs 27.1 |
| 705 | 705 | ||
| 706 | +++ | ||
| 707 | ** New function 'proper-list-p'. | ||
| 708 | Given a proper list as argument, this predicate returns its length; | ||
| 709 | otherwise, it returns nil. | ||
| 710 | |||
| 706 | ** define-minor-mode automatically documents the meaning of ARG | 711 | ** define-minor-mode automatically documents the meaning of ARG |
| 707 | 712 | ||
| 708 | +++ | 713 | +++ |
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6a..5c0b5e340bb 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el | |||
| @@ -982,8 +982,7 @@ | |||
| 982 | ;; (if <test> <then> nil) ==> (if <test> <then>) | 982 | ;; (if <test> <then> nil) ==> (if <test> <then>) |
| 983 | (let ((clause (nth 1 form))) | 983 | (let ((clause (nth 1 form))) |
| 984 | (cond ((and (eq (car-safe clause) 'progn) | 984 | (cond ((and (eq (car-safe clause) 'progn) |
| 985 | ;; `clause' is a proper list. | 985 | (proper-list-p clause)) |
| 986 | (null (cdr (last clause)))) | ||
| 987 | (if (null (cddr clause)) | 986 | (if (null (cddr clause)) |
| 988 | ;; A trivial `progn'. | 987 | ;; A trivial `progn'. |
| 989 | (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) | 988 | (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index b50961adac9..011965acb54 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -498,7 +498,7 @@ its argument list allows full Common Lisp conventions." | |||
| 498 | ;; `&aux' args aren't arguments, so let's just drop them from the | 498 | ;; `&aux' args aren't arguments, so let's just drop them from the |
| 499 | ;; usage info. | 499 | ;; usage info. |
| 500 | (setq arglist (cl-subseq arglist 0 aux)))) | 500 | (setq arglist (cl-subseq arglist 0 aux)))) |
| 501 | (if (cdr-safe (last arglist)) ;Not a proper list. | 501 | (if (not (proper-list-p arglist)) |
| 502 | (let* ((last (last arglist)) | 502 | (let* ((last (last arglist)) |
| 503 | (tail (cdr last))) | 503 | (tail (cdr last))) |
| 504 | (unwind-protect | 504 | (unwind-protect |
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb3..cad21044f15 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el | |||
| @@ -472,18 +472,6 @@ Errors during evaluation are caught and handled like nil." | |||
| 472 | ;; buffer. Perhaps explanations should be reported through `ert-info' | 472 | ;; buffer. Perhaps explanations should be reported through `ert-info' |
| 473 | ;; rather than as part of the condition. | 473 | ;; rather than as part of the condition. |
| 474 | 474 | ||
| 475 | (defun ert--proper-list-p (x) | ||
| 476 | "Return non-nil if X is a proper list, nil otherwise." | ||
| 477 | (cl-loop | ||
| 478 | for firstp = t then nil | ||
| 479 | for fast = x then (cddr fast) | ||
| 480 | for slow = x then (cdr slow) do | ||
| 481 | (when (null fast) (cl-return t)) | ||
| 482 | (when (not (consp fast)) (cl-return nil)) | ||
| 483 | (when (null (cdr fast)) (cl-return t)) | ||
| 484 | (when (not (consp (cdr fast))) (cl-return nil)) | ||
| 485 | (when (and (not firstp) (eq fast slow)) (cl-return nil)))) | ||
| 486 | |||
| 487 | (defun ert--explain-format-atom (x) | 475 | (defun ert--explain-format-atom (x) |
| 488 | "Format the atom X for `ert--explain-equal'." | 476 | "Format the atom X for `ert--explain-equal'." |
| 489 | (pcase x | 477 | (pcase x |
| @@ -494,17 +482,17 @@ Errors during evaluation are caught and handled like nil." | |||
| 494 | (defun ert--explain-equal-rec (a b) | 482 | (defun ert--explain-equal-rec (a b) |
| 495 | "Return a programmer-readable explanation of why A and B are not `equal'. | 483 | "Return a programmer-readable explanation of why A and B are not `equal'. |
| 496 | Returns nil if they are." | 484 | Returns nil if they are." |
| 497 | (if (not (equal (type-of a) (type-of b))) | 485 | (if (not (eq (type-of a) (type-of b))) |
| 498 | `(different-types ,a ,b) | 486 | `(different-types ,a ,b) |
| 499 | (pcase-exhaustive a | 487 | (pcase-exhaustive a |
| 500 | ((pred consp) | 488 | ((pred consp) |
| 501 | (let ((a-proper-p (ert--proper-list-p a)) | 489 | (let ((a-length (proper-list-p a)) |
| 502 | (b-proper-p (ert--proper-list-p b))) | 490 | (b-length (proper-list-p b))) |
| 503 | (if (not (eql (not a-proper-p) (not b-proper-p))) | 491 | (if (not (eq (not a-length) (not b-length))) |
| 504 | `(one-list-proper-one-improper ,a ,b) | 492 | `(one-list-proper-one-improper ,a ,b) |
| 505 | (if a-proper-p | 493 | (if a-length |
| 506 | (if (not (equal (length a) (length b))) | 494 | (if (/= a-length b-length) |
| 507 | `(proper-lists-of-different-length ,(length a) ,(length b) | 495 | `(proper-lists-of-different-length ,a-length ,b-length |
| 508 | ,a ,b | 496 | ,a ,b |
| 509 | first-mismatch-at | 497 | first-mismatch-at |
| 510 | ,(cl-mismatch a b :test 'equal)) | 498 | ,(cl-mismatch a b :test 'equal)) |
| @@ -523,7 +511,7 @@ Returns nil if they are." | |||
| 523 | (cl-assert (equal a b) t) | 511 | (cl-assert (equal a b) t) |
| 524 | nil)))))))) | 512 | nil)))))))) |
| 525 | ((pred arrayp) | 513 | ((pred arrayp) |
| 526 | (if (not (equal (length a) (length b))) | 514 | (if (/= (length a) (length b)) |
| 527 | `(arrays-of-different-length ,(length a) ,(length b) | 515 | `(arrays-of-different-length ,(length a) ,(length b) |
| 528 | ,a ,b | 516 | ,a ,b |
| 529 | ,@(unless (char-table-p a) | 517 | ,@(unless (char-table-p a) |
diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb71..1222abbf658 100644 --- a/lisp/format.el +++ b/lisp/format.el | |||
| @@ -539,14 +539,6 @@ Compare using `equal'." | |||
| 539 | (setq tail next))) | 539 | (setq tail next))) |
| 540 | (cons acopy bcopy))) | 540 | (cons acopy bcopy))) |
| 541 | 541 | ||
| 542 | (defun format-proper-list-p (list) | ||
| 543 | "Return t if LIST is a proper list. | ||
| 544 | A proper list is a list ending with a nil cdr, not with an atom " | ||
| 545 | (when (listp list) | ||
| 546 | (while (consp list) | ||
| 547 | (setq list (cdr list))) | ||
| 548 | (null list))) | ||
| 549 | |||
| 550 | (defun format-reorder (items order) | 542 | (defun format-reorder (items order) |
| 551 | "Arrange ITEMS to follow partial ORDER. | 543 | "Arrange ITEMS to follow partial ORDER. |
| 552 | Elements of ITEMS equal to elements of ORDER will be rearranged | 544 | Elements of ITEMS equal to elements of ORDER will be rearranged |
| @@ -1005,8 +997,8 @@ either strings, or lists of the form (PARAMETER VALUE)." | |||
| 1005 | ;; If either old or new is a list, have to treat both that way. | 997 | ;; If either old or new is a list, have to treat both that way. |
| 1006 | (if (and (or (listp old) (listp new)) | 998 | (if (and (or (listp old) (listp new)) |
| 1007 | (not (get prop 'format-list-atomic-p))) | 999 | (not (get prop 'format-list-atomic-p))) |
| 1008 | (if (or (not (format-proper-list-p old)) | 1000 | (if (not (and (proper-list-p old) |
| 1009 | (not (format-proper-list-p new))) | 1001 | (proper-list-p new))) |
| 1010 | (format-annotate-atomic-property-change prop-alist old new) | 1002 | (format-annotate-atomic-property-change prop-alist old new) |
| 1011 | (let* ((old (if (listp old) old (list old))) | 1003 | (let* ((old (if (listp old) old (list old))) |
| 1012 | (new (if (listp new) new (list new))) | 1004 | (new (if (listp new) new (list new))) |
diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd0..a5449fe35e9 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el | |||
| @@ -2310,10 +2310,9 @@ INFO may provide the values of these header arguments (in the | |||
| 2310 | (lambda (r) | 2310 | (lambda (r) |
| 2311 | ;; Non-nil when result R can be turned into | 2311 | ;; Non-nil when result R can be turned into |
| 2312 | ;; a table. | 2312 | ;; a table. |
| 2313 | (and (listp r) | 2313 | (and (proper-list-p r) |
| 2314 | (null (cdr (last r))) | ||
| 2315 | (cl-every | 2314 | (cl-every |
| 2316 | (lambda (e) (or (atom e) (null (cdr (last e))))) | 2315 | (lambda (e) (or (atom e) (proper-list-p e))) |
| 2317 | result))))) | 2316 | result))))) |
| 2318 | ;; insert results based on type | 2317 | ;; insert results based on type |
| 2319 | (cond | 2318 | (cond |
diff --git a/lisp/subr.el b/lisp/subr.el index ca184d8fc81..c1d90e3fb18 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -555,6 +555,12 @@ If N is omitted or nil, remove the last element." | |||
| 555 | (declare (compiler-macro (lambda (_) `(= 0 ,number)))) | 555 | (declare (compiler-macro (lambda (_) `(= 0 ,number)))) |
| 556 | (= 0 number)) | 556 | (= 0 number)) |
| 557 | 557 | ||
| 558 | (defun proper-list-p (object) | ||
| 559 | "Return OBJECT's length if it is a proper list, nil otherwise. | ||
| 560 | A proper list is neither circular nor dotted (i.e., its last cdr | ||
| 561 | is nil)." | ||
| 562 | (and (listp object) (ignore-errors (length object)))) | ||
| 563 | |||
| 558 | (defun delete-dups (list) | 564 | (defun delete-dups (list) |
| 559 | "Destructively remove `equal' duplicates from LIST. | 565 | "Destructively remove `equal' duplicates from LIST. |
| 560 | Store the result in LIST and return it. LIST must be a proper list. | 566 | Store the result in LIST and return it. LIST must be a proper list. |
diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b4342748..cb957bd9fd6 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el | |||
| @@ -496,48 +496,6 @@ This macro is used to test if macroexpansion in `should' works." | |||
| 496 | 496 | ||
| 497 | 497 | ||
| 498 | ;;; Tests for utility functions. | 498 | ;;; Tests for utility functions. |
| 499 | (ert-deftest ert-test-proper-list-p () | ||
| 500 | (should (ert--proper-list-p '())) | ||
| 501 | (should (ert--proper-list-p '(1))) | ||
| 502 | (should (ert--proper-list-p '(1 2))) | ||
| 503 | (should (ert--proper-list-p '(1 2 3))) | ||
| 504 | (should (ert--proper-list-p '(1 2 3 4))) | ||
| 505 | (should (not (ert--proper-list-p 'a))) | ||
| 506 | (should (not (ert--proper-list-p '(1 . a)))) | ||
| 507 | (should (not (ert--proper-list-p '(1 2 . a)))) | ||
| 508 | (should (not (ert--proper-list-p '(1 2 3 . a)))) | ||
| 509 | (should (not (ert--proper-list-p '(1 2 3 4 . a)))) | ||
| 510 | (let ((a (list 1))) | ||
| 511 | (setf (cdr (last a)) a) | ||
| 512 | (should (not (ert--proper-list-p a)))) | ||
| 513 | (let ((a (list 1 2))) | ||
| 514 | (setf (cdr (last a)) a) | ||
| 515 | (should (not (ert--proper-list-p a)))) | ||
| 516 | (let ((a (list 1 2 3))) | ||
| 517 | (setf (cdr (last a)) a) | ||
| 518 | (should (not (ert--proper-list-p a)))) | ||
| 519 | (let ((a (list 1 2 3 4))) | ||
| 520 | (setf (cdr (last a)) a) | ||
| 521 | (should (not (ert--proper-list-p a)))) | ||
| 522 | (let ((a (list 1 2))) | ||
| 523 | (setf (cdr (last a)) (cdr a)) | ||
| 524 | (should (not (ert--proper-list-p a)))) | ||
| 525 | (let ((a (list 1 2 3))) | ||
| 526 | (setf (cdr (last a)) (cdr a)) | ||
| 527 | (should (not (ert--proper-list-p a)))) | ||
| 528 | (let ((a (list 1 2 3 4))) | ||
| 529 | (setf (cdr (last a)) (cdr a)) | ||
| 530 | (should (not (ert--proper-list-p a)))) | ||
| 531 | (let ((a (list 1 2 3))) | ||
| 532 | (setf (cdr (last a)) (cddr a)) | ||
| 533 | (should (not (ert--proper-list-p a)))) | ||
| 534 | (let ((a (list 1 2 3 4))) | ||
| 535 | (setf (cdr (last a)) (cddr a)) | ||
| 536 | (should (not (ert--proper-list-p a)))) | ||
| 537 | (let ((a (list 1 2 3 4))) | ||
| 538 | (setf (cdr (last a)) (cl-cdddr a)) | ||
| 539 | (should (not (ert--proper-list-p a))))) | ||
| 540 | |||
| 541 | (ert-deftest ert-test-parse-keys-and-body () | 499 | (ert-deftest ert-test-parse-keys-and-body () |
| 542 | (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) | 500 | (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) |
| 543 | (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) | 501 | (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) |
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb97..86938d5dbe0 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el | |||
| @@ -306,6 +306,24 @@ cf. Bug#25477." | |||
| 306 | (should (eq (string-to-char (symbol-name (gensym))) ?g)) | 306 | (should (eq (string-to-char (symbol-name (gensym))) ?g)) |
| 307 | (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) | 307 | (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) |
| 308 | 308 | ||
| 309 | (ert-deftest subr-tests--proper-list-p () | ||
| 310 | "Test `proper-list-p' behavior." | ||
| 311 | (dotimes (length 4) | ||
| 312 | ;; Proper and dotted lists. | ||
| 313 | (let ((list (make-list length 0))) | ||
| 314 | (should (= (proper-list-p list) length)) | ||
| 315 | (should (not (proper-list-p (nconc list 0))))) | ||
| 316 | ;; Circular lists. | ||
| 317 | (dotimes (n (1+ length)) | ||
| 318 | (let ((circle (make-list (1+ length) 0))) | ||
| 319 | (should (not (proper-list-p (nconc circle (nthcdr n circle)))))))) | ||
| 320 | ;; Atoms. | ||
| 321 | (should (not (proper-list-p 0))) | ||
| 322 | (should (not (proper-list-p ""))) | ||
| 323 | (should (not (proper-list-p []))) | ||
| 324 | (should (not (proper-list-p (make-bool-vector 0 nil)))) | ||
| 325 | (should (not (proper-list-p (make-symbol "a"))))) | ||
| 326 | |||
| 309 | (ert-deftest subr-tests--assq-delete-all () | 327 | (ert-deftest subr-tests--assq-delete-all () |
| 310 | "Test `assq-delete-all' behavior." | 328 | "Test `assq-delete-all' behavior." |
| 311 | (cl-flet ((new-list-fn | 329 | (cl-flet ((new-list-fn |