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