Javathcript - Javascript with a lisp

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.

Contents

  1. Introduction
  2. Example Usage
  3. Javathcript Functions
    1. Functions from A Micro-manual for Lisp - Not the Whole Truth
    2. Scope
    3. Syntactic Sugar, Comments and Aliases
    4. Strings
    5. Numbers and Maths
    6. Objects
    7. Interacting with Javascript and the Browser
  4. More Examples
    1. The Y-Combinator
    2. Lisp in Lisp
    3. Graphics and the Canvas tag
  5. Have a Go
  6. Getting the Source
  7. Limitations
  8. Acknowledgements
  9. Contact
  10. Comments

Example Usage

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.

<script type="text/javascript" src="Javathcript.js"></script>
<script type="text/lisp" src="lisp/prelude.lsp"></script>

The block of code below attaches a javathcript handler to the onclick attribute of the button. Click the button to see the code execute.

<script type="text/lisp">
	(let*
		(	(button (getElement "btn"))
			(nameField (getElement "name"))
			(clickHandler (lambda () (alert (concat "Hello " (get nameField "value")))))	)
		(set button "onclick" (export clickHandler))
	)
</script>

Javathcript Functions

Functions from A Micro-manual for Lisp - Not the Whole Truth

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.

Scope

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.

Syntactic Sugar, Comments and Aliases

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.

Strings

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.

Numbers and Maths

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.

Objects

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.

Interacting with Javascript and the Browser

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.

More Examples

Y-Combinator

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.

Lisp in Lisp

The Micro-manual included a complete Lisp interpreter written in Lisp. Here is a slight modification of it that I took from here.

Graphics and the Canvas tag

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.

	

Have a go


	

	

Getting the Source

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

Limitations

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.

Acknowledgements

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:

Contact

You can contact me by email at

, or check out my blog.

Comments

Leave a comment.

comments powered by Disqus