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