throw
statement somewhat to allow throwing exceptions into
continuation objects.
function_continuation
statementfunction_continuation
statement takes no arguments and
returns the return continuation for the current function. For example, in the
following code:
function sleep(msec)
{
var k = function_continuation;
setTimeout(function() { resume k; }, msec);
suspend;
}
the local variable k
is set to the return continuation for the
sleep function. This continuation is resumed by the timeout function that is
set by the setTimeout
call. When the k
continuation
is resumed, the sleep function "returns" to the caller, in the same state as
when it was called.
You can think of the value returned by the
function_continuation
statement as being a copy of the call stack
(complete with all locals, parameters, and return addresses) at the time of
entry into the current function.
resume
statementresume
statement takes either one or two arguments. The
first argument is an expression that returns a continuation to resume, and the
second, optional argument is an expression that returns the value for the
resumed continuation's function to return. If the optional second argument is
omitted, the function returns undefined
. The syntax is:
The anonymous function in the setTimeout
call above shows the
single-argument version of resume
. The version of
sleep
above will return undefined when its continuation is
resumed. The following, modified version of sleep
will return
the number of milliseconds that it was called with:
function sleep(msec)
{
var k = function_continuation;
setTimeout(function() { resume k <- msec; }, msec);
suspend;
}
Note that the first and second arguments to resume
must be
separated by a left-pointing arrow token. This slightly-hokey syntax is
intended to convey that the value is passed into the continuation. Also it
makes the grammar unambiguous. :)
You can think of the resume
statement as replacing the current
call stack with the stack saved in the continuation and then executing a
return
statement. In particular, since they replace the call
stack, resume
statements never return.
So, in this code:
function foo(k) { if(k) return 20; else resume k <- 20; alert("you'll never see this"); }the
alert
statement is never executed, since foo
either returns or resumes the continuation argument k
.
suspend
statementsuspend
statement suspends the current thread. It accepts no
arguments.
It is frequently the case that a function that captures its own
continuation in one form or another will want to delay its return until some
event has occurred. In the case of the fetchData
function (see
the library section), we want to delay returning until
the data has returned from the server, so we save a continuation in an event
handler and execute a suspend
statement. In the case of the
sleep
function defined above, we want to delay returning until a
specified number of milliseconds have elapsed, so we save a continuation to be
resumed by a timer and then execute a suspend
statement.
In the above definition of sleep
, if the suspend
statement were missing, then sleep
would actually return
twice: Once immediately, and once when the timer fired and resumed its
continuation.
You can think of the suspend
statement as discarding the
current call stack.
throw
syntaxthrow
statement to
allow you to throw an exception "into" a continuation:
throw value -> continuationThe usual, 1-argument syntax still works as expected (ie, it throws an exception in the current control context). However, there is now also an optional second argument that specifies that an exception is to be thrown into a continuation. Note that the second argument is separated from the first by a right-facing arrow (indicating that the exception is thrown into the exception).
For example:
function strictSleep(msec)
{
var k = function_continuation;
var s = (new Date).getTime();
setTimeout(function() {
var e = (new Date).getTime();
if(e - s < msec)
throw "setTimeout did not sleep for long enough!" -> k;
else
resume k <- msec;
}, msec);
}
In the above code, strictSleep
verifies that the timeout is not
called until at least msec
milliseconds have elapsed. If enough
time has elapsed, then it returns msec
as in the definition of
sleep
above. However, if enough time has not elapsed, it
throws an error. Note that the exception is thrown into
strictSleep
's return stack, not into the timeout
handler's stack, just as it is strictSleep
that returns
msec
when there is no error, and not the event handler.
You can think of the two-argument version of throw as replacing the current call stack with the stack saved in the continuation argument, and then throwing the exception argument.
import
statementimport
statement for
linking together multiple source files. It has the following syntax:
import [type] "path";The type can be one of
jwacs
, jw
,
javascript
, or js
. The type is optional; if
it is omitted it will be inferred from the extension of the path.
The path should be a relative or absolute path to a Javascript or jwacs source file to include in the web app. (See the compiler section for details of how absolute paths are resolved). These paths will be passed straight through to the src attribute of a <script> tag, so use absolute paths with care.
Imports of Javascript files will be turned directly into a <script> tag in the output html file. jwacs files that are imported will be transformed into Javascript files, which will then be referenced from a <script> tag in the output html file.
Ex:
import "../lib/prototype.js"; import "utils.jw";will cause a <script> tag to be omitted for ../lib/prototype.js and utils.js. The utils.jw file will be transformed into utils.js.
resume
,
suspend
, and extended throw
statements all replace
the existing call stack, but only back to the nearest non-jwacs
function.
In practice, this means that event handlers called from non-jwacs code (eg,
handlers for built-in events and those called by third-party libraries) will
return undefined
as soon as a resume
,
suspend
, or extended throw
statement is executed.
In the following code:
window.addEventListener("load", function() { alert("alpha"); JwacsLib.sleep(5000); alert("beta"); }, false);The user will see an "alpha" alert box followed by a "beta" alert box 5 seconds later. However, the anonymous event handler for the "load" event will return as soon as
JwacsLib.sleep
is executed, because
JwacsLib.sleep
executes a suspend
statement.
The compiler is invoked on a single jwacs source file. That jwacs file may contain imports to other Javascript or jwacs files. The compiler transforms all imported jwacs files (and all jwacs files that they import, and so forth) into standard Javascript.
Once Javascript files have been generated, an html file is generated by adding <script> tags to a template html file. If no template file exists, a standard template is used. (If you don't care about generating an html file directly, you can ignore these aspects of the output and just use the Javascript file that will be generated).
jwacs [options] main_source_fileThe following options are available:
build-app
Lisp functionbuild-app
function takes one required argument and four
keyword arguments. The required argument is a path specifier designating the
main jwacs source file to transform. The keyword arguments closely mirror the
command-line arguments of the executable (or perhaps it's the other way
around):
:template-uripath
:runtime-uripath
:output-uripath
:prefix-lookup
import
statements
may be relative paths, in which case files are found in a straightforward
fashion, or they may be absolute paths. If they are absolute paths, then you
must specify a translation from absolute URI paths to file system paths.
When using the executable compiler, use the -p option to specify
the translation. When using the Lisp function, use the
:prefix-lookup
keyword argument to specify a list of cons cells;
the CAR of each cell is the path prefix, and the CDR is a pathname that
specifies which filesystem directory that prefix represents.
For example, to indicate that absolute import paths beginning with /lib/ refer to files in the /home/james/lib directory, and all other absolute paths refer to files in the /home/james/jwacs directory, pass the following arguments to the binary:
-p /lib=/home/james/lib;/=/home/james/jwacsor pass the following list as the
:prefix-lookup
keyword argument
to build-app
:
'(("/lib/" . #P"/home/james/lib/") ("/" . #P"/home/james/jwacs/"))