Commit | Line | Data |
---|---|---|
8e198a08 MB |
1 | ;;;# ParenScript Language Reference |
2 | ||
3 | ;;; This chapters describes the core constructs of ParenScript, as | |
4 | ;;; well as its compilation model. This chapter is aimed to be a | |
5 | ;;; comprehensive reference for ParenScript developers. Programmers | |
6 | ;;; looking for how to tweak the ParenScript compiler itself should | |
7 | ;;; turn to the ParenScript Internals chapter. | |
8 | ||
9 | ;;;# Statements and Expressions | |
10 | ;;;t \index{statement} | |
11 | ;;;t \index{expression} | |
12 | ||
13 | ;;; In contrast to Lisp, where everything is an expression, JavaScript | |
14 | ;;; makes the difference between an expression, which evaluates to a | |
15 | ;;; value, and a statement, which has no value. Examples for | |
16 | ;;; JavaScript statements are `for', `with' and `while'. Most | |
17 | ;;; ParenScript forms are expression, but certain special forms are | |
18 | ;;; not (the forms which are transformed to a JavaScript | |
19 | ;;; statement). All ParenScript expressions are statements | |
20 | ;;; though. Certain forms, like `IF' and `PROGN', generate different | |
21 | ;;; JavaScript constructs whether they are used in an expression | |
22 | ;;; context or a statement context. For example: | |
23 | ||
24 | (+ i (if 1 2 3)) => i + (1 ? 2 : 3) | |
25 | ||
26 | (if 1 2 3) | |
27 | => if (1) { | |
28 | 2; | |
29 | } else { | |
30 | 3; | |
31 | } | |
32 | ||
33 | ;;;# Symbol conversion | |
34 | ;;;t \index{symbol} | |
35 | ;;;t \index{symbol conversion} | |
36 | ||
37 | ;;; Lisp symbols are converted to JavaScript symbols by following a | |
38 | ;;; few simple rules. Special characters `!', `?', `#', `$', `@', `%', | |
39 | ;;; '/', `*' and `+' get replaced by their written-out equivalents | |
40 | ;;; "bang", "what", "hash", "dollar", "at", "percent", "slash", | |
41 | ;;; "start" and "plus" respectively. | |
42 | ||
43 | !?#$@% => bangwhathashdollaratpercent | |
44 | ||
45 | ;;; The `-' is an indication that the following character should be | |
46 | ;;; converted to uppercase. Thus, `-' separated symbols are converted | |
47 | ;;; to camelcase. The `_' character however is left untouched. | |
48 | ||
49 | bla-foo-bar => blaFooBar | |
50 | ||
51 | ;;; If you want a JavaScript symbol beginning with an uppercase, you | |
52 | ;;; can either use a leading `-', which can be misleading in a | |
53 | ;;; mathematical context, or a leading `*'. | |
54 | ||
55 | *array => Array | |
56 | ||
57 | ;;; The `.' character is left as is in symbols. This allows the | |
58 | ;;; ParenScript programmer to use a practical shortcut when accessing | |
59 | ;;; slots or methods of JavaScript objects. Instead of writing | |
60 | ||
61 | (slot-value foobar 'slot) | |
62 | ||
63 | ;;; we can write | |
64 | ||
65 | foobar.slot | |
66 | ||
67 | ;;; A symbol beggining and ending with `+' or `*' is converted to all | |
68 | ;;; uppercase, to signify that this is a constant or a global | |
69 | ;;; variable. | |
70 | ||
71 | *global-array* => GLOBALARRAY | |
72 | ||
73 | *global-array*.length => GLOBALARRAY.length | |
74 | ||
75 | ;;;## Reserved Keywords | |
76 | ;;;t \index{keyword} | |
77 | ;;;t \index{reserved keywords} | |
78 | ||
79 | ;;; The following keywords and symbols are reserved in ParenScript, | |
80 | ;;; and should not be used as variable names. | |
81 | ||
82 | ! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || | |
83 | *= /= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ | |
84 | ABSTRACT AND AREF ARRAY BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS | |
85 | COMMA CONST CONTINUE CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE | |
86 | DO DOEACH DOLIST DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS FALSE | |
87 | FINAL FINALLY FLOAT FLOOR FOR FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF | |
88 | INSTANCEOF INT INTERFACE JS LAMBDA LET LISP LIST LONG MAKE-ARRAY NATIVE NEW | |
89 | NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED PUBLIC RANDOM REGEX RETURN | |
90 | SETF SHORT SLOT-VALUE STATIC SUPER SWITCH SYMBOL-MACROLET SYNCHRONIZED T | |
91 | THIS THROW THROWS TRANSIENT TRY TYPEOF UNDEFINED UNLESS VAR VOID VOLATILE | |
92 | WHEN WHILE WITH WITH-SLOTS | |
93 | ||
94 | ;;;# Literal values | |
95 | ;;;t \index{literal value} | |
96 | ||
97 | ;;;## Number literals | |
98 | ;;;t \index{number} | |
99 | ;;;t \index{number literal} | |
100 | ||
101 | ; number ::= a Lisp number | |
102 | ||
103 | ;;; | |
104 | ;;; ParenScript supports the standard JavaScript literal | |
105 | ;;; values. Numbers are compiled into JavaScript numbers. | |
106 | ||
107 | 1 => 1 | |
108 | ||
109 | 123.123 => 123.123 | |
110 | ||
111 | ;;; Note that the base is not conserved between Lisp and JavaScript. | |
112 | ||
113 | #x10 => 16 | |
114 | ||
115 | ;;;## String literals | |
116 | ;;;t \index{string} | |
117 | ;;;t \index{string literal} | |
118 | ||
119 | ; string ::= a Lisp string | |
120 | ||
121 | ;;; Lisp strings are converted into JavaScript literals. | |
122 | ||
7a7d6c73 HH |
123 | "foobar" => 'foobar' |
124 | ||
125 | "bratzel bub" => 'bratzel bub' | |
8e198a08 MB |
126 | |
127 | ;;; Escapes in Lisp are not converted to JavaScript escapes. However, | |
128 | ;;; to avoid having to use double backslashes when constructing a | |
129 | ;;; string, you can use the CL-INTERPOL library by Edi Weitz. | |
130 | ||
131 | ;;;## Array literals | |
132 | ;;;t \index{array} | |
133 | ;;;t \index{ARRAY} | |
134 | ;;;t \index{MAKE-ARRAY} | |
135 | ;;;t \index{AREF} | |
136 | ;;;t \index{array literal} | |
137 | ||
138 | ; (ARRAY {values}*) | |
139 | ; (MAKE-ARRAY {values}*) | |
140 | ; (AREF array index) | |
141 | ; | |
142 | ; values ::= a ParenScript expression | |
143 | ; array ::= a ParenScript expression | |
144 | ; index ::= a ParenScript expression | |
145 | ||
146 | ;;; Array literals can be created using the `ARRAY' form. | |
147 | ||
7a7d6c73 | 148 | (array) => [ ] |
8e198a08 MB |
149 | |
150 | (array 1 2 3) => [ 1, 2, 3 ] | |
151 | ||
152 | (array (array 2 3) | |
153 | (array "foobar" "bratzel bub")) | |
7a7d6c73 | 154 | => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ] |
8e198a08 MB |
155 | |
156 | ;;; Arrays can also be created with a call to the `Array' function | |
157 | ;;; using the `MAKE-ARRAY'. The two forms have the exact same semantic | |
158 | ;;; on the JavaScript side. | |
159 | ||
160 | (make-array) => new Array() | |
161 | ||
162 | (make-array 1 2 3) => new Array(1, 2, 3) | |
163 | ||
164 | (make-array | |
165 | (make-array 2 3) | |
166 | (make-array "foobar" "bratzel bub")) | |
7a7d6c73 | 167 | => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub')) |
8e198a08 MB |
168 | |
169 | ;;; Indexing arrays in ParenScript is done using the form `AREF'. Note | |
170 | ;;; that JavaScript knows of no such thing as an array. Subscripting | |
171 | ;;; an array is in fact reading a property from an object. So in a | |
172 | ;;; semantic sense, there is no real difference between `AREF' and | |
173 | ;;; `SLOT-VALUE'. | |
174 | ||
175 | ;;;## Object literals | |
176 | ;;;t \index{CREATE} | |
177 | ;;;t \index{SLOT-VALUE} | |
178 | ;;;t \index{WITH-SLOTS} | |
179 | ;;;t \index{object literal} | |
180 | ;;;t \index{object} | |
181 | ;;;t \index{object property} | |
182 | ;;;t \index{property} | |
183 | ||
184 | ; (CREATE {name value}*) | |
185 | ; (SLOT-VALUE object slot-name) | |
186 | ; (WITH-SLOTS ({slot-name}*) object body) | |
187 | ; | |
188 | ; name ::= a ParenScript symbol or a Lisp keyword | |
189 | ; value ::= a ParenScript expression | |
190 | ; object ::= a ParenScript object expression | |
191 | ; slot-name ::= a quoted Lisp symbol | |
192 | ; body ::= a list of ParenScript statements | |
193 | ||
194 | ;;; | |
195 | ;;; Object literals can be create using the `CREATE' form. Arguments | |
196 | ;;; to the `CREATE' form is a list of property names and values. To be | |
197 | ;;; more "lispy", the property names can be keywords. | |
198 | ||
199 | (create :foo "bar" :blorg 1) | |
7a7d6c73 | 200 | => { foo : 'bar', |
8e198a08 MB |
201 | blorg : 1 } |
202 | ||
203 | (create :foo "hihi" | |
204 | :blorg (array 1 2 3) | |
205 | :another-object (create :schtrunz 1)) | |
7a7d6c73 | 206 | => { foo : 'hihi', |
8e198a08 MB |
207 | blorg : [ 1, 2, 3 ], |
208 | anotherObject : { schtrunz : 1 } } | |
209 | ||
210 | ;;; Object properties can be accessed using the `SLOT-VALUE' form, | |
211 | ;;; which takes an object and a slot-name. | |
212 | ||
213 | (slot-value an-object 'foo) => anObject.foo | |
214 | ||
215 | ;;; A programmer can also use the "." symbol notation explained above. | |
216 | ||
217 | an-object.foo => anObject.foo | |
218 | ||
219 | ;;; The form `WITH-SLOTS' can be used to bind the given slot-name | |
220 | ;;; symbols to a macro that will expand into a `SLOT-VALUE' form at | |
221 | ;;; expansion time. | |
222 | ||
223 | (with-slots (a b c) this | |
224 | (+ a b c)) | |
7a7d6c73 | 225 | => this.a + this.b + this.c; |
8e198a08 MB |
226 | |
227 | ;;;## Regular Expression literals | |
228 | ;;;t \index{REGEX} | |
229 | ;;;t \index{regular expression} | |
230 | ;;;t \index{CL-INTERPOL} | |
231 | ||
232 | ; (REGEX regex) | |
233 | ; | |
234 | ; regex ::= a Lisp string | |
235 | ||
236 | ;;; Regular expressions can be created by using the `REGEX' form. The | |
237 | ;;; regex form actually does nothing at all to its argument, and | |
238 | ;;; prints it as is. | |
239 | ||
240 | (regex "/foobar/i") => /foobar/i | |
241 | ||
242 | ;;; Here CL-INTERPOL proves really useful. | |
243 | ||
244 | (regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i | |
245 | ||
246 | ;;;## Literal symbols | |
247 | ;;;t \index{T} | |
248 | ;;;t \index{FALSE} | |
249 | ;;;t \index{NIL} | |
250 | ;;;t \index{UNDEFINED} | |
251 | ;;;t \index{THIS} | |
252 | ;;;t \index{literal symbols} | |
253 | ;;;t \index{null} | |
254 | ;;;t \index{true} | |
255 | ||
256 | ; T, FALSE, NIL, UNDEFINED, THIS | |
257 | ||
258 | ;;; The Lisp symbols `T' and `FALSE' are converted to their JavaScript | |
259 | ;;; boolean equivalents `true' and `false'. | |
260 | ||
261 | T => true | |
7a7d6c73 | 262 | |
8e198a08 MB |
263 | FALSE => false |
264 | ||
265 | ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword | |
266 | ;;; `null'. | |
267 | ||
268 | NIL => null | |
269 | ||
270 | ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword | |
271 | ;;; `undefined'. | |
272 | ||
273 | UNDEFINED => undefined | |
274 | ||
275 | ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword | |
276 | ;;; `this'. | |
277 | ||
278 | THIS => this | |
279 | ||
280 | ;;;# Variables | |
281 | ;;;t \index{variable} | |
282 | ;;;t \index{symbol} | |
283 | ||
284 | ; variable ::= a Lisp symbol | |
285 | ||
286 | ;;; All the other literal Lisp values that are not recognized as | |
287 | ;;; special forms or symbol macros are converted to JavaScript | |
288 | ;;; variables. This extreme freedom is actually quite useful, as it | |
289 | ;;; allows the ParenScript programmer to be flexible, as flexible as | |
290 | ;;; JavaScript itself. | |
291 | ||
292 | variable => variable | |
293 | ||
294 | a-variable => aVariable | |
295 | ||
296 | *math => Math | |
297 | ||
298 | *math.floor => Math.floor | |
299 | ||
300 | ;;;# Function calls and method calls | |
301 | ;;;t \index{function} | |
302 | ;;;t \index{function call} | |
303 | ;;;t \index{method} | |
304 | ;;;t \index{method call} | |
305 | ||
306 | ; (function {argument}*) | |
307 | ; (method object {argument}*) | |
308 | ; | |
309 | ; function ::= a ParenScript expression or a Lisp symbol | |
310 | ; method ::= a Lisp symbol beginning with . | |
311 | ; object ::= a ParenScript expression | |
312 | ; argument ::= a ParenScript expression | |
313 | ||
314 | ;;; Any list passed to the JavaScript that is not recognized as a | |
315 | ;;; macro or a special form (see "Macro Expansion" below) is | |
316 | ;;; interpreted as a function call. The function call is converted to | |
317 | ;;; the normal JavaScript function call representation, with the | |
318 | ;;; arguments given in paren after the function name. | |
319 | ||
320 | (blorg 1 2) => blorg(1, 2) | |
321 | ||
322 | (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4)) | |
323 | => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]) | |
324 | ||
325 | ((aref foo i) 1 2) => foo[i](1, 2) | |
326 | ||
327 | ;;; A method call is a function call where the function name is a | |
328 | ;;; symbol and begins with a "." . In a method call, the name of the | |
329 | ;;; function is append to its first argument, thus reflecting the | |
330 | ;;; method call syntax of JavaScript. Please note that most method | |
331 | ;;; calls can be abbreviated using the "." trick in symbol names (see | |
332 | ;;; "Symbol Conversion" above). | |
333 | ||
334 | (.blorg this 1 2) => this.blorg(1, 2) | |
335 | ||
336 | (this.blorg 1 2) => this.blorg(1, 2) | |
337 | ||
338 | (.blorg (aref foobar 1) NIL T) | |
339 | => foobar[1].blorg(null, true) | |
340 | ||
341 | ;;;# Operator Expressions | |
342 | ;;;t \index{operator} | |
343 | ;;;t \index{operator expression} | |
344 | ;;;t \index{assignment operator} | |
345 | ;;;t \index{EQL} | |
346 | ;;;t \index{NOT} | |
347 | ;;;t \index{AND} | |
348 | ;;;t \index{OR} | |
349 | ||
350 | ; (operator {argument}*) | |
351 | ; (single-operator argument) | |
352 | ; | |
353 | ; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL, | |
354 | ; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR. | |
355 | ; single-operator ::= one of INCF, DECF, ++, --, NOT, ! | |
356 | ; argument ::= a ParenScript expression | |
357 | ||
358 | ;;; Operator forms are similar to function call forms, but have an | |
359 | ;;; operator as function name. | |
360 | ;;; | |
361 | ;;; Please note that `=' is converted to `==' in JavaScript. The `=' | |
362 | ;;; ParenScript operator is not the assignment operator. Unlike | |
363 | ;;; JavaScript, ParenScript supports multiple arguments to the | |
364 | ;;; operators. | |
365 | ||
366 | (* 1 2) => 1 * 2 | |
367 | ||
368 | (= 1 2) => 1 == 2 | |
369 | ||
370 | (eql 1 2) => 1 == 2 | |
371 | ||
372 | ;;; Note that the resulting expression is correctly parenthized, | |
373 | ;;; according to the JavaScript operator precedence that can be found | |
374 | ;;; in table form at: | |
375 | ||
376 | http://www.codehouse.com/javascript/precedence/ | |
377 | ||
378 | (* 1 (+ 2 3 4) 4 (/ 6 7)) | |
379 | => 1 * (2 + 3 + 4) * 4 * (6 / 7) | |
380 | ||
381 | ;;; The pre/post increment and decrement operators are also | |
382 | ;;; available. `INCF' and `DECF' are the pre-incrementing and | |
383 | ;;; pre-decrementing operators, and `++' and `--' are the | |
384 | ;;; post-decrementing version of the operators. These operators can | |
385 | ;;; take only one argument. | |
386 | ||
387 | (++ i) => i++ | |
388 | ||
389 | (-- i) => i-- | |
390 | ||
391 | (incf i) => ++i | |
392 | ||
393 | (decf i) => --i | |
394 | ||
395 | ;;; The `1+' and `1-' operators are shortforms for adding and | |
396 | ;;; substracting 1. | |
397 | ||
398 | (1- i) => i - 1 | |
399 | ||
400 | (1+ i) => i + 1 | |
401 | ||
402 | ;;; The `not' operator actually optimizes the code a bit. If `not' is | |
403 | ;;; used on another boolean-returning operator, the operator is | |
404 | ;;; reversed. | |
405 | ||
406 | (not (< i 2)) => i >= 2 | |
407 | ||
408 | (not (eql i 2)) => i != 2 | |
409 | ||
410 | ;;;# Body forms | |
411 | ;;;t \index{body form} | |
412 | ;;;t \index{PROGN} | |
413 | ;;;t \index{body statement} | |
414 | ||
415 | ; (PROGN {statement}*) in statement context | |
416 | ; (PROGN {expression}*) in expression context | |
417 | ; | |
418 | ; statement ::= a ParenScript statement | |
419 | ; expression ::= a ParenScript expression | |
420 | ||
421 | ;;; The `PROGN' special form defines a sequence of statements when | |
422 | ;;; used in a statement context, or sequence of expression when used | |
423 | ;;; in an expression context. The `PROGN' special form is added | |
424 | ;;; implicitly around the branches of conditional executions forms, | |
425 | ;;; function declarations and iteration constructs. | |
426 | ||
427 | ;;; For example, in a statement context: | |
428 | ||
429 | (progn (blorg i) (blafoo i)) | |
430 | => blorg(i); | |
431 | blafoo(i); | |
432 | ||
433 | ;;; In an expression context: | |
434 | ||
435 | (+ i (progn (blorg i) (blafoo i))) | |
436 | => i + (blorg(i), blafoo(i)) | |
437 | ||
438 | ;;; A `PROGN' form doesn't lead to additional indentation or | |
439 | ;;; additional braces around it's body. | |
440 | ||
441 | ;;;# Function Definition | |
442 | ;;;t \index{function} | |
443 | ;;;t \index{method} | |
444 | ;;;t \index{function definition} | |
445 | ;;;t \index{DEFUN} | |
446 | ;;;t \index{LAMBDA} | |
447 | ;;;t \index{closure} | |
448 | ;;;t \index{anonymous function} | |
449 | ||
450 | ; (DEFUN name ({argument}*) body) | |
451 | ; (LAMBDA ({argument}*) body) | |
452 | ; | |
453 | ; name ::= a Lisp Symbol | |
454 | ; argument ::= a Lisp symbol | |
455 | ; body ::= a list of ParenScript statements | |
456 | ||
457 | ;;; As in Lisp, functions are defined using the `DEFUN' form, which | |
458 | ;;; takes a name, a list of arguments, and a function body. An | |
459 | ;;; implicit `PROGN' is added around the body statements. | |
460 | ||
461 | (defun a-function (a b) | |
462 | (return (+ a b))) | |
463 | => function aFunction(a, b) { | |
464 | return a + b; | |
465 | } | |
466 | ||
467 | ;;; Anonymous functions can be created using the `LAMBDA' form, which | |
468 | ;;; is the same as `DEFUN', but without function name. In fact, | |
469 | ;;; `LAMBDA' creates a `DEFUN' with an empty function name. | |
470 | ||
471 | (lambda (a b) (return (+ a b))) | |
472 | => function (a, b) { | |
473 | return a + b; | |
474 | } | |
475 | ||
476 | ;;;# Assignment | |
477 | ;;;t \index{assignment} | |
478 | ;;;t \index{SETF} | |
479 | ;;;t \index{assignment operator} | |
480 | ||
481 | ; (SETF {lhs rhs}*) | |
482 | ; | |
483 | ; lhs ::= a ParenScript left hand side expression | |
484 | ; rhs ::= a ParenScript expression | |
485 | ||
486 | ;;; Assignment is done using the `SETF' form, which is transformed | |
487 | ;;; into a series of assignments using the JavaScript `=' operator. | |
488 | ||
489 | (setf a 1) => a = 1 | |
490 | ||
491 | (setf a 2 b 3 c 4 x (+ a b c)) | |
492 | => a = 2; | |
493 | b = 3; | |
494 | c = 4; | |
495 | x = a + b + c; | |
496 | ||
497 | ;;; The `SETF' form can transform assignments of a variable with an | |
498 | ;;; operator expression using this variable into a more "efficient" | |
499 | ;;; assignment operator form. For example: | |
500 | ||
501 | (setf a (1+ a)) => a++ | |
502 | ||
503 | (setf a (* 2 3 4 a 4 a)) => a *= 2 * 3 * 4 * 4 * a | |
504 | ||
505 | (setf a (- 1 a)) => a = 1 - a | |
506 | ||
507 | ;;;# Single argument statements | |
508 | ;;;t \index{single-argument statement} | |
509 | ;;;t \index{RETURN} | |
510 | ;;;t \index{THROW} | |
511 | ;;;t \index{THROW} | |
512 | ;;;t \index{function} | |
513 | ||
514 | ; (RETURN {value}?) | |
515 | ; (THROW {value}?) | |
516 | ; | |
517 | ; value ::= a ParenScript expression | |
518 | ||
519 | ;;; The single argument statements `return' and `throw' are generated | |
520 | ;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a | |
521 | ;;; `TRY' form. `RETURN' is used to return a value from a function | |
522 | ;;; call. | |
523 | ||
524 | (return 1) => return 1 | |
525 | ||
7a7d6c73 | 526 | (throw "foobar") => throw 'foobar' |
8e198a08 MB |
527 | |
528 | ;;;# Single argument expression | |
529 | ;;;t \index{single-argument expression} | |
530 | ;;;t \index{object creation} | |
531 | ;;;t \index{object deletion} | |
532 | ;;;t \index{DELETE} | |
533 | ;;;t \index{VOID} | |
534 | ;;;t \index{TYPEOF} | |
535 | ;;;t \index{INSTANCEOF} | |
536 | ;;;t \index{NEW} | |
537 | ;;;t \index{new} | |
538 | ||
539 | ; (DELETE {value}) | |
540 | ; (VOID {value}) | |
541 | ; (TYPEOF {value}) | |
542 | ; (INSTANCEOF {value}) | |
543 | ; (NEW {value}) | |
544 | ; | |
545 | ; value ::= a ParenScript expression | |
546 | ||
547 | ;;; The single argument expressions `delete', `void', `typeof', | |
548 | ;;; `instanceof' and `new' are generated by the forms `DELETE', | |
549 | ;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a | |
550 | ;;; ParenScript expression. | |
551 | ||
552 | (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4) | |
553 | ||
554 | (if (= (typeof blorg) *string) | |
555 | (alert (+ "blorg is a string: " blorg)) | |
556 | (alert "blorg is not a string")) | |
557 | => if (typeof blorg == String) { | |
7a7d6c73 | 558 | alert('blorg is a string: ' + blorg); |
8e198a08 | 559 | } else { |
7a7d6c73 | 560 | alert('blorg is not a string'); |
8e198a08 MB |
561 | } |
562 | ||
563 | ;;;# Conditional Statements | |
564 | ;;;t \index{conditional statements} | |
565 | ;;;t \index{IF} | |
566 | ;;;t \index{WHEN} | |
567 | ;;;t \index{UNLESS} | |
568 | ;;;t \index{conditionals} | |
569 | ||
570 | ; (IF conditional then {else}) | |
571 | ; (WHEN condition then) | |
572 | ; (UNLESS condition then) | |
573 | ; | |
574 | ; condition ::= a ParenScript expression | |
575 | ; then ::= a ParenScript statement in statement context, a | |
576 | ; ParenScript expression in expression context | |
577 | ; else ::= a ParenScript statement in statement context, a | |
578 | ; ParenScript expression in expression context | |
579 | ||
580 | ;;; The `IF' form compiles to the `if' javascript construct. An | |
581 | ;;; explicit `PROGN' around the then branch and the else branch is | |
582 | ;;; needed if they consist of more than one statement. When the `IF' | |
583 | ;;; form is used in an expression context, a JavaScript `?', `:' | |
584 | ;;; operator form is generated. | |
585 | ||
586 | (if (blorg.is-correct) | |
587 | (progn (carry-on) (return i)) | |
588 | (alert "blorg is not correct!")) | |
589 | => if (blorg.isCorrect()) { | |
590 | carryOn(); | |
591 | return i; | |
592 | } else { | |
7a7d6c73 | 593 | alert('blorg is not correct!'); |
8e198a08 MB |
594 | } |
595 | ||
596 | (+ i (if (blorg.add-one) 1 2)) | |
597 | => i + (blorg.addOne() ? 1 : 2) | |
598 | ||
599 | ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the | |
600 | ;;; `IF' form. | |
601 | ||
602 | (when (blorg.is-correct) | |
603 | (carry-on) | |
604 | (return i)) | |
605 | => if (blorg.isCorrect()) { | |
606 | carryOn(); | |
607 | return i; | |
608 | } | |
609 | ||
610 | (unless (blorg.is-correct) | |
611 | (alert "blorg is not correct!")) | |
612 | => if (!blorg.isCorrect()) { | |
7a7d6c73 | 613 | alert('blorg is not correct!'); |
8e198a08 MB |
614 | } |
615 | ||
616 | ;;;# Variable declaration | |
617 | ;;;t \index{variable} | |
618 | ;;;t \index{variable declaration} | |
619 | ;;;t \index{binding} | |
620 | ;;;t \index{scoping} | |
621 | ;;;t \index{DEFVAR} | |
622 | ;;;t \index{LET} | |
623 | ||
624 | ; (DEFVAR var {value}?) | |
625 | ; (LET ({var | (var value)) body) | |
626 | ; | |
627 | ; var ::= a Lisp symbol | |
628 | ; value ::= a ParenScript expression | |
629 | ; body ::= a list of ParenScript statements | |
630 | ||
631 | ;;; Variables (either local or global) can be declared using the | |
632 | ;;; `DEFVAR' form, which is similar to its equivalent form in | |
633 | ;;; Lisp. The `DEFVAR' is converted to "var ... = ..." form in | |
634 | ;;; JavaScript. | |
635 | ||
7a7d6c73 | 636 | (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]; |
8e198a08 MB |
637 | |
638 | (if (= i 1) | |
639 | (progn (defvar blorg "hallo") | |
640 | (alert blorg)) | |
641 | (progn (defvar blorg "blitzel") | |
642 | (alert blorg))) | |
643 | => if (i == 1) { | |
7a7d6c73 | 644 | var blorg = 'hallo'; |
8e198a08 MB |
645 | alert(blorg); |
646 | } else { | |
7a7d6c73 | 647 | var blorg = 'blitzel'; |
8e198a08 MB |
648 | alert(blorg); |
649 | } | |
650 | ||
651 | ;;; A more lispy way to declare local variable is to use the `LET' | |
652 | ;;; form, which is similar to its Lisp form. | |
653 | ||
654 | (if (= i 1) | |
655 | (let ((blorg "hallo")) | |
656 | (alert blorg)) | |
657 | (let ((blorg "blitzel")) | |
658 | (alert blorg))) | |
659 | => if (i == 1) { | |
7a7d6c73 | 660 | var blorg = 'hallo'; |
8e198a08 MB |
661 | alert(blorg); |
662 | } else { | |
7a7d6c73 | 663 | var blorg = 'blitzel'; |
8e198a08 MB |
664 | alert(blorg); |
665 | } | |
666 | ||
667 | ;;; However, beware that scoping in Lisp and JavaScript are quite | |
668 | ;;; different. For example, don't rely on closures capturing local | |
669 | ;;; variables in the way you'd think they would. | |
670 | ||
671 | ;;;# Iteration constructs | |
672 | ;;;t \index{iteration} | |
673 | ;;;t \index{iteration construct} | |
674 | ;;;t \index{loop} | |
675 | ;;;t \index{array traversal} | |
676 | ;;;t \index{property} | |
677 | ;;;t \index{object property} | |
678 | ;;;t \index{DO} | |
679 | ;;;t \index{DOTIMES} | |
680 | ;;;t \index{DOLIST} | |
681 | ;;;t \index{DOEACH} | |
682 | ;;;t \index{WHILE} | |
683 | ||
684 | ; (DO ({var | (var {init}? {step}?)}*) (end-test) body) | |
685 | ; (DOTIMES (var numeric-form) body) | |
686 | ; (DOLIST (var list-form) body) | |
687 | ; (DOEACH (var object) body) | |
688 | ; (WHILE end-test body) | |
689 | ; | |
690 | ; var ::= a Lisp symbol | |
691 | ; numeric-form ::= a ParenScript expression resulting in a number | |
692 | ; list-form ::= a ParenScript expression resulting in an array | |
693 | ; object ::= a ParenScript expression resulting in an object | |
694 | ; init ::= a ParenScript expression | |
695 | ; step ::= a ParenScript expression | |
696 | ; end-test ::= a ParenScript expression | |
697 | ; body ::= a list of ParenScript statements | |
698 | ||
699 | ;;; The `DO' form, which is similar to its Lisp form, is transformed | |
700 | ;;; into a JavaScript `for' statement. Note that the ParenScript `DO' | |
701 | ;;; form does not have a return value, that is because `for' is a | |
702 | ;;; statement and not an expression in JavaScript. | |
703 | ||
704 | (do ((i 0 (1+ i)) | |
705 | (l (aref blorg i) (aref blorg i))) | |
706 | ((or (= i blorg.length) | |
707 | (eql l "Fumitastic"))) | |
708 | (document.write (+ "L is " l))) | |
7a7d6c73 HH |
709 | => for (var i = 0, l = blorg[i]; |
710 | !(i == blorg.length || l == 'Fumitastic'); | |
8e198a08 | 711 | i = i + 1, l = blorg[i]) { |
7a7d6c73 | 712 | document.write('L is ' + l); |
8e198a08 MB |
713 | } |
714 | ||
715 | ;;; The `DOTIMES' form, which lets a variable iterate from 0 upto an | |
716 | ;;; end value, is a shortcut for `DO'. | |
717 | ||
718 | (dotimes (i blorg.length) | |
719 | (document.write (+ "L is " (aref blorg i)))) | |
7a7d6c73 HH |
720 | => for (var i = 0; i < blorg.length; i = i + 1) { |
721 | document.write('L is ' + blorg[i]); | |
8e198a08 MB |
722 | } |
723 | ||
724 | ;;; The `DOLIST' form is a shortcut for iterating over an array. Note | |
725 | ;;; that this form creates temporary variables using a function called | |
726 | ;;; `JS-GENSYM', which is similar to its Lisp counterpart `GENSYM'. | |
727 | ||
728 | (dolist (l blorg) | |
729 | (document.write (+ "L is " l))) | |
7a7d6c73 HH |
730 | => { |
731 | var tmpArr1 = blorg; | |
732 | for (var tmpI2 = 0; tmpI2 < tmpArr1.length; | |
733 | tmpI2 = tmpI2 + 1) { | |
734 | var l = tmpArr1[tmpI2]; | |
735 | document.write('L is ' + l); | |
736 | } | |
8e198a08 MB |
737 | } |
738 | ||
7a7d6c73 | 739 | |
8e198a08 MB |
740 | ;;; The `DOEACH' form is converted to a `for (var .. in ..)' form in |
741 | ;;; JavaScript. It is used to iterate over the enumerable properties | |
742 | ;;; of an object. | |
743 | ||
744 | (doeach (i object) | |
745 | (document.write (+ i " is " (aref object i)))) | |
746 | => for (var i in object) { | |
7a7d6c73 | 747 | document.write(i + ' is ' + object[i]); |
8e198a08 MB |
748 | } |
749 | ||
750 | ;;; The `WHILE' form is transformed to the JavaScript form `while', | |
751 | ;;; and loops until a termination test evaluates to false. | |
752 | ||
753 | (while (film.is-not-finished) | |
754 | (this.eat (new *popcorn))) | |
755 | => while (film.isNotFinished()) { | |
756 | this.eat(new Popcorn); | |
757 | } | |
758 | ||
759 | ;;;# The `CASE' statement | |
760 | ;;;t \index{CASE} | |
761 | ;;;t \index{switch} | |
762 | ||
763 | ; (CASE case-value clause*) | |
764 | ; | |
765 | ; clause ::= (value body) | |
766 | ; case-value ::= a ParenScript expression | |
767 | ; value ::= a ParenScript expression | |
768 | ; body ::= a list of ParenScript statements | |
769 | ||
770 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in | |
771 | ;;; JavaScript. Note that `CASE' is not an expression in | |
772 | ;;; ParenScript. The default case is not named `T' in ParenScript, but | |
773 | ;;; `DEFAULT' instead. | |
774 | ||
775 | (case (aref blorg i) | |
776 | (1 (alert "one")) | |
777 | (2 (alert "two")) | |
778 | (default (alert "default clause"))) | |
779 | => switch (blorg[i]) { | |
7a7d6c73 HH |
780 | case 1: alert('one'); |
781 | case 2: alert('two'); | |
782 | default: alert('default clause'); | |
8e198a08 MB |
783 | } |
784 | ||
785 | ;;;# The `WITH' statement | |
786 | ;;;t \index{WITH} | |
787 | ;;;t \index{dynamic scope} | |
788 | ;;;t \index{binding} | |
789 | ;;;t \index{scoping} | |
790 | ;;;t \index{closure} | |
791 | ||
792 | ; (WITH (object) body) | |
793 | ; | |
794 | ; object ::= a ParenScript expression evaluating to an object | |
795 | ; body ::= a list of ParenScript statements | |
796 | ||
797 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and | |
798 | ;;; adds the object `object' as an intermediary scope objects when | |
799 | ;;; executing the body. | |
800 | ||
801 | (with ((create :foo "foo" :i "i")) | |
802 | (alert (+ "i is now intermediary scoped: " i))) | |
7a7d6c73 HH |
803 | => with ({ foo : 'foo', |
804 | i : 'i' }) { | |
805 | alert('i is now intermediary scoped: ' + i); | |
8e198a08 MB |
806 | } |
807 | ||
808 | ;;;# The `TRY' statement | |
809 | ;;;t \index{TRY} | |
810 | ;;;t \index{CATCH} | |
811 | ;;;t \index{FINALLY} | |
812 | ;;;t \index{exception} | |
813 | ;;;t \index{error handling} | |
814 | ||
815 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) | |
816 | ; | |
817 | ; body ::= a list of ParenScript statements | |
818 | ; var ::= a Lisp symbol | |
819 | ||
820 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and | |
821 | ;;; can be used to catch expressions thrown by the `THROW' | |
822 | ;;; form. The body of the catch clause is invoked when an exception | |
823 | ;;; is catched, and the body of the finally is always invoked when | |
824 | ;;; leaving the body of the `TRY' form. | |
825 | ||
826 | (try (throw "i") | |
827 | (:catch (error) | |
828 | (alert (+ "an error happened: " error))) | |
829 | (:finally | |
830 | (alert "Leaving the try form"))) | |
831 | => try { | |
7a7d6c73 | 832 | throw 'i'; |
8e198a08 | 833 | } catch (error) { |
7a7d6c73 | 834 | alert('an error happened: ' + error); |
8e198a08 | 835 | } finally { |
7a7d6c73 | 836 | alert('Leaving the try form'); |
8e198a08 MB |
837 | } |
838 | ||
839 | ;;;# The HTML Generator | |
840 | ;;;t \index{HTML} | |
841 | ;;;t \index{HTML generation} | |
28967ee4 HH |
842 | ;;;t \index{CSS} |
843 | ;;;t \index{CSS generation} | |
844 | ||
8e198a08 MB |
845 | |
846 | ; (HTML html-expression) | |
847 | ||
848 | ;;; The HTML generator of ParenScript is very similar to the HTML | |
849 | ;;; generator included in AllegroServe. It accepts the same input | |
850 | ;;; forms as the AllegroServer HTML generator. However, non-HTML | |
851 | ;;; construct are compiled to JavaScript by the ParenScript | |
852 | ;;; compiler. The resulting expression is a JavaScript expression. | |
853 | ||
854 | (html ((:a :href "foobar") "blorg")) | |
7a7d6c73 | 855 | => '<a href=\"foobar\">blorg</a>' |
8e198a08 MB |
856 | |
857 | (html ((:a :href (generate-a-link)) "blorg")) | |
7a7d6c73 | 858 | => '<a href=\"' + generateALink() + '\">blorg</a>' |
8e198a08 MB |
859 | |
860 | ;;; We can recursively call the JS compiler in a HTML expression. | |
861 | ||
862 | (document.write | |
7a7d6c73 | 863 | (html ((:a :href "#" |
8e198a08 | 864 | :onclick (js-inline (transport))) "link"))) |
7a7d6c73 HH |
865 | => document.write |
866 | ('<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">link</a>') | |
8e198a08 | 867 | |
28967ee4 HH |
868 | ; (CSS-INLINE css-expression) |
869 | ||
870 | ;;; Stylesheets can also be created in ParenScript. | |
871 | ||
872 | (css-inline :color "red" | |
873 | :font-size "x-small") | |
874 | => 'color:red;font-size:x-small' | |
875 | ||
876 | (defun make-color-div(color-name) | |
877 | (return (html ((:div :style (css-inline :color color-name)) | |
878 | color-name " looks like this.")))) | |
879 | => function makeColorDiv(colorName) { | |
880 | return '<div style=\"' + ('color:' + colorName) + '\">' + colorName | |
881 | + ' looks like this.</div>'; | |
882 | } | |
883 | ||
8e198a08 MB |
884 | ;;;# Macrology |
885 | ;;;t \index{macro} | |
886 | ;;;t \index{macrology} | |
887 | ;;;t \index{DEFJSMACRO} | |
888 | ;;;t \index{MACROLET} | |
889 | ;;;t \index{SYMBOL-MACROLET} | |
890 | ;;;t \index{JS-GENSYM} | |
891 | ;;;t \index{compiler} | |
892 | ||
893 | ; (DEFJSMACRO name lambda-list macro-body) | |
894 | ; (MACROLET ({name lambda-list macro-body}*) body) | |
895 | ; (SYMBOL-MACROLET ({name macro-body}*) body) | |
896 | ; (JS-GENSYM {string}?) | |
897 | ; | |
898 | ; name ::= a Lisp symbol | |
899 | ; lambda-list ::= a lambda list | |
900 | ; macro-body ::= a Lisp body evaluating to ParenScript code | |
901 | ; body ::= a list of ParenScript statements | |
902 | ; string ::= a string | |
903 | ||
904 | ;;; ParenScript can be extended using macros, just like Lisp can be | |
905 | ;;; extended using Lisp macros. Using the special Lisp form | |
906 | ;;; `DEFJSMACRO', the ParenScript language can be | |
907 | ;;; extended. `DEFJSMACRO' adds the new macro to the toplevel macro | |
908 | ;;; environment, which is always accessible during ParenScript | |
909 | ;;; compilation. For example, the `1+' and `1-' operators are | |
910 | ;;; implemented using macros. | |
911 | ||
912 | (defjsmacro 1- (form) | |
913 | `(- ,form 1)) | |
914 | ||
915 | (defjsmacro 1+ (form) | |
916 | `(+ ,form 1)) | |
917 | ||
918 | ;;; A more complicated ParenScript macro example is the implementation | |
919 | ;;; of the `DOLIST' form (note how `JS-GENSYM', the ParenScript of | |
920 | ;;; `GENSYM', is used to generate new ParenScript variable names): | |
921 | ||
922 | (defjsmacro dolist (i-array &rest body) | |
923 | (let ((var (first i-array)) | |
924 | (array (second i-array)) | |
925 | (arrvar (js-gensym "arr")) | |
926 | (idx (js-gensym "i"))) | |
927 | `(let ((,arrvar ,array)) | |
928 | (do ((,idx 0 (++ ,idx))) | |
929 | ((>= ,idx (slot-value ,arrvar 'length))) | |
930 | (let ((,var (aref ,arrvar ,idx))) | |
931 | ,@body))))) | |
932 | ||
933 | ;;; Macros can be added dynamically to the macro environment by using | |
934 | ;;; the ParenScript `MACROLET' form (note that while `DEFJSMACRO' is a | |
935 | ;;; Lisp form, `MACROLET' and `SYMBOL-MACROLET' are ParenScript forms). | |
936 | ||
937 | ;;; ParenScript also supports symbol macros, which can be introduced | |
938 | ;;; using the ParenScript form `SYMBOL-MACROLET'. A new macro | |
939 | ;;; environment is created and added to the current macro environment | |
940 | ;;; list while compiling the body of the `SYMBOL-MACROLET' form. For | |
941 | ;;; example, the ParenScript `WITH-SLOTS' is implemented using symbol | |
942 | ;;; macros. | |
943 | ||
944 | (defjsmacro with-slots (slots object &rest body) | |
945 | `(symbol-macrolet ,(mapcar #'(lambda (slot) | |
946 | `(,slot '(slot-value ,object ',slot))) | |
947 | slots) | |
948 | ,@body)) | |
949 | ||
950 | ;;;# The ParenScript Compiler | |
951 | ;;;t \index{compiler} | |
952 | ;;;t \index{ParenScript compiler} | |
953 | ;;;t \index{JS-COMPILE} | |
954 | ;;;t \index{JS-TO-STRINGS} | |
955 | ;;;t \index{JS-TO-STATEMENT-STRINGS} | |
956 | ;;;t \index{JS-TO-STRING} | |
957 | ;;;t \index{JS-TO-LINE} | |
958 | ;;;t \index{JS} | |
959 | ;;;t \index{JS-INLINE} | |
960 | ;;;t \index{JS-FILE} | |
961 | ;;;t \index{JS-SCRIPT} | |
962 | ;;;t \index{nested compilation} | |
963 | ||
964 | ; (JS-COMPILE expr) | |
965 | ; (JS-TO-STRINGS compiled-expr position) | |
966 | ; (JS-TO-STATEMENT-STRINGS compiled-expr position) | |
967 | ; | |
968 | ; compiled-expr ::= a compiled ParenScript expression | |
969 | ; position ::= a column number | |
970 | ; | |
971 | ; (JS-TO-STRING expression) | |
972 | ; (JS-TO-LINE expression) | |
973 | ; | |
974 | ; expression ::= a Lisp list of ParenScript code | |
975 | ; | |
976 | ; (JS body) | |
977 | ; (JS-INLINE body) | |
978 | ; (JS-FILE body) | |
979 | ; (JS-SCRIPT body) | |
980 | ; | |
981 | ; body ::= a list of ParenScript statements | |
982 | ||
983 | ;;; The ParenScript compiler can be invoked from withing Lisp and from | |
984 | ;;; within ParenScript itself. The primary API function is | |
985 | ;;; `JS-COMPILE', which takes a list of ParenScript, and returns an | |
986 | ;;; internal object representing the compiled ParenScript. | |
987 | ||
988 | (js-compile '(foobar 1 2)) | |
989 | => #<JS::FUNCTION-CALL {584AA5DD}> | |
990 | ||
991 | ;;; This internal object can be transformed to a string using the | |
992 | ;;; methods `JS-TO-STRINGS' and `JS-TO-STATEMENT-STRINGS', which | |
993 | ;;; interpret the ParenScript in expression and in statement context | |
994 | ;;; respectively. They take an additional parameter indicating the | |
995 | ;;; start-position on a line (please note that the indentation code is | |
996 | ;;; not perfect, and this string interface will likely be | |
997 | ;;; changed). They return a list of strings, where each string | |
998 | ;;; represents a new line of JavaScript code. They can be joined | |
999 | ;;; together to form a single string. | |
1000 | ||
1001 | (js-to-strings (js-compile '(foobar 1 2)) 0) | |
1002 | => ("foobar(1, 2)") | |
1003 | ||
1004 | ;;; As a shortcut, ParenScript provides the functions `JS-TO-STRING' | |
1005 | ;;; and `JS-TO-LINE', which return the JavaScript string of the | |
1006 | ;;; compiled expression passed as an argument. | |
1007 | ||
1008 | (js-to-string '(foobar 1 2)) | |
1009 | => "foobar(1, 2)" | |
1010 | ||
1011 | ;;; For static ParenScript code, the macros `JS', `JS-INLINE', | |
1012 | ;;; `JS-FILE' and `JS-SCRIPT' avoid the need to quote the ParenScript | |
1013 | ;;; expression. All these forms add an implicit `PROGN' form around | |
1014 | ;;; the body. `JS' returns a string of the compiled body, where the | |
1015 | ;;; other expression return an expression that can be embedded in a | |
1016 | ;;; HTML generation construct using the AllegroServe HTML | |
1017 | ;;; generator. `JS-SCRIPT' generates a "SCRIPT" node, `JS-INLINE' | |
1018 | ;;; generates a string to be used in node attributs, and `JS-FILE' | |
1019 | ;;; prints the compiled ParenScript code to the HTML stream. | |
1020 | ||
1021 | ;;; These macros are also available inside ParenScript itself, and | |
1022 | ;;; generate strings that can be used inside ParenScript code. Note | |
1023 | ;;; that `JS-INLINE' in ParenScript is not the same `JS-INLINE' form | |
1024 | ;;; as in Lisp, for example. The same goes for the other compilation | |
1025 | ;;; macros. | |
1026 |