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") |
---|