Skip to content
A self-hosting Scheme to WebAssembly compiler
Scheme JavaScript Shell HTML
Branch: master
Clone or download
Latest commit 2aa18f0 Nov 20, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs Fix nit Aug 8, 2019
lib Tweaks to enable Guile bootstrap Aug 27, 2019
rt distinguish strings created from symbol->string Nov 20, 2019
scheme-lib fix string eq? Nov 13, 2019
schism fix string eq? Nov 13, 2019
test distinguish strings created from symbol->string Nov 20, 2019
.editorconfig Add a really simple playground Nov 30, 2017
.gitignore Adding a few familiar cli commands Jun 28, 2018
.travis.yml Remove Node LTS from Travis Jul 24, 2019 Add license Feb 17, 2018
LICENSE Add license Feb 17, 2018 Update documentation; add convenience call method on Module Jul 31, 2019
benchmark-compiler.mjs Add compiler benchmark script Jul 5, 2019 Add support for WebAssembly return_call (tail calls) Jul 31, 2019
bootstrap-from-guile.scm Fix bootstrap-from-guile.scm Aug 27, 2019 Add bootstrap-from-guile shims Jul 16, 2019
cli.js Make stage-0 the default compile Jul 2, 2018
mksnapshot.mjs Attempt to fix Travis CI with Node 12 Jul 5, 2019 Add support for WebAssembly return_call (tail calls) Jul 31, 2019
package.json Add filesystem wrapper for Schism.Engine Aug 6, 2019
playground.html Steps towards fixing Playground Aug 29, 2019
playground.js use options object to configure Schism.Engine Aug 8, 2019
root.cjs Add filesystem wrapper for Schism.Engine Aug 6, 2019
run-schism.mjs Attempt to fix Travis CI with Node 12 Jul 5, 2019
run-tests.mjs use options object to configure Schism.Engine Aug 8, 2019 Add support for WebAssembly return_call (tail calls) Jul 31, 2019
run-utils.mjs use options object to configure Schism.Engine Aug 8, 2019
schism-stage0.wasm update snapshot (%string=?) Nov 13, 2019 Add support for WebAssembly return_call (tail calls) Jul 31, 2019


Schism is an experimental self-hosting compiler from a subset of R6RS Scheme to WebAssembly.

This is not an officially supported Google product.

Development so far has focused on features necessary for self-hosting. The compiler itself is written in, and compiles, a very small subset of Scheme. Now that self-hosting has been achieved, development can shift towards supporting a more complete subset of Scheme. Just to be clear, by subset we mean that all programs supported by Schism are also legal R6RS Scheme programs and they will have the same behavior when run under Schism or an R6RS-compliant Scheme.

Besides just being fun, one of the goals of this project is to explore different ways to use WebAssembly. This includes implementing garbage collection, possibly using the WebAssembly GC proposal, dynamic linking and code generation, etc.

Chez Scheme was used to bootstrap, but development no longer relies on an existing Scheme implementation and can instead run purely based on snapshots checked into the repository. The compiler can also bootstrap from GNU Guile.

As mentioned, the goal has been to prioritize features needed for self hosting. Here are some of the current restrictions:

  • Functions may only be created with define, and particularly only the (define (foo args ...) body) form.
  • No use of syntax-case, syntax-rules, or define-syntax.
  • Only one file to start with, so we don't have to figure out how to link multiple libraries.
  • Only use define to create functions, not global variables or objects.
  • Use a small amount of syntax, because we won't have a proper macro expander at first. There is a pass to expand some of the simpler and more useful macros.
  • Restrict data types and operations on those. For now, we can use:
    • Booleans
    • Numbers (integers within the int32 range)
    • Characters
    • Pairs
    • Strings
    • Symbols

As more features are supported by the compiler, we will remove these restrictions.

See the docs directory for more information about how various features are implemented.

Current Status and Next Steps

The compiler is self-hosting! Now the goal is to make a more complete language. Some of the big missing features are:

  • Variable length argument lists
  • Macros
  • Support for multiple files and modules
  • Ports


We currently use a very simple testing protocol. The test/ directory includes a number of Scheme libraries, each of which export a function called do-test. This function can do whatever it wants, but it must return a value other than #f to pass.

To run all the tests, do ./

Schism uses experimental WebAssembly features

Note! One of the purposes of Schism is to advance the state of the art in WebAssembly implementations. Currently, the WebAssembly emitted by Schism uses the following experimental features:

As of August 2019, the only production WebAssembly implementation that has both of these features is V8, and both features are behind a flag. To use the features with Node, we add the --experimental-wasm-return-call and --experimental-wasm-anyref flags to Node's argument list. We hope to improve this situation in the future.

The Playground

This repository includes a very simple playground.html, which gives a lightweight way to play around with the compiler. Be warned, there is almost no error checking right now, so strange things can happen.

The best way to use it is to start up a web server (python -m SimpleHTTPServer should work) and point your browser at the page.

Note that because Schism uses experimental WebAssembly features, you need a browser that supports these features. To get a Chrome that has these features, try:

chrome --js-flags="--experimental-wasm-anyref --experimental-wasm-return-call"

Then navigate to http://localhost:8000/playground.html.


The compiler code all lives in schism/ There is a JavaScript runtime in rt/rt.mjs. The goal is to keep as much code as possible in Scheme, but the runtime is needed to interact with the rest of the world. Also, until larger parts of the GC proposal are implemented in WebAssembly engines, the run-time also has some routines to allocate memory using the host (usually the JavaScript GC), to enable garbage collection. In the future, the runtime should also handle dynamic module loading.

We have a staged build system. Stage0 is the compiler snapshot, stored in schism-stage0.wasm. Stage1 is generated by compiling schism/ with the Stage0 compiler, Stage2 by compiling with the Stage1 compiler, and Stage3 by compiling with the Stage2 compiler. Stage3 should be equal to the Stage2 compiler, and currently we only generate it to verify this is the case.

To add a new feature, the usual flow is to start by adding a small test that uses it. This test will probably fail at least the Stage0 compiler. Once the feature is implemented, then the Stage1 and Stage2 compilers should pass the test. Note that you cannot use the new feature in the compiler until it works in stage2. Once this happens, you should make a new snapshot using

You can’t perform that action at this time.