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