Parenscript is a subset of Common Lisp that compiles to JavaScript. The emphasis is on the fact that it strives to produce minimal JavaScript like 1:1 as much as possible. Unlike ClojureScript which produces a lot of dependent code because of the use of Google Closure library for everything. Here we will see how to compile Parenscript to JavaScript.

I am making use of an existing script cl2js written by Michelle Chen.

Parenscript dev environment setup

Before delving into the compiler setup I want to write a little about Parenscript workflow.

1. Install SBCL.

brew install sbcl

Add alias sbcl with rlwrap to get commandline history in shell in ~/.zshrc.

alias sbcl="rlwrap sbcl"

2. Install quicklisp.

$ cd ~/bin
$ curl -O https://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)
* (quit)

This will setup quicklisp package manager and add it to the init file of SBCL, ~/.sbclrc.

3. Load (or install) parenscript using quicklisp.

$ sbcl
* (ql:quickload :parenscript)

Parenscript to JavaScript

There is a function parenscript:ps which compiles or transpiles Lisp code to JavaScript. This can be invoked like below.

$ sbcl
* (ql:quickload :parenscript)
* (import 'parenscript:ps)
* (ps (defun greet () (format t "hello world")))
"function greet() {
    __PS_MV_REG = [];
    return format(true, 'hello world');
};"

Here we can see the JavaScript output. Since JavaScript does not use format to print output, we need to use JavaScript functions instead than using Common Lisp functions when required.

Compiling Parenscript to JavaScript

1. Clone the repo cl2js into say ~/env.

2. Build the project to get the binary cl2js.

Make sure wget is installed. Otherwise it will give the following error, which is a bit cryptic.

./scripts/package sbcl                    
./scripts/build: line 87: /dev.null: Read-only file system
./scripts/build: line 87: 2: command not found
Failed to download quicklisp.lisp
mv: rename ./scripts/../cl2js to ./scripts/../temp/libexec/cl2js: No such file or directory
Failed to move ./scripts/../cl2js to ./scripts/../temp/libexec
$ cd cl2js
./scripts/package sbcl

If everything is built without any error we should see log like below.

...
[saving current Lisp image into cl2js:
writing 65536 bytes from the linkage space at 0xb7ffc00000
writing 3168 bytes from the static space at 0x50000000
writing 17006592 bytes from the dynamic space at 0x1000000000
writing 8758032 bytes from the read-only space at 0xfff7a0000
writing 1294336 bytes from the fixedobj space at 0x52100000
writing 13205504 bytes from the text space at 0xb800000000
done]

Add the directory ~/env/cl2js/cl2js/bin to the PATH.

If for some reason the build does not work, I have the repo feature/build-output with the binary which can be used readily.

3. Compile an existing Parenscript file using the below format. Let's say we have the below Lisp code in test.lisp.

(defun greet ()
  (chain console (log "Hello World")))
$ cl2js test.lisp > test.js

This will produce the corresponding JavaScript file.

function greet() {
    return console.log('Hello World');
};

The biggest challenge with any new language or library is to get the development environment working properly. With this we have a full dev env setup. This will help to further learn Parenscript, Common Lisp and JavaScript.