PS &body body)PS-TO-STREAM stream &body body)PS* &rest body)PS-DOC &body body)PS-DOC* &body body)PS-INLINE form &optional *JS-STRING-DELIMITER*)PS-INLINE* form &optional *JS-STRING-DELIMITER*)PS-COMPILE-STREAM stream)PS-COMPILE-FILE file)LISP lisp-forms)SYMBOL-TO-JS-STRING symbol)*JS-TARGET-VERSION**PARENSCRIPT-STREAM**JS-STRING-DELIMITER**JS-INLINE-STRING-DELIMITER**PS-PRINT-PRETTY**INDENT-NUM-SPACES**PS-READ-FUNCTION*PROGNThe difference between the regular and * versions
of the Parenscript compiler forms is roughly the difference
between COMPILE
and EVAL. The * forms are functions
that do all compilation when they are evaluated, while the
regular forms are macros that do almost all (except for the use
of the LISP special form, see below) compilation at
macro-expansion time.
PS and PS* are the main interfaces to
the Parenscript compiler. They come with PS-DOC
and PS-DOC* counterparts which compile the given
code
with *PS-GENSYM-COUNTER*
bound to 0, and are useful for writing automated tests.
By default, Parenscript writes output to a string. You can
output directly to a stream in one of two ways: either by
using PS-TO-STREAM instead of PS, or by
binding *PARENSCRIPT-STREAM* before
calling PS*.
PS-INLINE and PS-INLINE* take a single
Parenscript form and output a string starting
with javascript: that can be used in HTML node
attributes. As well, they provide an argument to bind the value of
*JS-STRING-DELIMITER* to control the value of the
JavaScript string escape character to be compatible with
whatever the HTML generation mechanism is used (for example, if
HTML strings are delimited using #\',
using #\" will avoid conflicts without
requiring the output JavaScript code to be escaped). By default
the value is taken
from *JS-INLINE-STRING-DELIMITER*.
Parenscript code can be compiled from a stream or file
via PS-COMPILE-STREAM
and PS-COMPILE-FILE, respectively. The special
variable *PS-READ-FUNCTION* is bound to the function
used to read the forms from the file/stream (READ by
default), and can be used to provide completely customizable
syntax for Parenscript files.
*PS-PRINT-PRETTY*
and *INDENT-NUM-SPACES* control whether the resulting
JavaScript code is pretty-printed, and if so, how many spaces go
into each indent level, respectively. By default the code is
pretty-printed with 4 spaces per indent level.
Parenscript can also call out to arbitrary Common Lisp code at
output time (that is, every time an expression containing
a call to the Parenscript compiler is evaluated, compared
to compile time, where the effect is accomplished using
macros) using the special form LISP. The form
provided to LISP is evaluated, and its result is
compiled as though it were Parenscript code. For PS
and PS-INLINE, the Parenscript output code is
generated at macro-expansion time, and the LISP
statements are inserted inline into the output and have access
to the enclosing Common Lisp lexical
environment. PS* and PS1* evaluate the
LISP forms using EVAL, providing them
access to the current dynamic environment only (of course,
using LISP when calling the * forms is
not strictly necessary, as the values can be inserted inline
into code).
*JS-TARGET-VERSION* (1.3 by default)
controls which version of JavaScript that Parenscript
targets. For newer versions of JS, some Parenscript special
forms may compile to more concise and/or efficient expressions
that are not present in earlier versions of JavaScript.
SYMBOL-TO-JS-STRING is the Parenscript function
responsible for translating Common Lisp symbols to JavaScript
identifiers (see the section
on symbol conversion for the
translation rules). It is helpful for writing libraries or other
pieces of code that will interface with Parenscript-generated
JavaScript.
In contrast to Lisp, where everything is an expression, JavaScript makes an arbitrary distinction between expressions, which yield a value and can be nested in other expressions, and statements, which have no value and cannot occur in expressions.
Some Parenscript special forms compile to expressions, while
others can only compile to statements. Certain Parenscript
forms, like IF and PROGN, generate
different JavaScript depending on if they are used in an
expression context or a statement context. In such cases,
Parenscript tries to generate statement code if possible to
increase readability, only falling back to the expression code
if it is necessary.
(+ i (if x (foo) (bar)))i + (x ? foo() : bar());(if x (foo) (bar))
if (x) {
foo();
} else {
bar();
};
One important feature found in Lisp but absent in JavaScript is
implicit return in functions. Parenscript supports implicit
return by having a RETURN special form that works
around the statement-expression dichotomy:
(defun foo (x)
(1+ x))
function foo(x) {
return x + 1;
};
(lambda (x)
(case x
(1 (loop repeat 3 do (alert "foo")))
(:bar (alert "bar"))
(otherwise 4)))
function (x) {
switch (x) {
case 1:
for (var _js1 = 0; _js1 < 3; _js1 += 1) {
alert('foo');
};
return null;
case 'bar':
return alert('bar');
default:
return 4;
};
};
Note that Parenscript does not enforce the statement-expression dichotomy, so it is possible to generate syntactically incorrect JavaScript by nesting special forms that only compile to statements in a context that calls for an expression:
(+ 1 (dotimes (x 3) (+ x x)))
1 + for (var x = 0; x < 3; x += 1) {
x + x;
};
Lisp symbols (except for keywords) are converted to JavaScript
symbols by following a few simple rules. Special
characters !, ?, #, @, %, /, * and +
get replaced by their written-out equivalents "bang", "what",
"hash", "at", "percent", "slash", "start" and "plus"
respectively. The $ character is untouched.
!?#@%bangwhathashatpercent;The - is an indication that the following
character should be converted to uppercase.
bla-foo-barblaFooBar;JavaScript identifiers that begin with an uppercase letter can
be obtained with a leading - or *.
*arrayArray;A symbol starting and ending with +
or * is converted to all uppercase, to signify that
this is a constant or a global variable.
*global-array*GLOBALARRAY;Keywords are not translated to JavaScript identifiers, but are printed in lower case without any character substitution as strings. This is done because strings are the closest equivalent to Common Lisp keywords (being self-evaluating objects in JavaScript), and to permit keywords to be used for identifying various symbols (for example, as tokens in a parser).
:+'+';:foo-Bar'foo-bar';The following symbols are reserved in Parenscript, and should not be used as variable names.
! ~ ++ -- * / % + - << >> >>> < >
<= >= == != === !== & ^ | && || *= /= %= += -= <<=
>>= >>>= &= ^= |= 1- 1+ @ ABSTRACT AND AREF ARRAY
BOOLEAN BREAK BYTE CASE CATCH CHAR CLASS COMMA CONST CONTINUE
CREATE DEBUGGER DECF DEFAULT DEFUN DEFVAR DELETE DO DO* DOEACH
DOLIST DOTIMES DOUBLE ELSE ENUM EQL EXPORT EXTENDS F FALSE FINAL
FINALLY FLOAT FLOOR FOR FOR-IN FUNCTION GOTO IF IMPLEMENTS
IMPORT IN INCF INSTANCEOF INT INTERFACE JS LABELED-FOR LAMBDA
LET LET* LISP LIST LONG MAKE-ARRAY NATIVE NEW NIL NOT OR PACKAGE
PRIVATE PROGN PROTECTED PUBLIC RANDOM REGEX RETURN SETF SHORT
GETPROP STATIC SUPER SWITCH SYMBOL-MACROLET SYNCHRONIZED T THIS
THROW THROWS TRANSIENT TRY TYPEOF UNDEFINED UNLESS VAR VOID
VOLATILE WHEN WHILE WITH WITH-SLOTS
TYPEOF object)INSTANCEOF object type)NULL object)UNDEFINED object)DEFINED object)STRINGP object)NUMBERP object)FUNCTIONP object)OBJECTP object)Parenscript is based around the JavaScript type system, and does not introduce any new types or objects, nor does it attempt to provide a Common Lisp-like interface to the type system.
Parenscript prints all integer literals as integers, and floats and rationals as floats, in base 10.
11;123.123123.123;3/40.75;#x1016;Lisp strings are converted to JavaScript strings.
"foobar"'foobar';Parenscript makes no effort to interpolate C-style escape strings. Rather, non-printable characters in Lisp strings are output using escape sequences:
#\Tab'\t';"\\n"'\\n';REGEX regex)Regular expressions can be created by using
the REGEX form. If the argument does not start with
/, it is surrounded by /, otherwise it
is left as it is.
(regex "foobar")/foobar/;(regex "/foobar/i")/foobar/i;CL-INTERPOL is convenient for writing regular expressions:
(regex #?r"/([^\s]+)foobar/i")/([^\s]+)foobar/i;TFFALSENILUNDEFINEDT and FALSE (or F) are
converted to their JavaScript boolean
equivalents true and false.
NIL is converted to the JavaScript keyword
null.
UNDEFINED is converted to the JavaScript global
variable undefined.
NEW constructor)CREATE {name value}*)GETPROP object {slot-specifier}*)@ {slot-specifier}*)CHAIN {slot-specifier | function-call}*)WITH-SLOTS ({slot-name}*) object body)DELETE object)PROGNObject literals can be create
using CREATE. CREATE takes a property
list of property names and values.
(create foo "bar" :blorg 1){ foo : 'bar', 'blorg' : 1 };
(create foo "hihi"
blorg (array 1 2 3)
another-object (create :schtrunz 1))
{ foo : 'hihi',
blorg : [ 1, 2, 3 ],
anotherObject : { 'schtrunz' : 1 } };
Object properties can be accessed using
GETPROP, which takes an object and a list of
properties.
(getprop obj 'foo)obj.foo;(getprop obj foo)obj[foo];(getprop element i 'child-node 0 'node-value)element[i].childNode[0].nodeValue;The convenience macro @ quotes all its given
symbol slot-specifiers to save typing:
(@ an-object foo bar)anObject.foo.bar;(@ foo bar child-node 0 node-value)foo.bar.childNode[0].nodeValue;CHAIN can be used to conveniently chain together
accessors and function calls:
(chain foo (bar x y) 0 baz)foo.bar(x, y)[0].baz;WITH-SLOTS can be used to bind the given
slot-names to a symbol macro that will expand into
a GETPROP form at expansion time:
(with-slots (a b c) this
(+ a b c))
this.a + this.b + this.c;ARRAY {values}*)LIST {values}*)[] {values}*)MAKE-ARRAY {values}*)LENGTH array)AREF array index)ELT array index)DESTRUCTURING-BIND bindings array body)CONCATENATE 'STRING {values}*)APPEND {values}*)Array literals can be created using the ARRAY
or LIST forms.
(array)[];(array 1 2 3)[1, 2, 3];(list (foo) (bar) 3)[foo(), bar(), 3];
(array (array 2 3)
(array "foo" "bar"))
[[ 2, 3 ], ['foo', 'bar']];Arrays can also be created with a call to the Array
function using MAKE-ARRAY.
(make-array)new Array();(make-array 1 2 3)new Array(1, 2, 3);
(make-array
(make-array 2 3)
(make-array "foobar" "bratzel bub"))
new Array(new Array(2, 3), new Array('foobar', 'bratzel bub'));Array elements can be accessed using AREF or ELT.
*, /, %, +, -, <<, >>, >>>, < >, EQL,
==, !=, =, ===, !==, &, ^, |, &&, AND, ||, OR
INCF, DECF, ++, --, NOT, !Operator forms are similar to function call forms, but have an operator as function name.
Please note that = is converted to == in
JavaScript. The = Parenscript operator is not the
assignment operator.
(* 1 2)1 * 2;(= 1 2)1 == 2;MAX {number}*)MIN {number}*)FLOOR number &optional divisor)CEILING number &optional divisor)ROUND number &optional divisor)SIN number)COS number)TAN number)ASIN number)ACOS number)ATAN number1 &optional number2)SINH number)COSH number)TANH number)ASINH number)ACOSH number)ATANH number)1+ number)1- number)ABS number)EVENP number)ODDP number)EXP number)EXPT base power)LOG number &optional base)SQRT number)RANDOM &optional limit)PIThe mathematical functions listed above work mostly like their Common Lisp counterparts when called directly, with the exception that complex numbers are not supported. However, most of them are implemented as macros, and as such cannot be treated as first-class functions.
PROGN {statement}*) in statement contextPROGN {expression}*) in expression contextPROG1 {expression | statement}*)PROG2 {expression | statement}*)The translation of PROGN depends on whether it is
found in a statement or expression context:
(progn (blorg i) (blafoo i))
blorg(i);
blafoo(i);
(+ i (progn (blorg i) (blafoo i)))i + (blorg(i), blafoo(i));DEFUN name lambda-list body)LAMBDA lambda-list body)FLET ({(name lambda-list body)}*) body)LABELS ({(name lambda-list body)}*) body)VALUES {expression}*)MULTIPLE-VALUE-BIND (var*) expression body)APPLY function expression*)FUNCALL function expression*)THISPROGNNew function definitions can be introduced using all the
regular Lisp forms
- DEFUN, LAMBDA, FLET,
and LABELS. Function lambda lists
support &optional, &rest and
&key arguments.
The Parenscript multiple value facility passes the first return
value using the regular JavaScript convention, therefore functions
returning multiple values can be called by regular JavaScript code
and MULTIPLE-VALUE-BIND works with regular JavaScript
functions.
APPLY is a macro that expands into a call to the
JavaScript apply method.
RETURN {value}?)THROW {exp}?)TRY form
{(:CATCH (var) body)}?
{(:FINALLY body)}?)IGNORE-ERRORS body)THROW is boundPROGNParenscript RETURN and THROW forms do
not work like the Common Lisp forms with the same names.
RETURN can only be used to return a value from
a function - Parenscript has no analogue of Common Lisp's
blocks. RETURN works when given either expressions or
statements (in which case it performs semantic analysis to
determine what should be returned).
(lambda (x)
(return (case x
(1 :a)
(2 :b))))
function (x) {
switch (x) {
case 1:
return 'a';
case 2:
return 'b';
};
};
Likewise, THROW translates directly into the
JavaScript throw, to be used with TRY,
which is translated to the JavaScript try.
(try (throw "i")
(:catch (error)
(alert (+ "an error happened: " error)))
(:finally
(alert "Leaving the try form")))
try {
throw 'i';
} catch (error) {
alert('an error happened: ' + error);
} finally {
alert('Leaving the try form');
};
IF condition then {else})WHEN condition then)UNLESS condition then)COND {clauses}*)CASE case-value clause*)SWITCH case-value clause*)BREAKIF, WHEN, UNLESS and COND work like
their Lisp counterparts, and are compiled either into statements
or expressions, depending on the context:
(cond ((= x 1) (+ x (if (foo y) 2 3))))
if (x == 1) {
x + (foo(y) ? 2 : 3);
};
CASE works like its Common Lisp equivalent. An
additional form, SWITCH, takes the same syntax
as CASE, but the individual branches must be
terminated with the
symbol BREAK. This allows
C-style case "fall-throughs" in switch statements:
(switch (aref blorg i)
(1 (alert "If I get here"))
(2 (alert "I also get here")
break)
(default (alert "I always get here")))
switch (blorg[i]) {
case 1:
alert('If I get here');
case 2:
alert('I also get here');
break;
default:
alert('I always get here');
};
Note that the default case in a SWITCH statement
must be named DEFAULT.
LET ({var | (var value)}*) body)LET* ({var | (var value)}*) body)DEFVAR var {value}?)VAR var {value}?)WITH object body)PROGNParenscript provides the LET and LET*
special forms for creating new variable bindings. Both special
forms implement lexical scope by renaming the provided variables
via GENSYM, and implement
dynamic binding
using TRY-FINALLY.
Top-level LET and LET* forms will
create new global variables, or overwrite the values of already
existing global variables with the same name.
The JavaScript with statement can be generated
using WITH.
Beware that lexical scoping rules in JavaScript differ from Lisp, and may even be implemented incorrectly in certain browser JavaScript implementations. This applies particularly to lexical closures.
Special variables can be declared using
DEFVAR. Note that the result is undefined
if DEFVAR does not occur as a top-level form.
One Parenscript feature that is not part of Common Lisp is the
lexically-scoped global variable, which is declared using
the VAR special form. The result is undefined
if VAR does not occur as a top-level form.
An example of variable declaration and binding:
(defvar *a* 4)
(var *b* 3)
(lambda ()
(let ((x 1)
(*a* 2)
(*b* 6))
(let* ((y (+ x 1))
(x (+ x y)))
(+ *a* *b* x y))))
var A = 4;
var B = 3;
function () {
var x = 1;
var B = 6;
var A_TMPSTACK1;
try {
A_TMPSTACK1 = A;
A = 2;
var y = x + 1;
var x2 = x + y;
return A + B + x2 + y;
} finally {
A = A_TMPSTACK1;
};
};
Parenscript assignment is done via the
standard SETF, SETQ,
PSETF, and PSETQ
Lisp special forms. Parenscript supports the Common Lisp
protocol of SETFable places.
New places can be defined in one of two ways: using
DEFSETF or using DEFUN with a setf
function name; both are analogous to their Common Lisp
counterparts. DEFSETF supports both long and short
forms, while DEFUN of a setf place generates a
JavaScript function name with the __setf_
prefix:
(defun (setf color) (new-color el)
(setf (@ el style color) new-color))
function __setf_color(newColor, el) {
return el.style.color = newColor;
};
(setf (color some-div) (+ 23 "em"))
var _js2 = someDiv;
var _js1 = 23 + 'em';
__setf_color(_js1, _js2);
The following example illustrates how setf places can be used to provide a uniform protocol for positioning elements in HTML pages:
(defsetf left (el) (offset)
`(setf (@ ,el style left) ,offset))
(defmacro left (el)
`(@ ,el offset-left))
(setf (left some-div) (+ 123 "px"))
(left some-div)
var _js2 = someDiv;
var _js1 = 123 + 'px';
_js2.style.left = _js1;
someDiv.offsetLeft;
DO ({var | (var {init}? {step}?)}*) (end-test {result}?) body)DO* ({var | (var {init}? {step}?)}*) (end-test {result}?) body)DOTIMES (var numeric-form {result}?) body)DOLIST (var list-form {result}?) body)FOR-IN (var object) body)WHILE end-test body)LOOP {loop clauses}*)PROGNParenscript comes with a wide array of Common Lisp iteration
constructs that compile to efficient JavaScript code,
including a partial implementation of LOOP.
DO-SET-TIMEOUT (timeout) body)DO-SET-TIMEOUT is a convenience macro for
using set-timeout.
DEFMACRO name lambda-list macro-body)DEFPSMACRO name lambda-list macro-body)DEFMACRO+PS name lambda-list macro-body)IMPORT-MACROS-FROM-LISP symbol*)MACROLET ({name lambda-list macro-body}*) body)PROGNParenscript macros are like Lisp macros in that they have access to the full Lisp language, but different in that they must produce Parenscript code. Since Parenscript provides a large subset of Common Lisp, many Lisp macros already produce valid Parenscript code, and vice-versa. Parenscript provides several different ways to define new macros, and to use already existing Common Lisp macros.
DEFMACRO and MACROLET can be used to
define new macros in Parenscript code. Note that macros defined
this way are defined in a null lexical environment (ex
- (let ((x 1)) (defmacro baz (y) `(+ ,y ,x))) will
not work), since the surrounding Parenscript code is just
translated to JavaScript and not actually evaluated.
DEFPSMACRO is a Lisp form (not a Parenscript one!)
that can be used by Lisp code to define Parenscript macros without
calling the Parenscript compiler.
The representation of Parenscript macro functions is the same as that of Common Lisp, and in fact Parenscript can use already defined macros this way.
DEFMACRO+PS defines two macros with the same name
and expansion, one in Parenscript and one in
Lisp. DEFMACRO+PS is used when the full macroexpansion of
the Lisp macro yields code that cannot be used by Parenscript.
Parenscript also supports the use of macros defined in the
underlying Lisp environment. Existing Lisp macros can be imported into
the Parenscript macro environment
by IMPORT-MACROS-FROM-LISP. This functionality enables
code sharing between Parenscript and Lisp, and is useful in debugging
since the full power of Lisp macroexpanders, editors and other
supporting facilities can be used. However, it is important to note
that the macroexpansion of Lisp macros and Parenscript macros takes
place in their own respective environments, and many Lisp macros
(especially those provided by the Lisp implementation) expand into
code that is not usable by Parenscript. To make it easy for users to
take advantage of these features, two additional macro definition
facilities are provided by Parenscript:
DEFINE-PS-SYMBOL-MACRO symbol expansion)
(SYMBOL-MACROLET ({name macro-body}*) body)
Symbol macros can be introduced using SYMBOL-MACROLET
or defined in Lisp with DEFINE-PS-SYMBOL-MACRO. For
example, the Parenscript
WITH-SLOTS is implemented using symbol macros:
(defpsmacro with-slots (slots object &rest body)
`(symbol-macrolet ,(mapcar #'(lambda (slot)
`(,slot '(getprop ,object ',slot)))
slots)
,@body))
PS-GENSYM {string})WITH-PS-GENSYMS symbols &body body)PS-ONCE-ONLY (&rest vars) &body body)*PS-GENSYM-COUNTER*JavaScript identifier equality is based on string
representations, as opposed to Common Lisp, where two uninterned
symbols with the same name are different objects. Therefore
Parenscript GENSYM depends
on *PS-GENSYM-COUNTER* values only for generating
unique identifiers. *PS-GENSYM-COUNTER* does not
persist and is not guaranteed to be thread-safe, so care should be
taken to avoid writing code where gensymed identifiers may clash
(for example, this could happen if you concatenate JS code from PS
compilers running in two different Lisp images, where the values
of *PS-GENSYM-COUNTER* overlap).
PS-PACKAGE-PREFIX package-designator) string)Although JavaScript does not offer namespacing or a package system,
Parenscript does provide a namespace mechanism for generated
JavaScript by integrating with the Common Lisp package system. Since
Parenscript code is normally read in by the Lisp reader, all symbols
(except for uninterned ones, ie - those specified with
the #: reader macro) have a Lisp package. By default, no
packages are prefixed. You can specify that symbols in a particular
package receive a prefix when translated to JavaScript with the
PS-PACKAGE-PREFIX place.
(defpackage "PS-REF.MY-LIBRARY"
(:use "PARENSCRIPT"))
(setf (ps-package-prefix "PS-REF.MY-LIBRARY") "my_library_")
(defun ps-ref.my-library::library-function (x y)
(return (+ x y)))
function my_library_libraryFunction(x, y) {
return x + y;
};
OBFUSCATE-PACKAGE package-designator &optional symbol-map)UNOBFUSCATE-PACKAGE package-designator)Similar to the namespace mechanism, Parenscript provides a
facility to generate obfuscated identifiers in specified CL
packages. The function OBFUSCATE-PACKAGE may
optionally be passed a closure that maps symbols to their
obfuscated counterparts. By default, the mapping is done
using PS-GENSYM.
(defpackage "PS-REF.OBFUSCATE-ME")
(obfuscate-package "PS-REF.OBFUSCATE-ME"
(let ((code-pt-counter #x8CF6)
(symbol-map (make-hash-table)))
(lambda (symbol)
(or (gethash symbol symbol-map)
(setf (gethash symbol symbol-map)
(make-symbol (string (code-char (incf code-pt-counter)))))))))
(defun ps-ref.obfuscate-me::a-function (a b ps-ref.obfuscate-me::foo)
(+ a (ps-ref.my-library::library-function b ps-ref.obfuscate-me::foo)))
function 賷(a, b, 賸) {
return a + libraryFunction(b, 賸);
};
The obfuscation and namespace facilities can be used on packages at the same time.
INNER-HTML el)URI-ENCODE el)ATTRIBUTE el)OFFSET compass el)SCROLL compass el)INNER wh el)CLIENT wh el):TOP, :LEFT, :HEIGHT, :WIDTH, :BOTTOM, :RIGHT:WIDTH, :HEIGHTPS-HTML html-expression)WHO-PS-HTML html-expression)*PS-HTML-EMPTY-TAG-AWARE-P**PS-HTML-MODE*Parenscript comes with two HTML markup generation facilities
that produce Parenscript code - PS-HTML
and WHO-PS-HTML. The former
accepts LHTML
style markup, while the latter
accepts CL-WHO style
markup.
*PS-HTML-EMPTY-TAG-AWARE-P*
and *PS-HTML-MODE* control how tags are closed when an
HTML element has no
content. When *PS-HTML-EMPTY-TAG-AWARE-P* is nil, all
tags are fully closed (ex - :BR is translated
as <BR></BR>). When *PS-HTML-EMPTY-TAG-AWARE-P*
has a non-nil value and *PS-HTML-MODE*
is :SGML, tags such as BR are output without
being closed; when *PS-HTML-MODE* is :XML,
the XML-style closing tags are used (ex - :BR is
translated as <BR />).
(ps-html ((:a :href "foobar") "blorg"))'<A HREF=\"foobar\">blorg</A>';(who-ps-html (:a :href (generate-a-link) "blorg"))'<A HREF=\"' + generateALink() + '\">blorg</A>';The Parenscript compiler can be recursively called in an HTML expression:
((@ document write)
(ps-html ((:a :href "#"
:onclick (ps-inline (transport))) "link")))
document.write('<A HREF=\"#\" ONCLICK=\"' + ('javascript:' + 'transport()') + '\">link</A>');Forms may be used in attribute lists to conditionally generate the next attribute. In this example the textarea is sometimes disabled.
(let ((disabled nil)
(authorized t))
(setf (@ element inner-h-t-m-l)
(ps-html ((:textarea (or disabled (not authorized)) :disabled "disabled")
"Edit me"))))
var disabled = null;
var authorized = true;
element.innerHTML =
'<TEXTAREA'
+ (disabled || !authorized ? ' DISABLED=\"' + 'disabled' + '\"' : '')
+ '>Edit me</TEXTAREA>';
MEMBER object array)MAP function array)MAPCAR function {array}*)REDUCE function array object)MAP-INTO function array)SET-DIFFERENCE array1 array2)*PS-LISP-LIBRARY*All the Parenscript constructs presented so far have been free
of any runtime dependencies. Parenscript also comes with a library
of useful predefined functions that can be added to your
project. These functions are kept as Parenscript code in
the *PS-LISP-LIBRARY* special variable.
MAP differs from its Common Lisp counterpart by
virtue of being a MAPCAR that only accepts a single
sequence to map over. MAP-UNTIL is
like MAP but replaces the contents of the given
array in-place.
The extras folder in the Parenscript distribution
contains js-expander.el, which when loaded in Emacs
with SLIME adds the ability to quickly see the translation of
any Lisp form in JavaScript, and works much like the Slime 'C-c
M-m' macroexpansion feature.
'C-c j' (PS) or
'C-c d' (PS-DOC)
at a ParenScript expression in a slime-mode buffer
will bring up a buffer with the resulting Javascript code. Note
that the extension does not work
in slime-repl-mode, which is intentional.
Last modified: 2010-02-24