Revise slot-value fix to not print useless parens.
[clinton/parenscript.git] / src / special-forms.lisp
CommitLineData
18dd299a
VS
1(in-package :parenscript)
2
3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4;;; literals
5(defmacro defpsliteral (name string)
4577df1c
TC
6 `(progn
7 (add-ps-literal ',name)
8 (define-ps-special-form ,name (expecting)
9 (declare (ignore expecting))
10 (list 'js-literal ,string))))
18dd299a
VS
11
12(defpsliteral this "this")
13(defpsliteral t "true")
14(defpsliteral true "true")
15(defpsliteral false "false")
16(defpsliteral f "false")
17(defpsliteral nil "null")
18(defpsliteral undefined "undefined")
19
c452748e
TC
20(macrolet ((def-for-literal (name printer)
21 `(progn
4577df1c 22 (add-ps-literal ',name)
c452748e
TC
23 (define-ps-special-form ,name (expecting &optional label)
24 (declare (ignore expecting))
25 (list ',printer label)))))
26 (def-for-literal break js-break)
27 (def-for-literal continue js-continue))
18dd299a
VS
28
29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30;;; unary operators
31(mapcar (lambda (op) (eval `(define-ps-special-form ,op (expecting value)
32 (declare (ignore expecting))
356992f3 33 (list 'js-named-operator ',op (compile-parenscript-form value :expecting :expression)))))
18dd299a
VS
34 '(throw delete void typeof new))
35
36(define-ps-special-form return (expecting &optional value)
37 (declare (ignore expecting))
38 (list 'js-return (compile-parenscript-form value :expecting :expression)))
39
40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41;;; arrays
42(define-ps-special-form array (expecting &rest values)
43 (declare (ignore expecting))
44 (cons 'array-literal (mapcar (lambda (form) (compile-parenscript-form form :expecting :expression))
45 values)))
46
47(define-ps-special-form aref (expecting array &rest coords)
48 (declare (ignore expecting))
49 (list 'js-aref (compile-parenscript-form array :expecting :expression)
50 (mapcar (lambda (form)
51 (compile-parenscript-form form :expecting :expression))
52 coords)))
53
4577df1c 54(add-ps-literal '{})
18dd299a
VS
55(define-ps-special-form {} (expecting &rest arrows)
56 (declare (ignore expecting))
57 (cons 'object-literal (loop for (key value) on arrows by #'cddr
58 collect (cons key (compile-parenscript-form value :expecting :expression)))))
59
60(defpsmacro list (&rest values)
61 `(array ,@values))
62
63(defpsmacro make-array (&rest inits)
64 `(new (*array ,@inits)))
65
66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67;;; operators
68(define-ps-special-form incf (expecting x &optional (delta 1))
69 (declare (ignore expecting))
70 (if (equal delta 1)
71 (list 'unary-operator "++" (compile-parenscript-form x :expecting :expression) :prefix t)
72 (list 'operator '+= (list (compile-parenscript-form x :expecting :expression)
73 (compile-parenscript-form delta :expecting :expression)))))
74
75(define-ps-special-form decf (expecting x &optional (delta 1))
76 (declare (ignore expecting))
77 (if (equal delta 1)
78 (list 'unary-operator "--" (compile-parenscript-form x :expecting :expression) :prefix t)
79 (list 'operator '-= (list (compile-parenscript-form x :expecting :expression)
80 (compile-parenscript-form delta :expecting :expression)))))
81
82(define-ps-special-form - (expecting first &rest rest)
83 (declare (ignore expecting))
84 (if (null rest)
85 (list 'unary-operator "-" (compile-parenscript-form first :expecting :expression) :prefix t)
86 (list 'operator '- (mapcar (lambda (val) (compile-parenscript-form val :expecting :expression))
87 (cons first rest)))))
88
89(define-ps-special-form not (expecting x)
90 (declare (ignore expecting))
91 (let ((form (compile-parenscript-form x :expecting :expression))
92 (not-op nil))
93 (if (and (eql (first form) 'operator)
b508414b 94 (= (length (third form)) 2)
18dd299a
VS
95 (setf not-op (case (second form)
96 (== '!=)
97 (< '>=)
98 (> '<=)
99 (<= '>)
100 (>= '<)
101 (!= '==)
102 (=== '!==)
103 (!== '===)
104 (t nil))))
105 (list 'operator not-op (third form))
106 (list 'unary-operator "!" form :prefix t))))
107
108(define-ps-special-form ~ (expecting x)
109 (declare (ignore expecting))
110 (list 'unary-operator "~" (compile-parenscript-form x :expecting :expression) :prefix t))
111
18dd299a
VS
112;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
113;;; control structures
114(defun flatten-blocks (body)
115 (when body
116 (if (and (listp (car body))
117 (eql 'js-block (caar body)))
118 (append (third (car body)) (flatten-blocks (cdr body)))
119 (cons (car body) (flatten-blocks (cdr body))))))
120
121(defun constant-literal-form-p (form)
122 (or (numberp form)
123 (stringp form)
124 (and (listp form)
125 (eql 'js-literal (car form)))))
126
127(define-ps-special-form progn (expecting &rest body)
128 (if (and (eql expecting :expression) (= 1 (length body)))
129 (compile-parenscript-form (car body) :expecting :expression)
130 (list 'js-block
131 expecting
132 (let* ((block (mapcar (lambda (form)
133 (compile-parenscript-form form :expecting expecting))
134 body))
135 (clean-block (remove nil block))
136 (flat-block (flatten-blocks clean-block))
137 (reachable-block (append (remove-if #'constant-literal-form-p (butlast flat-block))
138 (last flat-block))))
139 reachable-block))))
140
141(define-ps-special-form cond (expecting &rest clauses)
142 (ecase expecting
143 (:statement (list 'js-cond-statement
144 (mapcar (lambda (clause)
145 (destructuring-bind (test &rest body)
146 clause
147 (list (compile-parenscript-form test :expecting :expression)
148 (compile-parenscript-form `(progn ,@body) :expecting :statement))))
149 clauses)))
150 (:expression (make-cond-clauses-into-nested-ifs clauses))))
151
152(defun make-cond-clauses-into-nested-ifs (clauses)
153 (if clauses
154 (destructuring-bind (test &rest body)
155 (car clauses)
156 (if (eq t test)
157 (compile-parenscript-form `(progn ,@body) :expecting :expression)
158 (list 'js-expression-if (compile-parenscript-form test :expecting :expression)
159 (compile-parenscript-form `(progn ,@body) :expecting :expression)
160 (make-cond-clauses-into-nested-ifs (cdr clauses)))))
161 (compile-parenscript-form nil :expecting :expression)))
162
163(define-ps-special-form if (expecting test then &optional else)
164 (ecase expecting
165 (:statement (list 'js-statement-if (compile-parenscript-form test :expecting :expression)
166 (compile-parenscript-form `(progn ,then))
167 (when else (compile-parenscript-form `(progn ,else)))))
168 (:expression (list 'js-expression-if (compile-parenscript-form test :expecting :expression)
169 (compile-parenscript-form then :expecting :expression)
170 (compile-parenscript-form else :expecting :expression)))))
171
172(define-ps-special-form switch (expecting test-expr &rest clauses)
173 (declare (ignore expecting))
174 (let ((clauses (mapcar (lambda (clause)
b508414b
TC
175 (let ((val (car clause))
176 (body (cdr clause)))
675edae3
TC
177 (cons (if (and (symbolp val)
178 (eq (ensure-ps-symbol val) 'default))
b508414b
TC
179 'default
180 (compile-parenscript-form val :expecting :expression))
18dd299a
VS
181 (mapcar (lambda (statement) (compile-parenscript-form statement :expecting :statement))
182 body))))
b508414b
TC
183 clauses))
184 (expr (compile-parenscript-form test-expr :expecting :expression)))
18dd299a
VS
185 (list 'js-switch expr clauses)))
186
187(defpsmacro case (value &rest clauses)
188 (labels ((make-clause (val body more)
587f3aa0 189 (cond ((and (listp val) (not (eq (car val) 'quote)))
18dd299a
VS
190 (append (mapcar #'list (butlast val))
191 (make-clause (first (last val)) body more)))
192 ((member val '(t otherwise))
193 (make-clause 'default body more))
194 (more `((,val ,@body break)))
195 (t `((,val ,@body))))))
196 `(switch ,value ,@(mapcon (lambda (clause)
197 (make-clause (car (first clause))
198 (cdr (first clause))
199 (rest clause)))
200 clauses))))
201
202(defpsmacro when (test &rest body)
203 `(if ,test (progn ,@body)))
204
205(defpsmacro unless (test &rest body)
206 `(if (not ,test) (progn ,@body)))
207
208;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
209;;; function definition
210(defun compile-function-definition (args body)
211 (list (mapcar (lambda (arg) (compile-parenscript-form arg :expecting :symbol)) args)
212 (let ((*enclosing-lexical-block-declarations* ()))
213 ;; the first compilation will produce a list of variables we need to declare in the function body
214 (compile-parenscript-form `(progn ,@body) :expecting :statement)
215 ;; now declare and compile
58c4ef4f 216 (compile-parenscript-form `(progn ,@(loop for var in *enclosing-lexical-block-declarations* collect `(var ,var))
18dd299a
VS
217 ,@body) :expecting :statement))))
218
219(define-ps-special-form %js-lambda (expecting args &rest body)
220 (declare (ignore expecting))
221 (cons 'js-lambda (compile-function-definition args body)))
222
223(define-ps-special-form %js-defun (expecting name args &rest body)
224 (declare (ignore expecting))
225 (append (list 'js-defun name) (compile-function-definition args body)))
226
227(defun parse-function-body (body)
228 (let* ((docstring
229 (when (stringp (first body))
230 (first body)))
231 (body-forms (if docstring (rest body) body)))
232 (values body-forms docstring)))
233
234(defun parse-key-spec (key-spec)
235 "parses an &key parameter. Returns 4 values:
236var, init-form, keyword-name, supplied-p-var, init-form-supplied-p.
237
238Syntax of key spec:
239[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}*
240"
241 (let* ((var (cond ((symbolp key-spec) key-spec)
242 ((and (listp key-spec) (symbolp (first key-spec))) (first key-spec))
243 ((and (listp key-spec) (listp (first key-spec))) (second key-spec))))
244 (keyword-name (if (and (listp key-spec) (listp (first key-spec)))
245 (first (first key-spec))
246 (intern (string var) :keyword)))
247 (init-form (if (listp key-spec) (second key-spec) nil))
248 (init-form-supplied-p (if (listp key-spec) t nil))
249 (supplied-p-var (if (listp key-spec) (third key-spec) nil)))
250 (values var init-form keyword-name supplied-p-var init-form-supplied-p)))
251
252(defun parse-optional-spec (spec)
253 "Parses an &optional parameter. Returns 3 values: var, init-form, supplied-p-var.
254[&optional {var | (var [init-form [supplied-p-parameter]])}*] "
255 (let* ((var (cond ((symbolp spec) spec)
256 ((and (listp spec) (first spec)))))
257 (init-form (if (listp spec) (second spec)))
258 (supplied-p-var (if (listp spec) (third spec))))
259 (values var init-form supplied-p-var)))
260
261(defun parse-aux-spec (spec)
262 "Returns two values: variable and init-form"
263 ;; [&aux {var | (var [init-form])}*])
264 (values (if (symbolp spec) spec (first spec))
265 (when (listp spec) (second spec))))
266
267(defpsmacro defaultf (place value)
268 `(setf ,place (or (and (=== undefined ,place) ,value)
b508414b 269 ,place)))
18dd299a
VS
270
271(defun parse-extended-function (lambda-list body &optional name)
272 "Returns two values: the effective arguments and body for a function with
273the given lambda-list and body."
274
275 ;; The lambda list is transformed as follows, since a javascript lambda list is just a
276 ;; list of variable names, and you have access to the arguments variable inside the function:
277 ;; * standard variables are the mapped directly into the js-lambda list
278 ;; * optional variables' variable names are mapped directly into the lambda list,
279 ;; and for each optional variable with name v and default value d, a form is produced
280 ;; (defaultf v d)
281 ;; * when any keyword variables are in the lambda list, a single 'optional-args' variable is
282 ;; appended to the js-lambda list as the last argument. WITH-SLOTS is used for all
283 ;; the variables with inside the body of the function,
284 ;; a (with-slots ((var-name key-name)) optional-args ...)
285 (declare (ignore name))
286 (multiple-value-bind (requireds optionals rest? rest keys? keys allow? aux? aux
287 more? more-context more-count key-object)
288 (parse-lambda-list lambda-list)
289 (declare (ignore allow? aux? aux more? more-context more-count))
290 (let* ((options-var (or key-object (ps-gensym)))
291 ;; optionals are of form (var default-value)
292 (effective-args
293 (remove-if
294 #'null
295 (append requireds
296 (mapcar #'parse-optional-spec optionals)
297 (when keys (list options-var)))))
298 ;; an alist of arg -> default val
299 (initform-pairs
300 (remove
301 nil
302 (append
303 ;; optional arguments first
304 (mapcar #'(lambda (opt-spec)
305 (multiple-value-bind (var val) (parse-optional-spec opt-spec)
306 (cons var val)))
307 optionals)
308 (if keys? (list (cons options-var '(create))))
309 (mapcar #'(lambda (key-spec)
310 (multiple-value-bind (var val x y specified?) (parse-key-spec key-spec)
311 (declare (ignore x y))
312 (when specified? (cons var val))))
313 keys))))
45c9f9c2 314 (body-paren-forms (parse-function-body body)) ; remove documentation
18dd299a
VS
315 (initform-forms
316 (mapcar #'(lambda (default-pair)
317 `(defaultf ,(car default-pair) ,(cdr default-pair)))
318 initform-pairs))
319 (rest-form
320 (if rest?
321 (with-ps-gensyms (i)
f326f929 322 `(progn (var ,rest (array))
18dd299a
VS
323 (dotimes (,i (- arguments.length ,(length effective-args)))
324 (setf (aref ,rest ,i) (aref arguments (+ ,i ,(length effective-args)))))))
325 `(progn)))
326 (effective-body (append initform-forms (list rest-form) body-paren-forms))
327 (effective-body
328 (if keys?
329 (list `(with-slots ,(mapcar #'(lambda (key-spec)
330 (multiple-value-bind (var x key-name)
331 (parse-key-spec key-spec)
332 (declare (ignore x))
333 (list var key-name)))
334 keys)
335 ,options-var
336 ,@effective-body))
337 effective-body)))
338 (values effective-args effective-body))))
339
340(defpsmacro defun (name lambda-list &body body)
341 "An extended defun macro that allows cool things like keyword arguments.
342lambda-list::=
343 (var*
344 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
345 [&rest var]
346 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
347 [&aux {var | (var [init-form])}*])"
348 (if (symbolp name)
349 `(defun-function ,name ,lambda-list ,@body)
675edae3 350 (progn (assert (and (= (length name) 2) (eq 'setf (ensure-ps-symbol (car name)))) ()
18dd299a
VS
351 "(defun ~s ~s ...) needs to have a symbol or (setf symbol) for a name." name lambda-list)
352 `(defun-setf ,name ,lambda-list ,@body))))
353
354(defpsmacro defun-function (name lambda-list &body body)
355 (multiple-value-bind (effective-args effective-body)
356 (parse-extended-function lambda-list body name)
357 `(%js-defun ,name ,effective-args
358 ,@effective-body)))
359
360(defvar *defun-setf-name-prefix* "__setf_")
361
362(defpsmacro defun-setf (setf-name lambda-list &body body)
363 (let ((mangled-function-name (intern (concatenate 'string *defun-setf-name-prefix* (symbol-name (second setf-name)))
364 (symbol-package (second setf-name))))
365 (function-args (cdr (ordered-set-difference lambda-list lambda-list-keywords))))
366 `(progn (defsetf ,(second setf-name) ,(cdr lambda-list) (store-var)
367 `(,',mangled-function-name ,store-var ,@(list ,@function-args)))
368 (defun ,mangled-function-name ,lambda-list ,@body))))
369
370(defpsmacro lambda (lambda-list &body body)
371 "An extended defun macro that allows cool things like keyword arguments.
372lambda-list::=
373 (var*
374 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
375 [&rest var]
376 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
377 [&aux {var | (var [init-form])}*])"
378 (multiple-value-bind (effective-args effective-body)
379 (parse-extended-function lambda-list body)
380 `(%js-lambda ,effective-args
381 ,@effective-body)))
382
383(defpsmacro defsetf-long (access-fn lambda-list (store-var) form)
462ca010 384 (setf (get-macro-spec access-fn *ps-setf-expanders*)
18dd299a
VS
385 (compile nil
386 (let ((var-bindings (ordered-set-difference lambda-list lambda-list-keywords)))
387 `(lambda (access-fn-args store-form)
388 (destructuring-bind ,lambda-list
389 access-fn-args
390 (let* ((,store-var (ps-gensym))
391 (gensymed-names (loop repeat ,(length var-bindings) collecting (ps-gensym)))
392 (gensymed-arg-bindings (mapcar #'list gensymed-names (list ,@var-bindings))))
393 (destructuring-bind ,var-bindings
394 gensymed-names
58c4ef4f
VS
395 `(let* (,@gensymed-arg-bindings
396 (,,store-var ,store-form))
18dd299a
VS
397 ,,form))))))))
398 nil)
399
400(defpsmacro defsetf-short (access-fn update-fn &optional docstring)
401 (declare (ignore docstring))
462ca010 402 (setf (get-macro-spec access-fn *ps-setf-expanders*)
18dd299a
VS
403 (lambda (access-fn-args store-form)
404 `(,update-fn ,@access-fn-args ,store-form)))
405 nil)
406
407(defpsmacro defsetf (access-fn &rest args)
408 `(,(if (= (length args) 3) 'defsetf-long 'defsetf-short) ,access-fn ,@args))
409
410;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
411;;; macros
412(defmacro with-temp-macro-environment ((var) &body body)
413 `(let* ((,var (make-macro-env-dictionary))
462ca010 414 (*ps-macro-env* (cons ,var *ps-macro-env*)))
18dd299a
VS
415 ,@body))
416
417(define-ps-special-form macrolet (expecting macros &body body)
418 (declare (ignore expecting))
419 (with-temp-macro-environment (macro-env-dict)
420 (dolist (macro macros)
421 (destructuring-bind (name arglist &body body)
422 macro
b508414b
TC
423 (setf (get-macro-spec name macro-env-dict)
424 (cons nil (make-ps-macro-function arglist body)))))
18dd299a
VS
425 (compile-parenscript-form `(progn ,@body))))
426
427(define-ps-special-form symbol-macrolet (expecting symbol-macros &body body)
428 (declare (ignore expecting))
429 (with-temp-macro-environment (macro-env-dict)
430 (dolist (macro symbol-macros)
431 (destructuring-bind (name expansion)
432 macro
b508414b
TC
433 (setf (get-macro-spec name macro-env-dict)
434 (cons t (make-ps-macro-function () (list `',expansion))))))
18dd299a
VS
435 (compile-parenscript-form `(progn ,@body))))
436
437(define-ps-special-form defmacro (expecting name args &body body)
438 (declare (ignore expecting))
462ca010 439 (define-ps-macro% name args body :symbol-macro-p nil)
18dd299a
VS
440 nil)
441
442(define-ps-special-form define-symbol-macro (expecting name expansion)
443 (declare (ignore expecting))
462ca010 444 (define-ps-macro% name () (list `',expansion) :symbol-macro-p t)
18dd299a
VS
445 nil)
446
447;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
448;;; objects
449(define-ps-special-form create (expecting &rest args)
450 (declare (ignore expecting))
451 (list 'js-object (loop for (name val) on args by #'cddr collecting
452 (let ((name-expr (compile-parenscript-form name :expecting :expression)))
453 (assert (or (stringp name-expr)
454 (numberp name-expr)
455 (and (listp name-expr)
456 (or (eql 'js-variable (car name-expr))
462ca010 457 (eql 'ps-quote (car name-expr)))))
18dd299a
VS
458 ()
459 "Slot ~s is not one of js-variable, keyword, string or number." name-expr)
460 (list name-expr (compile-parenscript-form val :expecting :expression))))))
461
462(define-ps-special-form %js-slot-value (expecting obj slot)
463 (declare (ignore expecting))
464 (if (ps::ps-macroexpand slot)
465 (list 'js-slot-value (compile-parenscript-form obj :expecting :expression) (compile-parenscript-form slot))
466 (compile-parenscript-form obj :expecting :expression)))
467
468(define-ps-special-form instanceof (expecting value type)
469 (declare (ignore expecting))
470 (list 'js-instanceof (compile-parenscript-form value :expecting :expression)
471 (compile-parenscript-form type :expecting :expression)))
472
473(defpsmacro slot-value (obj &rest slots)
474 (if (null (rest slots))
475 `(%js-slot-value ,obj ,(first slots))
476 `(slot-value (slot-value ,obj ,(first slots)) ,@(rest slots))))
477
478(defpsmacro with-slots (slots object &rest body)
479 (flet ((slot-var (slot) (if (listp slot) (first slot) slot))
b508414b 480 (slot-symbol (slot) (if (listp slot) (second slot) slot)))
18dd299a 481 `(symbol-macrolet ,(mapcar #'(lambda (slot)
b508414b
TC
482 `(,(slot-var slot) (slot-value ,object ',(slot-symbol slot))))
483 slots)
18dd299a
VS
484 ,@body)))
485
486;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
487;;; assignment and binding
488(defun assignment-op (op)
489 (case op
490 (+ '+=)
491 (~ '~=)
492 (\& '\&=)
493 (\| '\|=)
494 (- '-=)
495 (* '*=)
496 (% '%=)
497 (>> '>>=)
498 (^ '^=)
499 (<< '<<=)
500 (>>> '>>>=)
501 (/ '/=)
502 (t nil)))
503
504(defun smart-setf (lhs rhs)
505 (if (and (listp rhs)
506 (eql 'operator (car rhs))
b508414b 507 (member lhs (third rhs) :test #'equalp))
d89456ee 508 (let ((args-without-first (remove lhs (third rhs) :count 1 :end 1 :test #'equalp)))
b508414b
TC
509 (cond ((and (assignment-op (second rhs))
510 (member (second rhs) '(+ *))
18dd299a 511 (equalp lhs (first (third rhs))))
b508414b 512 (list 'operator (assignment-op (second rhs))
18dd299a 513 (list lhs (list 'operator (second rhs) args-without-first))))
b508414b 514 (t (list 'js-assign lhs rhs))))
18dd299a
VS
515 (list 'js-assign lhs rhs)))
516
517(define-ps-special-form setf1% (expecting lhs rhs)
518 (declare (ignore expecting))
519 (smart-setf (compile-parenscript-form lhs :expecting :expression) (compile-parenscript-form rhs :expecting :expression)))
520
521(defpsmacro setf (&rest args)
522 (flet ((process-setf-clause (place value-form)
462ca010
TC
523 (if (and (listp place) (get-macro-spec (car place) *ps-setf-expanders*))
524 (funcall (get-macro-spec (car place) *ps-setf-expanders*) (cdr place) value-form)
18dd299a 525 (let ((exp-place (ps-macroexpand place)))
462ca010
TC
526 (if (and (listp exp-place) (get-macro-spec (car exp-place) *ps-setf-expanders*))
527 (funcall (get-macro-spec (car exp-place) *ps-setf-expanders*) (cdr exp-place) value-form)
18dd299a
VS
528 `(setf1% ,exp-place ,value-form))))))
529 (assert (evenp (length args)) ()
530 "~s does not have an even number of arguments." (cons 'setf args))
531 `(progn ,@(loop for (place value) on args by #'cddr collect (process-setf-clause place value)))))
532
1fe28ee1
TC
533(defpsmacro psetf (&rest args)
534 (let ((vars (loop for x in args by #'cddr collect x))
535 (vals (loop for x in (cdr args) by #'cddr collect x)))
536 (let ((gensyms (mapcar (lambda (x) (declare (ignore x)) (ps-gensym)) vars)))
537 `(simple-let* ,(mapcar #'list gensyms vals)
538 (setf ,@(mapcan #'list vars gensyms))))))
539
ec227186
TC
540(defun check-setq-args (args)
541 (let ((vars (loop for x in args by #'cddr collect x)))
542 (let ((non-var (find-if (complement #'symbolp) vars)))
543 (when non-var
544 (error 'type-error :datum non-var :expected-type 'symbol)))))
545
546(defpsmacro setq (&rest args)
547 (check-setq-args args)
548 `(setf ,@args))
549
550(defpsmacro psetq (&rest args)
551 (check-setq-args args)
552 `(psetf ,@args))
553
58c4ef4f 554(define-ps-special-form var (expecting name &rest value)
18dd299a 555 (declare (ignore expecting))
58c4ef4f 556 (append (list 'js-var name)
18dd299a 557 (when value
58c4ef4f
VS
558 (assert (= (length value) 1) () "Wrong number of arguments to var: ~s" `(var ,name ,@value))
559 (list (compile-parenscript-form (car value) :expecting :expression)))))
18dd299a 560
58c4ef4f
VS
561(defpsmacro defvar (name &rest value)
562 "Note: this must be used as a top-level form, otherwise the result will be undefined behavior."
563 (pushnew name *ps-special-variables*)
564 (assert (or (null value) (= (length value) 1)) () "Wrong number of arguments to defvar: ~s" `(defvar ,name ,@value))
565 `(var ,name ,@value))
566
3530f5e1
TC
567(defun make-let-vars (bindings)
568 (mapcar (lambda (x) (if (listp x) (car x) x)) bindings))
569
570(defun make-let-vals (bindings)
571 (mapcar (lambda (x) (if (or (atom x) (endp (cdr x))) nil (second x))) bindings))
572
58c4ef4f 573(defpsmacro lexical-let* (bindings &body body)
241c2c5b
TC
574 `((lambda ()
575 (let* ,bindings
576 ,@body))))
58c4ef4f 577
3530f5e1
TC
578(defpsmacro lexical-let (bindings &body body)
579 `((lambda ,(make-let-vars bindings)
580 ,@body)
581 ,@(make-let-vals bindings)))
582
b5cf9e71 583(defpsmacro simple-let* (bindings &body body)
58c4ef4f
VS
584 (if bindings
585 (let ((var (if (listp (car bindings)) (caar bindings) (car bindings))))
586 `(,(if (member var *ps-special-variables*) 'let1-dynamic 'let1) ,(car bindings)
b5cf9e71 587 (simple-let* ,(cdr bindings) ,@body)))
58c4ef4f
VS
588 `(progn ,@body)))
589
ca25a40e
TC
590(defpsmacro simple-let (bindings &body body)
591 (let ((vars (mapcar (lambda (x) (if (atom x) x (first x))) bindings))
592 (vals (mapcar (lambda (x) (if (or (atom x) (endp (cdr x))) nil (second x))) bindings)))
593 (let ((gensyms (mapcar (lambda (x) (ps-gensym (format nil "_js_~a" x))) vars)))
594 `(simple-let* ,(mapcar #'list gensyms vals)
595 (simple-let* ,(mapcar #'list vars gensyms)
596 ,@(mapcar (lambda (x) `(delete ,x)) gensyms)
597 ,@body)))))
598
b5cf9e71
TC
599(defpsmacro let* (bindings &body body)
600 `(simple-let* ,bindings ,@body))
601
da7ec1c0
TC
602(defpsmacro let (bindings &body body)
603 `(simple-let ,bindings ,@body))
58c4ef4f
VS
604
605(define-ps-special-form let1 (expecting binding &rest body)
18dd299a
VS
606 (ecase expecting
607 (:statement
58c4ef4f 608 (compile-parenscript-form `(progn ,(if (atom binding) `(var ,binding) `(var ,@binding)) ,@body) :expecting :statement))
18dd299a 609 (:expression
58c4ef4f
VS
610 (let ((var (if (atom binding) binding (car binding)))
611 (variable-assignment (when (listp binding) (cons 'setf binding))))
612 (push var *enclosing-lexical-block-declarations*)
613 (compile-parenscript-form `(progn ,variable-assignment ,@body) :expecting :expression)))))
614
615(defpsmacro let1-dynamic ((var value) &rest body)
616 (with-ps-gensyms (temp-stack-var)
617 `(progn (var ,temp-stack-var)
618 (try (progn (setf ,temp-stack-var ,var)
619 (setf ,var ,value)
620 ,@body)
915342fb
TC
621 (:finally
622 (setf ,var ,temp-stack-var)
623 (delete ,temp-stack-var))))))
18dd299a
VS
624
625;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
626;;; iteration
6a2ce72d
TC
627(defun make-for-vars/inits (init-forms)
628 (mapcar (lambda (x)
629 (cons (compile-parenscript-form (if (atom x) x (first x)) :expecting :symbol)
630 (compile-parenscript-form (if (atom x) nil (second x)))))
631 init-forms))
18dd299a 632
6a2ce72d
TC
633(define-ps-special-form labeled-for (expecting label init-forms cond-forms step-forms &rest body)
634 (declare (ignore expecting))
635 (let ((vars (make-for-vars/inits init-forms))
636 (steps (mapcar (lambda (x) (compile-parenscript-form x :expecting :expression)) step-forms))
637 (tests (mapcar (lambda (x) (compile-parenscript-form x :expecting :expression)) cond-forms))
638 (body (compile-parenscript-form `(progn ,@body))))
639 (list 'js-for label vars tests steps body)))
640
641(defpsmacro for (init-forms cond-forms step-forms &body body)
642 `(labeled-for nil ,init-forms ,cond-forms ,step-forms ,@body))
643
644(defun do-make-let-bindings (decls)
645 (mapcar (lambda (x)
646 (if (atom x) x
647 (if (endp (cdr x)) (list (car x))
648 (subseq x 0 2))))
649 decls))
650
651(defun do-make-init-vars (decls)
652 (mapcar (lambda (x) (if (atom x) x (first x))) decls))
653
654(defun do-make-init-vals (decls)
655 (mapcar (lambda (x) (if (or (atom x) (endp (cdr x))) nil (second x))) decls))
656
657(defun do-make-for-vars/init (decls)
658 (mapcar (lambda (x)
659 (if (atom x) x
660 (if (endp (cdr x)) x
661 (subseq x 0 2))))
662 decls))
663
664(defun do-make-for-steps (decls)
665 (mapcar (lambda (x)
666 `(setf ,(first x) ,(third x)))
667 (remove-if (lambda (x) (or (atom x) (< (length x) 3))) decls)))
668
669(defun do-make-iter-psteps (decls)
670 `(psetq
671 ,@(mapcan (lambda (x) (list (first x) (third x)))
672 (remove-if (lambda (x) (or (atom x) (< (length x) 3))) decls))))
673
674(defpsmacro do* (decls (termination &optional (result nil result?)) &body body)
675 (if result?
676 `((lambda ()
677 (for ,(do-make-for-vars/init decls) ((not ,termination)) ,(do-make-for-steps decls)
678 ,@body)
679 (return ,result)))
680 `(progn
681 (for ,(do-make-for-vars/init decls) ((not ,termination)) ,(do-make-for-steps decls)
682 ,@body))))
683
684(defpsmacro do (decls (termination &optional (result nil result?)) &body body)
685 (if result?
686 `((lambda ,(do-make-init-vars decls)
687 (for () ((not ,termination)) ()
688 ,@body
689 ,(do-make-iter-psteps decls))
690 (return ,result))
691 ,@(do-make-init-vals decls))
692 `(let ,(do-make-let-bindings decls)
693 (for () ((not ,termination)) ()
694 ,@body
695 ,(do-make-iter-psteps decls)))))
696
697(define-ps-special-form for-in (expecting decl &rest body)
18dd299a 698 (declare (ignore expecting))
6a2ce72d
TC
699 (list 'js-for-in
700 (compile-parenscript-form (first decl) :expecting :expression)
18dd299a 701 (compile-parenscript-form (second decl) :expecting :expression)
b508414b 702 (compile-parenscript-form `(progn ,@body))))
18dd299a 703
6a2ce72d
TC
704(defpsmacro doeach ((var array &optional (result (values) result?)) &body body)
705 "Iterates over `array'. If `var' is a symbol, binds `var' to each
706element key. If `var' is a list, it must be a list of two
707symbols, (key value), which will be bound to each successive key/value
708pair in `array'."
709 (if result?
710 (if (consp var)
711 (destructuring-bind (key val) var
712 `((lambda ()
713 (let* (,val)
714 (for-in ((var ,key) ,array)
715 (setf ,val (aref ,array ,key))
716 ,@body)
717 (return ,result)))))
718 `((lambda ()
719 (for-in ((var ,var) ,array)
720 ,@body)
721 (return ,result))))
722 (if (consp var)
723 (destructuring-bind (key val) var
724 `(progn
725 (let* (,val)
726 (for-in ((var ,key) ,array)
727 (setf ,val (aref ,array ,key))
728 ,@body))))
729 `(progn
730 (for-in ((var ,var) ,array) ,@body)))))
731
18dd299a
VS
732(define-ps-special-form while (expecting test &rest body)
733 (declare (ignore expecting))
734 (list 'js-while (compile-parenscript-form test :expecting :expression)
735 (compile-parenscript-form `(progn ,@body))))
736
6a2ce72d
TC
737(defpsmacro dotimes ((var count &optional (result nil result?)) &rest body)
738 `(do* ((,var 0 (1+ ,var)))
739 ((>= ,var ,count) ,@(when result? (list result)))
740 ,@body))
741
742(defpsmacro dolist ((var array &optional (result nil result?)) &body body)
743 (let ((idx (ps-gensym "_js_idx"))
744 (arrvar (ps-gensym "_js_arrvar")))
745 `(do* (,var
746 (,arrvar ,array)
747 (,idx 0 (1+ ,idx)))
748 ((>= ,idx (slot-value ,arrvar 'length))
749 ,@(when result? (list result)))
750 (setq ,var (aref ,arrvar ,idx))
751 ,@body)))
18dd299a
VS
752
753;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
754;;; misc
755(define-ps-special-form with (expecting expression &rest body)
756 (declare (ignore expecting))
757 (list 'js-with (compile-parenscript-form expression :expecting :expression)
b508414b 758 (compile-parenscript-form `(progn ,@body))))
18dd299a
VS
759
760(define-ps-special-form try (expecting form &rest clauses)
761 (declare (ignore expecting))
762 (let ((catch (cdr (assoc :catch clauses)))
763 (finally (cdr (assoc :finally clauses))))
764 (assert (not (cdar catch)) nil "Sorry, currently only simple catch forms are supported.")
765 (assert (or catch finally) ()
766 "Try form should have either a catch or a finally clause or both.")
767 (list 'js-try (compile-parenscript-form `(progn ,form))
768 :catch (when catch (list (compile-parenscript-form (caar catch) :expecting :symbol)
769 (compile-parenscript-form `(progn ,@(cdr catch)))))
770 :finally (when finally (compile-parenscript-form `(progn ,@finally))))))
771
772(define-ps-special-form cc-if (expecting test &rest body)
773 (declare (ignore expecting))
774 (list 'cc-if test (mapcar #'compile-parenscript-form body)))
775
776(define-ps-special-form regex (expecting regex)
777 (declare (ignore expecting))
778 (list 'js-regex (string regex)))
779
780(defpsmacro lisp (&body forms)
781 "Evaluates the given forms in Common Lisp at ParenScript
782macro-expansion time. The value of the last form is treated as a
783ParenScript expression and is inserted into the generated Javascript
784\(use nil for no-op)."
785 (eval (cons 'progn forms)))