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