1 (in-package :parenscript
)
3 (defun complex-js-expr?
(expr)
5 (find #\.
(symbol-name expr
))
8 (defvar *loop-keywords
*
9 '(:for
:do
:when
:unless
:initially
:finally
:first-time
:last-time
:while
:until
10 :from
:to
:below
:downto
:above
:by
:in
:across
:index
:= :then
:sum
:collect
:into
))
12 (defun normalize-loop-keywords (args)
15 (or (find-if (lambda (key) (and (symbolp x
) (equal (symbol-name x
) (symbol-name key
))))
20 (defun parse-ps-loop (terms)
22 init-step-forms end-test-forms
25 default-accum-var default-accum-kind
27 (macrolet ((with-local-var ((name expr
) &body body
)
29 `(let ((,name
(aif (and (complex-js-expr?
,expr
) (ps-gensym))
30 (progn (push (list 'var it
,expr
) prologue
)
39 (error "PS-LOOP expected ~a, got ~a." expected got
))
40 (consume (&optional what
)
41 (let ((term (pop terms
)))
42 (when (and what
(not (eq what term
)))
48 (err "an atom" (next))))
50 (cons 'progn
(loop :collect
(if (consp (next))
52 (err "a compound form" (next)))
53 :until
(atom (next)))))
58 (accumulate (kind term var
)
60 (when (and default-accum-kind
(not (eq kind default-accum-kind
)))
61 (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
))
62 (unless default-accum-var
63 (setf default-accum-var
(ps-gensym kind
)
64 default-accum-kind kind
))
65 (setf var default-accum-var
))
66 (let ((initial (case kind
(:sum
0) (:collect
'(array)))))
67 (pushnew `(var ,var
,initial
) prologue
:key
#'second
))
69 (:sum
`(incf ,var
,term
))
70 (:collect
`((@ ,var
:push
) ,term
))))
73 ((:when
:unless
) (list (intern (symbol-name term
))
75 (body-clause (consume-atom))))
76 ((:sum
:collect
) (accumulate term
(consume) (consume-if :into
)))
78 (otherwise (err "a PS-LOOP keyword" term
))))
80 (let ((start (consume))
85 (loop while
(member (next) '(:to
:below
:downto
:above
:by
)) do
86 (let ((term (consume)))
89 (setf op
(case term
((:downto
:above
) '-
) (otherwise '+))
90 test
(case term
(:to
'>) (:below
'>=) (:downto
'<) (:above
'<=))
92 (push `(,var
,start
(,op
,var
,(or by
1))) init-step-forms
)
94 (with-local-var (end-var end
)
95 (push (list test var end-var
) end-test-forms
)))))
97 (let ((start (consume))
98 (then (consume-if :then
)))
99 (push (list var start
(or then start
)) init-step-forms
)))
101 (with-local-var (arr (consume))
102 (let* ((index (or (consume-if :index
) (ps-gensym)))
103 (equiv `(:for
,index
:from
0 :below
(length ,arr
)
104 :for
,var
:= (aref ,arr
,index
))))
105 (setf terms
(append equiv terms
))
109 (let* ((place (consume))
110 (var (when (atom place
) place
))
111 (varlist (unless var place
))
112 (term (consume-atom)))
114 (when (eq term
:from
)
115 (err "an atom after FROM" varlist
))
116 (setf var
(ps-gensym))
117 (push (list varlist var
) destructurings
))
119 (:from
(for-from var
))
121 ((:in
:across
) (for-in var
))
122 (otherwise (error "FOR ~s ~s is not valid in PS-LOOP." var term
)))))
124 (let ((term (consume-atom)))
127 (:while
(push `(unless ,(consume) break
) body
))
128 (:until
(push `(when ,(consume) break
) body
))
129 (:initially
(push (consume-progn) initially
))
130 (:finally
(push (consume-progn) finally
))
131 (:first-time
(push (consume-progn) first-time
))
132 (:last-time
(push (consume-progn) last-time
))
133 (otherwise (push (body-clause term
) body
))))))
135 (loop :while terms
:do
(clause))
136 (err "loop definition" nil
)))
138 (aif (nreverse end-test-forms
)
143 (add-destructurings-to-body ()
144 (setf body
(nreverse body
))
145 (loop :for
(list var
) :in destructurings
:do
146 (setf body
`((destructuring-bind ,list
,var
,@body
))))
148 (values (nreverse prologue
)
149 (nreverse init-step-forms
)
153 (nreverse first-time
)
156 (add-destructurings-to-body))))))
158 (defpsmacro loop
(&rest args
)
159 (multiple-value-bind (prologue
160 init-step-forms end-test
165 (parse-ps-loop (normalize-loop-keywords args
))
166 (let ((first-guard (and first-time
(ps-gensym)))
167 (last-guard (and last-time
(ps-gensym))))
168 `(,@(if default-accum-var
'(with-lambda ()) '(progn))
169 ,@(when first-time
`((var ,first-guard t
)))
170 ,@(when last-time
`((var ,last-guard nil
)))
173 (do* ,init-step-forms
178 (setf ,first-guard nil
))))
181 `((setf ,last-guard t
))))
182 ,@(when last-time
`((when ,last-guard
,@last-time
)))
184 ,@(when default-accum-var
`((return ,default-accum-var
)))))))