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