Commit | Line | Data |
---|---|---|
f037cd4c | 1 | ;;;# Parenscript Language Reference |
8e198a08 | 2 | |
d777a405 TC |
3 | ;;; Create a useful package for the code here... |
4 | (in-package #:cl-user) | |
5 | (defpackage #:ps-ref (:use #:ps)) | |
6 | (in-package #:ps-ref) | |
7 | ||
f037cd4c | 8 | ;;; This chapters describes the core constructs of Parenscript, as |
8e198a08 | 9 | ;;; well as its compilation model. This chapter is aimed to be a |
f037cd4c VS |
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. | |
8e198a08 MB |
13 | |
14 | ;;;# Statements and Expressions | |
15 | ;;;t \index{statement} | |
16 | ;;;t \index{expression} | |
17 | ||
18 | ;;; In contrast to Lisp, where everything is an expression, JavaScript | |
19 | ;;; makes the difference between an expression, which evaluates to a | |
20 | ;;; value, and a statement, which has no value. Examples for | |
21 | ;;; JavaScript statements are `for', `with' and `while'. Most | |
f037cd4c | 22 | ;;; Parenscript forms are expression, but certain special forms are |
8e198a08 | 23 | ;;; not (the forms which are transformed to a JavaScript |
f037cd4c | 24 | ;;; statement). All Parenscript expressions are statements |
8e198a08 MB |
25 | ;;; though. Certain forms, like `IF' and `PROGN', generate different |
26 | ;;; JavaScript constructs whether they are used in an expression | |
27 | ;;; context or a statement context. For example: | |
28 | ||
5a69278c | 29 | (+ i (if 1 2 3)) => i + (1 ? 2 : 3); |
8e198a08 MB |
30 | |
31 | (if 1 2 3) | |
31c5dbde TC |
32 | => if (1) { |
33 | 2; | |
34 | } else { | |
35 | 3; | |
5a69278c | 36 | }; |
8e198a08 MB |
37 | |
38 | ;;;# Symbol conversion | |
39 | ;;;t \index{symbol} | |
40 | ;;;t \index{symbol conversion} | |
41 | ||
42 | ;;; Lisp symbols are converted to JavaScript symbols by following a | |
3b238048 | 43 | ;;; few simple rules. Special characters `!', `?', `#', `@', `%', |
8e198a08 | 44 | ;;; '/', `*' and `+' get replaced by their written-out equivalents |
3b238048 HH |
45 | ;;; "bang", "what", "hash", "at", "percent", "slash", |
46 | ;;; "start" and "plus" respectively. The `$' character is untouched. | |
8e198a08 | 47 | |
5a69278c | 48 | !?#@% => bangwhathashatpercent; |
8e198a08 MB |
49 | |
50 | ;;; The `-' is an indication that the following character should be | |
51 | ;;; converted to uppercase. Thus, `-' separated symbols are converted | |
52 | ;;; to camelcase. The `_' character however is left untouched. | |
53 | ||
5a69278c | 54 | bla-foo-bar => blaFooBar; |
8e198a08 MB |
55 | |
56 | ;;; If you want a JavaScript symbol beginning with an uppercase, you | |
57 | ;;; can either use a leading `-', which can be misleading in a | |
58 | ;;; mathematical context, or a leading `*'. | |
59 | ||
5a69278c | 60 | *array => Array; |
8e198a08 | 61 | |
8e198a08 MB |
62 | ;;; A symbol beggining and ending with `+' or `*' is converted to all |
63 | ;;; uppercase, to signify that this is a constant or a global | |
64 | ;;; variable. | |
65 | ||
5a69278c | 66 | *global-array* => GLOBALARRAY; |
8e198a08 | 67 | |
8e198a08 MB |
68 | ;;;## Reserved Keywords |
69 | ;;;t \index{keyword} | |
70 | ;;;t \index{reserved keywords} | |
71 | ||
f037cd4c | 72 | ;;; The following keywords and symbols are reserved in Parenscript, |
8e198a08 MB |
73 | ;;; and should not be used as variable names. |
74 | ||
58c4ef4f | 75 | ! ~ ++ -- * / % + - << >> >>> < > <= >= == != ==== !== & ^ | && || *= |
5ffb1eba | 76 | /= %= += -= <<= >>= >>>= &= ^= |= 1- 1+ @ ABSTRACT AND AREF ARRAY |
58c4ef4f | 77 | BOOLEAN BREAK BYTE CASE CATCH CC-IF CHAR CLASS COMMA CONST CONTINUE |
d777a405 TC |
78 | CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO* DOEACH DOLIST |
79 | DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL FINALLY | |
80 | FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS IMPORT IN INCF | |
5ffb1eba VS |
81 | INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA LET LET* LISP LIST LONG |
82 | MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE PRIVATE PROGN PROTECTED | |
83 | PUBLIC RANDOM REGEX RETURN SETF SHORT SLOT-VALUE STATIC SUPER SWITCH | |
84 | SYMBOL-MACROLET SYNCHRONIZED T THIS THROW THROWS TRANSIENT TRY TYPEOF | |
85 | UNDEFINED UNLESS VAR VOID VOLATILE WHEN WHILE WITH WITH-SLOTS | |
8e198a08 MB |
86 | |
87 | ;;;# Literal values | |
88 | ;;;t \index{literal value} | |
89 | ||
90 | ;;;## Number literals | |
91 | ;;;t \index{number} | |
92 | ;;;t \index{number literal} | |
93 | ||
94 | ; number ::= a Lisp number | |
95 | ||
96 | ;;; | |
f037cd4c | 97 | ;;; Parenscript supports the standard JavaScript literal |
94a05cdf | 98 | ;;; values. Numbers are compiled into JavaScript numbers. |
8e198a08 | 99 | |
5a69278c | 100 | 1 => 1; |
8e198a08 | 101 | |
5a69278c | 102 | 123.123 => 123.123; |
8e198a08 MB |
103 | |
104 | ;;; Note that the base is not conserved between Lisp and JavaScript. | |
105 | ||
5a69278c | 106 | #x10 => 16; |
8e198a08 MB |
107 | |
108 | ;;;## String literals | |
109 | ;;;t \index{string} | |
110 | ;;;t \index{string literal} | |
111 | ||
112 | ; string ::= a Lisp string | |
113 | ||
114 | ;;; Lisp strings are converted into JavaScript literals. | |
115 | ||
5a69278c | 116 | "foobar" => 'foobar'; |
7a7d6c73 | 117 | |
5a69278c | 118 | "bratzel bub" => 'bratzel bub'; |
8e198a08 | 119 | |
ed954200 VS |
120 | ;;; Special characters such as newline and backspace are converted |
121 | ;;; into their corresponding JavaScript escape sequences. | |
122 | ||
5a69278c | 123 | " " => '\\t'; |
8e198a08 MB |
124 | |
125 | ;;;## Array literals | |
126 | ;;;t \index{array} | |
127 | ;;;t \index{ARRAY} | |
128 | ;;;t \index{MAKE-ARRAY} | |
129 | ;;;t \index{AREF} | |
130 | ;;;t \index{array literal} | |
131 | ||
132 | ; (ARRAY {values}*) | |
133 | ; (MAKE-ARRAY {values}*) | |
134 | ; (AREF array index) | |
135 | ; | |
f037cd4c VS |
136 | ; values ::= a Parenscript expression |
137 | ; array ::= a Parenscript expression | |
138 | ; index ::= a Parenscript expression | |
8e198a08 MB |
139 | |
140 | ;;; Array literals can be created using the `ARRAY' form. | |
141 | ||
5a69278c | 142 | (array) => [ ]; |
8e198a08 | 143 | |
5a69278c | 144 | (array 1 2 3) => [ 1, 2, 3 ]; |
8e198a08 MB |
145 | |
146 | (array (array 2 3) | |
147 | (array "foobar" "bratzel bub")) | |
5a69278c | 148 | => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ]; |
8e198a08 MB |
149 | |
150 | ;;; Arrays can also be created with a call to the `Array' function | |
151 | ;;; using the `MAKE-ARRAY'. The two forms have the exact same semantic | |
152 | ;;; on the JavaScript side. | |
153 | ||
5a69278c | 154 | (make-array) => new Array(); |
8e198a08 | 155 | |
5a69278c | 156 | (make-array 1 2 3) => new Array(1, 2, 3); |
8e198a08 MB |
157 | |
158 | (make-array | |
159 | (make-array 2 3) | |
160 | (make-array "foobar" "bratzel bub")) | |
5a69278c | 161 | => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub')); |
8e198a08 | 162 | |
f037cd4c | 163 | ;;; Indexing arrays in Parenscript is done using the form `AREF'. Note |
8e198a08 MB |
164 | ;;; that JavaScript knows of no such thing as an array. Subscripting |
165 | ;;; an array is in fact reading a property from an object. So in a | |
166 | ;;; semantic sense, there is no real difference between `AREF' and | |
167 | ;;; `SLOT-VALUE'. | |
168 | ||
169 | ;;;## Object literals | |
170 | ;;;t \index{CREATE} | |
171 | ;;;t \index{SLOT-VALUE} | |
5ffb1eba | 172 | ;;;t \index{@} |
8e198a08 MB |
173 | ;;;t \index{WITH-SLOTS} |
174 | ;;;t \index{object literal} | |
175 | ;;;t \index{object} | |
176 | ;;;t \index{object property} | |
177 | ;;;t \index{property} | |
178 | ||
179 | ; (CREATE {name value}*) | |
180 | ; (SLOT-VALUE object slot-name) | |
181 | ; (WITH-SLOTS ({slot-name}*) object body) | |
182 | ; | |
f037cd4c VS |
183 | ; name ::= a Parenscript symbol or a Lisp keyword |
184 | ; value ::= a Parenscript expression | |
185 | ; object ::= a Parenscript object expression | |
8e198a08 | 186 | ; slot-name ::= a quoted Lisp symbol |
f037cd4c | 187 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
188 | |
189 | ;;; | |
190 | ;;; Object literals can be create using the `CREATE' form. Arguments | |
191 | ;;; to the `CREATE' form is a list of property names and values. To be | |
192 | ;;; more "lispy", the property names can be keywords. | |
193 | ||
194 | (create :foo "bar" :blorg 1) | |
5a69278c | 195 | => { foo : 'bar', blorg : 1 }; |
8e198a08 MB |
196 | |
197 | (create :foo "hihi" | |
198 | :blorg (array 1 2 3) | |
199 | :another-object (create :schtrunz 1)) | |
31c5dbde TC |
200 | => { foo : 'hihi', |
201 | blorg : [ 1, 2, 3 ], | |
5a69278c | 202 | anotherObject : { schtrunz : 1 } }; |
8e198a08 MB |
203 | |
204 | ;;; Object properties can be accessed using the `SLOT-VALUE' form, | |
205 | ;;; which takes an object and a slot-name. | |
206 | ||
5a69278c | 207 | (slot-value an-object 'foo) => anObject.foo; |
8e198a08 | 208 | |
5ffb1eba VS |
209 | ;;; The convenience macro `@' is provided to make multiple levels of |
210 | ;;; indirection easy to express | |
8e198a08 | 211 | |
5a69278c | 212 | (@ an-object foo bar) => anObject.foo.bar; |
8e198a08 MB |
213 | |
214 | ;;; The form `WITH-SLOTS' can be used to bind the given slot-name | |
215 | ;;; symbols to a macro that will expand into a `SLOT-VALUE' form at | |
216 | ;;; expansion time. | |
217 | ||
218 | (with-slots (a b c) this | |
219 | (+ a b c)) | |
31c5dbde | 220 | => this.a + this.b + this.c; |
8e198a08 MB |
221 | |
222 | ;;;## Regular Expression literals | |
223 | ;;;t \index{REGEX} | |
224 | ;;;t \index{regular expression} | |
225 | ;;;t \index{CL-INTERPOL} | |
226 | ||
227 | ; (REGEX regex) | |
228 | ; | |
229 | ; regex ::= a Lisp string | |
230 | ||
ca4fb762 HH |
231 | ;;; Regular expressions can be created by using the `REGEX' form. If |
232 | ;;; the argument does not start with a slash, it is surrounded by | |
233 | ;;; slashes to make it a proper JavaScript regex. If the argument | |
234 | ;;; starts with a slash it is left as it is. This makes it possible | |
235 | ;;; to use modifiers such as slash-i (case-insensitive) or | |
236 | ;;; slash-g (match-globally (all)). | |
237 | ||
5a69278c | 238 | (regex "foobar") => /foobar/; |
8e198a08 | 239 | |
5a69278c | 240 | (regex "/foobar/i") => /foobar/i; |
8e198a08 MB |
241 | |
242 | ;;; Here CL-INTERPOL proves really useful. | |
243 | ||
5a69278c | 244 | (regex #?r"/([^\s]+)foobar/i") => /([^\s]+)foobar/i; |
8e198a08 MB |
245 | |
246 | ;;;## Literal symbols | |
247 | ;;;t \index{T} | |
d777a405 | 248 | ;;;t \index{F} |
8e198a08 MB |
249 | ;;;t \index{FALSE} |
250 | ;;;t \index{NIL} | |
251 | ;;;t \index{UNDEFINED} | |
252 | ;;;t \index{THIS} | |
253 | ;;;t \index{literal symbols} | |
254 | ;;;t \index{null} | |
255 | ;;;t \index{true} | |
256 | ||
d777a405 | 257 | ; T, F, FALSE, NIL, UNDEFINED, THIS |
8e198a08 | 258 | |
d777a405 TC |
259 | ;;; The Lisp symbols `T' and `FALSE' (or `F') are converted to their |
260 | ;;; JavaScript boolean equivalents `true' and `false'. | |
8e198a08 | 261 | |
5a69278c | 262 | T => true; |
7a7d6c73 | 263 | |
5a69278c | 264 | FALSE => false; |
8e198a08 | 265 | |
5a69278c | 266 | F => false; |
d777a405 | 267 | |
8e198a08 MB |
268 | ;;; The Lisp symbol `NIL' is converted to the JavaScript keyword |
269 | ;;; `null'. | |
270 | ||
5a69278c | 271 | NIL => null; |
8e198a08 MB |
272 | |
273 | ;;; The Lisp symbol `UNDEFINED' is converted to the JavaScript keyword | |
274 | ;;; `undefined'. | |
275 | ||
5a69278c | 276 | UNDEFINED => undefined; |
8e198a08 MB |
277 | |
278 | ;;; The Lisp symbol `THIS' is converted to the JavaScript keyword | |
279 | ;;; `this'. | |
280 | ||
5a69278c | 281 | THIS => this; |
8e198a08 MB |
282 | |
283 | ;;;# Variables | |
284 | ;;;t \index{variable} | |
285 | ;;;t \index{symbol} | |
286 | ||
287 | ; variable ::= a Lisp symbol | |
288 | ||
289 | ;;; All the other literal Lisp values that are not recognized as | |
290 | ;;; special forms or symbol macros are converted to JavaScript | |
291 | ;;; variables. This extreme freedom is actually quite useful, as it | |
f037cd4c | 292 | ;;; allows the Parenscript programmer to be flexible, as flexible as |
8e198a08 MB |
293 | ;;; JavaScript itself. |
294 | ||
5a69278c | 295 | variable => variable; |
8e198a08 | 296 | |
5a69278c | 297 | a-variable => aVariable; |
8e198a08 | 298 | |
5a69278c | 299 | *math => Math; |
8e198a08 | 300 | |
8e198a08 MB |
301 | ;;;# Function calls and method calls |
302 | ;;;t \index{function} | |
303 | ;;;t \index{function call} | |
304 | ;;;t \index{method} | |
305 | ;;;t \index{method call} | |
306 | ||
307 | ; (function {argument}*) | |
efe8a33f | 308 | |
8e198a08 | 309 | ; |
f037cd4c | 310 | ; function ::= a Parenscript expression or a Lisp symbol |
f037cd4c VS |
311 | ; object ::= a Parenscript expression |
312 | ; argument ::= a Parenscript expression | |
8e198a08 MB |
313 | |
314 | ;;; Any list passed to the JavaScript that is not recognized as a | |
315 | ;;; macro or a special form (see "Macro Expansion" below) is | |
316 | ;;; interpreted as a function call. The function call is converted to | |
317 | ;;; the normal JavaScript function call representation, with the | |
318 | ;;; arguments given in paren after the function name. | |
319 | ||
5a69278c | 320 | (blorg 1 2) => blorg(1, 2); |
8e198a08 MB |
321 | |
322 | (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4)) | |
5a69278c | 323 | => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ]); |
8e198a08 | 324 | |
5a69278c | 325 | ((slot-value this 'blorg) 1 2) => this.blorg(1, 2); |
79630c82 | 326 | |
5a69278c | 327 | ((aref foo i) 1 2) => foo[i](1, 2); |
8e198a08 | 328 | |
5a69278c | 329 | ((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true); |
8e198a08 | 330 | |
8e198a08 MB |
331 | ;;;# Operator Expressions |
332 | ;;;t \index{operator} | |
333 | ;;;t \index{operator expression} | |
334 | ;;;t \index{assignment operator} | |
335 | ;;;t \index{EQL} | |
336 | ;;;t \index{NOT} | |
337 | ;;;t \index{AND} | |
338 | ;;;t \index{OR} | |
339 | ||
340 | ; (operator {argument}*) | |
341 | ; (single-operator argument) | |
342 | ; | |
343 | ; operator ::= one of *, /, %, +, -, <<, >>, >>>, < >, EQL, | |
344 | ; ==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR. | |
345 | ; single-operator ::= one of INCF, DECF, ++, --, NOT, ! | |
f037cd4c | 346 | ; argument ::= a Parenscript expression |
8e198a08 MB |
347 | |
348 | ;;; Operator forms are similar to function call forms, but have an | |
94a05cdf | 349 | ;;; operator as function name. |
8e198a08 MB |
350 | ;;; |
351 | ;;; Please note that `=' is converted to `==' in JavaScript. The `=' | |
f037cd4c VS |
352 | ;;; Parenscript operator is not the assignment operator. Unlike |
353 | ;;; JavaScript, Parenscript supports multiple arguments to the | |
8e198a08 MB |
354 | ;;; operators. |
355 | ||
5a69278c | 356 | (* 1 2) => 1 * 2; |
8e198a08 | 357 | |
5a69278c | 358 | (= 1 2) => 1 == 2; |
8e198a08 | 359 | |
5a69278c | 360 | (eql 1 2) => 1 == 2; |
8e198a08 | 361 | |
ecc3218c | 362 | ;;; Note that the resulting expression is correctly parenthesized, |
8e198a08 MB |
363 | ;;; according to the JavaScript operator precedence that can be found |
364 | ;;; in table form at: | |
365 | ||
31c5dbde | 366 | ;;; http://www.codehouse.com/javascript/precedence/ |
8e198a08 MB |
367 | |
368 | (* 1 (+ 2 3 4) 4 (/ 6 7)) | |
5a69278c | 369 | => 1 * (2 + 3 + 4) * 4 * (6 / 7); |
8e198a08 | 370 | |
f7c2734a | 371 | ;;; The pre increment and decrement operators are also |
8e198a08 | 372 | ;;; available. `INCF' and `DECF' are the pre-incrementing and |
f7c2734a | 373 | ;;; pre-decrementing operators. These operators can |
8e198a08 MB |
374 | ;;; take only one argument. |
375 | ||
5a69278c | 376 | (incf i) => ++i; |
8e198a08 | 377 | |
5a69278c | 378 | (decf i) => --i; |
8e198a08 MB |
379 | |
380 | ;;; The `1+' and `1-' operators are shortforms for adding and | |
381 | ;;; substracting 1. | |
382 | ||
5a69278c | 383 | (1- i) => i - 1; |
8e198a08 | 384 | |
5a69278c | 385 | (1+ i) => i + 1; |
8e198a08 MB |
386 | |
387 | ;;; The `not' operator actually optimizes the code a bit. If `not' is | |
388 | ;;; used on another boolean-returning operator, the operator is | |
389 | ;;; reversed. | |
390 | ||
5a69278c | 391 | (not (< i 2)) => i >= 2; |
8e198a08 | 392 | |
5a69278c | 393 | (not (eql i 2)) => i != 2; |
8e198a08 MB |
394 | |
395 | ;;;# Body forms | |
396 | ;;;t \index{body form} | |
397 | ;;;t \index{PROGN} | |
398 | ;;;t \index{body statement} | |
399 | ||
400 | ; (PROGN {statement}*) in statement context | |
401 | ; (PROGN {expression}*) in expression context | |
402 | ; | |
f037cd4c VS |
403 | ; statement ::= a Parenscript statement |
404 | ; expression ::= a Parenscript expression | |
8e198a08 MB |
405 | |
406 | ;;; The `PROGN' special form defines a sequence of statements when | |
407 | ;;; used in a statement context, or sequence of expression when used | |
408 | ;;; in an expression context. The `PROGN' special form is added | |
409 | ;;; implicitly around the branches of conditional executions forms, | |
410 | ;;; function declarations and iteration constructs. | |
411 | ||
412 | ;;; For example, in a statement context: | |
413 | ||
414 | (progn (blorg i) (blafoo i)) | |
31c5dbde TC |
415 | => blorg(i); |
416 | blafoo(i); | |
8e198a08 MB |
417 | |
418 | ;;; In an expression context: | |
419 | ||
420 | (+ i (progn (blorg i) (blafoo i))) | |
5a69278c | 421 | => i + (blorg(i), blafoo(i)); |
8e198a08 MB |
422 | |
423 | ;;; A `PROGN' form doesn't lead to additional indentation or | |
424 | ;;; additional braces around it's body. | |
425 | ||
426 | ;;;# Function Definition | |
427 | ;;;t \index{function} | |
428 | ;;;t \index{method} | |
429 | ;;;t \index{function definition} | |
430 | ;;;t \index{DEFUN} | |
431 | ;;;t \index{LAMBDA} | |
432 | ;;;t \index{closure} | |
433 | ;;;t \index{anonymous function} | |
434 | ||
435 | ; (DEFUN name ({argument}*) body) | |
436 | ; (LAMBDA ({argument}*) body) | |
437 | ; | |
438 | ; name ::= a Lisp Symbol | |
439 | ; argument ::= a Lisp symbol | |
f037cd4c | 440 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
441 | |
442 | ;;; As in Lisp, functions are defined using the `DEFUN' form, which | |
443 | ;;; takes a name, a list of arguments, and a function body. An | |
444 | ;;; implicit `PROGN' is added around the body statements. | |
445 | ||
446 | (defun a-function (a b) | |
447 | (return (+ a b))) | |
31c5dbde | 448 | => function aFunction(a, b) { |
8e198a08 | 449 | return a + b; |
5a69278c | 450 | }; |
8e198a08 MB |
451 | |
452 | ;;; Anonymous functions can be created using the `LAMBDA' form, which | |
453 | ;;; is the same as `DEFUN', but without function name. In fact, | |
454 | ;;; `LAMBDA' creates a `DEFUN' with an empty function name. | |
455 | ||
456 | (lambda (a b) (return (+ a b))) | |
31c5dbde | 457 | => function (a, b) { |
8e198a08 | 458 | return a + b; |
5a69278c | 459 | }; |
8e198a08 MB |
460 | |
461 | ;;;# Assignment | |
462 | ;;;t \index{assignment} | |
463 | ;;;t \index{SETF} | |
d777a405 TC |
464 | ;;;t \index{PSETF} |
465 | ;;;t \index{SETQ} | |
466 | ;;;t \index{PSETQ} | |
a2a9eab0 | 467 | ;;;t \index{DEFSETF} |
8e198a08 MB |
468 | ;;;t \index{assignment operator} |
469 | ||
470 | ; (SETF {lhs rhs}*) | |
d777a405 | 471 | ; (PSETF {lhs rhs}*) |
8e198a08 | 472 | ; |
f037cd4c VS |
473 | ; lhs ::= a Parenscript left hand side expression |
474 | ; rhs ::= a Parenscript expression | |
8e198a08 | 475 | |
d777a405 TC |
476 | ; (SETQ {lhs rhs}*) |
477 | ; (PSETQ {lhs rhs}*) | |
478 | ; | |
f037cd4c VS |
479 | ; lhs ::= a Parenscript symbol |
480 | ; rhs ::= a Parenscript expression | |
d777a405 TC |
481 | |
482 | ;;; Assignment is done using the `SETF', `PSETF', `SETQ', and `PSETQ' | |
483 | ;;; forms, which are transformed into a series of assignments using | |
484 | ;;; the JavaScript `=' operator. | |
8e198a08 | 485 | |
72332f2a | 486 | (setf a 1) => a = 1; |
8e198a08 MB |
487 | |
488 | (setf a 2 b 3 c 4 x (+ a b c)) | |
31c5dbde TC |
489 | => a = 2; |
490 | b = 3; | |
491 | c = 4; | |
492 | x = a + b + c; | |
8e198a08 MB |
493 | |
494 | ;;; The `SETF' form can transform assignments of a variable with an | |
495 | ;;; operator expression using this variable into a more "efficient" | |
496 | ;;; assignment operator form. For example: | |
497 | ||
72332f2a | 498 | (setf a (+ a 2 3 4 a)) => a += 2 + 3 + 4 + a; |
8e198a08 | 499 | |
72332f2a | 500 | (setf a (- 1 a)) => a = 1 - a; |
8e198a08 | 501 | |
d777a405 TC |
502 | ;;; The `PSETF' and `PSETQ' forms perform parallel assignment of |
503 | ;;; places or variables using a number of temporary variables created | |
504 | ;;; by `PS-GENSYM'. For example: | |
505 | ||
5ffb1eba | 506 | (let ((a 1) (b 2)) |
d777a405 | 507 | (psetf a b b a)) |
5ffb1eba VS |
508 | => var a1 = 1; |
509 | var b2 = 2; | |
510 | var _js3_5 = b2; | |
511 | var _js4_6 = a1; | |
512 | a1 = _js3_5; | |
513 | b2 = _js4_6; | |
d777a405 TC |
514 | |
515 | ;;; The `SETQ' and `PSETQ' forms operate identically to `SETF' and | |
516 | ;;; `PSETF', but throw a compile-time error if the left-hand side form | |
517 | ;;; is not a symbol. For example: | |
518 | ||
519 | (setq a 1) => a = 1; | |
520 | ||
521 | ;; but... | |
522 | ||
523 | (setq (aref a 0) 1) | |
31c5dbde | 524 | ;; => ERROR: The value (AREF A 0) is not of type SYMBOL. |
d777a405 | 525 | |
a2a9eab0 VS |
526 | ;;; New types of setf places can be defined in one of two ways: using |
527 | ;;; `DEFSETF' or using `DEFUN' with a setf function name; both are | |
528 | ;;; analogous to their Common Lisp counterparts. | |
529 | ||
530 | ;;; `DEFSETF' supports both long and short forms, while `DEFUN' of a | |
531 | ;;; setf place generates a JavaScript function name with the __setf_ | |
532 | ;;; prefix: | |
533 | ||
534 | (defun (setf color) (new-color el) | |
535 | (setf (slot-value (slot-value el 'style) 'color) new-color)) | |
31c5dbde | 536 | => function __setf_color(newColor, el) { |
a2a9eab0 | 537 | el.style.color = newColor; |
31c5dbde | 538 | }; |
a2a9eab0 VS |
539 | |
540 | (setf (color some-div) (+ 23 "em")) | |
5ffb1eba VS |
541 | => var _js2_3 = someDiv; |
542 | var _js1_4 = 23 + 'em'; | |
543 | __setf_color(_js1_4, _js2_3); | |
a2a9eab0 | 544 | |
a2a9eab0 VS |
545 | ;;; Note that temporary variables are generated to preserve evaluation |
546 | ;;; order of the arguments as they would be in Lisp. | |
547 | ||
548 | ;;; The following example illustrates how setf places can be used to | |
549 | ;;; provide a uniform protocol for positioning elements in HTML pages: | |
550 | ||
551 | (defsetf left (el) (offset) | |
31c5dbde | 552 | `(setf (slot-value (slot-value ,el 'style) 'left) ,offset)) |
5a69278c | 553 | => null; |
a2a9eab0 VS |
554 | |
555 | (setf (left some-div) (+ 123 "px")) | |
5ffb1eba VS |
556 | => var _js2_3 = someDiv; |
557 | var _js1_4 = 123 + 'px'; | |
558 | _js2_3.style.left = _js1_4; | |
a2a9eab0 | 559 | |
5a69278c VS |
560 | (macrolet ((left (el) |
561 | `(slot-value ,el 'offset-left))) | |
562 | (left some-div)) | |
31c5dbde | 563 | => someDiv.offsetLeft; |
a2a9eab0 | 564 | |
8e198a08 MB |
565 | ;;;# Single argument statements |
566 | ;;;t \index{single-argument statement} | |
567 | ;;;t \index{RETURN} | |
568 | ;;;t \index{THROW} | |
569 | ;;;t \index{THROW} | |
570 | ;;;t \index{function} | |
571 | ||
572 | ; (RETURN {value}?) | |
573 | ; (THROW {value}?) | |
574 | ; | |
f037cd4c | 575 | ; value ::= a Parenscript expression |
8e198a08 MB |
576 | |
577 | ;;; The single argument statements `return' and `throw' are generated | |
578 | ;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a | |
579 | ;;; `TRY' form. `RETURN' is used to return a value from a function | |
580 | ;;; call. | |
581 | ||
5a69278c | 582 | (return 1) => return 1; |
8e198a08 | 583 | |
5a69278c | 584 | (throw "foobar") => throw 'foobar'; |
8e198a08 MB |
585 | |
586 | ;;;# Single argument expression | |
587 | ;;;t \index{single-argument expression} | |
588 | ;;;t \index{object creation} | |
589 | ;;;t \index{object deletion} | |
590 | ;;;t \index{DELETE} | |
591 | ;;;t \index{VOID} | |
592 | ;;;t \index{TYPEOF} | |
593 | ;;;t \index{INSTANCEOF} | |
594 | ;;;t \index{NEW} | |
595 | ;;;t \index{new} | |
596 | ||
597 | ; (DELETE {value}) | |
598 | ; (VOID {value}) | |
599 | ; (TYPEOF {value}) | |
600 | ; (INSTANCEOF {value}) | |
601 | ; (NEW {value}) | |
602 | ; | |
f037cd4c | 603 | ; value ::= a Parenscript expression |
8e198a08 MB |
604 | |
605 | ;;; The single argument expressions `delete', `void', `typeof', | |
606 | ;;; `instanceof' and `new' are generated by the forms `DELETE', | |
607 | ;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a | |
f037cd4c | 608 | ;;; Parenscript expression. |
8e198a08 | 609 | |
5a69278c | 610 | (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4); |
8e198a08 MB |
611 | |
612 | (if (= (typeof blorg) *string) | |
613 | (alert (+ "blorg is a string: " blorg)) | |
614 | (alert "blorg is not a string")) | |
31c5dbde | 615 | => if (typeof blorg == String) { |
7a7d6c73 | 616 | alert('blorg is a string: ' + blorg); |
31c5dbde | 617 | } else { |
7a7d6c73 | 618 | alert('blorg is not a string'); |
5a69278c | 619 | }; |
8e198a08 MB |
620 | |
621 | ;;;# Conditional Statements | |
622 | ;;;t \index{conditional statements} | |
623 | ;;;t \index{IF} | |
624 | ;;;t \index{WHEN} | |
625 | ;;;t \index{UNLESS} | |
626 | ;;;t \index{conditionals} | |
627 | ||
628 | ; (IF conditional then {else}) | |
629 | ; (WHEN condition then) | |
630 | ; (UNLESS condition then) | |
631 | ; | |
f037cd4c VS |
632 | ; condition ::= a Parenscript expression |
633 | ; then ::= a Parenscript statement in statement context, a | |
634 | ; Parenscript expression in expression context | |
635 | ; else ::= a Parenscript statement in statement context, a | |
636 | ; Parenscript expression in expression context | |
8e198a08 MB |
637 | |
638 | ;;; The `IF' form compiles to the `if' javascript construct. An | |
639 | ;;; explicit `PROGN' around the then branch and the else branch is | |
640 | ;;; needed if they consist of more than one statement. When the `IF' | |
641 | ;;; form is used in an expression context, a JavaScript `?', `:' | |
642 | ;;; operator form is generated. | |
643 | ||
5ffb1eba | 644 | (if ((@ blorg is-correct)) |
8e198a08 MB |
645 | (progn (carry-on) (return i)) |
646 | (alert "blorg is not correct!")) | |
31c5dbde TC |
647 | => if (blorg.isCorrect()) { |
648 | carryOn(); | |
649 | return i; | |
650 | } else { | |
651 | alert('blorg is not correct!'); | |
5a69278c | 652 | }; |
8e198a08 | 653 | |
5ffb1eba | 654 | (+ i (if ((@ blorg add-one)) 1 2)) |
5a69278c | 655 | => i + (blorg.addOne() ? 1 : 2); |
8e198a08 MB |
656 | |
657 | ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the | |
658 | ;;; `IF' form. | |
659 | ||
5ffb1eba | 660 | (when ((@ blorg is-correct)) |
8e198a08 MB |
661 | (carry-on) |
662 | (return i)) | |
31c5dbde TC |
663 | => if (blorg.isCorrect()) { |
664 | carryOn(); | |
665 | return i; | |
5a69278c | 666 | }; |
8e198a08 | 667 | |
5ffb1eba | 668 | (unless ((@ blorg is-correct)) |
8e198a08 | 669 | (alert "blorg is not correct!")) |
31c5dbde TC |
670 | => if (!blorg.isCorrect()) { |
671 | alert('blorg is not correct!'); | |
5a69278c | 672 | }; |
8e198a08 MB |
673 | |
674 | ;;;# Variable declaration | |
675 | ;;;t \index{variable} | |
676 | ;;;t \index{variable declaration} | |
677 | ;;;t \index{binding} | |
678 | ;;;t \index{scoping} | |
679 | ;;;t \index{DEFVAR} | |
58c4ef4f | 680 | ;;;t \index{VAR} |
d777a405 | 681 | ;;;t \index{LET} |
58c4ef4f | 682 | ;;;t \index{LET*} |
8e198a08 MB |
683 | |
684 | ; (DEFVAR var {value}?) | |
58c4ef4f | 685 | ; (VAR var {value}?) |
d777a405 TC |
686 | ; (LET ({var | (var value)}*) body) |
687 | ; (LET* ({var | (var value)}*) body) | |
8e198a08 MB |
688 | ; |
689 | ; var ::= a Lisp symbol | |
f037cd4c VS |
690 | ; value ::= a Parenscript expression |
691 | ; body ::= a list of Parenscript statements | |
8e198a08 | 692 | |
58c4ef4f VS |
693 | ;;; Parenscript special variables can be declared using the `DEFVAR' |
694 | ;;; special form, which is similar to its equivalent form in | |
695 | ;;; Lisp. Note that the result is undefined if `DEFVAR' is not used as | |
696 | ;;; a top-level form. | |
8e198a08 | 697 | |
5a69278c | 698 | (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]; |
8e198a08 | 699 | |
58c4ef4f | 700 | ;;; One feature present in Parenscript that is not part of Common Lisp |
bd363c96 | 701 | ;;; are lexically-scoped global variables, which are declared using |
58c4ef4f VS |
702 | ;;; the `VAR' special form. |
703 | ||
5ffb1eba VS |
704 | ;;; Parenscript provides the `LET' and `LET*' special forms for |
705 | ;;; creating new variable bindings. Both special forms implement | |
706 | ;;; lexical scope by renaming the provided variables via `GENSYM', and | |
707 | ;;; implement dynamic binding using `TRY'-`FINALY'. Note that | |
708 | ;;; top-level `LET' and `LET*' forms will create new global variables. | |
d777a405 | 709 | |
5ffb1eba VS |
710 | ;;; Moreover, beware that scoping rules in Lisp and JavaScript are |
711 | ;;; quite different. For example, don't rely on closures capturing | |
712 | ;;; local variables in the way that you would normally expect. | |
d777a405 | 713 | |
d777a405 TC |
714 | |
715 | ;;; examples: | |
716 | ||
5ffb1eba VS |
717 | (progn |
718 | (defvar *a* 4) | |
719 | (let ((x 1) | |
720 | (*a* 2)) | |
721 | (let* ((y (+ x 1)) | |
722 | (x (+ x y))) | |
723 | (+ *a* x y)))) | |
724 | => var A = 4; | |
725 | var x1 = 1; | |
726 | var A2; | |
727 | try { | |
728 | A2 = A; | |
729 | A = 2; | |
730 | var y3 = x1 + 1; | |
731 | var x4 = x1 + y3; | |
732 | A + x4 + y3; | |
733 | } finally { | |
734 | A = A2; | |
735 | }; | |
8e198a08 MB |
736 | |
737 | ;;;# Iteration constructs | |
738 | ;;;t \index{iteration} | |
739 | ;;;t \index{iteration construct} | |
740 | ;;;t \index{loop} | |
741 | ;;;t \index{array traversal} | |
742 | ;;;t \index{property} | |
743 | ;;;t \index{object property} | |
744 | ;;;t \index{DO} | |
745 | ;;;t \index{DOTIMES} | |
746 | ;;;t \index{DOLIST} | |
0ce67a33 | 747 | ;;;t \index{FOR-IN} |
8e198a08 MB |
748 | ;;;t \index{WHILE} |
749 | ||
d777a405 TC |
750 | ; (DO ({var | (var {init}? {step}?)}*) (end-test {result}?) body) |
751 | ; (DO* ({var | (var {init}? {step}?)}*) (end-test {result}?) body) | |
752 | ; (DOTIMES (var numeric-form {result}?) body) | |
753 | ; (DOLIST (var list-form {result}?) body) | |
0ce67a33 | 754 | ; (FOR-IN (var object) body) |
8e198a08 MB |
755 | ; (WHILE end-test body) |
756 | ; | |
757 | ; var ::= a Lisp symbol | |
f037cd4c VS |
758 | ; numeric-form ::= a Parenscript expression resulting in a number |
759 | ; list-form ::= a Parenscript expression resulting in an array | |
760 | ; object-form ::= a Parenscript expression resulting in an object | |
761 | ; init ::= a Parenscript expression | |
762 | ; step ::= a Parenscript expression | |
763 | ; end-test ::= a Parenscript expression | |
764 | ; result ::= a Parenscript expression | |
765 | ; body ::= a list of Parenscript statements | |
8e198a08 | 766 | |
d777a405 TC |
767 | ;;; All interation special forms are transformed into JavaScript `for' |
768 | ;;; statements and, if needed, lambda expressions. | |
8e198a08 | 769 | |
d777a405 TC |
770 | ;;; `DO', `DO*', and `DOTIMES' carry the same semantics as their |
771 | ;;; Common Lisp equivalents. | |
8e198a08 | 772 | |
d777a405 | 773 | ;;; `DO*' (note the variety of possible init-forms: |
8e198a08 | 774 | |
d777a405 TC |
775 | (do* ((a) b (c (array "a" "b" "c" "d" "e")) |
776 | (d 0 (1+ d)) | |
777 | (e (aref c d) (aref c d))) | |
5ffb1eba | 778 | ((or (= d (@ c length)) (eql e "x"))) |
d777a405 | 779 | (setf a d b e) |
5ffb1eba | 780 | ((@ document write) (+ "a: " a " b: " b "<br/>"))) |
31c5dbde TC |
781 | => for (var a = null, b = null, c = ['a', 'b', 'c', 'd', 'e'], d = 0, e = c[d]; !(d == c.length || e == 'x'); d += 1, e = c[d]) { |
782 | a = d; | |
783 | b = e; | |
784 | document.write('a: ' + a + ' b: ' + b + '<br/>'); | |
785 | }; | |
8e198a08 | 786 | |
5ffb1eba | 787 | ;;; `DO' |
d777a405 TC |
788 | |
789 | (do ((i 0 (1+ i)) | |
790 | (s 0 (+ s i (1+ i)))) | |
791 | ((> i 10)) | |
5ffb1eba VS |
792 | ((@ document write) (+ "i: " i " s: " s "<br/>"))) |
793 | => var i1 = 0; | |
794 | var s2 = 0; | |
795 | for (; i1 <= 10; ) { | |
796 | document.write('i: ' + i1 + ' s: ' + s2 + '<br/>'); | |
797 | var _js3_5 = i1 + 1; | |
798 | var _js4_6 = s2 + i1 + (i1 + 1); | |
799 | i1 = _js3_5; | |
800 | s2 = _js4_6; | |
31c5dbde | 801 | }; |
d777a405 TC |
802 | |
803 | ;;; compare to `DO*': | |
804 | ||
805 | (do* ((i 0 (1+ i)) | |
806 | (s 0 (+ s i (1- i)))) | |
807 | ((> i 10)) | |
5ffb1eba | 808 | ((@ document write) (+ "i: " i " s: " s "<br/>"))) |
31c5dbde TC |
809 | => for (var i = 0, s = 0; i <= 10; i += 1, s += i + (i - 1)) { |
810 | document.write('i: ' + i + ' s: ' + s + '<br/>'); | |
811 | }; | |
d777a405 TC |
812 | |
813 | ;;; `DOTIMES': | |
814 | ||
5ffb1eba VS |
815 | (let ((arr (array "a" "b" "c" "d" "e"))) |
816 | (dotimes (i (@ arr length)) | |
817 | ((@ document write) (+ "i: " i " arr[i]: " (aref arr i) "<br/>")))) | |
818 | => var arr1 = ['a', 'b', 'c', 'd', 'e']; | |
819 | for (var i = 0; i < arr1.length; i += 1) { | |
820 | document.write('i: ' + i + ' arr[i]: ' + arr1[i] + '<br/>'); | |
31c5dbde | 821 | }; |
d777a405 TC |
822 | |
823 | ;;; `DOTIMES' with return value: | |
824 | ||
5ffb1eba | 825 | (let ((res 0)) |
d777a405 TC |
826 | (alert (+ "Summation to 10 is " |
827 | (dotimes (i 10 res) | |
828 | (incf res (1+ i)))))) | |
5ffb1eba | 829 | => var res1 = 0; |
31c5dbde TC |
830 | alert('Summation to 10 is ' + (function () { |
831 | for (var i = 0; i < 10; i += 1) { | |
5ffb1eba | 832 | res1 += i + 1; |
31c5dbde | 833 | }; |
5ffb1eba | 834 | return res1; |
31c5dbde | 835 | })()); |
d777a405 TC |
836 | |
837 | ;;; `DOLIST' is like CL:DOLIST, but that it operates on numbered JS | |
838 | ;;; arrays/vectors. | |
839 | ||
5ffb1eba | 840 | (let ((l (list 1 2 4 8 16 32))) |
d777a405 | 841 | (dolist (c l) |
5ffb1eba VS |
842 | ((@ document write) (+ "c: " c "<br/>")))) |
843 | => var l1 = [1, 2, 4, 8, 16, 32]; | |
844 | for (var c = null, _js_arrvar3 = l1, _js_idx2 = 0; _js_idx2 < _js_arrvar3.length; _js_idx2 += 1) { | |
845 | c = _js_arrvar3[_js_idx2]; | |
31c5dbde TC |
846 | document.write('c: ' + c + '<br/>'); |
847 | }; | |
d777a405 | 848 | |
5ffb1eba VS |
849 | (let ((l '(1 2 4 8 16 32)) |
850 | (s 0)) | |
d777a405 TC |
851 | (alert (+ "Sum of " l " is: " |
852 | (dolist (c l s) | |
853 | (incf s c))))) | |
5ffb1eba VS |
854 | => var l1 = [1, 2, 4, 8, 16, 32]; |
855 | var s2 = 0; | |
856 | alert('Sum of ' + l1 + ' is: ' + (function () { | |
857 | for (var c = null, _js_arrvar4 = l1, _js_idx3 = 0; _js_idx3 < _js_arrvar4.length; _js_idx3 += 1) { | |
858 | c = _js_arrvar4[_js_idx3]; | |
859 | s2 += c; | |
31c5dbde | 860 | }; |
5ffb1eba | 861 | return s2; |
31c5dbde | 862 | })()); |
d777a405 | 863 | |
0ce67a33 | 864 | ;;; `FOR-IN' is translated to the JS `for...in' statement. |
d777a405 | 865 | |
5ffb1eba | 866 | (let ((obj (create :a 1 :b 2 :c 3))) |
0ce67a33 | 867 | (for-in (i obj) |
5ffb1eba VS |
868 | ((@ document write) (+ i ": " (aref obj i) "<br/>")))) |
869 | => var obj1 = { a : 1, b : 2, c : 3 }; | |
870 | for (var i in obj1) { | |
871 | document.write(i + ': ' + obj1[i] + '<br/>'); | |
31c5dbde | 872 | }; |
d777a405 | 873 | |
8e198a08 MB |
874 | ;;; The `WHILE' form is transformed to the JavaScript form `while', |
875 | ;;; and loops until a termination test evaluates to false. | |
876 | ||
5ffb1eba VS |
877 | (while ((@ film is-not-finished)) |
878 | ((@ this eat) (new *popcorn))) | |
31c5dbde TC |
879 | => while (film.isNotFinished()) { |
880 | this.eat(new Popcorn); | |
5a69278c | 881 | }; |
551080b7 | 882 | |
8e198a08 MB |
883 | ;;;# The `CASE' statement |
884 | ;;;t \index{CASE} | |
3c393e09 | 885 | ;;;t \index{SWITCH} |
8e198a08 MB |
886 | ;;;t \index{switch} |
887 | ||
888 | ; (CASE case-value clause*) | |
889 | ; | |
3c393e09 | 890 | ; clause ::= (value body) | ((value*) body) | t-clause |
f037cd4c VS |
891 | ; case-value ::= a Parenscript expression |
892 | ; value ::= a Parenscript expression | |
3c393e09 | 893 | ; t-clause ::= {t | otherwise | default} body |
f037cd4c | 894 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
895 | |
896 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in | |
897 | ;;; JavaScript. Note that `CASE' is not an expression in | |
f037cd4c | 898 | ;;; Parenscript. |
8e198a08 MB |
899 | |
900 | (case (aref blorg i) | |
3c393e09 | 901 | ((1 "one") (alert "one")) |
8e198a08 | 902 | (2 (alert "two")) |
3c393e09 | 903 | (t (alert "default clause"))) |
31c5dbde TC |
904 | => switch (blorg[i]) { |
905 | case 1: | |
906 | case 'one': | |
907 | alert('one'); | |
908 | break; | |
909 | case 2: | |
910 | alert('two'); | |
911 | break; | |
912 | default: | |
913 | alert('default clause'); | |
5a69278c | 914 | }; |
8e198a08 | 915 | |
3c393e09 HH |
916 | ; (SWITCH case-value clause*) |
917 | ; clause ::= (value body) | (default body) | |
918 | ||
919 | ;;; The `SWITCH' form is the equivalent to a javascript switch statement. | |
920 | ;;; No break statements are inserted, and the default case is named `DEFAULT'. | |
921 | ;;; The `CASE' form should be prefered in most cases. | |
922 | ||
923 | (switch (aref blorg i) | |
924 | (1 (alert "If I get here")) | |
925 | (2 (alert "I also get here")) | |
926 | (default (alert "I always get here"))) | |
31c5dbde TC |
927 | => switch (blorg[i]) { |
928 | case 1: alert('If I get here'); | |
929 | case 2: alert('I also get here'); | |
930 | default: alert('I always get here'); | |
5a69278c | 931 | }; |
3c393e09 | 932 | |
8e198a08 MB |
933 | ;;;# The `WITH' statement |
934 | ;;;t \index{WITH} | |
935 | ;;;t \index{dynamic scope} | |
936 | ;;;t \index{binding} | |
937 | ;;;t \index{scoping} | |
938 | ;;;t \index{closure} | |
939 | ||
5d9cdcad | 940 | ; (WITH object body) |
8e198a08 | 941 | ; |
f037cd4c VS |
942 | ; object ::= a Parenscript expression evaluating to an object |
943 | ; body ::= a list of Parenscript statements | |
8e198a08 MB |
944 | |
945 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and | |
946 | ;;; adds the object `object' as an intermediary scope objects when | |
947 | ;;; executing the body. | |
948 | ||
5d9cdcad | 949 | (with (create :foo "foo" :i "i") |
8e198a08 | 950 | (alert (+ "i is now intermediary scoped: " i))) |
31c5dbde TC |
951 | => with ({ foo : 'foo', i : 'i' }) { |
952 | alert('i is now intermediary scoped: ' + i); | |
5a69278c | 953 | }; |
8e198a08 MB |
954 | |
955 | ;;;# The `TRY' statement | |
956 | ;;;t \index{TRY} | |
957 | ;;;t \index{CATCH} | |
958 | ;;;t \index{FINALLY} | |
959 | ;;;t \index{exception} | |
960 | ;;;t \index{error handling} | |
961 | ||
962 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) | |
963 | ; | |
f037cd4c | 964 | ; body ::= a list of Parenscript statements |
8e198a08 MB |
965 | ; var ::= a Lisp symbol |
966 | ||
967 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and | |
968 | ;;; can be used to catch expressions thrown by the `THROW' | |
969 | ;;; form. The body of the catch clause is invoked when an exception | |
970 | ;;; is catched, and the body of the finally is always invoked when | |
971 | ;;; leaving the body of the `TRY' form. | |
972 | ||
94a05cdf | 973 | (try (throw "i") |
8e198a08 MB |
974 | (:catch (error) |
975 | (alert (+ "an error happened: " error))) | |
976 | (:finally | |
977 | (alert "Leaving the try form"))) | |
31c5dbde TC |
978 | => try { |
979 | throw 'i'; | |
980 | } catch (error) { | |
981 | alert('an error happened: ' + error); | |
982 | } finally { | |
983 | alert('Leaving the try form'); | |
5a69278c | 984 | }; |
8e198a08 MB |
985 | |
986 | ;;;# The HTML Generator | |
ecc3218c | 987 | ;;;t \index{PS-HTML} |
8e198a08 MB |
988 | ;;;t \index{HTML generation} |
989 | ||
ecc3218c | 990 | ; (PS-HTML html-expression) |
8e198a08 | 991 | |
f037cd4c | 992 | ;;; The HTML generator of Parenscript is very similar to the htmlgen |
8bb28ead VS |
993 | ;;; HTML generator library included with AllegroServe. It accepts the |
994 | ;;; same input forms as the AllegroServer HTML generator. However, | |
f037cd4c | 995 | ;;; non-HTML construct are compiled to JavaScript by the Parenscript |
8e198a08 MB |
996 | ;;; compiler. The resulting expression is a JavaScript expression. |
997 | ||
8bb28ead | 998 | (ps-html ((:a :href "foobar") "blorg")) |
5a69278c | 999 | => '<A HREF=\"foobar\">blorg</A>'; |
8e198a08 | 1000 | |
8bb28ead | 1001 | (ps-html ((:a :href (generate-a-link)) "blorg")) |
5a69278c | 1002 | => '<A HREF=\"' + generateALink() + '\">blorg</A>'; |
8e198a08 | 1003 | |
f037cd4c | 1004 | ;;; We can recursively call the Parenscript compiler in an HTML |
ecc3218c | 1005 | ;;; expression. |
8e198a08 | 1006 | |
5ffb1eba | 1007 | ((@ document write) |
8bb28ead | 1008 | (ps-html ((:a :href "#" |
e69d0a12 | 1009 | :onclick (ps-inline (transport))) "link"))) |
5a69278c | 1010 | => document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport' + '(' + ')') + '\">link</A>'); |
8e198a08 | 1011 | |
7abef5d4 HH |
1012 | ;;; Forms may be used in attribute lists to conditionally generate |
1013 | ;;; the next attribute. In this example the textarea is sometimes disabled. | |
1014 | ||
5ffb1eba | 1015 | (let ((disabled nil) |
7abef5d4 | 1016 | (authorized t)) |
5ffb1eba | 1017 | (setf (@ element inner-h-t-m-l) |
8bb28ead | 1018 | (ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled") |
7abef5d4 | 1019 | "Edit me")))) |
5ffb1eba VS |
1020 | => var disabled1 = null; |
1021 | var authorized2 = true; | |
31c5dbde | 1022 | element.innerHTML = |
1937c30a | 1023 | '<TEXTAREA' |
5ffb1eba | 1024 | + (disabled1 || !authorized2 ? ' DISABLED=\"' + 'disabled' + '\"' : '') |
1937c30a | 1025 | + '>Edit me</TEXTAREA>'; |
7abef5d4 | 1026 | |
8e198a08 MB |
1027 | ;;;# Macrology |
1028 | ;;;t \index{macro} | |
1029 | ;;;t \index{macrology} | |
ecc3218c | 1030 | ;;;t \index{DEFPSMACRO} |
8cfc6fe9 VS |
1031 | ;;;t \index{DEFMACRO/PS} |
1032 | ;;;t \index{DEFMACRO+PS} | |
1033 | ;;;t \index{DEFINE-PS-SYMBOL-MACRO} | |
1034 | ;;;t \index{IMPORT-MACROS-FROM-LISP} | |
8e198a08 MB |
1035 | ;;;t \index{MACROLET} |
1036 | ;;;t \index{SYMBOL-MACROLET} | |
ecc3218c | 1037 | ;;;t \index{PS-GENSYM} |
8e198a08 MB |
1038 | ;;;t \index{compiler} |
1039 | ||
ecc3218c | 1040 | ; (DEFPSMACRO name lambda-list macro-body) |
8cfc6fe9 VS |
1041 | ; (DEFPSMACRO/PS name lambda-list macro-body) |
1042 | ; (DEFPSMACRO+PS name lambda-list macro-body) | |
1043 | ; (DEFINE-PS-SYMBOL-MACRO symbol expansion) | |
1044 | ; (IMPORT-MACROS-FROM-LISP symbol*) | |
8e198a08 MB |
1045 | ; (MACROLET ({name lambda-list macro-body}*) body) |
1046 | ; (SYMBOL-MACROLET ({name macro-body}*) body) | |
49c50da4 | 1047 | ; (PS-GENSYM {string}) |
8e198a08 MB |
1048 | ; |
1049 | ; name ::= a Lisp symbol | |
1050 | ; lambda-list ::= a lambda list | |
f037cd4c VS |
1051 | ; macro-body ::= a Lisp body evaluating to Parenscript code |
1052 | ; body ::= a list of Parenscript statements | |
8e198a08 MB |
1053 | ; string ::= a string |
1054 | ||
f037cd4c | 1055 | ;;; Parenscript can be extended using macros, just like Lisp can be |
8e198a08 | 1056 | ;;; extended using Lisp macros. Using the special Lisp form |
f037cd4c | 1057 | ;;; `DEFPSMACRO', the Parenscript language can be |
ecc3218c | 1058 | ;;; extended. `DEFPSMACRO' adds the new macro to the toplevel macro |
f037cd4c | 1059 | ;;; environment, which is always accessible during Parenscript |
8e198a08 MB |
1060 | ;;; compilation. For example, the `1+' and `1-' operators are |
1061 | ;;; implemented using macros. | |
1062 | ||
ecc3218c | 1063 | (defpsmacro 1- (form) |
8e198a08 MB |
1064 | `(- ,form 1)) |
1065 | ||
ecc3218c | 1066 | (defpsmacro 1+ (form) |
8e198a08 MB |
1067 | `(+ ,form 1)) |
1068 | ||
f037cd4c VS |
1069 | ;;; A more complicated Parenscript macro example is the implementation |
1070 | ;;; of the `DOLIST' form (note how `PS-GENSYM', the Parenscript of | |
1071 | ;;; `GENSYM', is used to generate new Parenscript variable names): | |
8e198a08 | 1072 | |
d777a405 TC |
1073 | (defpsmacro dolist ((var array &optional (result nil result?)) &body body) |
1074 | (let ((idx (ps-gensym "_js_idx")) | |
1075 | (arrvar (ps-gensym "_js_arrvar"))) | |
1076 | `(do* (,var | |
1077 | (,arrvar ,array) | |
1078 | (,idx 0 (1+ ,idx))) | |
1079 | ((>= ,idx (slot-value ,arrvar 'length)) | |
1080 | ,@(when result? (list result))) | |
1081 | (setq ,var (aref ,arrvar ,idx)) | |
1082 | ,@body))) | |
8e198a08 | 1083 | |
f037cd4c VS |
1084 | ;;; Macros can be defined in Parenscript code itself (as opposed to |
1085 | ;;; from Lisp) by using the Parenscript `MACROLET' and `DEFMACRO' | |
8cfc6fe9 VS |
1086 | ;;; forms. Note that macros defined this way are defined in a null |
1087 | ;;; lexical environment (ex - (let ((x 1)) (defmacro baz (y) `(+ ,y | |
1088 | ;;; ,x))) will not work), since the surrounding Parenscript code is | |
1089 | ;;; just translated to JavaScript and not actually evaluated. | |
1d9f472a | 1090 | |
f037cd4c | 1091 | ;;; Parenscript also supports the use of macros defined in the |
ecc3218c | 1092 | ;;; underlying Lisp environment. Existing Lisp macros can be imported |
f037cd4c | 1093 | ;;; into the Parenscript macro environment by |
ecc3218c | 1094 | ;;; `IMPORT-MACROS-FROM-LISP'. This functionality enables code sharing |
f037cd4c | 1095 | ;;; between Parenscript and Lisp, and is useful in debugging since the |
ecc3218c VS |
1096 | ;;; full power of Lisp macroexpanders, editors and other supporting |
1097 | ;;; facilities can be used. However, it is important to note that the | |
f037cd4c | 1098 | ;;; macroexpansion of Lisp macros and Parenscript macros takes place |
ecc3218c VS |
1099 | ;;; in their own respective environments, and many Lisp macros |
1100 | ;;; (especially those provided by the Lisp implementation) expand into | |
f037cd4c | 1101 | ;;; code that is not usable by Parenscript. To make it easy for users |
ecc3218c | 1102 | ;;; to take advantage of these features, two additional macro |
f037cd4c | 1103 | ;;; definition facilities are provided by Parenscript: `DEFMACRO/PS' |
ecc3218c | 1104 | ;;; and `DEFMACRO+PS'. `DEFMACRO/PS' defines a Lisp macro and then |
f037cd4c | 1105 | ;;; imports it into the Parenscript macro environment, while |
ecc3218c | 1106 | ;;; `DEFMACRO+PS' defines two macros with the same name and expansion, |
f037cd4c | 1107 | ;;; one in Parenscript and one in Lisp. `DEFMACRO+PS' is used when the |
ecc3218c | 1108 | ;;; full 'macroexpand' of the Lisp macro yields code that cannot be |
f037cd4c | 1109 | ;;; used by Parenscript. |
8e198a08 | 1110 | |
f037cd4c | 1111 | ;;; Parenscript also supports symbol macros, which can be introduced |
8cfc6fe9 VS |
1112 | ;;; using the Parenscript form `SYMBOL-MACROLET' or defined in Lisp |
1113 | ;;; with `DEFINE-PS-SYMBOL-MACRO'. For example, the Parenscript | |
1114 | ;;; `WITH-SLOTS' is implemented using symbol macros. | |
8e198a08 | 1115 | |
8cfc6fe9 | 1116 | (defpsmacro with-slots (slots object &rest body) |
8e198a08 MB |
1117 | `(symbol-macrolet ,(mapcar #'(lambda (slot) |
1118 | `(,slot '(slot-value ,object ',slot))) | |
1119 | slots) | |
1120 | ,@body)) | |
1121 | ||
f037cd4c | 1122 | ;;;# The Parenscript namespace system |
5e74b5ce VS |
1123 | ;;;t \index{package} |
1124 | ;;;t \index{namespace} | |
1125 | ;;;t \index{PS-PACKAGE-PREFIX} | |
1126 | ||
0c542be0 | 1127 | ; (setf (PS-PACKAGE-PREFIX package-designator) string) |
5e74b5ce VS |
1128 | |
1129 | ;;; Although JavaScript does not offer namespacing or a package | |
f037cd4c | 1130 | ;;; system, Parenscript does provide a namespace mechanism for |
5e74b5ce | 1131 | ;;; generated JavaScript by integrating with the Common Lisp package |
f037cd4c | 1132 | ;;; system. Since Parenscript code is normally read in by the Lisp |
5e74b5ce VS |
1133 | ;;; reader, all symbols (except for uninterned ones, ie - those |
1134 | ;;; specified with the #: reader macro) have a Lisp package. By | |
1135 | ;;; default, no packages are prefixed. You can specify that symbols in | |
1136 | ;;; a particular package receive a prefix when translated to | |
1137 | ;;; JavaScript with the `PS-PACKAGE-PREFIX' place. | |
1138 | ||
7b8a74ee VS |
1139 | (defpackage "PS-REF.MY-LIBRARY" |
1140 | (:use "PARENSCRIPT")) | |
1141 | (setf (ps-package-prefix "PS-REF.MY-LIBRARY") "my_library_") | |
5e74b5ce | 1142 | |
7b8a74ee | 1143 | (defun ps-ref.my-library::library-function (x y) |
5e74b5ce | 1144 | (return (+ x y))) |
0c542be0 | 1145 | -> function my_library_libraryFunction(x, y) { |
5e74b5ce | 1146 | return x + y; |
5a69278c | 1147 | }; |
5e74b5ce | 1148 | |
0c542be0 VS |
1149 | ;;;# Identifier obfuscation |
1150 | ;;;t \index{obfuscation} | |
1151 | ;;;t \index{identifiers} | |
1152 | ;;;t \index{OBFUSCATE-PACKAGE} | |
1153 | ;;;t \index{UNOBFUSCATE-PACKAGE} | |
1154 | ||
7b8a74ee | 1155 | ; (OBFUSCATE-PACKAGE package-designator &optional symbol-map) |
0c542be0 VS |
1156 | ; (UNOBFUSCATE-PACKAGE package-designator) |
1157 | ||
f037cd4c | 1158 | ;;; Similar to the namespace mechanism, Parenscript provides a |
7b8a74ee VS |
1159 | ;;; facility to generate obfuscated identifiers in specified CL |
1160 | ;;; packages. The function `OBFUSCATE-PACKAGE' may optionally be | |
1161 | ;;; passed a hash-table or a closure that maps symbols to their | |
1162 | ;;; obfuscated counterparts. By default, the mapping is done using | |
1163 | ;;; `PS-GENSYM'. | |
1164 | ||
1165 | (defpackage "PS-REF.OBFUSCATE-ME") | |
1166 | (obfuscate-package "PS-REF.OBFUSCATE-ME" | |
1167 | (let ((code-pt-counter #x8CF0) | |
1168 | (symbol-map (make-hash-table))) | |
1169 | (lambda (symbol) | |
1170 | (or (gethash symbol symbol-map) | |
1171 | (setf (gethash symbol symbol-map) | |
1172 | (make-symbol (string (code-char (incf code-pt-counter))))))))) | |
1173 | ||
1174 | (defun ps-ref.obfuscate-me::a-function (a b ps-ref.obfuscate-me::foo) | |
1175 | (+ a (ps-ref.my-library::library-function b ps-ref.obfuscate-me::foo))) | |
1176 | -> function 賱(a, b, 賲) { | |
1177 | a + my_library_libraryFunction(b, 賲); | |
5a69278c | 1178 | }; |
0c542be0 VS |
1179 | |
1180 | ;;; The obfuscation and namespace facilities can be used on packages | |
1181 | ;;; at the same time. | |
1182 | ||
f037cd4c | 1183 | ;;;# The Parenscript Compiler |
8e198a08 | 1184 | ;;;t \index{compiler} |
f037cd4c | 1185 | ;;;t \index{Parenscript compiler} |
ecc3218c VS |
1186 | ;;;t \index{PS} |
1187 | ;;;t \index{PS*} | |
cb8f8e58 | 1188 | ;;;t \index{PS1*} |
ecc3218c | 1189 | ;;;t \index{PS-INLINE} |
cb8f8e58 | 1190 | ;;;t \index{PS-INLINE*} |
ecc3218c | 1191 | ;;;t \index{LISP} |
8e198a08 | 1192 | |
ecc3218c VS |
1193 | ; (PS &body body) |
1194 | ; (PS* &body body) | |
cb8f8e58 VS |
1195 | ; (PS1* parenscript-form) |
1196 | ; (PS-INLINE form &optional *js-string-delimiter*) | |
1197 | ; (PS-INLINE* form &optional *js-string-delimiter*) | |
1198 | ||
1199 | ; (LISP lisp-forms) | |
8e198a08 | 1200 | ; |
f037cd4c | 1201 | ; body ::= Parenscript statements comprising an implicit `PROGN' |
ecc3218c | 1202 | |
f037cd4c | 1203 | ;;; For static Parenscript code, the macro `PS' compiles the provided |
cb8f8e58 VS |
1204 | ;;; forms at Common Lisp macro-expansion time. `PS*' and `PS1*' |
1205 | ;;; evaluate their arguments and then compile them. All these forms | |
1206 | ;;; except for `PS1*' treat the given forms as an implicit | |
1207 | ;;; `PROGN'. | |
1208 | ||
f037cd4c | 1209 | ;;; `PS-INLINE' and `PS-INLINE*' take a single Parenscript form and |
cb8f8e58 VS |
1210 | ;;; output a string starting with "javascript:" that can be used in |
1211 | ;;; HTML node attributes. As well, they provide an argument to bind | |
1212 | ;;; the value of *js-string-delimiter* to control the value of the | |
1213 | ;;; JavaScript string escape character to be compatible with whatever | |
1214 | ;;; the HTML generation mechanism is used (for example, if HTML | |
1215 | ;;; strings are delimited using #\', using #\" will avoid conflicts | |
1216 | ;;; without requiring the output JavaScript code to be escaped). By | |
1217 | ;;; default the value is taken from *js-inline-string-delimiter*. | |
1218 | ||
f037cd4c | 1219 | ;;; Parenscript can also call out to arbitrary Common Lisp code at |
cb8f8e58 VS |
1220 | ;;; code output time using the special form `LISP'. The form provided |
1221 | ;;; to `LISP' is evaluated, and its result is compiled as though it | |
f037cd4c | 1222 | ;;; were Parenscript code. For `PS' and `PS-INLINE', the Parenscript |
cb8f8e58 VS |
1223 | ;;; output code is generated at macro-expansion time, and the `LISP' |
1224 | ;;; statements are inserted inline and have access to the enclosing | |
1225 | ;;; Common Lisp lexical environment. `PS*' and `PS1*' evaluate the | |
1226 | ;;; `LISP' forms with eval, providing them access to the current | |
1227 | ;;; dynamic environment only. |