1 (in-package #:parenscript
)
3 (defparameter %compiling-reserved-forms-p% t
4 "Used to issue warnings when replacing PS special operators or macros.")
6 (defvar *ps-defined-operators
* ()
7 "Special operators and macros defined by Parenscript. Replace at your own risk!")
9 (defun defined-operator-override-check (name &rest body
)
10 (when (and (not %compiling-reserved-forms-p%
) (member name
*ps-defined-operators
*))
11 (warn "Redefining Parenscript operator/macro ~A" name
))
12 `(progn ,(when %compiling-reserved-forms-p%
`(pushnew ',name
*ps-defined-operators
*))
15 (defvar *reserved-symbol-names
*
16 (list "break" "case" "catch" "continue" "default" "delete" "do" "else"
17 "finally" "for" "function" "if" "in" "instanceof" "new" "return"
18 "switch" "this" "throw" "try" "typeof" "var" "void" "while" "with"
19 "abstract" "boolean" "byte" "char" "class" "const" "debugger" "double"
20 "enum" "export" "extends" "final" "float" "goto" "implements" "import"
21 "int" "interface" "long" "native" "package" "private" "protected"
22 "public" "short" "static" "super" "synchronized" "throws" "transient"
23 "volatile" "{}" "true" "false" "null" "undefined"))
25 (defun reserved-symbol?
(symbol)
26 (find (string-downcase (symbol-name symbol
)) *reserved-symbol-names
* :test
#'string
=))
30 (defvar *special-expression-operators
* (make-hash-table :test
'eq
))
31 (defvar *special-statement-operators
* (make-hash-table :test
'eq
))
33 ;; need to split special op definition into two parts - statement and expression
34 (defmacro %define-special-operator
(type name lambda-list
&body body
)
35 (defined-operator-override-check name
36 `(setf (gethash ',name
,type
)
38 (destructuring-bind ,lambda-list whole
41 (defmacro define-expression-operator
(name lambda-list
&body body
)
42 `(%define-special-operator
*special-expression-operators
* ,name
,lambda-list
,@body
))
44 (defmacro define-statement-operator
(name lambda-list
&body body
)
45 `(%define-special-operator
*special-statement-operators
* ,name
,lambda-list
,@body
))
47 (defun special-form?
(form)
50 (or (gethash (car form
) *special-expression-operators
*) (gethash (car form
) *special-statement-operators
*))))
52 ;;; scoping and lexical environment
54 (defvar *enclosing-lexical-block-declarations
* ()
55 "This special variable is expected to be bound to a fresh list by
56 special forms that introduce a new JavaScript lexical block (currently
57 function definitions and lambdas). Enclosed special forms are expected
58 to push variable declarations onto the list when the variables
59 declaration cannot be made by the enclosed form for example, a
60 x,y,z expression progn. It is then the responsibility of the
61 enclosing special form to introduce the variable bindings in its
64 (defvar in-loop-scope? nil
65 "Used for seeing when we're in loops, so that we can introduce
66 proper scoping for lambdas closing over loop-bound
67 variables (otherwise they all share the same binding).")
69 (defvar *loop-scope-lexicals
*)
70 (defvar *loop-scope-lexicals-captured
*)
72 (defvar *function-block-name
* nil
)
73 (defvar *lexical-extent-return-tags
* ())
74 (defvar *dynamic-extent-return-tags
* ())
75 (defvar *tags-that-return-throws-to
*)
77 (defvar *special-variables
* ())
79 (defun special-variable?
(sym)
80 (member sym
*special-variables
*))
83 (defun make-macro-dictionary ()
84 (make-hash-table :test
'eq
))
86 (defvar *macro-toplevel
* (make-macro-dictionary)
87 "Toplevel macro environment dictionary.")
89 (defvar *macro-env
* (list *macro-toplevel
*)
90 "Current macro environment.")
92 (defvar *symbol-macro-toplevel
* (make-macro-dictionary))
94 (defvar *symbol-macro-env
* (list *symbol-macro-toplevel
*))
96 (defvar *local-function-names
* ())
98 (defvar *enclosing-lexicals
* ())
100 (defvar *setf-expanders
* (make-macro-dictionary)
101 "Setf expander dictionary. Key is the symbol of the access
102 function of the place, value is an expansion function that takes the
103 arguments of the access functions as a first value and the form to be
104 stored as the second value.")
106 (defun lookup-macro-def (name env
)
107 (loop for e in env thereis
(gethash name e
)))
109 (defun make-ps-macro-function (args body
)
110 (let* ((whole-var (when (eql '&whole
(first args
)) (second args
)))
111 (effective-lambda-list (if whole-var
(cddr args
) args
))
112 (whole-arg (or whole-var
(gensym "ps-macro-form-arg-"))))
113 `(lambda (,whole-arg
)
114 (destructuring-bind ,effective-lambda-list
118 (defmacro defpsmacro
(name args
&body body
)
119 (defined-operator-override-check name
120 `(setf (gethash ',name
*macro-toplevel
*) ,(make-ps-macro-function args body
))))
122 (defmacro define-ps-symbol-macro
(symbol expansion
)
123 (defined-operator-override-check symbol
124 `(setf (gethash ',symbol
*symbol-macro-toplevel
*) (lambda (form) (declare (ignore form
)) ',expansion
))))
126 (defun import-macros-from-lisp (&rest names
)
127 "Import the named Lisp macros into the Parenscript macro
128 environment. When the imported macro is macroexpanded by Parenscript,
129 it is first fully macroexpanded in the Lisp macro environment, and
130 then that expansion is further expanded by Parenscript."
132 (eval `(defpsmacro ,name
(&rest args
)
133 (macroexpand `(,',name
,@args
))))))
135 (defmacro defmacro
+ps
(name args
&body body
)
136 "Define a Lisp macro and a Parenscript macro with the same macro
137 function (ie - the same result from macroexpand-1), for cases when the
138 two have different full macroexpansions (for example if the CL macro
139 contains implementation-specific code when macroexpanded fully in the
141 `(progn (defmacro ,name
,args
,@body
)
142 (defpsmacro ,name
,args
,@body
)))
144 (defun ps-macroexpand-1 (form)
145 (aif (or (and (symbolp form
) (lookup-macro-def form
*symbol-macro-env
*))
146 (and (consp form
) (lookup-macro-def (car form
) *macro-env
*)))
147 (values (ps-macroexpand (funcall it form
)) t
)
150 (defun ps-macroexpand (form)
151 (multiple-value-bind (form1 expanded?
) (ps-macroexpand-1 form
)
153 (values (ps-macroexpand form1
) t
)
156 ;;;; compiler interface
158 (defparameter *compilation-level
* :toplevel
159 "This value takes on the following values:
160 :toplevel indicates that we are traversing toplevel forms.
161 :inside-toplevel-form indicates that we are inside a call to ps-compile-*
162 nil indicates we are no longer toplevel-related.")
164 (defun adjust-compilation-level (form level
)
165 "Given the current *compilation-level*, LEVEL, and the fully macroexpanded
166 form, FORM, returns the new value for *compilation-level*."
167 (cond ((or (and (consp form
) (member (car form
) '(progn locally macrolet symbol-macrolet
)))
168 (and (symbolp form
) (eq :toplevel level
)))
170 ((eq :toplevel level
)
171 :inside-toplevel-form
)))
173 (defvar compile-expression?
)
175 (define-condition compile-expression-error
(error)
176 ((form :initarg
:form
:reader error-form
))
177 (:report
(lambda (condition stream
)
178 (format stream
"The Parenscript form ~A cannot be compiled into an expression." (error-form condition
)))))
180 (defun compile-special-form (form)
181 (apply (if compile-expression?
182 (or (gethash (car form
) *special-expression-operators
*)
183 (error 'compile-expression-error
:form form
))
184 (or (gethash (car form
) *special-statement-operators
*)
185 (gethash (car form
) *special-expression-operators
*)))
188 (defun ps-compile (form)
190 ((or null number string character
) form
)
192 (multiple-value-bind (expansion expanded?
) (ps-macroexpand form
)
194 (ps-compile expansion
)
197 (let ((*compilation-level
* (adjust-compilation-level form
*compilation-level
*)))
198 (if (special-form? form
)
199 (compile-special-form form
)
200 `(ps-js:funcall
,(if (symbolp (car form
))
201 (maybe-rename-local-function (car form
))
202 (compile-expression (car form
)))
203 ,@(mapcar #'compile-expression
(cdr form
)))))))))
204 (vector (ps-compile `(quote ,(coerce form
'list
))))))
206 (defun compile-statement (form)
207 (let ((compile-expression? nil
))
210 (defun compile-expression (form)
211 (let ((compile-expression? t
))
214 (defvar *ps-gensym-counter
* 0)
216 (defun ps-gensym (&optional
(prefix "_js"))
217 (let ((prefix (if (stringp prefix
) prefix
(symbol-to-js-string prefix nil
))))
218 (make-symbol (format nil
"~A~:[~;_~]~A" prefix
219 (digit-char-p (char prefix
(1- (length prefix
))))
220 (incf *ps-gensym-counter
*)))))
222 (defmacro with-ps-gensyms
(symbols &body body
)
223 "Each element of SYMBOLS is either a symbol or a list of (symbol
224 gensym-prefix-string)."
225 `(let* ,(mapcar (lambda (symbol)
226 (destructuring-bind (symbol &optional prefix
)
231 `(,symbol
(ps-gensym ,prefix
))
232 `(,symbol
(ps-gensym ,(symbol-to-js-string symbol
))))))
236 (defmacro ps-once-only
((&rest vars
) &body body
)
237 (let ((gensyms (mapcar (lambda (x) (ps-gensym (string x
))) vars
)))
238 `(let ,(mapcar (lambda (g v
) `(,g
(ps-gensym ,(string v
)))) gensyms vars
)
239 `(let* (,,@(mapcar (lambda (g v
) ``(,,g
,,v
)) gensyms vars
))
240 ,(let ,(mapcar (lambda (g v
) `(,v
,g
)) gensyms vars
)