diff options
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 96 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-macs-tests.el | 68 |
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 |