1 ;;;# ParenScript Language Reference
3 ;;; Create a useful package for the code here...
5 (defpackage #:ps-ref
(:use
#:ps
))
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.
14 ;;;# Statements and Expressions
15 ;;;t \index{statement}
16 ;;;t \index{expression}
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:
29 (+ i
(if 1 2 3)) => i
+ (1 ?
2 : 3)
38 ;;;# Symbol conversion
40 ;;;t \index{symbol conversion}
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.
48 !?
#@%
=> bangwhathashatpercent
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.
54 bla-foo-bar
=> blaFooBar
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 `*'.
62 ;;; The `.' character is left as is in symbols. This allows the
63 ;;; ParenScript programmer to use a practical shortcut when accessing
64 ;;; slots or methods of JavaScript objects. Instead of writing
66 (slot-value foobar
'slot
)
72 ;;; A symbol beggining and ending with `+' or `*' is converted to all
73 ;;; uppercase, to signify that this is a constant or a global
76 *global-array
* => GLOBALARRAY
78 *global-array
*.length
=> GLOBALARRAY.length
80 ;;;## Reserved Keywords
82 ;;;t \index{reserved keywords}
84 ;;; The following keywords and symbols are reserved in ParenScript,
85 ;;; and should not be used as variable names.
87 ! ~
++ --
* / %
+ -
<< >> >>> < > <= >= == != ==== !== & ^ |
&& ||
*=
88 /= %
= += -
= <<= >>= >>>= &= ^
= |
= 1-
1+ ABSTRACT AND AREF ARRAY
89 BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS COMMA CONST CONTINUE
90 CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO
* DOEACH DOLIST
91 DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL FINALLY
92 FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF
93 INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA LET LET
* LEXICAL-LET
94 LEXICAL-LET
* LISP LIST LONG MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE
95 PRIVATE PROGN PROTECTED PUBLIC RANDOM REGEX RETURN SETF SHORT
96 SLOT-VALUE STATIC SUPER SWITCH SYMBOL-MACROLET SYNCHRONIZED T THIS
97 THROW THROWS TRANSIENT TRY TYPEOF UNDEFINED UNLESS VAR VOID VOLATILE
98 WHEN WHILE WITH WITH-SLOTS
101 ;;;t \index{literal value}
103 ;;;## Number literals
105 ;;;t \index{number literal}
107 ; number ::= a Lisp number
110 ;;; ParenScript supports the standard JavaScript literal
111 ;;; values. Numbers are compiled into JavaScript numbers.
117 ;;; Note that the base is not conserved between Lisp and JavaScript.
121 ;;;## String literals
123 ;;;t \index{string literal}
125 ; string ::= a Lisp string
127 ;;; Lisp strings are converted into JavaScript literals.
131 "bratzel bub" => 'bratzel bub
'
133 ;;; Escapes in Lisp are not converted to JavaScript escapes. However,
134 ;;; to avoid having to use double backslashes when constructing a
135 ;;; string, you can use the CL-INTERPOL library by Edi Weitz.
140 ;;;t \index{MAKE-ARRAY}
142 ;;;t \index{array literal}
145 ; (MAKE-ARRAY {values}*)
148 ; values ::= a ParenScript expression
149 ; array ::= a ParenScript expression
150 ; index ::= a ParenScript expression
152 ;;; Array literals can be created using the `ARRAY' form.
156 (array 1 2 3) => [ 1, 2, 3 ]
159 (array "foobar" "bratzel bub"))
160 => [ [ 2, 3 ], [ 'foobar
', 'bratzel bub
' ] ]
162 ;;; Arrays can also be created with a call to the `Array' function
163 ;;; using the `MAKE-ARRAY'. The two forms have the exact same semantic
164 ;;; on the JavaScript side.
166 (make-array) => new Array
()
168 (make-array 1 2 3) => new Array
(1, 2, 3)
172 (make-array "foobar" "bratzel bub"))
173 => new Array
(new Array
(2, 3), new Array
('foobar
', 'bratzel bub
'))
175 ;;; Indexing arrays in ParenScript is done using the form `AREF'. Note
176 ;;; that JavaScript knows of no such thing as an array. Subscripting
177 ;;; an array is in fact reading a property from an object. So in a
178 ;;; semantic sense, there is no real difference between `AREF' and
181 ;;;## Object literals
183 ;;;t \index{SLOT-VALUE}
184 ;;;t \index{WITH-SLOTS}
185 ;;;t \index{object literal}
187 ;;;t \index{object property}
188 ;;;t \index{property}
190 ; (CREATE {name value}*)
191 ; (SLOT-VALUE object slot-name)
192 ; (WITH-SLOTS ({slot-name}*) object body)
194 ; name ::= a ParenScript symbol or a Lisp keyword
195 ; value ::= a ParenScript expression
196 ; object ::= a ParenScript object expression
197 ; slot-name ::= a quoted Lisp symbol
198 ; body ::= a list of ParenScript statements
201 ;;; Object literals can be create using the `CREATE' form. Arguments
202 ;;; to the `CREATE' form is a list of property names and values. To be
203 ;;; more "lispy", the property names can be keywords.
205 (create :foo
"bar" :blorg
1)
206 => { foo
: 'bar
', blorg
: 1 }
210 :another-object
(create :schtrunz
1))
213 anotherObject
: { schtrunz
: 1 } }
215 ;;; Object properties can be accessed using the `SLOT-VALUE' form,
216 ;;; which takes an object and a slot-name.
218 (slot-value an-object
'foo
) => anObject.foo
220 ;;; A programmer can also use the "." symbol notation explained above.
222 an-object.foo
=> anObject.foo
224 ;;; The form `WITH-SLOTS' can be used to bind the given slot-name
225 ;;; symbols to a macro that will expand into a `SLOT-VALUE' form at
228 (with-slots (a b c
) this
230 => this.a
+ this.b
+ this.c
;
232 ;;;## Regular Expression literals
234 ;;;t \index{regular expression}
235 ;;;t \index{CL-INTERPOL}
239 ; regex ::= a Lisp string
241 ;;; Regular expressions can be created by using the `REGEX' form. If
242 ;;; the argument does not start with a slash, it is surrounded by
243 ;;; slashes to make it a proper JavaScript regex. If the argument
244 ;;; starts with a slash it is left as it is. This makes it possible
245 ;;; to use modifiers such as slash-i (case-insensitive) or
246 ;;; slash-g (match-globally (all)).
248 (regex "foobar") => /foobar
/
250 (regex "/foobar/i") => /foobar
/i
252 ;;; Here CL-INTERPOL proves really useful.
254 (regex #?r
"/([^\s]+)foobar/i") => /([^\s
]+)foobar
/i
256 ;;;## Literal symbols
261 ;;;t \index{UNDEFINED}
263 ;;;t \index{literal symbols}
267 ; T, F, FALSE, NIL, UNDEFINED, THIS
269 ;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their
270 ;;; JavaScript boolean equivalents `true' and `false'.
278 ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword
283 ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword
286 UNDEFINED
=> undefined
288 ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword
294 ;;;t \index{variable}
297 ; variable ::= a Lisp symbol
299 ;;; All the other literal Lisp values that are not recognized as
300 ;;; special forms or symbol macros are converted to JavaScript
301 ;;; variables. This extreme freedom is actually quite useful, as it
302 ;;; allows the ParenScript programmer to be flexible, as flexible as
303 ;;; JavaScript itself.
307 a-variable
=> aVariable
311 *math.floor
=> Math.floor
313 ;;;# Function calls and method calls
314 ;;;t \index{function}
315 ;;;t \index{function call}
317 ;;;t \index{method call}
319 ; (function {argument}*)
320 ; (method object {argument}*)
322 ; function ::= a ParenScript expression or a Lisp symbol
323 ; method ::= a Lisp symbol beginning with .
324 ; object ::= a ParenScript expression
325 ; argument ::= a ParenScript expression
327 ;;; Any list passed to the JavaScript that is not recognized as a
328 ;;; macro or a special form (see "Macro Expansion" below) is
329 ;;; interpreted as a function call. The function call is converted to
330 ;;; the normal JavaScript function call representation, with the
331 ;;; arguments given in paren after the function name.
333 (blorg 1 2) => blorg
(1, 2)
335 (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4))
336 => foobar
(blorg(1, 2), blabla
(3, 4), [ 2, 3, 4 ])
338 ((aref foo i
) 1 2) => foo
[i](1, 2)
340 ;;; A method call is a function call where the function name is a
341 ;;; symbol and begins with a "." . In a method call, the name of the
342 ;;; function is append to its first argument, thus reflecting the
343 ;;; method call syntax of JavaScript. Please note that most method
344 ;;; calls can be abbreviated using the "." trick in symbol names (see
345 ;;; "Symbol Conversion" above).
347 (.blorg this 1 2) => this.blorg(1, 2)
349 (this.blorg 1 2) => this.blorg(1, 2)
351 (.blorg (aref foobar 1) NIL T)
352 => foobar[1].blorg(null, true)
354 ;;;# Operator Expressions
355 ;;;t \index{operator}
356 ;;;t \index{operator expression}
357 ;;;t \index{assignment operator}
363 ; (operator {argument}*)
364 ; (single-operator argument)
366 ; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL,
367 ; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR.
368 ; single-operator ::= one of INCF, DECF, ++, --, NOT, !
369 ; argument ::= a ParenScript expression
371 ;;; Operator forms are similar to function call forms, but have an
372 ;;; operator as function name.
374 ;;; Please note that `=' is converted to `==' in JavaScript. The `='
375 ;;; ParenScript operator is not the assignment operator. Unlike
376 ;;; JavaScript, ParenScript supports multiple arguments to the
385 ;;; Note that the resulting expression is correctly parenthesized,
386 ;;; according to the JavaScript operator precedence that can be found
387 ;;; in table form at:
389 ;;; http://www.codehouse.com/javascript/precedence/
391 (* 1 (+ 2 3 4) 4 (/ 6 7))
392 => 1 * (2 + 3 + 4) * 4 * (6 / 7)
394 ;;; The pre increment and decrement operators are also
395 ;;; available. `INCF' and `DECF' are the pre-incrementing and
396 ;;; pre-decrementing operators. These operators can
397 ;;; take only one argument.
403 ;;; The `1+' and `1-' operators are shortforms for adding and
410 ;;; The `not' operator actually optimizes the code a bit. If `not' is
411 ;;; used on another boolean-returning operator, the operator is
414 (not (< i 2)) => i >= 2
416 (not (eql i 2)) => i != 2
419 ;;;t \index{body form}
421 ;;;t \index{body statement}
423 ; (PROGN {statement}*) in statement context
424 ; (PROGN {expression}*) in expression context
426 ; statement ::= a ParenScript statement
427 ; expression ::= a ParenScript expression
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.
435 ;;; For example, in a statement context:
437 (progn (blorg i) (blafoo i))
441 ;;; In an expression context:
443 (+ i (progn (blorg i) (blafoo i)))
444 => i + (blorg(i), blafoo(i))
446 ;;; A `PROGN' form doesn't lead to additional indentation or
447 ;;; additional braces around it's body.
449 ;;;# Function Definition
450 ;;;t \index{function}
452 ;;;t \index{function definition}
456 ;;;t \index{anonymous function}
458 ; (DEFUN name ({argument}*) body)
459 ; (LAMBDA ({argument}*) body)
461 ; name ::= a Lisp Symbol
462 ; argument ::= a Lisp symbol
463 ; body ::= a list of ParenScript statements
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.
469 (defun a-function (a b)
471 => function aFunction(a, b) {
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.
479 (lambda (a b) (return (+ a b)))
485 ;;;t \index{assignment}
491 ;;;t \index{assignment operator}
496 ; lhs ::= a ParenScript left hand side expression
497 ; rhs ::= a ParenScript expression
502 ; lhs ::= a ParenScript symbol
503 ; rhs ::= a ParenScript expression
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.
511 (setf a 2 b 3 c 4 x (+ a b c))
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:
521 (setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a;
523 (setf a (- 1 a)) => a = 1 - a;
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:
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:
547 ;; => ERROR: The value (AREF A 0) is not of type SYMBOL.
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.
553 ;;; `DEFSETF' supports both long and short forms, while `DEFUN' of a
554 ;;; setf place generates a JavaScript function name with the __setf_
557 (defun (setf color) (new-color el)
558 (setf (slot-value (slot-value el 'style) 'color) new-color))
559 => function __setf_color(newColor, el) {
560 el.style.color = newColor;
563 (setf (color some-div) (+ 23 "em"))
564 => var _js2 = someDiv;
565 var _js1 = 23 + 'em';
566 __setf_color(_js1, _js2);
568 ;;; Note that temporary variables are generated to preserve evaluation
569 ;;; order of the arguments as they would be in Lisp.
571 ;;; The following example illustrates how setf places can be used to
572 ;;; provide a uniform protocol for positioning elements in HTML pages:
574 (defsetf left (el) (offset)
575 `(setf (slot-value (slot-value ,el 'style) 'left) ,offset))
578 (setf (left some-div) (+ 123 "px"))
579 => var _js2 = someDiv;
580 var _js1 = 123 + 'px';
581 _js2.style.left = _js1;
583 (progn (defmacro left (el)
584 `(slot-value ,el 'offset-left))
586 => someDiv.offsetLeft;
588 ;;;# Single argument statements
589 ;;;t \index{single-argument statement}
593 ;;;t \index{function}
598 ; value ::= a ParenScript expression
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
605 (return 1) => return 1
607 (throw "foobar") => throw 'foobar'
609 ;;;# Single argument expression
610 ;;;t \index{single-argument expression}
611 ;;;t \index{object creation}
612 ;;;t \index{object deletion}
616 ;;;t \index{INSTANCEOF}
623 ; (INSTANCEOF {value})
626 ; value ::= a ParenScript expression
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
631 ;;; ParenScript expression.
633 (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4)
635 (if (= (typeof blorg) *string)
636 (alert (+ "blorg is a string: " blorg))
637 (alert "blorg is not a string"))
638 => if (typeof blorg == String) {
639 alert('blorg is a string: ' + blorg);
641 alert('blorg is not a string');
644 ;;;# Conditional Statements
645 ;;;t \index{conditional statements}
649 ;;;t \index{conditionals}
651 ; (IF conditional then {else})
652 ; (WHEN condition then)
653 ; (UNLESS condition then)
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
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.
667 (if (blorg.is-correct)
668 (progn (carry-on) (return i))
669 (alert "blorg is not correct!"))
670 => if (blorg.isCorrect()) {
674 alert('blorg is not correct!');
677 (+ i (if (blorg.add-one) 1 2))
678 => i + (blorg.addOne() ? 1 : 2)
680 ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the
683 (when (blorg.is-correct)
686 => if (blorg.isCorrect()) {
691 (unless (blorg.is-correct)
692 (alert "blorg is not correct!"))
693 => if (!blorg.isCorrect()) {
694 alert('blorg is not correct!');
697 ;;;# Variable declaration
698 ;;;t \index{variable}
699 ;;;t \index{variable declaration}
706 ;;;t \index{LEXICAL-LET}
707 ;;;t \index{LEXICAL-LET*}
709 ; (DEFVAR var {value}?)
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)
716 ; var ::= a Lisp symbol
717 ; value ::= a ParenScript expression
718 ; body ::= a list of ParenScript statements
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.
725 (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]
727 ;;; One feature present in Parenscript that is not part of Common Lisp
728 ;;; is lexically-scoped function variables, which are declared using
729 ;;; the `VAR' special form.
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*',
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.
741 ;;; `LEXICAL-LET' and `LEXICAL-LET*' actually introduce new lexical
742 ;;; environments for the variable bindings by creating anonymous
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.
751 (simple-let* ((a 0) (b 1))
757 (simple-let* ((a "World") (b "Hello"))
758 (simple-let ((a b) (b a))
770 (simple-let* ((a 0) (b 1))
771 (lexical-let* ((a 9) (b 8))
783 (simple-let* ((a "World") (b "Hello"))
784 (lexical-let ((a b) (b a))
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.
798 ;;;# Iteration constructs
799 ;;;t \index{iteration}
800 ;;;t \index{iteration construct}
802 ;;;t \index{array traversal}
803 ;;;t \index{property}
804 ;;;t \index{object property}
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)
816 ; (WHILE end-test body)
818 ; var ::= a Lisp symbol
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
828 ;;; All interation special forms are transformed into JavaScript `for'
829 ;;; statements and, if needed, lambda expressions.
831 ;;; `DO', `DO*', and `DOTIMES' carry the same semantics as their
832 ;;; Common Lisp equivalents.
834 ;;; `DO*' (note the variety of possible init-forms:
836 (do* ((a) b (c (array "a" "b" "c" "d" "e"))
838 (e (aref c d) (aref c d)))
839 ((or (= d c.length) (eql e "x")))
841 (document.write (+ "a: " a " b: " b "<br/>")))
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]) {
845 document.write('a: ' + a + ' b: ' + b + '<br/>');
848 ;;; `DO' (note the parallel assignment):
851 (s 0 (+ s i (1+ i))))
853 (document.write (+ "i: " i " s: " s "<br/>")))
861 document.write('i: ' + i + ' s: ' + s + '<br/>');
863 var _js4 = s + i + (i + 1);
868 ;;; compare to `DO*':
871 (s 0 (+ s i (1- i))))
873 (document.write (+ "i: " i " s: " s "<br/>")))
874 => for (var i = 0, s = 0; i <= 10; i += 1, s += i + (i - 1)) {
875 document.write('i: ' + i + ' s: ' + s + '<br/>');
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
/>"))))
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/>');
888 ;;; `DOTIMES' with return value:
891 (alert (+ "Summation to
10 is
"
893 (incf res (1+ i))))))
895 alert('Summation to 10 is ' + (function () {
896 for (var i = 0; i < 10; i += 1) {
902 ;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS
905 (let* ((l (list 1 2 4 8 16 32)))
907 (document.write (+ "c
: " c "<br
/>"))))
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/>');
914 (let* ((l (list 1 2 4 8 16 32))
916 (alert (+ "Sum of
" l " is
: "
919 => var l = [1, 2, 4, 8, 16, 32];
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];
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.
933 (let* ((obj (create :a 1 :b 2 :c 3)))
935 (document.write (+ i ": " (aref obj i) "<br
/>"))))
936 => var obj = { a : 1, b : 2, c : 3 };
938 document.write(i + ': ' + obj[i] + '<br/>');
941 (let* ((obj (create :a 1 :b 2 :c 3)))
943 (document.write (+ k ": " v "<br
/>"))))
944 => var obj = { a : 1, b : 2, c : 3 };
948 document.write(k + ': ' + v + '<br/>');
951 ;;; The `WHILE' form is transformed to the JavaScript form `while',
952 ;;; and loops until a termination test evaluates to false.
954 (while (film.is-not-finished)
955 (this.eat (new *popcorn)))
956 => while (film.isNotFinished()) {
957 this.eat(new Popcorn);
960 ;;;# The `CASE' statement
965 ; (CASE case-value clause*)
967 ; clause ::= (value body) | ((value*) body) | t-clause
968 ; case-value ::= a ParenScript expression
969 ; value ::= a ParenScript expression
970 ; t-clause ::= {t | otherwise | default} body
971 ; body ::= a list of ParenScript statements
973 ;;; The Lisp `CASE' form is transformed to a `switch' statement in
974 ;;; JavaScript. Note that `CASE' is not an expression in
978 ((1 "one
") (alert "one
"))
980 (t (alert "default clause
")))
981 => switch (blorg[i]) {
990 alert('default clause');
993 ; (SWITCH case-value clause*)
994 ; clause ::= (value body) | (default body)
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.
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
")))
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');
1010 ;;;# The `WITH' statement
1012 ;;;t \index{dynamic scope}
1013 ;;;t \index{binding}
1014 ;;;t \index{scoping}
1015 ;;;t \index{closure}
1017 ; (WITH object body)
1019 ; object ::= a ParenScript expression evaluating to an object
1020 ; body ::= a list of ParenScript statements
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.
1026 (with (create :foo "foo
" :i "i
")
1027 (alert (+ "i is now intermediary scoped
: " i)))
1028 => with ({ foo : 'foo', i : 'i' }) {
1029 alert('i is now intermediary scoped: ' + i);
1032 ;;;# The `TRY' statement
1035 ;;;t \index{FINALLY}
1036 ;;;t \index{exception}
1037 ;;;t \index{error handling}
1039 ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?)
1041 ; body ::= a list of ParenScript statements
1042 ; var ::= a Lisp symbol
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.
1052 (alert (+ "an error happened
: " error)))
1054 (alert "Leaving the try form
")))
1058 alert('an error happened: ' + error);
1060 alert('Leaving the try form');
1063 ;;;# The HTML Generator
1064 ;;;t \index{PS-HTML}
1065 ;;;t \index{HTML generation}
1067 ; (PS-HTML html-expression)
1069 ;;; The HTML generator of ParenScript is very similar to the htmlgen
1070 ;;; HTML generator library included with AllegroServe. It accepts the
1071 ;;; same input forms as the AllegroServer HTML generator. However,
1072 ;;; non-HTML construct are compiled to JavaScript by the ParenScript
1073 ;;; compiler. The resulting expression is a JavaScript expression.
1075 (ps-html ((:a :href "foobar
") "blorg
"))
1076 => '<a href=\"foobar\">blorg</a>'
1078 (ps-html ((:a :href (generate-a-link)) "blorg
"))
1079 => '<a href=\"' + generateALink() + '\">blorg</a>'
1081 ;;; We can recursively call the ParenScript compiler in an HTML
1085 (ps-html ((:a :href "#"
1086 :onclick (lisp (ps-inline (transport)))) "link
")))
1087 => document.write('<a href=\"#\" onclick=\"' + 'javascript:transport()' + '\">link</a>')
1089 ;;; Forms may be used in attribute lists to conditionally generate
1090 ;;; the next attribute. In this example the textarea is sometimes disabled.
1092 (let* ((disabled nil)
1094 (setf element.inner-h-t-m-l
1095 (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled
")
1097 => var disabled = null;
1098 var authorized = true;
1101 + (disabled || !authorized ? ' disabled=\"' + 'disabled' + '\"' : '')
1102 + '>Edit me</textarea>';
1106 ;;;t \index{macrology}
1107 ;;;t \index{DEFPSMACRO}
1108 ;;;t \index{MACROLET}
1109 ;;;t \index{SYMBOL-MACROLET}
1110 ;;;t \index{PS-GENSYM}
1111 ;;;t \index{compiler}
1113 ; (DEFPSMACRO name lambda-list macro-body)
1114 ; (MACROLET ({name lambda-list macro-body}*) body)
1115 ; (SYMBOL-MACROLET ({name macro-body}*) body)
1116 ; (PS-GENSYM {string})
1118 ; name ::= a Lisp symbol
1119 ; lambda-list ::= a lambda list
1120 ; macro-body ::= a Lisp body evaluating to ParenScript code
1121 ; body ::= a list of ParenScript statements
1122 ; string ::= a string
1124 ;;; ParenScript can be extended using macros, just like Lisp can be
1125 ;;; extended using Lisp macros. Using the special Lisp form
1126 ;;; `DEFPSMACRO', the ParenScript language can be
1127 ;;; extended. `DEFPSMACRO' adds the new macro to the toplevel macro
1128 ;;; environment, which is always accessible during ParenScript
1129 ;;; compilation. For example, the `1+' and `1-' operators are
1130 ;;; implemented using macros.
1132 (defpsmacro 1- (form)
1135 (defpsmacro 1+ (form)
1138 ;;; A more complicated ParenScript macro example is the implementation
1139 ;;; of the `DOLIST' form (note how `PS-GENSYM', the ParenScript of
1140 ;;; `GENSYM', is used to generate new ParenScript variable names):
1142 (defpsmacro dolist ((var array &optional (result nil result?)) &body body)
1143 (let ((idx (ps-gensym "_js_idx
"))
1144 (arrvar (ps-gensym "_js_arrvar
")))
1148 ((>= ,idx (slot-value ,arrvar 'length))
1149 ,@(when result? (list result)))
1150 (setq ,var (aref ,arrvar ,idx))
1153 ;;; Macros can be defined in ParenScript code itself (as opposed to
1154 ;;; from Lisp) by using the ParenScript `MACROLET' and `DEFMACRO'
1157 ;;; ParenScript also supports the use of macros defined in the
1158 ;;; underlying Lisp environment. Existing Lisp macros can be imported
1159 ;;; into the ParenScript macro environment by
1160 ;;; `IMPORT-MACROS-FROM-LISP'. This functionality enables code sharing
1161 ;;; between ParenScript and Lisp, and is useful in debugging since the
1162 ;;; full power of Lisp macroexpanders, editors and other supporting
1163 ;;; facilities can be used. However, it is important to note that the
1164 ;;; macroexpansion of Lisp macros and ParenScript macros takes place
1165 ;;; in their own respective environments, and many Lisp macros
1166 ;;; (especially those provided by the Lisp implementation) expand into
1167 ;;; code that is not usable by ParenScript. To make it easy for users
1168 ;;; to take advantage of these features, two additional macro
1169 ;;; definition facilities are provided by ParenScript: `DEFMACRO/PS'
1170 ;;; and `DEFMACRO+PS'. `DEFMACRO/PS' defines a Lisp macro and then
1171 ;;; imports it into the ParenScript macro environment, while
1172 ;;; `DEFMACRO+PS' defines two macros with the same name and expansion,
1173 ;;; one in ParenScript and one in Lisp. `DEFMACRO+PS' is used when the
1174 ;;; full 'macroexpand' of the Lisp macro yields code that cannot be
1175 ;;; used by ParenScript.
1177 ;;; ParenScript also supports symbol macros, which can be introduced
1178 ;;; using the ParenScript form `SYMBOL-MACROLET'.For example, the
1179 ;;; ParenScript `WITH-SLOTS' is implemented using symbol macros.
1181 (defjsmacro with-slots (slots object &rest body)
1182 `(symbol-macrolet ,(mapcar #'(lambda (slot)
1183 `(,slot '(slot-value ,object ',slot)))
1188 ;;;# The ParenScript namespace system
1189 ;;;t \index{package}
1190 ;;;t \index{namespace}
1191 ;;;t \index{PS-PACKAGE-PREFIX}
1193 ; (setf (PS-PACKAGE-PREFIX package-designator) string)
1195 ;;; Although JavaScript does not offer namespacing or a package
1196 ;;; system, ParenScript does provide a namespace mechanism for
1197 ;;; generated JavaScript by integrating with the Common Lisp package
1198 ;;; system. Since ParenScript code is normally read in by the Lisp
1199 ;;; reader, all symbols (except for uninterned ones, ie - those
1200 ;;; specified with the #: reader macro) have a Lisp package. By
1201 ;;; default, no packages are prefixed. You can specify that symbols in
1202 ;;; a particular package receive a prefix when translated to
1203 ;;; JavaScript with the `PS-PACKAGE-PREFIX' place.
1205 (defpackage "MY-LIBRARY
"
1206 (:use #:parenscript))
1207 (setf (ps-package-prefix :my-library) "my_library_
")
1209 (defun my-library::library-function (x y)
1211 -> function my_library_libraryFunction(x, y) {
1215 ;;;# Identifier obfuscation
1216 ;;;t \index{obfuscation}
1217 ;;;t \index{identifiers}
1218 ;;;t \index{OBFUSCATE-PACKAGE}
1219 ;;;t \index{UNOBFUSCATE-PACKAGE}
1221 ; (OBFUSCATE-PACKAGE package-designator)
1222 ; (UNOBFUSCATE-PACKAGE package-designator)
1224 ;;; Similar to the namespace mechanism, ParenScript provides a
1225 ;;; facility to generate obfuscated identifiers in certain Lisp
1228 (defpackage "OBFUSCATE-ME
")
1229 (obfuscate-package :obfuscate-me)
1231 (defun obfuscate-me::library-function2 (a b obfuscate-me::foo)
1232 (+ a (my-library::library-function b obfuscate-me::foo)))
1234 ;;; The obfuscation and namespace facilities can be used on packages
1235 ;;; at the same time.
1237 ;;;# The ParenScript Compiler
1238 ;;;t \index{compiler}
1239 ;;;t \index{ParenScript compiler}
1240 ;;;t \index{COMPILE-SCRIPT}
1243 ;;;t \index{PS-INLINE}
1245 ;;;t \index{nested compilation}
1247 ; (COMPILE-SCRIPT script-form &key (output-stream nil))
1250 ; (PS-INLINE &body body)
1251 ; (LISP &body lisp-forms)
1253 ; body ::= ParenScript statements comprising an implicit `PROGN'
1255 ;;; For static ParenScript code, the macros `PS' and `PS-INLINE',
1256 ;;; avoid the need to quote the ParenScript expression. `PS*' and
1257 ;;; `COMPILE-SCRIPT' evaluate their arguments. All these forms except
1258 ;;; for `COMPILE-SCRIPT' treat the given forms as an implicit
1259 ;;; `PROGN'. `PS' and `PS*' return a string of the compiled body,
1260 ;;; while `COMPILE-SCRIPT' takes an optional output-stream parameter
1261 ;;; that can be used to specify a stream to which the generated
1262 ;;; JavaScript will be written. `PS-INLINE' generates a string that
1263 ;;; can be used in HTML node attributes.
1265 ;;; ParenScript can also call out to arbitrary Lisp code at
1266 ;;; compile-time using the special form `LISP'. This is typically used
1267 ;;; to insert the values of Lisp special variables into ParenScript
1268 ;;; code at compile-time, and can also be used to make nested calls to
1269 ;;; the ParenScript compiler, which comes in useful when you want to
1270 ;;; use the result of `PS-INLINE' in `PS-HTML' forms, for
1271 ;;; example. Alternatively the same thing can be accomplished by
1272 ;;; constructing ParenScript programs as lists and passing them to
1273 ;;; `PS*' or `COMPILE-SCRIPT'.