X-Git-Url: http://git.hcoop.net/clinton/parenscript.git/blobdiff_plain/ca4fb7622804d5e5d35fdb7f4161f355422a3dc2..fc772f726fef4f366bf0a2529db348a554a092fa:/docs/reference.lisp
diff --git a/docs/reference.lisp b/docs/reference.lisp
index 70a718f..173e70b 100644
--- a/docs/reference.lisp
+++ b/docs/reference.lisp
@@ -1,10 +1,15 @@
-;;;# ParenScript Language Reference
+;;;# Parenscript Language Reference
-;;; This chapters describes the core constructs of ParenScript, as
+;;; Create a useful package for the code here...
+(in-package #:cl-user)
+(defpackage #:ps-ref (:use #:ps))
+(in-package #:ps-ref)
+
+;;; This chapters describes the core constructs of Parenscript, as
;;; well as its compilation model. This chapter is aimed to be a
-;;; comprehensive reference for ParenScript developers. Programmers
-;;; looking for how to tweak the ParenScript compiler itself should
-;;; turn to the ParenScript Internals chapter.
+;;; comprehensive reference for Parenscript developers. Programmers
+;;; looking for how to tweak the Parenscript compiler itself should
+;;; turn to the Parenscript Internals chapter.
;;;# Statements and Expressions
;;;t \index{statement}
@@ -14,21 +19,21 @@
;;; makes the difference between an expression, which evaluates to a
;;; value, and a statement, which has no value. Examples for
;;; JavaScript statements are `for', `with' and `while'. Most
-;;; ParenScript forms are expression, but certain special forms are
+;;; Parenscript forms are expression, but certain special forms are
;;; not (the forms which are transformed to a JavaScript
-;;; statement). All ParenScript expressions are statements
+;;; statement). All Parenscript expressions are statements
;;; though. Certain forms, like `IF' and `PROGN', generate different
;;; JavaScript constructs whether they are used in an expression
;;; context or a statement context. For example:
-(+ i (if 1 2 3)) => i + (1 ? 2 : 3)
+(+ i (if 1 2 3)) => i + (1 ? 2 : 3);
(if 1 2 3)
- => if (1) {
- 2;
- } else {
- 3;
- }
+=> if (1) {
+ 2;
+ } else {
+ 3;
+ };
;;;# Symbol conversion
;;;t \index{symbol}
@@ -40,56 +45,44 @@
;;; "bang", "what", "hash", "at", "percent", "slash",
;;; "start" and "plus" respectively. The `$' character is untouched.
-!?#@% => bangwhathashatpercent
+!?#@% => bangwhathashatpercent;
;;; The `-' is an indication that the following character should be
;;; converted to uppercase. Thus, `-' separated symbols are converted
;;; to camelcase. The `_' character however is left untouched.
-bla-foo-bar => blaFooBar
+bla-foo-bar => blaFooBar;
;;; If you want a JavaScript symbol beginning with an uppercase, you
;;; can either use a leading `-', which can be misleading in a
;;; mathematical context, or a leading `*'.
-*array => Array
-
-;;; The `.' character is left as is in symbols. This allows the
-;;; ParenScript programmer to use a practical shortcut when accessing
-;;; slots or methods of JavaScript objects. Instead of writing
-
-(slot-value foobar 'slot)
-
-;;; we can write
-
-foobar.slot
+*array => Array;
;;; A symbol beggining and ending with `+' or `*' is converted to all
;;; uppercase, to signify that this is a constant or a global
;;; variable.
-*global-array* => GLOBALARRAY
-
-*global-array*.length => GLOBALARRAY.length
+*global-array* => GLOBALARRAY;
;;;## Reserved Keywords
;;;t \index{keyword}
;;;t \index{reserved keywords}
-;;; The following keywords and symbols are reserved in ParenScript,
+;;; The following keywords and symbols are reserved in Parenscript,
;;; and should not be used as variable names.
-! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && ||
-*= /= %= += -= <<= >>= >>>= &= ^= |= 1- 1+
-ABSTRACT AND AREF ARRAY BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS
-COMMA CONST CONTINUE CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE
-DO DOEACH DOLIST DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS FALSE
-FINAL FINALLY FLOAT FLOOR FOR FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF
-INSTANCEOF INT INTERFACE JS LAMBDA LET LISP LIST LONG MAKE-ARRAY NATIVE NEW
-NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED PUBLIC RANDOM REGEX RETURN
-SETF SHORT SLOT-VALUE STATIC SUPER SWITCH SYMBOL-MACROLET SYNCHRONIZED T
-THIS THROW THROWS TRANSIENT TRY TYPEOF UNDEFINED UNLESS VAR VOID VOLATILE
-WHEN WHILE WITH WITH-SLOTS
+! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || *=
+/= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ @ ABSTRACT AND AREF ARRAY
+BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS COMMA CONST CONTINUE
+CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO* DOEACH DOLIST
+DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL FINALLY
+FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF
+INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA LET LET* LISP LIST LONG
+MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED
+PUBLIC RANDOM REGEX RETURN SETF SHORT SLOT-VALUE STATIC SUPER SWITCH
+SYMBOL-MACROLET SYNCHRONIZED T THIS THROW THROWS TRANSIENT TRY TYPEOF
+UNDEFINED UNLESS VAR VOID VOLATILE WHEN WHILE WITH WITH-SLOTS
;;;# Literal values
;;;t \index{literal value}
@@ -101,16 +94,16 @@ WHEN WHILE WITH WITH-SLOTS
; number ::= a Lisp number
;;;
-;;; ParenScript supports the standard JavaScript literal
+;;; Parenscript supports the standard JavaScript literal
;;; values. Numbers are compiled into JavaScript numbers.
-1 => 1
+1 => 1;
-123.123 => 123.123
+123.123 => 123.123;
;;; Note that the base is not conserved between Lisp and JavaScript.
-#x10 => 16
+#x10 => 16;
;;;## String literals
;;;t \index{string}
@@ -120,13 +113,14 @@ WHEN WHILE WITH WITH-SLOTS
;;; Lisp strings are converted into JavaScript literals.
-"foobar" => 'foobar'
+"foobar" => 'foobar';
-"bratzel bub" => 'bratzel bub'
+"bratzel bub" => 'bratzel bub';
-;;; Escapes in Lisp are not converted to JavaScript escapes. However,
-;;; to avoid having to use double backslashes when constructing a
-;;; string, you can use the CL-INTERPOL library by Edi Weitz.
+;;; Special characters such as newline and backspace are converted
+;;; into their corresponding JavaScript escape sequences.
+
+" " => '\\t';
;;;## Array literals
;;;t \index{array}
@@ -139,34 +133,34 @@ WHEN WHILE WITH WITH-SLOTS
; (MAKE-ARRAY {values}*)
; (AREF array index)
;
-; values ::= a ParenScript expression
-; array ::= a ParenScript expression
-; index ::= a ParenScript expression
+; values ::= a Parenscript expression
+; array ::= a Parenscript expression
+; index ::= a Parenscript expression
;;; Array literals can be created using the `ARRAY' form.
-(array) => [ ]
+(array) => [ ];
-(array 1 2 3) => [ 1, 2, 3 ]
+(array 1 2 3) => [ 1, 2, 3 ];
(array (array 2 3)
(array "foobar" "bratzel bub"))
- => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ]
+=> [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ];
;;; Arrays can also be created with a call to the `Array' function
;;; using the `MAKE-ARRAY'. The two forms have the exact same semantic
;;; on the JavaScript side.
-(make-array) => new Array()
+(make-array) => new Array();
-(make-array 1 2 3) => new Array(1, 2, 3)
+(make-array 1 2 3) => new Array(1, 2, 3);
(make-array
(make-array 2 3)
(make-array "foobar" "bratzel bub"))
- => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub'))
+=> new Array(new Array(2, 3), new Array('foobar', 'bratzel bub'));
-;;; Indexing arrays in ParenScript is done using the form `AREF'. Note
+;;; Indexing arrays in Parenscript is done using the form `AREF'. Note
;;; that JavaScript knows of no such thing as an array. Subscripting
;;; an array is in fact reading a property from an object. So in a
;;; semantic sense, there is no real difference between `AREF' and
@@ -175,6 +169,7 @@ WHEN WHILE WITH WITH-SLOTS
;;;## Object literals
;;;t \index{CREATE}
;;;t \index{SLOT-VALUE}
+;;;t \index{@}
;;;t \index{WITH-SLOTS}
;;;t \index{object literal}
;;;t \index{object}
@@ -185,36 +180,36 @@ WHEN WHILE WITH WITH-SLOTS
; (SLOT-VALUE object slot-name)
; (WITH-SLOTS ({slot-name}*) object body)
;
-; name ::= a ParenScript symbol or a Lisp keyword
-; value ::= a ParenScript expression
-; object ::= a ParenScript object expression
+; name ::= a Parenscript symbol or a Lisp keyword
+; value ::= a Parenscript expression
+; object ::= a Parenscript object expression
; slot-name ::= a quoted Lisp symbol
-; body ::= a list of ParenScript statements
+; body ::= a list of Parenscript statements
;;;
;;; Object literals can be create using the `CREATE' form. Arguments
;;; to the `CREATE' form is a list of property names and values. To be
;;; more "lispy", the property names can be keywords.
-(create :foo "bar" :blorg 1)
- => { foo : 'bar',
- blorg : 1 }
+(create foo "bar" :blorg 1)
+=> { foo : 'bar', 'blorg' : 1 };
-(create :foo "hihi"
- :blorg (array 1 2 3)
- :another-object (create :schtrunz 1))
- => { foo : 'hihi',
- blorg : [ 1, 2, 3 ],
- anotherObject : { schtrunz : 1 } }
+(create foo "hihi"
+ blorg (array 1 2 3)
+ another-object (create :schtrunz 1))
+=> { foo : 'hihi',
+ blorg : [ 1, 2, 3 ],
+ anotherObject : { 'schtrunz' : 1 } };
;;; Object properties can be accessed using the `SLOT-VALUE' form,
;;; which takes an object and a slot-name.
-(slot-value an-object 'foo) => anObject.foo
+(slot-value an-object 'foo) => anObject.foo;
-;;; A programmer can also use the "." symbol notation explained above.
+;;; The convenience macro `@' is provided to make multiple levels of
+;;; indirection easy to express
-an-object.foo => anObject.foo
+(@ an-object foo bar) => anObject.foo.bar;
;;; The form `WITH-SLOTS' can be used to bind the given slot-name
;;; symbols to a macro that will expand into a `SLOT-VALUE' form at
@@ -222,7 +217,7 @@ an-object.foo => anObject.foo
(with-slots (a b c) this
(+ a b c))
- => this.a + this.b + this.c;
+=> this.a + this.b + this.c;
;;;## Regular Expression literals
;;;t \index{REGEX}
@@ -240,16 +235,17 @@ an-object.foo => anObject.foo
;;; to use modifiers such as slash-i (case-insensitive) or
;;; slash-g (match-globally (all)).
-(regex "foobar") => /foobar/
+(regex "foobar") => /foobar/;
-(regex "/foobar/i") => /foobar/i
+(regex "/foobar/i") => /foobar/i;
;;; Here CL-INTERPOL proves really useful.
-(regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i
+(regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i;
;;;## Literal symbols
;;;t \index{T}
+;;;t \index{F}
;;;t \index{FALSE}
;;;t \index{NIL}
;;;t \index{UNDEFINED}
@@ -258,29 +254,31 @@ an-object.foo => anObject.foo
;;;t \index{null}
;;;t \index{true}
-; T, FALSE, NIL, UNDEFINED, THIS
+; T, F, FALSE, NIL, UNDEFINED, THIS
+
+;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their
+;;; JavaScript boolean equivalents `true' and `false'.
-;;; The Lisp symbols `T' and `FALSE' are converted to their JavaScript
-;;; boolean equivalents `true' and `false'.
+T => true;
-T => true
+FALSE => false;
-FALSE => false
+F => false;
;;; The Lisp symbol `NIL' is converted to the JavaScript keyword
;;; `null'.
-NIL => null
+NIL => null;
;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword
;;; `undefined'.
-UNDEFINED => undefined
+UNDEFINED => undefined;
;;; The Lisp symbol `THIS' is converted to the JavaScript keyword
;;; `this'.
-THIS => this
+THIS => this;
;;;# Variables
;;;t \index{variable}
@@ -291,16 +289,14 @@ THIS => this
;;; All the other literal Lisp values that are not recognized as
;;; special forms or symbol macros are converted to JavaScript
;;; variables. This extreme freedom is actually quite useful, as it
-;;; allows the ParenScript programmer to be flexible, as flexible as
+;;; allows the Parenscript programmer to be flexible, as flexible as
;;; JavaScript itself.
-variable => variable
-
-a-variable => aVariable
+variable => variable;
-*math => Math
+a-variable => aVariable;
-*math.floor => Math.floor
+*math => Math;
;;;# Function calls and method calls
;;;t \index{function}
@@ -309,12 +305,11 @@ a-variable => aVariable
;;;t \index{method call}
; (function {argument}*)
-; (method object {argument}*)
+
;
-; function ::= a ParenScript expression or a Lisp symbol
-; method ::= a Lisp symbol beginning with .
-; object ::= a ParenScript expression
-; argument ::= a ParenScript expression
+; function ::= a Parenscript expression or a Lisp symbol
+; object ::= a Parenscript expression
+; argument ::= a Parenscript expression
;;; Any list passed to the JavaScript that is not recognized as a
;;; macro or a special form (see "Macro Expansion" below) is
@@ -322,26 +317,16 @@ a-variable => aVariable
;;; the normal JavaScript function call representation, with the
;;; arguments given in paren after the function name.
-(blorg 1 2) => blorg(1, 2)
+(blorg 1 2) => blorg(1, 2);
(foobar (blorg 1 2) (blabla 3 4) (array 2 3 4))
- => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ])
-
-((aref foo i) 1 2) => foo[i](1, 2)
+=> foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]);
-;;; A method call is a function call where the function name is a
-;;; symbol and begins with a "." . In a method call, the name of the
-;;; function is append to its first argument, thus reflecting the
-;;; method call syntax of JavaScript. Please note that most method
-;;; calls can be abbreviated using the "." trick in symbol names (see
-;;; "Symbol Conversion" above).
+((slot-value this 'blorg) 1 2) => this.blorg(1, 2);
-(.blorg this 1 2) => this.blorg(1, 2)
+((aref foo i) 1 2) => foo[i](1, 2);
-(this.blorg 1 2) => this.blorg(1, 2)
-
-(.blorg (aref foobar 1) NIL T)
- => foobar[1].blorg(null, true)
+((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true);
;;;# Operator Expressions
;;;t \index{operator}
@@ -358,59 +343,47 @@ a-variable => aVariable
; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL,
; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR.
; single-operator ::= one of INCF, DECF, ++, --, NOT, !
-; argument ::= a ParenScript expression
+; argument ::= a Parenscript expression
;;; Operator forms are similar to function call forms, but have an
;;; operator as function name.
;;;
;;; Please note that `=' is converted to `==' in JavaScript. The `='
-;;; ParenScript operator is not the assignment operator. Unlike
-;;; JavaScript, ParenScript supports multiple arguments to the
-;;; operators.
-
-(* 1 2) => 1 * 2
+;;; Parenscript operator is not the assignment operator.
-(= 1 2) => 1 == 2
+(* 1 2) => 1 * 2;
-(eql 1 2) => 1 == 2
+(= 1 2) => 1 == 2;
-;;; Note that the resulting expression is correctly parenthized,
+;;; Note that the resulting expression is correctly parenthesized,
;;; according to the JavaScript operator precedence that can be found
;;; in table form at:
- http://www.codehouse.com/javascript/precedence/
+;;; http://www.codehouse.com/javascript/precedence/
(* 1 (+ 2 3 4) 4 (/ 6 7))
- => 1 * (2 + 3 + 4) * 4 * (6 / 7)
+=> 1 * (2 + 3 + 4) * 4 * (6 / 7);
-;;; The pre/post increment and decrement operators are also
+;;; The pre increment and decrement operators are also
;;; available. `INCF' and `DECF' are the pre-incrementing and
-;;; pre-decrementing operators, and `++' and `--' are the
-;;; post-decrementing version of the operators. These operators can
+;;; pre-decrementing operators. These operators can
;;; take only one argument.
-(++ i) => i++
-
-(-- i) => i--
+(incf i) => ++i;
-(incf i) => ++i
-
-(decf i) => --i
+(decf i) => --i;
;;; The `1+' and `1-' operators are shortforms for adding and
;;; substracting 1.
-(1- i) => i - 1
-
-(1+ i) => i + 1
+(1- i) => i - 1;
-;;; The `not' operator actually optimizes the code a bit. If `not' is
-;;; used on another boolean-returning operator, the operator is
-;;; reversed.
+(1+ i) => i + 1;
-(not (< i 2)) => i >= 2
+;;; If `not' is used on another boolean-returning operator, the
+;;; operator is reversed.
-(not (eql i 2)) => i != 2
+(not (< i 2)) => i >= 2;
;;;# Body forms
;;;t \index{body form}
@@ -420,8 +393,8 @@ a-variable => aVariable
; (PROGN {statement}*) in statement context
; (PROGN {expression}*) in expression context
;
-; statement ::= a ParenScript statement
-; expression ::= a ParenScript expression
+; statement ::= a Parenscript statement
+; expression ::= a Parenscript expression
;;; The `PROGN' special form defines a sequence of statements when
;;; used in a statement context, or sequence of expression when used
@@ -432,13 +405,13 @@ a-variable => aVariable
;;; For example, in a statement context:
(progn (blorg i) (blafoo i))
- => blorg(i);
- blafoo(i);
+=> blorg(i);
+ blafoo(i);
;;; In an expression context:
(+ i (progn (blorg i) (blafoo i)))
- => i + (blorg(i), blafoo(i))
+=> i + (blorg(i), blafoo(i));
;;; A `PROGN' form doesn't lead to additional indentation or
;;; additional braces around it's body.
@@ -457,7 +430,7 @@ a-variable => aVariable
;
; name ::= a Lisp Symbol
; argument ::= a Lisp symbol
-; body ::= a list of ParenScript statements
+; body ::= a list of Parenscript statements
;;; As in Lisp, functions are defined using the `DEFUN' form, which
;;; takes a name, a list of arguments, and a function body. An
@@ -465,49 +438,122 @@ a-variable => aVariable
(defun a-function (a b)
(return (+ a b)))
- => function aFunction(a, b) {
+=> function aFunction(a, b) {
return a + b;
- }
+ };
;;; Anonymous functions can be created using the `LAMBDA' form, which
;;; is the same as `DEFUN', but without function name. In fact,
;;; `LAMBDA' creates a `DEFUN' with an empty function name.
(lambda (a b) (return (+ a b)))
- => function (a, b) {
+=> function (a, b) {
return a + b;
- }
+ };
;;;# Assignment
;;;t \index{assignment}
;;;t \index{SETF}
+;;;t \index{PSETF}
+;;;t \index{SETQ}
+;;;t \index{PSETQ}
+;;;t \index{DEFSETF}
;;;t \index{assignment operator}
; (SETF {lhs rhs}*)
+; (PSETF {lhs rhs}*)
;
-; lhs ::= a ParenScript left hand side expression
-; rhs ::= a ParenScript expression
+; lhs ::= a Parenscript left hand side expression
+; rhs ::= a Parenscript expression
-;;; Assignment is done using the `SETF' form, which is transformed
-;;; into a series of assignments using the JavaScript `=' operator.
+; (SETQ {lhs rhs}*)
+; (PSETQ {lhs rhs}*)
+;
+; lhs ::= a Parenscript symbol
+; rhs ::= a Parenscript expression
+
+;;; Assignment is done using the `SETF', `PSETF', `SETQ', and `PSETQ'
+;;; forms, which are transformed into a series of assignments using
+;;; the JavaScript `=' operator.
-(setf a 1) => a = 1
+(setf a 1) => a = 1;
(setf a 2 b 3 c 4 x (+ a b c))
- => a = 2;
- b = 3;
- c = 4;
- x = a + b + c;
+=> a = 2;
+ b = 3;
+ c = 4;
+ x = a + b + c;
;;; The `SETF' form can transform assignments of a variable with an
;;; operator expression using this variable into a more "efficient"
;;; assignment operator form. For example:
-(setf a (1+ a)) => a++
+(setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a;
+
+(setf a (- 1 a)) => a = 1 - a;
+
+;;; The `PSETF' and `PSETQ' forms perform parallel assignment of
+;;; places or variables using a number of temporary variables created
+;;; by `PS-GENSYM'. For example:
+
+(let ((a 1) (b 2))
+ (psetf a b b a))
+=> var a = 1;
+ var b = 2;
+ var _js1 = b;
+ var _js2 = a;
+ a = _js1;
+ b = _js2;
-(setf a (* 2 3 4 a 4 a)) => a *= 2 * 3 * 4 * 4 * a
+;;; The `SETQ' and `PSETQ' forms operate identically to `SETF' and
+;;; `PSETF', but throw a compile-time error if the left-hand side form
+;;; is not a symbol. For example:
-(setf a (- 1 a)) => a = 1 - a
+(setq a 1) => a = 1;
+
+;; but...
+
+(setq (aref a 0) 1)
+;; => ERROR: The value (AREF A 0) is not of type SYMBOL.
+
+;;; New types of setf places can be defined in one of two ways: using
+;;; `DEFSETF' or using `DEFUN' with a setf function name; both are
+;;; analogous to their Common Lisp counterparts.
+
+;;; `DEFSETF' supports both long and short forms, while `DEFUN' of a
+;;; setf place generates a JavaScript function name with the __setf_
+;;; prefix:
+
+(defun (setf color) (new-color el)
+ (setf (slot-value (slot-value el 'style) 'color) new-color))
+=> function __setf_color(newColor, el) {
+ el.style.color = newColor;
+ };
+
+(setf (color some-div) (+ 23 "em"))
+=> var _js2 = someDiv;
+ var _js1 = 23 + 'em';
+ __setf_color(_js1, _js2);
+
+;;; Note that temporary variables are generated to preserve evaluation
+;;; order of the arguments as they would be in Lisp.
+
+;;; The following example illustrates how setf places can be used to
+;;; provide a uniform protocol for positioning elements in HTML pages:
+
+(defsetf left (el) (offset)
+ `(setf (slot-value (slot-value ,el 'style) 'left) ,offset))
+=> null;
+
+(setf (left some-div) (+ 123 "px"))
+=> var _js2 = someDiv;
+ var _js1 = 123 + 'px';
+ _js2.style.left = _js1;
+
+(macrolet ((left (el)
+ `(slot-value ,el 'offset-left)))
+ (left some-div))
+=> someDiv.offsetLeft;
;;;# Single argument statements
;;;t \index{single-argument statement}
@@ -519,16 +565,16 @@ a-variable => aVariable
; (RETURN {value}?)
; (THROW {value}?)
;
-; value ::= a ParenScript expression
+; value ::= a Parenscript expression
;;; The single argument statements `return' and `throw' are generated
;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a
;;; `TRY' form. `RETURN' is used to return a value from a function
;;; call.
-(return 1) => return 1
+(return 1) => return 1;
-(throw "foobar") => throw 'foobar'
+(throw "foobar") => throw 'foobar';
;;;# Single argument expression
;;;t \index{single-argument expression}
@@ -547,23 +593,23 @@ a-variable => aVariable
; (INSTANCEOF {value})
; (NEW {value})
;
-; value ::= a ParenScript expression
+; value ::= a Parenscript expression
;;; The single argument expressions `delete', `void', `typeof',
;;; `instanceof' and `new' are generated by the forms `DELETE',
;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a
-;;; ParenScript expression.
+;;; Parenscript expression.
-(delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4)
+(delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4);
(if (= (typeof blorg) *string)
(alert (+ "blorg is a string: " blorg))
(alert "blorg is not a string"))
- => if (typeof blorg == String) {
+=> if (typeof blorg == String) {
alert('blorg is a string: ' + blorg);
- } else {
+ } else {
alert('blorg is not a string');
- }
+ };
;;;# Conditional Statements
;;;t \index{conditional statements}
@@ -576,11 +622,11 @@ a-variable => aVariable
; (WHEN condition then)
; (UNLESS condition then)
;
-; condition ::= a ParenScript expression
-; then ::= a ParenScript statement in statement context, a
-; ParenScript expression in expression context
-; else ::= a ParenScript statement in statement context, a
-; ParenScript expression in expression context
+; condition ::= a Parenscript expression
+; then ::= a Parenscript statement in statement context, a
+; Parenscript expression in expression context
+; else ::= a Parenscript statement in statement context, a
+; Parenscript expression in expression context
;;; The `IF' form compiles to the `if' javascript construct. An
;;; explicit `PROGN' around the then branch and the else branch is
@@ -588,35 +634,35 @@ a-variable => aVariable
;;; form is used in an expression context, a JavaScript `?', `:'
;;; operator form is generated.
-(if (blorg.is-correct)
+(if ((@ blorg is-correct))
(progn (carry-on) (return i))
(alert "blorg is not correct!"))
- => if (blorg.isCorrect()) {
- carryOn();
- return i;
- } else {
- alert('blorg is not correct!');
- }
+=> if (blorg.isCorrect()) {
+ carryOn();
+ return i;
+ } else {
+ alert('blorg is not correct!');
+ };
-(+ i (if (blorg.add-one) 1 2))
- => i + (blorg.addOne() ? 1 : 2)
+(+ i (if ((@ blorg add-one)) 1 2))
+=> i + (blorg.addOne() ? 1 : 2);
;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the
;;; `IF' form.
-(when (blorg.is-correct)
+(when ((@ blorg is-correct))
(carry-on)
(return i))
- => if (blorg.isCorrect()) {
- carryOn();
- return i;
- }
+=> if (blorg.isCorrect()) {
+ carryOn();
+ return i;
+ };
-(unless (blorg.is-correct)
+(unless ((@ blorg is-correct))
(alert "blorg is not correct!"))
- => if (!blorg.isCorrect()) {
- alert('blorg is not correct!');
- }
+=> if (!blorg.isCorrect()) {
+ alert('blorg is not correct!');
+ };
;;;# Variable declaration
;;;t \index{variable}
@@ -624,54 +670,62 @@ a-variable => aVariable
;;;t \index{binding}
;;;t \index{scoping}
;;;t \index{DEFVAR}
+;;;t \index{VAR}
;;;t \index{LET}
+;;;t \index{LET*}
; (DEFVAR var {value}?)
-; (LET ({var | (var value)) body)
+; (VAR var {value}?)
+; (LET ({var | (var value)}*) body)
+; (LET* ({var | (var value)}*) body)
;
; var ::= a Lisp symbol
-; value ::= a ParenScript expression
-; body ::= a list of ParenScript statements
+; value ::= a Parenscript expression
+; body ::= a list of Parenscript statements
-;;; Variables (either local or global) can be declared using the
-;;; `DEFVAR' form, which is similar to its equivalent form in
-;;; Lisp. The `DEFVAR' is converted to "var ... = ..." form in
-;;; JavaScript.
+;;; Parenscript special variables can be declared using the `DEFVAR'
+;;; special form, which is similar to its equivalent form in
+;;; Lisp. Note that the result is undefined if `DEFVAR' is not used as
+;;; a top-level form.
(defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ];
-(if (= i 1)
- (progn (defvar blorg "hallo")
- (alert blorg))
- (progn (defvar blorg "blitzel")
- (alert blorg)))
- => if (i == 1) {
- var blorg = 'hallo';
- alert(blorg);
- } else {
- var blorg = 'blitzel';
- alert(blorg);
- }
-
-;;; A more lispy way to declare local variable is to use the `LET'
-;;; form, which is similar to its Lisp form.
-
-(if (= i 1)
- (let ((blorg "hallo"))
- (alert blorg))
- (let ((blorg "blitzel"))
- (alert blorg)))
- => if (i == 1) {
- var blorg = 'hallo';
- alert(blorg);
- } else {
- var blorg = 'blitzel';
- alert(blorg);
- }
-
-;;; However, beware that scoping in Lisp and JavaScript are quite
-;;; different. For example, don't rely on closures capturing local
-;;; variables in the way you'd think they would.
+;;; One feature present in Parenscript that is not part of Common Lisp
+;;; are lexically-scoped global variables, which are declared using
+;;; the `VAR' special form.
+
+;;; Parenscript provides the `LET' and `LET*' special forms for
+;;; creating new variable bindings. Both special forms implement
+;;; lexical scope by renaming the provided variables via `GENSYM', and
+;;; implement dynamic binding using `TRY'-`FINALY'. Note that
+;;; top-level `LET' and `LET*' forms will create new global variables.
+
+;;; Moreover, beware that scoping rules in Lisp and JavaScript are
+;;; quite different. For example, don't rely on closures capturing
+;;; local variables in the way that you would normally expect.
+
+
+;;; examples:
+
+(progn
+ (defvar *a* 4)
+ (let ((x 1)
+ (*a* 2))
+ (let* ((y (+ x 1))
+ (x (+ x y)))
+ (+ *a* x y))))
+=> var A = 4;
+ var x = 1;
+ var A_TMPSTACK1;
+ try {
+ A_TMPSTACK1 = A;
+ A = 2;
+ var y = x + 1;
+ var x2 = x + y;
+ A + x2 + y;
+ } finally {
+ A = A_TMPSTACK1;
+ };
;;;# Iteration constructs
;;;t \index{iteration}
@@ -683,83 +737,141 @@ a-variable => aVariable
;;;t \index{DO}
;;;t \index{DOTIMES}
;;;t \index{DOLIST}
-;;;t \index{DOEACH}
+;;;t \index{FOR-IN}
;;;t \index{WHILE}
-; (DO ({var | (var {init}? {step}?)}*) (end-test) body)
-; (DOTIMES (var numeric-form) body)
-; (DOLIST (var list-form) body)
-; (DOEACH (var object) body)
+; (DO ({var | (var {init}? {step}?)}*) (end-test {result}?) body)
+; (DO* ({var | (var {init}? {step}?)}*) (end-test {result}?) body)
+; (DOTIMES (var numeric-form {result}?) body)
+; (DOLIST (var list-form {result}?) body)
+; (FOR-IN (var object) body)
; (WHILE end-test body)
;
; var ::= a Lisp symbol
-; numeric-form ::= a ParenScript expression resulting in a number
-; list-form ::= a ParenScript expression resulting in an array
-; object ::= a ParenScript expression resulting in an object
-; init ::= a ParenScript expression
-; step ::= a ParenScript expression
-; end-test ::= a ParenScript expression
-; body ::= a list of ParenScript statements
-
-;;; The `DO' form, which is similar to its Lisp form, is transformed
-;;; into a JavaScript `for' statement. Note that the ParenScript `DO'
-;;; form does not have a return value, that is because `for' is a
-;;; statement and not an expression in JavaScript.
+; numeric-form ::= a Parenscript expression resulting in a number
+; list-form ::= a Parenscript expression resulting in an array
+; object-form ::= a Parenscript expression resulting in an object
+; init ::= a Parenscript expression
+; step ::= a Parenscript expression
+; end-test ::= a Parenscript expression
+; result ::= a Parenscript expression
+; body ::= a list of Parenscript statements
+
+;;; All interation special forms are transformed into JavaScript `for'
+;;; statements and, if needed, lambda expressions.
+
+;;; `DO', `DO*', and `DOTIMES' carry the same semantics as their
+;;; Common Lisp equivalents.
+
+;;; `DO*' (note the variety of possible init-forms:
+
+(do* ((a) b (c (array "a" "b" "c" "d" "e"))
+ (d 0 (1+ d))
+ (e (aref c d) (aref c d)))
+ ((or (= d (@ c length)) (== e "x")))
+ (setf a d b e)
+ ((@ document write) (+ "a: " a " b: " b "
")))
+=> 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]) {
+ a = d;
+ b = e;
+ document.write('a: ' + a + ' b: ' + b + '
');
+ };
+
+;;; `DO'
(do ((i 0 (1+ i))
- (l (aref blorg i) (aref blorg i)))
- ((or (= i blorg.length)
- (eql l "Fumitastic")))
- (document.write (+ "L is " l)))
- => for (var i = 0, l = blorg[i];
- !(i == blorg.length || l == 'Fumitastic');
- i = i + 1, l = blorg[i]) {
- document.write('L is ' + l);
- }
-
-;;; The `DOTIMES' form, which lets a variable iterate from 0 upto an
-;;; end value, is a shortcut for `DO'.
-
-(dotimes (i blorg.length)
- (document.write (+ "L is " (aref blorg i))))
- => for (var i = 0; i < blorg.length; i = i + 1) {
- document.write('L is ' + blorg[i]);
- }
-
-;;; The `DOLIST' form is a shortcut for iterating over an array. Note
-;;; that this form creates temporary variables using a function called
-;;; `JS-GENSYM', which is similar to its Lisp counterpart `GENSYM'.
-
-(dolist (l blorg)
- (document.write (+ "L is " l)))
- => {
- var tmpArr1 = blorg;
- for (var tmpI2 = 0; tmpI2 < tmpArr1.length;
- tmpI2 = tmpI2 + 1) {
- var l = tmpArr1[tmpI2];
- document.write('L is ' + l);
- }
- }
-
-
-;;; The `DOEACH' form is converted to a `for (var .. in ..)' form in
-;;; JavaScript. It is used to iterate over the enumerable properties
-;;; of an object.
-
-(doeach (i object)
- (document.write (+ i " is " (aref object i))))
- => for (var i in object) {
- document.write(i + ' is ' + object[i]);
- }
+ (s 0 (+ s i (1+ i))))
+ ((> i 10))
+ ((@ document write) (+ "i: " i " s: " s "
")))
+=> var i = 0;
+ var s = 0;
+ for (; i <= 10; ) {
+ document.write('i: ' + i + ' s: ' + s + '
');
+ var _js1 = i + 1;
+ var _js2 = s + i + (i + 1);
+ i = _js1;
+ s = _js2;
+ };
+
+;;; compare to `DO*':
+
+(do* ((i 0 (1+ i))
+ (s 0 (+ s i (1- i))))
+ ((> i 10))
+ ((@ document write) (+ "i: " i " s: " s "
")))
+=> for (var i = 0, s = 0; i <= 10; i += 1, s += i + (i - 1)) {
+ document.write('i: ' + i + ' s: ' + s + '
');
+ };
+
+;;; `DOTIMES':
+
+(let ((arr (array "a" "b" "c" "d" "e")))
+ (dotimes (i (@ arr length))
+ ((@ document write) (+ "i: " i " arr[i]: " (aref arr i) "
"))))
+=> var arr = ['a', 'b', 'c', 'd', 'e'];
+ for (var i = 0; i < arr.length; i += 1) {
+ document.write('i: ' + i + ' arr[i]: ' + arr[i] + '
');
+ };
+
+;;; `DOTIMES' with return value:
+
+(let ((res 0))
+ (alert (+ "Summation to 10 is "
+ (dotimes (i 10 res)
+ (incf res (1+ i))))))
+=> var res = 0;
+ alert('Summation to 10 is ' + (function () {
+ for (var i = 0; i < 10; i += 1) {
+ res += i + 1;
+ };
+ return res;
+ })());
+
+;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS
+;;; arrays/vectors.
+
+(let ((l (list 1 2 4 8 16 32)))
+ (dolist (c l)
+ ((@ document write) (+ "c: " c "
"))))
+=> var l = [1, 2, 4, 8, 16, 32];
+ for (var c = null, _js_arrvar2 = l, _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) {
+ c = _js_arrvar2[_js_idx1];
+ document.write('c: ' + c + '
');
+ };
+
+(let ((l '(1 2 4 8 16 32))
+ (s 0))
+ (alert (+ "Sum of " l " is: "
+ (dolist (c l s)
+ (incf s c)))))
+=> var l = [1, 2, 4, 8, 16, 32];
+ var s = 0;
+ alert('Sum of ' + l + ' is: ' + (function () {
+ for (var c = null, _js_arrvar2 = l, _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) {
+ c = _js_arrvar2[_js_idx1];
+ s += c;
+ };
+ return s;
+ })());
+
+;;; `FOR-IN' is translated to the JS `for...in' statement.
+
+(let ((obj (create a 1 b 2 c 3)))
+ (for-in (i obj)
+ ((@ document write) (+ i ": " (aref obj i) "
"))))
+=> var obj = { a : 1, b : 2, c : 3 };
+ for (var i in obj) {
+ document.write(i + ': ' + obj[i] + '
');
+ };
;;; The `WHILE' form is transformed to the JavaScript form `while',
;;; and loops until a termination test evaluates to false.
-(while (film.is-not-finished)
- (this.eat (new *popcorn)))
- => while (film.isNotFinished()) {
- this.eat(new Popcorn);
- }
+(while ((@ film is-not-finished))
+ ((@ this eat) (new *popcorn)))
+=> while (film.isNotFinished()) {
+ this.eat(new Popcorn);
+ };
;;;# The `CASE' statement
;;;t \index{CASE}
@@ -769,29 +881,30 @@ a-variable => aVariable
; (CASE case-value clause*)
;
; clause ::= (value body) | ((value*) body) | t-clause
-; case-value ::= a ParenScript expression
-; value ::= a ParenScript expression
+; case-value ::= a Parenscript expression
+; value ::= a Parenscript expression
; t-clause ::= {t | otherwise | default} body
-; body ::= a list of ParenScript statements
+; body ::= a list of Parenscript statements
;;; The Lisp `CASE' form is transformed to a `switch' statement in
;;; JavaScript. Note that `CASE' is not an expression in
-;;; ParenScript.
+;;; Parenscript.
(case (aref blorg i)
((1 "one") (alert "one"))
(2 (alert "two"))
(t (alert "default clause")))
- => switch (blorg[i]) {
- case 1: ;
- case 'one':
- alert('one');
- break;
- case 2:
- alert('two');
- break;
- default: alert('default clause');
- }
+=> switch (blorg[i]) {
+ case 1:
+ case 'one':
+ alert('one');
+ break;
+ case 2:
+ alert('two');
+ break;
+ default:
+ alert('default clause');
+ };
; (SWITCH case-value clause*)
; clause ::= (value body) | (default body)
@@ -804,12 +917,11 @@ a-variable => aVariable
(1 (alert "If I get here"))
(2 (alert "I also get here"))
(default (alert "I always get here")))
- => switch (blorg[i]) {
- case 1: alert('If I get here');
- case 2: alert('I also get here');
- default: alert('I always get here');
- }
-
+=> switch (blorg[i]) {
+ case 1: alert('If I get here');
+ case 2: alert('I also get here');
+ default: alert('I always get here');
+ };
;;;# The `WITH' statement
;;;t \index{WITH}
@@ -818,21 +930,20 @@ a-variable => aVariable
;;;t \index{scoping}
;;;t \index{closure}
-; (WITH (object) body)
+; (WITH object body)
;
-; object ::= a ParenScript expression evaluating to an object
-; body ::= a list of ParenScript statements
+; object ::= a Parenscript expression evaluating to an object
+; body ::= a list of Parenscript statements
;;; The `WITH' form is compiled to a JavaScript `with' statements, and
;;; adds the object `object' as an intermediary scope objects when
;;; executing the body.
-(with ((create :foo "foo" :i "i"))
+(with (create foo "foo" i "i")
(alert (+ "i is now intermediary scoped: " i)))
- => with ({ foo : 'foo',
- i : 'i' }) {
- alert('i is now intermediary scoped: ' + i);
- }
+=> with ({ foo : 'foo', i : 'i' }) {
+ alert('i is now intermediary scoped: ' + i);
+ };
;;;# The `TRY' statement
;;;t \index{TRY}
@@ -843,7 +954,7 @@ a-variable => aVariable
; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?)
;
-; body ::= a list of ParenScript statements
+; body ::= a list of Parenscript statements
; var ::= a Lisp symbol
;;; The `TRY' form is converted to a JavaScript `try' statement, and
@@ -857,199 +968,253 @@ a-variable => aVariable
(alert (+ "an error happened: " error)))
(:finally
(alert "Leaving the try form")))
- => try {
- throw 'i';
- } catch (error) {
- alert('an error happened: ' + error);
- } finally {
- alert('Leaving the try form');
- }
+=> try {
+ throw 'i';
+ } catch (error) {
+ alert('an error happened: ' + error);
+ } finally {
+ alert('Leaving the try form');
+ };
;;;# The HTML Generator
-;;;t \index{HTML}
+;;;t \index{PS-HTML}
;;;t \index{HTML generation}
-;;;t \index{CSS}
-;;;t \index{CSS generation}
-
-; (HTML html-expression)
+; (PS-HTML html-expression)
-;;; The HTML generator of ParenScript is very similar to the HTML
-;;; generator included in AllegroServe. It accepts the same input
-;;; forms as the AllegroServer HTML generator. However, non-HTML
-;;; construct are compiled to JavaScript by the ParenScript
+;;; The HTML generator of Parenscript is very similar to the htmlgen
+;;; HTML generator library included with AllegroServe. It accepts the
+;;; same input forms as the AllegroServer HTML generator. However,
+;;; non-HTML construct are compiled to JavaScript by the Parenscript
;;; compiler. The resulting expression is a JavaScript expression.
-(html ((:a :href "foobar") "blorg"))
- => 'blorg'
-
-(html ((:a :href (generate-a-link)) "blorg"))
- => 'blorg'
-
-;;; We can recursively call the JS compiler in a HTML expression.
+(ps-html ((:a :href "foobar") "blorg"))
+=> 'blorg';
-(document.write
- (html ((:a :href "#"
- :onclick (js-inline (transport))) "link")))
- => document.write
- ('link')
+(ps-html ((:a :href (generate-a-link)) "blorg"))
+=> 'blorg';
-; (CSS-INLINE css-expression)
+;;; We can recursively call the Parenscript compiler in an HTML
+;;; expression.
-;;; Stylesheets can also be created in ParenScript.
+((@ document write)
+ (ps-html ((:a :href "#"
+ :onclick (ps-inline (transport))) "link")))
+=> document.write('link');
-(css-inline :color "red"
- :font-size "x-small")
- => 'color:red;font-size:x-small'
+;;; Forms may be used in attribute lists to conditionally generate
+;;; the next attribute. In this example the textarea is sometimes disabled.
-(defun make-color-div(color-name)
- (return (html ((:div :style (css-inline :color color-name))
- color-name " looks like this."))))
- => function makeColorDiv(colorName) {
- return '