Javathcript allows you to script your web pages in a simple lisp variant. Once you include Javathcript.js,
any script tags in your document with type="text/lisp"
will be evaluated. It will also download lisp files (only from the original server),
if you have a script tag that has a src
attribute. Finally, you can also evaluate lisp code from javascript using
Javathcript.eval(lispString)
.
While it is not an exact implementation of any pre-existing variant of lisp, if you know lisp most of it should be familiar. If you don't you might find it useful to follow a tutorial, e.g. this one. There will be differences between this implementation and others, but there is also much that is common.
Since the browser doesn't natively understand script tags with type="text/lisp"
, the Javathcript.js file
is included in the header to provide parsing and evaluation for lisp code. I have also included the prelude.lsp lisp
source file in the header since it defines a number of functions that this code uses (e.g. getElement
and alert
).
To include these files, the following script tags are in the head of this page. Note that the prelude.lsp
file has to be served from the same
server as any page that uses it, as it is fetched by an XHR.
<type="text/javascript" src="Javathcript.js">< > < type="text/lisp" src="lisp/prelude.lsp">< >
The block of code below attaches a javathcript handler to the onclick
attribute of the button.
Click the button to see the code execute.
<type="text/lisp"> (let* ( (button (getElement "btn")) (nameField (getElement "name")) (clickHandler (lambda () (alert (concat "Hello " (get nameField "value"))))) ) (set button "onclick" (export clickHandler)) ) < >
quote car cdr cons equal atom cond lambda label
According to A Micro-Manual for Lisp - Not the whole truth , these functions provide the core of Lisp, and are enough to create a self-hosting LISP interpreter. Here are some examples (click to evaluate them):
The Micro-manual also suggests t Nil or and not null defun list
which I have included, and cadr
with arbitrary combinations
of 'a' and 'd', some of which are in the prelude. I've modified the following example slightly from that given in the paper as the empty list is not regarded as an
atom in javathcript.
def defun let let*
As well as defun
to set a name for a function in the global scope, there is also def
, to allow you to set names for
other kinds of values in the global scope. Functions provide their own scope, but you can also use let
and let*
to create
temporary scopes. The code example at the top of this page used let*
to assign names for the button, the nameField and the clickHandler.
if ' ;
You can quote something by simply putting the single quote in front of it. This is a bit more terse than having to wrap it in (quote ...)
.
Comments are lines that start with the ;
character. if
provides a slighly more intuitive alternative to cond
.
A number of aliases are provided in the prelude.lsp file, eg. eq eq? head tail first rest
, as well as
a number of useful functions, e.g. map
.
length concat substring
As well as atoms and lists, Javathcript also provides some support for strings Strings literals can be entered by surrounding them in double quotes.
Most functions that work on lists will also work on strings, although unlike in some lisps, Javathcript strings are not lists.
Also note that concat
if started with a string will concatenate numbers into the string too.
plus minus divide times rem < > <= >= = /=
Javathcript supports numeric literals, and the normal arithmetic functions (which are aliased by prelude.lsp to be accessible by the symbols too).
sin cos tan asin acos atan floor max min log abs ceil pow exp atan2 random sqrt round
The prelude.lsp also imports all the javascript Math functions.
set get
Object literals can be entered surrounded by curly braces, similarly to in javascript. set
sets properties on them, and get
returns previously set properties.
js method export
The js
function evaluates its string argument, and returns the result. If the result is a function, a small adjustment is made to it
so that it will evaluate its arguments in the current Javathcript scope before executing. If the result is a function that belongs to an
object, executing separately will unbind the function from the object it was connected to (the this
reference will be wrong). In that
case, you should use (method object "methodname")
to get a reference to the function that will execute against the provided object.
Check out the Graphics and the Canvas tag section to see an example of some of this in action.
While Javathcript functions turn into normal javascript functions, they are bound to the scope that they execute in, and won't work if
disconnected from that scope. If you want to call them from, e.g. event handlers, then you can use the export
function to create
a function with the scope boiled in.
document body window getElement alert message confirm
These functions are defined in the prelude.lsp file, and provide you references to the document and the window. getElement
is
the same as javascript getElementById
, and message is a synonym for alert
.
The Y combinator is "one of the most strange and wonderful artifacts of Computer Science" according to Douglas Crockford. It allows you to write an anoymous function that can use recursion (not that this is something that comes up a lot...). There's a good explanation of it here.
The Micro-manual included a complete Lisp interpreter written in Lisp. Here is a slight modification of it that I took from here.
The following image was rendered into a canvas by some Javathcript code in an embedded script tag. In order to have this demo work in internet explorer too, I've included excanvas on this page. If your browser doesn't support canvas, you would have seen an alert message when you loaded this page, and this demo won't work.
The project is hosted on github here. Depending on your setup, the following command should get you a local copy of the code.
git clone git@github.com:kybernetikos/Javathcript.git
This implementation does not have any tail recursion optimisations or macros. I haven't spent any time making it fast, so it is unlikely to be suitable for performance sensitive applications. It also uses the built in Javascript numbers, so no BigNum support for those used to it.
Parsing is done with parsing code that I ported from Java. The original java is the code that accompanies the book Building Parsers With Java and is copyright Steven J. Metsker.
The lisp engine was created by me, based on A Micro-Manual for Lisp - Not the whole truth by John McCarthy.
Although Javathcript doesn't take code from other projects, running lisp variants in the browser is not a new thing. It's been done before by at least:
You can contact me by email at
, or check out my blog.Leave a comment.
comments powered by Disqus