1 (in-package #:parenscript
)
2 (in-readtable :parenscript
)
4 (macrolet ((define-trivial-mappings (&rest mappings
)
6 ,@(loop for
(macro-name ps-op
) on mappings by
#'cddr collect
7 `(defpsmacro ,macro-name
(&rest args
)
8 (cons ',ps-op args
))))))
9 (define-trivial-mappings
21 (defmacro def-js-maths
(&rest mathdefs
)
22 `(progn ,@(mapcar (lambda (def) (cons 'defpsmacro def
)) mathdefs
)))
25 (max (&rest nums
) `((@ *math max
) ,@nums
))
26 (min (&rest nums
) `((@ *math min
) ,@nums
))
27 (floor (n &optional divisor
) `((@ *math floor
) ,(if divisor
`(/ ,n
,divisor
) n
)))
28 (ceiling (n &optional divisor
) `((@ *math ceil
) ,(if divisor
`(/ ,n
,divisor
) n
)))
29 (round (n &optional divisor
) `((@ *math round
) ,(if divisor
`(/ ,n
,divisor
) n
)))
30 (sin (n) `((@ *math sin
) ,n
))
31 (cos (n) `((@ *math cos
) ,n
))
32 (tan (n) `((@ *math tan
) ,n
))
33 (asin (n) `((@ *math asin
) ,n
))
34 (acos (n) `((@ *math acos
) ,n
))
35 (atan (y &optional x
) (if x
`((@ *math atan2
) ,y
,x
) `((@ *math atan
) ,y
)))
36 (sinh (n) `((lambda (x) (/ (- (exp x
) (exp (- x
))) 2)) ,n
))
37 (cosh (n) `((lambda (x) (/ (+ (exp x
) (exp (- x
))) 2)) ,n
))
38 (tanh (n) `((lambda (x) (/ (- (exp x
) (exp (- x
))) (+ (exp x
) (exp (- x
))))) ,n
))
39 (asinh (n) `((lambda (x) (log (+ x
(sqrt (1+ (* x x
)))))) ,n
))
40 (acosh (n) `((lambda (x) (* 2 (log (+ (sqrt (/ (1+ x
) 2)) (sqrt (/ (1- x
) 2)))))) ,n
))
41 (atanh (n) `((lambda (x) (/ (- (log (+ 1 x
)) (log (- 1 x
))) 2)) ,n
))
44 (abs (n) `((@ *math abs
) ,n
))
45 (evenp (n) `(not (oddp ,n
)))
46 (oddp (n) `(rem ,n
2))
47 (exp (n) `((@ *math exp
) ,n
))
48 (expt (base power
) `((@ *math pow
) ,base
,power
))
49 (log (n &optional base
)
50 (or (and (null base
) `((@ *math log
) ,n
))
51 (and (numberp base
) (= base
10) `(* (log ,n
) (@ *math
*log10e
*)))
52 `(/ (log ,n
) (log ,base
))))
53 (sqrt (n) `((@ *math sqrt
) ,n
))
54 (random (&optional upto
) (if upto
55 `(floor (* ,upto
(random)))
56 '(funcall (@ *math random
)))))
58 (defpsmacro ash
(integer count
)
59 (let ((count (ps-macroexpand count
)))
60 (cond ((and (numberp count
) (> count
0)) `(<< ,integer
,count
))
61 ((numberp count
) `(>> ,integer
,(- count
)))
62 ((complex-js-expr? count
)
63 (let ((count-var (ps-gensym)))
64 `(let ((,count-var
,count
))
66 (<< ,integer
,count-var
)
67 (>> ,integer
(- ,count-var
))))))
70 (>> ,integer
(- ,count
)))))))
72 (define-ps-symbol-macro pi
(getprop *math
'*pi
*))
76 (defpsmacro stringp
(x)
77 `(string= (typeof ,x
) "string"))
79 (defpsmacro numberp
(x)
80 `(string= (typeof ,x
) "number"))
82 (defpsmacro functionp
(x)
83 `(string= (typeof ,x
) "function"))
87 (defpsmacro make-array
(&rest initial-values
)
88 `(new (*array
,@initial-values
)))
90 (defpsmacro length
(a)
91 `(getprop ,a
'length
))
95 (defpsmacro with-slots
(slots object
&rest body
)
96 (flet ((slot-var (slot)
104 `(symbol-macrolet ,(mapcar (lambda (slot)
105 `(,(slot-var slot
) (getprop ,object
',(slot-symbol slot
))))
111 (defpsmacro values
(&optional main
&rest additional
)
114 (with-ps-gensyms (val1 valrest
)
116 (,valrest
(list ,@additional
)))
117 (when (defined (@ arguments
:callee
:caller
:mv
))
118 (setf (@ arguments
:callee
:caller
:mv
) ,valrest
))
122 (defpsmacro multiple-value-bind
(vars form
&body body
)
123 (let* ((form (ps-macroexpand form
))
124 (progn-form (when (and (consp form
) (member (car form
) '(with label let flet labels macrolet symbol-macrolet progn
)))
126 (with-ps-gensyms (mv prev-mv
)
128 (,(or progn-form
'progn
)
129 ,@(when progn-form
(butlast form
))
130 (setf ,prev-mv
(@ arguments
:callee
:mv
))
133 (setf (@ arguments
:callee
:mv
) t
)
134 (let ((,(car vars
) ,(if progn-form
(car (last form
)) form
))
135 (,mv
(if (objectp (@ arguments
:callee
:mv
))
136 (@ arguments
:callee
:mv
)
137 (make-array ,(1- (length vars
))))))
138 (destructuring-bind ,(cdr vars
) ,mv
140 (:finally
(if (undefined ,prev-mv
)
141 (delete (@ arguments
:callee
:mv
))
142 (setf (@ arguments
:callee
:mv
) ,prev-mv
)))))))))
146 (defpsmacro case
(value &rest clauses
)
147 (labels ((make-clause (val body more
)
148 (cond ((and (listp val
) (not (eq (car val
) 'quote
)))
149 (append (mapcar #'list
(butlast val
))
150 (make-clause (first (last val
)) body more
)))
151 ((member val
'(t otherwise
))
152 (make-clause 'default body more
))
153 (more `((,val
,@body break
)))
154 (t `((,val
,@body
))))))
155 `(switch ,value
,@(mapcon (lambda (clause)
156 (make-clause (car (first clause
))
161 (defpsmacro when
(test &rest body
)
162 `(if ,test
(progn ,@body
)))
164 (defpsmacro unless
(test &rest body
)
165 `(when (not ,test
) ,@body
))
167 ;;; function definition
169 (defpsmacro defun
(name lambda-list
&body body
)
170 "An extended defun macro that allows cool things like keyword arguments.
173 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
175 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
176 [&aux {var | (var [init-form])}*])"
178 `(defun%
,name
,lambda-list
,@body
)
179 (progn (assert (and (listp name
) (= (length name
) 2) (eq 'setf
(car name
))) ()
180 "(defun ~s ~s ...) needs to have a symbol or (setf symbol) for a name." name lambda-list
)
181 `(defun-setf ,name
,lambda-list
,@body
))))
183 ;;; defining setf expanders
185 (defvar *defun-setf-name-prefix
* '__setf_
)
187 (defpsmacro defun-setf
(setf-name lambda-list
&body body
)
188 (let ((mangled-function-name
189 (intern (concatenate 'string
(string *defun-setf-name-prefix
*) (string (second setf-name
)))
190 (symbol-package (second setf-name
)))))
191 (setf (gethash (second setf-name
) *setf-expanders
*)
194 (lambda (access-args store-form
)
195 `(,mangled-function-name
,store-form
,@access-args
))))
196 `(defun ,mangled-function-name
,lambda-list
,@body
)))
198 ;;; slightly broken WRT lambda lists
199 (defpsmacro defsetf-long
(access-fn lambda-list
(store-var) form
)
200 (setf (gethash access-fn
*setf-expanders
*)
203 (let ((var-bindings (ordered-set-difference lambda-list
204 lambda-list-keywords
)))
205 `(lambda (access-fn-args store-form
)
206 (destructuring-bind ,lambda-list
208 (let* ((,store-var
(ps-gensym))
209 (gensymed-names (loop repeat
,(length var-bindings
)
210 collecting
(ps-gensym)))
211 (gensymed-arg-bindings (mapcar #'list
213 (list ,@var-bindings
))))
214 (destructuring-bind ,var-bindings
216 `(let* (,@gensymed-arg-bindings
217 (,,store-var
,store-form
))
221 (defpsmacro defsetf-short
(access-fn update-fn
&optional docstring
)
222 (declare (ignore docstring
))
223 (setf (gethash access-fn
*setf-expanders
*)
224 (lambda (access-fn-args store-form
)
225 `(,update-fn
,@access-fn-args
,store-form
)))
228 (defpsmacro defsetf
(access-fn &rest args
)
229 `(,(if (= (length args
) 3) 'defsetf-long
'defsetf-short
) ,access-fn
,@args
))
233 (defpsmacro setf
(&rest args
)
234 (assert (evenp (length args
)) ()
235 "~s does not have an even number of arguments." `(setf ,args
))
236 `(progn ,@(loop for
(place value
) on args by
#'cddr collect
237 (aif (and (listp place
) (gethash (car place
) *setf-expanders
*))
238 (funcall it
(cdr place
) value
)
239 `(ps-assign ,place
,value
)))))
241 (defpsmacro psetf
(&rest args
)
242 (let ((places (loop for x in args by
#'cddr collect x
))
243 (vals (loop for x in
(cdr args
) by
#'cddr collect x
)))
244 (let ((gensyms (loop repeat
(length places
) collect
(ps-gensym))))
245 `(let ,(mapcar #'list gensyms vals
)
246 (setf ,@(mapcan #'list places gensyms
))))))
248 (defun check-setq-args (args)
249 (let ((vars (loop for x in args by
#'cddr collect x
)))
250 (let ((non-var (find-if (complement #'symbolp
) vars
)))
252 (error 'type-error
:datum non-var
:expected-type
'symbol
)))))
254 (defpsmacro setq
(&rest args
)
255 (check-setq-args args
)
258 (defpsmacro psetq
(&rest args
)
259 (check-setq-args args
)
264 (defun do-make-let-bindings (decls)
273 (defun do-make-init-vars (decls)
280 (defun do-make-init-vals (decls)
282 (if (or (atom x
) (endp (cdr x
)))
287 (defun do-make-for-vars/init
(decls)
294 (defun do-make-for-steps (decls)
296 `(setf ,(first x
) ,(third x
)))
297 (remove-if (lambda (x)
298 (or (atom x
) (< (length x
) 3)))
301 (defun do-make-iter-psteps (decls)
303 ,@(mapcan (lambda (x)
304 (list (first x
) (third x
)))
305 (remove-if (lambda (x)
306 (or (atom x
) (< (length x
) 3)))
309 (defpsmacro do
* (decls (termination &optional
(result nil result?
)) &body body
)
312 (for ,(do-make-for-vars/init decls
) ((not ,termination
)) ,(do-make-for-steps decls
)
315 `(for ,(do-make-for-vars/init decls
) ((not ,termination
)) ,(do-make-for-steps decls
)
318 (defpsmacro do
(decls (termination &optional
(result nil result?
)) &body body
)
320 `((lambda ,(do-make-init-vars decls
)
321 (for () ((not ,termination
)) ()
323 ,(do-make-iter-psteps decls
))
325 ,@(do-make-init-vals decls
))
326 `(let ,(do-make-let-bindings decls
)
327 (for () ((not ,termination
)) ()
329 ,(do-make-iter-psteps decls
)))))
331 (defpsmacro dotimes
((var count
&optional
(result nil result?
)) &rest body
)
332 `(do* ((,var
0 (1+ ,var
)))
333 ((>= ,var
,count
) ,@(when result?
(list result
)))
336 (defpsmacro dolist
((var array
&optional
(result nil result?
)) &body body
)
337 (let* ((idx (ps-gensym "_JS_IDX"))
338 (introduce-array-var?
(not (symbolp array
)))
339 (arrvar (if introduce-array-var?
340 (ps-gensym "_JS_ARRVAR")
343 ,@(when introduce-array-var?
344 (list (list arrvar array
)))
346 ((>= ,idx
(getprop ,arrvar
'length
))
347 ,@(when result?
(list result
)))
348 (setq ,var
(aref ,arrvar
,idx
))
353 (defpsmacro concatenate
(result-type &rest sequences
)
354 (assert (equal result-type
''string
) () "Right now Parenscript 'concatenate' only support strings.")
357 (defpsmacro append
(arr1 &rest arrs
)
359 `((@ ,arr1 concat
) ,@arrs
)
362 ;;; Destructuring bind
364 (defun destructuring-wrap (arr n bindings body
&key setf?
)
365 (labels ((bind-expr (var expr inner-body
)
367 `(progn (setf ,var
,expr
) ,inner-body
)
368 `(let ((,var
,expr
)) ,inner-body
)))
370 (bind-expr sym
`(if (> (length ,arr
) ,n
)
374 (cond ((null bindings
)
376 ((atom bindings
) ;; dotted destructuring list
377 (bind-rest bindings
))
378 ((eq (car bindings
) '&rest
)
379 (if (and (= (length bindings
) 2)
380 (atom (second bindings
)))
381 (bind-rest (second bindings
))
382 (error "~a is invalid in destructuring list." bindings
)))
383 ((eq (car bindings
) '&optional
)
384 (destructuring-wrap arr n
(cdr bindings
) body
:setf? setf?
))
385 (t (let ((var (car bindings
))
386 (inner-body (destructuring-wrap arr
(1+ n
) (cdr bindings
) body
:setf? setf?
)))
387 (cond ((null var
) inner-body
)
388 ((atom var
) (bind-expr var
`(aref ,arr
,n
) inner-body
))
389 (t `(,(if setf?
'dset
'destructuring-bind
)
393 (defpsmacro dset
(bindings expr
&body body
)
394 (let ((arr (if (complex-js-expr? expr
) (ps-gensym) expr
)))
396 ,@(unless (eq arr expr
) `((setf ,arr
,expr
)))
397 ,(destructuring-wrap arr
0 bindings
(cons 'progn body
) :setf? t
))))
399 (defpsmacro destructuring-bind
(bindings expr
&body body
)
400 (let* ((arr (if (complex-js-expr? expr
) (ps-gensym) expr
))
401 (bound (destructuring-wrap arr
0 bindings
(cons 'progn body
))))
404 `(let ((,arr
,expr
)) ,bound
))))
406 ;;; Control structures
408 (defpsmacro return
(&optional result
)
409 `(return-from nil
,result
))
411 (defpsmacro ignore-errors
(&body forms
)
413 `(try (progn ,@forms
)
416 (defpsmacro prog1
(first &rest others
)
417 (with-ps-gensyms (val)
418 `(let ((,val
,first
))
422 (defpsmacro prog2
(first second
&rest others
)
423 `(progn ,first
(prog1 ,second
,@others
)))
425 (defpsmacro apply
(fn &rest args
)
426 (let ((arglist (if (> (length args
) 1)
427 `(append (list ,@(butlast args
)) ,(car (last args
)))
429 `(funcall (getprop ,fn
'apply
) this
,arglist
)))
433 (defpsmacro defvar
(name &optional
434 (value (values) value-provided?
)
436 ;; this must be used as a top-level form, otherwise the resulting
437 ;; behavior will be undefined.
438 (declare (ignore documentation
))
439 (pushnew name
*special-variables
*)
440 `(var ,name
,@(when value-provided?
(list value
))))
442 (defpsmacro let
* (bindings &body body
)
444 `(let (,(car bindings
))
445 (let* ,(cdr bindings
)
449 (defpsmacro in-package
(package-designator)
450 `(eval-when (:compile-toplevel
)
451 (in-package ,package-designator
)))
453 (defpsmacro use-package
(package-designator &optional package
)
454 `(eval-when (:compile-toplevel
)
455 (use-package ,package-designator
,@(when package
(list package
)))))