1 (in-package #:parenscript
)
2 (in-readtable :parenscript
)
4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 (defun parse-key-spec (key-spec)
8 "parses an &key parameter. Returns 5 values:
9 var, init-form, keyword-name, supplied-p-var, init-form-supplied-p.
12 [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}*
14 (let* ((var (cond ((symbolp key-spec
) key-spec
)
15 ((and (listp key-spec
) (symbolp (first key-spec
))) (first key-spec
))
16 ((and (listp key-spec
) (listp (first key-spec
))) (second (first key-spec
)))))
17 (keyword-name (if (and (listp key-spec
) (listp (first key-spec
)))
18 (first (first key-spec
))
19 (intern (string var
) :keyword
)))
20 (init-form (if (listp key-spec
) (second key-spec
) nil
))
21 (init-form-supplied-p (if (listp key-spec
) t nil
))
22 (supplied-p-var (if (listp key-spec
) (third key-spec
) nil
)))
23 (values var init-form keyword-name supplied-p-var init-form-supplied-p
)))
25 (defun parse-optional-spec (spec)
26 "Parses an &optional parameter. Returns 3 values: var, init-form, supplied-p-var.
27 [&optional {var | (var [init-form [supplied-p-parameter]])}*] "
28 (let* ((var (cond ((symbolp spec
) spec
)
29 ((and (listp spec
) (first spec
)))))
30 (init-form (if (listp spec
) (second spec
)))
31 (supplied-p-var (if (listp spec
) (third spec
))))
32 (values var init-form supplied-p-var
)))
34 (defun parse-extended-function (lambda-list body
)
35 "The lambda list is transformed as follows:
37 * standard and optional variables are the mapped directly into
40 * keyword variables are not included in the js-lambda list, but
41 instead are obtained from the magic js ARGUMENTS
42 pseudo-array. Code assigning values to keyword vars is
43 prepended to the body of the function."
44 (multiple-value-bind (requireds optionals rest? rest keys? keys allow? aux?
45 aux more? more-context more-count key-object
)
46 (parse-lambda-list lambda-list
)
47 (declare (ignore allow? aux? aux more? more-context more-count key-object
))
48 (let* ( ;; optionals are of form (var default-value)
52 (mapcar #'parse-optional-spec optionals
))))
54 (mapcar (lambda (opt-spec)
55 (multiple-value-bind (name value suppl
)
56 (parse-optional-spec opt-spec
)
59 (var ,suppl
(not (eql ,name undefined
)))
61 `((when (not ,suppl
) (setf ,name
,value
)))))
63 `(when (eql ,name undefined
)
64 (setf ,name
,value
))))))
69 (let (defaults assigns
)
72 (multiple-value-bind (var init-form keyword-str suppl
)
74 (push `(var ,var
,@(when init-form
`((if (undefined ,var
) ,init-form
,var
)))) defaults
)
75 (when suppl
(push `(var ,suppl
) defaults
))
77 (setf ,var
(aref arguments
(1+ ,n
))
78 ,@(when suppl
`(,suppl t
))))
81 `((loop for
,n from
,(length requireds
) below
(length arguments
) by
2 do
82 (case (aref arguments
,n
)
88 `(progn (var ,rest
(array))
89 (dotimes (,i
(- (getprop arguments
'length
)
90 ,(length effective-args
)))
94 (+ ,i
,(length effective-args
)))))))))
95 (docstring (and (cdr body
) (stringp (car body
)) (car body
)))
96 (effective-body (append opt-forms
98 (awhen rest-form
(list it
))
99 (if docstring
(rest body
) body
))))
100 (values effective-args effective-body docstring
))))
102 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
105 (defun collapse-function-return-blocks (body)
106 (append (butlast body
)
107 (let ((last (ps-macroexpand (car (last body
)))))
108 (if (and (listp last
) (eq 'block
(car last
)))
109 (progn (push (or (second last
) 'nilBlock
) *function-block-names
*)
113 (defun compile-function-body (args body
)
114 (with-declaration-effects (body body
)
115 (let* ((*enclosing-lexical-block-declarations
* ())
116 (*enclosing-function-arguments
*
117 (append args
*enclosing-function-arguments
*))
118 (*enclosing-lexicals
*
119 (set-difference *enclosing-lexicals
* args
))
120 (collapsed-body (collapse-function-return-blocks body
))
121 (*dynamic-return-tags
* (append (mapcar (lambda (x) (cons x nil
))
122 *function-block-names
*)
123 *dynamic-return-tags
*))
125 (let ((in-loop-scope? nil
)
126 (*loop-scope-lexicals
* ())
127 (*loop-scope-lexicals-captured
* ()))
129 `(return-from %function
(progn ,@collapsed-body
)))))
136 (remove-duplicates *enclosing-lexical-block-declarations
*))))))
137 (when in-loop-scope?
;; this is probably broken when it comes to let-renaming
138 (setf *loop-scope-lexicals-captured
*
139 (append (intersection (flatten body
) *loop-scope-lexicals
*)
140 *loop-scope-lexicals-captured
*)))
141 `(ps-js:block
,@(cdr var-decls
)
142 ,@(cdr (wrap-for-dynamic-return *function-block-names
* body
))))))
144 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
147 (define-expression-operator lambda
(lambda-list &rest body
)
148 (multiple-value-bind (effective-args effective-body
)
149 (parse-extended-function lambda-list body
)
150 `(ps-js:lambda
,effective-args
151 ,(let ((*function-block-names
* ()))
152 (compile-function-body effective-args effective-body
)))))
154 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
157 (defun compile-named-function-body (name lambda-list body
)
158 (let ((*enclosing-lexicals
* (cons name
*enclosing-lexicals
*))
159 (*function-block-names
* (list name
)))
160 (multiple-value-bind (effective-args effective-body docstring
)
161 (parse-extended-function lambda-list body
)
162 (values effective-args
163 (compile-function-body effective-args effective-body
)
166 (define-statement-operator defun%
(name lambda-list
&rest body
)
167 (multiple-value-bind (effective-args body-block docstring
)
168 (compile-named-function-body name lambda-list body
)
169 (list 'ps-js
:defun
name effective-args docstring body-block
)))
171 (defun maybe-rename-local-function (fun-name)
172 (or (getf *local-function-names
* fun-name
) fun-name
))
174 (defun collect-function-names (fn-defs)
175 (loop for
(fn-name) in fn-defs
177 collect
(if (or (member fn-name
*enclosing-lexicals
*)
178 (lookup-macro-def fn-name
*symbol-macro-env
*))
179 (ps-gensym (string fn-name
))
182 (define-expression-operator flet
(fn-defs &rest body
)
183 (let* ((fn-renames (collect-function-names fn-defs
))
184 ;; the function definitions need to be compiled with previous lexical bindings
186 (loop for
(fn-name .
(args . body
)) in fn-defs collect
187 (progn (when compile-expression?
188 (push (getf fn-renames fn-name
)
189 *enclosing-lexical-block-declarations
*))
190 (list (if compile-expression?
'ps-js
:= 'ps-js
:var
)
191 (getf fn-renames fn-name
)
192 (multiple-value-bind (args1 body-block
)
193 (compile-named-function-body fn-name args body
)
194 `(ps-js:lambda
,args1
,body-block
))))))
195 ;; the flet body needs to be compiled with the extended lexical environment
196 (*enclosing-lexicals
*
197 (append fn-renames
*enclosing-lexicals
*))
198 (*loop-scope-lexicals
*
199 (when in-loop-scope?
(append fn-renames
*loop-scope-lexicals
*)))
200 (*local-function-names
*
201 (append fn-renames
*local-function-names
*)))
202 `(,(if compile-expression?
'ps-js
:|
,|
'ps-js
:block
)
204 ,@(compile-progn body
))))
206 (define-expression-operator labels
(fn-defs &rest body
)
207 (let* ((fn-renames (collect-function-names fn-defs
))
208 (*local-function-names
* (append fn-renames
*local-function-names
*))
209 (*enclosing-lexicals
* (append fn-renames
*enclosing-lexicals
*))
210 (*loop-scope-lexicals
* (when in-loop-scope?
211 (append fn-renames
*loop-scope-lexicals
*))))
212 `(,(if compile-expression?
'ps-js
:|
,|
'ps-js
:block
)
213 ,@(loop for
(fn-name .
(args . body
)) in fn-defs collect
214 (progn (when compile-expression?
215 (push (getf *local-function-names
* fn-name
)
216 *enclosing-lexical-block-declarations
*))
217 (list (if compile-expression?
'ps-js
:= 'ps-js
:var
)
218 (getf *local-function-names
* fn-name
)
219 (let ((*function-block-names
* (list fn-name
)))
220 (compile-expression `(lambda ,args
,@body
))))))
221 ,@(compile-progn body
))))
223 (define-expression-operator function
(fn-name) ;; one of the things responsible for function namespace
224 (ps-compile (maybe-rename-local-function fn-name
)))