Commit | Line | Data |
---|---|---|
f037cd4c | 1 | ;;;# Parenscript Language Reference |
8e198a08 | 2 | |
d777a405 TC |
3 | ;;; Create a useful package for the code here... |
4 | (in-package #:cl-user) | |
5 | (defpackage #:ps-ref (:use #:ps)) | |
6 | (in-package #:ps-ref) | |
7 | ||
f037cd4c | 8 | ;;; This chapters describes the core constructs of Parenscript, as |
8e198a08 | 9 | ;;; well as its compilation model. This chapter is aimed to be a |
f037cd4c VS |
10 | ;;; comprehensive reference for Parenscript developers. Programmers |
11 | ;;; looking for how to tweak the Parenscript compiler itself should | |
12 | ;;; turn to the Parenscript Internals chapter. | |
8e198a08 MB |
13 | |
14 | ;;;# Statements and Expressions | |
15 | ;;;t \index{statement} | |
16 | ;;;t \index{expression} | |
17 | ||
18 | ;;; In contrast to Lisp, where everything is an expression, JavaScript | |
19 | ;;; makes the difference between an expression, which evaluates to a | |
20 | ;;; value, and a statement, which has no value. Examples for | |
21 | ;;; JavaScript statements are `for', `with' and `while'. Most | |
f037cd4c | 22 | ;;; Parenscript forms are expression, but certain special forms are |
8e198a08 | 23 | ;;; not (the forms which are transformed to a JavaScript |
f037cd4c | 24 | ;;; statement). All Parenscript expressions are statements |
8e198a08 MB |
25 | ;;; though. Certain forms, like `IF' and `PROGN', generate different |
26 | ;;; JavaScript constructs whether they are used in an expression | |
27 | ;;; context or a statement context. For example: | |
28 | ||
5a69278c | 29 | (+ i (if 1 2 3)) => i + (1 ? 2 : 3); |
8e198a08 MB |
30 | |
31 | (if 1 2 3) | |
31c5dbde TC |
32 | => if (1) { |
33 | 2; | |
34 | } else { | |
35 | 3; | |
5a69278c | 36 | }; |
8e198a08 MB |
37 | |
38 | ;;;# Symbol conversion | |
39 | ;;;t \index{symbol} | |
40 | ;;;t \index{symbol conversion} | |
41 | ||
42 | ;;; Lisp symbols are converted to JavaScript symbols by following a | |
3b238048 | 43 | ;;; few simple rules. Special characters `!', `?', `#', `@', `%', |
8e198a08 | 44 | ;;; '/', `*' and `+' get replaced by their written-out equivalents |
3b238048 HH |
45 | ;;; "bang", "what", "hash", "at", "percent", "slash", |
46 | ;;; "start" and "plus" respectively. The `$' character is untouched. | |
8e198a08 | 47 | |
5a69278c | 48 | !?#@% => bangwhathashatpercent; |
8e198a08 MB |
49 | |
50 | ;;; The `-' is an indication that the following character should be | |
51 | ;;; converted to uppercase. Thus, `-' separated symbols are converted | |
52 | ;;; to camelcase. The `_' character however is left untouched. | |
53 | ||
5a69278c | 54 | bla-foo-bar => blaFooBar; |
8e198a08 MB |
55 | |
56 | ;;; If you want a JavaScript symbol beginning with an uppercase, you | |
57 | ;;; can either use a leading `-', which can be misleading in a | |
58 | ;;; mathematical context, or a leading `*'. | |
59 | ||
5a69278c | 60 | *array => Array; |
8e198a08 | 61 | |
8e198a08 MB |
62 | ;;; A symbol beggining and ending with `+' or `*' is converted to all |
63 | ;;; uppercase, to signify that this is a constant or a global | |
64 | ;;; variable. | |
65 | ||
5a69278c | 66 | *global-array* => GLOBALARRAY; |
8e198a08 | 67 | |
8e198a08 MB |
68 | ;;;## Reserved Keywords |
69 | ;;;t \index{keyword} | |
70 | ;;;t \index{reserved keywords} | |
71 | ||
f037cd4c | 72 | ;;; The following keywords and symbols are reserved in Parenscript, |
8e198a08 MB |
73 | ;;; and should not be used as variable names. |
74 | ||
58c4ef4f | 75 | ! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || *= |
5ffb1eba | 76 | /= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ @ ABSTRACT AND AREF ARRAY |
58c4ef4f | 77 | BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS COMMA CONST CONTINUE |
d777a405 TC |
78 | CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO* DOEACH DOLIST |
79 | DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL FINALLY | |
80 | FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF | |
5ffb1eba VS |
81 | INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA LET LET* LISP LIST LONG |
82 | MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED | |
83 | PUBLIC RANDOM REGEX RETURN SETF SHORT SLOT-VALUE STATIC SUPER SWITCH | |
84 | SYMBOL-MACROLET SYNCHRONIZED T THIS THROW THROWS TRANSIENT TRY TYPEOF | |
85 | UNDEFINED UNLESS VAR VOID VOLATILE WHEN WHILE WITH WITH-SLOTS | |
8e198a08 MB |
86 | |
87 | ;;;# Literal values | |
88 | ;;;t \index{literal value} | |
89 | ||
90 | ;;;## Number literals | |
91 | ;;;t \index{number} | |
92 | ;;;t \index{number literal} | |
93 | ||
94 | ; number ::= a Lisp number | |
95 | ||
96 | ;;; | |
f037cd4c | 97 | ;;; Parenscript supports the standard JavaScript literal |
94a05cdf | 98 | ;;; values. Numbers are compiled into JavaScript numbers. |
8e198a08 | 99 | |
5a69278c | 100 | 1 => 1; |
8e198a08 | 101 | |
5a69278c | 102 | 123.123 => 123.123; |
8e198a08 MB |
103 | |
104 | ;;; Note that the base is not conserved between Lisp and JavaScript. | |
105 | ||
5a69278c | 106 | #x10 => 16; |
8e198a08 MB |
107 | |
108 | ;;;## String literals | |
109 | ;;;t \index{string} | |
110 | ;;;t \index{string literal} | |
111 | ||
112 | ; string ::= a Lisp string | |
113 | ||
114 | ;;; Lisp strings are converted into JavaScript literals. | |
115 | ||
5a69278c | 116 | "foobar" => 'foobar'; |
7a7d6c73 | 117 | |
5a69278c | 118 | "bratzel bub" => 'bratzel bub'; |
8e198a08 | 119 | |
ed954200 VS |
120 | ;;; Special characters such as newline and backspace are converted |
121 | ;;; into their corresponding JavaScript escape sequences. | |
122 | ||
5a69278c | 123 | " " => '\\t'; |
8e198a08 MB |
124 | |
125 | ;;;## Array literals | |
126 | ;;;t \index{array} | |
127 | ;;;t \index{ARRAY} | |
128 | ;;;t \index{MAKE-ARRAY} | |
129 | ;;;t \index{AREF} | |
130 | ;;;t \index{array literal} | |
131 | ||
132 | ; (ARRAY {values}*) | |
133 | ; (MAKE-ARRAY {values}*) | |
134 | ; (AREF array index) | |
135 | ; | |
f037cd4c VS |
136 | ; values ::= a Parenscript expression |
137 | ; array ::= a Parenscript expression | |
138 | ; index ::= a Parenscript expression | |
8e198a08 MB |
139 | |
140 | ;;; Array literals can be created using the `ARRAY' form. | |
141 | ||
5a69278c | 142 | (array) => [ ]; |
8e198a08 | 143 | |
5a69278c | 144 | (array 1 2 3) => [ 1, 2, 3 ]; |
8e198a08 MB |
145 | |
146 | (array (array 2 3) | |
147 | (array "foobar" "bratzel bub")) | |
5a69278c | 148 | => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ]; |
8e198a08 MB |
149 | |
150 | ;;; Arrays can also be created with a call to the `Array' function | |
151 | ;;; using the `MAKE-ARRAY'. The two forms have the exact same semantic | |
152 | ;;; on the JavaScript side. | |
153 | ||
5a69278c | 154 | (make-array) => new Array(); |
8e198a08 | 155 | |
5a69278c | 156 | (make-array 1 2 3) => new Array(1, 2, 3); |
8e198a08 MB |
157 | |
158 | (make-array | |
159 | (make-array 2 3) | |
160 | (make-array "foobar" "bratzel bub")) | |
5a69278c | 161 | => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub')); |
8e198a08 | 162 | |
f037cd4c | 163 | ;;; Indexing arrays in Parenscript is done using the form `AREF'. Note |
8e198a08 MB |
164 | ;;; that JavaScript knows of no such thing as an array. Subscripting |
165 | ;;; an array is in fact reading a property from an object. So in a | |
166 | ;;; semantic sense, there is no real difference between `AREF' and | |
167 | ;;; `SLOT-VALUE'. | |
168 | ||
169 | ;;;## Object literals | |
170 | ;;;t \index{CREATE} | |
171 | ;;;t \index{SLOT-VALUE} | |
5ffb1eba | 172 | ;;;t \index{@} |
8e198a08 MB |
173 | ;;;t \index{WITH-SLOTS} |
174 | ;;;t \index{object literal} | |
175 | ;;;t \index{object} | |
176 | ;;;t \index{object property} | |
177 | ;;;t \index{property} | |
178 | ||
179 | ; (CREATE {name value}*) | |
180 | ; (SLOT-VALUE object slot-name) | |
181 | ; (WITH-SLOTS ({slot-name}*) object body) | |
182 | ; | |
f037cd4c VS |
183 | ; name ::= a Parenscript symbol or a Lisp keyword |
184 | ; value ::= a Parenscript expression | |
185 | ; object ::= a Parenscript object expression | |
8e198a08 | 186 | ; slot-name ::= a quoted Lisp symbol |
f037cd4c | 187 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
188 | |
189 | ;;; | |
190 | ;;; Object literals can be create using the `CREATE' form. Arguments | |
191 | ;;; to the `CREATE' form is a list of property names and values. To be | |
192 | ;;; more "lispy", the property names can be keywords. | |
193 | ||
194 | (create :foo "bar" :blorg 1) | |
5a69278c | 195 | => { foo : 'bar', blorg : 1 }; |
8e198a08 MB |
196 | |
197 | (create :foo "hihi" | |
198 | :blorg (array 1 2 3) | |
199 | :another-object (create :schtrunz 1)) | |
31c5dbde TC |
200 | => { foo : 'hihi', |
201 | blorg : [ 1, 2, 3 ], | |
5a69278c | 202 | anotherObject : { schtrunz : 1 } }; |
8e198a08 MB |
203 | |
204 | ;;; Object properties can be accessed using the `SLOT-VALUE' form, | |
205 | ;;; which takes an object and a slot-name. | |
206 | ||
5a69278c | 207 | (slot-value an-object 'foo) => anObject.foo; |
8e198a08 | 208 | |
5ffb1eba VS |
209 | ;;; The convenience macro `@' is provided to make multiple levels of |
210 | ;;; indirection easy to express | |
8e198a08 | 211 | |
5a69278c | 212 | (@ an-object foo bar) => anObject.foo.bar; |
8e198a08 MB |
213 | |
214 | ;;; The form `WITH-SLOTS' can be used to bind the given slot-name | |
215 | ;;; symbols to a macro that will expand into a `SLOT-VALUE' form at | |
216 | ;;; expansion time. | |
217 | ||
218 | (with-slots (a b c) this | |
219 | (+ a b c)) | |
31c5dbde | 220 | => this.a + this.b + this.c; |
8e198a08 MB |
221 | |
222 | ;;;## Regular Expression literals | |
223 | ;;;t \index{REGEX} | |
224 | ;;;t \index{regular expression} | |
225 | ;;;t \index{CL-INTERPOL} | |
226 | ||
227 | ; (REGEX regex) | |
228 | ; | |
229 | ; regex ::= a Lisp string | |
230 | ||
ca4fb762 HH |
231 | ;;; Regular expressions can be created by using the `REGEX' form. If |
232 | ;;; the argument does not start with a slash, it is surrounded by | |
233 | ;;; slashes to make it a proper JavaScript regex. If the argument | |
234 | ;;; starts with a slash it is left as it is. This makes it possible | |
235 | ;;; to use modifiers such as slash-i (case-insensitive) or | |
236 | ;;; slash-g (match-globally (all)). | |
237 | ||
5a69278c | 238 | (regex "foobar") => /foobar/; |
8e198a08 | 239 | |
5a69278c | 240 | (regex "/foobar/i") => /foobar/i; |
8e198a08 MB |
241 | |
242 | ;;; Here CL-INTERPOL proves really useful. | |
243 | ||
5a69278c | 244 | (regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i; |
8e198a08 MB |
245 | |
246 | ;;;## Literal symbols | |
247 | ;;;t \index{T} | |
d777a405 | 248 | ;;;t \index{F} |
8e198a08 MB |
249 | ;;;t \index{FALSE} |
250 | ;;;t \index{NIL} | |
251 | ;;;t \index{UNDEFINED} | |
252 | ;;;t \index{THIS} | |
253 | ;;;t \index{literal symbols} | |
254 | ;;;t \index{null} | |
255 | ;;;t \index{true} | |
256 | ||
d777a405 | 257 | ; T, F, FALSE, NIL, UNDEFINED, THIS |
8e198a08 | 258 | |
d777a405 TC |
259 | ;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their |
260 | ;;; JavaScript boolean equivalents `true' and `false'. | |
8e198a08 | 261 | |
5a69278c | 262 | T => true; |
7a7d6c73 | 263 | |
5a69278c | 264 | FALSE => false; |
8e198a08 | 265 | |
5a69278c | 266 | F => false; |
d777a405 | 267 | |
8e198a08 MB |
268 | ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword |
269 | ;;; `null'. | |
270 | ||
5a69278c | 271 | NIL => null; |
8e198a08 MB |
272 | |
273 | ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword | |
274 | ;;; `undefined'. | |
275 | ||
5a69278c | 276 | UNDEFINED => undefined; |
8e198a08 MB |
277 | |
278 | ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword | |
279 | ;;; `this'. | |
280 | ||
5a69278c | 281 | THIS => this; |
8e198a08 MB |
282 | |
283 | ;;;# Variables | |
284 | ;;;t \index{variable} | |
285 | ;;;t \index{symbol} | |
286 | ||
287 | ; variable ::= a Lisp symbol | |
288 | ||
289 | ;;; All the other literal Lisp values that are not recognized as | |
290 | ;;; special forms or symbol macros are converted to JavaScript | |
291 | ;;; variables. This extreme freedom is actually quite useful, as it | |
f037cd4c | 292 | ;;; allows the Parenscript programmer to be flexible, as flexible as |
8e198a08 MB |
293 | ;;; JavaScript itself. |
294 | ||
5a69278c | 295 | variable => variable; |
8e198a08 | 296 | |
5a69278c | 297 | a-variable => aVariable; |
8e198a08 | 298 | |
5a69278c | 299 | *math => Math; |
8e198a08 | 300 | |
8e198a08 MB |
301 | ;;;# Function calls and method calls |
302 | ;;;t \index{function} | |
303 | ;;;t \index{function call} | |
304 | ;;;t \index{method} | |
305 | ;;;t \index{method call} | |
306 | ||
307 | ; (function {argument}*) | |
efe8a33f | 308 | |
8e198a08 | 309 | ; |
f037cd4c | 310 | ; function ::= a Parenscript expression or a Lisp symbol |
f037cd4c VS |
311 | ; object ::= a Parenscript expression |
312 | ; argument ::= a Parenscript expression | |
8e198a08 MB |
313 | |
314 | ;;; Any list passed to the JavaScript that is not recognized as a | |
315 | ;;; macro or a special form (see "Macro Expansion" below) is | |
316 | ;;; interpreted as a function call. The function call is converted to | |
317 | ;;; the normal JavaScript function call representation, with the | |
318 | ;;; arguments given in paren after the function name. | |
319 | ||
5a69278c | 320 | (blorg 1 2) => blorg(1, 2); |
8e198a08 MB |
321 | |
322 | (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4)) | |
5a69278c | 323 | => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]); |
8e198a08 | 324 | |
5a69278c | 325 | ((slot-value this 'blorg) 1 2) => this.blorg(1, 2); |
79630c82 | 326 | |
5a69278c | 327 | ((aref foo i) 1 2) => foo[i](1, 2); |
8e198a08 | 328 | |
5a69278c | 329 | ((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true); |
8e198a08 | 330 | |
8e198a08 MB |
331 | ;;;# Operator Expressions |
332 | ;;;t \index{operator} | |
333 | ;;;t \index{operator expression} | |
334 | ;;;t \index{assignment operator} | |
335 | ;;;t \index{EQL} | |
336 | ;;;t \index{NOT} | |
337 | ;;;t \index{AND} | |
338 | ;;;t \index{OR} | |
339 | ||
340 | ; (operator {argument}*) | |
341 | ; (single-operator argument) | |
342 | ; | |
343 | ; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL, | |
344 | ; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR. | |
345 | ; single-operator ::= one of INCF, DECF, ++, --, NOT, ! | |
f037cd4c | 346 | ; argument ::= a Parenscript expression |
8e198a08 MB |
347 | |
348 | ;;; Operator forms are similar to function call forms, but have an | |
94a05cdf | 349 | ;;; operator as function name. |
8e198a08 MB |
350 | ;;; |
351 | ;;; Please note that `=' is converted to `==' in JavaScript. The `=' | |
a14fb2cb | 352 | ;;; Parenscript operator is not the assignment operator. |
8e198a08 | 353 | |
5a69278c | 354 | (* 1 2) => 1 * 2; |
8e198a08 | 355 | |
5a69278c | 356 | (= 1 2) => 1 == 2; |
8e198a08 | 357 | |
ecc3218c | 358 | ;;; Note that the resulting expression is correctly parenthesized, |
8e198a08 MB |
359 | ;;; according to the JavaScript operator precedence that can be found |
360 | ;;; in table form at: | |
361 | ||
31c5dbde | 362 | ;;; http://www.codehouse.com/javascript/precedence/ |
8e198a08 MB |
363 | |
364 | (* 1 (+ 2 3 4) 4 (/ 6 7)) | |
5a69278c | 365 | => 1 * (2 + 3 + 4) * 4 * (6 / 7); |
8e198a08 | 366 | |
f7c2734a | 367 | ;;; The pre increment and decrement operators are also |
8e198a08 | 368 | ;;; available. `INCF' and `DECF' are the pre-incrementing and |
f7c2734a | 369 | ;;; pre-decrementing operators. These operators can |
8e198a08 MB |
370 | ;;; take only one argument. |
371 | ||
5a69278c | 372 | (incf i) => ++i; |
8e198a08 | 373 | |
5a69278c | 374 | (decf i) => --i; |
8e198a08 MB |
375 | |
376 | ;;; The `1+' and `1-' operators are shortforms for adding and | |
377 | ;;; substracting 1. | |
378 | ||
5a69278c | 379 | (1- i) => i - 1; |
8e198a08 | 380 | |
5a69278c | 381 | (1+ i) => i + 1; |
8e198a08 | 382 | |
a14fb2cb VS |
383 | ;;; If `not' is used on another boolean-returning operator, the |
384 | ;;; operator is reversed. | |
8e198a08 | 385 | |
5a69278c | 386 | (not (< i 2)) => i >= 2; |
8e198a08 | 387 | |
8e198a08 MB |
388 | ;;;# Body forms |
389 | ;;;t \index{body form} | |
390 | ;;;t \index{PROGN} | |
391 | ;;;t \index{body statement} | |
392 | ||
393 | ; (PROGN {statement}*) in statement context | |
394 | ; (PROGN {expression}*) in expression context | |
395 | ; | |
f037cd4c VS |
396 | ; statement ::= a Parenscript statement |
397 | ; expression ::= a Parenscript expression | |
8e198a08 MB |
398 | |
399 | ;;; The `PROGN' special form defines a sequence of statements when | |
400 | ;;; used in a statement context, or sequence of expression when used | |
401 | ;;; in an expression context. The `PROGN' special form is added | |
402 | ;;; implicitly around the branches of conditional executions forms, | |
403 | ;;; function declarations and iteration constructs. | |
404 | ||
405 | ;;; For example, in a statement context: | |
406 | ||
407 | (progn (blorg i) (blafoo i)) | |
31c5dbde TC |
408 | => blorg(i); |
409 | blafoo(i); | |
8e198a08 MB |
410 | |
411 | ;;; In an expression context: | |
412 | ||
413 | (+ i (progn (blorg i) (blafoo i))) | |
5a69278c | 414 | => i + (blorg(i), blafoo(i)); |
8e198a08 MB |
415 | |
416 | ;;; A `PROGN' form doesn't lead to additional indentation or | |
417 | ;;; additional braces around it's body. | |
418 | ||
419 | ;;;# Function Definition | |
420 | ;;;t \index{function} | |
421 | ;;;t \index{method} | |
422 | ;;;t \index{function definition} | |
423 | ;;;t \index{DEFUN} | |
424 | ;;;t \index{LAMBDA} | |
425 | ;;;t \index{closure} | |
426 | ;;;t \index{anonymous function} | |
427 | ||
428 | ; (DEFUN name ({argument}*) body) | |
429 | ; (LAMBDA ({argument}*) body) | |
430 | ; | |
431 | ; name ::= a Lisp Symbol | |
432 | ; argument ::= a Lisp symbol | |
f037cd4c | 433 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
434 | |
435 | ;;; As in Lisp, functions are defined using the `DEFUN' form, which | |
436 | ;;; takes a name, a list of arguments, and a function body. An | |
437 | ;;; implicit `PROGN' is added around the body statements. | |
438 | ||
439 | (defun a-function (a b) | |
440 | (return (+ a b))) | |
31c5dbde | 441 | => function aFunction(a, b) { |
8e198a08 | 442 | return a + b; |
5a69278c | 443 | }; |
8e198a08 MB |
444 | |
445 | ;;; Anonymous functions can be created using the `LAMBDA' form, which | |
446 | ;;; is the same as `DEFUN', but without function name. In fact, | |
447 | ;;; `LAMBDA' creates a `DEFUN' with an empty function name. | |
448 | ||
449 | (lambda (a b) (return (+ a b))) | |
31c5dbde | 450 | => function (a, b) { |
8e198a08 | 451 | return a + b; |
5a69278c | 452 | }; |
8e198a08 MB |
453 | |
454 | ;;;# Assignment | |
455 | ;;;t \index{assignment} | |
456 | ;;;t \index{SETF} | |
d777a405 TC |
457 | ;;;t \index{PSETF} |
458 | ;;;t \index{SETQ} | |
459 | ;;;t \index{PSETQ} | |
a2a9eab0 | 460 | ;;;t \index{DEFSETF} |
8e198a08 MB |
461 | ;;;t \index{assignment operator} |
462 | ||
463 | ; (SETF {lhs rhs}*) | |
d777a405 | 464 | ; (PSETF {lhs rhs}*) |
8e198a08 | 465 | ; |
f037cd4c VS |
466 | ; lhs ::= a Parenscript left hand side expression |
467 | ; rhs ::= a Parenscript expression | |
8e198a08 | 468 | |
d777a405 TC |
469 | ; (SETQ {lhs rhs}*) |
470 | ; (PSETQ {lhs rhs}*) | |
471 | ; | |
f037cd4c VS |
472 | ; lhs ::= a Parenscript symbol |
473 | ; rhs ::= a Parenscript expression | |
d777a405 TC |
474 | |
475 | ;;; Assignment is done using the `SETF', `PSETF', `SETQ', and `PSETQ' | |
476 | ;;; forms, which are transformed into a series of assignments using | |
477 | ;;; the JavaScript `=' operator. | |
8e198a08 | 478 | |
72332f2a | 479 | (setf a 1) => a = 1; |
8e198a08 MB |
480 | |
481 | (setf a 2 b 3 c 4 x (+ a b c)) | |
31c5dbde TC |
482 | => a = 2; |
483 | b = 3; | |
484 | c = 4; | |
485 | x = a + b + c; | |
8e198a08 MB |
486 | |
487 | ;;; The `SETF' form can transform assignments of a variable with an | |
488 | ;;; operator expression using this variable into a more "efficient" | |
489 | ;;; assignment operator form. For example: | |
490 | ||
72332f2a | 491 | (setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a; |
8e198a08 | 492 | |
72332f2a | 493 | (setf a (- 1 a)) => a = 1 - a; |
8e198a08 | 494 | |
d777a405 TC |
495 | ;;; The `PSETF' and `PSETQ' forms perform parallel assignment of |
496 | ;;; places or variables using a number of temporary variables created | |
497 | ;;; by `PS-GENSYM'. For example: | |
498 | ||
5ffb1eba | 499 | (let ((a 1) (b 2)) |
d777a405 | 500 | (psetf a b b a)) |
16151f19 VS |
501 | => var a = 1; |
502 | var b = 2; | |
503 | var _js1 = b; | |
504 | var _js2 = a; | |
505 | a = _js1; | |
506 | b = _js2; | |
d777a405 TC |
507 | |
508 | ;;; The `SETQ' and `PSETQ' forms operate identically to `SETF' and | |
509 | ;;; `PSETF', but throw a compile-time error if the left-hand side form | |
510 | ;;; is not a symbol. For example: | |
511 | ||
512 | (setq a 1) => a = 1; | |
513 | ||
514 | ;; but... | |
515 | ||
516 | (setq (aref a 0) 1) | |
31c5dbde | 517 | ;; => ERROR: The value (AREF A 0) is not of type SYMBOL. |
d777a405 | 518 | |
a2a9eab0 VS |
519 | ;;; New types of setf places can be defined in one of two ways: using |
520 | ;;; `DEFSETF' or using `DEFUN' with a setf function name; both are | |
521 | ;;; analogous to their Common Lisp counterparts. | |
522 | ||
523 | ;;; `DEFSETF' supports both long and short forms, while `DEFUN' of a | |
524 | ;;; setf place generates a JavaScript function name with the __setf_ | |
525 | ;;; prefix: | |
526 | ||
527 | (defun (setf color) (new-color el) | |
528 | (setf (slot-value (slot-value el 'style) 'color) new-color)) | |
31c5dbde | 529 | => function __setf_color(newColor, el) { |
a2a9eab0 | 530 | el.style.color = newColor; |
31c5dbde | 531 | }; |
a2a9eab0 VS |
532 | |
533 | (setf (color some-div) (+ 23 "em")) | |
16151f19 VS |
534 | => var _js2 = someDiv; |
535 | var _js1 = 23 + 'em'; | |
536 | __setf_color(_js1, _js2); | |
a2a9eab0 | 537 | |
a2a9eab0 VS |
538 | ;;; Note that temporary variables are generated to preserve evaluation |
539 | ;;; order of the arguments as they would be in Lisp. | |
540 | ||
541 | ;;; The following example illustrates how setf places can be used to | |
542 | ;;; provide a uniform protocol for positioning elements in HTML pages: | |
543 | ||
544 | (defsetf left (el) (offset) | |
31c5dbde | 545 | `(setf (slot-value (slot-value ,el 'style) 'left) ,offset)) |
5a69278c | 546 | => null; |
a2a9eab0 VS |
547 | |
548 | (setf (left some-div) (+ 123 "px")) | |
16151f19 VS |
549 | => var _js2 = someDiv; |
550 | var _js1 = 123 + 'px'; | |
551 | _js2.style.left = _js1; | |
a2a9eab0 | 552 | |
5a69278c VS |
553 | (macrolet ((left (el) |
554 | `(slot-value ,el 'offset-left))) | |
555 | (left some-div)) | |
31c5dbde | 556 | => someDiv.offsetLeft; |
a2a9eab0 | 557 | |
8e198a08 MB |
558 | ;;;# Single argument statements |
559 | ;;;t \index{single-argument statement} | |
560 | ;;;t \index{RETURN} | |
561 | ;;;t \index{THROW} | |
562 | ;;;t \index{THROW} | |
563 | ;;;t \index{function} | |
564 | ||
565 | ; (RETURN {value}?) | |
566 | ; (THROW {value}?) | |
567 | ; | |
f037cd4c | 568 | ; value ::= a Parenscript expression |
8e198a08 MB |
569 | |
570 | ;;; The single argument statements `return' and `throw' are generated | |
571 | ;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a | |
572 | ;;; `TRY' form. `RETURN' is used to return a value from a function | |
573 | ;;; call. | |
574 | ||
5a69278c | 575 | (return 1) => return 1; |
8e198a08 | 576 | |
5a69278c | 577 | (throw "foobar") => throw 'foobar'; |
8e198a08 MB |
578 | |
579 | ;;;# Single argument expression | |
580 | ;;;t \index{single-argument expression} | |
581 | ;;;t \index{object creation} | |
582 | ;;;t \index{object deletion} | |
583 | ;;;t \index{DELETE} | |
584 | ;;;t \index{VOID} | |
585 | ;;;t \index{TYPEOF} | |
586 | ;;;t \index{INSTANCEOF} | |
587 | ;;;t \index{NEW} | |
588 | ;;;t \index{new} | |
589 | ||
590 | ; (DELETE {value}) | |
591 | ; (VOID {value}) | |
592 | ; (TYPEOF {value}) | |
593 | ; (INSTANCEOF {value}) | |
594 | ; (NEW {value}) | |
595 | ; | |
f037cd4c | 596 | ; value ::= a Parenscript expression |
8e198a08 MB |
597 | |
598 | ;;; The single argument expressions `delete', `void', `typeof', | |
599 | ;;; `instanceof' and `new' are generated by the forms `DELETE', | |
600 | ;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a | |
f037cd4c | 601 | ;;; Parenscript expression. |
8e198a08 | 602 | |
5a69278c | 603 | (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4); |
8e198a08 MB |
604 | |
605 | (if (= (typeof blorg) *string) | |
606 | (alert (+ "blorg is a string: " blorg)) | |
607 | (alert "blorg is not a string")) | |
31c5dbde | 608 | => if (typeof blorg == String) { |
7a7d6c73 | 609 | alert('blorg is a string: ' + blorg); |
31c5dbde | 610 | } else { |
7a7d6c73 | 611 | alert('blorg is not a string'); |
5a69278c | 612 | }; |
8e198a08 MB |
613 | |
614 | ;;;# Conditional Statements | |
615 | ;;;t \index{conditional statements} | |
616 | ;;;t \index{IF} | |
617 | ;;;t \index{WHEN} | |
618 | ;;;t \index{UNLESS} | |
619 | ;;;t \index{conditionals} | |
620 | ||
621 | ; (IF conditional then {else}) | |
622 | ; (WHEN condition then) | |
623 | ; (UNLESS condition then) | |
624 | ; | |
f037cd4c VS |
625 | ; condition ::= a Parenscript expression |
626 | ; then ::= a Parenscript statement in statement context, a | |
627 | ; Parenscript expression in expression context | |
628 | ; else ::= a Parenscript statement in statement context, a | |
629 | ; Parenscript expression in expression context | |
8e198a08 MB |
630 | |
631 | ;;; The `IF' form compiles to the `if' javascript construct. An | |
632 | ;;; explicit `PROGN' around the then branch and the else branch is | |
633 | ;;; needed if they consist of more than one statement. When the `IF' | |
634 | ;;; form is used in an expression context, a JavaScript `?', `:' | |
635 | ;;; operator form is generated. | |
636 | ||
5ffb1eba | 637 | (if ((@ blorg is-correct)) |
8e198a08 MB |
638 | (progn (carry-on) (return i)) |
639 | (alert "blorg is not correct!")) | |
31c5dbde TC |
640 | => if (blorg.isCorrect()) { |
641 | carryOn(); | |
642 | return i; | |
643 | } else { | |
644 | alert('blorg is not correct!'); | |
5a69278c | 645 | }; |
8e198a08 | 646 | |
5ffb1eba | 647 | (+ i (if ((@ blorg add-one)) 1 2)) |
5a69278c | 648 | => i + (blorg.addOne() ? 1 : 2); |
8e198a08 MB |
649 | |
650 | ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the | |
651 | ;;; `IF' form. | |
652 | ||
5ffb1eba | 653 | (when ((@ blorg is-correct)) |
8e198a08 MB |
654 | (carry-on) |
655 | (return i)) | |
31c5dbde TC |
656 | => if (blorg.isCorrect()) { |
657 | carryOn(); | |
658 | return i; | |
5a69278c | 659 | }; |
8e198a08 | 660 | |
5ffb1eba | 661 | (unless ((@ blorg is-correct)) |
8e198a08 | 662 | (alert "blorg is not correct!")) |
31c5dbde TC |
663 | => if (!blorg.isCorrect()) { |
664 | alert('blorg is not correct!'); | |
5a69278c | 665 | }; |
8e198a08 MB |
666 | |
667 | ;;;# Variable declaration | |
668 | ;;;t \index{variable} | |
669 | ;;;t \index{variable declaration} | |
670 | ;;;t \index{binding} | |
671 | ;;;t \index{scoping} | |
672 | ;;;t \index{DEFVAR} | |
58c4ef4f | 673 | ;;;t \index{VAR} |
d777a405 | 674 | ;;;t \index{LET} |
58c4ef4f | 675 | ;;;t \index{LET*} |
8e198a08 MB |
676 | |
677 | ; (DEFVAR var {value}?) | |
58c4ef4f | 678 | ; (VAR var {value}?) |
d777a405 TC |
679 | ; (LET ({var | (var value)}*) body) |
680 | ; (LET* ({var | (var value)}*) body) | |
8e198a08 MB |
681 | ; |
682 | ; var ::= a Lisp symbol | |
f037cd4c VS |
683 | ; value ::= a Parenscript expression |
684 | ; body ::= a list of Parenscript statements | |
8e198a08 | 685 | |
58c4ef4f VS |
686 | ;;; Parenscript special variables can be declared using the `DEFVAR' |
687 | ;;; special form, which is similar to its equivalent form in | |
688 | ;;; Lisp. Note that the result is undefined if `DEFVAR' is not used as | |
689 | ;;; a top-level form. | |
8e198a08 | 690 | |
5a69278c | 691 | (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]; |
8e198a08 | 692 | |
58c4ef4f | 693 | ;;; One feature present in Parenscript that is not part of Common Lisp |
bd363c96 | 694 | ;;; are lexically-scoped global variables, which are declared using |
58c4ef4f VS |
695 | ;;; the `VAR' special form. |
696 | ||
5ffb1eba VS |
697 | ;;; Parenscript provides the `LET' and `LET*' special forms for |
698 | ;;; creating new variable bindings. Both special forms implement | |
699 | ;;; lexical scope by renaming the provided variables via `GENSYM', and | |
700 | ;;; implement dynamic binding using `TRY'-`FINALY'. Note that | |
701 | ;;; top-level `LET' and `LET*' forms will create new global variables. | |
d777a405 | 702 | |
5ffb1eba VS |
703 | ;;; Moreover, beware that scoping rules in Lisp and JavaScript are |
704 | ;;; quite different. For example, don't rely on closures capturing | |
705 | ;;; local variables in the way that you would normally expect. | |
d777a405 | 706 | |
d777a405 TC |
707 | |
708 | ;;; examples: | |
709 | ||
5ffb1eba VS |
710 | (progn |
711 | (defvar *a* 4) | |
712 | (let ((x 1) | |
713 | (*a* 2)) | |
714 | (let* ((y (+ x 1)) | |
715 | (x (+ x y))) | |
716 | (+ *a* x y)))) | |
717 | => var A = 4; | |
16151f19 VS |
718 | var x = 1; |
719 | var A_TMPSTACK1; | |
5ffb1eba | 720 | try { |
16151f19 | 721 | A_TMPSTACK1 = A; |
5ffb1eba | 722 | A = 2; |
16151f19 VS |
723 | var y = x + 1; |
724 | var x2 = x + y; | |
725 | A + x2 + y; | |
5ffb1eba | 726 | } finally { |
16151f19 | 727 | A = A_TMPSTACK1; |
5ffb1eba | 728 | }; |
8e198a08 MB |
729 | |
730 | ;;;# Iteration constructs | |
731 | ;;;t \index{iteration} | |
732 | ;;;t \index{iteration construct} | |
733 | ;;;t \index{loop} | |
734 | ;;;t \index{array traversal} | |
735 | ;;;t \index{property} | |
736 | ;;;t \index{object property} | |
737 | ;;;t \index{DO} | |
738 | ;;;t \index{DOTIMES} | |
739 | ;;;t \index{DOLIST} | |
0ce67a33 | 740 | ;;;t \index{FOR-IN} |
8e198a08 MB |
741 | ;;;t \index{WHILE} |
742 | ||
d777a405 TC |
743 | ; (DO ({var | (var {init}? {step}?)}*) (end-test {result}?) body) |
744 | ; (DO* ({var | (var {init}? {step}?)}*) (end-test {result}?) body) | |
745 | ; (DOTIMES (var numeric-form {result}?) body) | |
746 | ; (DOLIST (var list-form {result}?) body) | |
0ce67a33 | 747 | ; (FOR-IN (var object) body) |
8e198a08 MB |
748 | ; (WHILE end-test body) |
749 | ; | |
750 | ; var ::= a Lisp symbol | |
f037cd4c VS |
751 | ; numeric-form ::= a Parenscript expression resulting in a number |
752 | ; list-form ::= a Parenscript expression resulting in an array | |
753 | ; object-form ::= a Parenscript expression resulting in an object | |
754 | ; init ::= a Parenscript expression | |
755 | ; step ::= a Parenscript expression | |
756 | ; end-test ::= a Parenscript expression | |
757 | ; result ::= a Parenscript expression | |
758 | ; body ::= a list of Parenscript statements | |
8e198a08 | 759 | |
d777a405 TC |
760 | ;;; All interation special forms are transformed into JavaScript `for' |
761 | ;;; statements and, if needed, lambda expressions. | |
8e198a08 | 762 | |
d777a405 TC |
763 | ;;; `DO', `DO*', and `DOTIMES' carry the same semantics as their |
764 | ;;; Common Lisp equivalents. | |
8e198a08 | 765 | |
d777a405 | 766 | ;;; `DO*' (note the variety of possible init-forms: |
8e198a08 | 767 | |
d777a405 TC |
768 | (do* ((a) b (c (array "a" "b" "c" "d" "e")) |
769 | (d 0 (1+ d)) | |
770 | (e (aref c d) (aref c d))) | |
a14fb2cb | 771 | ((or (= d (@ c length)) (== e "x"))) |
d777a405 | 772 | (setf a d b e) |
5ffb1eba | 773 | ((@ document write) (+ "a: " a " b: " b "<br/>"))) |
31c5dbde TC |
774 | => for (var a = null, b = null, c = ['a', 'b', 'c', 'd', 'e'], d = 0, e = c[d]; !(d == c.length || e == 'x'); d += 1, e = c[d]) { |
775 | a = d; | |
776 | b = e; | |
777 | document.write('a: ' + a + ' b: ' + b + '<br/>'); | |
778 | }; | |
8e198a08 | 779 | |
5ffb1eba | 780 | ;;; `DO' |
d777a405 TC |
781 | |
782 | (do ((i 0 (1+ i)) | |
783 | (s 0 (+ s i (1+ i)))) | |
784 | ((> i 10)) | |
5ffb1eba | 785 | ((@ document write) (+ "i: " i " s: " s "<br/>"))) |
16151f19 VS |
786 | => var i = 0; |
787 | var s = 0; | |
788 | for (; i <= 10; ) { | |
789 | document.write('i: ' + i + ' s: ' + s + '<br/>'); | |
790 | var _js1 = i + 1; | |
791 | var _js2 = s + i + (i + 1); | |
792 | i = _js1; | |
793 | s = _js2; | |
31c5dbde | 794 | }; |
d777a405 TC |
795 | |
796 | ;;; compare to `DO*': | |
797 | ||
798 | (do* ((i 0 (1+ i)) | |
799 | (s 0 (+ s i (1- i)))) | |
800 | ((> i 10)) | |
5ffb1eba | 801 | ((@ document write) (+ "i: " i " s: " s "<br/>"))) |
31c5dbde TC |
802 | => for (var i = 0, s = 0; i <= 10; i += 1, s += i + (i - 1)) { |
803 | document.write('i: ' + i + ' s: ' + s + '<br/>'); | |
804 | }; | |
d777a405 TC |
805 | |
806 | ;;; `DOTIMES': | |
807 | ||
5ffb1eba VS |
808 | (let ((arr (array "a" "b" "c" "d" "e"))) |
809 | (dotimes (i (@ arr length)) | |
810 | ((@ document write) (+ "i: " i " arr[i]: " (aref arr i) "<br/>")))) | |
16151f19 VS |
811 | => var arr = ['a', 'b', 'c', 'd', 'e']; |
812 | for (var i = 0; i < arr.length; i += 1) { | |
813 | document.write('i: ' + i + ' arr[i]: ' + arr[i] + '<br/>'); | |
31c5dbde | 814 | }; |
d777a405 TC |
815 | |
816 | ;;; `DOTIMES' with return value: | |
817 | ||
5ffb1eba | 818 | (let ((res 0)) |
d777a405 TC |
819 | (alert (+ "Summation to 10 is " |
820 | (dotimes (i 10 res) | |
821 | (incf res (1+ i)))))) | |
16151f19 | 822 | => var res = 0; |
31c5dbde TC |
823 | alert('Summation to 10 is ' + (function () { |
824 | for (var i = 0; i < 10; i += 1) { | |
16151f19 | 825 | res += i + 1; |
31c5dbde | 826 | }; |
16151f19 | 827 | return res; |
31c5dbde | 828 | })()); |
d777a405 TC |
829 | |
830 | ;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS | |
831 | ;;; arrays/vectors. | |
832 | ||
5ffb1eba | 833 | (let ((l (list 1 2 4 8 16 32))) |
d777a405 | 834 | (dolist (c l) |
5ffb1eba | 835 | ((@ document write) (+ "c: " c "<br/>")))) |
16151f19 VS |
836 | => var l = [1, 2, 4, 8, 16, 32]; |
837 | for (var c = null, _js_arrvar2 = l, _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) { | |
838 | c = _js_arrvar2[_js_idx1]; | |
31c5dbde TC |
839 | document.write('c: ' + c + '<br/>'); |
840 | }; | |
d777a405 | 841 | |
5ffb1eba VS |
842 | (let ((l '(1 2 4 8 16 32)) |
843 | (s 0)) | |
d777a405 TC |
844 | (alert (+ "Sum of " l " is: " |
845 | (dolist (c l s) | |
846 | (incf s c))))) | |
16151f19 VS |
847 | => var l = [1, 2, 4, 8, 16, 32]; |
848 | var s = 0; | |
849 | alert('Sum of ' + l + ' is: ' + (function () { | |
850 | for (var c = null, _js_arrvar2 = l, _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) { | |
851 | c = _js_arrvar2[_js_idx1]; | |
852 | s += c; | |
31c5dbde | 853 | }; |
16151f19 | 854 | return s; |
31c5dbde | 855 | })()); |
d777a405 | 856 | |
0ce67a33 | 857 | ;;; `FOR-IN' is translated to the JS `for...in' statement. |
d777a405 | 858 | |
5ffb1eba | 859 | (let ((obj (create :a 1 :b 2 :c 3))) |
0ce67a33 | 860 | (for-in (i obj) |
5ffb1eba | 861 | ((@ document write) (+ i ": " (aref obj i) "<br/>")))) |
16151f19 VS |
862 | => var obj = { a : 1, b : 2, c : 3 }; |
863 | for (var i in obj) { | |
864 | document.write(i + ': ' + obj[i] + '<br/>'); | |
31c5dbde | 865 | }; |
d777a405 | 866 | |
8e198a08 MB |
867 | ;;; The `WHILE' form is transformed to the JavaScript form `while', |
868 | ;;; and loops until a termination test evaluates to false. | |
869 | ||
5ffb1eba VS |
870 | (while ((@ film is-not-finished)) |
871 | ((@ this eat) (new *popcorn))) | |
31c5dbde TC |
872 | => while (film.isNotFinished()) { |
873 | this.eat(new Popcorn); | |
5a69278c | 874 | }; |
551080b7 | 875 | |
8e198a08 MB |
876 | ;;;# The `CASE' statement |
877 | ;;;t \index{CASE} | |
3c393e09 | 878 | ;;;t \index{SWITCH} |
8e198a08 MB |
879 | ;;;t \index{switch} |
880 | ||
881 | ; (CASE case-value clause*) | |
882 | ; | |
3c393e09 | 883 | ; clause ::= (value body) | ((value*) body) | t-clause |
f037cd4c VS |
884 | ; case-value ::= a Parenscript expression |
885 | ; value ::= a Parenscript expression | |
3c393e09 | 886 | ; t-clause ::= {t | otherwise | default} body |
f037cd4c | 887 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
888 | |
889 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in | |
890 | ;;; JavaScript. Note that `CASE' is not an expression in | |
f037cd4c | 891 | ;;; Parenscript. |
8e198a08 MB |
892 | |
893 | (case (aref blorg i) | |
3c393e09 | 894 | ((1 "one") (alert "one")) |
8e198a08 | 895 | (2 (alert "two")) |
3c393e09 | 896 | (t (alert "default clause"))) |
31c5dbde TC |
897 | => switch (blorg[i]) { |
898 | case 1: | |
899 | case 'one': | |
900 | alert('one'); | |
901 | break; | |
902 | case 2: | |
903 | alert('two'); | |
904 | break; | |
905 | default: | |
906 | alert('default clause'); | |
5a69278c | 907 | }; |
8e198a08 | 908 | |
3c393e09 HH |
909 | ; (SWITCH case-value clause*) |
910 | ; clause ::= (value body) | (default body) | |
911 | ||
912 | ;;; The `SWITCH' form is the equivalent to a javascript switch statement. | |
913 | ;;; No break statements are inserted, and the default case is named `DEFAULT'. | |
914 | ;;; The `CASE' form should be prefered in most cases. | |
915 | ||
916 | (switch (aref blorg i) | |
917 | (1 (alert "If I get here")) | |
918 | (2 (alert "I also get here")) | |
919 | (default (alert "I always get here"))) | |
31c5dbde TC |
920 | => switch (blorg[i]) { |
921 | case 1: alert('If I get here'); | |
922 | case 2: alert('I also get here'); | |
923 | default: alert('I always get here'); | |
5a69278c | 924 | }; |
3c393e09 | 925 | |
8e198a08 MB |
926 | ;;;# The `WITH' statement |
927 | ;;;t \index{WITH} | |
928 | ;;;t \index{dynamic scope} | |
929 | ;;;t \index{binding} | |
930 | ;;;t \index{scoping} | |
931 | ;;;t \index{closure} | |
932 | ||
5d9cdcad | 933 | ; (WITH object body) |
8e198a08 | 934 | ; |
f037cd4c VS |
935 | ; object ::= a Parenscript expression evaluating to an object |
936 | ; body ::= a list of Parenscript statements | |
8e198a08 MB |
937 | |
938 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and | |
939 | ;;; adds the object `object' as an intermediary scope objects when | |
940 | ;;; executing the body. | |
941 | ||
5d9cdcad | 942 | (with (create :foo "foo" :i "i") |
8e198a08 | 943 | (alert (+ "i is now intermediary scoped: " i))) |
31c5dbde TC |
944 | => with ({ foo : 'foo', i : 'i' }) { |
945 | alert('i is now intermediary scoped: ' + i); | |
5a69278c | 946 | }; |
8e198a08 MB |
947 | |
948 | ;;;# The `TRY' statement | |
949 | ;;;t \index{TRY} | |
950 | ;;;t \index{CATCH} | |
951 | ;;;t \index{FINALLY} | |
952 | ;;;t \index{exception} | |
953 | ;;;t \index{error handling} | |
954 | ||
955 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) | |
956 | ; | |
f037cd4c | 957 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
958 | ; var ::= a Lisp symbol |
959 | ||
960 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and | |
961 | ;;; can be used to catch expressions thrown by the `THROW' | |
962 | ;;; form. The body of the catch clause is invoked when an exception | |
963 | ;;; is catched, and the body of the finally is always invoked when | |
964 | ;;; leaving the body of the `TRY' form. | |
965 | ||
94a05cdf | 966 | (try (throw "i") |
8e198a08 MB |
967 | (:catch (error) |
968 | (alert (+ "an error happened: " error))) | |
969 | (:finally | |
970 | (alert "Leaving the try form"))) | |
31c5dbde TC |
971 | => try { |
972 | throw 'i'; | |
973 | } catch (error) { | |
974 | alert('an error happened: ' + error); | |
975 | } finally { | |
976 | alert('Leaving the try form'); | |
5a69278c | 977 | }; |
8e198a08 MB |
978 | |
979 | ;;;# The HTML Generator | |
ecc3218c | 980 | ;;;t \index{PS-HTML} |
8e198a08 MB |
981 | ;;;t \index{HTML generation} |
982 | ||
ecc3218c | 983 | ; (PS-HTML html-expression) |
8e198a08 | 984 | |
f037cd4c | 985 | ;;; The HTML generator of Parenscript is very similar to the htmlgen |
8bb28ead VS |
986 | ;;; HTML generator library included with AllegroServe. It accepts the |
987 | ;;; same input forms as the AllegroServer HTML generator. However, | |
f037cd4c | 988 | ;;; non-HTML construct are compiled to JavaScript by the Parenscript |
8e198a08 MB |
989 | ;;; compiler. The resulting expression is a JavaScript expression. |
990 | ||
8bb28ead | 991 | (ps-html ((:a :href "foobar") "blorg")) |
5a69278c | 992 | => '<A HREF=\"foobar\">blorg</A>'; |
8e198a08 | 993 | |
8bb28ead | 994 | (ps-html ((:a :href (generate-a-link)) "blorg")) |
5a69278c | 995 | => '<A HREF=\"' + generateALink() + '\">blorg</A>'; |
8e198a08 | 996 | |
f037cd4c | 997 | ;;; We can recursively call the Parenscript compiler in an HTML |
ecc3218c | 998 | ;;; expression. |
8e198a08 | 999 | |
5ffb1eba | 1000 | ((@ document write) |
8bb28ead | 1001 | (ps-html ((:a :href "#" |
e69d0a12 | 1002 | :onclick (ps-inline (transport))) "link"))) |
5a69278c | 1003 | => document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport' + '(' + ')') + '\">link</A>'); |
8e198a08 | 1004 | |
7abef5d4 HH |
1005 | ;;; Forms may be used in attribute lists to conditionally generate |
1006 | ;;; the next attribute. In this example the textarea is sometimes disabled. | |
1007 | ||
5ffb1eba | 1008 | (let ((disabled nil) |
7abef5d4 | 1009 | (authorized t)) |
5ffb1eba | 1010 | (setf (@ element inner-h-t-m-l) |
8bb28ead | 1011 | (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled") |
7abef5d4 | 1012 | "Edit me")))) |
16151f19 VS |
1013 | => var disabled = null; |
1014 | var authorized = true; | |
31c5dbde | 1015 | element.innerHTML = |
1937c30a | 1016 | '<TEXTAREA' |
16151f19 | 1017 | + (disabled || !authorized ? ' DISABLED=\"' + 'disabled' + '\"' : '') |
1937c30a | 1018 | + '>Edit me</TEXTAREA>'; |
7abef5d4 | 1019 | |
8e198a08 MB |
1020 | ;;;# Macrology |
1021 | ;;;t \index{macro} | |
1022 | ;;;t \index{macrology} | |
ecc3218c | 1023 | ;;;t \index{DEFPSMACRO} |
8cfc6fe9 VS |
1024 | ;;;t \index{DEFMACRO/PS} |
1025 | ;;;t \index{DEFMACRO+PS} | |
1026 | ;;;t \index{DEFINE-PS-SYMBOL-MACRO} | |
1027 | ;;;t \index{IMPORT-MACROS-FROM-LISP} | |
8e198a08 MB |
1028 | ;;;t \index{MACROLET} |
1029 | ;;;t \index{SYMBOL-MACROLET} | |
ecc3218c | 1030 | ;;;t \index{PS-GENSYM} |
8e198a08 MB |
1031 | ;;;t \index{compiler} |
1032 | ||
ecc3218c | 1033 | ; (DEFPSMACRO name lambda-list macro-body) |
8cfc6fe9 VS |
1034 | ; (DEFPSMACRO/PS name lambda-list macro-body) |
1035 | ; (DEFPSMACRO+PS name lambda-list macro-body) | |
1036 | ; (DEFINE-PS-SYMBOL-MACRO symbol expansion) | |
1037 | ; (IMPORT-MACROS-FROM-LISP symbol*) | |
8e198a08 MB |
1038 | ; (MACROLET ({name lambda-list macro-body}*) body) |
1039 | ; (SYMBOL-MACROLET ({name macro-body}*) body) | |
49c50da4 | 1040 | ; (PS-GENSYM {string}) |
8e198a08 MB |
1041 | ; |
1042 | ; name ::= a Lisp symbol | |
1043 | ; lambda-list ::= a lambda list | |
f037cd4c VS |
1044 | ; macro-body ::= a Lisp body evaluating to Parenscript code |
1045 | ; body ::= a list of Parenscript statements | |
8e198a08 MB |
1046 | ; string ::= a string |
1047 | ||
f037cd4c | 1048 | ;;; Parenscript can be extended using macros, just like Lisp can be |
8e198a08 | 1049 | ;;; extended using Lisp macros. Using the special Lisp form |
f037cd4c | 1050 | ;;; `DEFPSMACRO', the Parenscript language can be |
ecc3218c | 1051 | ;;; extended. `DEFPSMACRO' adds the new macro to the toplevel macro |
f037cd4c | 1052 | ;;; environment, which is always accessible during Parenscript |
8e198a08 MB |
1053 | ;;; compilation. For example, the `1+' and `1-' operators are |
1054 | ;;; implemented using macros. | |
1055 | ||
ecc3218c | 1056 | (defpsmacro 1- (form) |
8e198a08 MB |
1057 | `(- ,form 1)) |
1058 | ||
ecc3218c | 1059 | (defpsmacro 1+ (form) |
8e198a08 MB |
1060 | `(+ ,form 1)) |
1061 | ||
f037cd4c VS |
1062 | ;;; A more complicated Parenscript macro example is the implementation |
1063 | ;;; of the `DOLIST' form (note how `PS-GENSYM', the Parenscript of | |
1064 | ;;; `GENSYM', is used to generate new Parenscript variable names): | |
8e198a08 | 1065 | |
d777a405 TC |
1066 | (defpsmacro dolist ((var array &optional (result nil result?)) &body body) |
1067 | (let ((idx (ps-gensym "_js_idx")) | |
1068 | (arrvar (ps-gensym "_js_arrvar"))) | |
1069 | `(do* (,var | |
1070 | (,arrvar ,array) | |
1071 | (,idx 0 (1+ ,idx))) | |
1072 | ((>= ,idx (slot-value ,arrvar 'length)) | |
1073 | ,@(when result? (list result))) | |
1074 | (setq ,var (aref ,arrvar ,idx)) | |
1075 | ,@body))) | |
8e198a08 | 1076 | |
f037cd4c VS |
1077 | ;;; Macros can be defined in Parenscript code itself (as opposed to |
1078 | ;;; from Lisp) by using the Parenscript `MACROLET' and `DEFMACRO' | |
8cfc6fe9 VS |
1079 | ;;; forms. Note that macros defined this way are defined in a null |
1080 | ;;; lexical environment (ex - (let ((x 1)) (defmacro baz (y) `(+ ,y | |
1081 | ;;; ,x))) will not work), since the surrounding Parenscript code is | |
1082 | ;;; just translated to JavaScript and not actually evaluated. | |
1d9f472a | 1083 | |
f037cd4c | 1084 | ;;; Parenscript also supports the use of macros defined in the |
ecc3218c | 1085 | ;;; underlying Lisp environment. Existing Lisp macros can be imported |
f037cd4c | 1086 | ;;; into the Parenscript macro environment by |
ecc3218c | 1087 | ;;; `IMPORT-MACROS-FROM-LISP'. This functionality enables code sharing |
f037cd4c | 1088 | ;;; between Parenscript and Lisp, and is useful in debugging since the |
ecc3218c VS |
1089 | ;;; full power of Lisp macroexpanders, editors and other supporting |
1090 | ;;; facilities can be used. However, it is important to note that the | |
f037cd4c | 1091 | ;;; macroexpansion of Lisp macros and Parenscript macros takes place |
ecc3218c VS |
1092 | ;;; in their own respective environments, and many Lisp macros |
1093 | ;;; (especially those provided by the Lisp implementation) expand into | |
f037cd4c | 1094 | ;;; code that is not usable by Parenscript. To make it easy for users |
ecc3218c | 1095 | ;;; to take advantage of these features, two additional macro |
f037cd4c | 1096 | ;;; definition facilities are provided by Parenscript: `DEFMACRO/PS' |
ecc3218c | 1097 | ;;; and `DEFMACRO+PS'. `DEFMACRO/PS' defines a Lisp macro and then |
f037cd4c | 1098 | ;;; imports it into the Parenscript macro environment, while |
ecc3218c | 1099 | ;;; `DEFMACRO+PS' defines two macros with the same name and expansion, |
f037cd4c | 1100 | ;;; one in Parenscript and one in Lisp. `DEFMACRO+PS' is used when the |
ecc3218c | 1101 | ;;; full 'macroexpand' of the Lisp macro yields code that cannot be |
f037cd4c | 1102 | ;;; used by Parenscript. |
8e198a08 | 1103 | |
f037cd4c | 1104 | ;;; Parenscript also supports symbol macros, which can be introduced |
8cfc6fe9 VS |
1105 | ;;; using the Parenscript form `SYMBOL-MACROLET' or defined in Lisp |
1106 | ;;; with `DEFINE-PS-SYMBOL-MACRO'. For example, the Parenscript | |
1107 | ;;; `WITH-SLOTS' is implemented using symbol macros. | |
8e198a08 | 1108 | |
8cfc6fe9 | 1109 | (defpsmacro with-slots (slots object &rest body) |
8e198a08 MB |
1110 | `(symbol-macrolet ,(mapcar #'(lambda (slot) |
1111 | `(,slot '(slot-value ,object ',slot))) | |
1112 | slots) | |
1113 | ,@body)) | |
1114 | ||
f037cd4c | 1115 | ;;;# The Parenscript namespace system |
5e74b5ce VS |
1116 | ;;;t \index{package} |
1117 | ;;;t \index{namespace} | |
1118 | ;;;t \index{PS-PACKAGE-PREFIX} | |
1119 | ||
0c542be0 | 1120 | ; (setf (PS-PACKAGE-PREFIX package-designator) string) |
5e74b5ce VS |
1121 | |
1122 | ;;; Although JavaScript does not offer namespacing or a package | |
f037cd4c | 1123 | ;;; system, Parenscript does provide a namespace mechanism for |
5e74b5ce | 1124 | ;;; generated JavaScript by integrating with the Common Lisp package |
f037cd4c | 1125 | ;;; system. Since Parenscript code is normally read in by the Lisp |
5e74b5ce VS |
1126 | ;;; reader, all symbols (except for uninterned ones, ie - those |
1127 | ;;; specified with the #: reader macro) have a Lisp package. By | |
1128 | ;;; default, no packages are prefixed. You can specify that symbols in | |
1129 | ;;; a particular package receive a prefix when translated to | |
1130 | ;;; JavaScript with the `PS-PACKAGE-PREFIX' place. | |
1131 | ||
7b8a74ee VS |
1132 | (defpackage "PS-REF.MY-LIBRARY" |
1133 | (:use "PARENSCRIPT")) | |
1134 | (setf (ps-package-prefix "PS-REF.MY-LIBRARY") "my_library_") | |
5e74b5ce | 1135 | |
7b8a74ee | 1136 | (defun ps-ref.my-library::library-function (x y) |
5e74b5ce | 1137 | (return (+ x y))) |
0c542be0 | 1138 | -> function my_library_libraryFunction(x, y) { |
5e74b5ce | 1139 | return x + y; |
5a69278c | 1140 | }; |
5e74b5ce | 1141 | |
0c542be0 VS |
1142 | ;;;# Identifier obfuscation |
1143 | ;;;t \index{obfuscation} | |
1144 | ;;;t \index{identifiers} | |
1145 | ;;;t \index{OBFUSCATE-PACKAGE} | |
1146 | ;;;t \index{UNOBFUSCATE-PACKAGE} | |
1147 | ||
7b8a74ee | 1148 | ; (OBFUSCATE-PACKAGE package-designator &optional symbol-map) |
0c542be0 VS |
1149 | ; (UNOBFUSCATE-PACKAGE package-designator) |
1150 | ||
f037cd4c | 1151 | ;;; Similar to the namespace mechanism, Parenscript provides a |
7b8a74ee VS |
1152 | ;;; facility to generate obfuscated identifiers in specified CL |
1153 | ;;; packages. The function `OBFUSCATE-PACKAGE' may optionally be | |
1154 | ;;; passed a hash-table or a closure that maps symbols to their | |
1155 | ;;; obfuscated counterparts. By default, the mapping is done using | |
1156 | ;;; `PS-GENSYM'. | |
1157 | ||
1158 | (defpackage "PS-REF.OBFUSCATE-ME") | |
1159 | (obfuscate-package "PS-REF.OBFUSCATE-ME" | |
1160 | (let ((code-pt-counter #x8CF0) | |
1161 | (symbol-map (make-hash-table))) | |
1162 | (lambda (symbol) | |
1163 | (or (gethash symbol symbol-map) | |
1164 | (setf (gethash symbol symbol-map) | |
1165 | (make-symbol (string (code-char (incf code-pt-counter))))))))) | |
1166 | ||
1167 | (defun ps-ref.obfuscate-me::a-function (a b ps-ref.obfuscate-me::foo) | |
1168 | (+ a (ps-ref.my-library::library-function b ps-ref.obfuscate-me::foo))) | |
1169 | -> function 賱(a, b, 賲) { | |
1170 | a + my_library_libraryFunction(b, 賲); | |
5a69278c | 1171 | }; |
0c542be0 VS |
1172 | |
1173 | ;;; The obfuscation and namespace facilities can be used on packages | |
1174 | ;;; at the same time. | |
1175 | ||
f037cd4c | 1176 | ;;;# The Parenscript Compiler |
8e198a08 | 1177 | ;;;t \index{compiler} |
f037cd4c | 1178 | ;;;t \index{Parenscript compiler} |
ecc3218c VS |
1179 | ;;;t \index{PS} |
1180 | ;;;t \index{PS*} | |
cb8f8e58 | 1181 | ;;;t \index{PS1*} |
ecc3218c | 1182 | ;;;t \index{PS-INLINE} |
cb8f8e58 | 1183 | ;;;t \index{PS-INLINE*} |
ecc3218c | 1184 | ;;;t \index{LISP} |
8e198a08 | 1185 | |
ecc3218c VS |
1186 | ; (PS &body body) |
1187 | ; (PS* &body body) | |
cb8f8e58 VS |
1188 | ; (PS1* parenscript-form) |
1189 | ; (PS-INLINE form &optional *js-string-delimiter*) | |
1190 | ; (PS-INLINE* form &optional *js-string-delimiter*) | |
1191 | ||
1192 | ; (LISP lisp-forms) | |
8e198a08 | 1193 | ; |
f037cd4c | 1194 | ; body ::= Parenscript statements comprising an implicit `PROGN' |
ecc3218c | 1195 | |
f037cd4c | 1196 | ;;; For static Parenscript code, the macro `PS' compiles the provided |
cb8f8e58 VS |
1197 | ;;; forms at Common Lisp macro-expansion time. `PS*' and `PS1*' |
1198 | ;;; evaluate their arguments and then compile them. All these forms | |
1199 | ;;; except for `PS1*' treat the given forms as an implicit | |
1200 | ;;; `PROGN'. | |
1201 | ||
f037cd4c | 1202 | ;;; `PS-INLINE' and `PS-INLINE*' take a single Parenscript form and |
cb8f8e58 VS |
1203 | ;;; output a string starting with "javascript:" that can be used in |
1204 | ;;; HTML node attributes. As well, they provide an argument to bind | |
1205 | ;;; the value of *js-string-delimiter* to control the value of the | |
1206 | ;;; JavaScript string escape character to be compatible with whatever | |
1207 | ;;; the HTML generation mechanism is used (for example, if HTML | |
1208 | ;;; strings are delimited using #\', using #\" will avoid conflicts | |
1209 | ;;; without requiring the output JavaScript code to be escaped). By | |
1210 | ;;; default the value is taken from *js-inline-string-delimiter*. | |
1211 | ||
f037cd4c | 1212 | ;;; Parenscript can also call out to arbitrary Common Lisp code at |
cb8f8e58 VS |
1213 | ;;; code output time using the special form `LISP'. The form provided |
1214 | ;;; to `LISP' is evaluated, and its result is compiled as though it | |
f037cd4c | 1215 | ;;; were Parenscript code. For `PS' and `PS-INLINE', the Parenscript |
cb8f8e58 VS |
1216 | ;;; output code is generated at macro-expansion time, and the `LISP' |
1217 | ;;; statements are inserted inline and have access to the enclosing | |
1218 | ;;; Common Lisp lexical environment. `PS*' and `PS1*' evaluate the | |
1219 | ;;; `LISP' forms with eval, providing them access to the current | |
1220 | ;;; dynamic environment only. |