| 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 |
| 38 | ;;; few simple rules. Special characters `!', `?', `#', `@', `%', |
| 39 | ;;; '/', `*' and `+' get replaced by their written-out equivalents |
| 40 | ;;; "bang", "what", "hash", "at", "percent", "slash", |
| 41 | ;;; "start" and "plus" respectively. The `$' character is untouched. |
| 42 | |
| 43 | !?#@% => bangwhathashatpercent |
| 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 |
| 105 | ;;; values. Numbers are compiled into JavaScript numbers. |
| 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 | |
| 123 | "foobar" => 'foobar' |
| 124 | |
| 125 | "bratzel bub" => 'bratzel bub' |
| 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 | |
| 148 | (array) => [ ] |
| 149 | |
| 150 | (array 1 2 3) => [ 1, 2, 3 ] |
| 151 | |
| 152 | (array (array 2 3) |
| 153 | (array "foobar" "bratzel bub")) |
| 154 | => [ [ 2, 3 ], [ 'foobar', 'bratzel bub' ] ] |
| 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")) |
| 167 | => new Array(new Array(2, 3), new Array('foobar', 'bratzel bub')) |
| 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) |
| 200 | => { foo : 'bar', |
| 201 | blorg : 1 } |
| 202 | |
| 203 | (create :foo "hihi" |
| 204 | :blorg (array 1 2 3) |
| 205 | :another-object (create :schtrunz 1)) |
| 206 | => { foo : 'hihi', |
| 207 | blorg : [ 1, 2, 3 ], |
| 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)) |
| 225 | => this.a + this.b + this.c; |
| 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 | |
| 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/ |
| 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 |
| 264 | ;;; boolean equivalents `true' and `false'. |
| 265 | |
| 266 | T => true |
| 267 | |
| 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 |
| 364 | ;;; operator as function name. |
| 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 | |
| 377 | ;;; Note that the resulting expression is correctly parenthized, |
| 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 | |
| 386 | ;;; The pre/post increment and decrement operators are also |
| 387 | ;;; available. `INCF' and `DECF' are the pre-incrementing and |
| 388 | ;;; pre-decrementing operators, and `++' and `--' are the |
| 389 | ;;; post-decrementing version of the operators. These operators can |
| 390 | ;;; take only one argument. |
| 391 | |
| 392 | (++ i) => i++ |
| 393 | |
| 394 | (-- i) => i-- |
| 395 | |
| 396 | (incf i) => ++i |
| 397 | |
| 398 | (decf i) => --i |
| 399 | |
| 400 | ;;; The `1+' and `1-' operators are shortforms for adding and |
| 401 | ;;; substracting 1. |
| 402 | |
| 403 | (1- i) => i - 1 |
| 404 | |
| 405 | (1+ i) => i + 1 |
| 406 | |
| 407 | ;;; The `not' operator actually optimizes the code a bit. If `not' is |
| 408 | ;;; used on another boolean-returning operator, the operator is |
| 409 | ;;; reversed. |
| 410 | |
| 411 | (not (< i 2)) => i >= 2 |
| 412 | |
| 413 | (not (eql i 2)) => i != 2 |
| 414 | |
| 415 | ;;;# Body forms |
| 416 | ;;;t \index{body form} |
| 417 | ;;;t \index{PROGN} |
| 418 | ;;;t \index{body statement} |
| 419 | |
| 420 | ; (PROGN {statement}*) in statement context |
| 421 | ; (PROGN {expression}*) in expression context |
| 422 | ; |
| 423 | ; statement ::= a ParenScript statement |
| 424 | ; expression ::= a ParenScript expression |
| 425 | |
| 426 | ;;; The `PROGN' special form defines a sequence of statements when |
| 427 | ;;; used in a statement context, or sequence of expression when used |
| 428 | ;;; in an expression context. The `PROGN' special form is added |
| 429 | ;;; implicitly around the branches of conditional executions forms, |
| 430 | ;;; function declarations and iteration constructs. |
| 431 | |
| 432 | ;;; For example, in a statement context: |
| 433 | |
| 434 | (progn (blorg i) (blafoo i)) |
| 435 | => blorg(i); |
| 436 | blafoo(i); |
| 437 | |
| 438 | ;;; In an expression context: |
| 439 | |
| 440 | (+ i (progn (blorg i) (blafoo i))) |
| 441 | => i + (blorg(i), blafoo(i)) |
| 442 | |
| 443 | ;;; A `PROGN' form doesn't lead to additional indentation or |
| 444 | ;;; additional braces around it's body. |
| 445 | |
| 446 | ;;;# Function Definition |
| 447 | ;;;t \index{function} |
| 448 | ;;;t \index{method} |
| 449 | ;;;t \index{function definition} |
| 450 | ;;;t \index{DEFUN} |
| 451 | ;;;t \index{LAMBDA} |
| 452 | ;;;t \index{closure} |
| 453 | ;;;t \index{anonymous function} |
| 454 | |
| 455 | ; (DEFUN name ({argument}*) body) |
| 456 | ; (LAMBDA ({argument}*) body) |
| 457 | ; |
| 458 | ; name ::= a Lisp Symbol |
| 459 | ; argument ::= a Lisp symbol |
| 460 | ; body ::= a list of ParenScript statements |
| 461 | |
| 462 | ;;; As in Lisp, functions are defined using the `DEFUN' form, which |
| 463 | ;;; takes a name, a list of arguments, and a function body. An |
| 464 | ;;; implicit `PROGN' is added around the body statements. |
| 465 | |
| 466 | (defun a-function (a b) |
| 467 | (return (+ a b))) |
| 468 | => function aFunction(a, b) { |
| 469 | return a + b; |
| 470 | } |
| 471 | |
| 472 | ;;; Anonymous functions can be created using the `LAMBDA' form, which |
| 473 | ;;; is the same as `DEFUN', but without function name. In fact, |
| 474 | ;;; `LAMBDA' creates a `DEFUN' with an empty function name. |
| 475 | |
| 476 | (lambda (a b) (return (+ a b))) |
| 477 | => function (a, b) { |
| 478 | return a + b; |
| 479 | } |
| 480 | |
| 481 | ;;;# Assignment |
| 482 | ;;;t \index{assignment} |
| 483 | ;;;t \index{SETF} |
| 484 | ;;;t \index{assignment operator} |
| 485 | |
| 486 | ; (SETF {lhs rhs}*) |
| 487 | ; |
| 488 | ; lhs ::= a ParenScript left hand side expression |
| 489 | ; rhs ::= a ParenScript expression |
| 490 | |
| 491 | ;;; Assignment is done using the `SETF' form, which is transformed |
| 492 | ;;; into a series of assignments using the JavaScript `=' operator. |
| 493 | |
| 494 | (setf a 1) => a = 1 |
| 495 | |
| 496 | (setf a 2 b 3 c 4 x (+ a b c)) |
| 497 | => a = 2; |
| 498 | b = 3; |
| 499 | c = 4; |
| 500 | x = a + b + c; |
| 501 | |
| 502 | ;;; The `SETF' form can transform assignments of a variable with an |
| 503 | ;;; operator expression using this variable into a more "efficient" |
| 504 | ;;; assignment operator form. For example: |
| 505 | |
| 506 | (setf a (1+ a)) => a++ |
| 507 | |
| 508 | (setf a (* 2 3 4 a 4 a)) => a *= 2 * 3 * 4 * 4 * a |
| 509 | |
| 510 | (setf a (- 1 a)) => a = 1 - a |
| 511 | |
| 512 | ;;;# Single argument statements |
| 513 | ;;;t \index{single-argument statement} |
| 514 | ;;;t \index{RETURN} |
| 515 | ;;;t \index{THROW} |
| 516 | ;;;t \index{THROW} |
| 517 | ;;;t \index{function} |
| 518 | |
| 519 | ; (RETURN {value}?) |
| 520 | ; (THROW {value}?) |
| 521 | ; |
| 522 | ; value ::= a ParenScript expression |
| 523 | |
| 524 | ;;; The single argument statements `return' and `throw' are generated |
| 525 | ;;; by the form `RETURN' and `THROW'. `THROW' has to be used inside a |
| 526 | ;;; `TRY' form. `RETURN' is used to return a value from a function |
| 527 | ;;; call. |
| 528 | |
| 529 | (return 1) => return 1 |
| 530 | |
| 531 | (throw "foobar") => throw 'foobar' |
| 532 | |
| 533 | ;;;# Single argument expression |
| 534 | ;;;t \index{single-argument expression} |
| 535 | ;;;t \index{object creation} |
| 536 | ;;;t \index{object deletion} |
| 537 | ;;;t \index{DELETE} |
| 538 | ;;;t \index{VOID} |
| 539 | ;;;t \index{TYPEOF} |
| 540 | ;;;t \index{INSTANCEOF} |
| 541 | ;;;t \index{NEW} |
| 542 | ;;;t \index{new} |
| 543 | |
| 544 | ; (DELETE {value}) |
| 545 | ; (VOID {value}) |
| 546 | ; (TYPEOF {value}) |
| 547 | ; (INSTANCEOF {value}) |
| 548 | ; (NEW {value}) |
| 549 | ; |
| 550 | ; value ::= a ParenScript expression |
| 551 | |
| 552 | ;;; The single argument expressions `delete', `void', `typeof', |
| 553 | ;;; `instanceof' and `new' are generated by the forms `DELETE', |
| 554 | ;;; `VOID', `TYPEOF', `INSTANCEOF' and `NEW'. They all take a |
| 555 | ;;; ParenScript expression. |
| 556 | |
| 557 | (delete (new (*foobar 2 3 4))) => delete new Foobar(2, 3, 4) |
| 558 | |
| 559 | (if (= (typeof blorg) *string) |
| 560 | (alert (+ "blorg is a string: " blorg)) |
| 561 | (alert "blorg is not a string")) |
| 562 | => if (typeof blorg == String) { |
| 563 | alert('blorg is a string: ' + blorg); |
| 564 | } else { |
| 565 | alert('blorg is not a string'); |
| 566 | } |
| 567 | |
| 568 | ;;;# Conditional Statements |
| 569 | ;;;t \index{conditional statements} |
| 570 | ;;;t \index{IF} |
| 571 | ;;;t \index{WHEN} |
| 572 | ;;;t \index{UNLESS} |
| 573 | ;;;t \index{conditionals} |
| 574 | |
| 575 | ; (IF conditional then {else}) |
| 576 | ; (WHEN condition then) |
| 577 | ; (UNLESS condition then) |
| 578 | ; |
| 579 | ; condition ::= a ParenScript expression |
| 580 | ; then ::= a ParenScript statement in statement context, a |
| 581 | ; ParenScript expression in expression context |
| 582 | ; else ::= a ParenScript statement in statement context, a |
| 583 | ; ParenScript expression in expression context |
| 584 | |
| 585 | ;;; The `IF' form compiles to the `if' javascript construct. An |
| 586 | ;;; explicit `PROGN' around the then branch and the else branch is |
| 587 | ;;; needed if they consist of more than one statement. When the `IF' |
| 588 | ;;; form is used in an expression context, a JavaScript `?', `:' |
| 589 | ;;; operator form is generated. |
| 590 | |
| 591 | (if (blorg.is-correct) |
| 592 | (progn (carry-on) (return i)) |
| 593 | (alert "blorg is not correct!")) |
| 594 | => if (blorg.isCorrect()) { |
| 595 | carryOn(); |
| 596 | return i; |
| 597 | } else { |
| 598 | alert('blorg is not correct!'); |
| 599 | } |
| 600 | |
| 601 | (+ i (if (blorg.add-one) 1 2)) |
| 602 | => i + (blorg.addOne() ? 1 : 2) |
| 603 | |
| 604 | ;;; The `WHEN' and `UNLESS' forms can be used as shortcuts for the |
| 605 | ;;; `IF' form. |
| 606 | |
| 607 | (when (blorg.is-correct) |
| 608 | (carry-on) |
| 609 | (return i)) |
| 610 | => if (blorg.isCorrect()) { |
| 611 | carryOn(); |
| 612 | return i; |
| 613 | } |
| 614 | |
| 615 | (unless (blorg.is-correct) |
| 616 | (alert "blorg is not correct!")) |
| 617 | => if (!blorg.isCorrect()) { |
| 618 | alert('blorg is not correct!'); |
| 619 | } |
| 620 | |
| 621 | ;;;# Variable declaration |
| 622 | ;;;t \index{variable} |
| 623 | ;;;t \index{variable declaration} |
| 624 | ;;;t \index{binding} |
| 625 | ;;;t \index{scoping} |
| 626 | ;;;t \index{DEFVAR} |
| 627 | ;;;t \index{LET} |
| 628 | |
| 629 | ; (DEFVAR var {value}?) |
| 630 | ; (LET ({var | (var value)) body) |
| 631 | ; |
| 632 | ; var ::= a Lisp symbol |
| 633 | ; value ::= a ParenScript expression |
| 634 | ; body ::= a list of ParenScript statements |
| 635 | |
| 636 | ;;; Variables (either local or global) can be declared using the |
| 637 | ;;; `DEFVAR' form, which is similar to its equivalent form in |
| 638 | ;;; Lisp. The `DEFVAR' is converted to "var ... = ..." form in |
| 639 | ;;; JavaScript. |
| 640 | |
| 641 | (defvar *a* (array 1 2 3)) => var A = [ 1, 2, 3 ]; |
| 642 | |
| 643 | (if (= i 1) |
| 644 | (progn (defvar blorg "hallo") |
| 645 | (alert blorg)) |
| 646 | (progn (defvar blorg "blitzel") |
| 647 | (alert blorg))) |
| 648 | => if (i == 1) { |
| 649 | var blorg = 'hallo'; |
| 650 | alert(blorg); |
| 651 | } else { |
| 652 | var blorg = 'blitzel'; |
| 653 | alert(blorg); |
| 654 | } |
| 655 | |
| 656 | ;;; A more lispy way to declare local variable is to use the `LET' |
| 657 | ;;; form, which is similar to its Lisp form. |
| 658 | |
| 659 | (if (= i 1) |
| 660 | (let ((blorg "hallo")) |
| 661 | (alert blorg)) |
| 662 | (let ((blorg "blitzel")) |
| 663 | (alert blorg))) |
| 664 | => if (i == 1) { |
| 665 | var blorg = 'hallo'; |
| 666 | alert(blorg); |
| 667 | } else { |
| 668 | var blorg = 'blitzel'; |
| 669 | alert(blorg); |
| 670 | } |
| 671 | |
| 672 | ;;; However, beware that scoping in Lisp and JavaScript are quite |
| 673 | ;;; different. For example, don't rely on closures capturing local |
| 674 | ;;; variables in the way you'd think they would. |
| 675 | |
| 676 | ;;;# Iteration constructs |
| 677 | ;;;t \index{iteration} |
| 678 | ;;;t \index{iteration construct} |
| 679 | ;;;t \index{loop} |
| 680 | ;;;t \index{array traversal} |
| 681 | ;;;t \index{property} |
| 682 | ;;;t \index{object property} |
| 683 | ;;;t \index{DO} |
| 684 | ;;;t \index{DOTIMES} |
| 685 | ;;;t \index{DOLIST} |
| 686 | ;;;t \index{DOEACH} |
| 687 | ;;;t \index{WHILE} |
| 688 | |
| 689 | ; (DO ({var | (var {init}? {step}?)}*) (end-test) body) |
| 690 | ; (DOTIMES (var numeric-form) body) |
| 691 | ; (DOLIST (var list-form) body) |
| 692 | ; (DOEACH (var object) body) |
| 693 | ; (WHILE end-test body) |
| 694 | ; |
| 695 | ; var ::= a Lisp symbol |
| 696 | ; numeric-form ::= a ParenScript expression resulting in a number |
| 697 | ; list-form ::= a ParenScript expression resulting in an array |
| 698 | ; object ::= a ParenScript expression resulting in an object |
| 699 | ; init ::= a ParenScript expression |
| 700 | ; step ::= a ParenScript expression |
| 701 | ; end-test ::= a ParenScript expression |
| 702 | ; body ::= a list of ParenScript statements |
| 703 | |
| 704 | ;;; The `DO' form, which is similar to its Lisp form, is transformed |
| 705 | ;;; into a JavaScript `for' statement. Note that the ParenScript `DO' |
| 706 | ;;; form does not have a return value, that is because `for' is a |
| 707 | ;;; statement and not an expression in JavaScript. |
| 708 | |
| 709 | (do ((i 0 (1+ i)) |
| 710 | (l (aref blorg i) (aref blorg i))) |
| 711 | ((or (= i blorg.length) |
| 712 | (eql l "Fumitastic"))) |
| 713 | (document.write (+ "L is " l))) |
| 714 | => for (var i = 0, l = blorg[i]; |
| 715 | !(i == blorg.length || l == 'Fumitastic'); |
| 716 | i = i + 1, l = blorg[i]) { |
| 717 | document.write('L is ' + l); |
| 718 | } |
| 719 | |
| 720 | ;;; The `DOTIMES' form, which lets a variable iterate from 0 upto an |
| 721 | ;;; end value, is a shortcut for `DO'. |
| 722 | |
| 723 | (dotimes (i blorg.length) |
| 724 | (document.write (+ "L is " (aref blorg i)))) |
| 725 | => for (var i = 0; i < blorg.length; i = i + 1) { |
| 726 | document.write('L is ' + blorg[i]); |
| 727 | } |
| 728 | |
| 729 | ;;; The `DOLIST' form is a shortcut for iterating over an array. Note |
| 730 | ;;; that this form creates temporary variables using a function called |
| 731 | ;;; `JS-GENSYM', which is similar to its Lisp counterpart `GENSYM'. |
| 732 | |
| 733 | (dolist (l blorg) |
| 734 | (document.write (+ "L is " l))) |
| 735 | => { |
| 736 | var tmpArr1 = blorg; |
| 737 | for (var tmpI2 = 0; tmpI2 < tmpArr1.length; |
| 738 | tmpI2 = tmpI2 + 1) { |
| 739 | var l = tmpArr1[tmpI2]; |
| 740 | document.write('L is ' + l); |
| 741 | }; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | ;;; The `DOEACH' form is converted to a `for (var .. in ..)' form in |
| 746 | ;;; JavaScript. It is used to iterate over the enumerable properties |
| 747 | ;;; of an object. |
| 748 | |
| 749 | (doeach (i object) |
| 750 | (document.write (+ i " is " (aref object i)))) |
| 751 | => for (var i in object) { |
| 752 | document.write(i + ' is ' + object[i]); |
| 753 | } |
| 754 | |
| 755 | ;;; The `WHILE' form is transformed to the JavaScript form `while', |
| 756 | ;;; and loops until a termination test evaluates to false. |
| 757 | |
| 758 | (while (film.is-not-finished) |
| 759 | (this.eat (new *popcorn))) |
| 760 | => while (film.isNotFinished()) { |
| 761 | this.eat(new Popcorn); |
| 762 | } |
| 763 | |
| 764 | ;;;# The `CASE' statement |
| 765 | ;;;t \index{CASE} |
| 766 | ;;;t \index{SWITCH} |
| 767 | ;;;t \index{switch} |
| 768 | |
| 769 | ; (CASE case-value clause*) |
| 770 | ; |
| 771 | ; clause ::= (value body) | ((value*) body) | t-clause |
| 772 | ; case-value ::= a ParenScript expression |
| 773 | ; value ::= a ParenScript expression |
| 774 | ; t-clause ::= {t | otherwise | default} body |
| 775 | ; body ::= a list of ParenScript statements |
| 776 | |
| 777 | ;;; The Lisp `CASE' form is transformed to a `switch' statement in |
| 778 | ;;; JavaScript. Note that `CASE' is not an expression in |
| 779 | ;;; ParenScript. |
| 780 | |
| 781 | (case (aref blorg i) |
| 782 | ((1 "one") (alert "one")) |
| 783 | (2 (alert "two")) |
| 784 | (t (alert "default clause"))) |
| 785 | => switch (blorg[i]) { |
| 786 | case 1: ; |
| 787 | case 'one': |
| 788 | alert('one'); |
| 789 | break; |
| 790 | case 2: |
| 791 | alert('two'); |
| 792 | break; |
| 793 | default: alert('default clause'); |
| 794 | } |
| 795 | |
| 796 | ; (SWITCH case-value clause*) |
| 797 | ; clause ::= (value body) | (default body) |
| 798 | |
| 799 | ;;; The `SWITCH' form is the equivalent to a javascript switch statement. |
| 800 | ;;; No break statements are inserted, and the default case is named `DEFAULT'. |
| 801 | ;;; The `CASE' form should be prefered in most cases. |
| 802 | |
| 803 | (switch (aref blorg i) |
| 804 | (1 (alert "If I get here")) |
| 805 | (2 (alert "I also get here")) |
| 806 | (default (alert "I always get here"))) |
| 807 | => switch (blorg[i]) { |
| 808 | case 1: alert('If I get here'); |
| 809 | case 2: alert('I also get here'); |
| 810 | default: alert('I always get here'); |
| 811 | } |
| 812 | |
| 813 | |
| 814 | ;;;# The `WITH' statement |
| 815 | ;;;t \index{WITH} |
| 816 | ;;;t \index{dynamic scope} |
| 817 | ;;;t \index{binding} |
| 818 | ;;;t \index{scoping} |
| 819 | ;;;t \index{closure} |
| 820 | |
| 821 | ; (WITH object body) |
| 822 | ; |
| 823 | ; object ::= a ParenScript expression evaluating to an object |
| 824 | ; body ::= a list of ParenScript statements |
| 825 | |
| 826 | ;;; The `WITH' form is compiled to a JavaScript `with' statements, and |
| 827 | ;;; adds the object `object' as an intermediary scope objects when |
| 828 | ;;; executing the body. |
| 829 | |
| 830 | (with (create :foo "foo" :i "i") |
| 831 | (alert (+ "i is now intermediary scoped: " i))) |
| 832 | => with ({ foo : 'foo', |
| 833 | i : 'i' }) { |
| 834 | alert('i is now intermediary scoped: ' + i); |
| 835 | } |
| 836 | |
| 837 | ;;;# The `TRY' statement |
| 838 | ;;;t \index{TRY} |
| 839 | ;;;t \index{CATCH} |
| 840 | ;;;t \index{FINALLY} |
| 841 | ;;;t \index{exception} |
| 842 | ;;;t \index{error handling} |
| 843 | |
| 844 | ; (TRY body {(:CATCH (var) body)}? {(:FINALLY body)}?) |
| 845 | ; |
| 846 | ; body ::= a list of ParenScript statements |
| 847 | ; var ::= a Lisp symbol |
| 848 | |
| 849 | ;;; The `TRY' form is converted to a JavaScript `try' statement, and |
| 850 | ;;; can be used to catch expressions thrown by the `THROW' |
| 851 | ;;; form. The body of the catch clause is invoked when an exception |
| 852 | ;;; is catched, and the body of the finally is always invoked when |
| 853 | ;;; leaving the body of the `TRY' form. |
| 854 | |
| 855 | (try (throw "i") |
| 856 | (:catch (error) |
| 857 | (alert (+ "an error happened: " error))) |
| 858 | (:finally |
| 859 | (alert "Leaving the try form"))) |
| 860 | => try { |
| 861 | throw 'i'; |
| 862 | } catch (error) { |
| 863 | alert('an error happened: ' + error); |
| 864 | } finally { |
| 865 | alert('Leaving the try form'); |
| 866 | } |
| 867 | |
| 868 | ;;;# The HTML Generator |
| 869 | ;;;t \index{HTML} |
| 870 | ;;;t \index{HTML generation} |
| 871 | ;;;t \index{CSS} |
| 872 | ;;;t \index{CSS generation} |
| 873 | |
| 874 | |
| 875 | ; (HTML html-expression) |
| 876 | |
| 877 | ;;; The HTML generator of ParenScript is very similar to the HTML |
| 878 | ;;; generator included in AllegroServe. It accepts the same input |
| 879 | ;;; forms as the AllegroServer HTML generator. However, non-HTML |
| 880 | ;;; construct are compiled to JavaScript by the ParenScript |
| 881 | ;;; compiler. The resulting expression is a JavaScript expression. |
| 882 | |
| 883 | (html ((:a :href "foobar") "blorg")) |
| 884 | => '<a href=\"foobar\">blorg</a>' |
| 885 | |
| 886 | (html ((:a :href (generate-a-link)) "blorg")) |
| 887 | => '<a href=\"' + generateALink() + '\">blorg</a>' |
| 888 | |
| 889 | ;;; We can recursively call the JS compiler in a HTML expression. |
| 890 | |
| 891 | (document.write |
| 892 | (html ((:a :href "#" |
| 893 | :onclick (js-inline (transport))) "link"))) |
| 894 | => document.write |
| 895 | ('<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">link</a>') |
| 896 | |
| 897 | ; (CSS-INLINE css-expression) |
| 898 | |
| 899 | ;;; Stylesheets can also be created in ParenScript. |
| 900 | |
| 901 | (css-inline :color "red" |
| 902 | :font-size "x-small") |
| 903 | => 'color:red;font-size:x-small' |
| 904 | |
| 905 | (defun make-color-div(color-name) |
| 906 | (return (html ((:div :style (css-inline :color color-name)) |
| 907 | color-name " looks like this.")))) |
| 908 | => function makeColorDiv(colorName) { |
| 909 | return '<div style=\"' + ('color:' + colorName) + '\">' + colorName |
| 910 | + ' looks like this.</div>'; |
| 911 | } |
| 912 | |
| 913 | ;;;# Macrology |
| 914 | ;;;t \index{macro} |
| 915 | ;;;t \index{macrology} |
| 916 | ;;;t \index{DEFJSMACRO} |
| 917 | ;;;t \index{MACROLET} |
| 918 | ;;;t \index{SYMBOL-MACROLET} |
| 919 | ;;;t \index{JS-GENSYM} |
| 920 | ;;;t \index{compiler} |
| 921 | |
| 922 | ; (DEFJSMACRO name lambda-list macro-body) |
| 923 | ; (MACROLET ({name lambda-list macro-body}*) body) |
| 924 | ; (SYMBOL-MACROLET ({name macro-body}*) body) |
| 925 | ; (JS-GENSYM {string}?) |
| 926 | ; |
| 927 | ; name ::= a Lisp symbol |
| 928 | ; lambda-list ::= a lambda list |
| 929 | ; macro-body ::= a Lisp body evaluating to ParenScript code |
| 930 | ; body ::= a list of ParenScript statements |
| 931 | ; string ::= a string |
| 932 | |
| 933 | ;;; ParenScript can be extended using macros, just like Lisp can be |
| 934 | ;;; extended using Lisp macros. Using the special Lisp form |
| 935 | ;;; `DEFJSMACRO', the ParenScript language can be |
| 936 | ;;; extended. `DEFJSMACRO' adds the new macro to the toplevel macro |
| 937 | ;;; environment, which is always accessible during ParenScript |
| 938 | ;;; compilation. For example, the `1+' and `1-' operators are |
| 939 | ;;; implemented using macros. |
| 940 | |
| 941 | (defjsmacro 1- (form) |
| 942 | `(- ,form 1)) |
| 943 | |
| 944 | (defjsmacro 1+ (form) |
| 945 | `(+ ,form 1)) |
| 946 | |
| 947 | ;;; A more complicated ParenScript macro example is the implementation |
| 948 | ;;; of the `DOLIST' form (note how `JS-GENSYM', the ParenScript of |
| 949 | ;;; `GENSYM', is used to generate new ParenScript variable names): |
| 950 | |
| 951 | (defjsmacro dolist (i-array &rest body) |
| 952 | (let ((var (first i-array)) |
| 953 | (array (second i-array)) |
| 954 | (arrvar (js-gensym "arr")) |
| 955 | (idx (js-gensym "i"))) |
| 956 | `(let ((,arrvar ,array)) |
| 957 | (do ((,idx 0 (++ ,idx))) |
| 958 | ((>= ,idx (slot-value ,arrvar 'length))) |
| 959 | (let ((,var (aref ,arrvar ,idx))) |
| 960 | ,@body))))) |
| 961 | |
| 962 | ;;; Macros can be added dynamically to the macro environment by using |
| 963 | ;;; the ParenScript `MACROLET' form (note that while `DEFJSMACRO' is a |
| 964 | ;;; Lisp form, `MACROLET' and `SYMBOL-MACROLET' are ParenScript forms). |
| 965 | |
| 966 | ;;; ParenScript also supports symbol macros, which can be introduced |
| 967 | ;;; using the ParenScript form `SYMBOL-MACROLET'. A new macro |
| 968 | ;;; environment is created and added to the current macro environment |
| 969 | ;;; list while compiling the body of the `SYMBOL-MACROLET' form. For |
| 970 | ;;; example, the ParenScript `WITH-SLOTS' is implemented using symbol |
| 971 | ;;; macros. |
| 972 | |
| 973 | (defjsmacro with-slots (slots object &rest body) |
| 974 | `(symbol-macrolet ,(mapcar #'(lambda (slot) |
| 975 | `(,slot '(slot-value ,object ',slot))) |
| 976 | slots) |
| 977 | ,@body)) |
| 978 | |
| 979 | ;;;# The ParenScript Compiler |
| 980 | ;;;t \index{compiler} |
| 981 | ;;;t \index{ParenScript compiler} |
| 982 | ;;;t \index{JS-COMPILE} |
| 983 | ;;;t \index{JS-TO-STRINGS} |
| 984 | ;;;t \index{JS-TO-STATEMENT-STRINGS} |
| 985 | ;;;t \index{JS-TO-STRING} |
| 986 | ;;;t \index{JS-TO-LINE} |
| 987 | ;;;t \index{JS} |
| 988 | ;;;t \index{JS-INLINE} |
| 989 | ;;;t \index{JS-FILE} |
| 990 | ;;;t \index{JS-SCRIPT} |
| 991 | ;;;t \index{nested compilation} |
| 992 | |
| 993 | ; (JS-COMPILE expr) |
| 994 | ; (JS-TO-STRINGS compiled-expr position) |
| 995 | ; (JS-TO-STATEMENT-STRINGS compiled-expr position) |
| 996 | ; |
| 997 | ; compiled-expr ::= a compiled ParenScript expression |
| 998 | ; position ::= a column number |
| 999 | ; |
| 1000 | ; (JS-TO-STRING expression) |
| 1001 | ; (JS-TO-LINE expression) |
| 1002 | ; |
| 1003 | ; expression ::= a Lisp list of ParenScript code |
| 1004 | ; |
| 1005 | ; (JS body) |
| 1006 | ; (JS-INLINE body) |
| 1007 | ; (JS-FILE body) |
| 1008 | ; (JS-SCRIPT body) |
| 1009 | ; |
| 1010 | ; body ::= a list of ParenScript statements |
| 1011 | |
| 1012 | ;;; The ParenScript compiler can be invoked from withing Lisp and from |
| 1013 | ;;; within ParenScript itself. The primary API function is |
| 1014 | ;;; `JS-COMPILE', which takes a list of ParenScript, and returns an |
| 1015 | ;;; internal object representing the compiled ParenScript. |
| 1016 | |
| 1017 | (js-compile '(foobar 1 2)) |
| 1018 | => #<JS::FUNCTION-CALL {584AA5DD}> |
| 1019 | |
| 1020 | ;;; This internal object can be transformed to a string using the |
| 1021 | ;;; methods `JS-TO-STRINGS' and `JS-TO-STATEMENT-STRINGS', which |
| 1022 | ;;; interpret the ParenScript in expression and in statement context |
| 1023 | ;;; respectively. They take an additional parameter indicating the |
| 1024 | ;;; start-position on a line (please note that the indentation code is |
| 1025 | ;;; not perfect, and this string interface will likely be |
| 1026 | ;;; changed). They return a list of strings, where each string |
| 1027 | ;;; represents a new line of JavaScript code. They can be joined |
| 1028 | ;;; together to form a single string. |
| 1029 | |
| 1030 | (js-to-strings (js-compile '(foobar 1 2)) 0) |
| 1031 | => ("foobar(1, 2)") |
| 1032 | |
| 1033 | ;;; As a shortcut, ParenScript provides the functions `JS-TO-STRING' |
| 1034 | ;;; and `JS-TO-LINE', which return the JavaScript string of the |
| 1035 | ;;; compiled expression passed as an argument. |
| 1036 | |
| 1037 | (js-to-string '(foobar 1 2)) |
| 1038 | => "foobar(1, 2)" |
| 1039 | |
| 1040 | ;;; For static ParenScript code, the macros `JS', `JS-INLINE', |
| 1041 | ;;; `JS-FILE' and `JS-SCRIPT' avoid the need to quote the ParenScript |
| 1042 | ;;; expression. All these forms add an implicit `PROGN' form around |
| 1043 | ;;; the body. `JS' returns a string of the compiled body, where the |
| 1044 | ;;; other expression return an expression that can be embedded in a |
| 1045 | ;;; HTML generation construct using the AllegroServe HTML |
| 1046 | ;;; generator. `JS-SCRIPT' generates a "SCRIPT" node, `JS-INLINE' |
| 1047 | ;;; generates a string to be used in node attributs, and `JS-FILE' |
| 1048 | ;;; prints the compiled ParenScript code to the HTML stream. |
| 1049 | |
| 1050 | ;;; These macros are also available inside ParenScript itself, and |
| 1051 | ;;; generate strings that can be used inside ParenScript code. Note |
| 1052 | ;;; that `JS-INLINE' in ParenScript is not the same `JS-INLINE' form |
| 1053 | ;;; as in Lisp, for example. The same goes for the other compilation |
| 1054 | ;;; macros. |
| 1055 | |