Packages

WebAssembly to BEAM bytecode compiler for the Erlang ecosystem

Current section

Files

Jump to
wa_embedder README.md
Raw

README.md

# wa_embedder
A WebAssembly-to-BEAM compiler for the Erlang ecosystem, written in Erlang. It
parses a `.wasm` binary (via [`wa_parser`](https://hex.pm/packages/wa_parser)),
translates each function to Erlang abstract format, and compiles and loads the
result as a native BEAM module that you can call directly.
This is the Erlang package. Elixir projects can depend on it directly, or use
the companion [`wa_embedder_ex`](https://hex.pm/packages/wa_embedder_ex) package
for an idiomatic Elixir API (`WaEmbedder.compile/1..4` + `WaEmbedder.ImportError`).
## Repository layout (monorepo)
Two hex packages live in this repository:
| Path | Package | Tool | Role |
|-------------------|------------------|--------|----------------------------------------|
| repo root | `wa_embedder` | rebar3 | The compiler core (pure Erlang) |
| `wa_embedder_ex/` | `wa_embedder_ex` | mix | Thin Elixir wrapper + the test suite |
| `test_data/` || cmake | Shared WAT/C `.wasm` fixtures |
The dependency direction is one-way: `wa_embedder_ex` (Elixir) → `wa_embedder`
(Erlang) → `wa_parser` (Erlang). The core has no Elixir dependency.
## Installation (rebar3)
```erlang
%% rebar.config
{deps, [{wa_embedder, "~> 0.1"}]}.
```
## Usage
```erlang
{module, Mod} = wa_embedder:compile("module.wasm"),
Result = Mod:some_exported_fun(Arg).
```
`compile/1,2,3,4` compiles and loads the module and returns the
`code:load_binary/3` result (`{module, ModuleName}` on success). Modules with
imports take an imports map resolving each WASM import to an Erlang target
(MFA tuple, external fun, or closure); unsatisfiable imports raise
`erlang:error({import_error, Map})`.
## Development
A `nix develop` shell provides `erlang`, `elixir`, `rebar3`, and the fixture
toolchain (`cmake`, `ninja`, `wabt`, `binaryen`). Local sibling dependencies are
wired automatically on shell entry:
- **Core → parser:** rebar3 has no native `{path, ...}` dep, so the shell hook
creates a `_checkouts/wa_parser` symlink to the sibling `../wa_parser` checkout
(a rebar3 checkout takes precedence over the `{wa_parser, "~> 0.1"}` hex dep).
- **Wrapper → core:** the shell sets `WA_EMBEDDER_PATH=..` so
`wa_embedder_ex/mix.exs` builds against the sibling Erlang core.
### Build and test
```bash
# Erlang core (repo root)
rebar3 compile
# Elixir wrapper + full fixture suite (in wa_embedder_ex/)
cd wa_embedder_ex && mix test
```
The test suite lives in `wa_embedder_ex/test/` and compiles the WAT/C fixtures
under the repo-root `test_data/` (built by CMake/Ninja before the suite runs).
### Building the fixtures manually
```bash
cd test_data
cmake -B build -G Ninja
ninja -C build # WAT fixtures (default); `ninja -C build c_fixtures` for the C chain
```
### Packaging
Build the hex tarballs **without** the local dev overrides so the correct hex
deps are recorded:
```bash
# Core: remove the dev checkout so {wa_parser, "~> 0.1"} is recorded
rm -rf _checkouts && rebar3 hex build
# Wrapper: unset WA_EMBEDDER_PATH so the {:wa_embedder, "~> 0.1"} hex dep is used
cd wa_embedder_ex && env -u WA_EMBEDDER_PATH mix hex.build
```
Publish order: publish `wa_embedder` (core) first, then `wa_embedder_ex` (the
wrapper's hex dep on the core cannot resolve until the core is on hex).
## License
Released into the public domain under the [Unlicense](UNLICENSE).