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