| 1 | ;;; loop.lisp |
|---|
| 2 | ;;; |
|---|
| 3 | ;;; Copyright (C) 2004-2007 Peter Graves |
|---|
| 4 | ;;; $Id: loop.lisp 11391 2008-11-15 22:38:34Z vvoutilainen $ |
|---|
| 5 | ;;; |
|---|
| 6 | ;;; This program is free software; you can redistribute it and/or |
|---|
| 7 | ;;; modify it under the terms of the GNU General Public License |
|---|
| 8 | ;;; as published by the Free Software Foundation; either version 2 |
|---|
| 9 | ;;; of the License, or (at your option) any later version. |
|---|
| 10 | ;;; |
|---|
| 11 | ;;; This program is distributed in the hope that it will be useful, |
|---|
| 12 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | ;;; GNU General Public License for more details. |
|---|
| 15 | ;;; |
|---|
| 16 | ;;; You should have received a copy of the GNU General Public License |
|---|
| 17 | ;;; along with this program; if not, write to the Free Software |
|---|
| 18 | ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 19 | ;;; |
|---|
| 20 | ;;; As a special exception, the copyright holders of this library give you |
|---|
| 21 | ;;; permission to link this library with independent modules to produce an |
|---|
| 22 | ;;; executable, regardless of the license terms of these independent |
|---|
| 23 | ;;; modules, and to copy and distribute the resulting executable under |
|---|
| 24 | ;;; terms of your choice, provided that you also meet, for each linked |
|---|
| 25 | ;;; independent module, the terms and conditions of the license of that |
|---|
| 26 | ;;; module. An independent module is a module which is not derived from |
|---|
| 27 | ;;; or based on this library. If you modify this library, you may extend |
|---|
| 28 | ;;; this exception to your version of the library, but you are not |
|---|
| 29 | ;;; obligated to do so. If you do not wish to do so, delete this |
|---|
| 30 | ;;; exception statement from your version. |
|---|
| 31 | |
|---|
| 32 | ;;; Adapted from SBCL. |
|---|
| 33 | |
|---|
| 34 | ;;;; the LOOP iteration macro |
|---|
| 35 | |
|---|
| 36 | ;;;; This software is part of the SBCL system. See the README file for |
|---|
| 37 | ;;;; more information. |
|---|
| 38 | |
|---|
| 39 | ;;;; This code was modified by William Harold Newman beginning |
|---|
| 40 | ;;;; 19981106, originally to conform to the new SBCL bootstrap package |
|---|
| 41 | ;;;; system and then subsequently to address other cross-compiling |
|---|
| 42 | ;;;; bootstrap issues, SBCLification (e.g. DECLARE used to check |
|---|
| 43 | ;;;; argument types), and other maintenance. Whether or not it then |
|---|
| 44 | ;;;; supported all the environments implied by the reader conditionals |
|---|
| 45 | ;;;; in the source code (e.g. #!+CLOE-RUNTIME) before that |
|---|
| 46 | ;;;; modification, it sure doesn't now. It might perhaps, by blind |
|---|
| 47 | ;;;; luck, be appropriate for some other CMU-CL-derived system, but |
|---|
| 48 | ;;;; really it only attempts to be appropriate for SBCL. |
|---|
| 49 | |
|---|
| 50 | ;;;; This software is derived from software originally released by the |
|---|
| 51 | ;;;; Massachusetts Institute of Technology and Symbolics, Inc. Copyright and |
|---|
| 52 | ;;;; release statements follow. Later modifications to the software are in |
|---|
| 53 | ;;;; the public domain and are provided with absolutely no warranty. See the |
|---|
| 54 | ;;;; COPYING and CREDITS files for more information. |
|---|
| 55 | |
|---|
| 56 | ;;;; Portions of LOOP are Copyright (c) 1986 by the Massachusetts Institute |
|---|
| 57 | ;;;; of Technology. All Rights Reserved. |
|---|
| 58 | ;;;; |
|---|
| 59 | ;;;; Permission to use, copy, modify and distribute this software and its |
|---|
| 60 | ;;;; documentation for any purpose and without fee is hereby granted, |
|---|
| 61 | ;;;; provided that the M.I.T. copyright notice appear in all copies and that |
|---|
| 62 | ;;;; both that copyright notice and this permission notice appear in |
|---|
| 63 | ;;;; supporting documentation. The names "M.I.T." and "Massachusetts |
|---|
| 64 | ;;;; Institute of Technology" may not be used in advertising or publicity |
|---|
| 65 | ;;;; pertaining to distribution of the software without specific, written |
|---|
| 66 | ;;;; prior permission. Notice must be given in supporting documentation that |
|---|
| 67 | ;;;; copying distribution is by permission of M.I.T. M.I.T. makes no |
|---|
| 68 | ;;;; representations about the suitability of this software for any purpose. |
|---|
| 69 | ;;;; It is provided "as is" without express or implied warranty. |
|---|
| 70 | ;;;; |
|---|
| 71 | ;;;; Massachusetts Institute of Technology |
|---|
| 72 | ;;;; 77 Massachusetts Avenue |
|---|
| 73 | ;;;; Cambridge, Massachusetts 02139 |
|---|
| 74 | ;;;; United States of America |
|---|
| 75 | ;;;; +1-617-253-1000 |
|---|
| 76 | |
|---|
| 77 | ;;;; Portions of LOOP are Copyright (c) 1989, 1990, 1991, 1992 by Symbolics, |
|---|
| 78 | ;;;; Inc. All Rights Reserved. |
|---|
| 79 | ;;;; |
|---|
| 80 | ;;;; Permission to use, copy, modify and distribute this software and its |
|---|
| 81 | ;;;; documentation for any purpose and without fee is hereby granted, |
|---|
| 82 | ;;;; provided that the Symbolics copyright notice appear in all copies and |
|---|
| 83 | ;;;; that both that copyright notice and this permission notice appear in |
|---|
| 84 | ;;;; supporting documentation. The name "Symbolics" may not be used in |
|---|
| 85 | ;;;; advertising or publicity pertaining to distribution of the software |
|---|
| 86 | ;;;; without specific, written prior permission. Notice must be given in |
|---|
| 87 | ;;;; supporting documentation that copying distribution is by permission of |
|---|
| 88 | ;;;; Symbolics. Symbolics makes no representations about the suitability of |
|---|
| 89 | ;;;; this software for any purpose. It is provided "as is" without express |
|---|
| 90 | ;;;; or implied warranty. |
|---|
| 91 | ;;;; |
|---|
| 92 | ;;;; Symbolics, CLOE Runtime, and Minima are trademarks, and CLOE, Genera, |
|---|
| 93 | ;;;; and Zetalisp are registered trademarks of Symbolics, Inc. |
|---|
| 94 | ;;;; |
|---|
| 95 | ;;;; Symbolics, Inc. |
|---|
| 96 | ;;;; 8 New England Executive Park, East |
|---|
| 97 | ;;;; Burlington, Massachusetts 01803 |
|---|
| 98 | ;;;; United States of America |
|---|
| 99 | ;;;; +1-617-221-1000 |
|---|
| 100 | |
|---|
| 101 | (in-package #:system) |
|---|
| 102 | |
|---|
| 103 | (defpackage "LOOP" (:use "COMMON-LISP")) |
|---|
| 104 | |
|---|
| 105 | (in-package "LOOP") |
|---|
| 106 | |
|---|
| 107 | ;;;; The design of this LOOP is intended to permit, using mostly the same |
|---|
| 108 | ;;;; kernel of code, up to three different "loop" macros: |
|---|
| 109 | ;;;; |
|---|
| 110 | ;;;; (1) The unextended, unextensible ANSI standard LOOP; |
|---|
| 111 | ;;;; |
|---|
| 112 | ;;;; (2) A clean "superset" extension of the ANSI LOOP which provides |
|---|
| 113 | ;;;; functionality similar to that of the old LOOP, but "in the style of" |
|---|
| 114 | ;;;; the ANSI LOOP. For instance, user-definable iteration paths, with a |
|---|
| 115 | ;;;; somewhat cleaned-up interface. |
|---|
| 116 | ;;;; |
|---|
| 117 | ;;;; (3) Extensions provided in another file which can make this LOOP |
|---|
| 118 | ;;;; kernel behave largely compatibly with the Genera-vintage LOOP macro, |
|---|
| 119 | ;;;; with only a small addition of code (instead of two whole, separate, |
|---|
| 120 | ;;;; LOOP macros). |
|---|
| 121 | ;;;; |
|---|
| 122 | ;;;; Each of the above three LOOP variations can coexist in the same LISP |
|---|
| 123 | ;;;; environment. |
|---|
| 124 | ;;;; |
|---|
| 125 | ;;;; KLUDGE: In SBCL, we only really use variant (1), and any generality |
|---|
| 126 | ;;;; for the other variants is wasted. -- WHN 20000121 |
|---|
| 127 | |
|---|
| 128 | ;;;; FIXME: the STEP-FUNCTION stuff in the code seems to've been |
|---|
| 129 | ;;;; intended to support code which was conditionalized with |
|---|
| 130 | ;;;; LOOP-PREFER-POP (not true on CMU CL) and which has since been |
|---|
| 131 | ;;;; removed. Thus, STEP-FUNCTION stuff could probably be removed too. |
|---|
| 132 | |
|---|
| 133 | ;;;; list collection macrology |
|---|
| 134 | |
|---|
| 135 | (defmacro with-loop-list-collection-head |
|---|
| 136 | ((head-var tail-var &optional user-head-var) &body body) |
|---|
| 137 | (let ((l (and user-head-var (list (list user-head-var nil))))) |
|---|
| 138 | `(let* ((,head-var (list nil)) (,tail-var ,head-var) ,@l) |
|---|
| 139 | ,@body))) |
|---|
| 140 | |
|---|
| 141 | (defmacro loop-collect-rplacd |
|---|
| 142 | (&environment env (head-var tail-var &optional user-head-var) form) |
|---|
| 143 | (setq form (macroexpand form env)) |
|---|
| 144 | (flet ((cdr-wrap (form n) |
|---|
| 145 | (declare (fixnum n)) |
|---|
| 146 | (do () ((<= n 4) (setq form `(,(case n |
|---|
| 147 | (1 'cdr) |
|---|
| 148 | (2 'cddr) |
|---|
| 149 | (3 'cdddr) |
|---|
| 150 | (4 'cddddr)) |
|---|
| 151 | ,form))) |
|---|
| 152 | (setq form `(cddddr ,form) n (- n 4))))) |
|---|
| 153 | (let ((tail-form form) (ncdrs nil)) |
|---|
| 154 | ;; Determine whether the form being constructed is a list of known |
|---|
| 155 | ;; length. |
|---|
| 156 | (when (consp form) |
|---|
| 157 | (cond ((eq (car form) 'list) |
|---|
| 158 | (setq ncdrs (1- (length (cdr form))))) |
|---|
| 159 | ((member (car form) '(list* cons)) |
|---|
| 160 | (when (and (cddr form) (member (car (last form)) '(nil 'nil))) |
|---|
| 161 | (setq ncdrs (- (length (cdr form)) 2)))))) |
|---|
| 162 | (let ((answer |
|---|
| 163 | (cond ((null ncdrs) |
|---|
| 164 | `(when (setf (cdr ,tail-var) ,tail-form) |
|---|
| 165 | (setq ,tail-var (last (cdr ,tail-var))))) |
|---|
| 166 | ((< ncdrs 0) (return-from loop-collect-rplacd nil)) |
|---|
| 167 | ((= ncdrs 0) |
|---|
| 168 | ;; @@@@ Here we have a choice of two idioms: |
|---|
| 169 | ;; (RPLACD TAIL (SETQ TAIL TAIL-FORM)) |
|---|
| 170 | ;; (SETQ TAIL (SETF (CDR TAIL) TAIL-FORM)). |
|---|
| 171 | ;; Genera and most others I have seen do better with the |
|---|
| 172 | ;; former. |
|---|
| 173 | `(rplacd ,tail-var (setq ,tail-var ,tail-form))) |
|---|
| 174 | (t `(setq ,tail-var ,(cdr-wrap `(setf (cdr ,tail-var) |
|---|
| 175 | ,tail-form) |
|---|
| 176 | ncdrs)))))) |
|---|
| 177 | ;; If not using locatives or something similar to update the |
|---|
| 178 | ;; user's head variable, we've got to set it... It's harmless |
|---|
| 179 | ;; to repeatedly set it unconditionally, and probably faster |
|---|
| 180 | ;; than checking. |
|---|
| 181 | (when user-head-var |
|---|
| 182 | (setq answer |
|---|
| 183 | `(progn ,answer |
|---|
| 184 | (setq ,user-head-var (cdr ,head-var))))) |
|---|
| 185 | answer)))) |
|---|
| 186 | |
|---|
| 187 | (defmacro loop-collect-answer (head-var |
|---|
| 188 | &optional user-head-var) |
|---|
| 189 | (or user-head-var |
|---|
| 190 | `(cdr ,head-var))) |
|---|
| 191 | |
|---|
| 192 | ;;;; maximization technology |
|---|
| 193 | |
|---|
| 194 | #| |
|---|
| 195 | The basic idea of all this minimax randomness here is that we have to |
|---|
| 196 | have constructed all uses of maximize and minimize to a particular |
|---|
| 197 | "destination" before we can decide how to code them. The goal is to not |
|---|
| 198 | have to have any kinds of flags, by knowing both that (1) the type is |
|---|
| 199 | something which we can provide an initial minimum or maximum value for |
|---|
| 200 | and (2) know that a MAXIMIZE and MINIMIZE are not being combined. |
|---|
| 201 | |
|---|
| 202 | SO, we have a datastructure which we annotate with all sorts of things, |
|---|
| 203 | incrementally updating it as we generate loop body code, and then use |
|---|
| 204 | a wrapper and internal macros to do the coding when the loop has been |
|---|
| 205 | constructed. |
|---|
| 206 | |# |
|---|
| 207 | |
|---|
| 208 | (defstruct (loop-minimax |
|---|
| 209 | (:constructor make-loop-minimax-internal) |
|---|
| 210 | (:copier nil) |
|---|
| 211 | (:predicate nil)) |
|---|
| 212 | answer-variable |
|---|
| 213 | type |
|---|
| 214 | temp-variable |
|---|
| 215 | flag-variable |
|---|
| 216 | operations |
|---|
| 217 | infinity-data) |
|---|
| 218 | |
|---|
| 219 | (defvar *loop-minimax-type-infinities-alist* |
|---|
| 220 | ;; FIXME: Now that SBCL supports floating point infinities again, we |
|---|
| 221 | ;; should have floating point infinities here, as cmucl-2.4.8 did. |
|---|
| 222 | '((fixnum most-positive-fixnum most-negative-fixnum))) |
|---|
| 223 | |
|---|
| 224 | (defun make-loop-minimax (answer-variable type) |
|---|
| 225 | (let ((infinity-data (cdr (assoc type |
|---|
| 226 | *loop-minimax-type-infinities-alist* |
|---|
| 227 | :test #'subtypep)))) |
|---|
| 228 | (make-loop-minimax-internal |
|---|
| 229 | :answer-variable answer-variable |
|---|
| 230 | :type type |
|---|
| 231 | :temp-variable (gensym "LOOP-MAXMIN-TEMP-") |
|---|
| 232 | :flag-variable (and (not infinity-data) |
|---|
| 233 | (gensym "LOOP-MAXMIN-FLAG-")) |
|---|
| 234 | :operations nil |
|---|
| 235 | :infinity-data infinity-data))) |
|---|
| 236 | |
|---|
| 237 | (defun loop-note-minimax-operation (operation minimax) |
|---|
| 238 | (pushnew (the symbol operation) (loop-minimax-operations minimax)) |
|---|
| 239 | (when (and (cdr (loop-minimax-operations minimax)) |
|---|
| 240 | (not (loop-minimax-flag-variable minimax))) |
|---|
| 241 | (setf (loop-minimax-flag-variable minimax) |
|---|
| 242 | (gensym "LOOP-MAXMIN-FLAG-"))) |
|---|
| 243 | operation) |
|---|
| 244 | |
|---|
| 245 | (defmacro with-minimax-value (lm &body body) |
|---|
| 246 | (let ((init (loop-typed-init (loop-minimax-type lm))) |
|---|
| 247 | (which (car (loop-minimax-operations lm))) |
|---|
| 248 | (infinity-data (loop-minimax-infinity-data lm)) |
|---|
| 249 | (answer-var (loop-minimax-answer-variable lm)) |
|---|
| 250 | (temp-var (loop-minimax-temp-variable lm)) |
|---|
| 251 | (flag-var (loop-minimax-flag-variable lm)) |
|---|
| 252 | (type (loop-minimax-type lm))) |
|---|
| 253 | (if flag-var |
|---|
| 254 | `(let ((,answer-var ,init) (,temp-var ,init) (,flag-var nil)) |
|---|
| 255 | (declare (type ,type ,answer-var ,temp-var)) |
|---|
| 256 | ,@body) |
|---|
| 257 | `(let ((,answer-var ,(if (eq which 'min) |
|---|
| 258 | (first infinity-data) |
|---|
| 259 | (second infinity-data))) |
|---|
| 260 | (,temp-var ,init)) |
|---|
| 261 | (declare (type ,type ,answer-var ,temp-var)) |
|---|
| 262 | ,@body)))) |
|---|
| 263 | |
|---|
| 264 | (defmacro loop-accumulate-minimax-value (lm operation form) |
|---|
| 265 | (let* ((answer-var (loop-minimax-answer-variable lm)) |
|---|
| 266 | (temp-var (loop-minimax-temp-variable lm)) |
|---|
| 267 | (flag-var (loop-minimax-flag-variable lm)) |
|---|
| 268 | (test `(,(ecase operation |
|---|
| 269 | (min '<) |
|---|
| 270 | (max '>)) |
|---|
| 271 | ,temp-var ,answer-var))) |
|---|
| 272 | `(progn |
|---|
| 273 | (setq ,temp-var ,form) |
|---|
| 274 | (when ,(if flag-var `(or (not ,flag-var) ,test) test) |
|---|
| 275 | (setq ,@(and flag-var `(,flag-var t)) |
|---|
| 276 | ,answer-var ,temp-var))))) |
|---|
| 277 | |
|---|
| 278 | ;;;; LOOP keyword tables |
|---|
| 279 | |
|---|
| 280 | #| |
|---|
| 281 | LOOP keyword tables are hash tables string keys and a test of EQUAL. |
|---|
| 282 | |
|---|
| 283 | The actual descriptive/dispatch structure used by LOOP is called a "loop |
|---|
| 284 | universe" contains a few tables and parameterizations. The basic idea is |
|---|
| 285 | that we can provide a non-extensible ANSI-compatible loop environment, |
|---|
| 286 | an extensible ANSI-superset loop environment, and (for such environments |
|---|
| 287 | as CLOE) one which is "sufficiently close" to the old Genera-vintage |
|---|
| 288 | LOOP for use by old user programs without requiring all of the old LOOP |
|---|
| 289 | code to be loaded. |
|---|
| 290 | |# |
|---|
| 291 | |
|---|
| 292 | ;;;; token hackery |
|---|
| 293 | |
|---|
| 294 | ;;; Compare two "tokens". The first is the frob out of *LOOP-SOURCE-CODE*, |
|---|
| 295 | ;;; the second a symbol to check against. |
|---|
| 296 | (defun loop-tequal (x1 x2) |
|---|
| 297 | (and (symbolp x1) (string= x1 x2))) |
|---|
| 298 | |
|---|
| 299 | (defun loop-tassoc (kwd alist) |
|---|
| 300 | (and (symbolp kwd) (assoc kwd alist :test #'string=))) |
|---|
| 301 | |
|---|
| 302 | (defun loop-tmember (kwd list) |
|---|
| 303 | (and (symbolp kwd) (member kwd list :test #'string=))) |
|---|
| 304 | |
|---|
| 305 | (defun loop-lookup-keyword (loop-token table) |
|---|
| 306 | (and (symbolp loop-token) |
|---|
| 307 | (values (gethash (symbol-name (the symbol loop-token)) table)))) |
|---|
| 308 | |
|---|
| 309 | (defmacro loop-store-table-data (symbol table datum) |
|---|
| 310 | `(setf (gethash (symbol-name ,symbol) ,table) ,datum)) |
|---|
| 311 | |
|---|
| 312 | (defstruct (loop-universe |
|---|
| 313 | (:copier nil) |
|---|
| 314 | (:predicate nil)) |
|---|
| 315 | keywords ; hash table, value = (fn-name . extra-data) |
|---|
| 316 | iteration-keywords ; hash table, value = (fn-name . extra-data) |
|---|
| 317 | for-keywords ; hash table, value = (fn-name . extra-data) |
|---|
| 318 | path-keywords ; hash table, value = (fn-name . extra-data) |
|---|
| 319 | type-symbols ; hash table of type SYMBOLS, test EQ, |
|---|
| 320 | ; value = CL type specifier |
|---|
| 321 | type-keywords ; hash table of type STRINGS, test EQUAL, |
|---|
| 322 | ; value = CL type spec |
|---|
| 323 | ansi ; NIL, T, or :EXTENDED |
|---|
| 324 | implicit-for-required) ; see loop-hack-iteration |
|---|
| 325 | |
|---|
| 326 | #+sbcl |
|---|
| 327 | (sb!int:def!method print-object ((u loop-universe) stream) |
|---|
| 328 | (let ((string (case (loop-universe-ansi u) |
|---|
| 329 | ((nil) "non-ANSI") |
|---|
| 330 | ((t) "ANSI") |
|---|
| 331 | (:extended "extended-ANSI") |
|---|
| 332 | (t (loop-universe-ansi u))))) |
|---|
| 333 | (print-unreadable-object (u stream :type t) |
|---|
| 334 | (write-string string stream)))) |
|---|
| 335 | |
|---|
| 336 | ;;; This is the "current" loop context in use when we are expanding a |
|---|
| 337 | ;;; loop. It gets bound on each invocation of LOOP. |
|---|
| 338 | (defvar *loop-universe*) |
|---|
| 339 | |
|---|
| 340 | (defun make-standard-loop-universe (&key keywords for-keywords |
|---|
| 341 | iteration-keywords path-keywords |
|---|
| 342 | type-keywords type-symbols ansi) |
|---|
| 343 | (declare (type (member nil t :extended) ansi)) |
|---|
| 344 | (flet ((maketable (entries) |
|---|
| 345 | (let* ((size (length entries)) |
|---|
| 346 | (ht (make-hash-table :size (if (< size 10) 10 size) |
|---|
| 347 | :test 'equal))) |
|---|
| 348 | (dolist (x entries) |
|---|
| 349 | (setf (gethash (symbol-name (car x)) ht) (cadr x))) |
|---|
| 350 | ht))) |
|---|
| 351 | (make-loop-universe |
|---|
| 352 | :keywords (maketable keywords) |
|---|
| 353 | :for-keywords (maketable for-keywords) |
|---|
| 354 | :iteration-keywords (maketable iteration-keywords) |
|---|
| 355 | :path-keywords (maketable path-keywords) |
|---|
| 356 | :ansi ansi |
|---|
| 357 | :implicit-for-required (not (null ansi)) |
|---|
| 358 | :type-keywords (maketable type-keywords) |
|---|
| 359 | :type-symbols (let* ((size (length type-symbols)) |
|---|
| 360 | (ht (make-hash-table :size (if (< size 10) 10 size) |
|---|
| 361 | :test 'eq))) |
|---|
| 362 | (dolist (x type-symbols) |
|---|
| 363 | (if (atom x) |
|---|
| 364 | (setf (gethash x ht) x) |
|---|
| 365 | (setf (gethash (car x) ht) (cadr x)))) |
|---|
| 366 | ht)))) |
|---|
| 367 | |
|---|
| 368 | ;;;; SETQ hackery, including destructuring ("DESETQ") |
|---|
| 369 | |
|---|
| 370 | (defun loop-make-psetq (frobs) |
|---|
| 371 | (and frobs |
|---|
| 372 | (loop-make-desetq |
|---|
| 373 | (list (car frobs) |
|---|
| 374 | (if (null (cddr frobs)) (cadr frobs) |
|---|
| 375 | `(prog1 ,(cadr frobs) |
|---|
| 376 | ,(loop-make-psetq (cddr frobs)))))))) |
|---|
| 377 | |
|---|
| 378 | (defun loop-make-desetq (var-val-pairs) |
|---|
| 379 | (if (null var-val-pairs) |
|---|
| 380 | nil |
|---|
| 381 | (cons 'loop-really-desetq var-val-pairs))) |
|---|
| 382 | |
|---|
| 383 | (defvar *loop-desetq-temporary* |
|---|
| 384 | (make-symbol "LOOP-DESETQ-TEMP")) |
|---|
| 385 | |
|---|
| 386 | (defmacro loop-really-desetq (&environment env |
|---|
| 387 | &rest var-val-pairs) |
|---|
| 388 | (labels ((find-non-null (var) |
|---|
| 389 | ;; See whether there's any non-null thing here. Recurse |
|---|
| 390 | ;; if the list element is itself a list. |
|---|
| 391 | (do ((tail var)) ((not (consp tail)) tail) |
|---|
| 392 | (when (find-non-null (pop tail)) (return t)))) |
|---|
| 393 | (loop-desetq-internal (var val &optional temp) |
|---|
| 394 | ;; returns a list of actions to be performed |
|---|
| 395 | (typecase var |
|---|
| 396 | (null |
|---|
| 397 | (when (consp val) |
|---|
| 398 | ;; Don't lose possible side effects. |
|---|
| 399 | (if (eq (car val) 'prog1) |
|---|
| 400 | ;; These can come from PSETQ or DESETQ below. |
|---|
| 401 | ;; Throw away the value, keep the side effects. |
|---|
| 402 | ;; Special case is for handling an expanded POP. |
|---|
| 403 | (mapcan (lambda (x) |
|---|
| 404 | (and (consp x) |
|---|
| 405 | (or (not (eq (car x) 'car)) |
|---|
| 406 | (not (symbolp (cadr x))) |
|---|
| 407 | (not (symbolp (setq x (macroexpand x env))))) |
|---|
| 408 | (cons x nil))) |
|---|
| 409 | (cdr val)) |
|---|
| 410 | `(,val)))) |
|---|
| 411 | (cons |
|---|
| 412 | (let* ((car (car var)) |
|---|
| 413 | (cdr (cdr var)) |
|---|
| 414 | (car-non-null (find-non-null car)) |
|---|
| 415 | (cdr-non-null (find-non-null cdr))) |
|---|
| 416 | (when (or car-non-null cdr-non-null) |
|---|
| 417 | (if cdr-non-null |
|---|
| 418 | (let* ((temp-p temp) |
|---|
| 419 | (temp (or temp *loop-desetq-temporary*)) |
|---|
| 420 | (body `(,@(loop-desetq-internal car |
|---|
| 421 | `(car ,temp)) |
|---|
| 422 | (setq ,temp (cdr ,temp)) |
|---|
| 423 | ,@(loop-desetq-internal cdr |
|---|
| 424 | temp |
|---|
| 425 | temp)))) |
|---|
| 426 | (if temp-p |
|---|
| 427 | `(,@(unless (eq temp val) |
|---|
| 428 | `((setq ,temp ,val))) |
|---|
| 429 | ,@body) |
|---|
| 430 | `((let ((,temp ,val)) |
|---|
| 431 | ,@body)))) |
|---|
| 432 | ;; no CDRing to do |
|---|
| 433 | (loop-desetq-internal car `(car ,val) temp))))) |
|---|
| 434 | (otherwise |
|---|
| 435 | (unless (eq var val) |
|---|
| 436 | `((setq ,var ,val))))))) |
|---|
| 437 | (do ((actions)) |
|---|
| 438 | ((null var-val-pairs) |
|---|
| 439 | (if (null (cdr actions)) (car actions) `(progn ,@(nreverse actions)))) |
|---|
| 440 | (setq actions (revappend |
|---|
| 441 | (loop-desetq-internal (pop var-val-pairs) |
|---|
| 442 | (pop var-val-pairs)) |
|---|
| 443 | actions))))) |
|---|
| 444 | |
|---|
| 445 | ;;;; LOOP-local variables |
|---|
| 446 | |
|---|
| 447 | ;;; This is the "current" pointer into the LOOP source code. |
|---|
| 448 | (defvar *loop-source-code*) |
|---|
| 449 | |
|---|
| 450 | ;;; This is the pointer to the original, for things like NAMED that |
|---|
| 451 | ;;; insist on being in a particular position |
|---|
| 452 | (defvar *loop-original-source-code*) |
|---|
| 453 | |
|---|
| 454 | ;;; This is *loop-source-code* as of the "last" clause. It is used |
|---|
| 455 | ;;; primarily for generating error messages (see loop-error, loop-warn). |
|---|
| 456 | (defvar *loop-source-context*) |
|---|
| 457 | |
|---|
| 458 | ;;; list of names for the LOOP, supplied by the NAMED clause |
|---|
| 459 | (defvar *loop-names*) |
|---|
| 460 | |
|---|
| 461 | ;;; The macroexpansion environment given to the macro. |
|---|
| 462 | (defvar *loop-macro-environment*) |
|---|
| 463 | |
|---|
| 464 | ;;; This holds variable names specified with the USING clause. |
|---|
| 465 | ;;; See LOOP-NAMED-VAR. |
|---|
| 466 | (defvar *loop-named-vars*) |
|---|
| 467 | |
|---|
| 468 | ;;; LETlist-like list being accumulated for one group of parallel bindings. |
|---|
| 469 | (defvar *loop-vars*) |
|---|
| 470 | |
|---|
| 471 | ;;; list of declarations being accumulated in parallel with *LOOP-VARS* |
|---|
| 472 | (defvar *loop-declarations*) |
|---|
| 473 | |
|---|
| 474 | ;;; This is used by LOOP for destructuring binding, if it is doing |
|---|
| 475 | ;;; that itself. See LOOP-MAKE-VAR. |
|---|
| 476 | (defvar *loop-desetq-crocks*) |
|---|
| 477 | |
|---|
| 478 | ;;; list of wrapping forms, innermost first, which go immediately |
|---|
| 479 | ;;; inside the current set of parallel bindings being accumulated in |
|---|
| 480 | ;;; *LOOP-VARS*. The wrappers are appended onto a body. E.g., |
|---|
| 481 | ;;; this list could conceivably have as its value |
|---|
| 482 | ;;; ((WITH-OPEN-FILE (G0001 G0002 ...))), |
|---|
| 483 | ;;; with G0002 being one of the bindings in *LOOP-VARS* (This is |
|---|
| 484 | ;;; why the wrappers go inside of the variable bindings). |
|---|
| 485 | (defvar *loop-wrappers*) |
|---|
| 486 | |
|---|
| 487 | ;;; This accumulates lists of previous values of *LOOP-VARS* and |
|---|
| 488 | ;;; the other lists above, for each new nesting of bindings. See |
|---|
| 489 | ;;; LOOP-BIND-BLOCK. |
|---|
| 490 | (defvar *loop-bind-stack*) |
|---|
| 491 | |
|---|
| 492 | ;;; This is simply a list of LOOP iteration variables, used for |
|---|
| 493 | ;;; checking for duplications. |
|---|
| 494 | (defvar *loop-iteration-vars*) |
|---|
| 495 | |
|---|
| 496 | ;;; list of prologue forms of the loop, accumulated in reverse order |
|---|
| 497 | (defvar *loop-prologue*) |
|---|
| 498 | |
|---|
| 499 | (defvar *loop-before-loop*) |
|---|
| 500 | (defvar *loop-body*) |
|---|
| 501 | (defvar *loop-after-body*) |
|---|
| 502 | |
|---|
| 503 | ;;; This is T if we have emitted any body code, so that iteration |
|---|
| 504 | ;;; driving clauses can be disallowed. This is not strictly the same |
|---|
| 505 | ;;; as checking *LOOP-BODY*, because we permit some clauses such as |
|---|
| 506 | ;;; RETURN to not be considered "real" body (so as to permit the user |
|---|
| 507 | ;;; to "code" an abnormal return value "in loop"). |
|---|
| 508 | (defvar *loop-emitted-body*) |
|---|
| 509 | |
|---|
| 510 | ;;; list of epilogue forms (supplied by FINALLY generally), accumulated |
|---|
| 511 | ;;; in reverse order |
|---|
| 512 | (defvar *loop-epilogue*) |
|---|
| 513 | |
|---|
| 514 | ;;; list of epilogue forms which are supplied after the above "user" |
|---|
| 515 | ;;; epilogue. "Normal" termination return values are provide by |
|---|
| 516 | ;;; putting the return form in here. Normally this is done using |
|---|
| 517 | ;;; LOOP-EMIT-FINAL-VALUE, q.v. |
|---|
| 518 | (defvar *loop-after-epilogue*) |
|---|
| 519 | |
|---|
| 520 | ;;; the "culprit" responsible for supplying a final value from the |
|---|
| 521 | ;;; loop. This is so LOOP-EMIT-FINAL-VALUE can moan about multiple |
|---|
| 522 | ;;; return values being supplied. |
|---|
| 523 | (defvar *loop-final-value-culprit*) |
|---|
| 524 | |
|---|
| 525 | ;;; If this is true, we are in some branch of a conditional. Some |
|---|
| 526 | ;;; clauses may be disallowed. |
|---|
| 527 | (defvar *loop-inside-conditional*) |
|---|
| 528 | |
|---|
| 529 | ;;; If not NIL, this is a temporary bound around the loop for holding |
|---|
| 530 | ;;; the temporary value for "it" in things like "when (f) collect it". |
|---|
| 531 | ;;; It may be used as a supertemporary by some other things. |
|---|
| 532 | (defvar *loop-when-it-var*) |
|---|
| 533 | |
|---|
| 534 | ;;; Sometimes we decide we need to fold together parts of the loop, |
|---|
| 535 | ;;; but some part of the generated iteration code is different for the |
|---|
| 536 | ;;; first and remaining iterations. This variable will be the |
|---|
| 537 | ;;; temporary which is the flag used in the loop to tell whether we |
|---|
| 538 | ;;; are in the first or remaining iterations. |
|---|
| 539 | (defvar *loop-never-stepped-var*) |
|---|
| 540 | |
|---|
| 541 | ;;; list of all the value-accumulation descriptor structures in the |
|---|
| 542 | ;;; loop. See LOOP-GET-COLLECTION-INFO. |
|---|
| 543 | (defvar *loop-collection-cruft*) ; for multiple COLLECTs (etc.) |
|---|
| 544 | |
|---|
| 545 | ;;;; code analysis stuff |
|---|
| 546 | |
|---|
| 547 | (defun loop-constant-fold-if-possible (form &optional expected-type) |
|---|
| 548 | (let ((new-form form) (constantp nil) (constant-value nil)) |
|---|
| 549 | (when (setq constantp (constantp new-form)) |
|---|
| 550 | (setq constant-value (eval new-form))) |
|---|
| 551 | (when (and constantp expected-type) |
|---|
| 552 | (unless (typep constant-value expected-type) |
|---|
| 553 | (loop-warn "~@<The form ~S evaluated to ~S, which was not of ~ |
|---|
| 554 | the anticipated type ~S.~:@>" |
|---|
| 555 | form constant-value expected-type) |
|---|
| 556 | (setq constantp nil constant-value nil))) |
|---|
| 557 | (values new-form constantp constant-value))) |
|---|
| 558 | |
|---|
| 559 | (defun loop-constantp (form) |
|---|
| 560 | (constantp form)) |
|---|
| 561 | |
|---|
| 562 | ;;;; LOOP iteration optimization |
|---|
| 563 | |
|---|
| 564 | (defvar *loop-duplicate-code* |
|---|
| 565 | nil) |
|---|
| 566 | |
|---|
| 567 | (defvar *loop-iteration-flag-var* |
|---|
| 568 | (make-symbol "LOOP-NOT-FIRST-TIME")) |
|---|
| 569 | |
|---|
| 570 | (defun loop-code-duplication-threshold (env) |
|---|
| 571 | (declare (ignore env)) |
|---|
| 572 | (let (;; If we could read optimization declaration information (as |
|---|
| 573 | ;; with the DECLARATION-INFORMATION function (present in |
|---|
| 574 | ;; CLTL2, removed from ANSI standard) we could set these |
|---|
| 575 | ;; values flexibly. Without DECLARATION-INFORMATION, we have |
|---|
| 576 | ;; to set them to constants. |
|---|
| 577 | ;; |
|---|
| 578 | ;; except FIXME: we've lost all pretence of portability, |
|---|
| 579 | ;; considering this instead an internal implementation, so |
|---|
| 580 | ;; we're free to couple to our own representation of the |
|---|
| 581 | ;; environment. |
|---|
| 582 | (speed 1) |
|---|
| 583 | (space 1)) |
|---|
| 584 | (+ 40 (* (- speed space) 10)))) |
|---|
| 585 | |
|---|
| 586 | (defmacro loop-body (&environment env |
|---|
| 587 | prologue |
|---|
| 588 | before-loop |
|---|
| 589 | main-body |
|---|
| 590 | after-loop |
|---|
| 591 | epilogue |
|---|
| 592 | &aux rbefore rafter flagvar) |
|---|
| 593 | (unless (= (length before-loop) (length after-loop)) |
|---|
| 594 | (error "LOOP-BODY called with non-synched before- and after-loop lists")) |
|---|
| 595 | ;;All our work is done from these copies, working backwards from the end: |
|---|
| 596 | (setq rbefore (reverse before-loop) rafter (reverse after-loop)) |
|---|
| 597 | (labels ((psimp (l) |
|---|
| 598 | (let ((ans nil)) |
|---|
| 599 | (dolist (x l) |
|---|
| 600 | (when x |
|---|
| 601 | (push x ans) |
|---|
| 602 | (when (and (consp x) |
|---|
| 603 | (member (car x) '(go return return-from))) |
|---|
| 604 | (return nil)))) |
|---|
| 605 | (nreverse ans))) |
|---|
| 606 | (pify (l) (if (null (cdr l)) (car l) `(progn ,@l))) |
|---|
| 607 | (makebody () |
|---|
| 608 | (let ((form `(tagbody |
|---|
| 609 | ,@(psimp (append prologue (nreverse rbefore))) |
|---|
| 610 | next-loop |
|---|
| 611 | ,@(psimp (append main-body |
|---|
| 612 | (nreconc rafter |
|---|
| 613 | `((go next-loop))))) |
|---|
| 614 | end-loop |
|---|
| 615 | ,@(psimp epilogue)))) |
|---|
| 616 | (if flagvar `(let ((,flagvar nil)) ,form) form)))) |
|---|
| 617 | (when (or *loop-duplicate-code* (not rbefore)) |
|---|
| 618 | (return-from loop-body (makebody))) |
|---|
| 619 | ;; This outer loop iterates once for each not-first-time flag test |
|---|
| 620 | ;; generated plus once more for the forms that don't need a flag test. |
|---|
| 621 | (do ((threshold (loop-code-duplication-threshold env))) (nil) |
|---|
| 622 | (declare (fixnum threshold)) |
|---|
| 623 | ;; Go backwards from the ends of before-loop and after-loop |
|---|
| 624 | ;; merging all the equivalent forms into the body. |
|---|
| 625 | (do () ((or (null rbefore) (not (equal (car rbefore) (car rafter))))) |
|---|
| 626 | (push (pop rbefore) main-body) |
|---|
| 627 | (pop rafter)) |
|---|
| 628 | (unless rbefore (return (makebody))) |
|---|
| 629 | ;; The first forms in RBEFORE & RAFTER (which are the |
|---|
| 630 | ;; chronologically last forms in the list) differ, therefore |
|---|
| 631 | ;; they cannot be moved into the main body. If everything that |
|---|
| 632 | ;; chronologically precedes them either differs or is equal but |
|---|
| 633 | ;; is okay to duplicate, we can just put all of rbefore in the |
|---|
| 634 | ;; prologue and all of rafter after the body. Otherwise, there |
|---|
| 635 | ;; is something that is not okay to duplicate, so it and |
|---|
| 636 | ;; everything chronologically after it in rbefore and rafter |
|---|
| 637 | ;; must go into the body, with a flag test to distinguish the |
|---|
| 638 | ;; first time around the loop from later times. What |
|---|
| 639 | ;; chronologically precedes the non-duplicatable form will be |
|---|
| 640 | ;; handled the next time around the outer loop. |
|---|
| 641 | (do ((bb rbefore (cdr bb)) |
|---|
| 642 | (aa rafter (cdr aa)) |
|---|
| 643 | (lastdiff nil) |
|---|
| 644 | (count 0) |
|---|
| 645 | (inc nil)) |
|---|
| 646 | ((null bb) (return-from loop-body (makebody))) ; Did it. |
|---|
| 647 | (cond ((not (equal (car bb) (car aa))) (setq lastdiff bb count 0)) |
|---|
| 648 | ((or (not (setq inc (estimate-code-size (car bb) env))) |
|---|
| 649 | (> (incf count inc) threshold)) |
|---|
| 650 | ;; Ok, we have found a non-duplicatable piece of code. |
|---|
| 651 | ;; Everything chronologically after it must be in the |
|---|
| 652 | ;; central body. Everything chronologically at and |
|---|
| 653 | ;; after LASTDIFF goes into the central body under a |
|---|
| 654 | ;; flag test. |
|---|
| 655 | (let ((then nil) (else nil)) |
|---|
| 656 | (do () (nil) |
|---|
| 657 | (push (pop rbefore) else) |
|---|
| 658 | (push (pop rafter) then) |
|---|
| 659 | (when (eq rbefore (cdr lastdiff)) (return))) |
|---|
| 660 | (unless flagvar |
|---|
| 661 | (push `(setq ,(setq flagvar *loop-iteration-flag-var*) |
|---|
| 662 | t) |
|---|
| 663 | else)) |
|---|
| 664 | (push `(if ,flagvar ,(pify (psimp then)) ,(pify (psimp else))) |
|---|
| 665 | main-body)) |
|---|
| 666 | ;; Everything chronologically before lastdiff until the |
|---|
| 667 | ;; non-duplicatable form (CAR BB) is the same in |
|---|
| 668 | ;; RBEFORE and RAFTER, so just copy it into the body. |
|---|
| 669 | (do () (nil) |
|---|
| 670 | (pop rafter) |
|---|
| 671 | (push (pop rbefore) main-body) |
|---|
| 672 | (when (eq rbefore (cdr bb)) (return))) |
|---|
| 673 | (return))))))) |
|---|
| 674 | |
|---|
| 675 | (defun duplicatable-code-p (expr env) |
|---|
| 676 | (if (null expr) 0 |
|---|
| 677 | (let ((ans (estimate-code-size expr env))) |
|---|
| 678 | (declare (fixnum ans)) |
|---|
| 679 | ;; @@@@ Use (DECLARATION-INFORMATION 'OPTIMIZE ENV) here to |
|---|
| 680 | ;; get an alist of optimize quantities back to help quantify |
|---|
| 681 | ;; how much code we are willing to duplicate. |
|---|
| 682 | ans))) |
|---|
| 683 | |
|---|
| 684 | (defvar *special-code-sizes* |
|---|
| 685 | '((return 0) (progn 0) |
|---|
| 686 | (null 1) (not 1) (eq 1) (car 1) (cdr 1) |
|---|
| 687 | (when 1) (unless 1) (if 1) |
|---|
| 688 | (caar 2) (cadr 2) (cdar 2) (cddr 2) |
|---|
| 689 | (caaar 3) (caadr 3) (cadar 3) (caddr 3) |
|---|
| 690 | (cdaar 3) (cdadr 3) (cddar 3) (cdddr 3) |
|---|
| 691 | (caaaar 4) (caaadr 4) (caadar 4) (caaddr 4) |
|---|
| 692 | (cadaar 4) (cadadr 4) (caddar 4) (cadddr 4) |
|---|
| 693 | (cdaaar 4) (cdaadr 4) (cdadar 4) (cdaddr 4) |
|---|
| 694 | (cddaar 4) (cddadr 4) (cdddar 4) (cddddr 4))) |
|---|
| 695 | |
|---|
| 696 | (defvar *estimate-code-size-punt* |
|---|
| 697 | '(block |
|---|
| 698 | do do* dolist |
|---|
| 699 | flet |
|---|
| 700 | labels lambda let let* locally |
|---|
| 701 | macrolet multiple-value-bind |
|---|
| 702 | prog prog* |
|---|
| 703 | symbol-macrolet |
|---|
| 704 | tagbody |
|---|
| 705 | unwind-protect |
|---|
| 706 | with-open-file)) |
|---|
| 707 | |
|---|
| 708 | (defun destructuring-size (x) |
|---|
| 709 | (do ((x x (cdr x)) (n 0 (+ (destructuring-size (car x)) n))) |
|---|
| 710 | ((atom x) (+ n (if (null x) 0 1))))) |
|---|
| 711 | |
|---|
| 712 | (defun estimate-code-size (x env) |
|---|
| 713 | (catch 'estimate-code-size |
|---|
| 714 | (estimate-code-size-1 x env))) |
|---|
| 715 | |
|---|
| 716 | (defun estimate-code-size-1 (x env) |
|---|
| 717 | (flet ((list-size (l) |
|---|
| 718 | (let ((n 0)) |
|---|
| 719 | (declare (fixnum n)) |
|---|
| 720 | (dolist (x l n) (incf n (estimate-code-size-1 x env)))))) |
|---|
| 721 | ;;@@@@ ???? (declare (function list-size (list) fixnum)) |
|---|
| 722 | (cond ((constantp x) 1) |
|---|
| 723 | ((symbolp x) (multiple-value-bind (new-form expanded-p) |
|---|
| 724 | (macroexpand-1 x env) |
|---|
| 725 | (if expanded-p |
|---|
| 726 | (estimate-code-size-1 new-form env) |
|---|
| 727 | 1))) |
|---|
| 728 | ((atom x) 1) ;; ??? self-evaluating??? |
|---|
| 729 | ((symbolp (car x)) |
|---|
| 730 | (let ((fn (car x)) (tem nil) (n 0)) |
|---|
| 731 | (declare (symbol fn) (fixnum n)) |
|---|
| 732 | (macrolet ((f (overhead &optional (args nil args-p)) |
|---|
| 733 | `(the fixnum (+ (the fixnum ,overhead) |
|---|
| 734 | (the fixnum |
|---|
| 735 | (list-size ,(if args-p |
|---|
| 736 | args |
|---|
| 737 | '(cdr x)))))))) |
|---|
| 738 | (cond ((setq tem (get fn 'estimate-code-size)) |
|---|
| 739 | (typecase tem |
|---|
| 740 | (fixnum (f tem)) |
|---|
| 741 | (t (funcall tem x env)))) |
|---|
| 742 | ((setq tem (assoc fn *special-code-sizes*)) |
|---|
| 743 | (f (second tem))) |
|---|
| 744 | ((eq fn 'cond) |
|---|
| 745 | (dolist (clause (cdr x) n) |
|---|
| 746 | (incf n (list-size clause)) (incf n))) |
|---|
| 747 | ((eq fn 'desetq) |
|---|
| 748 | (do ((l (cdr x) (cdr l))) ((null l) n) |
|---|
| 749 | (setq n (+ n |
|---|
| 750 | (destructuring-size (car l)) |
|---|
| 751 | (estimate-code-size-1 (cadr l) env))))) |
|---|
| 752 | ((member fn '(setq psetq)) |
|---|
| 753 | (do ((l (cdr x) (cdr l))) ((null l) n) |
|---|
| 754 | (setq n (+ n (estimate-code-size-1 (cadr l) env) 1)))) |
|---|
| 755 | ((eq fn 'go) 1) |
|---|
| 756 | ((eq fn 'function) |
|---|
| 757 | (if #+sbcl |
|---|
| 758 | (sb!int:legal-fun-name-p (cadr x)) |
|---|
| 759 | #+armedbear |
|---|
| 760 | (or (symbolp (cadr x)) |
|---|
| 761 | (and (consp (cadr x)) (eq (caadr x) 'setf))) |
|---|
| 762 | 1 |
|---|
| 763 | ;; FIXME: This tag appears not to be present |
|---|
| 764 | ;; anywhere. |
|---|
| 765 | (throw 'duplicatable-code-p nil))) |
|---|
| 766 | ((eq fn 'multiple-value-setq) |
|---|
| 767 | (f (length (second x)) (cddr x))) |
|---|
| 768 | ((eq fn 'return-from) |
|---|
| 769 | (1+ (estimate-code-size-1 (third x) env))) |
|---|
| 770 | ((or (special-operator-p fn) |
|---|
| 771 | (member fn *estimate-code-size-punt*)) |
|---|
| 772 | (throw 'estimate-code-size nil)) |
|---|
| 773 | (t (multiple-value-bind (new-form expanded-p) |
|---|
| 774 | (macroexpand-1 x env) |
|---|
| 775 | (if expanded-p |
|---|
| 776 | (estimate-code-size-1 new-form env) |
|---|
| 777 | (f 3)))))))) |
|---|
| 778 | (t (throw 'estimate-code-size nil))))) |
|---|
| 779 | |
|---|
| 780 | ;;;; loop errors |
|---|
| 781 | |
|---|
| 782 | (defun loop-context () |
|---|
| 783 | (do ((l *loop-source-context* (cdr l)) (new nil (cons (car l) new))) |
|---|
| 784 | ((eq l (cdr *loop-source-code*)) (nreverse new)))) |
|---|
| 785 | |
|---|
| 786 | (defun loop-error (format-string &rest format-args) |
|---|
| 787 | (error 'program-error |
|---|
| 788 | :format-control "~?~%Current LOOP context:~{ ~S~}." |
|---|
| 789 | :format-arguments (list format-string format-args (loop-context)))) |
|---|
| 790 | |
|---|
| 791 | (defun loop-warn (format-string &rest format-args) |
|---|
| 792 | (warn "~?~%Current LOOP context:~{ ~S~}." |
|---|
| 793 | format-string |
|---|
| 794 | format-args |
|---|
| 795 | (loop-context))) |
|---|
| 796 | |
|---|
| 797 | (defun loop-check-data-type (specified-type required-type |
|---|
| 798 | &optional (default-type required-type)) |
|---|
| 799 | (if (null specified-type) |
|---|
| 800 | default-type |
|---|
| 801 | (multiple-value-bind (a b) (subtypep specified-type required-type) |
|---|
| 802 | (cond ((not b) |
|---|
| 803 | (loop-warn "LOOP couldn't verify that ~S is a subtype of the required type ~S." |
|---|
| 804 | specified-type required-type)) |
|---|
| 805 | ((not a) |
|---|
| 806 | (loop-error "The specified data type ~S is not a subtype of ~S." |
|---|
| 807 | specified-type required-type))) |
|---|
| 808 | specified-type))) |
|---|
| 809 | |
|---|
| 810 | (defun subst-gensyms-for-nil (tree) |
|---|
| 811 | (declare (special *ignores*)) |
|---|
| 812 | (cond |
|---|
| 813 | ((null tree) |
|---|
| 814 | (car (push (gensym "LOOP-IGNORED-VAR-") *ignores*))) |
|---|
| 815 | ((atom tree) |
|---|
| 816 | tree) |
|---|
| 817 | (t |
|---|
| 818 | (cons (subst-gensyms-for-nil (car tree)) |
|---|
| 819 | (subst-gensyms-for-nil (cdr tree)))))) |
|---|
| 820 | |
|---|
| 821 | (defmacro loop-destructuring-bind |
|---|
| 822 | (lambda-list arg-list &rest body) |
|---|
| 823 | (let ((*ignores* nil)) |
|---|
| 824 | (declare (special *ignores*)) |
|---|
| 825 | (let ((d-var-lambda-list (subst-gensyms-for-nil lambda-list))) |
|---|
| 826 | `(destructuring-bind ,d-var-lambda-list |
|---|
| 827 | ,arg-list |
|---|
| 828 | (declare (ignore ,@*ignores*)) |
|---|
| 829 | ,@body)))) |
|---|
| 830 | |
|---|
| 831 | (defun loop-build-destructuring-bindings (crocks forms) |
|---|
| 832 | (if crocks |
|---|
| 833 | `((loop-destructuring-bind ,(car crocks) ,(cadr crocks) |
|---|
| 834 | ,@(loop-build-destructuring-bindings (cddr crocks) forms))) |
|---|
| 835 | forms)) |
|---|
| 836 | |
|---|
| 837 | (defun loop-translate (*loop-source-code* |
|---|
| 838 | *loop-macro-environment* |
|---|
| 839 | *loop-universe*) |
|---|
| 840 | (let ((*loop-original-source-code* *loop-source-code*) |
|---|
| 841 | (*loop-source-context* nil) |
|---|
| 842 | (*loop-iteration-vars* nil) |
|---|
| 843 | (*loop-vars* nil) |
|---|
| 844 | (*loop-named-vars* nil) |
|---|
| 845 | (*loop-declarations* nil) |
|---|
| 846 | (*loop-desetq-crocks* nil) |
|---|
| 847 | (*loop-bind-stack* nil) |
|---|
| 848 | (*loop-prologue* nil) |
|---|
| 849 | (*loop-wrappers* nil) |
|---|
| 850 | (*loop-before-loop* nil) |
|---|
| 851 | (*loop-body* nil) |
|---|
| 852 | (*loop-emitted-body* nil) |
|---|
| 853 | (*loop-after-body* nil) |
|---|
| 854 | (*loop-epilogue* nil) |
|---|
| 855 | (*loop-after-epilogue* nil) |
|---|
| 856 | (*loop-final-value-culprit* nil) |
|---|
| 857 | (*loop-inside-conditional* nil) |
|---|
| 858 | (*loop-when-it-var* nil) |
|---|
| 859 | (*loop-never-stepped-var* nil) |
|---|
| 860 | (*loop-names* nil) |
|---|
| 861 | (*loop-collection-cruft* nil)) |
|---|
| 862 | (loop-iteration-driver) |
|---|
| 863 | (loop-bind-block) |
|---|
| 864 | (let ((answer `(loop-body |
|---|
| 865 | ,(nreverse *loop-prologue*) |
|---|
| 866 | ,(nreverse *loop-before-loop*) |
|---|
| 867 | ,(nreverse *loop-body*) |
|---|
| 868 | ,(nreverse *loop-after-body*) |
|---|
| 869 | ,(nreconc *loop-epilogue* |
|---|
| 870 | (nreverse *loop-after-epilogue*))))) |
|---|
| 871 | (dolist (entry *loop-bind-stack*) |
|---|
| 872 | (let ((vars (first entry)) |
|---|
| 873 | (dcls (second entry)) |
|---|
| 874 | (crocks (third entry)) |
|---|
| 875 | (wrappers (fourth entry))) |
|---|
| 876 | (dolist (w wrappers) |
|---|
| 877 | (setq answer (append w (list answer)))) |
|---|
| 878 | (when (or vars dcls crocks) |
|---|
| 879 | (let ((forms (list answer))) |
|---|
| 880 | ;;(when crocks (push crocks forms)) |
|---|
| 881 | (when dcls (push `(declare ,@dcls) forms)) |
|---|
| 882 | (setq answer `(,(if vars 'let 'locally) |
|---|
| 883 | ,vars |
|---|
| 884 | ,@(loop-build-destructuring-bindings crocks |
|---|
| 885 | forms))))))) |
|---|
| 886 | (do () (nil) |
|---|
| 887 | (setq answer `(block ,(pop *loop-names*) ,answer)) |
|---|
| 888 | (unless *loop-names* (return nil))) |
|---|
| 889 | answer))) |
|---|
| 890 | |
|---|
| 891 | (defun loop-iteration-driver () |
|---|
| 892 | (do () ((null *loop-source-code*)) |
|---|
| 893 | (let ((keyword (car *loop-source-code*)) (tem nil)) |
|---|
| 894 | (cond ((not (symbolp keyword)) |
|---|
| 895 | (loop-error "~S found where LOOP keyword expected" keyword)) |
|---|
| 896 | (t (setq *loop-source-context* *loop-source-code*) |
|---|
| 897 | (loop-pop-source) |
|---|
| 898 | (cond ((setq tem |
|---|
| 899 | (loop-lookup-keyword keyword |
|---|
| 900 | (loop-universe-keywords |
|---|
| 901 | *loop-universe*))) |
|---|
| 902 | ;; It's a "miscellaneous" toplevel LOOP keyword (DO, |
|---|
| 903 | ;; COLLECT, NAMED, etc.) |
|---|
| 904 | (apply (symbol-function (first tem)) (rest tem))) |
|---|
| 905 | ((setq tem |
|---|
| 906 | (loop-lookup-keyword keyword |
|---|
| 907 | (loop-universe-iteration-keywords *loop-universe*))) |
|---|
| 908 | (loop-hack-iteration tem)) |
|---|
| 909 | ((loop-tmember keyword '(and else)) |
|---|
| 910 | ;; The alternative is to ignore it, i.e. let it go |
|---|
| 911 | ;; around to the next keyword... |
|---|
| 912 | (loop-error "secondary clause misplaced at top level in LOOP macro: ~S ~S ~S ..." |
|---|
| 913 | keyword |
|---|
| 914 | (car *loop-source-code*) |
|---|
| 915 | (cadr *loop-source-code*))) |
|---|
| 916 | (t (loop-error "unknown LOOP keyword: ~S" keyword)))))))) |
|---|
| 917 | |
|---|
| 918 | (defun loop-pop-source () |
|---|
| 919 | (if *loop-source-code* |
|---|
| 920 | (pop *loop-source-code*) |
|---|
| 921 | (loop-error "LOOP source code ran out when another token was expected."))) |
|---|
| 922 | |
|---|
| 923 | (defun loop-get-form () |
|---|
| 924 | (if *loop-source-code* |
|---|
| 925 | (loop-pop-source) |
|---|
| 926 | (loop-error "LOOP code ran out where a form was expected."))) |
|---|
| 927 | |
|---|
| 928 | (defun loop-get-compound-form () |
|---|
| 929 | (let ((form (loop-get-form))) |
|---|
| 930 | (unless (consp form) |
|---|
| 931 | (loop-error "A compound form was expected, but ~S found." form)) |
|---|
| 932 | form)) |
|---|
| 933 | |
|---|
| 934 | (defun loop-get-progn () |
|---|
| 935 | (do ((forms (list (loop-get-compound-form)) |
|---|
| 936 | (cons (loop-get-compound-form) forms)) |
|---|
| 937 | (nextform (car *loop-source-code*) |
|---|
| 938 | (car *loop-source-code*))) |
|---|
| 939 | ((atom nextform) |
|---|
| 940 | (if (null (cdr forms)) (car forms) (cons 'progn (nreverse forms)))))) |
|---|
| 941 | |
|---|
| 942 | (defun loop-construct-return (form) |
|---|
| 943 | `(return-from ,(car *loop-names*) ,form)) |
|---|
| 944 | |
|---|
| 945 | (defun loop-pseudo-body (form) |
|---|
| 946 | (cond ((or *loop-emitted-body* *loop-inside-conditional*) |
|---|
| 947 | (push form *loop-body*)) |
|---|
| 948 | (t (push form *loop-before-loop*) (push form *loop-after-body*)))) |
|---|
| 949 | |
|---|
| 950 | (defun loop-emit-body (form) |
|---|
| 951 | (setq *loop-emitted-body* t) |
|---|
| 952 | (loop-pseudo-body form)) |
|---|
| 953 | |
|---|
| 954 | (defun loop-emit-final-value (&optional (form nil form-supplied-p)) |
|---|
| 955 | (when form-supplied-p |
|---|
| 956 | (push (loop-construct-return form) *loop-after-epilogue*)) |
|---|
| 957 | (when *loop-final-value-culprit* |
|---|
| 958 | (loop-warn "The LOOP clause is providing a value for the iteration;~@ |
|---|
| 959 | however, one was already established by a ~S clause." |
|---|
| 960 | *loop-final-value-culprit*)) |
|---|
| 961 | (setq *loop-final-value-culprit* (car *loop-source-context*))) |
|---|
| 962 | |
|---|
| 963 | (defun loop-disallow-conditional (&optional kwd) |
|---|
| 964 | (when *loop-inside-conditional* |
|---|
| 965 | (loop-error "~:[This LOOP~;The LOOP ~:*~S~] clause is not permitted inside a conditional." kwd))) |
|---|
| 966 | |
|---|
| 967 | (defun loop-disallow-anonymous-collectors () |
|---|
| 968 | (when (find-if-not 'loop-collector-name *loop-collection-cruft*) |
|---|
| 969 | (loop-error "This LOOP clause is not permitted with anonymous collectors."))) |
|---|
| 970 | |
|---|
| 971 | (defun loop-disallow-aggregate-booleans () |
|---|
| 972 | (when (loop-tmember *loop-final-value-culprit* '(always never thereis)) |
|---|
| 973 | (loop-error "This anonymous collection LOOP clause is not permitted with aggregate booleans."))) |
|---|
| 974 | |
|---|
| 975 | ;;;; loop types |
|---|
| 976 | |
|---|
| 977 | (defun loop-typed-init (data-type &optional step-var-p) |
|---|
| 978 | (when (and data-type (subtypep data-type 'number)) |
|---|
| 979 | (if (or (subtypep data-type 'float) |
|---|
| 980 | (subtypep data-type '(complex float))) |
|---|
| 981 | (coerce (if step-var-p 1 0) data-type) |
|---|
| 982 | (if step-var-p 1 0)))) |
|---|
| 983 | |
|---|
| 984 | (defun loop-optional-type (&optional variable) |
|---|
| 985 | ;; No variable specified implies that no destructuring is permissible. |
|---|
| 986 | (and *loop-source-code* ; Don't get confused by NILs.. |
|---|
| 987 | (let ((z (car *loop-source-code*))) |
|---|
| 988 | (cond ((loop-tequal z 'of-type) |
|---|
| 989 | ;; This is the syntactically unambigous form in that |
|---|
| 990 | ;; the form of the type specifier does not matter. |
|---|
| 991 | ;; Also, it is assumed that the type specifier is |
|---|
| 992 | ;; unambiguously, and without need of translation, a |
|---|
| 993 | ;; common lisp type specifier or pattern (matching the |
|---|
| 994 | ;; variable) thereof. |
|---|
| 995 | (loop-pop-source) |
|---|
| 996 | (loop-pop-source)) |
|---|
| 997 | |
|---|
| 998 | ((symbolp z) |
|---|
| 999 | ;; This is the (sort of) "old" syntax, even though we |
|---|
| 1000 | ;; didn't used to support all of these type symbols. |
|---|
| 1001 | (let ((type-spec (or (gethash z |
|---|
| 1002 | (loop-universe-type-symbols |
|---|
| 1003 | *loop-universe*)) |
|---|
| 1004 | (gethash (symbol-name z) |
|---|
| 1005 | (loop-universe-type-keywords |
|---|
| 1006 | *loop-universe*))))) |
|---|
| 1007 | (when type-spec |
|---|
| 1008 | (loop-pop-source) |
|---|
| 1009 | type-spec))) |
|---|
| 1010 | (t |
|---|
| 1011 | ;; This is our sort-of old syntax. But this is only |
|---|
| 1012 | ;; valid for when we are destructuring, so we will be |
|---|
| 1013 | ;; compulsive (should we really be?) and require that |
|---|
| 1014 | ;; we in fact be doing variable destructuring here. We |
|---|
| 1015 | ;; must translate the old keyword pattern typespec |
|---|
| 1016 | ;; into a fully-specified pattern of real type |
|---|
| 1017 | ;; specifiers here. |
|---|
| 1018 | (if (consp variable) |
|---|
| 1019 | (unless (consp z) |
|---|
| 1020 | (loop-error |
|---|
| 1021 | "~S found where a LOOP keyword, LOOP type keyword, or LOOP type pattern expected" |
|---|
| 1022 | z)) |
|---|
| 1023 | (loop-error "~S found where a LOOP keyword or LOOP type keyword expected" z)) |
|---|
| 1024 | (loop-pop-source) |
|---|
| 1025 | (labels ((translate (k v) |
|---|
| 1026 | (cond ((null k) nil) |
|---|
| 1027 | ((atom k) |
|---|
| 1028 | (replicate |
|---|
| 1029 | (or (gethash k |
|---|
| 1030 | (loop-universe-type-symbols |
|---|
| 1031 | *loop-universe*)) |
|---|
| 1032 | (gethash (symbol-name k) |
|---|
| 1033 | (loop-universe-type-keywords |
|---|
| 1034 | *loop-universe*)) |
|---|
| 1035 | (loop-error |
|---|
| 1036 | "The destructuring type pattern ~S contains the unrecognized type keyword ~S." |
|---|
| 1037 | z k)) |
|---|
| 1038 | v)) |
|---|
| 1039 | ((atom v) |
|---|
| 1040 | (loop-error |
|---|
| 1041 | "The destructuring type pattern ~S doesn't match the variable pattern ~S." |
|---|
| 1042 | z variable)) |
|---|
| 1043 | (t (cons (translate (car k) (car v)) |
|---|
| 1044 | (translate (cdr k) (cdr v)))))) |
|---|
| 1045 | (replicate (typ v) |
|---|
| 1046 | (if (atom v) |
|---|
| 1047 | typ |
|---|
| 1048 | (cons (replicate typ (car v)) |
|---|
| 1049 | (replicate typ (cdr v)))))) |
|---|
| 1050 | (translate z variable))))))) |
|---|
| 1051 | |
|---|
| 1052 | ;;;; loop variables |
|---|
| 1053 | |
|---|
| 1054 | (defun loop-bind-block () |
|---|
| 1055 | (when (or *loop-vars* *loop-declarations* *loop-wrappers*) |
|---|
| 1056 | (push (list (nreverse *loop-vars*) |
|---|
| 1057 | *loop-declarations* |
|---|
| 1058 | *loop-desetq-crocks* |
|---|
| 1059 | *loop-wrappers*) |
|---|
| 1060 | *loop-bind-stack*) |
|---|
| 1061 | (setq *loop-vars* nil |
|---|
| 1062 | *loop-declarations* nil |
|---|
| 1063 | *loop-desetq-crocks* nil |
|---|
| 1064 | *loop-wrappers* nil))) |
|---|
| 1065 | |
|---|
| 1066 | (defun loop-var-p (name) |
|---|
| 1067 | (do ((entry *loop-bind-stack* (cdr entry))) |
|---|
| 1068 | (nil) |
|---|
| 1069 | (cond |
|---|
| 1070 | ((null entry) (return nil)) |
|---|
| 1071 | ((assoc name (caar entry) :test #'eq) (return t))))) |
|---|
| 1072 | |
|---|
| 1073 | (defun loop-make-var (name initialization dtype &optional iteration-var-p step-var-p) |
|---|
| 1074 | (cond ((null name) |
|---|
| 1075 | (setq name (gensym "LOOP-IGNORE-")) |
|---|
| 1076 | (push (list name initialization) *loop-vars*) |
|---|
| 1077 | (if (null initialization) |
|---|
| 1078 | (push `(ignore ,name) *loop-declarations*) |
|---|
| 1079 | (loop-declare-var name dtype))) |
|---|
| 1080 | ((atom name) |
|---|
| 1081 | (cond (iteration-var-p |
|---|
| 1082 | (if (member name *loop-iteration-vars*) |
|---|
| 1083 | (loop-error "duplicated LOOP iteration variable ~S" name) |
|---|
| 1084 | (push name *loop-iteration-vars*))) |
|---|
| 1085 | ((assoc name *loop-vars*) |
|---|
| 1086 | (loop-error "duplicated variable ~S in LOOP parallel binding" |
|---|
| 1087 | name))) |
|---|
| 1088 | (unless (symbolp name) |
|---|
| 1089 | (loop-error "bad variable ~S somewhere in LOOP" name)) |
|---|
| 1090 | (loop-declare-var name dtype step-var-p) |
|---|
| 1091 | ;; We use ASSOC on this list to check for duplications (above), |
|---|
| 1092 | ;; so don't optimize out this list: |
|---|
| 1093 | (push (list name (or initialization (loop-typed-init dtype step-var-p))) |
|---|
| 1094 | *loop-vars*)) |
|---|
| 1095 | (initialization |
|---|
| 1096 | (let ((newvar (gensym "LOOP-DESTRUCTURE-"))) |
|---|
| 1097 | (loop-declare-var name dtype) |
|---|
| 1098 | (push (list newvar initialization) *loop-vars*) |
|---|
| 1099 | ;; *LOOP-DESETQ-CROCKS* gathered in reverse order. |
|---|
| 1100 | (setq *loop-desetq-crocks* |
|---|
| 1101 | (list* name newvar *loop-desetq-crocks*)))) |
|---|
| 1102 | (t (let ((tcar nil) (tcdr nil)) |
|---|
| 1103 | (if (atom dtype) (setq tcar (setq tcdr dtype)) |
|---|
| 1104 | (setq tcar (car dtype) tcdr (cdr dtype))) |
|---|
| 1105 | (loop-make-var (car name) nil tcar iteration-var-p) |
|---|
| 1106 | (loop-make-var (cdr name) nil tcdr iteration-var-p)))) |
|---|
| 1107 | name) |
|---|
| 1108 | |
|---|
| 1109 | (defun loop-make-iteration-var (name initialization dtype) |
|---|
| 1110 | (when (and name (loop-var-p name)) |
|---|
| 1111 | (loop-error "Variable ~S has already been used." name)) |
|---|
| 1112 | (loop-make-var name initialization dtype t)) |
|---|
| 1113 | |
|---|
| 1114 | (defun loop-declare-var (name dtype &optional step-var-p) |
|---|
| 1115 | (cond ((or (null name) (null dtype) (eq dtype t)) nil) |
|---|
| 1116 | ((symbolp name) |
|---|
| 1117 | (unless (subtypep t dtype) |
|---|
| 1118 | (let ((dtype (let ((init (loop-typed-init dtype step-var-p))) |
|---|
| 1119 | (if (typep init dtype) |
|---|
| 1120 | dtype |
|---|
| 1121 | `(or (member ,init) ,dtype))))) |
|---|
| 1122 | (push `(type ,dtype ,name) *loop-declarations*)))) |
|---|
| 1123 | ((consp name) |
|---|
| 1124 | (cond ((consp dtype) |
|---|
| 1125 | (loop-declare-var (car name) (car dtype)) |
|---|
| 1126 | (loop-declare-var (cdr name) (cdr dtype))) |
|---|
| 1127 | (t (loop-declare-var (car name) dtype) |
|---|
| 1128 | (loop-declare-var (cdr name) dtype)))) |
|---|
| 1129 | (t (error "invalid LOOP variable passed in: ~S" name)))) |
|---|
| 1130 | |
|---|
| 1131 | (defun loop-maybe-bind-form (form data-type) |
|---|
| 1132 | (if (loop-constantp form) |
|---|
| 1133 | form |
|---|
| 1134 | (loop-make-var (gensym "LOOP-BIND-") form data-type))) |
|---|
| 1135 | |
|---|
| 1136 | (defun loop-do-if (for negatep) |
|---|
| 1137 | (let ((form (loop-get-form)) |
|---|
| 1138 | (*loop-inside-conditional* t) |
|---|
| 1139 | (it-p nil) |
|---|
| 1140 | (first-clause-p t)) |
|---|
| 1141 | (flet ((get-clause (for) |
|---|
| 1142 | (do ((body nil)) (nil) |
|---|
| 1143 | (let ((key (car *loop-source-code*)) (*loop-body* nil) data) |
|---|
| 1144 | (cond ((not (symbolp key)) |
|---|
| 1145 | (loop-error |
|---|
| 1146 | "~S found where keyword expected getting LOOP clause after ~S" |
|---|
| 1147 | key for)) |
|---|
| 1148 | (t (setq *loop-source-context* *loop-source-code*) |
|---|
| 1149 | (loop-pop-source) |
|---|
| 1150 | (when (and (loop-tequal (car *loop-source-code*) 'it) |
|---|
| 1151 | first-clause-p) |
|---|
| 1152 | (setq *loop-source-code* |
|---|
| 1153 | (cons (or it-p |
|---|
| 1154 | (setq it-p |
|---|
| 1155 | (loop-when-it-var))) |
|---|
| 1156 | (cdr *loop-source-code*)))) |
|---|
| 1157 | (cond ((or (not (setq data (loop-lookup-keyword |
|---|
| 1158 | key (loop-universe-keywords *loop-universe*)))) |
|---|
| 1159 | (progn (apply (symbol-function (car data)) |
|---|
| 1160 | (cdr data)) |
|---|
| 1161 | (null *loop-body*))) |
|---|
| 1162 | (loop-error |
|---|
| 1163 | "~S does not introduce a LOOP clause that can follow ~S." |
|---|
| 1164 | key for)) |
|---|
| 1165 | (t (setq body (nreconc *loop-body* body))))))) |
|---|
| 1166 | (setq first-clause-p nil) |
|---|
| 1167 | (if (loop-tequal (car *loop-source-code*) :and) |
|---|
| 1168 | (loop-pop-source) |
|---|
| 1169 | (return (if (cdr body) |
|---|
| 1170 | `(progn ,@(nreverse body)) |
|---|
| 1171 | (car body))))))) |
|---|
| 1172 | (let ((then (get-clause for)) |
|---|
| 1173 | (else (when (loop-tequal (car *loop-source-code*) :else) |
|---|
| 1174 | (loop-pop-source) |
|---|
| 1175 | (list (get-clause :else))))) |
|---|
| 1176 | (when (loop-tequal (car *loop-source-code*) :end) |
|---|
| 1177 | (loop-pop-source)) |
|---|
| 1178 | (when it-p (setq form `(setq ,it-p ,form))) |
|---|
| 1179 | (loop-pseudo-body |
|---|
| 1180 | `(if ,(if negatep `(not ,form) form) |
|---|
| 1181 | ,then |
|---|
| 1182 | ,@else)))))) |
|---|
| 1183 | |
|---|
| 1184 | (defun loop-do-initially () |
|---|
| 1185 | (loop-disallow-conditional :initially) |
|---|
| 1186 | (push (loop-get-progn) *loop-prologue*)) |
|---|
| 1187 | |
|---|
| 1188 | (defun loop-do-finally () |
|---|
| 1189 | (loop-disallow-conditional :finally) |
|---|
| 1190 | (push (loop-get-progn) *loop-epilogue*)) |
|---|
| 1191 | |
|---|
| 1192 | (defun loop-do-do () |
|---|
| 1193 | (loop-emit-body (loop-get-progn))) |
|---|
| 1194 | |
|---|
| 1195 | (defun loop-do-named () |
|---|
| 1196 | (let ((name (loop-pop-source))) |
|---|
| 1197 | (unless (symbolp name) |
|---|
| 1198 | (loop-error "~S is an invalid name for your LOOP" name)) |
|---|
| 1199 | (when (or *loop-before-loop* *loop-body* *loop-after-epilogue* *loop-inside-conditional*) |
|---|
| 1200 | (loop-error "The NAMED ~S clause occurs too late." name)) |
|---|
| 1201 | (when *loop-names* |
|---|
| 1202 | (loop-error "You may only use one NAMED clause in your loop: NAMED ~S ... NAMED ~S." |
|---|
| 1203 | (car *loop-names*) name)) |
|---|
| 1204 | (setq *loop-names* (list name)))) |
|---|
| 1205 | |
|---|
| 1206 | (defun loop-do-return () |
|---|
| 1207 | (loop-emit-body (loop-construct-return (loop-get-form)))) |
|---|
| 1208 | |
|---|
| 1209 | ;;;; value accumulation: LIST |
|---|
| 1210 | |
|---|
| 1211 | (defstruct (loop-collector |
|---|
| 1212 | (:copier nil) |
|---|
| 1213 | (:predicate nil)) |
|---|
| 1214 | name |
|---|
| 1215 | class |
|---|
| 1216 | (history nil) |
|---|
| 1217 | (tempvars nil) |
|---|
| 1218 | dtype |
|---|
| 1219 | (data nil)) ;collector-specific data |
|---|
| 1220 | |
|---|
| 1221 | (defun loop-get-collection-info (collector class default-type) |
|---|
| 1222 | (let ((form (loop-get-form)) |
|---|
| 1223 | (dtype (and (not (loop-universe-ansi *loop-universe*)) (loop-optional-type))) |
|---|
| 1224 | (name (when (loop-tequal (car *loop-source-code*) 'into) |
|---|
| 1225 | (loop-pop-source) |
|---|
| 1226 | (loop-pop-source)))) |
|---|
| 1227 | (when (not (symbolp name)) |
|---|
| 1228 | (loop-error "The value accumulation recipient name, ~S, is not a symbol." name)) |
|---|
| 1229 | (unless name |
|---|
| 1230 | (loop-disallow-aggregate-booleans)) |
|---|
| 1231 | (unless dtype |
|---|
| 1232 | (setq dtype (or (loop-optional-type) default-type))) |
|---|
| 1233 | (let ((cruft (find (the symbol name) *loop-collection-cruft* |
|---|
| 1234 | :key #'loop-collector-name))) |
|---|
| 1235 | (cond ((not cruft) |
|---|
| 1236 | (when (and name (loop-var-p name)) |
|---|
| 1237 | (loop-error "Variable ~S in INTO clause is a duplicate" name)) |
|---|
| 1238 | (push (setq cruft (make-loop-collector |
|---|
| 1239 | :name name :class class |
|---|
| 1240 | :history (list collector) :dtype dtype)) |
|---|
| 1241 | *loop-collection-cruft*)) |
|---|
| 1242 | (t (unless (eq (loop-collector-class cruft) class) |
|---|
| 1243 | (loop-error |
|---|
| 1244 | "incompatible kinds of LOOP value accumulation specified for collecting~@ |
|---|
| 1245 | ~:[as the value of the LOOP~;~:*INTO ~S~]: ~S and ~S" |
|---|
| 1246 | name (car (loop-collector-history cruft)) collector)) |
|---|
| 1247 | (unless (equal dtype (loop-collector-dtype cruft)) |
|---|
| 1248 | (loop-warn |
|---|
| 1249 | "unequal datatypes specified in different LOOP value accumulations~@ |
|---|
| 1250 | into ~S: ~S and ~S" |
|---|
| 1251 | name dtype (loop-collector-dtype cruft)) |
|---|
| 1252 | (when (eq (loop-collector-dtype cruft) t) |
|---|
| 1253 | (setf (loop-collector-dtype cruft) dtype))) |
|---|
| 1254 | (push collector (loop-collector-history cruft)))) |
|---|
| 1255 | (values cruft form)))) |
|---|
| 1256 | |
|---|
| 1257 | (defun loop-list-collection (specifically) ; NCONC, LIST, or APPEND |
|---|
| 1258 | (multiple-value-bind (lc form) |
|---|
| 1259 | (loop-get-collection-info specifically 'list 'list) |
|---|
| 1260 | (let ((tempvars (loop-collector-tempvars lc))) |
|---|
| 1261 | (unless tempvars |
|---|
| 1262 | (setf (loop-collector-tempvars lc) |
|---|
| 1263 | (setq tempvars (list* (gensym "LOOP-LIST-HEAD-") |
|---|
| 1264 | (gensym "LOOP-LIST-TAIL-") |
|---|
| 1265 | (and (loop-collector-name lc) |
|---|
| 1266 | (list (loop-collector-name lc)))))) |
|---|
| 1267 | (push `(with-loop-list-collection-head ,tempvars) *loop-wrappers*) |
|---|
| 1268 | (unless (loop-collector-name lc) |
|---|
| 1269 | (loop-emit-final-value `(loop-collect-answer ,(car tempvars) |
|---|
| 1270 | ,@(cddr tempvars))))) |
|---|
| 1271 | (ecase specifically |
|---|
| 1272 | (list (setq form `(list ,form))) |
|---|
| 1273 | (nconc nil) |
|---|
| 1274 | (append (unless (and (consp form) (eq (car form) 'list)) |
|---|
| 1275 | (setq form `(copy-list ,form))))) |
|---|
| 1276 | (loop-emit-body `(loop-collect-rplacd ,tempvars ,form))))) |
|---|
| 1277 | |
|---|
| 1278 | ;;;; value accumulation: MAX, MIN, SUM, COUNT |
|---|
| 1279 | |
|---|
| 1280 | (defun loop-sum-collection (specifically required-type default-type);SUM, COUNT |
|---|
| 1281 | (multiple-value-bind (lc form) |
|---|
| 1282 | (loop-get-collection-info specifically 'sum default-type) |
|---|
| 1283 | (loop-check-data-type (loop-collector-dtype lc) required-type) |
|---|
| 1284 | (let ((tempvars (loop-collector-tempvars lc))) |
|---|
| 1285 | (unless tempvars |
|---|
| 1286 | (setf (loop-collector-tempvars lc) |
|---|
| 1287 | (setq tempvars (list (loop-make-var |
|---|
| 1288 | (or (loop-collector-name lc) |
|---|
| 1289 | (gensym "LOOP-SUM-")) |
|---|
| 1290 | nil (loop-collector-dtype lc))))) |
|---|
| 1291 | (unless (loop-collector-name lc) |
|---|
| 1292 | (loop-emit-final-value (car (loop-collector-tempvars lc))))) |
|---|
| 1293 | (loop-emit-body |
|---|
| 1294 | (if (eq specifically 'count) |
|---|
| 1295 | `(when ,form |
|---|
| 1296 | (setq ,(car tempvars) |
|---|
| 1297 | (1+ ,(car tempvars)))) |
|---|
| 1298 | `(setq ,(car tempvars) |
|---|
| 1299 | (+ ,(car tempvars) |
|---|
| 1300 | ,form))))))) |
|---|
| 1301 | |
|---|
| 1302 | (defun loop-maxmin-collection (specifically) |
|---|
| 1303 | (multiple-value-bind (lc form) |
|---|
| 1304 | (loop-get-collection-info specifically 'maxmin 'real) |
|---|
| 1305 | (loop-check-data-type (loop-collector-dtype lc) 'real) |
|---|
| 1306 | (let ((data (loop-collector-data lc))) |
|---|
| 1307 | (unless data |
|---|
| 1308 | (setf (loop-collector-data lc) |
|---|
| 1309 | (setq data (make-loop-minimax |
|---|
| 1310 | (or (loop-collector-name lc) |
|---|
| 1311 | (gensym "LOOP-MAXMIN-")) |
|---|
| 1312 | (loop-collector-dtype lc)))) |
|---|
| 1313 | (unless (loop-collector-name lc) |
|---|
| 1314 | (loop-emit-final-value (loop-minimax-answer-variable data)))) |
|---|
| 1315 | (loop-note-minimax-operation specifically data) |
|---|
| 1316 | (push `(with-minimax-value ,data) *loop-wrappers*) |
|---|
| 1317 | (loop-emit-body `(loop-accumulate-minimax-value ,data |
|---|
| 1318 | ,specifically |
|---|
| 1319 | ,form))))) |
|---|
| 1320 | |
|---|
| 1321 | ;;;; value accumulation: aggregate booleans |
|---|
| 1322 | |
|---|
| 1323 | ;;; handling the ALWAYS and NEVER loop keywords |
|---|
| 1324 | ;;; |
|---|
| 1325 | ;;; Under ANSI these are not permitted to appear under conditionalization. |
|---|
| 1326 | (defun loop-do-always (restrictive negate) |
|---|
| 1327 | (let ((form (loop-get-form))) |
|---|
| 1328 | (when restrictive (loop-disallow-conditional)) |
|---|
| 1329 | (loop-disallow-anonymous-collectors) |
|---|
| 1330 | (loop-emit-body `(,(if negate 'when 'unless) ,form |
|---|
| 1331 | ,(loop-construct-return nil))) |
|---|
| 1332 | (loop-emit-final-value t))) |
|---|
| 1333 | |
|---|
| 1334 | ;;; handling the THEREIS loop keyword |
|---|
| 1335 | ;;; |
|---|
| 1336 | ;;; Under ANSI this is not permitted to appear under conditionalization. |
|---|
| 1337 | (defun loop-do-thereis (restrictive) |
|---|
| 1338 | (when restrictive (loop-disallow-conditional)) |
|---|
| 1339 | (loop-disallow-anonymous-collectors) |
|---|
| 1340 | (loop-emit-final-value) |
|---|
| 1341 | (loop-emit-body `(when (setq ,(loop-when-it-var) ,(loop-get-form)) |
|---|
| 1342 | ,(loop-construct-return *loop-when-it-var*)))) |
|---|
| 1343 | |
|---|
| 1344 | (defun loop-do-while (negate kwd &aux (form (loop-get-form))) |
|---|
| 1345 | (loop-disallow-conditional kwd) |
|---|
| 1346 | (loop-pseudo-body `(,(if negate 'when 'unless) ,form (go end-loop)))) |
|---|
| 1347 | |
|---|
| 1348 | (defun loop-do-repeat () |
|---|
| 1349 | (loop-disallow-conditional :repeat) |
|---|
| 1350 | (let ((form (loop-get-form)) |
|---|
| 1351 | (type 'integer)) |
|---|
| 1352 | (let ((var (loop-make-var (gensym "LOOP-REPEAT-") `(ceiling ,form) type))) |
|---|
| 1353 | (push `(if (<= ,var 0) (go end-loop) (decf ,var)) *loop-before-loop*) |
|---|
| 1354 | (push `(if (<= ,var 0) (go end-loop) (decf ,var)) *loop-after-body*) |
|---|
| 1355 | ;; FIXME: What should |
|---|
| 1356 | ;; (loop count t into a |
|---|
| 1357 | ;; repeat 3 |
|---|
| 1358 | ;; count t into b |
|---|
| 1359 | ;; finally (return (list a b))) |
|---|
| 1360 | ;; return: (3 3) or (4 3)? PUSHes above are for the former |
|---|
| 1361 | ;; variant, L-P-B below for the latter. |
|---|
| 1362 | #+nil (loop-pseudo-body `(when (minusp (decf ,var)) (go end-loop)))))) |
|---|
| 1363 | |
|---|
| 1364 | (defun loop-do-with () |
|---|
| 1365 | (loop-disallow-conditional :with) |
|---|
| 1366 | (do ((var) (val) (dtype)) (nil) |
|---|
| 1367 | (setq var (loop-pop-source) |
|---|
| 1368 | dtype (loop-optional-type var) |
|---|
| 1369 | val (cond ((loop-tequal (car *loop-source-code*) :=) |
|---|
| 1370 | (loop-pop-source) |
|---|
| 1371 | (loop-get-form)) |
|---|
| 1372 | (t nil))) |
|---|
| 1373 | (when (and var (loop-var-p var)) |
|---|
| 1374 | (loop-error "Variable ~S has already been used" var)) |
|---|
| 1375 | (loop-make-var var val dtype) |
|---|
| 1376 | (if (loop-tequal (car *loop-source-code*) :and) |
|---|
| 1377 | (loop-pop-source) |
|---|
| 1378 | (return (loop-bind-block))))) |
|---|
| 1379 | |
|---|
| 1380 | ;;;; the iteration driver |
|---|
| 1381 | |
|---|
| 1382 | (defun loop-hack-iteration (entry) |
|---|
| 1383 | (flet ((make-endtest (list-of-forms) |
|---|
| 1384 | (cond ((null list-of-forms) nil) |
|---|
| 1385 | ((member t list-of-forms) '(go end-loop)) |
|---|
| 1386 | (t `(when ,(if (null (cdr (setq list-of-forms |
|---|
| 1387 | (nreverse list-of-forms)))) |
|---|
| 1388 | (car list-of-forms) |
|---|
| 1389 | (cons 'or list-of-forms)) |
|---|
| 1390 | (go end-loop)))))) |
|---|
| 1391 | (do ((pre-step-tests nil) |
|---|
| 1392 | (steps nil) |
|---|
| 1393 | (post-step-tests nil) |
|---|
| 1394 | (pseudo-steps nil) |
|---|
| 1395 | (pre-loop-pre-step-tests nil) |
|---|
| 1396 | (pre-loop-steps nil) |
|---|
| 1397 | (pre-loop-post-step-tests nil) |
|---|
| 1398 | (pre-loop-pseudo-steps nil) |
|---|
| 1399 | (tem) (data)) |
|---|
| 1400 | (nil) |
|---|
| 1401 | ;; Note that we collect endtests in reverse order, but steps in correct |
|---|
| 1402 | ;; order. MAKE-ENDTEST does the nreverse for us. |
|---|
| 1403 | (setq tem (setq data |
|---|
| 1404 | (apply (symbol-function (first entry)) (rest entry)))) |
|---|
| 1405 | (and (car tem) (push (car tem) pre-step-tests)) |
|---|
| 1406 | (setq steps (nconc steps (copy-list (car (setq tem (cdr tem)))))) |
|---|
| 1407 | (and (car (setq tem (cdr tem))) (push (car tem) post-step-tests)) |
|---|
| 1408 | (setq pseudo-steps |
|---|
| 1409 | (nconc pseudo-steps (copy-list (car (setq tem (cdr tem)))))) |
|---|
| 1410 | (setq tem (cdr tem)) |
|---|
| 1411 | (when *loop-emitted-body* |
|---|
| 1412 | (loop-error "iteration in LOOP follows body code")) |
|---|
| 1413 | (unless tem (setq tem data)) |
|---|
| 1414 | (when (car tem) (push (car tem) pre-loop-pre-step-tests)) |
|---|
| 1415 | ;; FIXME: This (SETF FOO (NCONC FOO BAR)) idiom appears often enough |
|---|
| 1416 | ;; that it might be worth making it into an NCONCF macro. |
|---|
| 1417 | (setq pre-loop-steps |
|---|
| 1418 | (nconc pre-loop-steps (copy-list (car (setq tem (cdr tem)))))) |
|---|
| 1419 | (when (car (setq tem (cdr tem))) |
|---|
| 1420 | (push (car tem) pre-loop-post-step-tests)) |
|---|
| 1421 | (setq pre-loop-pseudo-steps |
|---|
| 1422 | (nconc pre-loop-pseudo-steps (copy-list (cadr tem)))) |
|---|
| 1423 | (unless (loop-tequal (car *loop-source-code*) :and) |
|---|
| 1424 | (setq *loop-before-loop* |
|---|
| 1425 | (list* (loop-make-desetq pre-loop-pseudo-steps) |
|---|
| 1426 | (make-endtest pre-loop-post-step-tests) |
|---|
| 1427 | (loop-make-psetq pre-loop-steps) |
|---|
| 1428 | (make-endtest pre-loop-pre-step-tests) |
|---|
| 1429 | *loop-before-loop*)) |
|---|
| 1430 | (setq *loop-after-body* |
|---|
| 1431 | (list* (loop-make-desetq pseudo-steps) |
|---|
| 1432 | (make-endtest post-step-tests) |
|---|
| 1433 | (loop-make-psetq steps) |
|---|
| 1434 | (make-endtest pre-step-tests) |
|---|
| 1435 | *loop-after-body*)) |
|---|
| 1436 | (loop-bind-block) |
|---|
| 1437 | (return nil)) |
|---|
| 1438 | (loop-pop-source) ; Flush the "AND". |
|---|
| 1439 | (when (and (not (loop-universe-implicit-for-required *loop-universe*)) |
|---|
| 1440 | (setq tem |
|---|
| 1441 | (loop-lookup-keyword |
|---|
| 1442 | (car *loop-source-code*) |
|---|
| 1443 | (loop-universe-iteration-keywords *loop-universe*)))) |
|---|
| 1444 | ;; The latest ANSI clarification is that the FOR/AS after the AND must |
|---|
| 1445 | ;; NOT be supplied. |
|---|
| 1446 | (loop-pop-source) |
|---|
| 1447 | (setq entry tem))))) |
|---|
| 1448 | |
|---|
| 1449 | ;;;; main iteration drivers |
|---|
| 1450 | |
|---|
| 1451 | ;;; FOR variable keyword ..args.. |
|---|
| 1452 | (defun loop-do-for () |
|---|
| 1453 | (let* ((var (loop-pop-source)) |
|---|
| 1454 | (data-type (loop-optional-type var)) |
|---|
| 1455 | (keyword (loop-pop-source)) |
|---|
| 1456 | (first-arg nil) |
|---|
| 1457 | (tem nil)) |
|---|
| 1458 | (setq first-arg (loop-get-form)) |
|---|
| 1459 | (unless (and (symbolp keyword) |
|---|
| 1460 | (setq tem (loop-lookup-keyword |
|---|
| 1461 | keyword |
|---|
| 1462 | (loop-universe-for-keywords *loop-universe*)))) |
|---|
| 1463 | (loop-error "~S is an unknown keyword in FOR or AS clause in LOOP." |
|---|
| 1464 | keyword)) |
|---|
| 1465 | (apply (car tem) var first-arg data-type (cdr tem)))) |
|---|
| 1466 | |
|---|
| 1467 | (defun loop-when-it-var () |
|---|
| 1468 | (or *loop-when-it-var* |
|---|
| 1469 | (setq *loop-when-it-var* |
|---|
| 1470 | (loop-make-var (gensym "LOOP-IT-") nil nil)))) |
|---|
| 1471 | |
|---|
| 1472 | ;;;; various FOR/AS subdispatches |
|---|
| 1473 | |
|---|
| 1474 | ;;; ANSI "FOR x = y [THEN z]" is sort of like the old Genera one when |
|---|
| 1475 | ;;; the THEN is omitted (other than being more stringent in its |
|---|
| 1476 | ;;; placement), and like the old "FOR x FIRST y THEN z" when the THEN |
|---|
| 1477 | ;;; is present. I.e., the first initialization occurs in the loop body |
|---|
| 1478 | ;;; (first-step), not in the variable binding phase. |
|---|
| 1479 | (defun loop-ansi-for-equals (var val data-type) |
|---|
| 1480 | (loop-make-iteration-var var nil data-type) |
|---|
| 1481 | (cond ((loop-tequal (car *loop-source-code*) :then) |
|---|
| 1482 | ;; Then we are the same as "FOR x FIRST y THEN z". |
|---|
| 1483 | (loop-pop-source) |
|---|
| 1484 | `(() (,var ,(loop-get-form)) () () |
|---|
| 1485 | () (,var ,val) () ())) |
|---|
| 1486 | (t ;; We are the same as "FOR x = y". |
|---|
| 1487 | `(() (,var ,val) () ())))) |
|---|
| 1488 | |
|---|
| 1489 | (defun loop-for-across (var val data-type) |
|---|
| 1490 | (loop-make-iteration-var var nil data-type) |
|---|
| 1491 | (let ((vector-var (gensym "LOOP-ACROSS-VECTOR-")) |
|---|
| 1492 | (index-var (gensym "LOOP-ACROSS-INDEX-"))) |
|---|
| 1493 | (multiple-value-bind (vector-form constantp vector-value) |
|---|
| 1494 | (loop-constant-fold-if-possible val 'vector) |
|---|
| 1495 | (loop-make-var |
|---|
| 1496 | vector-var vector-form |
|---|
| 1497 | (if (and (consp vector-form) (eq (car vector-form) 'the)) |
|---|
| 1498 | (cadr vector-form) |
|---|
| 1499 | 'vector)) |
|---|
| 1500 | (loop-make-var index-var 0 'fixnum) |
|---|
| 1501 | (let* ((length 0) |
|---|
| 1502 | (length-form (cond ((not constantp) |
|---|
| 1503 | (let ((v (gensym "LOOP-ACROSS-LIMIT-"))) |
|---|
| 1504 | (push `(setq ,v (length ,vector-var)) |
|---|
| 1505 | *loop-prologue*) |
|---|
| 1506 | (loop-make-var v 0 'fixnum))) |
|---|
| 1507 | (t (setq length (length vector-value))))) |
|---|
| 1508 | (first-test `(>= ,index-var ,length-form)) |
|---|
| 1509 | (other-test first-test) |
|---|
| 1510 | (step `(,var (aref ,vector-var ,index-var))) |
|---|
| 1511 | (pstep `(,index-var (1+ ,index-var)))) |
|---|
| 1512 | (declare (fixnum length)) |
|---|
| 1513 | (when constantp |
|---|
| 1514 | (setq first-test (= length 0)) |
|---|
| 1515 | (when (<= length 1) |
|---|
| 1516 | (setq other-test t))) |
|---|
| 1517 | `(,other-test ,step () ,pstep |
|---|
| 1518 | ,@(and (not (eq first-test other-test)) |
|---|
| 1519 | `(,first-test ,step () ,pstep))))))) |
|---|
| 1520 | |
|---|
| 1521 | ;;;; list iteration |
|---|
| 1522 | |
|---|
| 1523 | (defun loop-list-step (listvar) |
|---|
| 1524 | ;; We are not equipped to analyze whether 'FOO is the same as #'FOO |
|---|
| 1525 | ;; here in any sensible fashion, so let's give an obnoxious warning |
|---|
| 1526 | ;; whenever 'FOO is used as the stepping function. |
|---|
| 1527 | ;; |
|---|
| 1528 | ;; While a Discerning Compiler may deal intelligently with |
|---|
| 1529 | ;; (FUNCALL 'FOO ...), not recognizing FOO may defeat some LOOP |
|---|
| 1530 | ;; optimizations. |
|---|
| 1531 | (let ((stepper (cond ((loop-tequal (car *loop-source-code*) :by) |
|---|
| 1532 | (loop-pop-source) |
|---|
| 1533 | (loop-get-form)) |
|---|
| 1534 | (t '(function cdr))))) |
|---|
| 1535 | (cond ((and (consp stepper) (eq (car stepper) 'quote)) |
|---|
| 1536 | (loop-warn "Use of QUOTE around stepping function in LOOP will be left verbatim.") |
|---|
| 1537 | `(funcall ,stepper ,listvar)) |
|---|
| 1538 | ((and (consp stepper) (eq (car stepper) 'function)) |
|---|
| 1539 | (list (cadr stepper) listvar)) |
|---|
| 1540 | (t |
|---|
| 1541 | `(funcall ,(loop-make-var (gensym "LOOP-FN-") stepper 'function) |
|---|
| 1542 | ,listvar))))) |
|---|
| 1543 | |
|---|
| 1544 | (defun loop-for-on (var val data-type) |
|---|
| 1545 | (multiple-value-bind (list constantp list-value) |
|---|
| 1546 | (loop-constant-fold-if-possible val) |
|---|
| 1547 | (let ((listvar var)) |
|---|
| 1548 | (cond ((and var (symbolp var)) |
|---|
| 1549 | (loop-make-iteration-var var list data-type)) |
|---|
| 1550 | (t (loop-make-var (setq listvar (gensym)) list 'list) |
|---|
| 1551 | (loop-make-iteration-var var nil data-type))) |
|---|
| 1552 | (let ((list-step (loop-list-step listvar))) |
|---|
| 1553 | (let* ((first-endtest |
|---|
| 1554 | ;; mysterious comment from original CMU CL sources: |
|---|
| 1555 | ;; the following should use `atom' instead of `endp', |
|---|
| 1556 | ;; per [bug2428] |
|---|
| 1557 | `(atom ,listvar)) |
|---|
| 1558 | (other-endtest first-endtest)) |
|---|
| 1559 | (when (and constantp (listp list-value)) |
|---|
| 1560 | (setq first-endtest (null list-value))) |
|---|
| 1561 | (cond ((eq var listvar) |
|---|
| 1562 | ;; The contour of the loop is different because we |
|---|
| 1563 | ;; use the user's variable... |
|---|
| 1564 | `(() (,listvar ,list-step) |
|---|
| 1565 | ,other-endtest () () () ,first-endtest ())) |
|---|
| 1566 | (t (let ((step `(,var ,listvar)) |
|---|
| 1567 | (pseudo `(,listvar ,list-step))) |
|---|
| 1568 | `(,other-endtest ,step () ,pseudo |
|---|
| 1569 | ,@(and (not (eq first-endtest other-endtest)) |
|---|
| 1570 | `(,first-endtest ,step () ,pseudo))))))))))) |
|---|
| 1571 | |
|---|
| 1572 | (defun loop-for-in (var val data-type) |
|---|
| 1573 | (multiple-value-bind (list constantp list-value) |
|---|
| 1574 | (loop-constant-fold-if-possible val) |
|---|
| 1575 | (let ((listvar (gensym "LOOP-LIST-"))) |
|---|
| 1576 | (loop-make-iteration-var var nil data-type) |
|---|
| 1577 | (loop-make-var listvar list 'list) |
|---|
| 1578 | (let ((list-step (loop-list-step listvar))) |
|---|
| 1579 | (let* ((first-endtest `(endp ,listvar)) |
|---|
| 1580 | (other-endtest first-endtest) |
|---|
| 1581 | (step `(,var (car ,listvar))) |
|---|
| 1582 | (pseudo-step `(,listvar ,list-step))) |
|---|
| 1583 | (when (and constantp (listp list-value)) |
|---|
| 1584 | (setq first-endtest (null list-value))) |
|---|
| 1585 | `(,other-endtest ,step () ,pseudo-step |
|---|
| 1586 | ,@(and (not (eq first-endtest other-endtest)) |
|---|
| 1587 | `(,first-endtest ,step () ,pseudo-step)))))))) |
|---|
| 1588 | |
|---|
| 1589 | ;;;; iteration paths |
|---|
| 1590 | |
|---|
| 1591 | (defstruct (loop-path |
|---|
| 1592 | (:copier nil) |
|---|
| 1593 | (:predicate nil)) |
|---|
| 1594 | names |
|---|
| 1595 | preposition-groups |
|---|
| 1596 | inclusive-permitted |
|---|
| 1597 | function |
|---|
| 1598 | user-data) |
|---|
| 1599 | |
|---|
| 1600 | (defun add-loop-path (names function universe |
|---|
| 1601 | &key preposition-groups inclusive-permitted user-data) |
|---|
| 1602 | (declare (type loop-universe universe)) |
|---|
| 1603 | (unless (listp names) |
|---|
| 1604 | (setq names (list names))) |
|---|
| 1605 | (let ((ht (loop-universe-path-keywords universe)) |
|---|
| 1606 | (lp (make-loop-path |
|---|
| 1607 | :names (mapcar #'symbol-name names) |
|---|
| 1608 | :function function |
|---|
| 1609 | :user-data user-data |
|---|
| 1610 | :preposition-groups (mapcar (lambda (x) |
|---|
| 1611 | (if (listp x) x (list x))) |
|---|
| 1612 | preposition-groups) |
|---|
| 1613 | :inclusive-permitted inclusive-permitted))) |
|---|
| 1614 | (dolist (name names) |
|---|
| 1615 | (setf (gethash (symbol-name name) ht) lp)) |
|---|
| 1616 | lp)) |
|---|
| 1617 | |
|---|
| 1618 | ;;; Note: Path functions are allowed to use LOOP-MAKE-VAR, hack |
|---|
| 1619 | ;;; the prologue, etc. |
|---|
| 1620 | (defun loop-for-being (var val data-type) |
|---|
| 1621 | ;; FOR var BEING each/the pathname prep-phrases using-stuff... each/the = |
|---|
| 1622 | ;; EACH or THE. Not clear if it is optional, so I guess we'll warn. |
|---|
| 1623 | (let ((path nil) |
|---|
| 1624 | (data nil) |
|---|
| 1625 | (inclusive nil) |
|---|
| 1626 | (stuff nil) |
|---|
| 1627 | (initial-prepositions nil)) |
|---|
| 1628 | (cond ((loop-tmember val '(:each :the)) (setq path (loop-pop-source))) |
|---|
| 1629 | ((loop-tequal (car *loop-source-code*) :and) |
|---|
| 1630 | (loop-pop-source) |
|---|
| 1631 | (setq inclusive t) |
|---|
| 1632 | (unless (loop-tmember (car *loop-source-code*) |
|---|
| 1633 | '(:its :each :his :her)) |
|---|
| 1634 | (loop-error "~S was found where ITS or EACH expected in LOOP iteration path syntax." |
|---|
| 1635 | (car *loop-source-code*))) |
|---|
| 1636 | (loop-pop-source) |
|---|
| 1637 | (setq path (loop-pop-source)) |
|---|
| 1638 | (setq initial-prepositions `((:in ,val)))) |
|---|
| 1639 | (t (loop-error "unrecognizable LOOP iteration path syntax: missing EACH or THE?"))) |
|---|
| 1640 | (cond ((not (symbolp path)) |
|---|
| 1641 | (loop-error |
|---|
| 1642 | "~S was found where a LOOP iteration path name was expected." |
|---|
| 1643 | path)) |
|---|
| 1644 | ((not (setq data (loop-lookup-keyword path (loop-universe-path-keywords *loop-universe*)))) |
|---|
| 1645 | (loop-error "~S is not the name of a LOOP iteration path." path)) |
|---|
| 1646 | ((and inclusive (not (loop-path-inclusive-permitted data))) |
|---|
| 1647 | (loop-error "\"Inclusive\" iteration is not possible with the ~S LOOP iteration path." path))) |
|---|
| 1648 | (let ((fun (loop-path-function data)) |
|---|
| 1649 | (preps (nconc initial-prepositions |
|---|
| 1650 | (loop-collect-prepositional-phrases |
|---|
| 1651 | (loop-path-preposition-groups data) |
|---|
| 1652 | t))) |
|---|
| 1653 | (user-data (loop-path-user-data data))) |
|---|
| 1654 | (when (symbolp fun) (setq fun (symbol-function fun))) |
|---|
| 1655 | (setq stuff (if inclusive |
|---|
| 1656 | (apply fun var data-type preps :inclusive t user-data) |
|---|
| 1657 | (apply fun var data-type preps user-data)))) |
|---|
| 1658 | (when *loop-named-vars* |
|---|
| 1659 | (loop-error "Unused USING vars: ~S." *loop-named-vars*)) |
|---|
| 1660 | ;; STUFF is now (bindings prologue-forms . stuff-to-pass-back). |
|---|
| 1661 | ;; Protect the system from the user and the user from himself. |
|---|
| 1662 | (unless (member (length stuff) '(6 10)) |
|---|
| 1663 | (loop-error "Value passed back by LOOP iteration path function for path ~S has invalid length." |
|---|
| 1664 | path)) |
|---|
| 1665 | (do ((l (car stuff) (cdr l)) (x)) ((null l)) |
|---|
| 1666 | (if (atom (setq x (car l))) |
|---|
| 1667 | (loop-make-iteration-var x nil nil) |
|---|
| 1668 | (loop-make-iteration-var (car x) (cadr x) (caddr x)))) |
|---|
| 1669 | (setq *loop-prologue* (nconc (reverse (cadr stuff)) *loop-prologue*)) |
|---|
| 1670 | (cddr stuff))) |
|---|
| 1671 | |
|---|
| 1672 | (defun loop-named-var (name) |
|---|
| 1673 | (let ((tem (loop-tassoc name *loop-named-vars*))) |
|---|
| 1674 | (declare (list tem)) |
|---|
| 1675 | (cond ((null tem) (values (gensym) nil)) |
|---|
| 1676 | (t (setq *loop-named-vars* (delete tem *loop-named-vars*)) |
|---|
| 1677 | (values (cdr tem) t))))) |
|---|
| 1678 | |
|---|
| 1679 | (defun loop-collect-prepositional-phrases (preposition-groups |
|---|
| 1680 | &optional |
|---|
| 1681 | using-allowed |
|---|
| 1682 | initial-phrases) |
|---|
| 1683 | (flet ((in-group-p (x group) (car (loop-tmember x group)))) |
|---|
| 1684 | (do ((token nil) |
|---|
| 1685 | (prepositional-phrases initial-phrases) |
|---|
| 1686 | (this-group nil nil) |
|---|
| 1687 | (this-prep nil nil) |
|---|
| 1688 | (disallowed-prepositions |
|---|
| 1689 | (mapcan (lambda (x) |
|---|
| 1690 | (copy-list |
|---|
| 1691 | (find (car x) preposition-groups :test #'in-group-p))) |
|---|
| 1692 | initial-phrases)) |
|---|
| 1693 | (used-prepositions (mapcar #'car initial-phrases))) |
|---|
| 1694 | ((null *loop-source-code*) (nreverse prepositional-phrases)) |
|---|
| 1695 | (declare (symbol this-prep)) |
|---|
| 1696 | (setq token (car *loop-source-code*)) |
|---|
| 1697 | (dolist (group preposition-groups) |
|---|
| 1698 | (when (setq this-prep (in-group-p token group)) |
|---|
| 1699 | (return (setq this-group group)))) |
|---|
| 1700 | (cond (this-group |
|---|
| 1701 | (when (member this-prep disallowed-prepositions) |
|---|
| 1702 | (loop-error |
|---|
| 1703 | (if (member this-prep used-prepositions) |
|---|
| 1704 | "A ~S prepositional phrase occurs multiply for some LOOP clause." |
|---|
| 1705 | "Preposition ~S was used when some other preposition has subsumed it.") |
|---|
| 1706 | token)) |
|---|
| 1707 | (setq used-prepositions (if (listp this-group) |
|---|
| 1708 | (append this-group used-prepositions) |
|---|
| 1709 | (cons this-group used-prepositions))) |
|---|
| 1710 | (loop-pop-source) |
|---|
| 1711 | (push (list this-prep (loop-get-form)) prepositional-phrases)) |
|---|
| 1712 | ((and using-allowed (loop-tequal token 'using)) |
|---|
| 1713 | (loop-pop-source) |
|---|
| 1714 | (do ((z (loop-pop-source) (loop-pop-source)) (tem)) (nil) |
|---|
| 1715 | (when (cadr z) |
|---|
| 1716 | (if (setq tem (loop-tassoc (car z) *loop-named-vars*)) |
|---|
| 1717 | (loop-error |
|---|
| 1718 | "The variable substitution for ~S occurs twice in a USING phrase,~@ |
|---|
| 1719 | with ~S and ~S." |
|---|
| 1720 | (car z) (cadr z) (cadr tem)) |
|---|
| 1721 | (push (cons (car z) (cadr z)) *loop-named-vars*))) |
|---|
| 1722 | (when (or (null *loop-source-code*) |
|---|
| 1723 | (symbolp (car *loop-source-code*))) |
|---|
| 1724 | (return nil)))) |
|---|
| 1725 | (t (return (nreverse prepositional-phrases))))))) |
|---|
| 1726 | |
|---|
| 1727 | ;;;; master sequencer function |
|---|
| 1728 | |
|---|
| 1729 | (defun loop-sequencer (indexv indexv-type |
|---|
| 1730 | variable variable-type |
|---|
| 1731 | sequence-variable sequence-type |
|---|
| 1732 | step-hack default-top |
|---|
| 1733 | prep-phrases) |
|---|
| 1734 | (let ((endform nil) ; form (constant or variable) with limit value |
|---|
| 1735 | (sequencep nil) ; T if sequence arg has been provided |
|---|
| 1736 | (testfn nil) ; endtest function |
|---|
| 1737 | (test nil) ; endtest form |
|---|
| 1738 | (stepby (1+ (or (loop-typed-init indexv-type) 0))) ; our increment |
|---|
| 1739 | (stepby-constantp t) |
|---|
| 1740 | (step nil) ; step form |
|---|
| 1741 | (dir nil) ; direction of stepping: NIL, :UP, :DOWN |
|---|
| 1742 | (inclusive-iteration nil) ; T if include last index |
|---|
| 1743 | (start-given nil) ; T when prep phrase has specified start |
|---|
| 1744 | (start-value nil) |
|---|
| 1745 | (start-constantp nil) |
|---|
| 1746 | (limit-given nil) ; T when prep phrase has specified end |
|---|
| 1747 | (limit-constantp nil) |
|---|
| 1748 | (limit-value nil) |
|---|
| 1749 | ) |
|---|
| 1750 | (flet ((assert-index-for-arithmetic (index) |
|---|
| 1751 | (unless (atom index) |
|---|
| 1752 | (loop-error "Arithmetic index must be an atom.")))) |
|---|
| 1753 | (when variable (loop-make-iteration-var variable nil variable-type)) |
|---|
| 1754 | (do ((l prep-phrases (cdr l)) (prep) (form) (odir)) ((null l)) |
|---|
| 1755 | (setq prep (caar l) form (cadar l)) |
|---|
| 1756 | (case prep |
|---|
| 1757 | ((:of :in) |
|---|
| 1758 | (setq sequencep t) |
|---|
| 1759 | (loop-make-var sequence-variable form sequence-type)) |
|---|
| 1760 | ((:from :downfrom :upfrom) |
|---|
| 1761 | (setq start-given t) |
|---|
| 1762 | (cond ((eq prep :downfrom) (setq dir ':down)) |
|---|
| 1763 | ((eq prep :upfrom) (setq dir ':up))) |
|---|
| 1764 | (multiple-value-setq (form start-constantp start-value) |
|---|
| 1765 | (loop-constant-fold-if-possible form indexv-type)) |
|---|
| 1766 | (assert-index-for-arithmetic indexv) |
|---|
| 1767 | ;; KLUDGE: loop-make-var generates a temporary symbol for |
|---|
| 1768 | ;; indexv if it is NIL. We have to use it to have the index |
|---|
| 1769 | ;; actually count |
|---|
| 1770 | (setq indexv (loop-make-iteration-var indexv form indexv-type))) |
|---|
| 1771 | ((:upto :to :downto :above :below) |
|---|
| 1772 | (cond ((loop-tequal prep :upto) (setq inclusive-iteration |
|---|
| 1773 | (setq dir ':up))) |
|---|
| 1774 | ((loop-tequal prep :to) (setq inclusive-iteration t)) |
|---|
| 1775 | ((loop-tequal prep :downto) (setq inclusive-iteration |
|---|
| 1776 | (setq dir ':down))) |
|---|
| 1777 | ((loop-tequal prep :above) (setq dir ':down)) |
|---|
| 1778 | ((loop-tequal prep :below) (setq dir ':up))) |
|---|
| 1779 | (setq limit-given t) |
|---|
| 1780 | (multiple-value-setq (form limit-constantp limit-value) |
|---|
| 1781 | (loop-constant-fold-if-possible form `(and ,indexv-type real))) |
|---|
| 1782 | (setq endform (if limit-constantp |
|---|
| 1783 | `',limit-value |
|---|
| 1784 | (loop-make-var |
|---|
| 1785 | (gensym "LOOP-LIMIT-") form |
|---|
| 1786 | `(and ,indexv-type real))))) |
|---|
| 1787 | (:by |
|---|
| 1788 | (multiple-value-setq (form stepby-constantp stepby) |
|---|
| 1789 | (loop-constant-fold-if-possible form `(and ,indexv-type (real (0))))) |
|---|
| 1790 | (unless stepby-constantp |
|---|
| 1791 | (loop-make-var (setq stepby (gensym "LOOP-STEP-BY-")) |
|---|
| 1792 | form |
|---|
| 1793 | `(and ,indexv-type (real (0))) |
|---|
| 1794 | nil t))) |
|---|
| 1795 | (t (loop-error |
|---|
| 1796 | "~S invalid preposition in sequencing or sequence path;~@ |
|---|
| 1797 | maybe invalid prepositions were specified in iteration path descriptor?" |
|---|
| 1798 | prep))) |
|---|
| 1799 | (when (and odir dir (not (eq dir odir))) |
|---|
| 1800 | (loop-error "conflicting stepping directions in LOOP sequencing path")) |
|---|
| 1801 | (setq odir dir)) |
|---|
| 1802 | (when (and sequence-variable (not sequencep)) |
|---|
| 1803 | (loop-error "missing OF or IN phrase in sequence path")) |
|---|
| 1804 | ;; Now fill in the defaults. |
|---|
| 1805 | (if start-given |
|---|
| 1806 | (when limit-given |
|---|
| 1807 | ;; if both start and limit are given, they had better both |
|---|
| 1808 | ;; be REAL. We already enforce the REALness of LIMIT, |
|---|
| 1809 | ;; above; here's the KLUDGE to enforce the type of START. |
|---|
| 1810 | (flet ((type-declaration-of (x) |
|---|
| 1811 | (and (eq (car x) 'type) (caddr x)))) |
|---|
| 1812 | (let ((decl (find indexv *loop-declarations* |
|---|
| 1813 | :key #'type-declaration-of)) |
|---|
| 1814 | (%decl (find indexv *loop-declarations* |
|---|
| 1815 | :key #'type-declaration-of |
|---|
| 1816 | :from-end t))) |
|---|
| 1817 | #+sbcl (aver (eq decl %decl)) |
|---|
| 1818 | #-sbcl (declare (ignore %decl)) |
|---|
| 1819 | (setf (cadr decl) |
|---|
| 1820 | `(and real ,(cadr decl)))))) |
|---|
| 1821 | ;; default start |
|---|
| 1822 | ;; DUPLICATE KLUDGE: loop-make-var generates a temporary |
|---|
| 1823 | ;; symbol for indexv if it is NIL. See also the comment in |
|---|
| 1824 | ;; the (:from :downfrom :upfrom) case |
|---|
| 1825 | (progn |
|---|
| 1826 | (assert-index-for-arithmetic indexv) |
|---|
| 1827 | (setq indexv |
|---|
| 1828 | (loop-make-iteration-var |
|---|
| 1829 | indexv |
|---|
| 1830 | (setq start-constantp t |
|---|
| 1831 | start-value (or (loop-typed-init indexv-type) 0)) |
|---|
| 1832 | `(and ,indexv-type real))))) |
|---|
| 1833 | (cond ((member dir '(nil :up)) |
|---|
| 1834 | (when (or limit-given default-top) |
|---|
| 1835 | (unless limit-given |
|---|
| 1836 | (loop-make-var (setq endform (gensym "LOOP-SEQ-LIMIT-")) |
|---|
| 1837 | nil |
|---|
| 1838 | indexv-type) |
|---|
| 1839 | (push `(setq ,endform ,default-top) *loop-prologue*)) |
|---|
| 1840 | (setq testfn (if inclusive-iteration '> '>=))) |
|---|
| 1841 | (setq step (if (eql stepby 1) `(1+ ,indexv) `(+ ,indexv ,stepby)))) |
|---|
| 1842 | (t (unless start-given |
|---|
| 1843 | (unless default-top |
|---|
| 1844 | (loop-error "don't know where to start stepping")) |
|---|
| 1845 | (push `(setq ,indexv (1- ,default-top)) *loop-prologue*)) |
|---|
| 1846 | (when (and default-top (not endform)) |
|---|
| 1847 | (setq endform (loop-typed-init indexv-type) |
|---|
| 1848 | inclusive-iteration t)) |
|---|
| 1849 | (when endform (setq testfn (if inclusive-iteration '< '<=))) |
|---|
| 1850 | (setq step |
|---|
| 1851 | (if (eql stepby 1) `(1- ,indexv) `(- ,indexv ,stepby))))) |
|---|
| 1852 | (when testfn |
|---|
| 1853 | (setq test |
|---|
| 1854 | `(,testfn ,indexv ,endform))) |
|---|
| 1855 | (when step-hack |
|---|
| 1856 | (setq step-hack |
|---|
| 1857 | `(,variable ,step-hack))) |
|---|
| 1858 | (let ((first-test test) (remaining-tests test)) |
|---|
| 1859 | (when (and stepby-constantp start-constantp limit-constantp |
|---|
| 1860 | (realp start-value) (realp limit-value)) |
|---|
| 1861 | (when (setq first-test |
|---|
| 1862 | (funcall (symbol-function testfn) |
|---|
| 1863 | start-value |
|---|
| 1864 | limit-value)) |
|---|
| 1865 | (setq remaining-tests t))) |
|---|
| 1866 | `(() (,indexv ,step) |
|---|
| 1867 | ,remaining-tests ,step-hack () () ,first-test ,step-hack))))) |
|---|
| 1868 | |
|---|
| 1869 | ;;;; interfaces to the master sequencer |
|---|
| 1870 | |
|---|
| 1871 | (defun loop-for-arithmetic (var val data-type kwd) |
|---|
| 1872 | (loop-sequencer |
|---|
| 1873 | var (loop-check-data-type data-type 'number) |
|---|
| 1874 | nil nil nil nil nil nil |
|---|
| 1875 | (loop-collect-prepositional-phrases |
|---|
| 1876 | '((:from :upfrom :downfrom) (:to :upto :downto :above :below) (:by)) |
|---|
| 1877 | nil (list (list kwd val))))) |
|---|
| 1878 | |
|---|
| 1879 | (defun loop-sequence-elements-path (variable data-type prep-phrases |
|---|
| 1880 | &key |
|---|
| 1881 | fetch-function |
|---|
| 1882 | size-function |
|---|
| 1883 | sequence-type |
|---|
| 1884 | element-type) |
|---|
| 1885 | (multiple-value-bind (indexv) (loop-named-var 'index) |
|---|
| 1886 | (let ((sequencev (loop-named-var 'sequence))) |
|---|
| 1887 | (list* nil nil ; dummy bindings and prologue |
|---|
| 1888 | (loop-sequencer |
|---|
| 1889 | indexv 'fixnum |
|---|
| 1890 | variable (or data-type element-type) |
|---|
| 1891 | sequencev sequence-type |
|---|
| 1892 | `(,fetch-function ,sequencev ,indexv) |
|---|
| 1893 | `(,size-function ,sequencev) |
|---|
| 1894 | prep-phrases))))) |
|---|
| 1895 | |
|---|
| 1896 | ;;;; builtin LOOP iteration paths |
|---|
| 1897 | |
|---|
| 1898 | #|| |
|---|
| 1899 | (loop for v being the hash-values of ht do (print v)) |
|---|
| 1900 | (loop for k being the hash-keys of ht do (print k)) |
|---|
| 1901 | (loop for v being the hash-values of ht using (hash-key k) do (print (list k v))) |
|---|
| 1902 | (loop for k being the hash-keys of ht using (hash-value v) do (print (list k v))) |
|---|
| 1903 | ||# |
|---|
| 1904 | |
|---|
| 1905 | (defun loop-hash-table-iteration-path (variable data-type prep-phrases |
|---|
| 1906 | &key which) |
|---|
| 1907 | (declare (type (member :hash-key :hash-value) which)) |
|---|
| 1908 | (cond ((or (cdr prep-phrases) (not (member (caar prep-phrases) '(:in :of)))) |
|---|
| 1909 | (loop-error "too many prepositions!")) |
|---|
| 1910 | ((null prep-phrases) |
|---|
| 1911 | (loop-error "missing OF or IN in ~S iteration path"))) |
|---|
| 1912 | (let ((ht-var (gensym "LOOP-HASHTAB-")) |
|---|
| 1913 | (next-fn (gensym "LOOP-HASHTAB-NEXT-")) |
|---|
| 1914 | (dummy-predicate-var nil) |
|---|
| 1915 | (post-steps nil)) |
|---|
| 1916 | (multiple-value-bind (other-var other-p) |
|---|
| 1917 | (loop-named-var (ecase which |
|---|
| 1918 | (:hash-key 'hash-value) |
|---|
| 1919 | (:hash-value 'hash-key))) |
|---|
| 1920 | ;; @@@@ LOOP-NAMED-VAR returns a second value of T if the name was |
|---|
| 1921 | ;; actually specified, so clever code can throw away the GENSYM'ed-up |
|---|
| 1922 | ;; variable if it isn't really needed. |
|---|
| 1923 | (unless other-p |
|---|
| 1924 | (push `(ignorable ,other-var) *loop-declarations*)) |
|---|
| 1925 | ;; The following is for those implementations in which we cannot put |
|---|
| 1926 | ;; dummy NILs into MULTIPLE-VALUE-SETQ variable lists. |
|---|
| 1927 | (setq other-p t |
|---|
| 1928 | dummy-predicate-var (loop-when-it-var)) |
|---|
| 1929 | (let* ((key-var nil) |
|---|
| 1930 | (val-var nil) |
|---|
| 1931 | (variable (or variable (gensym "LOOP-HASH-VAR-TEMP-"))) |
|---|
| 1932 | (bindings `((,variable nil ,data-type) |
|---|
| 1933 | (,ht-var ,(cadar prep-phrases)) |
|---|
| 1934 | ,@(and other-p other-var `((,other-var nil)))))) |
|---|
| 1935 | (ecase which |
|---|
| 1936 | (:hash-key (setq key-var variable |
|---|
| 1937 | val-var (and other-p other-var))) |
|---|
| 1938 | (:hash-value (setq key-var (and other-p other-var) |
|---|
| 1939 | val-var variable))) |
|---|
| 1940 | (push `(with-hash-table-iterator (,next-fn ,ht-var)) *loop-wrappers*) |
|---|
| 1941 | (when (or (consp key-var) data-type) |
|---|
| 1942 | (setq post-steps |
|---|
| 1943 | `(,key-var ,(setq key-var (gensym "LOOP-HASH-KEY-TEMP-")) |
|---|
| 1944 | ,@post-steps)) |
|---|
| 1945 | (push `(,key-var nil) bindings)) |
|---|
| 1946 | (when (or (consp val-var) data-type) |
|---|
| 1947 | (setq post-steps |
|---|
| 1948 | `(,val-var ,(setq val-var (gensym "LOOP-HASH-VAL-TEMP-")) |
|---|
| 1949 | ,@post-steps)) |
|---|
| 1950 | (push `(,val-var nil) bindings)) |
|---|
| 1951 | (push `(ignorable ,dummy-predicate-var) *loop-declarations*) |
|---|
| 1952 | `(,bindings ;bindings |
|---|
| 1953 | () ;prologue |
|---|
| 1954 | () ;pre-test |
|---|
| 1955 | () ;parallel steps |
|---|
| 1956 | (not (multiple-value-setq (,dummy-predicate-var ,key-var ,val-var) |
|---|
| 1957 | (,next-fn))) ;post-test |
|---|
| 1958 | ,post-steps))))) |
|---|
| 1959 | |
|---|
| 1960 | (defun loop-package-symbols-iteration-path (variable data-type prep-phrases |
|---|
| 1961 | &key symbol-types) |
|---|
| 1962 | (cond ((and prep-phrases (cdr prep-phrases)) |
|---|
| 1963 | (loop-error "Too many prepositions!")) |
|---|
| 1964 | ((and prep-phrases (not (member (caar prep-phrases) '(:in :of)))) |
|---|
| 1965 | (loop-error "Unknown preposition ~S." (caar prep-phrases)))) |
|---|
| 1966 | (unless (symbolp variable) |
|---|
| 1967 | (loop-error "Destructuring is not valid for package symbol iteration.")) |
|---|
| 1968 | (let ((pkg-var (gensym "LOOP-PKGSYM-")) |
|---|
| 1969 | (next-fn (gensym "LOOP-PKGSYM-NEXT-")) |
|---|
| 1970 | (variable (or variable (gensym "LOOP-PKGSYM-VAR-"))) |
|---|
| 1971 | (package (or (cadar prep-phrases) '*package*))) |
|---|
| 1972 | (push `(with-package-iterator (,next-fn ,pkg-var ,@symbol-types)) |
|---|
| 1973 | *loop-wrappers*) |
|---|
| 1974 | (push `(ignorable ,(loop-when-it-var)) *loop-declarations*) |
|---|
| 1975 | `(((,variable nil ,data-type) (,pkg-var ,package)) |
|---|
| 1976 | () |
|---|
| 1977 | () |
|---|
| 1978 | () |
|---|
| 1979 | (not (multiple-value-setq (,(loop-when-it-var) |
|---|
| 1980 | ,variable) |
|---|
| 1981 | (,next-fn))) |
|---|
| 1982 | ()))) |
|---|
| 1983 | |
|---|
| 1984 | ;;;; ANSI LOOP |
|---|
| 1985 | |
|---|
| 1986 | (defun make-ansi-loop-universe (extended-p) |
|---|
| 1987 | (let ((w (make-standard-loop-universe |
|---|
| 1988 | :keywords '((named (loop-do-named)) |
|---|
| 1989 | (initially (loop-do-initially)) |
|---|
| 1990 | (finally (loop-do-finally)) |
|---|
| 1991 | (do (loop-do-do)) |
|---|
| 1992 | (doing (loop-do-do)) |
|---|
| 1993 | (return (loop-do-return)) |
|---|
| 1994 | (collect (loop-list-collection list)) |
|---|
| 1995 | (collecting (loop-list-collection list)) |
|---|
| 1996 | (append (loop-list-collection append)) |
|---|
| 1997 | (appending (loop-list-collection append)) |
|---|
| 1998 | (nconc (loop-list-collection nconc)) |
|---|
| 1999 | (nconcing (loop-list-collection nconc)) |
|---|
| 2000 | (count (loop-sum-collection count |
|---|
| 2001 | real |
|---|
| 2002 | fixnum)) |
|---|
| 2003 | (counting (loop-sum-collection count |
|---|
| 2004 | real |
|---|
| 2005 | fixnum)) |
|---|
| 2006 | (sum (loop-sum-collection sum number number)) |
|---|
| 2007 | (summing (loop-sum-collection sum number number)) |
|---|
| 2008 | (maximize (loop-maxmin-collection max)) |
|---|
| 2009 | (minimize (loop-maxmin-collection min)) |
|---|
| 2010 | (maximizing (loop-maxmin-collection max)) |
|---|
| 2011 | (minimizing (loop-maxmin-collection min)) |
|---|
| 2012 | (always (loop-do-always t nil)) ; Normal, do always |
|---|
| 2013 | (never (loop-do-always t t)) ; Negate test on always. |
|---|
| 2014 | (thereis (loop-do-thereis t)) |
|---|
| 2015 | (while (loop-do-while nil :while)) ; Normal, do while |
|---|
| 2016 | (until (loop-do-while t :until)) ;Negate test on while |
|---|
| 2017 | (when (loop-do-if when nil)) ; Normal, do when |
|---|
| 2018 | (if (loop-do-if if nil)) ; synonymous |
|---|
| 2019 | (unless (loop-do-if unless t)) ; Negate test on when |
|---|
| 2020 | (with (loop-do-with)) |
|---|
| 2021 | (repeat (loop-do-repeat))) |
|---|
| 2022 | :for-keywords '((= (loop-ansi-for-equals)) |
|---|
| 2023 | (across (loop-for-across)) |
|---|
| 2024 | (in (loop-for-in)) |
|---|
| 2025 | (on (loop-for-on)) |
|---|
| 2026 | (from (loop-for-arithmetic :from)) |
|---|
| 2027 | (downfrom (loop-for-arithmetic :downfrom)) |
|---|
| 2028 | (upfrom (loop-for-arithmetic :upfrom)) |
|---|
| 2029 | (below (loop-for-arithmetic :below)) |
|---|
| 2030 | (above (loop-for-arithmetic :above)) |
|---|
| 2031 | (to (loop-for-arithmetic :to)) |
|---|
| 2032 | (upto (loop-for-arithmetic :upto)) |
|---|
| 2033 | (downto (loop-for-arithmetic :downto)) |
|---|
| 2034 | (by (loop-for-arithmetic :by)) |
|---|
| 2035 | (being (loop-for-being))) |
|---|
| 2036 | :iteration-keywords '((for (loop-do-for)) |
|---|
| 2037 | (as (loop-do-for))) |
|---|
| 2038 | :type-symbols '(array atom bignum bit bit-vector character |
|---|
| 2039 | compiled-function complex cons double-float |
|---|
| 2040 | fixnum float function hash-table integer |
|---|
| 2041 | keyword list long-float nil null number |
|---|
| 2042 | package pathname random-state ratio rational |
|---|
| 2043 | readtable sequence short-float simple-array |
|---|
| 2044 | simple-bit-vector simple-string simple-vector |
|---|
| 2045 | single-float standard-char stream string |
|---|
| 2046 | base-char symbol t vector) |
|---|
| 2047 | :type-keywords nil |
|---|
| 2048 | :ansi (if extended-p :extended t)))) |
|---|
| 2049 | (add-loop-path '(hash-key hash-keys) 'loop-hash-table-iteration-path w |
|---|
| 2050 | :preposition-groups '((:of :in)) |
|---|
| 2051 | :inclusive-permitted nil |
|---|
| 2052 | :user-data '(:which :hash-key)) |
|---|
| 2053 | (add-loop-path '(hash-value hash-values) 'loop-hash-table-iteration-path w |
|---|
| 2054 | :preposition-groups '((:of :in)) |
|---|
| 2055 | :inclusive-permitted nil |
|---|
| 2056 | :user-data '(:which :hash-value)) |
|---|
| 2057 | (add-loop-path '(symbol symbols) 'loop-package-symbols-iteration-path w |
|---|
| 2058 | :preposition-groups '((:of :in)) |
|---|
| 2059 | :inclusive-permitted nil |
|---|
| 2060 | :user-data '(:symbol-types (:internal |
|---|
| 2061 | :external |
|---|
| 2062 | :inherited))) |
|---|
| 2063 | (add-loop-path '(external-symbol external-symbols) |
|---|
| 2064 | 'loop-package-symbols-iteration-path w |
|---|
| 2065 | :preposition-groups '((:of :in)) |
|---|
| 2066 | :inclusive-permitted nil |
|---|
| 2067 | :user-data '(:symbol-types (:external))) |
|---|
| 2068 | (add-loop-path '(present-symbol present-symbols) |
|---|
| 2069 | 'loop-package-symbols-iteration-path w |
|---|
| 2070 | :preposition-groups '((:of :in)) |
|---|
| 2071 | :inclusive-permitted nil |
|---|
| 2072 | :user-data '(:symbol-types (:internal |
|---|
| 2073 | :external))) |
|---|
| 2074 | w)) |
|---|
| 2075 | |
|---|
| 2076 | (defparameter *loop-ansi-universe* |
|---|
| 2077 | (make-ansi-loop-universe nil)) |
|---|
| 2078 | |
|---|
| 2079 | (defun loop-standard-expansion (keywords-and-forms environment universe) |
|---|
| 2080 | (if (and keywords-and-forms (symbolp (car keywords-and-forms))) |
|---|
| 2081 | (loop-translate keywords-and-forms environment universe) |
|---|
| 2082 | (let ((tag (gensym))) |
|---|
| 2083 | `(block nil (tagbody ,tag (progn ,@keywords-and-forms) (go ,tag)))))) |
|---|
| 2084 | |
|---|
| 2085 | (defmacro loop (&environment env &rest keywords-and-forms) |
|---|
| 2086 | (loop-standard-expansion keywords-and-forms env *loop-ansi-universe*)) |
|---|
| 2087 | |
|---|
| 2088 | (defmacro loop-finish () |
|---|
| 2089 | "Cause the iteration to terminate \"normally\", the same as implicit |
|---|
| 2090 | termination by an iteration driving clause, or by use of WHILE or |
|---|
| 2091 | UNTIL -- the epilogue code (if any) will be run, and any implicitly |
|---|
| 2092 | collected result will be returned as the value of the LOOP." |
|---|
| 2093 | '(go end-loop)) |
|---|
| 2094 | |
|---|
| 2095 | (provide "LOOP") |
|---|