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