Commit | Line | Data |
---|---|---|
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))))))) |