;;; 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;
- }
+ };
;;;# Symbol conversion
;;;t \index{symbol}
;;; "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
+*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* => GLOBALARRAY;
;;;## Reserved Keywords
;;;t \index{keyword}
;;; 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}
;;; Lisp strings are converted into JavaScript literals.
-"foobar" => 'foobar'
+"foobar" => 'foobar';
-"bratzel bub" => 'bratzel bub'
+"bratzel bub" => 'bratzel bub';
;;; Special characters such as newline and backspace are converted
;;; into their corresponding JavaScript escape sequences.
-" " => '\\t'
+" " => '\\t';
;;;## Array literals
;;;t \index{array}
;;; 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
;;; that JavaScript knows of no such thing as an array. Subscripting
;;; 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))
+(create foo "hihi"
+ blorg (array 1 2 3)
+ another-object (create :schtrunz 1))
=> { foo : 'hihi',
blorg : [ 1, 2, 3 ],
- anotherObject : { schtrunz : 1 } }
+ 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;
;;; The convenience macro `@' is provided to make multiple levels of
;;; indirection easy to express
-(@ an-object foo bar) => anObject.foo.bar
+(@ 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
;;; 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}
;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their
;;; JavaScript boolean equivalents `true' and `false'.
-T => true
+T => true;
-FALSE => false
+FALSE => false;
-F => 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}
;;; allows the Parenscript programmer to be flexible, as flexible as
;;; JavaScript itself.
-variable => variable
+variable => variable;
-a-variable => aVariable
+a-variable => aVariable;
-*math => Math
+*math => Math;
;;;# Function calls and method calls
;;;t \index{function}
;;; 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 ])
+=> foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]);
-((slot-value this 'blorg) 1 2) => this.blorg(1, 2)
+((slot-value this 'blorg) 1 2) => this.blorg(1, 2);
-((aref foo i) 1 2) => foo[i](1, 2)
+((aref foo i) 1 2) => foo[i](1, 2);
-((slot-value (aref foobar 1) 'blorg) 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}
;;; 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 parenthesized,
;;; according to the JavaScript operator precedence that can be found
;;; 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 increment and decrement operators are also
;;; available. `INCF' and `DECF' are the pre-incrementing and
;;; pre-decrementing operators. These operators can
;;; take only one argument.
-(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
+(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.
+;;; If `not' is used on another boolean-returning operator, the
+;;; operator is reversed.
-(not (< i 2)) => i >= 2
-
-(not (eql i 2)) => i != 2
+(not (< i 2)) => i >= 2;
;;;# Body forms
;;;t \index{body form}
;;; 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.
(return (+ 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 (a b) (return (+ a b)))
=> function (a, b) {
return a + b;
- }
+ };
;;;# Assignment
;;;t \index{assignment}
(let ((a 1) (b 2))
(psetf a b b a))
-=> var a1 = 1;
- var b2 = 2;
- var _js3_5 = b2;
- var _js4_6 = a1;
- a1 = _js3_5;
- b2 = _js4_6;
+=> var a = 1;
+ var b = 2;
+ var _js1 = b;
+ var _js2 = a;
+ a = _js1;
+ b = _js2;
;;; The `SETQ' and `PSETQ' forms operate identically to `SETF' and
;;; `PSETF', but throw a compile-time error if the left-hand side form
};
(setf (color some-div) (+ 23 "em"))
-=> var _js2_3 = someDiv;
- var _js1_4 = 23 + 'em';
- __setf_color(_js1_4, _js2_3);
+=> 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.
(defsetf left (el) (offset)
`(setf (slot-value (slot-value ,el 'style) 'left) ,offset))
-=> null
+=> null;
(setf (left some-div) (+ 123 "px"))
-=> var _js2_3 = someDiv;
- var _js1_4 = 123 + 'px';
- _js2_3.style.left = _js1_4;
+=> var _js2 = someDiv;
+ var _js1 = 123 + 'px';
+ _js2.style.left = _js1;
-(progn (defmacro left (el)
- `(slot-value ,el 'offset-left))
- (left some-div))
+(macrolet ((left (el)
+ `(slot-value ,el 'offset-left)))
+ (left some-div))
=> someDiv.offsetLeft;
;;;# Single argument statements
;;; `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}
;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a
;;; 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 a string: ' + blorg);
} else {
alert('blorg is not a string');
- }
+ };
;;;# Conditional Statements
;;;t \index{conditional statements}
return i;
} else {
alert('blorg is not correct!');
- }
+ };
(+ i (if ((@ blorg add-one)) 1 2))
-=> i + (blorg.addOne() ? 1 : 2)
+=> i + (blorg.addOne() ? 1 : 2);
;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the
;;; `IF' form.
=> if (blorg.isCorrect()) {
carryOn();
return i;
- }
+ };
(unless ((@ blorg is-correct))
(alert "blorg is not correct!"))
=> if (!blorg.isCorrect()) {
alert('blorg is not correct!');
- }
+ };
;;;# Variable declaration
;;;t \index{variable}
;;; 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 ]
+(defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ];
;;; One feature present in Parenscript that is not part of Common Lisp
;;; are lexically-scoped global variables, which are declared using
(x (+ x y)))
(+ *a* x y))))
=> var A = 4;
- var x1 = 1;
- var A2;
+ var x = 1;
+ var A_TMPSTACK1;
try {
- A2 = A;
+ A_TMPSTACK1 = A;
A = 2;
- var y3 = x1 + 1;
- var x4 = x1 + y3;
- A + x4 + y3;
+ var y = x + 1;
+ var x2 = x + y;
+ A + x2 + y;
} finally {
- A = A2;
+ A = A_TMPSTACK1;
};
;;;# Iteration constructs
(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)) (eql e "x")))
+ ((or (= d (@ c length)) (== e "x")))
(setf a d b e)
((@ document write) (+ "a: " a " b: " b "<br/>")))
=> 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]) {
(s 0 (+ s i (1+ i))))
((> i 10))
((@ document write) (+ "i: " i " s: " s "<br/>")))
-=> var i1 = 0;
- var s2 = 0;
- for (; i1 <= 10; ) {
- document.write('i: ' + i1 + ' s: ' + s2 + '<br/>');
- var _js3_5 = i1 + 1;
- var _js4_6 = s2 + i1 + (i1 + 1);
- i1 = _js3_5;
- s2 = _js4_6;
+=> var i = 0;
+ var s = 0;
+ for (; i <= 10; ) {
+ document.write('i: ' + i + ' s: ' + s + '<br/>');
+ var _js1 = i + 1;
+ var _js2 = s + i + (i + 1);
+ i = _js1;
+ s = _js2;
};
;;; compare to `DO*':
(let ((arr (array "a" "b" "c" "d" "e")))
(dotimes (i (@ arr length))
((@ document write) (+ "i: " i " arr[i]: " (aref arr i) "<br/>"))))
-=> var arr1 = ['a', 'b', 'c', 'd', 'e'];
- for (var i = 0; i < arr1.length; i += 1) {
- document.write('i: ' + i + ' arr[i]: ' + arr1[i] + '<br/>');
+=> var arr = ['a', 'b', 'c', 'd', 'e'];
+ for (var i = 0; i < arr.length; i += 1) {
+ document.write('i: ' + i + ' arr[i]: ' + arr[i] + '<br/>');
};
;;; `DOTIMES' with return value:
(alert (+ "Summation to 10 is "
(dotimes (i 10 res)
(incf res (1+ i))))))
-=> var res1 = 0;
+=> var res = 0;
alert('Summation to 10 is ' + (function () {
for (var i = 0; i < 10; i += 1) {
- res1 += i + 1;
+ res += i + 1;
};
- return res1;
+ return res;
})());
;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS
(let ((l (list 1 2 4 8 16 32)))
(dolist (c l)
((@ document write) (+ "c: " c "<br/>"))))
-=> var l1 = [1, 2, 4, 8, 16, 32];
- for (var c = null, _js_arrvar3 = l1, _js_idx2 = 0; _js_idx2 < _js_arrvar3.length; _js_idx2 += 1) {
- c = _js_arrvar3[_js_idx2];
+=> 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 + '<br/>');
};
(alert (+ "Sum of " l " is: "
(dolist (c l s)
(incf s c)))))
-=> var l1 = [1, 2, 4, 8, 16, 32];
- var s2 = 0;
- alert('Sum of ' + l1 + ' is: ' + (function () {
- for (var c = null, _js_arrvar4 = l1, _js_idx3 = 0; _js_idx3 < _js_arrvar4.length; _js_idx3 += 1) {
- c = _js_arrvar4[_js_idx3];
- s2 += 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 s2;
+ return s;
})());
;;; `FOR-IN' is translated to the JS `for...in' statement.
-(let ((obj (create :a 1 :b 2 :c 3)))
+(let ((obj (create a 1 b 2 c 3)))
(for-in (i obj)
((@ document write) (+ i ": " (aref obj i) "<br/>"))))
-=> var obj1 = { a : 1, b : 2, c : 3 };
- for (var i in obj1) {
- document.write(i + ': ' + obj1[i] + '<br/>');
+=> var obj = { a : 1, b : 2, c : 3 };
+ for (var i in obj) {
+ document.write(i + ': ' + obj[i] + '<br/>');
};
;;; The `WHILE' form is transformed to the JavaScript form `while',
((@ this eat) (new *popcorn)))
=> while (film.isNotFinished()) {
this.eat(new Popcorn);
- }
+ };
;;;# The `CASE' statement
;;;t \index{CASE}
break;
default:
alert('default clause');
- }
+ };
; (SWITCH case-value clause*)
; clause ::= (value body) | (default body)
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}
;;; 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);
- }
+ };
;;;# The `TRY' statement
;;;t \index{TRY}
alert('an error happened: ' + error);
} finally {
alert('Leaving the try form');
- }
+ };
;;;# The HTML Generator
;;;t \index{PS-HTML}
;;; compiler. The resulting expression is a JavaScript expression.
(ps-html ((:a :href "foobar") "blorg"))
-=> '<A HREF=\"foobar\">blorg</A>'
+=> '<A HREF=\"foobar\">blorg</A>';
(ps-html ((:a :href (generate-a-link)) "blorg"))
-=> '<A HREF=\"' + generateALink() + '\">blorg</A>'
+=> '<A HREF=\"' + generateALink() + '\">blorg</A>';
;;; We can recursively call the Parenscript compiler in an HTML
;;; expression.
((@ document write)
(ps-html ((:a :href "#"
:onclick (ps-inline (transport))) "link")))
-=> document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport' + '(' + ')') + '\">link</A>')
+=> document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport' + '(' + ')') + '\">link</A>');
;;; Forms may be used in attribute lists to conditionally generate
;;; the next attribute. In this example the textarea is sometimes disabled.
(setf (@ element inner-h-t-m-l)
(ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled")
"Edit me"))))
-=> var disabled1 = null;
- var authorized2 = true;
+=> var disabled = null;
+ var authorized = true;
element.innerHTML =
'<TEXTAREA'
- + (disabled1 || !authorized2 ? ' DISABLED=\"' + 'disabled' + '\"' : '')
+ + (disabled || !authorized ? ' DISABLED=\"' + 'disabled' + '\"' : '')
+ '>Edit me</TEXTAREA>';
;;;# Macrology
(return (+ x y)))
-> function my_library_libraryFunction(x, y) {
return x + y;
- }
+ };
;;;# Identifier obfuscation
;;;t \index{obfuscation}
(+ a (ps-ref.my-library::library-function b ps-ref.obfuscate-me::foo)))
-> function 賱(a, b, 賲) {
a + my_library_libraryFunction(b, 賲);
- }
+ };
;;; The obfuscation and namespace facilities can be used on packages
;;; at the same time.