CLI Reference
The wax binary is the primary interface for the Wax toolchain. It supports conversion between Wax, WebAssembly Text (WAT), and WebAssembly Binary (Wasm) formats.
Usage
wax [OPTIONS] [INPUT] # convert (the default command)
wax convert [OPTIONS] [INPUT] # the same, named explicitly
wax format [OPTIONS] FILE… # reformat files
wax check [OPTIONS] FILE… # validate files
By default wax converts between formats; this command is also available under
its explicit name, wax convert (useful when an input filename could be
mistaken for a subcommand). The format subcommand reformats files (see
Formatting) and check validates them (see
Checking).
Positional Arguments
[INPUT]: Source file to convert/format. Supported extensions:.wax,.wat,.wasm.- If omitted,
waxreads fromstdin.
- If omitted,
Options
-
-o,--outputFILE- Output file. Writes to
stdoutif not specified.
- Output file. Writes to
-
-f,--format,--output-formatFORMAT- Specify the output format.
- Values:
wax,wat,wasm. - Default:
wasm(if not auto-detected from output filename).
-
-i,--input-formatFORMAT- Specify the input format.
- Values:
wax,wat,wasm. - Default: Auto-detected from input filename, or
waxif reading from stdin.
-
-v,--validate- Force validation, and additionally report unused locals.
- For Wax: Runs type checking.
- For Wasm Text: Runs well-formedness checks.
- A text input (Wax/WAT) is validated by default before it is converted to a different format: the conversion and lowering passes trust that their input is well-formed, so a malformed module is rejected up front rather than reaching them. A same-format conversion (
wat→wat,wax→wax) only re-prints and is not validated by default; a Wasm binary input is trusted and not validated.--validateforces validation in every case. - Reports a warning for any local that is declared but never read: a Wax
letbinding, or a Wasm Text(local …). This reporting is only enabled by--validate(the default validation above does not turn it on). Prefix the name with_(e.g.let _x = …,(local $_x i32)) to mark it intentionally unused and silence the warning; function parameters are never reported. Thisunused-localwarning can be silenced, kept, or promoted to an error with-W(e.g.-W unused-local=error). - For input containing conditional annotations (
#[if]/(@if ...)), every reachable combination of conditions is checked independently, and each error is reported with the assumption (reachable when ...) under which it occurs.
-
-s,--strict-validate- Perform strict reference validation for Wasm Text input (overrides the default relaxed validation). Wasm binary input is always validated strictly, regardless of this flag. When compiling Wax or Wasm Text to a binary, functions referenced by
ref.funconly inside a function body are automatically declared (via a declarative element segment), so the output passes strict validation.
- Perform strict reference validation for Wasm Text input (overrides the default relaxed validation). Wasm binary input is always validated strictly, regardless of this flag. When compiling Wax or Wasm Text to a binary, functions referenced by
-
-DNAME[=VALUE],--defineNAME[=VALUE]- Set a conditional-compilation variable, specializing
#[if(...)](Wax) and(@if ...)(WAT) annotations. A fully determined conditional is removed (its surviving branch is spliced in) and a partially determined one is kept with its condition simplified (the set variables substituted, the rest left). Comments inside a removed branch are dropped. - The value kind is inferred: a bare NAME (or
NAME=true/NAME=false) sets a boolean;NAME=N.N.N(three integers) sets a version; any otherNAME=VALUEsets a string. - Repeatable, to set several variables. Has no effect on Wasm binary input (which carries no conditionals).
- Set a conditional-compilation variable, specializing
-
-XNAME[=on|off],--featureNAME[=on|off]- Enable (or disable) an optional WebAssembly proposal, off by default.
Bare NAME or
NAME=on/true/yesenables it;NAME=off/false/nodisables it. Repeatable. Known features:custom-descriptors: exact reference types (&!t),descriptor/describesstruct clauses, and the descriptor instructions. Without it these constructs are rejected during validation.compact-import-section: group a module’s consecutive same-module imports under one module name in the binary import section. Gated on output only: it is emitted just when enabled, but the compact form is always accepted on input.
- Enable (or disable) an optional WebAssembly proposal, off by default.
Bare NAME or
-
-WNAME=LEVEL,--warnNAME=LEVEL- Set the reporting level of a warning produced during validation.
- NAME is a single warning, a group, or
all:unused-local(groupunused): a local that is declared but never read. Produced while validating; shown by default.unused-field(groupsunused,correctness): a module field (a function or global) that is defined but never referenced, exported, or used as the start function. The module-level analog ofunused-local; prefix its name with_to silence one. Shown by default.unused-import(groupsunused,correctness): an imported function or global that is never referenced, exported, or used as the start function. Likeunused-field, but for imports;_silences one. Shown by default.unused-label(groupsunused,correctness): a block label that is declared but never branched to. Prefix its name with_to silence one. Shown by default.shift-count-overflow(groupcorrectness): a shift by a constant count at least the operand’s bit width. Wasm masks the count modulo the width, so the shift is almost certainly not what was meant. Shown by default.constant-trap(groupcorrectness): an operation that always traps on a constant operand: an integer division or remainder by zero, or a trapping float-to-integer conversion of an out-of-range constant. Shown by default.tautological-comparison(groupcorrectness): a comparison whose result is constant regardless of its variable operand: an unsigned comparison against zero (x >=u 0,x <u 0), or two identical operands (x == x). Shown by default.constant-condition(groupcorrectness): a branch, loop, orselectcondition that is a constant literal (the idiomatic infinite loopwhile <nonzero>is not flagged). Shown by default.unused-result(groupcorrectness): the result of a side-effect-free expression is computed and then discarded, as in_ = x + 1. Covers reads, constants, pure arithmetic, and heap allocations (a discarded struct/array literal) whose operands are themselves effect-free. Shown by default.dead-code(groupcorrectness): a statement that can never be reached, following an unconditional branch,return, orunreachable. Shown by default.cast-always-fails(groupcorrectness): a reference cast or test whose operand can never have the target type (the two are unrelated in the type hierarchy), so the cast always traps and the test is always false. Shown by default.eager-select(groupcorrectness): a trapping or effectful operation in a branch of a?:(which compiles to aselect, evaluating both branches unconditionally), so it runs even when the condition selects the other branch. Shown by default. On Wasm input the check covers a foldedselect(its value operands are distinct subtrees); an unfoldedselectis not flagged.precedence(groupcorrectness): two operators whose relative precedence is easy to misremember are mixed without parentheses: a shift (<</>>) with arithmetic (+,-, …), or a comparison with a bitwise operator (&,|,^). The code is correct, but a reader may misread the grouping (1 << nbits - 1is1 << (nbits - 1)). Shown by default. Wax-only: WAT/Wasm have no infix precedence.redundant-operation(groupredundant): an operation with no effect on its result: an arithmetic identity (x + 0,x * 1,x << 0, …), an absorbing operand (x * 0,x & 0), two identical operands (x - x,x ^ x), a self-assignment (x = x), or a cast to a type the operand already has. Hidden by default (these are common in generated code); enable with-W redundant=warning.truncated-coverage: path-sensitive validation gave up after too many conditional configurations. Shown by default.naming-conflict(groupnaming): converting from Wasm, a source name collided with another and was renamed (e.g.foo→foo_2). Hidden by default.reserved-word-rename(groupnaming): converting from Wasm, a source name is a Wax reserved word and was renamed (e.g.if→if_2). Hidden by default.generated-name(groupnaming): converting from Wasm, an unnamed but referenced parameter was given a generated name (e.g.x), since it cannot be rendered anonymously. Hidden by default.
- LEVEL is one of:
hidden: suppress the warning entirely.warning: report it as a warning.error: promote it to an error, so the run fails.
- Repeatable; later settings override earlier ones. For example,
-W all=error -W unused-local=warningmakes every warning fatal except unused locals. - The validation warnings (
truncated-coverage, and theunusedandcorrectnesslints) are produced only while validating.truncated-coveragecan arise from the validationconvertruns on a text input; theunused/correctnesslints are turned on only by--validate, and always forcheck. Thenamingwarnings are produced when converting a Wasm module to Wax. - Almost all of these lints apply to WebAssembly text/binary input as well
as Wax: the Wasm validator runs the same checks, so
wax check foo.watreports them. (The exceptions areprecedence, which is Wax-only since WAT/Wasm have no infix precedence, andeager-select, whose Wasm side covers only a foldedselect.) On Wasm inputunused-resultcovers discarding a constant or a purelocal.get/global.getread; forunused-labela numericbr Ncounts as a use of the labelNlevels out;cast-always-failsandredundant-operationreason aboutref.cast/ref.testand constant operands directly from the bytecode. As withunused-local, they fire only when unused reporting is on (undercheck, or under a conversion with--validate). - The
WAX_WARNenvironment variable sets default levels applied before the-Woptions. Its value is a list ofNAME=LEVELspecs separated by commas or whitespace, e.g.WAX_WARN="correctness=hidden unused-local=error". Unlike a plain environment fallback, these defaults still apply when-Wis given: the command line only refines them (soWAX_WARN=correctness=hiddenwith-W dead-code=warninghides the tier except dead code). A malformed or unknown entry is reported on stderr and skipped.
-
--colorWHEN- Colorize output.
- Values:
always,never,auto. - Default:
auto(colors enabled only if output is a TTY).
-
--fold- Fold instructions into nested S-expressions.
- Affects WAT output (the default form is chosen automatically).
-
--unfold- Unfold instructions into flat instruction lists.
- Affects WAT output (the default form is chosen automatically).
-
--desugar- Expand the Wax-specific
(@string …)and(@char …)annotations into core WebAssembly (array.new_fixed/i32.const), so the output is plain WebAssembly text that other tools accept. Ani8string keeps its raw UTF-8 bytes; ani16string is UTF-16-encoded; an untyped string reuses an existing(array (mut i8))type when the module has one and otherwise pins a synthesised one. A module-level(@string …)global becomes an ordinary global. - Only valid with wat output (
-f wat); requesting it for wasm or wax output is a usage error. - Fails (exit
128) if a conditional-compilation directive(@if …)remains unresolved; resolve them first with-D/--define.
- Expand the Wax-specific
-
--source-map-fileFILE- Generate a source map file. Only valid with wasm output (
-f wasm); requesting one for wat or wax output is an error.
- Generate a source map file. Only valid with wasm output (
-
--debugCATEGORY- Enable developer debug output for a category. Repeatable, and a single
value may list several categories separated by commas
(
--debug timing,…). - Categories:
timing: log the wall-clock running time of each compiler pass (parse, specialize, validate, type-check, convert, output) to stderr, one line per pass as it finishes. The normal output on stdout is unchanged.
- Enable developer debug output for a category. Repeatable, and a single
value may list several categories separated by commas
(
Examples
Convert a Wax file to Wasm binary:
wax input.wax -o output.wasm
Convert a Wasm Text file to Wax (decompilation):
wax input.wat -o output.wax
Format a Wax file (round-trip):
wax input.wax -f wax
Read from stdin and write to stdout:
cat input.wax | wax -f wasm > output.wasm
Specialize conditional compilation while converting:
wax input.wax -D debug=false -D ocaml_version=5.1.0 -D target=wasi -o output.wasm
Formatting
The format subcommand reformats files in their own format (.wat → .wat,
.wax → .wax, .wasm → .wasm), detected from each file’s extension.
Formatting never validates: it only re-prints; use check to
validate.
wax format [OPTIONS] FILE…
Options
-i,--inplace- Write the formatted output back to each input file.
- Without this flag (and without
--check), exactly one file must be given and its formatted output is written tostdout.
-c,--check- Write nothing; list the files that are not already formatted and exit with
a non-zero status if any are found. Useful in CI. Cannot be combined with
--inplace.
- Write nothing; list the files that are not already formatted and exit with
a non-zero status if any are found. Useful in CI. Cannot be combined with
-f,--format,--input-formatFORMAT- Treat all input files as this format (
wat,wasmorwax), overriding the detection from each file’s extension.
- Treat all input files as this format (
-WNAME=LEVEL,--warnNAME=LEVEL: set a warning’s level, as above.--colorWHEN: as above (ignored when writing back in place).--fold/--unfold: as above.--debugCATEGORY: as above.
Examples
Format a single file to stdout:
wax format input.wat
Reformat several files in place:
wax format -i a.wax b.wax c.wax
Check formatting in CI (non-zero exit if any file differs):
wax format --check src/*.wax
Checking
The check subcommand validates files (type-checking for Wax, well-formedness
for Wasm) without producing any output, reporting diagnostics and exiting with a
non-zero status if any file fails. It takes one or more files. Unused-local
warnings (see --validate) are reported too, but do not by
themselves cause a non-zero exit, unless promoted to an error with
-W (e.g. wax check -W unused-local=error src/*.wax).
wax check [OPTIONS] FILE…
Options
-f,--format,--input-formatFORMAT: force the format of all files, overriding extension detection.-s,--strict-validate: strict reference validation for Wasm Text, as above (Wasm binary is always strict).-DNAME[=VALUE],--defineNAME[=VALUE]: set a conditional-compilation variable, as forconvert: the conditionals are specialized before validation. A full set validates one configuration; a partial set leaves the remaining conditionals for the path-sensitive check to explore. Repeatable.-XNAME[=on|off],--featureNAME[=on|off]: enable or disable an optional proposal, as forconvert. Repeatable.-WNAME=LEVEL,--warnNAME=LEVEL: set a warning’s level, as above.--colorWHEN: as above.--debugCATEGORY: as above.
Example
Type-check several Wax files (e.g. in CI):
wax check src/*.wax
Exit status
All three commands share the same exit codes:
| Code | Meaning |
|---|---|
0 | Success. For check / format --check, also means every file passed. |
123 | A usage error: an invalid combination of flags (e.g. --source-map-file with text output, or binary output to a terminal), or a format --check run that found files needing formatting. |
124 | A command-line parse error: an unknown flag or a bad option value (reported by the argument parser). |
125 | An internal error (an uncaught exception). |
128 | The input was rejected by a diagnostic: a parse, validation, or type error, or a malformed wasm binary. This is also the status of a check run that found any problem. |
The distinction is how you used the tool (123/124) versus the input is
bad (128). A rejected input reports 128 wherever the error is found
(syntax, validation, or type-checking) so check exits 0 when every input is
valid and 128 otherwise, which is what a CI gate wants. For scripting, treat
any non-zero status as failure.
Shell completion
wax supports zsh, bash, and PowerShell completion: subcommands, flag
names, flag values (--format → wat/wasm/wax, -W/-X names and values,
--color, --debug), and input/output file paths.
Completion has two halves. The generic per-shell scripts ship with the
cmdliner package; the wax
package installs its own completion definitions (and man pages) alongside the
binary. Installing wax through opam puts both in place. (These extra files are
installed on Unix only; they are skipped on native Windows.)
Then hook the generic scripts into your shell once. For zsh, ensure the
site-functions directory is on $fpath before compinit:
FPATH="$(opam var share)/zsh/site-functions:$FPATH"
autoload -Uz compinit && compinit
For bash, with bash-completion
installed, point it at the cmdliner share directory. See the
cmdliner completion guide
for the per-shell details and the PowerShell setup.