1 (in-package :parenscript
)
3 (defun complex-js-expr?
(expr)
5 (or (find #\.
(symbol-name expr
))
6 (not (eq (ps-macroexpand expr
) expr
)))
9 (defvar *loop-keywords
*
10 '(:for
:do
:when
:unless
:initially
:finally
:first-time
:last-time
:while
:until
11 :from
:to
:below
:downto
:above
:by
:in
:across
:on
:index
:= :then
:sum
:collect
12 :count
:minimize
:maximize
:into
:repeat
))
14 (defun normalize-loop-keywords (args)
17 (or (find-if (lambda (key) (and (symbolp x
) (equal (symbol-name x
) (symbol-name key
))))
22 (defun reduce-function-symbol (sym)
23 (if (and (consp sym
) (eq 'function
(first sym
)))
27 (defun parse-ps-loop (terms)
29 init-step-forms end-test-forms
32 default-accum-var default-accum-kind
34 (macrolet ((with-local-var ((name expr
) &body body
)
36 `(let ((,name
(aif (and (complex-js-expr?
,expr
) (ps-gensym))
37 (progn (push (list 'var it
,expr
) prologue
)
46 (error "PS-LOOP expected ~a, got ~a." expected got
))
47 (consume (&optional what
)
48 (let ((term (pop terms
)))
49 (when (and what
(not (eq what term
)))
55 (err "an atom" (next))))
57 (cons 'progn
(loop :collect
(if (consp (next))
59 (err "a compound form" (next)))
60 :until
(atom (next)))))
65 (accumulate (kind term var
)
67 (when (and default-accum-kind
(not (eq kind default-accum-kind
)))
68 (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
))
69 (unless default-accum-var
70 (setf default-accum-var
(ps-gensym (case kind
74 default-accum-kind kind
))
75 (setf var default-accum-var
))
76 (let ((initial (case kind
78 ((:maximize
:minimize
) nil
)
79 (:collect
'(array)))))
80 (pushnew `(var ,var
,initial
) prologue
:key
#'second
))
82 (:sum
`(incf ,var
,term
))
83 (:count
`(unless (null ,term
) (incf ,var
)))
84 (:minimize
`(setf ,var
(if (null ,var
) ,term
(min ,var
,term
))))
85 (:maximize
`(setf ,var
(if (null ,var
) ,term
(max ,var
,term
))))
86 (:collect
`((@ ,var
:push
) ,term
))))
89 ((:when
:unless
) (list (intern (symbol-name term
))
91 (body-clause (consume-atom))))
92 ((:sum
:collect
:count
:minimize
:maximize
) (accumulate term
(consume) (consume-if :into
)))
94 (otherwise (err "a PS-LOOP keyword" term
))))
96 (let ((start (consume))
101 (loop while
(member (next) '(:to
:below
:downto
:above
:by
)) do
102 (let ((term (consume)))
105 (setf op
(case term
((:downto
:above
) '-
) (otherwise '+))
106 test
(case term
(:to
'>) (:below
'>=) (:downto
'<) (:above
'<=))
108 (push `(,var
,start
(,op
,var
,(or by
1))) init-step-forms
)
110 (with-local-var (end-var end
)
111 (push (list test var end-var
) end-test-forms
)))))
113 (let ((start (consume))
114 (then (consume-if :then
)))
115 (push (list var start
(or then start
)) init-step-forms
)))
117 (with-local-var (arr (consume))
118 (let* ((index (or (consume-if :index
) (ps-gensym)))
119 (equiv `(:for
,index
:from
0 :below
(length ,arr
)
120 :for
,var
:= (aref ,arr
,index
))))
121 (setf terms
(append equiv terms
))
125 (with-local-var (arr (consume))
126 (push `(or (null ,var
) (= (length ,var
) 0)) end-test-forms
)
127 (let* ((by (aif (consume-if :by
)
128 `(,(reduce-function-symbol it
) ,var
)
129 `((@ ,var
:slice
) 1)))
130 (equiv `(:for
,var
:= ,arr
:then
,by
)))
131 (setf terms
(append equiv terms
))
134 (let* ((place (consume))
135 (var (when (atom place
) place
))
136 (varlist (unless var place
))
137 (term (consume-atom)))
139 (when (eq term
:from
)
140 (err "an atom after FOR" varlist
))
141 (setf var
(ps-gensym))
142 (push (list varlist var
) destructurings
))
144 (:from
(for-from var
))
146 ((:in
:across
) (for-in var
))
148 (otherwise (error "FOR ~s ~s is not valid in PS-LOOP." var term
)))))
150 (let ((index (ps-gensym)))
151 (setf terms
(append `(:for
,index
:from
0 :below
,(consume)) terms
))
154 (let ((term (consume-atom)))
157 (:repeat
(repeat-clause))
158 (:while
(push `(unless ,(consume) break
) body
))
159 (:until
(push `(when ,(consume) break
) body
))
160 (:initially
(push (consume-progn) initially
))
161 (:finally
(push (consume-progn) finally
))
162 (:first-time
(push (consume-progn) first-time
))
163 (:last-time
(push (consume-progn) last-time
))
164 (otherwise (push (body-clause term
) body
))))))
166 (loop :while terms
:do
(clause))
167 (err "loop definition" nil
)))
169 (aif (nreverse end-test-forms
)
174 (add-destructurings-to-body ()
175 (setf body
(nreverse body
))
176 (loop :for
(list var
) :in destructurings
:do
177 (setf body
`((destructuring-bind ,list
,var
,@body
))))
179 (values (nreverse prologue
)
180 (nreverse init-step-forms
)
184 (nreverse first-time
)
187 (add-destructurings-to-body))))))
189 (defpsmacro loop
(&rest args
)
190 (multiple-value-bind (prologue
191 init-step-forms end-test
196 (parse-ps-loop (normalize-loop-keywords args
))
197 (let ((first-guard (and first-time
(ps-gensym)))
198 (last-guard (and last-time
(ps-gensym))))
199 `(,@(if default-accum-var
'(with-lambda ()) '(progn))
200 ,@(when first-time
`((var ,first-guard t
)))
201 ,@(when last-time
`((var ,last-guard nil
)))
204 (do* ,init-step-forms
209 (setf ,first-guard nil
))))
212 `((setf ,last-guard t
))))
213 ,@(when last-time
`((when ,last-guard
,@last-time
)))
215 ,@(when default-accum-var
`((return ,default-accum-var
)))))))