Substantially modified the way Parenscript compilation and
[clinton/parenscript.git] / docs / reference.lisp
1 ;;;# Parenscript Language Reference
2
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
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
43 ;;; few simple rules. Special characters `!', `?', `#', `@', `%',
44 ;;; '/', `*' and `+' get replaced by their written-out equivalents
45 ;;; "bang", "what", "hash", "at", "percent", "slash",
46 ;;; "start" and "plus" respectively. The `$' character is untouched.
47
48 !?#@% => bangwhathashatpercent;
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
54 bla-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 ;;; 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
66 *global-array* => GLOBALARRAY;
67
68 ;;;## Reserved Keywords
69 ;;;t \index{keyword}
70 ;;;t \index{reserved keywords}
71
72 ;;; The following keywords and symbols are reserved in Parenscript,
73 ;;; and should not be used as variable names.
74
75 ! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || *=
76 /= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ @ ABSTRACT AND AREF ARRAY
77 BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS COMMA CONST CONTINUE
78 CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO* DOEACH DOLIST
79 DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL FINALLY
80 FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF
81 INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA LET LET* LISP LIST LONG
82 MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED
83 PUBLIC RANDOM REGEX RETURN SETF SHORT SLOT-VALUE STATIC SUPER SWITCH
84 SYMBOL-MACROLET SYNCHRONIZED T THIS THROW THROWS TRANSIENT TRY TYPEOF
85 UNDEFINED UNLESS VAR VOID VOLATILE WHEN WHILE WITH WITH-SLOTS
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 ;;;
97 ;;; Parenscript supports the standard JavaScript literal
98 ;;; values. Numbers are compiled into JavaScript numbers.
99
100 1 => 1;
101
102 123.123 => 123.123;
103
104 ;;; Note that the base is not conserved between Lisp and JavaScript.
105
106 #x10 => 16;
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
116 "foobar" => 'foobar';
117
118 "bratzel bub" => 'bratzel bub';
119
120 ;;; Special characters such as newline and backspace are converted
121 ;;; into their corresponding JavaScript escape sequences.
122
123 " " => '\\t';
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 ;
136 ; values ::= a Parenscript expression
137 ; array ::= a Parenscript expression
138 ; index ::= a Parenscript expression
139
140 ;;; Array literals can be created using the `ARRAY' form.
141
142 (array) => [ ];
143
144 (array 1 2 3) => [ 1, 2, 3 ];
145
146 (array (array 2 3)
147 (array "foobar" "bratzel bub"))
148 => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ];
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
154 (make-array) => new Array();
155
156 (make-array 1 2 3) => new Array(1, 2, 3);
157
158 (make-array
159 (make-array 2 3)
160 (make-array "foobar" "bratzel bub"))
161 => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub'));
162
163 ;;; Indexing arrays in Parenscript is done using the form `AREF'. Note
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}
172 ;;;t \index{@}
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 ;
183 ; name ::= a Parenscript symbol or a Lisp keyword
184 ; value ::= a Parenscript expression
185 ; object ::= a Parenscript object expression
186 ; slot-name ::= a quoted Lisp symbol
187 ; body ::= a list of Parenscript statements
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)
195 => { foo : 'bar', blorg : 1 };
196
197 (create :foo "hihi"
198 :blorg (array 1 2 3)
199 :another-object (create :schtrunz 1))
200 => { foo : 'hihi',
201 blorg : [ 1, 2, 3 ],
202 anotherObject : { schtrunz : 1 } };
203
204 ;;; Object properties can be accessed using the `SLOT-VALUE' form,
205 ;;; which takes an object and a slot-name.
206
207 (slot-value an-object 'foo) => anObject.foo;
208
209 ;;; The convenience macro `@' is provided to make multiple levels of
210 ;;; indirection easy to express
211
212 (@ an-object foo bar) => anObject.foo.bar;
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))
220 => this.a + this.b + this.c;
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
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
238 (regex "foobar") => /foobar/;
239
240 (regex "/foobar/i") => /foobar/i;
241
242 ;;; Here CL-INTERPOL proves really useful.
243
244 (regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i;
245
246 ;;;## Literal symbols
247 ;;;t \index{T}
248 ;;;t \index{F}
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
257 ; T, F, FALSE, NIL, UNDEFINED, THIS
258
259 ;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their
260 ;;; JavaScript boolean equivalents `true' and `false'.
261
262 T => true;
263
264 FALSE => false;
265
266 F => false;
267
268 ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword
269 ;;; `null'.
270
271 NIL => null;
272
273 ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword
274 ;;; `undefined'.
275
276 UNDEFINED => undefined;
277
278 ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword
279 ;;; `this'.
280
281 THIS => this;
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
292 ;;; allows the Parenscript programmer to be flexible, as flexible as
293 ;;; JavaScript itself.
294
295 variable => variable;
296
297 a-variable => aVariable;
298
299 *math => Math;
300
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}*)
308
309 ;
310 ; function ::= a Parenscript expression or a Lisp symbol
311 ; object ::= a Parenscript expression
312 ; argument ::= a Parenscript expression
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
320 (blorg 1 2) => blorg(1, 2);
321
322 (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4))
323 => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]);
324
325 ((slot-value this 'blorg) 1 2) => this.blorg(1, 2);
326
327 ((aref foo i) 1 2) => foo[i](1, 2);
328
329 ((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true);
330
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, !
346 ; argument ::= a Parenscript expression
347
348 ;;; Operator forms are similar to function call forms, but have an
349 ;;; operator as function name.
350 ;;;
351 ;;; Please note that `=' is converted to `==' in JavaScript. The `='
352 ;;; Parenscript operator is not the assignment operator. Unlike
353 ;;; JavaScript, Parenscript supports multiple arguments to the
354 ;;; operators.
355
356 (* 1 2) => 1 * 2;
357
358 (= 1 2) => 1 == 2;
359
360 (eql 1 2) => 1 == 2;
361
362 ;;; Note that the resulting expression is correctly parenthesized,
363 ;;; according to the JavaScript operator precedence that can be found
364 ;;; in table form at:
365
366 ;;; http://www.codehouse.com/javascript/precedence/
367
368 (* 1 (+ 2 3 4) 4 (/ 6 7))
369 => 1 * (2 + 3 + 4) * 4 * (6 / 7);
370
371 ;;; The pre increment and decrement operators are also
372 ;;; available. `INCF' and `DECF' are the pre-incrementing and
373 ;;; pre-decrementing operators. These operators can
374 ;;; take only one argument.
375
376 (incf i) => ++i;
377
378 (decf i) => --i;
379
380 ;;; The `1+' and `1-' operators are shortforms for adding and
381 ;;; substracting 1.
382
383 (1- i) => i - 1;
384
385 (1+ i) => i + 1;
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
391 (not (< i 2)) => i >= 2;
392
393 (not (eql i 2)) => i != 2;
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 ;
403 ; statement ::= a Parenscript statement
404 ; expression ::= a Parenscript expression
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))
415 => blorg(i);
416 blafoo(i);
417
418 ;;; In an expression context:
419
420 (+ i (progn (blorg i) (blafoo i)))
421 => i + (blorg(i), blafoo(i));
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
440 ; body ::= a list of Parenscript statements
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)))
448 => function aFunction(a, b) {
449 return a + b;
450 };
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)))
457 => function (a, b) {
458 return a + b;
459 };
460
461 ;;;# Assignment
462 ;;;t \index{assignment}
463 ;;;t \index{SETF}
464 ;;;t \index{PSETF}
465 ;;;t \index{SETQ}
466 ;;;t \index{PSETQ}
467 ;;;t \index{DEFSETF}
468 ;;;t \index{assignment operator}
469
470 ; (SETF {lhs rhs}*)
471 ; (PSETF {lhs rhs}*)
472 ;
473 ; lhs ::= a Parenscript left hand side expression
474 ; rhs ::= a Parenscript expression
475
476 ; (SETQ {lhs rhs}*)
477 ; (PSETQ {lhs rhs}*)
478 ;
479 ; lhs ::= a Parenscript symbol
480 ; rhs ::= a Parenscript expression
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.
485
486 (setf a 1) => a = 1;
487
488 (setf a 2 b 3 c 4 x (+ a b c))
489 => a = 2;
490 b = 3;
491 c = 4;
492 x = a + b + c;
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
498 (setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a;
499
500 (setf a (- 1 a)) => a = 1 - a;
501
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
506 (let ((a 1) (b 2))
507 (psetf a b b a))
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;
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)
524 ;; => ERROR: The value (AREF A 0) is not of type SYMBOL.
525
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))
536 => function __setf_color(newColor, el) {
537 el.style.color = newColor;
538 };
539
540 (setf (color some-div) (+ 23 "em"))
541 => var _js2_3 = someDiv;
542 var _js1_4 = 23 + 'em';
543 __setf_color(_js1_4, _js2_3);
544
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)
552 `(setf (slot-value (slot-value ,el 'style) 'left) ,offset))
553 => null;
554
555 (setf (left some-div) (+ 123 "px"))
556 => var _js2_3 = someDiv;
557 var _js1_4 = 123 + 'px';
558 _js2_3.style.left = _js1_4;
559
560 (macrolet ((left (el)
561 `(slot-value ,el 'offset-left)))
562 (left some-div))
563 => someDiv.offsetLeft;
564
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 ;
575 ; value ::= a Parenscript expression
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
582 (return 1) => return 1;
583
584 (throw "foobar") => throw 'foobar';
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 ;
603 ; value ::= a Parenscript expression
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
608 ;;; Parenscript expression.
609
610 (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4);
611
612 (if (= (typeof blorg) *string)
613 (alert (+ "blorg is a string: " blorg))
614 (alert "blorg is not a string"))
615 => if (typeof blorg == String) {
616 alert('blorg is a string: ' + blorg);
617 } else {
618 alert('blorg is not a string');
619 };
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 ;
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
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
644 (if ((@ blorg is-correct))
645 (progn (carry-on) (return i))
646 (alert "blorg is not correct!"))
647 => if (blorg.isCorrect()) {
648 carryOn();
649 return i;
650 } else {
651 alert('blorg is not correct!');
652 };
653
654 (+ i (if ((@ blorg add-one)) 1 2))
655 => i + (blorg.addOne() ? 1 : 2);
656
657 ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the
658 ;;; `IF' form.
659
660 (when ((@ blorg is-correct))
661 (carry-on)
662 (return i))
663 => if (blorg.isCorrect()) {
664 carryOn();
665 return i;
666 };
667
668 (unless ((@ blorg is-correct))
669 (alert "blorg is not correct!"))
670 => if (!blorg.isCorrect()) {
671 alert('blorg is not correct!');
672 };
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}
680 ;;;t \index{VAR}
681 ;;;t \index{LET}
682 ;;;t \index{LET*}
683
684 ; (DEFVAR var {value}?)
685 ; (VAR var {value}?)
686 ; (LET ({var | (var value)}*) body)
687 ; (LET* ({var | (var value)}*) body)
688 ;
689 ; var ::= a Lisp symbol
690 ; value ::= a Parenscript expression
691 ; body ::= a list of Parenscript statements
692
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.
697
698 (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ];
699
700 ;;; One feature present in Parenscript that is not part of Common Lisp
701 ;;; are lexically-scoped global variables, which are declared using
702 ;;; the `VAR' special form.
703
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.
709
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.
713
714
715 ;;; examples:
716
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 };
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}
747 ;;;t \index{FOR-IN}
748 ;;;t \index{WHILE}
749
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)
754 ; (FOR-IN (var object) body)
755 ; (WHILE end-test body)
756 ;
757 ; var ::= a Lisp symbol
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
766
767 ;;; All interation special forms are transformed into JavaScript `for'
768 ;;; statements and, if needed, lambda expressions.
769
770 ;;; `DO', `DO*', and `DOTIMES' carry the same semantics as their
771 ;;; Common Lisp equivalents.
772
773 ;;; `DO*' (note the variety of possible init-forms:
774
775 (do* ((a) b (c (array "a" "b" "c" "d" "e"))
776 (d 0 (1+ d))
777 (e (aref c d) (aref c d)))
778 ((or (= d (@ c length)) (eql e "x")))
779 (setf a d b e)
780 ((@ document write) (+ "a: " a " b: " b "<br/>")))
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 };
786
787 ;;; `DO'
788
789 (do ((i 0 (1+ i))
790 (s 0 (+ s i (1+ i))))
791 ((> i 10))
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;
801 };
802
803 ;;; compare to `DO*':
804
805 (do* ((i 0 (1+ i))
806 (s 0 (+ s i (1- i))))
807 ((> i 10))
808 ((@ document write) (+ "i: " i " s: " s "<br/>")))
809 => for (var i = 0, s = 0; i <= 10; i += 1, s += i + (i - 1)) {
810 document.write('i: ' + i + ' s: ' + s + '<br/>');
811 };
812
813 ;;; `DOTIMES':
814
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/>');
821 };
822
823 ;;; `DOTIMES' with return value:
824
825 (let ((res 0))
826 (alert (+ "Summation to 10 is "
827 (dotimes (i 10 res)
828 (incf res (1+ i))))))
829 => var res1 = 0;
830 alert('Summation to 10 is ' + (function () {
831 for (var i = 0; i < 10; i += 1) {
832 res1 += i + 1;
833 };
834 return res1;
835 })());
836
837 ;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS
838 ;;; arrays/vectors.
839
840 (let ((l (list 1 2 4 8 16 32)))
841 (dolist (c l)
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];
846 document.write('c: ' + c + '<br/>');
847 };
848
849 (let ((l '(1 2 4 8 16 32))
850 (s 0))
851 (alert (+ "Sum of " l " is: "
852 (dolist (c l s)
853 (incf s c)))))
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;
860 };
861 return s2;
862 })());
863
864 ;;; `FOR-IN' is translated to the JS `for...in' statement.
865
866 (let ((obj (create :a 1 :b 2 :c 3)))
867 (for-in (i obj)
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/>');
872 };
873
874 ;;; The `WHILE' form is transformed to the JavaScript form `while',
875 ;;; and loops until a termination test evaluates to false.
876
877 (while ((@ film is-not-finished))
878 ((@ this eat) (new *popcorn)))
879 => while (film.isNotFinished()) {
880 this.eat(new Popcorn);
881 };
882
883 ;;;# The `CASE' statement
884 ;;;t \index{CASE}
885 ;;;t \index{SWITCH}
886 ;;;t \index{switch}
887
888 ; (CASE case-value clause*)
889 ;
890 ; clause ::= (value body) | ((value*) body) | t-clause
891 ; case-value ::= a Parenscript expression
892 ; value ::= a Parenscript expression
893 ; t-clause ::= {t | otherwise | default} body
894 ; body ::= a list of Parenscript statements
895
896 ;;; The Lisp `CASE' form is transformed to a `switch' statement in
897 ;;; JavaScript. Note that `CASE' is not an expression in
898 ;;; Parenscript.
899
900 (case (aref blorg i)
901 ((1 "one") (alert "one"))
902 (2 (alert "two"))
903 (t (alert "default clause")))
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');
914 };
915
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")))
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');
931 };
932
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
940 ; (WITH object body)
941 ;
942 ; object ::= a Parenscript expression evaluating to an object
943 ; body ::= a list of Parenscript statements
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
949 (with (create :foo "foo" :i "i")
950 (alert (+ "i is now intermediary scoped: " i)))
951 => with ({ foo : 'foo', i : 'i' }) {
952 alert('i is now intermediary scoped: ' + i);
953 };
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 ;
964 ; body ::= a list of Parenscript statements
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
973 (try (throw "i")
974 (:catch (error)
975 (alert (+ "an error happened: " error)))
976 (:finally
977 (alert "Leaving the try form")))
978 => try {
979 throw 'i';
980 } catch (error) {
981 alert('an error happened: ' + error);
982 } finally {
983 alert('Leaving the try form');
984 };
985
986 ;;;# The HTML Generator
987 ;;;t \index{PS-HTML}
988 ;;;t \index{HTML generation}
989
990 ; (PS-HTML html-expression)
991
992 ;;; The HTML generator of Parenscript is very similar to the htmlgen
993 ;;; HTML generator library included with AllegroServe. It accepts the
994 ;;; same input forms as the AllegroServer HTML generator. However,
995 ;;; non-HTML construct are compiled to JavaScript by the Parenscript
996 ;;; compiler. The resulting expression is a JavaScript expression.
997
998 (ps-html ((:a :href "foobar") "blorg"))
999 => '<A HREF=\"foobar\">blorg</A>';
1000
1001 (ps-html ((:a :href (generate-a-link)) "blorg"))
1002 => '<A HREF=\"' + generateALink() + '\">blorg</A>';
1003
1004 ;;; We can recursively call the Parenscript compiler in an HTML
1005 ;;; expression.
1006
1007 ((@ document write)
1008 (ps-html ((:a :href "#"
1009 :onclick (ps-inline (transport))) "link")))
1010 => document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport' + '(' + ')') + '\">link</A>');
1011
1012 ;;; Forms may be used in attribute lists to conditionally generate
1013 ;;; the next attribute. In this example the textarea is sometimes disabled.
1014
1015 (let ((disabled nil)
1016 (authorized t))
1017 (setf (@ element inner-h-t-m-l)
1018 (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled")
1019 "Edit me"))))
1020 => var disabled1 = null;
1021 var authorized2 = true;
1022 element.innerHTML =
1023 '<TEXTAREA'
1024 + (disabled1 || !authorized2 ? ' DISABLED=\"' + 'disabled' + '\"' : '')
1025 + '>Edit me</TEXTAREA>';
1026
1027 ;;;# Macrology
1028 ;;;t \index{macro}
1029 ;;;t \index{macrology}
1030 ;;;t \index{DEFPSMACRO}
1031 ;;;t \index{DEFMACRO/PS}
1032 ;;;t \index{DEFMACRO+PS}
1033 ;;;t \index{DEFINE-PS-SYMBOL-MACRO}
1034 ;;;t \index{IMPORT-MACROS-FROM-LISP}
1035 ;;;t \index{MACROLET}
1036 ;;;t \index{SYMBOL-MACROLET}
1037 ;;;t \index{PS-GENSYM}
1038 ;;;t \index{compiler}
1039
1040 ; (DEFPSMACRO name lambda-list macro-body)
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*)
1045 ; (MACROLET ({name lambda-list macro-body}*) body)
1046 ; (SYMBOL-MACROLET ({name macro-body}*) body)
1047 ; (PS-GENSYM {string})
1048 ;
1049 ; name ::= a Lisp symbol
1050 ; lambda-list ::= a lambda list
1051 ; macro-body ::= a Lisp body evaluating to Parenscript code
1052 ; body ::= a list of Parenscript statements
1053 ; string ::= a string
1054
1055 ;;; Parenscript can be extended using macros, just like Lisp can be
1056 ;;; extended using Lisp macros. Using the special Lisp form
1057 ;;; `DEFPSMACRO', the Parenscript language can be
1058 ;;; extended. `DEFPSMACRO' adds the new macro to the toplevel macro
1059 ;;; environment, which is always accessible during Parenscript
1060 ;;; compilation. For example, the `1+' and `1-' operators are
1061 ;;; implemented using macros.
1062
1063 (defpsmacro 1- (form)
1064 `(- ,form 1))
1065
1066 (defpsmacro 1+ (form)
1067 `(+ ,form 1))
1068
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):
1072
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)))
1083
1084 ;;; Macros can be defined in Parenscript code itself (as opposed to
1085 ;;; from Lisp) by using the Parenscript `MACROLET' and `DEFMACRO'
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.
1090
1091 ;;; Parenscript also supports the use of macros defined in the
1092 ;;; underlying Lisp environment. Existing Lisp macros can be imported
1093 ;;; into the Parenscript macro environment by
1094 ;;; `IMPORT-MACROS-FROM-LISP'. This functionality enables code sharing
1095 ;;; between Parenscript and Lisp, and is useful in debugging since the
1096 ;;; full power of Lisp macroexpanders, editors and other supporting
1097 ;;; facilities can be used. However, it is important to note that the
1098 ;;; macroexpansion of Lisp macros and Parenscript macros takes place
1099 ;;; in their own respective environments, and many Lisp macros
1100 ;;; (especially those provided by the Lisp implementation) expand into
1101 ;;; code that is not usable by Parenscript. To make it easy for users
1102 ;;; to take advantage of these features, two additional macro
1103 ;;; definition facilities are provided by Parenscript: `DEFMACRO/PS'
1104 ;;; and `DEFMACRO+PS'. `DEFMACRO/PS' defines a Lisp macro and then
1105 ;;; imports it into the Parenscript macro environment, while
1106 ;;; `DEFMACRO+PS' defines two macros with the same name and expansion,
1107 ;;; one in Parenscript and one in Lisp. `DEFMACRO+PS' is used when the
1108 ;;; full 'macroexpand' of the Lisp macro yields code that cannot be
1109 ;;; used by Parenscript.
1110
1111 ;;; Parenscript also supports symbol macros, which can be introduced
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.
1115
1116 (defpsmacro with-slots (slots object &rest body)
1117 `(symbol-macrolet ,(mapcar #'(lambda (slot)
1118 `(,slot '(slot-value ,object ',slot)))
1119 slots)
1120 ,@body))
1121
1122 ;;;# The Parenscript namespace system
1123 ;;;t \index{package}
1124 ;;;t \index{namespace}
1125 ;;;t \index{PS-PACKAGE-PREFIX}
1126
1127 ; (setf (PS-PACKAGE-PREFIX package-designator) string)
1128
1129 ;;; Although JavaScript does not offer namespacing or a package
1130 ;;; system, Parenscript does provide a namespace mechanism for
1131 ;;; generated JavaScript by integrating with the Common Lisp package
1132 ;;; system. Since Parenscript code is normally read in by the Lisp
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
1139 (defpackage "PS-REF.MY-LIBRARY"
1140 (:use "PARENSCRIPT"))
1141 (setf (ps-package-prefix "PS-REF.MY-LIBRARY") "my_library_")
1142
1143 (defun ps-ref.my-library::library-function (x y)
1144 (return (+ x y)))
1145 -> function my_library_libraryFunction(x, y) {
1146 return x + y;
1147 };
1148
1149 ;;;# Identifier obfuscation
1150 ;;;t \index{obfuscation}
1151 ;;;t \index{identifiers}
1152 ;;;t \index{OBFUSCATE-PACKAGE}
1153 ;;;t \index{UNOBFUSCATE-PACKAGE}
1154
1155 ; (OBFUSCATE-PACKAGE package-designator &optional symbol-map)
1156 ; (UNOBFUSCATE-PACKAGE package-designator)
1157
1158 ;;; Similar to the namespace mechanism, Parenscript provides a
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, 賲);
1178 };
1179
1180 ;;; The obfuscation and namespace facilities can be used on packages
1181 ;;; at the same time.
1182
1183 ;;;# The Parenscript Compiler
1184 ;;;t \index{compiler}
1185 ;;;t \index{Parenscript compiler}
1186 ;;;t \index{PS}
1187 ;;;t \index{PS*}
1188 ;;;t \index{PS1*}
1189 ;;;t \index{PS-INLINE}
1190 ;;;t \index{PS-INLINE*}
1191 ;;;t \index{LISP}
1192
1193 ; (PS &body body)
1194 ; (PS* &body body)
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)
1200 ;
1201 ; body ::= Parenscript statements comprising an implicit `PROGN'
1202
1203 ;;; For static Parenscript code, the macro `PS' compiles the provided
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
1209 ;;; `PS-INLINE' and `PS-INLINE*' take a single Parenscript form and
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
1219 ;;; Parenscript can also call out to arbitrary Common Lisp code at
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
1222 ;;; were Parenscript code. For `PS' and `PS-INLINE', the Parenscript
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.