Made PS LOOP conform to LOOP's semantics regarding parallel FOR clauses. A clause...
[clinton/parenscript.git] / src / lib / ps-loop.lisp
CommitLineData
e4ee1b86
DG
1(in-package :parenscript)
2
e4ee1b86
DG
3(defun complex-js-expr? (expr)
4 (if (symbolp expr)
70c34384
DG
5 (or (find #\. (symbol-name expr))
6 (not (eq (ps-macroexpand expr) expr)))
e4ee1b86
DG
7 (consp expr)))
8
e4ee1b86 9(defvar *loop-keywords*
a3939827 10 '(:for :do :when :unless :initially :finally :first-time :last-time :while :until
70c34384 11 :from :to :below :downto :above :by :in :across :on :index := :then :sum :collect
578e8565 12 :count :minimize :maximize :into :repeat))
e4ee1b86
DG
13
14(defun normalize-loop-keywords (args)
512017c6
DG
15 (mapcar
16 (lambda (x)
17 (or (find-if (lambda (key) (and (symbolp x) (equal (symbol-name x) (symbol-name key))))
18 *loop-keywords*)
19 x))
20 args))
e4ee1b86 21
70c34384
DG
22(defun reduce-function-symbol (sym)
23 (if (and (consp sym) (eq 'function (first sym)))
24 (second sym)
25 sym))
26
2100b87b
DG
27(defun err (expected got)
28 (error "PS-LOOP expected ~a, got ~a." expected got))
29
30(defclass loop-state ()
31 ((tokens :initarg :tokens :accessor tokens)
32 (iterations :initform nil :accessor iterations)
33 (prologue :initform nil :accessor prologue)
34 (destructurings :initform nil :accessor destructurings)
35 (initially :initform nil :accessor initially)
36 (finally :initform nil :accessor finally)
37 (during-first :initform nil :accessor during-first)
56ae43a5 38 (first-guard :initform nil :accessor first-guard)
2100b87b 39 (during-last :initform nil :accessor during-last)
56ae43a5 40 (last-guard :initform nil :accessor last-guard)
2100b87b
DG
41 (default-accum-var :initform nil :accessor default-accum-var)
42 (default-accum-kind :initform nil :accessor default-accum-kind)
43 (body :initform nil :accessor body)))
44
45(defun nreverse-loop-state (state)
46 (macrolet ((rev% (&rest accs)
47 (cons 'progn (loop :for a :in accs :collect `(setf (,a state) (nreverse (,a state)))))))
48 (rev% iterations prologue initially finally during-first during-last)
49 (let ((body (nreverse (body state))))
50 (loop :for (list var) :in (destructurings state) :do
51 (setf body `((destructuring-bind ,list ,var ,@body))))
52 (setf (body state) body)))
53 state)
54
55(defun push-tokens (state toks)
56 (setf (tokens state) (append toks (tokens state))))
57
58(defun peek (state)
59 (car (tokens state)))
60
61(defun eat (state &optional what tag)
62 (case what
63 (:if (when (eq (peek state) tag)
64 (eat state)
65 (eat state)))
66 (:progn (cons 'progn (loop :collect (if (consp (peek state))
67 (eat state)
68 (err "a compound form" (peek state)))
69 :until (atom (peek state)))))
70 (otherwise (let ((tok (pop (tokens state))))
71 (when (and (eq what :atom) (not (atom tok)))
72 (err "an atom" tok))
73 tok))))
74
75(defmacro with-local-var ((name expr state) &body body)
76 (once-only (expr)
77 `(let ((,name (aif (and (complex-js-expr? ,expr) (ps-gensym))
78 (progn (push (list 'var it ,expr) (prologue ,state))
79 it)
80 ,expr)))
81 ,@body)))
82
83(defun for-from (var state)
84 (let ((start (eat state))
85 (op '+)
86 (test-op nil)
87 (by nil)
88 (end nil))
89 (loop while (member (peek state) '(:to :below :downto :above :by)) do
90 (let ((term (eat state)))
91 (if (eq term :by)
92 (setf by (eat state))
93 (setf op (case term ((:downto :above) '-) (otherwise '+))
94 test-op (case term (:to '>) (:below '>=) (:downto '<) (:above '<=))
95 end (eat state)))))
96 (let ((test (when test-op
97 (with-local-var (v end state)
98 (list test-op var v)))))
99 (push `(,var ,start (,op ,var ,(or by 1)) ,test) (iterations state)))))
100
101(defun for-= (var state)
102 (let ((start (eat state))
103 (then (eat state :if :then)))
104 (push (list var start (or then start) nil) (iterations state))))
105
106(defun for-in (var state)
107 (with-local-var (arr (eat state) state)
108 (let ((index (or (eat state :if :index) (ps-gensym))))
109 (push-tokens state `(,index :from 0 :below (length ,arr)
110 ,var := (aref ,arr ,index)))
111 (for-clause state)
112 (for-clause state))))
113
114(defun for-on (var state)
115 (with-local-var (arr (eat state) state)
116 (let ((by (aif (eat state :if :by)
117 `(,(reduce-function-symbol it) ,var)
118 `((@ ,var :slice) 1))))
119 (push-tokens state `(,var := ,arr :then ,by))
120 (for-clause state)
121 ;; set the end-test
122 (setf (fourth (car (iterations state))) `(or (null ,var) (= (length ,var) 0))))))
123
124(defun for-clause (state)
125 (let* ((place (eat state))
126 (var (when (atom place) place))
127 (varlist (unless var place))
128 (term (eat state :atom)))
129 (when varlist
130 (when (eq term :from)
131 (err "an atom after FROM" varlist))
132 (setf var (ps-gensym))
133 (push (list varlist var) (destructurings state)))
134 (case term
135 (:from (for-from var state))
136 (:= (for-= var state))
137 ((:in :across) (for-in var state))
138 (:on (for-on var state))
139 (otherwise (error "FOR ~s ~s is not valid in PS-LOOP." var term)))))
140
141(defun accumulate (kind term var state)
142 (when (null var)
143 (when (and (default-accum-kind state) (not (eq kind (default-accum-kind state))))
144 (error "PS-LOOP encountered illegal ~a: ~a was already declared, and there can only be one kind of default accumulation per loop." kind (default-accum-kind state)))
145 (unless (default-accum-var state)
146 (setf (default-accum-var state)
147 (ps-gensym (case kind
148 (:minimize 'min)
149 (:maximize 'max)
150 (t kind))))
151 (setf (default-accum-kind state) kind))
152 (setf var (default-accum-var state)))
153 (let ((initial (case kind
154 ((:sum :count) 0)
155 ((:maximize :minimize) nil)
156 (:collect '(array)))))
157 (pushnew `(var ,var ,initial) (prologue state) :key #'second))
158 (case kind
159 (:sum `(incf ,var ,term))
160 (:count `(unless (null ,term) (incf ,var)))
161 (:minimize `(setf ,var (if (null ,var) ,term (min ,var ,term))))
162 (:maximize `(setf ,var (if (null ,var) ,term (max ,var ,term))))
163 (:collect `((@ ,var :push) ,term))))
164
56ae43a5
DG
165(defun first-time-clause (state)
166 (push (eat state :progn) (during-first state))
167 (unless (first-guard state)
168 (setf (first-guard state) (ps-gensym))))
169
170(defun last-time-clause (state)
171 (push (eat state :progn) (during-last state))
172 (unless (last-guard state)
173 (setf (last-guard state) (ps-gensym))))
174
2100b87b
DG
175(defun repeat-clause (state)
176 (let ((index (ps-gensym)))
177 (setf (tokens state) (append `(,index :from 0 :below ,(eat state)) (tokens state)))
178 (for-clause state)))
179
180(defun body-clause (term state)
181 (case term
182 ((:when :unless) (list (intern (symbol-name term))
183 (eat state)
184 (body-clause (eat state :atom) state)))
185 ((:sum :collect :count :minimize :maximize) (accumulate term (eat state) (eat state :if :into) state))
186 (:do (eat state :progn))
187 (otherwise (err "a PS-LOOP keyword" term))))
188
189(defun clause (state)
190 (let ((term (eat state :atom)))
191 (case term
192 (:for (for-clause state))
193 (:repeat (repeat-clause state))
194 (:while (push `(unless ,(eat state) break) (body state)))
195 (:until (push `(when ,(eat state) break) (body state)))
196 (:initially (push (eat state :progn) (initially state)))
197 (:finally (push (eat state :progn) (finally state)))
56ae43a5
DG
198 (:first-time (first-time-clause state))
199 (:last-time (last-time-clause state))
2100b87b
DG
200 (otherwise (push (body-clause term state) (body state))))))
201
a3939827 202(defun parse-ps-loop (terms)
2100b87b
DG
203 (if (null terms)
204 (err "loop definition" nil)
205 (let ((state (make-instance 'loop-state :tokens terms)))
206 (loop :while (tokens state) :do (clause state))
207 (nreverse-loop-state state))))
208
56ae43a5
DG
209(defun multiple-fors? (loop)
210 (> (length (iterations loop)) 1))
211
212(defun inits (loop)
2100b87b
DG
213 (mapcar (lambda (x) (subseq x 0 2)) (iterations loop)))
214
56ae43a5 215(defun steps (loop)
2100b87b
DG
216 (mapcar (lambda (x) `(setf ,(first x) ,(third x))) (iterations loop)))
217
218(defun end-test (loop)
56ae43a5
DG
219 (aif (loop :for (nil nil nil test) :in (iterations loop)
220 :when test :collect test)
2100b87b
DG
221 (if (cdr it)
222 (list 'not (cons 'or it))
223 (cons 'not it))
56ae43a5
DG
224 t))
225
226(defun inner-body (loop)
227 (if (multiple-fors? loop)
228 (append (body loop)
229 (loop :for (var nil step test) :in (iterations loop)
230 :collect `(setf ,var ,step)
231 :when test :collect `(when ,test (break))))
232 (body loop)))
233
234(defun the-actual-loop (loop)
235 (let ((body `(,@(awhen (during-first loop)
236 `((when ,(first-guard loop)
237 ,@it
238 (setf ,(first-guard loop) nil))))
239 ,@(inner-body loop)
240 ,@(when (during-last loop)
241 `((setf ,(last-guard loop) t))))))
242 (if (multiple-fors? loop)
243 `(while t ,@body)
244 `(for ,(inits loop) (,(end-test loop)) ,(steps loop) ,@body))))
245
246(defun the-loop-form (loop)
247 (let ((form `(progn
248 ,@(initially loop)
249 ,(the-actual-loop loop)
250 ,@(awhen (during-last loop)
251 `((when ,(last-guard loop) ,@it)))
252 ,@(finally loop))))
253 (when (multiple-fors? loop)
254 (loop :for (var init nil test) :in (reverse (iterations loop))
255 :when test :do (setf form `(unless ,test ,form))
256 :do (setf form `(let ((,var ,init)) ,form))))
257 form))
e4ee1b86
DG
258
259(defpsmacro loop (&rest args)
56ae43a5 260 (let ((loop (parse-ps-loop (normalize-loop-keywords args))))
2100b87b 261 `(,@(if (default-accum-var loop) '(with-lambda ()) '(progn))
56ae43a5
DG
262 ,@(when (during-first loop) `((var ,(first-guard loop) t)))
263 ,@(when (during-last loop) `((var ,(last-guard loop) nil)))
2100b87b 264 ,@(prologue loop)
56ae43a5 265 ,(the-loop-form loop)
2100b87b 266 ,@(when (default-accum-var loop) `((return ,(default-accum-var loop)))))))