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)) | |
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 | ||
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 | ||
377 | ;;; Note that the resulting expression is correctly parenthized, | |
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 | ||
386 | ;;; The pre/post increment and decrement operators are also | |
387 | ;;; available. `INCF' and `DECF' are the pre-incrementing and | |
388 | ;;; pre-decrementing operators, and `++' and `--' are the | |
389 | ;;; post-decrementing version of the operators. These operators can | |
390 | ;;; take only one argument. | |
391 | ||
392 | (++ i) => i++ | |
393 | ||
394 | (-- i) => i-- | |
395 | ||
396 | (incf i) => ++i | |
397 | ||
398 | (decf i) => --i | |
399 | ||
400 | ;;; The `1+' and `1-' operators are shortforms for adding and | |
401 | ;;; substracting 1. | |
402 | ||
403 | (1- i) => i - 1 | |
404 | ||
405 | (1+ i) => i + 1 | |
406 | ||
407 | ;;; The `not' operator actually optimizes the code a bit. If `not' is | |
408 | ;;; used on another boolean-returning operator, the operator is | |
409 | ;;; reversed. | |
410 | ||
411 | (not (< i 2)) => i >= 2 | |
412 | ||
413 | (not (eql i 2)) => i != 2 | |
414 | ||
415 | ;;;# Body forms | |
416 | ;;;t \index{body form} | |
417 | ;;;t \index{PROGN} | |
418 | ;;;t \index{body statement} | |
419 | ||
420 | ; (PROGN {statement}*) in statement context | |
421 | ; (PROGN {expression}*) in expression context | |
422 | ; | |
423 | ; statement ::= a ParenScript statement | |
424 | ; expression ::= a ParenScript expression | |
425 | ||
426 | ;;; The `PROGN' special form defines a sequence of statements when | |
427 | ;;; used in a statement context, or sequence of expression when used | |
428 | ;;; in an expression context. The `PROGN' special form is added | |
429 | ;;; implicitly around the branches of conditional executions forms, | |
430 | ;;; function declarations and iteration constructs. | |
431 | ||
432 | ;;; For example, in a statement context: | |
433 | ||
434 | (progn (blorg i) (blafoo i)) | |
435 | => blorg(i); | |
436 | blafoo(i); | |
437 | ||
438 | ;;; In an expression context: | |
439 | ||
440 | (+ i (progn (blorg i) (blafoo i))) | |
441 | => i + (blorg(i), blafoo(i)) | |
442 | ||
443 | ;;; A `PROGN' form doesn't lead to additional indentation or | |
444 | ;;; additional braces around it's body. | |
445 | ||
446 | ;;;# Function Definition | |
447 | ;;;t \index{function} | |
448 | ;;;t \index{method} | |
449 | ;;;t \index{function definition} | |
450 | ;;;t \index{DEFUN} | |
451 | ;;;t \index{LAMBDA} | |
452 | ;;;t \index{closure} | |
453 | ;;;t \index{anonymous function} | |
454 | ||
455 | ; (DEFUN name ({argument}*) body) | |
456 | ; (LAMBDA ({argument}*) body) | |
457 | ; | |
458 | ; name ::= a Lisp Symbol | |
459 | ; argument ::= a Lisp symbol | |
460 | ; body ::= a list of ParenScript statements | |
461 | ||
462 | ;;; As in Lisp, functions are defined using the `DEFUN' form, which | |
463 | ;;; takes a name, a list of arguments, and a function body. An | |
464 | ;;; implicit `PROGN' is added around the body statements. | |
465 | ||
466 | (defun a-function (a b) | |
467 | (return (+ a b))) | |
468 | => function aFunction(a, b) { | |
469 | return a + b; | |
470 | } | |
471 | ||
472 | ;;; Anonymous functions can be created using the `LAMBDA' form, which | |
473 | ;;; is the same as `DEFUN', but without function name. In fact, | |
474 | ;;; `LAMBDA' creates a `DEFUN' with an empty function name. | |
475 | ||
476 | (lambda (a b) (return (+ a b))) | |
477 | => function (a, b) { | |
478 | return a + b; | |
479 | } | |
480 | ||
481 | ;;;# Assignment | |
482 | ;;;t \index{assignment} | |
483 | ;;;t \index{SETF} | |
484 | ;;;t \index{assignment operator} | |
485 | ||
486 | ; (SETF {lhs rhs}*) | |
487 | ; | |
488 | ; lhs ::= a ParenScript left hand side expression | |
489 | ; rhs ::= a ParenScript expression | |
490 | ||
491 | ;;; Assignment is done using the `SETF' form, which is transformed | |
492 | ;;; into a series of assignments using the JavaScript `=' operator. | |
493 | ||
494 | (setf a 1) => a = 1 | |
495 | ||
496 | (setf a 2 b 3 c 4 x (+ a b c)) | |
497 | => a = 2; | |
498 | b = 3; | |
499 | c = 4; | |
500 | x = a + b + c; | |
501 | ||
502 | ;;; The `SETF' form can transform assignments of a variable with an | |
503 | ;;; operator expression using this variable into a more "efficient" | |
504 | ;;; assignment operator form. For example: | |
505 | ||
506 | (setf a (1+ a)) => a++ | |
507 | ||
508 | (setf a (* 2 3 4 a 4 a)) => a *= 2 * 3 * 4 * 4 * a | |
509 | ||
510 | (setf a (- 1 a)) => a = 1 - a | |
511 | ||
512 | ;;;# Single argument statements | |
513 | ;;;t \index{single-argument statement} | |
514 | ;;;t \index{RETURN} | |
515 | ;;;t \index{THROW} | |
516 | ;;;t \index{THROW} | |
517 | ;;;t \index{function} | |
518 | ||
519 | ; (RETURN {value}?) | |
520 | ; (THROW {value}?) | |
521 | ; | |
522 | ; value ::= a ParenScript expression | |
523 | ||
524 | ;;; The single argument statements `return' and `throw' are generated | |
525 | ;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a | |
526 | ;;; `TRY' form. `RETURN' is used to return a value from a function | |
527 | ;;; call. | |
528 | ||
529 | (return 1) => return 1 | |
530 | ||
7a7d6c73 | 531 | (throw "foobar") => throw 'foobar' |
8e198a08 MB |
532 | |
533 | ;;;# Single argument expression | |
534 | ;;;t \index{single-argument expression} | |
535 | ;;;t \index{object creation} | |
536 | ;;;t \index{object deletion} | |
537 | ;;;t \index{DELETE} | |
538 | ;;;t \index{VOID} | |
539 | ;;;t \index{TYPEOF} | |
540 | ;;;t \index{INSTANCEOF} | |
541 | ;;;t \index{NEW} | |
542 | ;;;t \index{new} | |
543 | ||
544 | ; (DELETE {value}) | |
545 | ; (VOID {value}) | |
546 | ; (TYPEOF {value}) | |
547 | ; (INSTANCEOF {value}) | |
548 | ; (NEW {value}) | |
549 | ; | |
550 | ; value ::= a ParenScript expression | |
551 | ||
552 | ;;; The single argument expressions `delete', `void', `typeof', | |
553 | ;;; `instanceof' and `new' are generated by the forms `DELETE', | |
554 | ;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a | |
555 | ;;; ParenScript expression. | |
556 | ||
557 | (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4) | |
558 | ||
559 | (if (= (typeof blorg) *string) | |
560 | (alert (+ "blorg is a string: " blorg)) | |
561 | (alert "blorg is not a string")) | |
562 | => if (typeof blorg == String) { | |
7a7d6c73 | 563 | alert('blorg is a string: ' + blorg); |
8e198a08 | 564 | } else { |
7a7d6c73 | 565 | alert('blorg is not a string'); |
8e198a08 MB |
566 | } |
567 | ||
568 | ;;;# Conditional Statements | |
569 | ;;;t \index{conditional statements} | |
570 | ;;;t \index{IF} | |
571 | ;;;t \index{WHEN} | |
572 | ;;;t \index{UNLESS} | |
573 | ;;;t \index{conditionals} | |
574 | ||
575 | ; (IF conditional then {else}) | |
576 | ; (WHEN condition then) | |
577 | ; (UNLESS condition then) | |
578 | ; | |
579 | ; condition ::= a ParenScript expression | |
580 | ; then ::= a ParenScript statement in statement context, a | |
581 | ; ParenScript expression in expression context | |
582 | ; else ::= a ParenScript statement in statement context, a | |
583 | ; ParenScript expression in expression context | |
584 | ||
585 | ;;; The `IF' form compiles to the `if' javascript construct. An | |
586 | ;;; explicit `PROGN' around the then branch and the else branch is | |
587 | ;;; needed if they consist of more than one statement. When the `IF' | |
588 | ;;; form is used in an expression context, a JavaScript `?', `:' | |
589 | ;;; operator form is generated. | |
590 | ||
591 | (if (blorg.is-correct) | |
592 | (progn (carry-on) (return i)) | |
593 | (alert "blorg is not correct!")) | |
594 | => if (blorg.isCorrect()) { | |
595 | carryOn(); | |
596 | return i; | |
597 | } else { | |
7a7d6c73 | 598 | alert('blorg is not correct!'); |
8e198a08 MB |
599 | } |
600 | ||
601 | (+ i (if (blorg.add-one) 1 2)) | |
602 | => i + (blorg.addOne() ? 1 : 2) | |
603 | ||
604 | ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the | |
605 | ;;; `IF' form. | |
606 | ||
607 | (when (blorg.is-correct) | |
608 | (carry-on) | |
609 | (return i)) | |
610 | => if (blorg.isCorrect()) { | |
611 | carryOn(); | |
612 | return i; | |
613 | } | |
614 | ||
615 | (unless (blorg.is-correct) | |
616 | (alert "blorg is not correct!")) | |
617 | => if (!blorg.isCorrect()) { | |
7a7d6c73 | 618 | alert('blorg is not correct!'); |
8e198a08 MB |
619 | } |
620 | ||
621 | ;;;# Variable declaration | |
622 | ;;;t \index{variable} | |
623 | ;;;t \index{variable declaration} | |
624 | ;;;t \index{binding} | |
625 | ;;;t \index{scoping} | |
626 | ;;;t \index{DEFVAR} | |
627 | ;;;t \index{LET} | |
628 | ||
629 | ; (DEFVAR var {value}?) | |
630 | ; (LET ({var | (var value)) body) | |
631 | ; | |
632 | ; var ::= a Lisp symbol | |
633 | ; value ::= a ParenScript expression | |
634 | ; body ::= a list of ParenScript statements | |
635 | ||
636 | ;;; Variables (either local or global) can be declared using the | |
637 | ;;; `DEFVAR' form, which is similar to its equivalent form in | |
638 | ;;; Lisp. The `DEFVAR' is converted to "var ... = ..." form in | |
639 | ;;; JavaScript. | |
640 | ||
7a7d6c73 | 641 | (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]; |
8e198a08 MB |
642 | |
643 | (if (= i 1) | |
644 | (progn (defvar blorg "hallo") | |
645 | (alert blorg)) | |
646 | (progn (defvar blorg "blitzel") | |
647 | (alert blorg))) | |
648 | => if (i == 1) { | |
7a7d6c73 | 649 | var blorg = 'hallo'; |
8e198a08 MB |
650 | alert(blorg); |
651 | } else { | |
7a7d6c73 | 652 | var blorg = 'blitzel'; |
8e198a08 MB |
653 | alert(blorg); |
654 | } | |
655 | ||
656 | ;;; A more lispy way to declare local variable is to use the `LET' | |
657 | ;;; form, which is similar to its Lisp form. | |
658 | ||
659 | (if (= i 1) | |
660 | (let ((blorg "hallo")) | |
661 | (alert blorg)) | |
662 | (let ((blorg "blitzel")) | |
663 | (alert blorg))) | |
664 | => if (i == 1) { | |
7a7d6c73 | 665 | var blorg = 'hallo'; |
8e198a08 MB |
666 | alert(blorg); |
667 | } else { | |
7a7d6c73 | 668 | var blorg = 'blitzel'; |
8e198a08 MB |
669 | alert(blorg); |
670 | } | |
671 | ||
672 | ;;; However, beware that scoping in Lisp and JavaScript are quite | |
673 | ;;; different. For example, don't rely on closures capturing local | |
674 | ;;; variables in the way you'd think they would. | |
675 | ||
676 | ;;;# Iteration constructs | |
677 | ;;;t \index{iteration} | |
678 | ;;;t \index{iteration construct} | |
679 | ;;;t \index{loop} | |
680 | ;;;t \index{array traversal} | |
681 | ;;;t \index{property} | |
682 | ;;;t \index{object property} | |
683 | ;;;t \index{DO} | |
684 | ;;;t \index{DOTIMES} | |
685 | ;;;t \index{DOLIST} | |
686 | ;;;t \index{DOEACH} | |
687 | ;;;t \index{WHILE} | |
688 | ||
689 | ; (DO ({var | (var {init}? {step}?)}*) (end-test) body) | |
690 | ; (DOTIMES (var numeric-form) body) | |
691 | ; (DOLIST (var list-form) body) | |
692 | ; (DOEACH (var object) body) | |
693 | ; (WHILE end-test body) | |
694 | ; | |
695 | ; var ::= a Lisp symbol | |
696 | ; numeric-form ::= a ParenScript expression resulting in a number | |
697 | ; list-form ::= a ParenScript expression resulting in an array | |
698 | ; object ::= a ParenScript expression resulting in an object | |
699 | ; init ::= a ParenScript expression | |
700 | ; step ::= a ParenScript expression | |
701 | ; end-test ::= a ParenScript expression | |
702 | ; body ::= a list of ParenScript statements | |
703 | ||
704 | ;;; The `DO' form, which is similar to its Lisp form, is transformed | |
705 | ;;; into a JavaScript `for' statement. Note that the ParenScript `DO' | |
706 | ;;; form does not have a return value, that is because `for' is a | |
707 | ;;; statement and not an expression in JavaScript. | |
708 | ||
709 | (do ((i 0 (1+ i)) | |
710 | (l (aref blorg i) (aref blorg i))) | |
711 | ((or (= i blorg.length) | |
712 | (eql l "Fumitastic"))) | |
713 | (document.write (+ "L is " l))) | |
94a05cdf | 714 | => for (var i = 0, l = blorg[i]; |
7a7d6c73 | 715 | !(i == blorg.length || l == 'Fumitastic'); |
8e198a08 | 716 | i = i + 1, l = blorg[i]) { |
7a7d6c73 | 717 | document.write('L is ' + l); |
8e198a08 MB |
718 | } |
719 | ||
720 | ;;; The `DOTIMES' form, which lets a variable iterate from 0 upto an | |
721 | ;;; end value, is a shortcut for `DO'. | |
722 | ||
723 | (dotimes (i blorg.length) | |
724 | (document.write (+ "L is " (aref blorg i)))) | |
7a7d6c73 HH |
725 | => for (var i = 0; i < blorg.length; i = i + 1) { |
726 | document.write('L is ' + blorg[i]); | |
8e198a08 MB |
727 | } |
728 | ||
729 | ;;; The `DOLIST' form is a shortcut for iterating over an array. Note | |
730 | ;;; that this form creates temporary variables using a function called | |
731 | ;;; `JS-GENSYM', which is similar to its Lisp counterpart `GENSYM'. | |
732 | ||
733 | (dolist (l blorg) | |
734 | (document.write (+ "L is " l))) | |
7a7d6c73 HH |
735 | => { |
736 | var tmpArr1 = blorg; | |
737 | for (var tmpI2 = 0; tmpI2 < tmpArr1.length; | |
738 | tmpI2 = tmpI2 + 1) { | |
739 | var l = tmpArr1[tmpI2]; | |
740 | document.write('L is ' + l); | |
379978f8 | 741 | }; |
8e198a08 MB |
742 | } |
743 | ||
7a7d6c73 | 744 | |
8e198a08 MB |
745 | ;;; The `DOEACH' form is converted to a `for (var .. in ..)' form in |
746 | ;;; JavaScript. It is used to iterate over the enumerable properties | |
747 | ;;; of an object. | |
748 | ||
749 | (doeach (i object) | |
750 | (document.write (+ i " is " (aref object i)))) | |
751 | => for (var i in object) { | |
7a7d6c73 | 752 | document.write(i + ' is ' + object[i]); |
8e198a08 MB |
753 | } |
754 | ||
755 | ;;; The `WHILE' form is transformed to the JavaScript form `while', | |
756 | ;;; and loops until a termination test evaluates to false. | |
757 | ||
758 | (while (film.is-not-finished) | |
759 | (this.eat (new *popcorn))) | |
760 | => while (film.isNotFinished()) { | |
761 | this.eat(new Popcorn); | |
762 | } | |
551080b7 | 763 | |
8e198a08 MB |
764 | ;;;# The `CASE' statement |
765 | ;;;t \index{CASE} | |
3c393e09 | 766 | ;;;t \index{SWITCH} |
8e198a08 MB |
767 | ;;;t \index{switch} |
768 | ||
769 | ; (CASE case-value clause*) | |
770 | ; | |
3c393e09 | 771 | ; clause ::= (value body) | ((value*) body) | t-clause |
8e198a08 MB |
772 | ; case-value ::= a ParenScript expression |
773 | ; value ::= a ParenScript expression | |
3c393e09 | 774 | ; t-clause ::= {t | otherwise | default} body |
8e198a08 MB |
775 | ; body ::= a list of ParenScript statements |
776 | ||
777 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in | |
778 | ;;; JavaScript. Note that `CASE' is not an expression in | |
3c393e09 | 779 | ;;; ParenScript. |
8e198a08 MB |
780 | |
781 | (case (aref blorg i) | |
3c393e09 | 782 | ((1 "one") (alert "one")) |
8e198a08 | 783 | (2 (alert "two")) |
3c393e09 | 784 | (t (alert "default clause"))) |
8e198a08 | 785 | => switch (blorg[i]) { |
3c393e09 HH |
786 | case 1: ; |
787 | case 'one': | |
788 | alert('one'); | |
789 | break; | |
790 | case 2: | |
791 | alert('two'); | |
792 | break; | |
7a7d6c73 | 793 | default: alert('default clause'); |
8e198a08 MB |
794 | } |
795 | ||
3c393e09 HH |
796 | ; (SWITCH case-value clause*) |
797 | ; clause ::= (value body) | (default body) | |
798 | ||
799 | ;;; The `SWITCH' form is the equivalent to a javascript switch statement. | |
800 | ;;; No break statements are inserted, and the default case is named `DEFAULT'. | |
801 | ;;; The `CASE' form should be prefered in most cases. | |
802 | ||
803 | (switch (aref blorg i) | |
804 | (1 (alert "If I get here")) | |
805 | (2 (alert "I also get here")) | |
806 | (default (alert "I always get here"))) | |
807 | => switch (blorg[i]) { | |
808 | case 1: alert('If I get here'); | |
809 | case 2: alert('I also get here'); | |
810 | default: alert('I always get here'); | |
811 | } | |
812 | ||
813 | ||
8e198a08 MB |
814 | ;;;# The `WITH' statement |
815 | ;;;t \index{WITH} | |
816 | ;;;t \index{dynamic scope} | |
817 | ;;;t \index{binding} | |
818 | ;;;t \index{scoping} | |
819 | ;;;t \index{closure} | |
820 | ||
5d9cdcad | 821 | ; (WITH object body) |
8e198a08 MB |
822 | ; |
823 | ; object ::= a ParenScript expression evaluating to an object | |
824 | ; body ::= a list of ParenScript statements | |
825 | ||
826 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and | |
827 | ;;; adds the object `object' as an intermediary scope objects when | |
828 | ;;; executing the body. | |
829 | ||
5d9cdcad | 830 | (with (create :foo "foo" :i "i") |
8e198a08 | 831 | (alert (+ "i is now intermediary scoped: " i))) |
94a05cdf | 832 | => with ({ foo : 'foo', |
7a7d6c73 HH |
833 | i : 'i' }) { |
834 | alert('i is now intermediary scoped: ' + i); | |
8e198a08 MB |
835 | } |
836 | ||
837 | ;;;# The `TRY' statement | |
838 | ;;;t \index{TRY} | |
839 | ;;;t \index{CATCH} | |
840 | ;;;t \index{FINALLY} | |
841 | ;;;t \index{exception} | |
842 | ;;;t \index{error handling} | |
843 | ||
844 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) | |
845 | ; | |
846 | ; body ::= a list of ParenScript statements | |
847 | ; var ::= a Lisp symbol | |
848 | ||
849 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and | |
850 | ;;; can be used to catch expressions thrown by the `THROW' | |
851 | ;;; form. The body of the catch clause is invoked when an exception | |
852 | ;;; is catched, and the body of the finally is always invoked when | |
853 | ;;; leaving the body of the `TRY' form. | |
854 | ||
94a05cdf | 855 | (try (throw "i") |
8e198a08 MB |
856 | (:catch (error) |
857 | (alert (+ "an error happened: " error))) | |
858 | (:finally | |
859 | (alert "Leaving the try form"))) | |
860 | => try { | |
7a7d6c73 | 861 | throw 'i'; |
8e198a08 | 862 | } catch (error) { |
7a7d6c73 | 863 | alert('an error happened: ' + error); |
8e198a08 | 864 | } finally { |
7a7d6c73 | 865 | alert('Leaving the try form'); |
8e198a08 MB |
866 | } |
867 | ||
868 | ;;;# The HTML Generator | |
869 | ;;;t \index{HTML} | |
870 | ;;;t \index{HTML generation} | |
28967ee4 HH |
871 | ;;;t \index{CSS} |
872 | ;;;t \index{CSS generation} | |
873 | ||
8e198a08 MB |
874 | |
875 | ; (HTML html-expression) | |
876 | ||
877 | ;;; The HTML generator of ParenScript is very similar to the HTML | |
878 | ;;; generator included in AllegroServe. It accepts the same input | |
879 | ;;; forms as the AllegroServer HTML generator. However, non-HTML | |
880 | ;;; construct are compiled to JavaScript by the ParenScript | |
881 | ;;; compiler. The resulting expression is a JavaScript expression. | |
882 | ||
883 | (html ((:a :href "foobar") "blorg")) | |
7a7d6c73 | 884 | => '<a href=\"foobar\">blorg</a>' |
8e198a08 MB |
885 | |
886 | (html ((:a :href (generate-a-link)) "blorg")) | |
7a7d6c73 | 887 | => '<a href=\"' + generateALink() + '\">blorg</a>' |
8e198a08 MB |
888 | |
889 | ;;; We can recursively call the JS compiler in a HTML expression. | |
890 | ||
891 | (document.write | |
7a7d6c73 | 892 | (html ((:a :href "#" |
8e198a08 | 893 | :onclick (js-inline (transport))) "link"))) |
7a7d6c73 HH |
894 | => document.write |
895 | ('<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">link</a>') | |
8e198a08 | 896 | |
28967ee4 HH |
897 | ; (CSS-INLINE css-expression) |
898 | ||
899 | ;;; Stylesheets can also be created in ParenScript. | |
900 | ||
901 | (css-inline :color "red" | |
902 | :font-size "x-small") | |
903 | => 'color:red;font-size:x-small' | |
904 | ||
905 | (defun make-color-div(color-name) | |
906 | (return (html ((:div :style (css-inline :color color-name)) | |
907 | color-name " looks like this.")))) | |
908 | => function makeColorDiv(colorName) { | |
909 | return '<div style=\"' + ('color:' + colorName) + '\">' + colorName | |
910 | + ' looks like this.</div>'; | |
911 | } | |
912 | ||
8e198a08 MB |
913 | ;;;# Macrology |
914 | ;;;t \index{macro} | |
915 | ;;;t \index{macrology} | |
916 | ;;;t \index{DEFJSMACRO} | |
917 | ;;;t \index{MACROLET} | |
918 | ;;;t \index{SYMBOL-MACROLET} | |
919 | ;;;t \index{JS-GENSYM} | |
920 | ;;;t \index{compiler} | |
921 | ||
922 | ; (DEFJSMACRO name lambda-list macro-body) | |
923 | ; (MACROLET ({name lambda-list macro-body}*) body) | |
924 | ; (SYMBOL-MACROLET ({name macro-body}*) body) | |
925 | ; (JS-GENSYM {string}?) | |
926 | ; | |
927 | ; name ::= a Lisp symbol | |
928 | ; lambda-list ::= a lambda list | |
929 | ; macro-body ::= a Lisp body evaluating to ParenScript code | |
930 | ; body ::= a list of ParenScript statements | |
931 | ; string ::= a string | |
932 | ||
933 | ;;; ParenScript can be extended using macros, just like Lisp can be | |
934 | ;;; extended using Lisp macros. Using the special Lisp form | |
935 | ;;; `DEFJSMACRO', the ParenScript language can be | |
936 | ;;; extended. `DEFJSMACRO' adds the new macro to the toplevel macro | |
937 | ;;; environment, which is always accessible during ParenScript | |
938 | ;;; compilation. For example, the `1+' and `1-' operators are | |
939 | ;;; implemented using macros. | |
940 | ||
941 | (defjsmacro 1- (form) | |
942 | `(- ,form 1)) | |
943 | ||
944 | (defjsmacro 1+ (form) | |
945 | `(+ ,form 1)) | |
946 | ||
947 | ;;; A more complicated ParenScript macro example is the implementation | |
948 | ;;; of the `DOLIST' form (note how `JS-GENSYM', the ParenScript of | |
949 | ;;; `GENSYM', is used to generate new ParenScript variable names): | |
950 | ||
951 | (defjsmacro dolist (i-array &rest body) | |
952 | (let ((var (first i-array)) | |
953 | (array (second i-array)) | |
954 | (arrvar (js-gensym "arr")) | |
955 | (idx (js-gensym "i"))) | |
956 | `(let ((,arrvar ,array)) | |
957 | (do ((,idx 0 (++ ,idx))) | |
958 | ((>= ,idx (slot-value ,arrvar 'length))) | |
959 | (let ((,var (aref ,arrvar ,idx))) | |
960 | ,@body))))) | |
961 | ||
962 | ;;; Macros can be added dynamically to the macro environment by using | |
963 | ;;; the ParenScript `MACROLET' form (note that while `DEFJSMACRO' is a | |
964 | ;;; Lisp form, `MACROLET' and `SYMBOL-MACROLET' are ParenScript forms). | |
965 | ||
966 | ;;; ParenScript also supports symbol macros, which can be introduced | |
967 | ;;; using the ParenScript form `SYMBOL-MACROLET'. A new macro | |
968 | ;;; environment is created and added to the current macro environment | |
969 | ;;; list while compiling the body of the `SYMBOL-MACROLET' form. For | |
970 | ;;; example, the ParenScript `WITH-SLOTS' is implemented using symbol | |
971 | ;;; macros. | |
972 | ||
973 | (defjsmacro with-slots (slots object &rest body) | |
974 | `(symbol-macrolet ,(mapcar #'(lambda (slot) | |
975 | `(,slot '(slot-value ,object ',slot))) | |
976 | slots) | |
977 | ,@body)) | |
978 | ||
979 | ;;;# The ParenScript Compiler | |
980 | ;;;t \index{compiler} | |
981 | ;;;t \index{ParenScript compiler} | |
982 | ;;;t \index{JS-COMPILE} | |
983 | ;;;t \index{JS-TO-STRINGS} | |
984 | ;;;t \index{JS-TO-STATEMENT-STRINGS} | |
985 | ;;;t \index{JS-TO-STRING} | |
986 | ;;;t \index{JS-TO-LINE} | |
987 | ;;;t \index{JS} | |
988 | ;;;t \index{JS-INLINE} | |
989 | ;;;t \index{JS-FILE} | |
990 | ;;;t \index{JS-SCRIPT} | |
991 | ;;;t \index{nested compilation} | |
992 | ||
993 | ; (JS-COMPILE expr) | |
994 | ; (JS-TO-STRINGS compiled-expr position) | |
995 | ; (JS-TO-STATEMENT-STRINGS compiled-expr position) | |
996 | ; | |
997 | ; compiled-expr ::= a compiled ParenScript expression | |
998 | ; position ::= a column number | |
999 | ; | |
1000 | ; (JS-TO-STRING expression) | |
1001 | ; (JS-TO-LINE expression) | |
1002 | ; | |
1003 | ; expression ::= a Lisp list of ParenScript code | |
1004 | ; | |
1005 | ; (JS body) | |
1006 | ; (JS-INLINE body) | |
1007 | ; (JS-FILE body) | |
1008 | ; (JS-SCRIPT body) | |
1009 | ; | |
1010 | ; body ::= a list of ParenScript statements | |
1011 | ||
1012 | ;;; The ParenScript compiler can be invoked from withing Lisp and from | |
1013 | ;;; within ParenScript itself. The primary API function is | |
1014 | ;;; `JS-COMPILE', which takes a list of ParenScript, and returns an | |
94a05cdf | 1015 | ;;; internal object representing the compiled ParenScript. |
8e198a08 MB |
1016 | |
1017 | (js-compile '(foobar 1 2)) | |
1018 | => #<JS::FUNCTION-CALL {584AA5DD}> | |
1019 | ||
1020 | ;;; This internal object can be transformed to a string using the | |
1021 | ;;; methods `JS-TO-STRINGS' and `JS-TO-STATEMENT-STRINGS', which | |
1022 | ;;; interpret the ParenScript in expression and in statement context | |
1023 | ;;; respectively. They take an additional parameter indicating the | |
1024 | ;;; start-position on a line (please note that the indentation code is | |
1025 | ;;; not perfect, and this string interface will likely be | |
1026 | ;;; changed). They return a list of strings, where each string | |
1027 | ;;; represents a new line of JavaScript code. They can be joined | |
1028 | ;;; together to form a single string. | |
1029 | ||
1030 | (js-to-strings (js-compile '(foobar 1 2)) 0) | |
1031 | => ("foobar(1, 2)") | |
1032 | ||
1033 | ;;; As a shortcut, ParenScript provides the functions `JS-TO-STRING' | |
1034 | ;;; and `JS-TO-LINE', which return the JavaScript string of the | |
1035 | ;;; compiled expression passed as an argument. | |
1036 | ||
1037 | (js-to-string '(foobar 1 2)) | |
1038 | => "foobar(1, 2)" | |
1039 | ||
1040 | ;;; For static ParenScript code, the macros `JS', `JS-INLINE', | |
1041 | ;;; `JS-FILE' and `JS-SCRIPT' avoid the need to quote the ParenScript | |
1042 | ;;; expression. All these forms add an implicit `PROGN' form around | |
1043 | ;;; the body. `JS' returns a string of the compiled body, where the | |
1044 | ;;; other expression return an expression that can be embedded in a | |
1045 | ;;; HTML generation construct using the AllegroServe HTML | |
1046 | ;;; generator. `JS-SCRIPT' generates a "SCRIPT" node, `JS-INLINE' | |
1047 | ;;; generates a string to be used in node attributs, and `JS-FILE' | |
1048 | ;;; prints the compiled ParenScript code to the HTML stream. | |
1049 | ||
1050 | ;;; These macros are also available inside ParenScript itself, and | |
1051 | ;;; generate strings that can be used inside ParenScript code. Note | |
1052 | ;;; that `JS-INLINE' in ParenScript is not the same `JS-INLINE' form | |
1053 | ;;; as in Lisp, for example. The same goes for the other compilation | |
1054 | ;;; macros. | |
1055 |