1 (in-package #:parenscript
)
3 (macrolet ((define-trivial-mappings (&rest mappings
)
5 ,@(loop for
(macro-name ps-op
) on mappings by
#'cddr collect
6 `(defpsmacro ,macro-name
(&rest args
)
7 (cons ',ps-op args
))))))
8 (define-trivial-mappings
20 (defmacro def-js-maths
(&rest mathdefs
)
21 `(progn ,@(mapcar (lambda (def) (cons 'defpsmacro def
)) mathdefs
)))
24 (max (&rest nums
) `((@ *math max
) ,@nums
))
25 (min (&rest nums
) `((@ *math min
) ,@nums
))
26 (floor (n &optional divisor
) `((@ *math floor
) ,(if divisor
`(/ ,n
,divisor
) n
)))
27 (ceiling (n &optional divisor
) `((@ *math ceil
) ,(if divisor
`(/ ,n
,divisor
) n
)))
28 (round (n &optional divisor
) `((@ *math round
) ,(if divisor
`(/ ,n
,divisor
) n
)))
29 (sin (n) `((@ *math sin
) ,n
))
30 (cos (n) `((@ *math cos
) ,n
))
31 (tan (n) `((@ *math tan
) ,n
))
32 (asin (n) `((@ *math asin
) ,n
))
33 (acos (n) `((@ *math acos
) ,n
))
34 (atan (y &optional x
) (if x
`((@ *math atan2
) ,y
,x
) `((@ *math atan
) ,y
)))
35 (sinh (n) `((lambda (x) (/ (- (exp x
) (exp (- x
))) 2)) ,n
))
36 (cosh (n) `((lambda (x) (/ (+ (exp x
) (exp (- x
))) 2)) ,n
))
37 (tanh (n) `((lambda (x) (/ (- (exp x
) (exp (- x
))) (+ (exp x
) (exp (- x
))))) ,n
))
38 (asinh (n) `((lambda (x) (log (+ x
(sqrt (1+ (* x x
)))))) ,n
))
39 (acosh (n) `((lambda (x) (* 2 (log (+ (sqrt (/ (1+ x
) 2)) (sqrt (/ (1- x
) 2)))))) ,n
))
40 (atanh (n) `((lambda (x) (/ (- (log (+ 1 x
)) (log (- 1 x
))) 2)) ,n
))
43 (abs (n) `((@ *math abs
) ,n
))
44 (evenp (n) `(not (oddp ,n
)))
45 (oddp (n) `(rem ,n
2))
46 (exp (n) `((@ *math exp
) ,n
))
47 (expt (base power
) `((@ *math pow
) ,base
,power
))
48 (log (n &optional base
)
49 (or (and (null base
) `((@ *math log
) ,n
))
50 (and (numberp base
) (= base
10) `(* (log ,n
) (@ *math
*log10e
*)))
51 `(/ (log ,n
) (log ,base
))))
52 (sqrt (n) `((@ *math sqrt
) ,n
))
53 (random (&optional upto
) (if upto
54 `(floor (* ,upto
(random)))
55 '(funcall (@ *math random
)))))
57 (defpsmacro ash
(integer count
)
58 (let ((count (ps-macroexpand count
)))
59 (cond ((and (numberp count
) (> count
0)) `(<< ,integer
,count
))
60 ((numberp count
) `(>> ,integer
,(- count
)))
61 ((complex-js-expr? count
)
62 (let ((count-var (ps-gensym)))
63 `(let ((,count-var
,count
))
65 (<< ,integer
,count-var
)
66 (>> ,integer
(- ,count-var
))))))
69 (>> ,integer
(- ,count
)))))))
71 (define-ps-symbol-macro pi
(getprop *math
'*pi
*))
75 (defpsmacro stringp
(x)
76 `(string= (typeof ,x
) "string"))
78 (defpsmacro numberp
(x)
79 `(string= (typeof ,x
) "number"))
81 (defpsmacro functionp
(x)
82 `(string= (typeof ,x
) "function"))
86 (defpsmacro make-array
(&rest initial-values
)
87 `(new (*array
,@initial-values
)))
89 (defpsmacro length
(a)
90 `(getprop ,a
'length
))
94 (defpsmacro with-slots
(slots object
&rest body
)
95 (flet ((slot-var (slot)
103 `(symbol-macrolet ,(mapcar (lambda (slot)
104 `(,(slot-var slot
) (getprop ,object
',(slot-symbol slot
))))
110 (defpsmacro values
(&optional main
&rest additional
)
113 (with-ps-gensyms (val1 valrest
)
115 (,valrest
(list ,@additional
)))
116 (when (defined (@ arguments
:callee
:caller
:mv
))
117 (setf (@ arguments
:callee
:caller
:mv
) ,valrest
))
121 (defpsmacro multiple-value-bind
(vars form
&body body
)
122 (let* ((form (ps-macroexpand form
))
123 (progn-form (if (and (consp form
) (member (car form
) '(with label let flet labels macrolet symbol-macrolet
)))
127 (with-ps-gensyms (mv prev-mv
)
130 ,@(unless (eq 'progn progn-form
) (list (pop form
)))
132 (setf ,prev-mv
(@ arguments
:callee
:mv
))
135 (setf (@ arguments
:callee
:mv
) t
)
136 (let ((,(car vars
) ,(car (last form
)))
137 (,mv
(if (objectp (@ arguments
:callee
:mv
))
138 (@ arguments
:callee
:mv
)
139 (make-array ,(1- (length vars
))))))
140 (destructuring-bind ,(cdr vars
) ,mv
142 (:finally
(if (undefined ,prev-mv
)
143 (delete (@ arguments
:callee
:mv
))
144 (setf (@ arguments
:callee
:mv
) ,prev-mv
)))))))))
148 (defpsmacro case
(value &rest clauses
)
149 (labels ((make-clause (val body more
)
150 (cond ((and (listp val
) (not (eq (car val
) 'quote
)))
151 (append (mapcar #'list
(butlast val
))
152 (make-clause (first (last val
)) body more
)))
153 ((member val
'(t otherwise
))
154 (make-clause 'default body more
))
155 (more `((,val
,@body break
)))
156 (t `((,val
,@body
))))))
157 `(switch ,value
,@(mapcon (lambda (clause)
158 (make-clause (car (first clause
))
163 (defpsmacro when
(test &rest body
)
164 `(if ,test
(progn ,@body
)))
166 (defpsmacro unless
(test &rest body
)
167 `(when (not ,test
) ,@body
))
169 ;;; function definition
171 (defpsmacro defun
(name lambda-list
&body body
)
172 "An extended defun macro that allows cool things like keyword arguments.
175 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
177 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
178 [&aux {var | (var [init-form])}*])"
180 `(defun-function ,name
,lambda-list
,@body
)
181 (progn (assert (and (listp name
) (= (length name
) 2) (eq 'setf
(car name
))) ()
182 "(defun ~s ~s ...) needs to have a symbol or (setf symbol) for a name." name lambda-list
)
183 `(defun-setf ,name
,lambda-list
,@body
))))
185 (defpsmacro defun-function
(name lambda-list
&body body
)
186 (multiple-value-bind (effective-args effective-body
)
187 (parse-extended-function lambda-list body
)
188 `(%js-defun
,name
,effective-args
191 (defpsmacro lambda
(lambda-list &body body
)
192 "An extended defun macro that allows cool things like keyword arguments.
195 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
197 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
198 [&aux {var | (var [init-form])}*])"
199 (multiple-value-bind (effective-args effective-body
) (parse-extended-function lambda-list body
)
200 `(%js-lambda
,effective-args
,@effective-body
)))
202 ;;; defining setf expanders
204 (defvar *defun-setf-name-prefix
* '__setf_
)
206 (defpsmacro defun-setf
(setf-name lambda-list
&body body
)
207 (let ((mangled-function-name
208 (intern (concatenate 'string
(string *defun-setf-name-prefix
*) (string (second setf-name
)))
209 (symbol-package (second setf-name
)))))
210 (setf (gethash (second setf-name
) *setf-expanders
*)
213 (lambda (access-args store-form
)
214 `(,mangled-function-name
,store-form
,@access-args
))))
215 `(defun ,mangled-function-name
,lambda-list
,@body
)))
217 ;;; slightly broken WRT lambda lists
218 (defpsmacro defsetf-long
(access-fn lambda-list
(store-var) form
)
219 (setf (gethash access-fn
*setf-expanders
*)
222 (let ((var-bindings (ordered-set-difference lambda-list
223 lambda-list-keywords
)))
224 `(lambda (access-fn-args store-form
)
225 (destructuring-bind ,lambda-list
227 (let* ((,store-var
(ps-gensym))
228 (gensymed-names (loop repeat
,(length var-bindings
)
229 collecting
(ps-gensym)))
230 (gensymed-arg-bindings (mapcar #'list
232 (list ,@var-bindings
))))
233 (destructuring-bind ,var-bindings
235 `(let* (,@gensymed-arg-bindings
236 (,,store-var
,store-form
))
240 (defpsmacro defsetf-short
(access-fn update-fn
&optional docstring
)
241 (declare (ignore docstring
))
242 (setf (gethash access-fn
*setf-expanders
*)
243 (lambda (access-fn-args store-form
)
244 `(,update-fn
,@access-fn-args
,store-form
)))
247 (defpsmacro defsetf
(access-fn &rest args
)
248 `(,(if (= (length args
) 3) 'defsetf-long
'defsetf-short
) ,access-fn
,@args
))
252 (defpsmacro setf
(&rest args
)
253 (assert (evenp (length args
)) ()
254 "~s does not have an even number of arguments." `(setf ,args
))
255 `(progn ,@(loop for
(place value
) on args by
#'cddr collect
256 (aif (and (listp place
) (gethash (car place
) *setf-expanders
*))
257 (funcall it
(cdr place
) value
)
258 `(ps-assign ,place
,value
)))))
260 (defpsmacro psetf
(&rest args
)
261 (let ((places (loop for x in args by
#'cddr collect x
))
262 (vals (loop for x in
(cdr args
) by
#'cddr collect x
)))
263 (let ((gensyms (loop repeat
(length places
) collect
(ps-gensym))))
264 `(let ,(mapcar #'list gensyms vals
)
265 (setf ,@(mapcan #'list places gensyms
))))))
267 (defun check-setq-args (args)
268 (let ((vars (loop for x in args by
#'cddr collect x
)))
269 (let ((non-var (find-if (complement #'symbolp
) vars
)))
271 (error 'type-error
:datum non-var
:expected-type
'symbol
)))))
273 (defpsmacro setq
(&rest args
)
274 (check-setq-args args
)
277 (defpsmacro psetq
(&rest args
)
278 (check-setq-args args
)
283 (defun do-make-let-bindings (decls)
292 (defun do-make-init-vars (decls)
299 (defun do-make-init-vals (decls)
301 (if (or (atom x
) (endp (cdr x
)))
306 (defun do-make-for-vars/init
(decls)
313 (defun do-make-for-steps (decls)
315 `(setf ,(first x
) ,(third x
)))
316 (remove-if (lambda (x)
317 (or (atom x
) (< (length x
) 3)))
320 (defun do-make-iter-psteps (decls)
322 ,@(mapcan (lambda (x)
323 (list (first x
) (third x
)))
324 (remove-if (lambda (x)
325 (or (atom x
) (< (length x
) 3)))
328 (defpsmacro do
* (decls (termination &optional
(result nil result?
)) &body body
)
331 (for ,(do-make-for-vars/init decls
) ((not ,termination
)) ,(do-make-for-steps decls
)
334 `(for ,(do-make-for-vars/init decls
) ((not ,termination
)) ,(do-make-for-steps decls
)
337 (defpsmacro do
(decls (termination &optional
(result nil result?
)) &body body
)
339 `((lambda ,(do-make-init-vars decls
)
340 (for () ((not ,termination
)) ()
342 ,(do-make-iter-psteps decls
))
344 ,@(do-make-init-vals decls
))
345 `(let ,(do-make-let-bindings decls
)
346 (for () ((not ,termination
)) ()
348 ,(do-make-iter-psteps decls
)))))
350 (defpsmacro dotimes
((var count
&optional
(result nil result?
)) &rest body
)
351 `(do* ((,var
0 (1+ ,var
)))
352 ((>= ,var
,count
) ,@(when result?
(list result
)))
355 (defpsmacro dolist
((var array
&optional
(result nil result?
)) &body body
)
356 (let* ((idx (ps-gensym "_JS_IDX"))
357 (introduce-array-var?
(not (symbolp array
)))
358 (arrvar (if introduce-array-var?
359 (ps-gensym "_JS_ARRVAR")
362 ,@(when introduce-array-var?
363 (list (list arrvar array
)))
365 ((>= ,idx
(getprop ,arrvar
'length
))
366 ,@(when result?
(list result
)))
367 (setq ,var
(aref ,arrvar
,idx
))
372 (defpsmacro concatenate
(result-type &rest sequences
)
373 (assert (equal result-type
''string
) () "Right now Parenscript 'concatenate' only support strings.")
376 (defpsmacro append
(arr1 &rest arrs
)
378 `((@ ,arr1 concat
) ,@arrs
)
381 ;;; Destructuring bind
383 (defun destructuring-wrap (arr n bindings body
&key setf?
)
384 (labels ((bind-expr (var expr inner-body
)
386 `(progn (setf ,var
,expr
) ,inner-body
)
387 `(let ((,var
,expr
)) ,inner-body
)))
389 (bind-expr sym
`(if (> (length ,arr
) ,n
)
393 (cond ((null bindings
)
395 ((atom bindings
) ;; dotted destructuring list
396 (bind-rest bindings
))
397 ((eq (car bindings
) '&rest
)
398 (if (and (= (length bindings
) 2)
399 (atom (second bindings
)))
400 (bind-rest (second bindings
))
401 (error "~a is invalid in destructuring list." bindings
)))
402 ((eq (car bindings
) '&optional
)
403 (destructuring-wrap arr n
(cdr bindings
) body
:setf? setf?
))
404 (t (let ((var (car bindings
))
405 (inner-body (destructuring-wrap arr
(1+ n
) (cdr bindings
) body
:setf? setf?
)))
406 (cond ((null var
) inner-body
)
407 ((atom var
) (bind-expr var
`(aref ,arr
,n
) inner-body
))
408 (t `(,(if setf?
'dset
'destructuring-bind
)
412 (defpsmacro dset
(bindings expr
&body body
)
413 (let ((arr (if (complex-js-expr? expr
) (ps-gensym) expr
)))
415 ,@(unless (eq arr expr
) `((setf ,arr
,expr
)))
416 ,(destructuring-wrap arr
0 bindings
(cons 'progn body
) :setf? t
))))
418 (defpsmacro destructuring-bind
(bindings expr
&body body
)
419 (let* ((arr (if (complex-js-expr? expr
) (ps-gensym) expr
))
420 (bound (destructuring-wrap arr
0 bindings
(cons 'progn body
))))
423 `(let ((,arr
,expr
)) ,bound
))))
425 ;;; Control structures
427 (defpsmacro return
(&optional result
)
428 `(return-from nil
,result
))
430 (defpsmacro ignore-errors
(&body body
)
431 `(try (progn ,@body
) (:catch
(e))))
433 (defpsmacro prog1
(first &rest others
)
434 (with-ps-gensyms (val)
435 `(let ((,val
,first
))
439 (defpsmacro prog2
(first second
&rest others
)
440 `(progn ,first
(prog1 ,second
,@others
)))
442 (defpsmacro apply
(fn &rest args
)
443 (let ((arglist (if (> (length args
) 1)
444 `(append (list ,@(butlast args
)) ,(car (last args
)))
446 `(funcall (getprop ,fn
'apply
) this
,arglist
)))
450 (defpsmacro defvar
(name &optional
451 (value (values) value-provided?
)
453 ;; this must be used as a top-level form, otherwise the resulting
454 ;; behavior will be undefined.
455 (declare (ignore documentation
))
456 (pushnew name
*special-variables
*)
457 `(var ,name
,@(when value-provided?
(list value
))))
459 (defpsmacro let
* (bindings &body body
)
461 `(let (,(car bindings
))
462 (let* ,(cdr bindings
)
466 (defpsmacro in-package
(package-designator)
467 `(eval-when (:compile-toplevel
)
468 (in-package ,package-designator
)))
470 (defpsmacro use-package
(package-designator &optional package
)
471 `(eval-when (:compile-toplevel
)
472 (use-package ,package-designator
,@(when package
(list package
)))))