Commit | Line | Data |
---|---|---|
4a987e2b | 1 | (in-package :parenscript) |
5aa10005 RD |
2 | |
3 | ;;;; The macrology of the basic Javascript-in-SEXPs language. Special forms and macros. | |
4 | ||
5 | ;;; literals | |
4a987e2b VS |
6 | (defmacro defpsliteral (name string) |
7 | `(define-ps-special-form ,name (expecting) (list 'js-literal ,string))) | |
8 | ||
9 | (defpsliteral this "this") | |
10 | (defpsliteral t "true") | |
11 | (defpsliteral true "true") | |
12 | (defpsliteral false "false") | |
13 | (defpsliteral f "false") | |
14 | (defpsliteral nil "null") | |
15 | (defpsliteral undefined "undefined") | |
16 | ||
17 | (defmacro defpskeyword (name string) | |
18 | `(define-ps-special-form ,name (expecting) (list 'js-keyword ,string))) | |
19 | ||
20 | (defpskeyword break "break") | |
21 | (defpskeyword continue "continue") | |
22 | ||
23 | (define-ps-special-form array (expecting &rest values) | |
24 | (cons 'array-literal (mapcar (lambda (form) (compile-parenscript-form form :expecting :expression)) | |
25 | values))) | |
26 | ||
27 | (define-ps-special-form aref (expecting array &rest coords) | |
28 | (list 'js-aref (compile-parenscript-form array :expecting :expression) | |
29 | (mapcar (lambda (form) | |
30 | (compile-parenscript-form form :expecting :expression)) | |
31 | coords))) | |
32 | ||
33 | (define-ps-special-form {} (expecting &rest arrows) | |
34 | (cons 'object-literal (loop for (key value) on arrows by #'cddr | |
35 | collect (cons key (compile-parenscript-form value :expecting :expression))))) | |
5aa10005 RD |
36 | |
37 | ;;; operators | |
4a987e2b VS |
38 | (define-ps-special-form incf (expecting x &optional (delta 1)) |
39 | (if (equal delta 1) | |
40 | (list 'unary-operator "++" (compile-parenscript-form x :expecting :expression) :prefix t) | |
41 | (list 'operator '+= (list (compile-parenscript-form x :expecting :expression) | |
42 | (compile-parenscript-form delta :expecting :expression))))) | |
43 | ||
44 | (define-ps-special-form decf (expecting x &optional (delta 1)) | |
45 | (if (equal delta 1) | |
46 | (list 'unary-operator "--" (compile-parenscript-form x :expecting :expression) :prefix t) | |
47 | (list 'operator '-= (list (compile-parenscript-form x :expecting :expression) | |
48 | (compile-parenscript-form delta :expecting :expression))))) | |
49 | ||
50 | (define-ps-special-form - (expecting first &rest rest) | |
5aa10005 | 51 | (if (null rest) |
4a987e2b VS |
52 | (list 'unary-operator "-" (compile-parenscript-form first :expecting :expression) :prefix t) |
53 | (list 'operator '- (mapcar (lambda (val) (compile-parenscript-form val :expecting :expression)) | |
54 | (cons first rest))))) | |
55 | ||
56 | (define-ps-special-form not (expecting x) | |
57 | (let ((form (compile-parenscript-form x :expecting :expression)) | |
58 | (not-op nil)) | |
59 | (if (and (eql (first form) 'operator) | |
60 | (= (length (third form)) 2) | |
61 | (setf not-op (case (second form) | |
62 | (== '!=) | |
63 | (< '>=) | |
64 | (> '<=) | |
65 | (<= '>) | |
66 | (>= '<) | |
67 | (!= '==) | |
68 | (=== '!==) | |
69 | (!== '===) | |
70 | (t nil)))) | |
71 | (list 'operator not-op (third form)) | |
72 | (list 'unary-operator "!" form :prefix t)))) | |
73 | ||
74 | (define-ps-special-form ~ (expecting x) | |
75 | (list 'unary-operator "~" (compile-parenscript-form x :expecting :expressin) :prefix t)) | |
76 | ||
839600e9 VS |
77 | (defun flatten-blocks (body) |
78 | (when body | |
4a987e2b | 79 | (if (and (listp (car body)) |
839600e9 VS |
80 | (eql 'js-block (caar body))) |
81 | (append (third (car body)) (flatten-blocks (cdr body))) | |
82 | (cons (car body) (flatten-blocks (cdr body)))))) | |
4a987e2b VS |
83 | |
84 | (define-ps-special-form progn (expecting &rest body) | |
b1017218 VS |
85 | (if (and (eql expecting :expression) (= 1 (length body))) |
86 | (compile-parenscript-form (car body) :expecting :expression) | |
87 | (list 'js-block | |
88 | (if (eql expecting :statement) t nil) | |
13b8268e VS |
89 | (let* ((block (mapcar (lambda (form) |
90 | (compile-parenscript-form form :expecting :statement)) | |
91 | body)) | |
92 | (clean-block (remove nil block)) | |
93 | (flat-block (flatten-blocks clean-block)) | |
94 | (reachable-block (append (remove-if #'constant-literal-form-p (butlast flat-block)) | |
95 | (last flat-block)))) | |
96 | reachable-block)))) | |
5aa10005 RD |
97 | |
98 | ;;; function definition | |
4a987e2b VS |
99 | (define-ps-special-form %js-lambda (expecting args &rest body) |
100 | (list 'js-lambda (mapcar (lambda (arg) | |
101 | (compile-parenscript-form arg :expecting :symbol)) | |
102 | args) | |
103 | (compile-parenscript-form `(progn ,@body)))) | |
104 | ||
105 | (define-ps-special-form %js-defun (expecting name args &rest body) | |
f26808d8 | 106 | (list 'js-defun name |
4a987e2b VS |
107 | (mapcar (lambda (val) (compile-parenscript-form val :expecting :symbol)) args) |
108 | (compile-parenscript-form `(progn ,@body)))) | |
5aa10005 RD |
109 | |
110 | ;;; object creation | |
4a987e2b VS |
111 | (define-ps-special-form create (expecting &rest args) |
112 | (list 'js-object (loop for (name val) on args by #'cddr collecting | |
113 | (let ((name-expr (compile-parenscript-form name :expecting :expression))) | |
114 | (assert (or (stringp name-expr) | |
115 | (numberp name-expr) | |
116 | (and (listp name-expr) | |
117 | (or (eql 'js-variable (car name-expr)) | |
118 | (eql 'script-quote (car name-expr))))) | |
119 | () | |
120 | "Slot ~s is not one of js-variable, keyword, string or number." name-expr) | |
121 | (list name-expr (compile-parenscript-form val :expecting :expression)))))) | |
122 | ||
123 | (define-ps-special-form %js-slot-value (expecting obj slot) | |
124 | (if (ps::ps-macroexpand slot) | |
125 | (list 'js-slot-value (compile-parenscript-form obj :expecting :expression) (compile-parenscript-form slot)) | |
126 | (compile-parenscript-form obj :expecting :expression))) | |
127 | ||
128 | (define-ps-special-form cond (expecting &rest clauses) | |
0949f072 VS |
129 | (ecase expecting |
130 | (:statement (list 'js-cond-statement | |
131 | (mapcar (lambda (clause) | |
132 | (destructuring-bind (test &rest body) | |
133 | clause | |
134 | (list (compile-parenscript-form test :expecting :expression) | |
135 | (compile-parenscript-form `(progn ,@body))))) | |
136 | clauses))) | |
137 | (:expression (make-cond-clauses-into-nested-ifs clauses)))) | |
138 | ||
139 | (defun make-cond-clauses-into-nested-ifs (clauses) | |
140 | (if clauses | |
141 | (destructuring-bind (test &rest body) | |
142 | (car clauses) | |
143 | (if (eq t test) | |
144 | (compile-parenscript-form `(progn ,@body) :expecting :expression) | |
145 | (list 'js-expression-if (compile-parenscript-form test :expecting :expression) | |
146 | (compile-parenscript-form `(progn ,@body) :expecting :expression) | |
147 | (make-cond-clauses-into-nested-ifs (cdr clauses))))) | |
148 | (compile-parenscript-form nil :expecting :expression))) | |
4a987e2b VS |
149 | |
150 | (define-ps-special-form if (expecting test then &optional else) | |
151 | (ecase expecting | |
152 | (:statement (list 'js-statement-if (compile-parenscript-form test :expecting :expression) | |
153 | (compile-parenscript-form `(progn ,then)) | |
154 | (when else (compile-parenscript-form `(progn ,else))))) | |
155 | (:expression (list 'js-expression-if (compile-parenscript-form test :expecting :expression) | |
156 | (compile-parenscript-form then :expecting :expression) | |
5705b542 | 157 | (compile-parenscript-form else :expecting :expression))))) |
4a987e2b VS |
158 | |
159 | (define-ps-special-form switch (expecting test-expr &rest clauses) | |
160 | (let ((clauses (mapcar (lambda (clause) | |
161 | (let ((val (car clause)) | |
5aa10005 RD |
162 | (body (cdr clause))) |
163 | (list (if (eql val 'default) | |
164 | 'default | |
4a987e2b VS |
165 | (compile-parenscript-form val :expecting :expression)) |
166 | (compile-parenscript-form `(progn ,@body))))) | |
5aa10005 | 167 | clauses)) |
4a987e2b VS |
168 | (expr (compile-parenscript-form test-expr :expecting :expression))) |
169 | (list 'js-switch expr clauses))) | |
5aa10005 RD |
170 | |
171 | ;;; assignment | |
172 | (defun assignment-op (op) | |
173 | (case op | |
174 | (+ '+=) | |
175 | (~ '~=) | |
176 | (\& '\&=) | |
177 | (\| '\|=) | |
178 | (- '-=) | |
179 | (* '*=) | |
180 | (% '%=) | |
181 | (>> '>>=) | |
182 | (^ '^=) | |
183 | (<< '<<=) | |
184 | (>>> '>>>=) | |
185 | (/ '/=) | |
186 | (t nil))) | |
187 | ||
4a987e2b VS |
188 | (defun smart-setf (lhs rhs) |
189 | (if (and (listp rhs) | |
190 | (eql 'operator (car rhs)) | |
191 | (member lhs (third rhs) :test #'equalp)) | |
192 | (let ((args-without (remove lhs (third rhs) :count 1 :test #'equalp)) | |
193 | (args-without-first (remove lhs (third rhs) :count 1 :end 1 :test #'equalp))) | |
194 | (cond ((and (equal (car args-without) 1) (eql (second rhs) '+)) | |
195 | (list 'unary-operator "++" lhs :prefix nil)) | |
196 | ((and (equal (second args-without-first) 1) (eql (second rhs) '-)) | |
197 | (list 'unary-operator "--" lhs :prefix nil)) | |
198 | ((and (assignment-op (second rhs)) | |
199 | (member (second rhs) '(+ *)) | |
200 | (equalp lhs (first (third rhs)))) | |
201 | (list 'operator (assignment-op (second rhs)) | |
202 | (list lhs (list 'operator (second rhs) args-without-first)))) | |
203 | ((and (assignment-op (second rhs)) (equalp (first (third rhs)) lhs)) | |
204 | (list 'operator (assignment-op (second rhs)) | |
205 | (list lhs (list 'operator (second rhs) (cdr (third rhs)))))) | |
206 | (t (list 'js-assign lhs rhs)))) | |
207 | (list 'js-assign lhs rhs))) | |
208 | ||
209 | (define-ps-special-form setf1% (expecting lhs rhs) | |
210 | (smart-setf (compile-parenscript-form lhs :expecting :expression) (compile-parenscript-form rhs :expecting :expression))) | |
211 | ||
212 | (define-ps-special-form defvar (expecting name &rest value) | |
f26808d8 | 213 | (append (list 'js-defvar name) |
4a987e2b VS |
214 | (when value |
215 | (assert (= (length value) 1) () "Wrong number of arguments to defvar: ~s" `(defvar ,name ,@value)) | |
216 | (list (compile-parenscript-form (car value) :expecting :expression))))) | |
5aa10005 RD |
217 | |
218 | ;;; iteration | |
219 | (defun make-for-vars (decls) | |
220 | (loop for decl in decls | |
221 | for var = (if (atom decl) decl (first decl)) | |
4a987e2b VS |
222 | for init-value = (if (atom decl) nil (second decl)) |
223 | collect (cons (compile-parenscript-form var :expecting :symbol) (compile-parenscript-form init-value)))) | |
5aa10005 RD |
224 | |
225 | (defun make-for-steps (decls) | |
226 | (loop for decl in decls | |
227 | when (= (length decl) 3) | |
4a987e2b | 228 | collect (compile-parenscript-form (third decl) :expecting :expression))) |
5aa10005 | 229 | |
4a987e2b | 230 | (define-ps-special-form do (expecting decls termination-test &rest body) |
5aa10005 RD |
231 | (let ((vars (make-for-vars decls)) |
232 | (steps (make-for-steps decls)) | |
4a987e2b VS |
233 | (test (compile-parenscript-form `(not ,(first termination-test)) :expecting :expression)) |
234 | (body (compile-parenscript-form `(progn ,@body)))) | |
235 | (list 'js-for vars steps test body))) | |
236 | ||
237 | (define-ps-special-form doeach (expecting decl &rest body) | |
238 | (list 'js-for-each | |
f26808d8 | 239 | (first decl) |
4a987e2b VS |
240 | (compile-parenscript-form (second decl) :expecting :expression) |
241 | (compile-parenscript-form `(progn ,@body)))) | |
242 | ||
243 | (define-ps-special-form while (expecting test &rest body) | |
244 | (list 'js-while (compile-parenscript-form test :expecting :expression) | |
245 | (compile-parenscript-form `(progn ,@body)))) | |
246 | ||
247 | (define-ps-special-form with (expecting expression &rest body) | |
248 | (list 'js-with (compile-parenscript-form expression :expecting :expression) | |
249 | (compile-parenscript-form `(progn ,@body)))) | |
250 | ||
251 | (define-ps-special-form try (expecting form &rest clauses) | |
252 | (let ((catch (cdr (assoc :catch clauses))) | |
253 | (finally (cdr (assoc :finally clauses)))) | |
5aa10005 | 254 | (assert (not (cdar catch)) nil "Sorry, currently only simple catch forms are supported.") |
839600e9 VS |
255 | (assert (or catch finally) () |
256 | "Try form should have either a catch or a finally clause or both.") | |
4a987e2b VS |
257 | (list 'js-try (compile-parenscript-form `(progn ,form)) |
258 | :catch (when catch (list (compile-parenscript-form (caar catch) :expecting :symbol) | |
259 | (compile-parenscript-form `(progn ,@(cdr catch))))) | |
260 | :finally (when finally (compile-parenscript-form `(progn ,@finally)))))) | |
261 | ||
262 | (define-ps-special-form regex (expecting regex) | |
263 | (list 'js-regex (string regex))) | |
5aa10005 RD |
264 | |
265 | ;;; TODO instanceof | |
4a987e2b VS |
266 | (define-ps-special-form instanceof (expecting value type) |
267 | (list 'js-instanceof (compile-parenscript-form value :expecting :expression) | |
268 | (compile-parenscript-form type :expecting :expression))) | |
5aa10005 RD |
269 | |
270 | ;;; single operations | |
4a987e2b VS |
271 | (mapcar (lambda (op) (eval `(define-ps-special-form ,op (expecting value) |
272 | (list 'js-named-operator ',op (compile-parenscript-form value))))) | |
273 | '(throw delete void typeof new)) | |
5aa10005 | 274 | |
4a987e2b VS |
275 | (define-ps-special-form return (expecting &optional value) |
276 | (list 'js-return (compile-parenscript-form value :expecting :expression))) | |
a2434734 | 277 | |
5aa10005 | 278 | ;;; conditional compilation |
4a987e2b VS |
279 | (define-ps-special-form cc-if (expecting test &rest body) |
280 | (list 'cc-if test (mapcar #'compile-parenscript-form body))) | |
5aa10005 RD |
281 | |
282 | ;;; standard macros | |
4a987e2b | 283 | (defpsmacro when (test &rest body) |
5aa10005 RD |
284 | `(if ,test (progn ,@body))) |
285 | ||
4a987e2b | 286 | (defpsmacro unless (test &rest body) |
5aa10005 RD |
287 | `(if (not ,test) (progn ,@body))) |
288 | ||
4a987e2b | 289 | (defpsmacro 1- (form) |
5aa10005 RD |
290 | `(- ,form 1)) |
291 | ||
4a987e2b | 292 | (defpsmacro 1+ (form) |
5aa10005 | 293 | `(+ ,form 1)) |