WAVE: Web Assembly Value Encoding

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}, {}

Usage

```rust use wasmtime::component::{Type, Val};

let val: Val = wasmwave::fromstr(&Type::String, ""๐Ÿ‘‹ Hello, world! ๐Ÿ‘‹"").unwrap(); println!("{}", wasmwave::tostring(&val).unwrap()); ```

โ†’ "๐Ÿ‘‹ Hello, world! ๐Ÿ‘‹"

Encoding

Values are encoded as Unicode text. UTF-8 should be used wherever an interoperable binary encoding is required.

Whitespace

Whitespace is insignificant between tokens and significant within tokens: keywords, labels, chars, and strings.

Labels

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

Bools are encoded as the keywords false or true.

Integers

Integers are encoded as base-10 numbers.

TBD: hex/bin repr? 0xab, 0b1011

Floats

Floats are encoded as JSON numbers or one of the keywords nan, inf, or -inf.

Chars

Chars are encoded as '<char>', where <char> is one of:

Escaping \ and ' is mandatory for chars.

Strings

Strings are encoded as a double-quote-delimited sequence of <char>s (as for Chars). Escaping \ and " is mandatory for strings.

Tuples

Tuples are encoded as a parenthesized sequence of comma-separated values. Trailing commas are permitted.

tuple<u8, string> โ†’ (123, "abc")

Lists

Lists are encoded as a square-braketed sequence of comma-separated values. Trailing commas are permitted.

list<char> โ†’ ['a', 'b', 'c']

Records

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

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

Enums are encoded as a case label.

enum hand { left, right } โ†’ left

Options

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โ€ .

Results

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โ€ .

Flags

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}

Resources

TBD (<named-type>(<idx>)?)

Appendix: Function calls

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") ```

Function arguments

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.

Function results

Results are encoded in one of several ways depending on the number of result values:

```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: