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 | |
3b238048 | 38 | ;;; few simple rules. Special characters `!', `?', `#', `@', `%', |
8e198a08 | 39 | ;;; '/', `*' and `+' get replaced by their written-out equivalents |
3b238048 HH |
40 | ;;; "bang", "what", "hash", "at", "percent", "slash", |
41 | ;;; "start" and "plus" respectively. The `$' character is untouched. | |
8e198a08 | 42 | |
3b238048 | 43 | !?#@% => bangwhathashatpercent |
8e198a08 MB |
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 | |
94a05cdf | 105 | ;;; values. Numbers are compiled into JavaScript numbers. |
8e198a08 MB |
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) | |
94a05cdf | 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)) | |
94a05cdf LC |
206 | => { foo : 'hihi', |
207 | blorg : [ 1, 2, 3 ], | |
8e198a08 MB |
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)) | |
b44afd8f | 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 | ||
ca4fb762 HH |
236 | ;;; Regular expressions can be created by using the `REGEX' form. If |
237 | ;;; the argument does not start with a slash, it is surrounded by | |
238 | ;;; slashes to make it a proper JavaScript regex. If the argument | |
239 | ;;; starts with a slash it is left as it is. This makes it possible | |
240 | ;;; to use modifiers such as slash-i (case-insensitive) or | |
241 | ;;; slash-g (match-globally (all)). | |
242 | ||
243 | (regex "foobar") => /foobar/ | |
8e198a08 MB |
244 | |
245 | (regex "/foobar/i") => /foobar/i | |
246 | ||
247 | ;;; Here CL-INTERPOL proves really useful. | |
248 | ||
249 | (regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i | |
250 | ||
251 | ;;;## Literal symbols | |
252 | ;;;t \index{T} | |
253 | ;;;t \index{FALSE} | |
254 | ;;;t \index{NIL} | |
255 | ;;;t \index{UNDEFINED} | |
256 | ;;;t \index{THIS} | |
257 | ;;;t \index{literal symbols} | |
258 | ;;;t \index{null} | |
259 | ;;;t \index{true} | |
260 | ||
261 | ; T, FALSE, NIL, UNDEFINED, THIS | |
262 | ||
263 | ;;; The Lisp symbols `T' and `FALSE' are converted to their JavaScript | |
94a05cdf | 264 | ;;; boolean equivalents `true' and `false'. |
8e198a08 MB |
265 | |
266 | T => true | |
7a7d6c73 | 267 | |
8e198a08 MB |
268 | FALSE => false |
269 | ||
270 | ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword | |
271 | ;;; `null'. | |
272 | ||
273 | NIL => null | |
274 | ||
275 | ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword | |
276 | ;;; `undefined'. | |
277 | ||
278 | UNDEFINED => undefined | |
279 | ||
280 | ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword | |
281 | ;;; `this'. | |
282 | ||
283 | THIS => this | |
284 | ||
285 | ;;;# Variables | |
286 | ;;;t \index{variable} | |
287 | ;;;t \index{symbol} | |
288 | ||
289 | ; variable ::= a Lisp symbol | |
290 | ||
291 | ;;; All the other literal Lisp values that are not recognized as | |
292 | ;;; special forms or symbol macros are converted to JavaScript | |
293 | ;;; variables. This extreme freedom is actually quite useful, as it | |
294 | ;;; allows the ParenScript programmer to be flexible, as flexible as | |
295 | ;;; JavaScript itself. | |
296 | ||
297 | variable => variable | |
298 | ||
299 | a-variable => aVariable | |
300 | ||
301 | *math => Math | |
302 | ||
303 | *math.floor => Math.floor | |
304 | ||
305 | ;;;# Function calls and method calls | |
306 | ;;;t \index{function} | |
307 | ;;;t \index{function call} | |
308 | ;;;t \index{method} | |
309 | ;;;t \index{method call} | |
310 | ||
311 | ; (function {argument}*) | |
312 | ; (method object {argument}*) | |
313 | ; | |
314 | ; function ::= a ParenScript expression or a Lisp symbol | |
315 | ; method ::= a Lisp symbol beginning with . | |
316 | ; object ::= a ParenScript expression | |
317 | ; argument ::= a ParenScript expression | |
318 | ||
319 | ;;; Any list passed to the JavaScript that is not recognized as a | |
320 | ;;; macro or a special form (see "Macro Expansion" below) is | |
321 | ;;; interpreted as a function call. The function call is converted to | |
322 | ;;; the normal JavaScript function call representation, with the | |
323 | ;;; arguments given in paren after the function name. | |
324 | ||
325 | (blorg 1 2) => blorg(1, 2) | |
326 | ||
327 | (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4)) | |
328 | => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]) | |
329 | ||
330 | ((aref foo i) 1 2) => foo[i](1, 2) | |
331 | ||
332 | ;;; A method call is a function call where the function name is a | |
333 | ;;; symbol and begins with a "." . In a method call, the name of the | |
334 | ;;; function is append to its first argument, thus reflecting the | |
335 | ;;; method call syntax of JavaScript. Please note that most method | |
336 | ;;; calls can be abbreviated using the "." trick in symbol names (see | |
337 | ;;; "Symbol Conversion" above). | |
338 | ||
339 | (.blorg this 1 2) => this.blorg(1, 2) | |
340 | ||
341 | (this.blorg 1 2) => this.blorg(1, 2) | |
342 | ||
343 | (.blorg (aref foobar 1) NIL T) | |
344 | => foobar[1].blorg(null, true) | |
345 | ||
346 | ;;;# Operator Expressions | |
347 | ;;;t \index{operator} | |
348 | ;;;t \index{operator expression} | |
349 | ;;;t \index{assignment operator} | |
350 | ;;;t \index{EQL} | |
351 | ;;;t \index{NOT} | |
352 | ;;;t \index{AND} | |
353 | ;;;t \index{OR} | |
354 | ||
355 | ; (operator {argument}*) | |
356 | ; (single-operator argument) | |
357 | ; | |
358 | ; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL, | |
359 | ; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR. | |
360 | ; single-operator ::= one of INCF, DECF, ++, --, NOT, ! | |
361 | ; argument ::= a ParenScript expression | |
362 | ||
363 | ;;; Operator forms are similar to function call forms, but have an | |
94a05cdf | 364 | ;;; operator as function name. |
8e198a08 MB |
365 | ;;; |
366 | ;;; Please note that `=' is converted to `==' in JavaScript. The `=' | |
367 | ;;; ParenScript operator is not the assignment operator. Unlike | |
368 | ;;; JavaScript, ParenScript supports multiple arguments to the | |
369 | ;;; operators. | |
370 | ||
371 | (* 1 2) => 1 * 2 | |
372 | ||
373 | (= 1 2) => 1 == 2 | |
374 | ||
375 | (eql 1 2) => 1 == 2 | |
376 | ||
ecc3218c | 377 | ;;; Note that the resulting expression is correctly parenthesized, |
8e198a08 MB |
378 | ;;; according to the JavaScript operator precedence that can be found |
379 | ;;; in table form at: | |
380 | ||
381 | http://www.codehouse.com/javascript/precedence/ | |
382 | ||
383 | (* 1 (+ 2 3 4) 4 (/ 6 7)) | |
384 | => 1 * (2 + 3 + 4) * 4 * (6 / 7) | |
385 | ||
f7c2734a | 386 | ;;; The pre increment and decrement operators are also |
8e198a08 | 387 | ;;; available. `INCF' and `DECF' are the pre-incrementing and |
f7c2734a | 388 | ;;; pre-decrementing operators. These operators can |
8e198a08 MB |
389 | ;;; take only one argument. |
390 | ||
8e198a08 MB |
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 | ||
72332f2a | 489 | (setf a 1) => a = 1; |
8e198a08 MB |
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 | ||
72332f2a | 501 | (setf a (1+ a)) => a++; |
8e198a08 | 502 | |
72332f2a | 503 | (setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a; |
8e198a08 | 504 | |
72332f2a | 505 | (setf a (- 1 a)) => a = 1 - a; |
8e198a08 MB |
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 | ||
b44afd8f | 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 | ||
ecc3218c VS |
651 | ;;; Another way to declare local variables is to use the `LET' form. |
652 | ;;; Note that the ParenScript `LET' compiles to a straightforward | |
653 | ;;; assignment and does not have lexical-scoping semantics, unlike its | |
654 | ;;; Lisp cousin. | |
8e198a08 MB |
655 | |
656 | (if (= i 1) | |
657 | (let ((blorg "hallo")) | |
658 | (alert blorg)) | |
659 | (let ((blorg "blitzel")) | |
660 | (alert blorg))) | |
661 | => if (i == 1) { | |
7a7d6c73 | 662 | var blorg = 'hallo'; |
8e198a08 MB |
663 | alert(blorg); |
664 | } else { | |
7a7d6c73 | 665 | var blorg = 'blitzel'; |
8e198a08 MB |
666 | alert(blorg); |
667 | } | |
668 | ||
ecc3218c | 669 | ;;; Moreover, beware that scoping in Lisp and JavaScript are quite |
8e198a08 MB |
670 | ;;; different. For example, don't rely on closures capturing local |
671 | ;;; variables in the way you'd think they would. | |
672 | ||
673 | ;;;# Iteration constructs | |
674 | ;;;t \index{iteration} | |
675 | ;;;t \index{iteration construct} | |
676 | ;;;t \index{loop} | |
677 | ;;;t \index{array traversal} | |
678 | ;;;t \index{property} | |
679 | ;;;t \index{object property} | |
680 | ;;;t \index{DO} | |
681 | ;;;t \index{DOTIMES} | |
682 | ;;;t \index{DOLIST} | |
683 | ;;;t \index{DOEACH} | |
684 | ;;;t \index{WHILE} | |
685 | ||
686 | ; (DO ({var | (var {init}? {step}?)}*) (end-test) body) | |
687 | ; (DOTIMES (var numeric-form) body) | |
688 | ; (DOLIST (var list-form) body) | |
689 | ; (DOEACH (var object) body) | |
690 | ; (WHILE end-test body) | |
691 | ; | |
692 | ; var ::= a Lisp symbol | |
693 | ; numeric-form ::= a ParenScript expression resulting in a number | |
694 | ; list-form ::= a ParenScript expression resulting in an array | |
695 | ; object ::= a ParenScript expression resulting in an object | |
696 | ; init ::= a ParenScript expression | |
697 | ; step ::= a ParenScript expression | |
698 | ; end-test ::= a ParenScript expression | |
699 | ; body ::= a list of ParenScript statements | |
700 | ||
701 | ;;; The `DO' form, which is similar to its Lisp form, is transformed | |
702 | ;;; into a JavaScript `for' statement. Note that the ParenScript `DO' | |
703 | ;;; form does not have a return value, that is because `for' is a | |
704 | ;;; statement and not an expression in JavaScript. | |
705 | ||
706 | (do ((i 0 (1+ i)) | |
707 | (l (aref blorg i) (aref blorg i))) | |
708 | ((or (= i blorg.length) | |
709 | (eql l "Fumitastic"))) | |
710 | (document.write (+ "L is " l))) | |
94a05cdf | 711 | => for (var i = 0, l = blorg[i]; |
7a7d6c73 | 712 | !(i == blorg.length || l == 'Fumitastic'); |
8e198a08 | 713 | i = i + 1, l = blorg[i]) { |
7a7d6c73 | 714 | document.write('L is ' + l); |
8e198a08 MB |
715 | } |
716 | ||
717 | ;;; The `DOTIMES' form, which lets a variable iterate from 0 upto an | |
718 | ;;; end value, is a shortcut for `DO'. | |
719 | ||
720 | (dotimes (i blorg.length) | |
721 | (document.write (+ "L is " (aref blorg i)))) | |
7a7d6c73 HH |
722 | => for (var i = 0; i < blorg.length; i = i + 1) { |
723 | document.write('L is ' + blorg[i]); | |
8e198a08 MB |
724 | } |
725 | ||
726 | ;;; The `DOLIST' form is a shortcut for iterating over an array. Note | |
727 | ;;; that this form creates temporary variables using a function called | |
f7c2734a | 728 | ;;; `PS-GENSYM', which is similar to its Lisp counterpart `GENSYM'. |
8e198a08 MB |
729 | |
730 | (dolist (l blorg) | |
731 | (document.write (+ "L is " l))) | |
f7c2734a | 732 | => var tmpArr1 = blorg; |
7a7d6c73 HH |
733 | for (var tmpI2 = 0; tmpI2 < tmpArr1.length; |
734 | tmpI2 = tmpI2 + 1) { | |
735 | var l = tmpArr1[tmpI2]; | |
736 | document.write('L is ' + l); | |
379978f8 | 737 | }; |
7a7d6c73 | 738 | |
8e198a08 MB |
739 | ;;; The `DOEACH' form is converted to a `for (var .. in ..)' form in |
740 | ;;; JavaScript. It is used to iterate over the enumerable properties | |
741 | ;;; of an object. | |
742 | ||
743 | (doeach (i object) | |
744 | (document.write (+ i " is " (aref object i)))) | |
745 | => for (var i in object) { | |
7a7d6c73 | 746 | document.write(i + ' is ' + object[i]); |
8e198a08 MB |
747 | } |
748 | ||
749 | ;;; The `WHILE' form is transformed to the JavaScript form `while', | |
750 | ;;; and loops until a termination test evaluates to false. | |
751 | ||
752 | (while (film.is-not-finished) | |
753 | (this.eat (new *popcorn))) | |
754 | => while (film.isNotFinished()) { | |
755 | this.eat(new Popcorn); | |
756 | } | |
551080b7 | 757 | |
8e198a08 MB |
758 | ;;;# The `CASE' statement |
759 | ;;;t \index{CASE} | |
3c393e09 | 760 | ;;;t \index{SWITCH} |
8e198a08 MB |
761 | ;;;t \index{switch} |
762 | ||
763 | ; (CASE case-value clause*) | |
764 | ; | |
3c393e09 | 765 | ; clause ::= (value body) | ((value*) body) | t-clause |
8e198a08 MB |
766 | ; case-value ::= a ParenScript expression |
767 | ; value ::= a ParenScript expression | |
3c393e09 | 768 | ; t-clause ::= {t | otherwise | default} body |
8e198a08 MB |
769 | ; body ::= a list of ParenScript statements |
770 | ||
771 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in | |
772 | ;;; JavaScript. Note that `CASE' is not an expression in | |
3c393e09 | 773 | ;;; ParenScript. |
8e198a08 MB |
774 | |
775 | (case (aref blorg i) | |
3c393e09 | 776 | ((1 "one") (alert "one")) |
8e198a08 | 777 | (2 (alert "two")) |
3c393e09 | 778 | (t (alert "default clause"))) |
8e198a08 | 779 | => switch (blorg[i]) { |
b44afd8f | 780 | case 1: |
3c393e09 HH |
781 | case 'one': |
782 | alert('one'); | |
783 | break; | |
784 | case 2: | |
785 | alert('two'); | |
786 | break; | |
7a7d6c73 | 787 | default: alert('default clause'); |
8e198a08 MB |
788 | } |
789 | ||
3c393e09 HH |
790 | ; (SWITCH case-value clause*) |
791 | ; clause ::= (value body) | (default body) | |
792 | ||
793 | ;;; The `SWITCH' form is the equivalent to a javascript switch statement. | |
794 | ;;; No break statements are inserted, and the default case is named `DEFAULT'. | |
795 | ;;; The `CASE' form should be prefered in most cases. | |
796 | ||
797 | (switch (aref blorg i) | |
798 | (1 (alert "If I get here")) | |
799 | (2 (alert "I also get here")) | |
800 | (default (alert "I always get here"))) | |
801 | => switch (blorg[i]) { | |
802 | case 1: alert('If I get here'); | |
803 | case 2: alert('I also get here'); | |
804 | default: alert('I always get here'); | |
805 | } | |
806 | ||
807 | ||
8e198a08 MB |
808 | ;;;# The `WITH' statement |
809 | ;;;t \index{WITH} | |
810 | ;;;t \index{dynamic scope} | |
811 | ;;;t \index{binding} | |
812 | ;;;t \index{scoping} | |
813 | ;;;t \index{closure} | |
814 | ||
5d9cdcad | 815 | ; (WITH object body) |
8e198a08 MB |
816 | ; |
817 | ; object ::= a ParenScript expression evaluating to an object | |
818 | ; body ::= a list of ParenScript statements | |
819 | ||
820 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and | |
821 | ;;; adds the object `object' as an intermediary scope objects when | |
822 | ;;; executing the body. | |
823 | ||
5d9cdcad | 824 | (with (create :foo "foo" :i "i") |
8e198a08 | 825 | (alert (+ "i is now intermediary scoped: " i))) |
94a05cdf | 826 | => with ({ foo : 'foo', |
7a7d6c73 HH |
827 | i : 'i' }) { |
828 | alert('i is now intermediary scoped: ' + i); | |
8e198a08 MB |
829 | } |
830 | ||
831 | ;;;# The `TRY' statement | |
832 | ;;;t \index{TRY} | |
833 | ;;;t \index{CATCH} | |
834 | ;;;t \index{FINALLY} | |
835 | ;;;t \index{exception} | |
836 | ;;;t \index{error handling} | |
837 | ||
838 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) | |
839 | ; | |
840 | ; body ::= a list of ParenScript statements | |
841 | ; var ::= a Lisp symbol | |
842 | ||
843 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and | |
844 | ;;; can be used to catch expressions thrown by the `THROW' | |
845 | ;;; form. The body of the catch clause is invoked when an exception | |
846 | ;;; is catched, and the body of the finally is always invoked when | |
847 | ;;; leaving the body of the `TRY' form. | |
848 | ||
94a05cdf | 849 | (try (throw "i") |
8e198a08 MB |
850 | (:catch (error) |
851 | (alert (+ "an error happened: " error))) | |
852 | (:finally | |
853 | (alert "Leaving the try form"))) | |
854 | => try { | |
7a7d6c73 | 855 | throw 'i'; |
8e198a08 | 856 | } catch (error) { |
7a7d6c73 | 857 | alert('an error happened: ' + error); |
8e198a08 | 858 | } finally { |
7a7d6c73 | 859 | alert('Leaving the try form'); |
8e198a08 MB |
860 | } |
861 | ||
862 | ;;;# The HTML Generator | |
ecc3218c | 863 | ;;;t \index{PS-HTML} |
8e198a08 | 864 | ;;;t \index{HTML generation} |
28967ee4 HH |
865 | ;;;t \index{CSS} |
866 | ;;;t \index{CSS generation} | |
867 | ||
8e198a08 | 868 | |
ecc3218c | 869 | ; (PS-HTML html-expression) |
8e198a08 | 870 | |
8bb28ead VS |
871 | ;;; The HTML generator of ParenScript is very similar to the htmlgen |
872 | ;;; HTML generator library included with AllegroServe. It accepts the | |
873 | ;;; same input forms as the AllegroServer HTML generator. However, | |
874 | ;;; non-HTML construct are compiled to JavaScript by the ParenScript | |
8e198a08 MB |
875 | ;;; compiler. The resulting expression is a JavaScript expression. |
876 | ||
8bb28ead | 877 | (ps-html ((:a :href "foobar") "blorg")) |
7a7d6c73 | 878 | => '<a href=\"foobar\">blorg</a>' |
8e198a08 | 879 | |
8bb28ead | 880 | (ps-html ((:a :href (generate-a-link)) "blorg")) |
7a7d6c73 | 881 | => '<a href=\"' + generateALink() + '\">blorg</a>' |
8e198a08 | 882 | |
ecc3218c VS |
883 | ;;; We can recursively call the ParenScript compiler in an HTML |
884 | ;;; expression. | |
8e198a08 MB |
885 | |
886 | (document.write | |
8bb28ead VS |
887 | (ps-html ((:a :href "#" |
888 | :onclick (lisp (ps-inline (transport)))) "link"))) | |
b44afd8f | 889 | => document.write('<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">link</a>') |
8e198a08 | 890 | |
7abef5d4 HH |
891 | ;;; Forms may be used in attribute lists to conditionally generate |
892 | ;;; the next attribute. In this example the textarea is sometimes disabled. | |
893 | ||
894 | (let ((disabled nil) | |
895 | (authorized t)) | |
896 | (setf element.inner-h-t-m-l | |
8bb28ead | 897 | (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled") |
7abef5d4 | 898 | "Edit me")))) |
f7c2734a | 899 | => var disabled = null; |
7abef5d4 HH |
900 | var authorized = true; |
901 | element.innerHTML = | |
902 | '<textarea' | |
903 | + (disabled || !authorized ? ' disabled=\"' + 'disabled' + '\"' : '') | |
904 | + '>Edit me</textarea>'; | |
7abef5d4 | 905 | |
28967ee4 HH |
906 | ; (CSS-INLINE css-expression) |
907 | ||
908 | ;;; Stylesheets can also be created in ParenScript. | |
909 | ||
910 | (css-inline :color "red" | |
911 | :font-size "x-small") | |
912 | => 'color:red;font-size:x-small' | |
913 | ||
914 | (defun make-color-div(color-name) | |
8bb28ead | 915 | (return (ps-html ((:div :style (css-inline :color color-name)) |
28967ee4 HH |
916 | color-name " looks like this.")))) |
917 | => function makeColorDiv(colorName) { | |
918 | return '<div style=\"' + ('color:' + colorName) + '\">' + colorName | |
919 | + ' looks like this.</div>'; | |
920 | } | |
921 | ||
8e198a08 MB |
922 | ;;;# Macrology |
923 | ;;;t \index{macro} | |
924 | ;;;t \index{macrology} | |
ecc3218c | 925 | ;;;t \index{DEFPSMACRO} |
8e198a08 MB |
926 | ;;;t \index{MACROLET} |
927 | ;;;t \index{SYMBOL-MACROLET} | |
ecc3218c | 928 | ;;;t \index{PS-GENSYM} |
8e198a08 MB |
929 | ;;;t \index{compiler} |
930 | ||
ecc3218c | 931 | ; (DEFPSMACRO name lambda-list macro-body) |
8e198a08 MB |
932 | ; (MACROLET ({name lambda-list macro-body}*) body) |
933 | ; (SYMBOL-MACROLET ({name macro-body}*) body) | |
ecc3218c | 934 | ; (PS-GENSYM {string}?) |
8e198a08 MB |
935 | ; |
936 | ; name ::= a Lisp symbol | |
937 | ; lambda-list ::= a lambda list | |
938 | ; macro-body ::= a Lisp body evaluating to ParenScript code | |
939 | ; body ::= a list of ParenScript statements | |
940 | ; string ::= a string | |
941 | ||
942 | ;;; ParenScript can be extended using macros, just like Lisp can be | |
943 | ;;; extended using Lisp macros. Using the special Lisp form | |
ecc3218c VS |
944 | ;;; `DEFPSMACRO', the ParenScript language can be |
945 | ;;; extended. `DEFPSMACRO' adds the new macro to the toplevel macro | |
8e198a08 MB |
946 | ;;; environment, which is always accessible during ParenScript |
947 | ;;; compilation. For example, the `1+' and `1-' operators are | |
948 | ;;; implemented using macros. | |
949 | ||
ecc3218c | 950 | (defpsmacro 1- (form) |
8e198a08 MB |
951 | `(- ,form 1)) |
952 | ||
ecc3218c | 953 | (defpsmacro 1+ (form) |
8e198a08 MB |
954 | `(+ ,form 1)) |
955 | ||
956 | ;;; A more complicated ParenScript macro example is the implementation | |
ecc3218c | 957 | ;;; of the `DOLIST' form (note how `PS-GENSYM', the ParenScript of |
8e198a08 MB |
958 | ;;; `GENSYM', is used to generate new ParenScript variable names): |
959 | ||
f7c2734a | 960 | (defpsmacro dolist (i-array &rest body) |
8e198a08 MB |
961 | (let ((var (first i-array)) |
962 | (array (second i-array)) | |
ecc3218c VS |
963 | (arrvar (ps-gensym "arr")) |
964 | (idx (ps-gensym "i"))) | |
8e198a08 | 965 | `(let ((,arrvar ,array)) |
f7c2734a | 966 | (do ((,idx 0 (incf ,idx))) |
8e198a08 MB |
967 | ((>= ,idx (slot-value ,arrvar 'length))) |
968 | (let ((,var (aref ,arrvar ,idx))) | |
969 | ,@body))))) | |
970 | ||
ecc3218c VS |
971 | ;;; Macros can be defined in ParenScript code itself (as opposed to |
972 | ;;; from Lisp) by using the ParenScript `MACROLET' and `DEFMACRO' | |
973 | ;;; forms. | |
1d9f472a VS |
974 | |
975 | ;;; ParenScript also supports the use of macros defined in the | |
ecc3218c VS |
976 | ;;; underlying Lisp environment. Existing Lisp macros can be imported |
977 | ;;; into the ParenScript macro environment by | |
978 | ;;; `IMPORT-MACROS-FROM-LISP'. This functionality enables code sharing | |
979 | ;;; between ParenScript and Lisp, and is useful in debugging since the | |
980 | ;;; full power of Lisp macroexpanders, editors and other supporting | |
981 | ;;; facilities can be used. However, it is important to note that the | |
982 | ;;; macroexpansion of Lisp macros and ParenScript macros takes place | |
983 | ;;; in their own respective environments, and many Lisp macros | |
984 | ;;; (especially those provided by the Lisp implementation) expand into | |
985 | ;;; code that is not usable by ParenScript. To make it easy for users | |
986 | ;;; to take advantage of these features, two additional macro | |
987 | ;;; definition facilities are provided by ParenScript: `DEFMACRO/PS' | |
988 | ;;; and `DEFMACRO+PS'. `DEFMACRO/PS' defines a Lisp macro and then | |
989 | ;;; imports it into the ParenScript macro environment, while | |
990 | ;;; `DEFMACRO+PS' defines two macros with the same name and expansion, | |
991 | ;;; one in ParenScript and one in Lisp. `DEFMACRO+PS' is used when the | |
992 | ;;; full 'macroexpand' of the Lisp macro yields code that cannot be | |
993 | ;;; used by ParenScript. | |
8e198a08 MB |
994 | |
995 | ;;; ParenScript also supports symbol macros, which can be introduced | |
ecc3218c VS |
996 | ;;; using the ParenScript form `SYMBOL-MACROLET'.For example, the |
997 | ;;; ParenScript `WITH-SLOTS' is implemented using symbol macros. | |
8e198a08 MB |
998 | |
999 | (defjsmacro with-slots (slots object &rest body) | |
1000 | `(symbol-macrolet ,(mapcar #'(lambda (slot) | |
1001 | `(,slot '(slot-value ,object ',slot))) | |
1002 | slots) | |
1003 | ,@body)) | |
1004 | ||
1005 | ;;;# The ParenScript Compiler | |
1006 | ;;;t \index{compiler} | |
1007 | ;;;t \index{ParenScript compiler} | |
ecc3218c VS |
1008 | ;;;t \index{COMPILE-SCRIPT} |
1009 | ;;;t \index{PS} | |
1010 | ;;;t \index{PS*} | |
1011 | ;;;t \index{PS-INLINE} | |
1012 | ;;;t \index{LISP} | |
8e198a08 MB |
1013 | ;;;t \index{nested compilation} |
1014 | ||
ecc3218c VS |
1015 | ; (COMPILE-SCRIPT script-form &key (output-stream nil)) |
1016 | ; (PS &body body) | |
1017 | ; (PS* &body body) | |
1018 | ; (PS-INLINE &body body) | |
1019 | ; (LISP &body lisp-forms) | |
8e198a08 | 1020 | ; |
ecc3218c VS |
1021 | ; body ::= ParenScript statements comprising an implicit `PROGN' |
1022 | ||
1023 | ;;; For static ParenScript code, the macros `PS' and `PS-INLINE', | |
1024 | ;;; avoid the need to quote the ParenScript expression. `PS*' and | |
1025 | ;;; `COMPILE-SCRIPT' evaluate their arguments. All these forms except | |
1026 | ;;; for `COMPILE-SCRIPT' treat the given forms as an implicit | |
1027 | ;;; `PROGN'. `PS' and `PS*' return a string of the compiled body, | |
1028 | ;;; while `COMPILE-SCRIPT' takes an optional output-stream parameter | |
1029 | ;;; that can be used to specify a stream to which the generated | |
1030 | ;;; JavaScript will be written. `PS-INLINE' generates a string that | |
1031 | ;;; can be used in HTML node attributes. | |
1032 | ||
1033 | ;;; ParenScript can also call out to arbitrary Lisp code at | |
1034 | ;;; compile-time using the special form `LISP'. This is typically used | |
1035 | ;;; to insert the values of Lisp special variables into ParenScript | |
1036 | ;;; code at compile-time, and can also be used to make nested calls to | |
1037 | ;;; the ParenScript compiler, which comes in useful when you want to | |
1038 | ;;; use the result of `PS-INLINE' in `PS-HTML' forms, for | |
1039 | ;;; example. Alternatively the same thing can be accomplished by | |
1040 | ;;; constructing ParenScript programs as lists and passing them to | |
1041 | ;;; `PS*' or `COMPILE-SCRIPT'. |