In spirit of SICP exercises 5.49-52.
- The compiler is based on the Scheme to register machine compiler from SICP section 5.5
- Wasm stands for WebAssembly
- WAT is the WebAssembly Text Format
- Implement the compiler in R7RS-small Scheme, using GNU Guile and GNU Emacs as the development platform. (N.B. this project originally used R6RS Scheme and Racket for development)
- Learn about Scheme and compilation of functional languages in general
- Get familiar with WebAssembly as an execution environment and compiler target
- Stay in the spirit of simple Scheme and target initially to support a subset of R7RS-small Scheme
- Compile Scheme forms directly to as idiomatic Wasm as feasible
- Use only basic, standardised Wasm core features initially
- Implement as much of the required run-time support in Scheme as possible
- Write the required native run-time support in WAT
- Compile a Scheme interpreter to Wasm using the compiler (see also SICP exercise 5.52)
- If the interpreter works, host it on a web page with minimal JavaScript-driven REPL
- Maybe try to bootstrap the compiler at some point as an interesting exercise
- Build a full-blown implementation with robust error reporting, tooling, full Scheme libarary support etc.
- Good interoperability with JavaScript is not critical, except where it helps in testing the compiler or hosting an interpreter or the compiler on the Web
- Do not aim to replace JavaScript on the Web
Required tools:
- GNU Make for build and test orchestration
- GNU Guile for running the compiler on the host
- WABT for assembling the WAT files produced by the compiler to Wasm binaries and for running the compiler tests as defined in the Makefile. Compiler's output can be tested with any Wasm tooling that provides a WAT to Wasm assembler and a Wasm runtime.
Run GNU make
or make help
to list the build targets with short explanations.
GNU Emacs with Geiser forms a good development platform for working with the code.
- Compilation of 32-bit integer values and open-coded application of + - * / = operators
- comparison operators = < > <= >= for 32-bit integers
- Scheme if statement to Wasm if statement
- Scheme lambda expressions to Wasm functions and values that can be applied to arguments
- Port the compiler from Racket
#lang sicp
to standard R6RS Scheme (still using Racket as the development platform) - A simple compiler "driver" that can be given a Scheme program from standard input and that emits a Wasm module to standard output in WAT format
- Makefile for building the compiler and regression test execution
- Regression tests for the implemented features
- Tests are specified in WAST and executed with WABT's spectest-interp tool
- The tests invoke the compiler with a Scheme library, compile it and check the executed WebAssembly's result with WAST assertions
- See test-compiler for the tests. They also give an overall idea of what works and has been implemented in the compiler.
- Compilation of Scheme R7RS library to a Wasm module with the top-level code in an exported
func
"main" - Top-level
define
of values and procedures set!
top-level and in-scope binding values- Support for exported top-level procedure definitions with R7RS libary syntax
- Basic syntax and semantic error detection and tests for error handling
- Implement
let
andlet*
with Wasm locals instead oflambda
, if possible. (Using the bindings in closures will need further work) and
andor
expressions with short circuit using Wasm block structure and conditional branch instructionsnot
expression- Compile
cond
to Wasm block structure and conditional branch instructions - Special form symbols and inlined procedures can be overridden with local bindings
- Add a way to raise errors from compiled code. Needed for halting the program when a type error is detected.
- Convert the source code R7RS-small Scheme and switch to Guile as the host Scheme implementation. (It would have been natural to use MIT Scheme for this project because of its SICP origins, but it does not support the M1 Apple Silicon Mac I am using as my main machine.)
- Add bit tagged typing to values and type predicates:
number?
,boolean?
,procedure?
and uninitialized and unspecified values and add type checking to generated code. - Literal symbols
- Literal strings
- eqv? implementation for supported types (6.1 [R7RS]). Equivalent to eq? with the currently supported types.
- Numeric computations are not checked for over- or underflow. WebAssembly semantics apply in case of over- or underflow
- Emit runtime library code into the compiled module to have the compiler produce self-sufficient Wasm modules that don't have to be linked together with a runtime module to be executable. This is in preparation for supporting programs and I/O. See also DevNotes.
- Implement support for programs (5.1 [R7RS])
- Implement basic output facility with WASI (6.13.3 [R7RS])
- Add run-time support for rudimentary heap-based values: vectors, pairs
- Implement simple garbage collection using SICP section 5.3 as a guideline in WAT
- Implement lexical closures with function activation records as vector lists on the heap
- Implement
letrec
form. It does not make sense to implement it before closure support to enable common use case of recursion and procedures calling each other. - Scan out (see SICP chapter 4.16) internal definitions and implement the bindings with
letrec*
- Come up with a name for this project
- Add high-level design documentation with guide to the source code of the compiler
- More R7RS-small features, prioritisation TBD.
- [R7RS] Revised7 Report on the Algorithmic Language Scheme
- Structure and Interpretation of Computer Programs (SICP) Web Site
- WebAssembly home page
- WebAssembly specification
- WebAssembly text format (WAT)