WAVE is a human-oriented text encoding of WebAssembly Component Model values. It is based largely on the WIT IDL format.
|Type|Example Values
|---|---
|Bools|true
, false
|Integers|123
, -9
|Floats|3.14
, 6.022e+23
, nan
, -inf
|Chars|'x'
, 'โ๏ธ'
, '\''
, '\u{0}'
|Strings|"abc\t123"
|Tuples|("abc", 123)
|Lists|[1, 2, 3]
|Records|{field-a: 1, field-b: "two"}
|Variants|days(30)
, forever
|Enums|south
, west
|Options|"bare some"
, some("explicit some")
, none
|Results|"bare ok"
, ok("explicit ok")
, err("oops")
|Flags|{read, write}
, {}
```rust use wasmtime::component::{Type, Val};
let val: Val = wasmwave::fromstr(&Type::String, ""๐ Hello, world! ๐"").unwrap(); println!("{}", wasmwave::tostring(&val).unwrap()); ```
โ "๐ Hello, world! ๐"
Values are encoded as Unicode text. UTF-8 should be used wherever an interoperable binary encoding is required.
Whitespace is insignificant between tokens and significant within tokens: keywords, labels, chars, and strings.
Kebab-case labels are used for record fields, variant cases, enum cases, and flags. Labels use ASCII alphanumeric characters and hyphens, following the Component Model label
syntax.
Bools are encoded as the keywords false
or true
.
Integers are encoded as base-10 numbers.
TBD: hex/bin repr?
0xab
,0b1011
Floats are encoded as JSON numbers or one of the keywords nan
, inf
, or -inf
.
Chars are encoded as '<char>'
, where <char>
is one of:
\'
โ '
\"
โ "
\\
โ \
\t
โ U+9 (HT)\n
โ U+A (LF)\r
โ U+D (CR)\u{ยทยทยท}
โ U+ยทยทยท (where ยทยทยท
is hex Unicode Scalar Value)Escaping \
and '
is mandatory for chars.
Strings are encoded as a double-quote-delimited sequence of <char>
s (as for Chars). Escaping \
and "
is mandatory for strings.
Tuples are encoded as a parenthesized sequence of comma-separated values. Trailing commas are permitted.
tuple<u8, string>
โ (123, "abc")
Lists are encoded as a square-braketed sequence of comma-separated values. Trailing commas are permitted.
list<char>
โ ['a', 'b', 'c']
Records are encoded as curly-braced set of comma-separated record entries. Trailing commas are permitted. Each record entry consists of a field label, a colon, and a value. Record entries with the option
-typed value none
may be omitted.
clike
record example {
must-have: u8,
optional: option<u8>,
}
โ {must-have: 123}
= {must-have: 123, optional: none,}
Variants are encoded as a case label. If the case has a payload, the label is followed by the parenthesized case payload value.
variant error { eof, other(string) }
โ other("oops")
Enums are encoded as a case label.
enum hand { left, right }
โ left
Options may be encoded in their variant form
(e.g. some(1)
or none
). A some
value may also be
encoded as the "bare" payload value itself, but only if
the payload is not an option, result, variant, or enum typeโ .
option<u8>
โ 123
= some(123)
option<option<u8>>
โ some(123)
= some(some(123))
Results may be encoded in their variant form
(e.g. ok(1)
, err("oops")
). An ok
value may also be
encoded as the "bare" payload value itself, but only if
it has a payload and the payload is not an option, result, variant, or enum typeโ .
result<u8>
โ 123
= ok(123)
result<_, string>
โ ok
, err("oops")
result
โ ok
, err
Flags are encoded as a curly-braced set of comma-separated flag labels. Trailing commas are permitted.
flags perms { read, write, exec }
โ {read, write,}
TBD: Allow record form?
{read: true, write: true, exec: false}
TBD (
<named-type>(<idx>)
?)
Some applications may benefit from a standard way to encode function calls and/or results, described here.
Function calls can be encoded as some application-dependent function identifier (such as a kebab-case label) followed by parenthesized function arguments.
If function results need to be encoded along with the call, they can be separated from the call by ->
.
```clike my-func("param")
with-result() -> ok("result") ```
Arguments are encoded as a sequence of comma-separated values.
Any number of option
none
values at the end of the
sequence may be omitted.
clike
// f: func(a: option<u8>, b: option<u8>, c: option<u8>)
// all equivalent:
f(some(1))
f(some(1), none)
f(some(1), none, none)
TBD: Named-parameter encoding? e.g.
my-func(named-param: 1)
Could allow omitting "middle"none
params.
Results are encoded in one of several ways depending on the number of result values:
()
or omitted entirely.```clike -> ()
-> some("single result") // or -> (0: some("single result"))
-> (result-a: "abc", result-b: 123) ```
โ These payload type restrictions on "bare" option
some
and result
ok
encodings simplify parsing and prevent ambiguity where a payload value encoding "looks like" the outer value, e.g. an enum
with a none
case or a variant
with an ok
case. While it may seem that this restriction could be loosened to only exclude types that actually have such an ambiguous case name, a subtype-compatible change to the payload type could cause previously-encoded data to become ambiguous retroactively.
:ocean: