Flex/Bison parser for .was files.
To build and run regression tests use:
make
make test
To parse a single file and print its grammar production rules, use:
./was test/test.was --debug
Was input is interpreted as a sequence of Unicode code points encoded in UTF-8. Most Was grammar rules are defined in terms of printable ASCII-range code points, but a small number are defined in terms of Unicode properties or explicit code point lists.
An identifier is prefixed with $ characters. The identifier can be any stream of bytes including zero-length one:
- The characters must be one of ASCII chars:
[0-9a-zA-Z_$.]; - Or, hex encoded byte in form:
\HEXDIGIT HEXDIGIT, where HEXDIGIT is[0-9a-fA-F].
Examples of valid identifiers include: $abc, $0, $_0, $\00 and $.
The $ sigil prefix on identifiers cleanly ensures that they never collide with as keywords.
Comments in Was code follow the general C++ style of line (//) and block (/* ... */) comment forms. Nested block comments are supported.
// This is a comment
/* This is also a comment */
/* This is a comment
/* as well */
Whitespace is any non-empty string containing only the following characters:
U+0020(space,)U+0009(tab,\t)U+000B(vertical tab,\v)U+000A(line feed,\n)U+000C(form feed,\f)U+000D(carriage return,\r)
Was is a "free-form" language, meaning that all forms of whitespace serve only to separate tokens in the grammar, and have no semantic significance. A Was program has identical meaning if each whitespace element is replaced with any other legal whitespace element, such as a single space character.
Was does not require you to write a semicolon ; after a statement or expression.
In fact, Was does not have statements, it only has expressions which are space delimited.
| Number literals | Example | Suffixes |
|---|---|---|
| Decimal integer | 123 | Integer suffixes |
| Hex integer | 0xff | Integer suffixes |
| Octal integer | 0o77 | Integer suffixes |
| Binary integer | 0b11001100 | Integer suffixes |
| Hex floating-point | 0x1.abc012p+789 | Floating-point suffixes |
| Floating-point | 123.0E+77 | Floating-point suffixes |
Number literals may be followed (immediately, without any spaces) by an integer or floating-point suffix, which forcibly sets the type of the literal.
The integer suffix must be the name of one of the integral types: u32, i32, u64, i64.
The floating-point suffix must be the name of one of the floating-point types: f32 or f64.
Control flow operators (described here)
| Name | Syntax | Examples |
|---|---|---|
nop |
nop |
|
block |
{ … [ break-label : ] } |
{}, { br $a nop $a:} |
loop |
loop [ continue-label ] { … [ break-label : ] } |
loop $a { br $a } |
if |
if ( expr ) { expr * } |
if (0) { 1 } |
if_else |
if ( expr ) { expr * } else { expr *} |
if (0) { 1 } else { 2 } |
select |
select ( expr , expr , expr ) |
select (1, 2, $x < $y) |
br |
br [ ( expr ) ] label |
br $a |
br_if |
br_if ( expr [ ',' expr ] ) label |
br_if (1, $x < $y) $a |
br_table |
br_table ( expr [ ',' expr ] ) [ case-label , … ] default-label |
br_table (1) [$x, $y] $z |
return |
return [ expr ] |
return |
unreachable |
unreachable |
unreachable |
Basic operators (described here)
| Name | Syntax | Example |
|---|---|---|
i32.const |
…i32opt |
123, 123i32, 0b101 |
i64.const |
…i64 |
456i64 |
f64.const |
…f32 or …f |
0.1f |
f32.const |
…f64opt |
0.2f64 |
get_local |
name | $x |
set_local |
name = expr |
$x = $x + 1 |
call |
call name (expr , … ) |
call $min(0, 2) |
call_import |
call_import name (expr , … ) |
call_import $max(0, 2) |
call_indirect |
call_indirect signature-name [ expr ] (expr , … ) |
call_indirect $type$1 [$i] $min(0, 2) |
Memory-related operators (described here)
| Name | Syntax | Example |
|---|---|---|
| memory-immediate | [ base-expression ( , offset ) ( , align = align ) ] |
[1 + 2, +4] |
i32.load8_s |
i32:8s memory-immediate |
i32:8s[0, +4] |
i32.load8_u |
… | … |
i32.load16_s |
… | … |
i32.load16_u |
… | … |
i64.load8_s |
… | … |
i64.load8_u |
… | … |
i64.load16_s |
… | … |
i64.load16_u |
… | … |
i64.load32_s |
… | … |
i64.load32_u |
… | … |
i32.load |
i32 memory-immediate |
i32[0, +4, align=2] |
i64.load |
… | … |
f32.load |
… | … |
f64.load |
… | … |
i32.store8 |
i32:8 memory-immediate = expr |
i32:8[0, align=1] = 1 + 2 |
i32.store16 |
… | … |
i64.store8 |
… | … |
i64.store16 |
… | … |
i64.store32 |
… | … |
i32.store |
… | … |
i64.store |
f64 memory-immediate = expr |
f64[0] = 5.0 |
f32.store |
… | … |
f64.store |
… | … |
memory_size |
||
grow_memory |
Simple operators (described here)
| Name | Syntax |
|---|---|
i32.add |
… + … |
i32.sub |
… - … |
i32.mul |
… * … |
i32.div_s |
… /s … |
i32.div_u |
… /u … |
i32.rem_s |
… %s … |
i32.rem_u |
… %u … |
i32.and |
… & … |
i32.or |
… | … |
i32.xor |
… ^ … |
i32.shl |
… << … |
i32.shr_u |
… >> … |
i32.shr_s |
… >>> … |
i32.eq |
… == … |
i32.ne |
… != … |
i32.lt_s |
… <s … |
i32.le_s |
… <=s … |
i32.lt_u |
… <u … |
i32.le_u |
… <=u … |
i32.gt_s |
… >s … |
i32.ge_s |
… >=s … |
i32.gt_u |
… >u … |
i32.ge_u |
… >=u … |
i32.clz |
i32.clz … |
i32.ctz |
i32.ctz … |
i32.eqz |
i32.eqz … |
i32.popcnt |
i32.popcnt … |
i64.add |
… + … |
i64.sub |
… - … |
i64.mul |
… * … |
i64.div_s |
… /s … |
i64.div_u |
… /u … |
i64.rem_s |
… %s … |
i64.rem_u |
… %u … |
i64.and |
… & … |
i64.or |
… | … |
i64.xor |
… ^ … |
i64.shl |
… << … |
i64.shr_u |
… >> … |
i64.shr_s |
… >>> … |
i64.eq |
… == … |
i64.ne |
… != … |
i64.lt_s |
… <s … |
i64.le_s |
… <=s … |
i64.lt_u |
… <u … |
i64.le_u |
… <=u … |
i64.gt_s |
… >s … |
i64.ge_s |
… >=s … |
i64.gt_u |
… >u … |
i64.ge_u |
… >=u … |
i64.clz |
i64.clz … |
i64.ctz |
i64.ctz … |
i64.eqz |
i64.eqz … |
i64.popcnt |
i32.popcnt … |
f32.add |
… + … |
f32.sub |
… - … |
f32.mul |
… * … |
f32.div |
… / … |
f32.min |
f32.min … |
f32.max |
f32.max … |
f32.abs |
f32.abs … |
f32.neg |
f32.neg … |
f32.copysign |
f32.copysign … |
f32.ceil |
f32.ceil … |
f32.floor |
f32.floor … |
f32.trunc |
f32.trunc … |
f32.nearest |
f32.nearest … |
f32.sqrt |
f32.sqrt |
f32.eq |
… == … |
f32.ne |
… != … |
f32.lt |
… < … |
f32.le |
… <= … |
f32.gt |
… > … |
f32.ge |
… >= … |
f64.add |
… + … |
f64.sub |
… - … |
f64.mul |
… * … |
f64.div |
… / … |
f64.min |
f64.min … |
f64.max |
f64.max … |
f64.abs |
f64.abs … |
f64.neg |
f64.neg … |
f64.copysign |
f64.copysign … |
f64.ceil |
f64.ceil … |
f64.floor |
f64.floor … |
f64.trunc |
f64.trunc … |
f64.nearest |
f64.nearest … |
f64.sqrt |
f64.sqrt … |
f64.eq |
… == … |
f64.ne |
… != … |
f64.lt |
… < … |
f64.le |
… <= … |
f64.gt |
… > … |
f64.ge |
… >= … |
| Name | Syntax |
|---|---|
i32.trunc_s/f32 |
i32.trunc_s/f32 |
i32.trunc_s/f64 |
… |
i32.trunc_u/f32 |
… |
i32.trunc_u/f64 |
… |
i32.wrap/i64 |
… |
i64.trunc_s/f32 |
… |
i64.trunc_s/f64 |
… |
i64.trunc_u/f32 |
… |
i64.trunc_u/f64 |
… |
i64.extend_s/i32 |
… |
i64.extend_u/i32 |
… |
f32.convert_s/i32 |
… |
f32.convert_u/i32 |
… |
f32.convert_s/i64 |
… |
f32.convert_u/i64 |
… |
f32.demote/f64 |
… |
f32.reinterpret/i32 |
… |
f64.convert_s/i32 |
… |
f64.convert_u/i32 |
… |
f64.convert_s/i64 |
… |
f64.convert_u/i64 |
… |
f64.promote/f32 |
… |
f64.reinterpret/i64 |
… |
i32.reinterpret/f32 |
… |
i64.reinterpret/f64 |
… |
| Name | Precedence | Direction | Example(s) |
|---|---|---|---|
| statements | 16 | if (…) { … }, { … } |
|
| literal | 15 | 2.0 |
|
| variable | 15 | $a |
|
| group | 15 | ( … ) |
|
| call | 15 | call, call_import |
|
| load | 15 | f32[2] |
|
| operators | 15 | f64.floor, f32.convert_s/i32, f32.min, select(…, …, …) |
|
| negate | 12 | ! |
|
| mutplication | 11 | left | *, / |
| addition | 10 | left | +, - |
| bitwise shift | 9 | left | << |
| comparison | 8 | left | <, >= |
| equality | 7 | left | ==, != |
| bitwise and | 6 | left | & |
| bitwise xor | 5 | left | ^ |
| bitwise or | 4 | left | ` |
| store | 1 | i32[0] = … |
|
| assignment | 1 | right | $a = … |