Documentation
The **SPAIK** Lisp implementation, with a [live
demo](https://moesys.no/en/#spaik)!

plus.lisp

``` commonlisp
(defun plus (&rest xs)
  (let ((s 0))
    (dolist (x xs)
      (set s (+ s x)))
    s))
```

call.rs

``` rust
use spaik::spaik::Spaik;
use spaik::error::Error;

fn api_func_call() -> Result<(), Error> {
    let mut vm = Spaik::new()?;
    vm.load("plus")?;
    let (x, y, z) = (1, 2, 3);
    let result: i32 = vm.call("plus", (x, y, z, 4, 5))?;
    assert_eq!(result, 15);
}
```

The implementation includes the following main parts:

-   **CHASM**: generates virtual ISAs from a simple description of
    op-codes
-   **R8VM**: Stack-based virtual machine
    -   Supports tracebacks on errors
-   **NKGC**: Moving tri-color mark-compact garbage collector
-   **NUKE**: Allocation tools for the garbage collector
    -   These allocation utilities are written in C
-   **COMPILER**: Single-pass compiler and macro-expander.
-   **SINTERN**: String-interner
-   **ERROR**: Structural error type for gradually building errors
    upwards through the call stack.
-   **SPAIK**: Public API.

See `lisp/test.lisp` and the `tests/*.lisp` files for an example of a
non-trivial macro, and `lisp/self.lisp` for a non-trivial program.

# Usage of `unsafe`

This crate heavily utilizes `unsafe`, this is required for the following
operations:

## Accessing GC-memory directly

**SPAIK** uses a moving garbage-collector, meaning that pointers are not
necessarily valid after a call to `gc.collect()`, even if the pointer is
known to be part of the root set. External references to GC-allocated
memory need to use indirection (see `SPV` type,) but this indirection is
not used inside the VM and GC internals because of the overhead.

Instead, the VM and GC maintain the following invariant

> A `PV` value retrieved from the garbage collector arena may not be
> used after `gc.collect()` runs.

In practice this invariant is very easy to uphold in the internals. The
`unsafe` blocks themselves are sometimes hidden behind macros for
convenience.

``` rust
// An example of unsafe GC memory access, done ergonomically using a macro
with_ref_mut!(vec, Vector(v) => {
    let elem = v.pop().unwrap_or(PV::Nil);
    self.mem.push(elem);
    Ok(())
}).map_err(|e| e.op(Builtin::Pop.sym()))?;
```

## Accessing VM program memory

Program memory needs to be randomly accessed -- fast. But by default,
Rust will use bounds-checking on arrays. This bounds-checking is usually
cheap enough, but not for this particular use-case. The VM also only
supports relative jumps, which would have required two additions when
using indexing, but only one when using pointer arithmetic.

This optimization relies on:

-   The correctness of the compiled bytecode, i.e that the compiler does
    not produce out-of-bounds jumps.
-   On-the-fly changes to the bytecode may invalidate the pointer, so
    all operations that *may* result in program memory reallocation must
    reset the pointer.
    -   Return-pointers pushed onto the **SPAIK** call stack are
        indices, not raw pointers, for this reason.

## Converting between `u8` discriminants and `enum Thing`

**SPAIK** often needs to convert integer discriminants into enums, and
vice-versa. For this it uses `unsafe { mem::transmute(num) }`.