aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordickmao2019-11-22 15:53:58 +0100
committerLars Ingebrigtsen2019-11-22 15:53:58 +0100
commit045cfbef09a67c334e4772cb045181cf2203d839 (patch)
tree691550485b176158f4cf3466c3ccb0a2699cdc74
parentf373cec7f51653130bff0844262d356c2bf7c649 (diff)
downloademacs-045cfbef09a67c334e4772cb045181cf2203d839.tar.gz
emacs-045cfbef09a67c334e4772cb045181cf2203d839.zip
Refix conditional step clauses in cl-loop
* lisp/emacs-lisp/cl-macs.el (cl--loop-bindings, cl--loop-symbol-macs, cl-loop): Add cl--loop-conditions, remove cl--loop-guard-cond. (cl--push-clause-loop-body): Apply clause to both cl--loop-conditions and cl--loop-body (cl--parse-loop-clause): Use cl--push-clause-loop-body. * test/lisp/emacs-lisp/cl-macs-tests.el (cl-macs-loop-and-assignment): Use docstring. (cl-macs-loop-for-as-arith): Removed expected failure. (cl-macs-loop-conditional-step-clauses): Add some tests (bug#29799).
-rw-r--r--lisp/emacs-lisp/cl-macs.el96
-rw-r--r--test/lisp/emacs-lisp/cl-macs-tests.el68
2 files changed, 101 insertions, 63 deletions
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 80e218884a0..a5ecf33203c 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -889,7 +889,7 @@ This is compatible with Common Lisp, but note that `defun' and
889;;; The "cl-loop" macro. 889;;; The "cl-loop" macro.
890 890
891(defvar cl--loop-args) (defvar cl--loop-accum-var) (defvar cl--loop-accum-vars) 891(defvar cl--loop-args) (defvar cl--loop-accum-var) (defvar cl--loop-accum-vars)
892(defvar cl--loop-bindings) (defvar cl--loop-body) 892(defvar cl--loop-bindings) (defvar cl--loop-body) (defvar cl--loop-conditions)
893(defvar cl--loop-finally) 893(defvar cl--loop-finally)
894(defvar cl--loop-finish-flag) ;Symbol set to nil to exit the loop? 894(defvar cl--loop-finish-flag) ;Symbol set to nil to exit the loop?
895(defvar cl--loop-first-flag) 895(defvar cl--loop-first-flag)
@@ -897,7 +897,7 @@ This is compatible with Common Lisp, but note that `defun' and
897(defvar cl--loop-name) 897(defvar cl--loop-name)
898(defvar cl--loop-result) (defvar cl--loop-result-explicit) 898(defvar cl--loop-result) (defvar cl--loop-result-explicit)
899(defvar cl--loop-result-var) (defvar cl--loop-steps) 899(defvar cl--loop-result-var) (defvar cl--loop-steps)
900(defvar cl--loop-symbol-macs) (defvar cl--loop-guard-cond) 900(defvar cl--loop-symbol-macs)
901 901
902(defun cl--loop-set-iterator-function (kind iterator) 902(defun cl--loop-set-iterator-function (kind iterator)
903 (if cl--loop-iterator-function 903 (if cl--loop-iterator-function
@@ -966,7 +966,8 @@ For more details, see Info node `(cl)Loop Facility'.
966 (cl--loop-accum-var nil) (cl--loop-accum-vars nil) 966 (cl--loop-accum-var nil) (cl--loop-accum-vars nil)
967 (cl--loop-initially nil) (cl--loop-finally nil) 967 (cl--loop-initially nil) (cl--loop-finally nil)
968 (cl--loop-iterator-function nil) (cl--loop-first-flag nil) 968 (cl--loop-iterator-function nil) (cl--loop-first-flag nil)
969 (cl--loop-symbol-macs nil) (cl--loop-guard-cond nil)) 969 (cl--loop-symbol-macs nil)
970 (cl--loop-conditions nil))
970 ;; Here is more or less how those dynbind vars are used after looping 971 ;; Here is more or less how those dynbind vars are used after looping
971 ;; over cl--parse-loop-clause: 972 ;; over cl--parse-loop-clause:
972 ;; 973 ;;
@@ -1001,24 +1002,7 @@ For more details, see Info node `(cl)Loop Facility'.
1001 (list (or cl--loop-result-explicit 1002 (list (or cl--loop-result-explicit
1002 cl--loop-result)))) 1003 cl--loop-result))))
1003 (ands (cl--loop-build-ands (nreverse cl--loop-body))) 1004 (ands (cl--loop-build-ands (nreverse cl--loop-body)))
1004 (while-body 1005 (while-body (nconc (cadr ands) (nreverse cl--loop-steps)))
1005 (nconc
1006 (cadr ands)
1007 (if (or (not cl--loop-guard-cond) (not cl--loop-first-flag))
1008 (nreverse cl--loop-steps)
1009 ;; Right after update the loop variable ensure that the loop
1010 ;; condition, i.e. (car ands), is still satisfied; otherwise,
1011 ;; set `cl--loop-first-flag' nil and skip the remaining
1012 ;; body forms (#Bug#29799).
1013 ;;
1014 ;; (last cl--loop-steps) updates the loop var
1015 ;; (car (butlast cl--loop-steps)) sets `cl--loop-first-flag' nil
1016 ;; (nreverse (cdr (butlast cl--loop-steps))) are the
1017 ;; remaining body forms.
1018 (append (last cl--loop-steps)
1019 `((and ,(car ands)
1020 ,@(nreverse (cdr (butlast cl--loop-steps)))))
1021 `(,(car (butlast cl--loop-steps)))))))
1022 (body (append 1006 (body (append
1023 (nreverse cl--loop-initially) 1007 (nreverse cl--loop-initially)
1024 (list (if cl--loop-iterator-function 1008 (list (if cl--loop-iterator-function
@@ -1051,6 +1035,12 @@ For more details, see Info node `(cl)Loop Facility'.
1051 (list `(cl-symbol-macrolet ,cl--loop-symbol-macs ,@body)))) 1035 (list `(cl-symbol-macrolet ,cl--loop-symbol-macs ,@body))))
1052 `(cl-block ,cl--loop-name ,@body))))) 1036 `(cl-block ,cl--loop-name ,@body)))))
1053 1037
1038(defmacro cl--push-clause-loop-body (clause)
1039 "Apply CLAUSE to both `cl--loop-conditions' and `cl--loop-body'."
1040 `(progn
1041 (push ,clause cl--loop-conditions)
1042 (push ,clause cl--loop-body)))
1043
1054;; Below is a complete spec for cl-loop, in several parts that correspond 1044;; Below is a complete spec for cl-loop, in several parts that correspond
1055;; to the syntax given in CLtL2. The specs do more than specify where 1045;; to the syntax given in CLtL2. The specs do more than specify where
1056;; the forms are; it also specifies, as much as Edebug allows, all the 1046;; the forms are; it also specifies, as much as Edebug allows, all the
@@ -1201,8 +1191,6 @@ For more details, see Info node `(cl)Loop Facility'.
1201;; (def-edebug-spec loop-d-type-spec 1191;; (def-edebug-spec loop-d-type-spec
1202;; (&or (loop-d-type-spec . [&or nil loop-d-type-spec]) cl-type-spec)) 1192;; (&or (loop-d-type-spec . [&or nil loop-d-type-spec]) cl-type-spec))
1203 1193
1204
1205
1206(defun cl--parse-loop-clause () ; uses loop-* 1194(defun cl--parse-loop-clause () ; uses loop-*
1207 (let ((word (pop cl--loop-args)) 1195 (let ((word (pop cl--loop-args))
1208 (hash-types '(hash-key hash-keys hash-value hash-values)) 1196 (hash-types '(hash-key hash-keys hash-value hash-values))
@@ -1281,11 +1269,11 @@ For more details, see Info node `(cl)Loop Facility'.
1281 (if end-var (push (list end-var end) loop-for-bindings)) 1269 (if end-var (push (list end-var end) loop-for-bindings))
1282 (if step-var (push (list step-var step) 1270 (if step-var (push (list step-var step)
1283 loop-for-bindings)) 1271 loop-for-bindings))
1284 (if end 1272 (when end
1285 (push (list 1273 (cl--push-clause-loop-body
1286 (if down (if excl '> '>=) (if excl '< '<=)) 1274 (list
1287 var (or end-var end)) 1275 (if down (if excl '> '>=) (if excl '< '<=))
1288 cl--loop-body)) 1276 var (or end-var end))))
1289 (push (list var (list (if down '- '+) var 1277 (push (list var (list (if down '- '+) var
1290 (or step-var step 1))) 1278 (or step-var step 1)))
1291 loop-for-steps))) 1279 loop-for-steps)))
@@ -1295,7 +1283,7 @@ For more details, see Info node `(cl)Loop Facility'.
1295 (temp (if (and on (symbolp var)) 1283 (temp (if (and on (symbolp var))
1296 var (make-symbol "--cl-var--")))) 1284 var (make-symbol "--cl-var--"))))
1297 (push (list temp (pop cl--loop-args)) loop-for-bindings) 1285 (push (list temp (pop cl--loop-args)) loop-for-bindings)
1298 (push `(consp ,temp) cl--loop-body) 1286 (cl--push-clause-loop-body `(consp ,temp))
1299 (if (eq word 'in-ref) 1287 (if (eq word 'in-ref)
1300 (push (list var `(car ,temp)) cl--loop-symbol-macs) 1288 (push (list var `(car ,temp)) cl--loop-symbol-macs)
1301 (or (eq temp var) 1289 (or (eq temp var)
@@ -1318,24 +1306,19 @@ For more details, see Info node `(cl)Loop Facility'.
1318 ((eq word '=) 1306 ((eq word '=)
1319 (let* ((start (pop cl--loop-args)) 1307 (let* ((start (pop cl--loop-args))
1320 (then (if (eq (car cl--loop-args) 'then) 1308 (then (if (eq (car cl--loop-args) 'then)
1321 (cl--pop2 cl--loop-args) start))) 1309 (cl--pop2 cl--loop-args) start))
1310 (first-assign (or cl--loop-first-flag
1311 (setq cl--loop-first-flag
1312 (make-symbol "--cl-var--")))))
1322 (push (list var nil) loop-for-bindings) 1313 (push (list var nil) loop-for-bindings)
1323 (if (or ands (eq (car cl--loop-args) 'and)) 1314 (if (or ands (eq (car cl--loop-args) 'and))
1324 (progn 1315 (progn
1325 (push `(,var 1316 (push `(,var (if ,first-assign ,start ,var)) loop-for-sets)
1326 (if ,(or cl--loop-first-flag 1317 (push `(,var (if ,(car (cl--loop-build-ands
1327 (setq cl--loop-first-flag 1318 (nreverse cl--loop-conditions)))
1328 (make-symbol "--cl-var--"))) 1319 ,then ,var))
1329 ,start ,var)) 1320 loop-for-steps))
1330 loop-for-sets) 1321 (push `(,var (if ,first-assign ,start ,then)) loop-for-sets))))
1331 (push (list var then) loop-for-steps))
1332 (push (list var
1333 (if (eq start then) start
1334 `(if ,(or cl--loop-first-flag
1335 (setq cl--loop-first-flag
1336 (make-symbol "--cl-var--")))
1337 ,start ,then)))
1338 loop-for-sets))))
1339 1322
1340 ((memq word '(across across-ref)) 1323 ((memq word '(across across-ref))
1341 (let ((temp-vec (make-symbol "--cl-vec--")) 1324 (let ((temp-vec (make-symbol "--cl-vec--"))
@@ -1344,9 +1327,8 @@ For more details, see Info node `(cl)Loop Facility'.
1344 (push (list temp-vec (pop cl--loop-args)) loop-for-bindings) 1327 (push (list temp-vec (pop cl--loop-args)) loop-for-bindings)
1345 (push (list temp-len `(length ,temp-vec)) loop-for-bindings) 1328 (push (list temp-len `(length ,temp-vec)) loop-for-bindings)
1346 (push (list temp-idx -1) loop-for-bindings) 1329 (push (list temp-idx -1) loop-for-bindings)
1347 (push `(< (setq ,temp-idx (1+ ,temp-idx)) 1330 (cl--push-clause-loop-body
1348 ,temp-len) 1331 `(< (setq ,temp-idx (1+ ,temp-idx)) ,temp-len))
1349 cl--loop-body)
1350 (if (eq word 'across-ref) 1332 (if (eq word 'across-ref)
1351 (push (list var `(aref ,temp-vec ,temp-idx)) 1333 (push (list var `(aref ,temp-vec ,temp-idx))
1352 cl--loop-symbol-macs) 1334 cl--loop-symbol-macs)
@@ -1376,15 +1358,14 @@ For more details, see Info node `(cl)Loop Facility'.
1376 loop-for-bindings) 1358 loop-for-bindings)
1377 (push (list var `(elt ,temp-seq ,temp-idx)) 1359 (push (list var `(elt ,temp-seq ,temp-idx))
1378 cl--loop-symbol-macs) 1360 cl--loop-symbol-macs)
1379 (push `(< ,temp-idx ,temp-len) cl--loop-body)) 1361 (cl--push-clause-loop-body `(< ,temp-idx ,temp-len)))
1380 ;; Evaluate seq length just if needed, that is, when seq is not a cons. 1362 ;; Evaluate seq length just if needed, that is, when seq is not a cons.
1381 (push (list temp-len (or (consp seq) `(length ,temp-seq))) 1363 (push (list temp-len (or (consp seq) `(length ,temp-seq)))
1382 loop-for-bindings) 1364 loop-for-bindings)
1383 (push (list var nil) loop-for-bindings) 1365 (push (list var nil) loop-for-bindings)
1384 (push `(and ,temp-seq 1366 (cl--push-clause-loop-body `(and ,temp-seq
1385 (or (consp ,temp-seq) 1367 (or (consp ,temp-seq)
1386 (< ,temp-idx ,temp-len))) 1368 (< ,temp-idx ,temp-len))))
1387 cl--loop-body)
1388 (push (list var `(if (consp ,temp-seq) 1369 (push (list var `(if (consp ,temp-seq)
1389 (pop ,temp-seq) 1370 (pop ,temp-seq)
1390 (aref ,temp-seq ,temp-idx))) 1371 (aref ,temp-seq ,temp-idx)))
@@ -1480,9 +1461,8 @@ For more details, see Info node `(cl)Loop Facility'.
1480 (push (list var '(selected-frame)) 1461 (push (list var '(selected-frame))
1481 loop-for-bindings) 1462 loop-for-bindings)
1482 (push (list temp nil) loop-for-bindings) 1463 (push (list temp nil) loop-for-bindings)
1483 (push `(prog1 (not (eq ,var ,temp)) 1464 (cl--push-clause-loop-body `(prog1 (not (eq ,var ,temp))
1484 (or ,temp (setq ,temp ,var))) 1465 (or ,temp (setq ,temp ,var))))
1485 cl--loop-body)
1486 (push (list var `(next-frame ,var)) 1466 (push (list var `(next-frame ,var))
1487 loop-for-steps))) 1467 loop-for-steps)))
1488 1468
@@ -1503,9 +1483,8 @@ For more details, see Info node `(cl)Loop Facility'.
1503 (push (list minip `(minibufferp (window-buffer ,var))) 1483 (push (list minip `(minibufferp (window-buffer ,var)))
1504 loop-for-bindings) 1484 loop-for-bindings)
1505 (push (list temp nil) loop-for-bindings) 1485 (push (list temp nil) loop-for-bindings)
1506 (push `(prog1 (not (eq ,var ,temp)) 1486 (cl--push-clause-loop-body `(prog1 (not (eq ,var ,temp))
1507 (or ,temp (setq ,temp ,var))) 1487 (or ,temp (setq ,temp ,var))))
1508 cl--loop-body)
1509 (push (list var `(next-window ,var ,minip)) 1488 (push (list var `(next-window ,var ,minip))
1510 loop-for-steps))) 1489 loop-for-steps)))
1511 1490
@@ -1529,7 +1508,6 @@ For more details, see Info node `(cl)Loop Facility'.
1529 t) 1508 t)
1530 cl--loop-body)) 1509 cl--loop-body))
1531 (when loop-for-steps 1510 (when loop-for-steps
1532 (setq cl--loop-guard-cond t)
1533 (push (cons (if ands 'cl-psetq 'setq) 1511 (push (cons (if ands 'cl-psetq 'setq)
1534 (apply 'append (nreverse loop-for-steps))) 1512 (apply 'append (nreverse loop-for-steps)))
1535 cl--loop-steps)))) 1513 cl--loop-steps))))
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el b/test/lisp/emacs-lisp/cl-macs-tests.el
index 09ce660a2fd..85230447148 100644
--- a/test/lisp/emacs-lisp/cl-macs-tests.el
+++ b/test/lisp/emacs-lisp/cl-macs-tests.el
@@ -30,7 +30,7 @@
30 30
31;;; ANSI 6.1.1.7 Destructuring 31;;; ANSI 6.1.1.7 Destructuring
32(ert-deftest cl-macs-loop-and-assignment () 32(ert-deftest cl-macs-loop-and-assignment ()
33 ;; Bug#6583 33 "Bug#6583"
34 :expected-result :failed 34 :expected-result :failed
35 (should (equal (cl-loop for numlist in '((1 2 4.0) (5 6 8.3) (8 9 10.4)) 35 (should (equal (cl-loop for numlist in '((1 2 4.0) (5 6 8.3) (8 9 10.4))
36 for a = (cl-first numlist) 36 for a = (cl-first numlist)
@@ -61,7 +61,6 @@
61;;; 6.1.2.1.1 The for-as-arithmetic subclause 61;;; 6.1.2.1.1 The for-as-arithmetic subclause
62(ert-deftest cl-macs-loop-for-as-arith () 62(ert-deftest cl-macs-loop-for-as-arith ()
63 "Test various for-as-arithmetic subclauses." 63 "Test various for-as-arithmetic subclauses."
64 :expected-result :failed
65 (should (equal (cl-loop for i to 10 by 3 collect i) 64 (should (equal (cl-loop for i to 10 by 3 collect i)
66 '(0 3 6 9))) 65 '(0 3 6 9)))
67 (should (equal (cl-loop for i upto 3 collect i) 66 (should (equal (cl-loop for i upto 3 collect i)
@@ -74,9 +73,9 @@
74 '(10 8 6))) 73 '(10 8 6)))
75 (should (equal (cl-loop for i from 10 downto 1 by 3 collect i) 74 (should (equal (cl-loop for i from 10 downto 1 by 3 collect i)
76 '(10 7 4 1))) 75 '(10 7 4 1)))
77 (should (equal (cl-loop for i above 0 by 2 downfrom 10 collect i) 76 (should (equal (cl-loop for i downfrom 10 above 0 by 2 collect i)
78 '(10 8 6 4 2))) 77 '(10 8 6 4 2)))
79 (should (equal (cl-loop for i downto 10 from 15 collect i) 78 (should (equal (cl-loop for i from 15 downto 10 collect i)
80 '(15 14 13 12 11 10)))) 79 '(15 14 13 12 11 10))))
81 80
82(ert-deftest cl-macs-loop-for-as-arith-order-side-effects () 81(ert-deftest cl-macs-loop-for-as-arith-order-side-effects ()
@@ -530,4 +529,65 @@ collection clause."
530 l) 529 l)
531 '(1)))) 530 '(1))))
532 531
532(ert-deftest cl-macs-loop-conditional-step-clauses ()
533 "These tests failed under the initial fixes in #bug#29799."
534 (should (cl-loop for i from 1 upto 100 and j = 1 then (1+ j)
535 if (not (= i j))
536 return nil
537 end
538 until (> j 10)
539 finally return t))
540
541 (should (equal (let* ((size 7)
542 (arr (make-vector size 0)))
543 (cl-loop for k below size
544 for x = (* 2 k) and y = (1+ (elt arr k))
545 collect (list k x y)))
546 '((0 0 1) (1 2 1) (2 4 1) (3 6 1) (4 8 1) (5 10 1) (6 12 1))))
547
548 (should (equal (cl-loop for x below 3
549 for y below 2 and z = 1
550 collect x)
551 '(0 1)))
552
553 (should (equal (cl-loop for x below 3
554 and y below 2
555 collect x)
556 '(0 1)))
557
558 ;; this is actually disallowed in clisp, but is semantically consistent
559 (should (equal (cl-loop with result
560 for x below 3
561 for y = (progn (push x result) x) and z = 1
562 append (list x y) into result1
563 finally return (append result result1))
564 '(2 1 0 0 0 1 1 2 2)))
565
566 (should (equal (cl-loop with result
567 for x below 3
568 for _y = (progn (push x result))
569 finally return result)
570 '(2 1 0)))
571
572 ;; this nonintuitive result is replicated by clisp
573 (should (equal (cl-loop with result
574 for x below 3
575 and y = (progn (push x result))
576 finally return result)
577 '(2 1 0 0)))
578
579 ;; this nonintuitive result is replicated by clisp
580 (should (equal (cl-loop with result
581 for x below 3
582 and y = (progn (push x result)) then (progn (push (1+ x) result))
583 finally return result)
584 '(3 2 1 0)))
585
586 (should (cl-loop with result
587 for x below 3
588 for y = (progn (push x result) x) then (progn (push (1+ x) result) (1+ x))
589 and z = 1
590 collect y into result1
591 finally return (equal (nreverse result) result1))))
592
533;;; cl-macs-tests.el ends here 593;;; cl-macs-tests.el ends here