# Guile Hoot

![Hoot logo](./hoot.png)

Hoot is an ahead-of-time, whole-program compiler for [Guile
Scheme](https://gnu.org/software/guile) to WebAssembly (aka Wasm)
developed by the [Spritely Institute](https://spritely.institute/).
In addition to the compiler, Hoot contains a full WebAssembly
toolchain with an assembler, a disassembler, a linker, an interpreter,
etc.  Hoot produces binaries that conform to the Wasm 3.0
specification which features tail calls and heap-allocated reference
types with garbage collection.

## Compatibility

Hoot is compatible with the following Wasm runtimes:

- Firefox 121+
- Chrome 119+
- Safari 26+
- NodeJS 22+

Hoot is currently *unsupported* on WASI runtimes.

## Compilation and runtime

A whole-program compilation approach has been chosen in order to
create smaller binaries at the expense of increased compilation time.
As we implement more of Scheme, we hope to preserve this "small
programs compile to small files" property, rather than having every
compiled program include the whole of Guile's standard library.

Despite whole-program compilation, it's possible for multiple Hoot
modules to be bound together and interoperate.  Hoot makes a
distinction between main and auxilary modules.  Main modules include
the full Scheme runtime and export their ABI whereas auxilary modules
exclude the runtime and import their ABI from a main module.

To run Hoot binaries on web browsers there is an associated JavaScript
module that handles booting the Wasm modules with the necessary host
imports and translates Scheme values to/from JavaScript.  Some non-web
targets are hosted by JavaScript implementations (e.g. NodeJS) and
those can make use of the same support module.

## Project status

Hoot has not yet achieved maximum Guile compatibility, but [R7RS-small
Scheme](https://small.r7rs.org/) is well supported.  Despite gaps in
compatibility, Guile libraries such as [Spritely
Goblins](https://spritely.institute/goblins/) are known to compile and
run on Hoot.

All Wasm features that Hoot relies upon are available in all major web
browsers and NodeJS.

To build and run Hoot, a bleeding-edge Guile built from the `main`
branch in Git is required, as several necessary changes that have been
upstreamed to Guile have not made it into a stable release yet.

For a fuller picture of project status, including known limitations,
see the ["Status" section of our
documentation.](https://spritely.institute/files/docs/guile-hoot/latest/Status.html).

## But... why the name "Hoot"?

We thought this project deserved a cute project name and mascot, and
everyone at the time agreed an owl was nice, and Christine
Lemmer-Webber had recently just drawn up this owl pixel art, and so it
became the mascot.  The name naturally flowed from there.

## Installing from a stable release

Note that at the time of writing, Hoot requires a development version
of Guile.  This may not be the case at your time of reading!

Below are system-specific instructions for installing Hoot.

### On Guix

Hoot is already available in [Guix](https://guix.gnu.org/):

```
guix shell --pure guile-next guile-hoot
```

(The `--pure` flag is to reduce the likelihood of failure due to
system-specific configuration.  You may not need this flag.)

### On macOS (Homebrew)

Hoot is [available in macOS thanks to to Alex Conchillo Flaqué and the
Guile Homebrew
repository](https://github.com/aconchillo/homebrew-guile).

Add the Guile Homebrew tap if you haven't already:

```
brew tap aconchillo/guile
```

If Guile is already installed with Homebrew, unlink it since we need a
newer version:

```
brew unlink guile
```

Now, just install Hoot:

```
brew install guile-hoot
```

This will also install `guile-next`, a bleeding edge version of Guile,
so it might take a while if there's no bottle available.

## Building from source

### Easy path: Use Guix

This is by far the easiest path because Guix does all the hard work
for you.

First, clone the repository:

```
git clone https://codeberg.org/spritely/hoot
cd hoot
guix shell
./bootstrap.sh && ./configure && make
```

If everything went okay then you can now run `make check`:

```
make check
```

Did everything pass?  Cool!  That means Hoot works on your machine!

### Advanced path: Build dependencies on your own

Maybe you want to understand better what Hoot is actually doing, or
maybe you want to hack on the version of Guile used for Hoot, or etc!
This section is for you.

First, you need to build Guile from the `main` branch.

Then you can clone and build this repo:

```
git clone https://codeberg.org/spritely/hoot
cd hoot
./bootstrap.sh && ./configure && make
```

To run the test suite against a production Wasm host, you will need a
V8 distribution such as NodeJS 22+ or a standalone V8 build.  NodeJS
is recommended.

Building V8 is annoying, to say the least.  You need to have
`depot_tools` installed; see https://v8.dev/docs/source-code.  Once
you have that see https://v8.dev/docs/build to build.  You will end up
with a `d8` binary in `out/x64.release` (if you are on an x86-64
platform).  We discourage this route except in the very unlikely case
that you are developing a Hoot feature that requires a fresher V8 than
NodeJS provides.

If all that works you should be able to run the test suite:

```
make check
```

By default, the test suite runs against both V8 (either NodeJS or V8's
`d8` tool) and Hoot's Wasm interpreter.  If you want to run the test
suite with just one or the other, you can use the `WASM_HOST`
environment variable:

```
make check WASM_HOST=hoot
# OR
make check WASM_HOST=node
# OR
make check WASM_HOST=d8
```

## Try it out

Hoot is a self-contained system, so the easiest way to try it is from
the Guile REPL:

```
./pre-inst-env guile
```

From the Guile prompt, enter the following to evaluate the program
`42` in Hoot's built-in Wasm interpreter:

```
scheme@(guile-user)> ,use (hoot reflect)
scheme@(guile-user)> (compile-value 42)
$5 = 42
```

More interestingly, Scheme procedures that live within the Wasm guest
module can be called from Scheme as if they were host procedures:

```
scheme@(guile-user)> (define hello (compile-value '(lambda (x) (list "hello" x))))
scheme@(guile-user)> hello
$6 = #<hoot #<procedure>>
scheme@(guile-user)> (hello "world")
$7 = #<hoot ("hello" "world")>
```

Hoot also extends Guile's build tool `guild` with the `guild
compile-wasm` subcommand which can be used to compile and (optionally)
run a Scheme file:

```
echo 42 > 42.scm
./pre-inst-env guild compile-wasm --run 42.scm
```

The above command compiles `42.scm` to Wasm and runs the resulting
binary in Hoot's Wasm interpreter.  Here's how to run it with NodeJS
instead:

```
./pre-inst-env guild compile-wasm --run=node 42.scm
```

The binaries can be saved to disk, too, of course:

```
./pre-inst-env guild compile-wasm -o 42.wasm 42.scm
```

To actually load `42.wasm` you could use the Hoot VM as mentioned
above or use a production Wasm implementation such as a web browser.
See the manual for further instructions on production deployment and
full API documentation.

## Examples

For quickly getting started with a new project, see
`examples/project-template/README.md` for an explanation of how to use
our project template.

For more examples of using Hoot, check out a some of our other repos:

* https://codeberg.org/spritely/hoot-ffi-demo
* https://codeberg.org/spritely/hoot-repl
* https://codeberg.org/spritely/hoot-game-jam-template

## Contributing

Hoot's Git repository is hosted on Codeberg:
https://codeberg.org/spritely/hoot

Bug reports and other issues can be filed here:
https://codeberg.org/spritely/hoot/issues

Pull requests are very much welcome!
