X-Git-Url: http://git.hcoop.net/clinton/parenscript.git/blobdiff_plain/ed954200c5072db239b261e1e1090df16bcbf238..fc772f726fef4f366bf0a2529db348a554a092fa:/docs/reference.lisp diff --git a/docs/reference.lisp b/docs/reference.lisp index 530baaf..173e70b 100644 --- a/docs/reference.lisp +++ b/docs/reference.lisp @@ -26,14 +26,14 @@ ;;; 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} @@ -45,37 +45,25 @@ ;;; "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} @@ -85,17 +73,16 @@ foobar.slot ;;; and should not be used as variable names. ! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || *= -/= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ ABSTRACT AND AREF ARRAY +/= %= += -= <<= >>= >>>= &= ^= |= 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* LEXICAL-LET -LEXICAL-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 +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} @@ -110,13 +97,13 @@ WHEN WHILE WITH WITH-SLOTS ;;; 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} @@ -126,14 +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'; ;;; Special characters such as newline and backspace are converted ;;; into their corresponding JavaScript escape sequences. -" " => '\\t\\b' +" " => '\\t'; ;;;## Array literals ;;;t \index{array} @@ -152,26 +139,26 @@ WHEN WHILE WITH WITH-SLOTS ;;; 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 @@ -182,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} @@ -203,24 +191,25 @@ WHEN WHILE WITH WITH-SLOTS ;;; 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; -;;; 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 @@ -246,13 +235,13 @@ 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} @@ -270,26 +259,26 @@ an-object.foo => anObject.foo ;;; 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} @@ -303,13 +292,11 @@ THIS => this ;;; 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} @@ -318,10 +305,9 @@ 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 @@ -331,25 +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 ]) - -((slot-value this 'blorg) 1 2) => this.blorg(1, 2) - -((aref foo i) 1 2) => foo[i](1, 2) +=> foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]); -((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true) +((slot-value this 'blorg) 1 2) => this.blorg(1, 2); -;;; Note that while most method calls can be abbreviated using the "." -;;; trick in symbol names (see "Symbol Conversion" above), this is not -;;; advised due to the fact that "object.function" is treated as a -;;; symbol distinct from both "object" and "function," which will -;;; cause problems if Parenscript package prefixes or package -;;; obfuscation is used. +((aref foo i) 1 2) => foo[i](1, 2); -(this.blorg 1 2) => this.blorg(1, 2) +((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true); ;;;# Operator Expressions ;;;t \index{operator} @@ -372,15 +349,11 @@ a-variable => aVariable ;;; 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. +;;; Parenscript operator is not the assignment operator. -(* 1 2) => 1 * 2 +(* 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 @@ -389,31 +362,28 @@ a-variable => aVariable ;;; 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; -;;; 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} @@ -441,7 +411,7 @@ a-variable => aVariable ;;; 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. @@ -470,7 +440,7 @@ a-variable => aVariable (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, @@ -479,7 +449,7 @@ a-variable => aVariable (lambda (a b) (return (+ a b))) => function (a, b) { return a + b; - } + }; ;;;# Assignment ;;;t \index{assignment} @@ -526,7 +496,7 @@ a-variable => aVariable ;;; places or variables using a number of temporary variables created ;;; by `PS-GENSYM'. For example: -(let* ((a 1) (b 2)) +(let ((a 1) (b 2)) (psetf a b b a)) => var a = 1; var b = 2; @@ -573,16 +543,16 @@ a-variable => aVariable (defsetf left (el) (offset) `(setf (slot-value (slot-value ,el 'style) 'left) ,offset)) -=> null +=> null; (setf (left some-div) (+ 123 "px")) => 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 @@ -602,9 +572,9 @@ a-variable => aVariable ;;; `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} @@ -630,7 +600,7 @@ a-variable => aVariable ;;; `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)) @@ -639,7 +609,7 @@ a-variable => aVariable alert('blorg is a string: ' + blorg); } else { alert('blorg is not a string'); - } + }; ;;;# Conditional Statements ;;;t \index{conditional statements} @@ -664,7 +634,7 @@ 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()) { @@ -672,27 +642,27 @@ a-variable => aVariable 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; - } + }; -(unless (blorg.is-correct) +(unless ((@ blorg is-correct)) (alert "blorg is not correct!")) => if (!blorg.isCorrect()) { alert('blorg is not correct!'); - } + }; ;;;# Variable declaration ;;;t \index{variable} @@ -703,15 +673,11 @@ a-variable => aVariable ;;;t \index{VAR} ;;;t \index{LET} ;;;t \index{LET*} -;;;t \index{LEXICAL-LET} -;;;t \index{LEXICAL-LET*} ; (DEFVAR var {value}?) ; (VAR var {value}?) ; (LET ({var | (var value)}*) body) ; (LET* ({var | (var value)}*) body) -; (LEXICAL-LET ({var | (var value)}*) body) -; (LEXICAL-LET* ({var | (var value)}*) body) ; ; var ::= a Lisp symbol ; value ::= a Parenscript expression @@ -722,78 +688,44 @@ a-variable => aVariable ;;; 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 ;;; the `VAR' special form. -;;; Parenscript provides two versions of the `LET' and `LET*' special -;;; forms for manipulating local variables: `SIMPLE-LET' / -;;; `SIMPLE-LET*' and `LEXICAL-LET' / `LEXICAL-LET*'. By default, -;;; `LET' and `LET*' are aliased to `SIMPLE-LET' and `SIMPLE-LET*', -;;; respectively. +;;; 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. -;;; `SIMPLE-LET' and `SIMPLE-LET*' bind their variable lists using -;;; simple JavaScript assignment. This means that you cannot rely on -;;; the bindings going out of scope at the end of the form. - -;;; `LEXICAL-LET' and `LEXICAL-LET*' actually introduce new lexical -;;; environments for the variable bindings by creating anonymous -;;; functions. +;;; 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. -;;; As you would expect, `SIMPLE-LET' and `LEXICAL-LET' do parallel -;;; binding of their variable lists, while `SIMPLE-LET*' and -;;; `LEXICAL-LET*' bind their variable lists sequentially. ;;; examples: -(simple-let* ((a 0) (b 1)) - (alert (+ a b))) -=> var a = 0; - var b = 1; - alert(a + b); - -(simple-let* ((a "World") (b "Hello")) - (simple-let ((a b) (b a)) - (alert (+ a b)))) -=> var a = 'World'; - var b = 'Hello'; - var _js_a1 = b; - var _js_b2 = a; - var a = _js_a1; - var b = _js_b2; - delete _js_a1; - delete _js_b2; - alert(a + b); - -(simple-let* ((a 0) (b 1)) - (lexical-let* ((a 9) (b 8)) - (alert (+ a b))) - (alert (+ a b))) -=> var a = 0; - var b = 1; - (function () { - var a = 9; - var b = 8; - alert(a + b); - })(); - alert(a + b); - -(simple-let* ((a "World") (b "Hello")) - (lexical-let ((a b) (b a)) - (alert (+ a b))) - (alert (+ a b))) -=> var a = 'World'; - var b = 'Hello'; - (function (a, b) { - alert(a + b); - })(b, a); - alert(a + b); - -;;; 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. +(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} @@ -805,14 +737,14 @@ 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 {result}?) body) ; (DO* ({var | (var {init}? {step}?)}*) (end-test {result}?) body) ; (DOTIMES (var numeric-form {result}?) body) ; (DOLIST (var list-form {result}?) body) -; (DOEACH ({var | (key value)} object-form {result}?) body) +; (FOR-IN (var object) body) ; (WHILE end-test body) ; ; var ::= a Lisp symbol @@ -836,33 +768,29 @@ a-variable => aVariable (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 "
"))) + ((@ 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' (note the parallel assignment): +;;; `DO' (do ((i 0 (1+ i)) (s 0 (+ s i (1+ i)))) ((> i 10)) - (document.write (+ "i: " i " s: " s "
"))) -=> var _js_i1 = 0; - var _js_s2 = 0; - var i = _js_i1; - var s = _js_s2; - delete _js_i1; - delete _js_s2; + ((@ document write) (+ "i: " i " s: " s "
"))) +=> var i = 0; + var s = 0; for (; i <= 10; ) { document.write('i: ' + i + ' s: ' + s + '
'); - var _js3 = i + 1; - var _js4 = s + i + (i + 1); - i = _js3; - s = _js4; + var _js1 = i + 1; + var _js2 = s + i + (i + 1); + i = _js1; + s = _js2; }; ;;; compare to `DO*': @@ -870,16 +798,16 @@ a-variable => aVariable (do* ((i 0 (1+ i)) (s 0 (+ s i (1- i)))) ((> i 10)) - (document.write (+ "i: " i " s: " s "
"))) + ((@ 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) "
")))) +(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] + '
'); @@ -887,7 +815,7 @@ a-variable => aVariable ;;; `DOTIMES' with return value: -(let* ((res 0)) +(let ((res 0)) (alert (+ "Summation to 10 is " (dotimes (i 10 res) (incf res (1+ i)))))) @@ -902,17 +830,17 @@ a-variable => aVariable ;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS ;;; arrays/vectors. -(let* ((l (list 1 2 4 8 16 32))) +(let ((l (list 1 2 4 8 16 32))) (dolist (c l) - (document.write (+ "c: " c "
")))) + ((@ 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 (list 1 2 4 8 16 32)) - (s 0)) +(let ((l '(1 2 4 8 16 32)) + (s 0)) (alert (+ "Sum of " l " is: " (dolist (c l s) (incf s c))))) @@ -926,36 +854,24 @@ a-variable => aVariable return s; })()); -;;; `DOEACH' iterates across the enumerable properties of JS objects, -;;; binding either simply the key of each slot, or alternatively, both -;;; the key and the value. +;;; `FOR-IN' is translated to the JS `for...in' statement. -(let* ((obj (create :a 1 :b 2 :c 3))) - (doeach (i obj) - (document.write (+ i ": " (aref obj i) "
")))) +(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] + '
'); }; -(let* ((obj (create :a 1 :b 2 :c 3))) - (doeach ((k v) obj) - (document.write (+ k ": " v "
")))) -=> var obj = { a : 1, b : 2, c : 3 }; - var v; - for (var k in obj) { - v = obj[k]; - document.write(k + ': ' + v + '
'); - }; - ;;; 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 is-not-finished)) + ((@ this eat) (new *popcorn))) => while (film.isNotFinished()) { this.eat(new Popcorn); - } + }; ;;;# The `CASE' statement ;;;t \index{CASE} @@ -988,7 +904,7 @@ a-variable => aVariable break; default: alert('default clause'); - } + }; ; (SWITCH case-value clause*) ; clause ::= (value body) | (default body) @@ -1005,7 +921,7 @@ a-variable => aVariable 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} @@ -1023,11 +939,11 @@ a-variable => aVariable ;;; 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} @@ -1058,7 +974,7 @@ a-variable => aVariable alert('an error happened: ' + error); } finally { alert('Leaving the try form'); - } + }; ;;;# The HTML Generator ;;;t \index{PS-HTML} @@ -1073,25 +989,25 @@ a-variable => aVariable ;;; compiler. The resulting expression is a JavaScript expression. (ps-html ((:a :href "foobar") "blorg")) -=> 'blorg' +=> 'blorg'; (ps-html ((:a :href (generate-a-link)) "blorg")) -=> 'blorg' +=> 'blorg'; ;;; We can recursively call the Parenscript compiler in an HTML ;;; expression. -(document.write +((@ document write) (ps-html ((:a :href "#" :onclick (ps-inline (transport))) "link"))) -=> document.write('link') +=> document.write('link'); ;;; Forms may be used in attribute lists to conditionally generate ;;; the next attribute. In this example the textarea is sometimes disabled. -(let* ((disabled nil) +(let ((disabled nil) (authorized t)) - (setf element.inner-h-t-m-l + (setf (@ element inner-h-t-m-l) (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled") "Edit me")))) => var disabled = null; @@ -1213,15 +1129,15 @@ a-variable => aVariable ;;; a particular package receive a prefix when translated to ;;; JavaScript with the `PS-PACKAGE-PREFIX' place. -(defpackage "MY-LIBRARY" - (:use #:parenscript)) -(setf (ps-package-prefix :my-library) "my_library_") +(defpackage "PS-REF.MY-LIBRARY" + (:use "PARENSCRIPT")) +(setf (ps-package-prefix "PS-REF.MY-LIBRARY") "my_library_") -(defun my-library::library-function (x y) +(defun ps-ref.my-library::library-function (x y) (return (+ x y))) -> function my_library_libraryFunction(x, y) { return x + y; - } + }; ;;;# Identifier obfuscation ;;;t \index{obfuscation} @@ -1229,18 +1145,30 @@ a-variable => aVariable ;;;t \index{OBFUSCATE-PACKAGE} ;;;t \index{UNOBFUSCATE-PACKAGE} -; (OBFUSCATE-PACKAGE package-designator) +; (OBFUSCATE-PACKAGE package-designator &optional symbol-map) ; (UNOBFUSCATE-PACKAGE package-designator) ;;; Similar to the namespace mechanism, Parenscript provides a -;;; facility to generate obfuscated identifiers in certain Lisp -;;; packages. - -(defpackage "OBFUSCATE-ME") -(obfuscate-package :obfuscate-me) - -(defun obfuscate-me::library-function2 (a b obfuscate-me::foo) - (+ a (my-library::library-function b obfuscate-me::foo))) +;;; facility to generate obfuscated identifiers in specified CL +;;; packages. The function `OBFUSCATE-PACKAGE' may optionally be +;;; passed a hash-table or a closure that maps symbols to their +;;; obfuscated counterparts. By default, the mapping is done using +;;; `PS-GENSYM'. + +(defpackage "PS-REF.OBFUSCATE-ME") +(obfuscate-package "PS-REF.OBFUSCATE-ME" + (let ((code-pt-counter #x8CF0) + (symbol-map (make-hash-table))) + (lambda (symbol) + (or (gethash symbol symbol-map) + (setf (gethash symbol symbol-map) + (make-symbol (string (code-char (incf code-pt-counter))))))))) + +(defun ps-ref.obfuscate-me::a-function (a b ps-ref.obfuscate-me::foo) + (+ 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.