1 ;; SPDX-License-Identifier: BSD-3-Clause
3 (in-package #:parenscript
)
4 (in-readtable :parenscript
)
6 (macrolet ((define-trivial-mappings (&rest mappings
)
8 ,@(loop for
(macro-name ps-op
) on mappings by
#'cddr collect
9 `(defpsmacro ,macro-name
(&rest args
)
10 (cons ',ps-op args
))))))
11 (define-trivial-mappings
23 (defmacro def-js-maths
(&rest mathdefs
)
24 `(progn ,@(mapcar (lambda (def) (cons 'defpsmacro def
)) mathdefs
)))
27 (max (&rest nums
) `((@ *math max
) ,@nums
))
28 (min (&rest nums
) `((@ *math min
) ,@nums
))
29 (floor (n &optional divisor
)
30 `((@ *math floor
) ,(if divisor
`(/ ,n
,divisor
) n
)))
31 (ceiling (n &optional divisor
)
32 `((@ *math ceil
) ,(if divisor
`(/ ,n
,divisor
) n
)))
33 (round (n &optional divisor
)
34 `((@ *math round
) ,(if divisor
`(/ ,n
,divisor
) n
)))
35 (sin (n) `((@ *math sin
) ,n
))
36 (cos (n) `((@ *math cos
) ,n
))
37 (tan (n) `((@ *math tan
) ,n
))
38 (asin (n) `((@ *math asin
) ,n
))
39 (acos (n) `((@ *math acos
) ,n
))
40 (atan (y &optional x
) (if x
`((@ *math atan2
) ,y
,x
) `((@ *math atan
) ,y
)))
43 `(/ (- (exp ,x
) (exp (- ,x
))) 2)))
46 `(/ (+ (exp ,x
) (exp (- ,x
))) 2)))
49 `(/ (- (exp ,x
) (exp (- ,x
))) (+ (exp ,x
) (exp (- ,x
))))))
52 `(log (+ ,x
(sqrt (1+ (* ,x
,x
)))))))
55 `(* 2 (log (+ (sqrt (/ (1+ ,x
) 2)) (sqrt (/ (1- ,x
) 2)))))))
56 (atanh (x) ;; real only for -1 < x < 1, otherwise complex
58 `(/ (- (log (+ 1 ,x
)) (log (- 1 ,x
))) 2)))
61 `(rem (+ (rem ,x
,n
) ,n
) ,n
)))
64 (abs (n) `((@ *math abs
) ,n
))
65 (evenp (n) `(not (oddp ,n
)))
66 (oddp (n) `(rem ,n
2))
67 (exp (n) `((@ *math exp
) ,n
))
68 (expt (base power
) `((@ *math pow
) ,base
,power
))
69 (log (n &optional base
)
70 (or (and (null base
) `((@ *math log
) ,n
))
71 (and (numberp base
) (= base
10) `(* (log ,n
) (@ *math
*log10e
*)))
72 `(/ (log ,n
) (log ,base
))))
73 (sqrt (n) `((@ *math sqrt
) ,n
))
74 (random (&optional upto
) (if upto
75 `(floor (* ,upto
(random)))
76 '(funcall (@ *math random
)))))
78 (defpsmacro ash
(integer count
)
79 (let ((count (ps-macroexpand count
)))
80 (cond ((and (numberp count
) (> count
0)) `(<< ,integer
,count
))
81 ((numberp count
) `(>> ,integer
,(- count
)))
82 ((complex-js-expr? count
)
83 (let ((count-var (ps-gensym)))
84 `(let ((,count-var
,count
))
86 (<< ,integer
,count-var
)
87 (>> ,integer
(- ,count-var
))))))
90 (>> ,integer
(- ,count
)))))))
92 (define-ps-symbol-macro pi
(getprop *math
'*pi
*))
96 (defpsmacro stringp
(x)
97 `(string= (typeof ,x
) "string"))
99 (defpsmacro numberp
(x)
100 `(string= (typeof ,x
) "number"))
102 (defpsmacro functionp
(x)
103 `(string= (typeof ,x
) "function"))
105 (defpsmacro booleanp
(x)
106 `(string= (typeof ,x
) "boolean"))
110 (defpsmacro make-array
(&rest args
)
112 (destructuring-bind (dim &key
(initial-element nil initial-element-p
)
113 initial-contents element-type
)
115 (declare (ignore element-type
))
116 (and (or initial-element-p initial-contents
)
117 (not (and initial-element-p initial-contents
))
118 (with-ps-gensyms (arr init elt i
)
119 `(let ((,arr
(new (*array
,dim
))))
120 ,@(when initial-element-p
121 `((let ((,elt
,initial-element
))
122 (dotimes (,i
(length ,arr
))
123 (setf (aref ,arr
,i
) ,elt
)))))
124 ,@(when initial-contents
125 `((let ((,init
,initial-contents
))
126 (dotimes (,i
(min (length ,arr
) (length ,init
)))
127 (setf (aref ,arr
,i
) (aref ,init
,i
))))))
129 `(new (*array
,@args
))))
131 (defpsmacro length
(a)
132 `(getprop ,a
'length
))
136 (defpsmacro with-slots
(slots object
&rest body
)
137 (flet ((slot-var (slot)
145 (maybe-once-only (object)
146 `(symbol-macrolet ,(mapcar (lambda (slot)
147 `(,(slot-var slot
) (getprop ,object
',(slot-symbol slot
))))
153 (defpsmacro multiple-value-bind
(vars form
&body body
)
154 (let* ((form (ps-macroexpand form
))
156 (when (and (consp form
)
159 '(with label let flet labels macrolet symbol-macrolet progn
)))
164 (multiple-value-bind ,vars
167 ;; assume function call
168 (with-ps-gensyms (prev-mv)
169 (let* ((fun-exp (car form
))
170 (funobj (if (symbolp fun-exp
)
172 (ps-gensym "funobj"))))
173 `(let (,@(unless (symbolp fun-exp
) `((,funobj
,fun-exp
)))
174 (,prev-mv
(if (undefined __PS_MV_REG
)
175 (setf __PS_MV_REG undefined
)
178 (let ((,(car vars
) (,funobj
,@(cdr form
))))
179 (destructuring-bind (&optional
,@(cdr vars
))
180 (if (eql ,funobj
(@ __PS_MV_REG
:tag
))
181 (@ __PS_MV_REG
:values
)
184 (:finally
(setf __PS_MV_REG
,prev-mv
)))))))))
188 (defpsmacro case
(value &rest clauses
)
189 (let ((allowed-symbols '(t otherwise false %true
)))
190 (labels ((make-switch-clause (val body more
)
192 (append (mapcar #'list
(butlast val
))
194 (if (eq t
(car (last val
))) ;; literal 'true'
200 (symbolp (ps-macroexpand-1 val
))
202 (not (member val allowed-symbols
)))
203 (error "Parenscript only supports keywords, numbers, and string literals as keys in case clauses. ~S is a symbol in clauses ~S"
207 ((t otherwise
) 'default
)
209 (t (ps-macroexpand-1 val
)))
211 ,@(when more
'(break))))))))
212 `(switch ,value
,@(mapcon (lambda (clause)
213 (make-switch-clause (car (first clause
))
218 (defpsmacro when
(test &rest body
)
219 `(if ,test
(progn ,@body
)))
221 (defpsmacro unless
(test &rest body
)
222 `(when (not ,test
) ,@body
))
224 ;;; function definition
226 (defpsmacro defun
(name lambda-list
&body body
)
227 "An extended defun macro that allows cool things like keyword arguments.
230 [&optional {var | (var [init-form [supplied-p-parameter]])}*]
232 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
233 [&aux {var | (var [init-form])}*])"
235 (progn (setf (gethash name
*function-lambda-list
*) lambda-list
)
236 `(defun%
,name
,lambda-list
,@body
))
237 (progn (assert (and (listp name
) (= (length name
) 2) (eq 'setf
(car name
))) ()
238 "(defun ~s ~s ...) needs to have a symbol or (setf symbol) for a name." name lambda-list
)
239 `(defun-setf ,(second name
) ,lambda-list
,@body
))))
241 ;;; defining setf expanders
243 (defvar *defun-setf-name-prefix
* '__setf_
)
245 (defpsmacro defun-setf
(name lambda-list
&body body
)
246 (let ((mangled-function-name
247 (intern (format nil
"~A~A" (string *defun-setf-name-prefix
*) (string name
))
248 (symbol-package name
))))
249 (setf (gethash name
*setf-expanders
*)
250 (lambda (access-args store-form
)
251 `(,mangled-function-name
,store-form
,@access-args
)))
252 `(defun ,mangled-function-name
,lambda-list
,@body
)))
254 ;;; slightly broken WRT lambda lists
255 (defpsmacro defsetf-long
(access-fn lambda-list
(store-var) form
)
256 (setf (gethash access-fn
*setf-expanders
*)
259 (let ((var-bindings (ordered-set-difference lambda-list
260 lambda-list-keywords
)))
261 `(lambda (access-fn-args store-form
)
262 (destructuring-bind ,lambda-list
264 (let* ((,store-var
(ps-gensym))
265 (gensymed-names (loop repeat
,(length var-bindings
)
266 collecting
(ps-gensym)))
267 (gensymed-arg-bindings (mapcar #'list
269 (list ,@var-bindings
))))
270 (destructuring-bind ,var-bindings
272 `(let* (,@gensymed-arg-bindings
273 (,,store-var
,store-form
))
277 (defpsmacro defsetf-short
(access-fn update-fn
&optional docstring
)
278 (declare (ignore docstring
))
279 (setf (gethash access-fn
*setf-expanders
*)
280 (lambda (access-fn-args store-form
)
281 `(,update-fn
,@access-fn-args
,store-form
)))
284 (defpsmacro defsetf
(access-fn &rest args
)
285 `(,(if (= (length args
) 3) 'defsetf-long
'defsetf-short
) ,access-fn
,@args
))
289 (defpsmacro setf
(&rest args
)
290 (assert (evenp (length args
)) ()
291 "~s does not have an even number of arguments." `(setf ,args
))
292 `(progn ,@(loop for
(place value
) on args by
#'cddr collect
293 (aif (and (listp place
) (gethash (car place
) *setf-expanders
*))
294 (funcall it
(cdr place
) value
)
295 `(ps-assign ,place
,value
)))))
297 (defpsmacro psetf
(&rest args
)
298 (let ((places (loop for x in args by
#'cddr collect x
))
299 (vals (loop for x in
(cdr args
) by
#'cddr collect x
)))
300 (let ((gensyms (loop repeat
(length places
) collect
(ps-gensym))))
301 `(let ,(mapcar #'list gensyms vals
)
302 (setf ,@(mapcan #'list places gensyms
))))))
304 (defun check-setq-args (args)
305 (let ((vars (loop for x in args by
#'cddr collect x
)))
306 (let ((non-var (find-if (complement #'symbolp
) vars
)))
308 (error 'type-error
:datum non-var
:expected-type
'symbol
)))))
310 (defpsmacro setq
(&rest args
)
311 (check-setq-args args
)
314 (defpsmacro psetq
(&rest args
)
315 (check-setq-args args
)
320 (defun do-make-iteration-bindings (decls)
323 ((endp (cdr x
)) (list (car x
)))
327 (defun do-make-for-steps (decls)
329 `(setf ,(first x
) ,(third x
)))
330 (remove-if (lambda (x)
331 (or (atom x
) (< (length x
) 3)))
334 (defun do-make-iter-psteps (decls)
336 ,@(mapcan (lambda (x)
337 (list (first x
) (third x
)))
338 (remove-if (lambda (x)
339 (or (atom x
) (< (length x
) 3)))
342 (defpsmacro do
* (decls (end-test &optional
(result nil result?
)) &body body
)
344 (for ,(do-make-iteration-bindings decls
)
346 ,(do-make-for-steps decls
)
348 ,@(when result?
(list result
))))
350 (defpsmacro do
(decls (end-test &optional
(result nil result?
)) &body body
)
351 (multiple-value-bind (declarations executable-body
) (parse-body body
)
353 (let ,(do-make-iteration-bindings decls
)
355 (for () ((not ,end-test
)) ()
357 ,(do-make-iter-psteps decls
))
358 ,@(when result?
(list result
))))))
360 (defpsmacro dotimes
((var count
&optional
(result nil result?
)) &rest body
)
361 `(do* ((,var
0 (1+ ,var
)))
362 ((>= ,var
,count
) ,@(when result?
(list result
)))
365 (defpsmacro dolist
((var array
&optional
(result nil result?
)) &body body
)
366 (let* ((idx (ps-gensym "_JS_IDX"))
367 (introduce-array-var?
(not (symbolp array
)))
368 (arrvar (if introduce-array-var?
369 (ps-gensym "_JS_ARRVAR")
372 ,@(when introduce-array-var?
373 (list (list arrvar array
)))
375 ((>= ,idx
(getprop ,arrvar
'length
))
376 ,@(when result?
(list result
)))
377 (setq ,var
(aref ,arrvar
,idx
))
382 (defpsmacro concatenate
(result-type &rest sequences
)
383 (assert (equal result-type
''string
) () "Right now Parenscript 'concatenate' only support strings.")
386 (defpsmacro append
(arr1 &rest arrs
)
388 `((@ ,arr1 concat
) ,@arrs
)
391 ;;; Destructuring bind
393 (defun complex-js-expr?
(expr)
394 (consp (if (symbolp expr
) (ps-macroexpand expr
) expr
)))
396 (defun hoist-expr?
(bindings expr
)
397 (and (> (length bindings
) 1) (complex-js-expr? expr
)))
399 (defun pop-declarations-for-var (var declarations
)
400 (loop for declarations
* on declarations
401 with var-declarations
= nil
402 do
(setf (first declarations
*)
403 (loop for spec in
(first declarations
*)
404 ;; We only care for SPECIAL declarations for now
405 ;; (cf. WITH-DECLARATION-EFFECTS)
406 if
(and (consp spec
) (eq 'special
(first spec
)))
408 (let ((vars* (remove var
(rest spec
))))
409 (if (eq vars
* (cdr spec
))
412 (pushnew var
(getf var-declarations
'special
))
413 (cons 'special vars
*))))
417 (loop for
(sym decls
) on var-declarations by
#'cddr
418 collect
(cons sym decls
)))))
420 (defun destructuring-wrap (arr n bindings declarations body
)
421 (cond ((null bindings
) body
)
422 ((eq (car bindings
) '&rest
)
423 (cond ((and (= (length bindings
) 2) (atom (second bindings
)))
424 `(let ((,(second bindings
) (if (> (length ,arr
) ,n
) ((@ ,arr slice
) ,n
) '())))
425 (declare ,@(pop-declarations-for-var (second bindings
) declarations
))
427 (t (error "~a is invalid in destructuring list." bindings
))))
428 ((eq (car bindings
) '&optional
)
429 (destructuring-wrap arr n
(cdr bindings
) declarations body
))
430 (t (let ((var (car bindings
))
431 (inner-body (destructuring-wrap arr
(1+ n
) (cdr bindings
) declarations body
)))
432 (cond ((null var
) inner-body
)
433 ((atom var
) `(let ((,var
(aref ,arr
,n
)))
434 (declare ,@(pop-declarations-for-var var declarations
))
436 (t `(,'destructuring-bind
,var
(aref ,arr
,n
)
440 (defpsmacro destructuring-bind
(bindings expr
&body body
)
441 (setf bindings
(dot->rest bindings
))
442 (multiple-value-bind (declarations executable-body
) (parse-body body
)
443 (let* ((arr (if (hoist-expr? bindings expr
) (ps-gensym "_DB") expr
))
444 (bound (destructuring-wrap arr
0 bindings declarations
445 (cons 'progn executable-body
))))
446 (cond ((eq arr expr
) bound
)
447 (t `(let ((,arr
,expr
)) ,bound
))))))
449 ;;; Control structures
451 (defpsmacro return
(&optional result
)
452 `(return-from nil
,result
))
454 (defpsmacro ignore-errors
(&body forms
)
456 `(try (progn ,@forms
)
459 (defpsmacro unwind-protect
(protected-form cleanup-form
)
460 `(try ,protected-form
461 (:finally
,cleanup-form
)))
463 (defpsmacro prog1
(first &rest others
)
464 (with-ps-gensyms (val)
465 `(let ((,val
,first
))
469 (defpsmacro prog2
(first second
&rest others
)
470 `(progn ,first
(prog1 ,second
,@others
)))
472 (defpsmacro apply
(fn &rest args
)
473 (let ((arglist (if (> (length args
) 1)
474 `(append (list ,@(butlast args
)) ,(car (last args
)))
477 (find (car fn
) #(getprop chain
@)))
478 (if (and (= (length fn
) 3) (symbolp (second fn
)))
479 `(funcall (getprop ,fn
'apply
) ,(second fn
) ,arglist
)
480 (let ((obj (ps-gensym)) (method (ps-gensym)))
481 `(let* ((,obj
,(butlast fn
))
482 (,method
(,(car fn
) ,obj
,(car (last fn
)))))
483 (funcall (getprop ,method
'apply
) ,obj
,arglist
))))
484 `(funcall (getprop ,fn
'apply
) this
,arglist
))))
488 (defpsmacro let
* (bindings &body body
)
489 (multiple-value-bind (declarations executive-body
) (parse-body body
)
490 (loop for binding in
(cons nil
(reverse bindings
))
491 for var
= (if (symbolp binding
) binding
(car binding
))
492 for body
= executive-body
493 then
`((let (,binding
)
494 (declare ,@(pop-declarations-for-var var declarations
))
496 finally
(return `(progn ,@body
)))))
498 (defpsmacro in-package
(package-designator)
499 `(eval-when (:compile-toplevel
)
500 (in-package ,package-designator
)))
502 (defpsmacro use-package
(package-designator &optional package
)
503 `(eval-when (:compile-toplevel
)
504 (use-package ,package-designator
,@(when package
(list package
)))))