// Type expressions.
//
// Was keeps Wax's spelling everywhere it can: `&t`, `&?t`, `&!t` and `&?!t` are
// the same four operators, `mut` is the same modifier, and the heap types have
// the same names. The only change is `::` where Wax writes `:`, which is forced
// -- a `:` opens a block in shrubbery and can appear only once, at the end of a
// group.
///|
/// The abstract heap type a name denotes.
fn abstract_heap(name : String) -> @wasm_types.HeapType[@ast.Ident]? {
match name {
"func" => Some(Func)
"nofunc" => Some(NoFunc)
"exn" => Some(Exn)
"noexn" => Some(NoExn)
"cont" => Some(Cont)
"nocont" => Some(NoCont)
"extern" => Some(Extern)
"noextern" => Some(NoExtern)
"any" => Some(Any)
"eq" => Some(Eq)
"i31" => Some(I31)
"struct" => Some(Struct)
"array" => Some(Array)
"none" => Some(None_)
_ => None
}
}
///|
/// The reference-type prefix an operator is: nullable, exact, or neither.
fn ref_prefix(name : String) -> (Bool, Bool)? {
match name {
"&" => Some((false, false))
"&?" => Some((true, false))
"&!" => Some((false, true))
"&?!" => Some((true, true))
_ => None
}
}
///|
/// One value type.
fn Cursor::valtype(
self : Cursor,
) -> @wasm_types.ValType[@ast.Ident] raise ReadError {
let here = self.here()
match self.peek() {
None => fail_at("expected a type", here, source=self.r.src)
Some(n) =>
match n.it {
Id("i32") => {
self.i += 1
I32
}
Id("i64") => {
self.i += 1
I64
}
Id("f32") => {
self.i += 1
F32
}
Id("f64") => {
self.i += 1
F64
}
Id("v128") => {
self.i += 1
V128
}
_ => Ref(self.reftype())
}
}
}
///|
/// One reference type: a prefix operator and a heap type.
fn Cursor::reftype(
self : Cursor,
) -> @wasm_types.RefType[@ast.Ident] raise ReadError {
let here = self.here()
let n = match self.next() {
Some(n) => n
None => fail_at("expected a reference type", here, source=self.r.src)
}
let (nullable, exact) = match as_op(n) {
Some(op) =>
match ref_prefix(op) {
Some(p) => p
None =>
fail_at(
"expected `&`, `&?`, `&!` or `&?!`",
node_span(n),
source=self.r.src,
)
}
None => fail_at("expected a type", node_span(n), source=self.r.src)
}
let typ = self.heaptype(exact)
{ nullable, typ, }
}
///|
fn Cursor::heaptype(
self : Cursor,
exact : Bool,
) -> @wasm_types.HeapType[@ast.Ident] raise ReadError {
let here = self.here()
let n = match self.next() {
Some(n) => n
None => fail_at("expected a heap type", here, source=self.r.src)
}
match as_id(n) {
Some(name) =>
match abstract_heap(name) {
Some(h) =>
if exact {
fail_at(
"an abstract heap type is never exact",
node_span(n),
source=self.r.src,
)
} else {
h
}
None =>
if exact {
Exact(self.r.ident(name, node_span(n)))
} else {
Type(self.r.ident(name, node_span(n)))
}
}
None => fail_at("expected a heap type", node_span(n), source=self.r.src)
}
}
///|
/// A field or element type: `i8`/`i16` stay packed, everything else is a value.
fn Cursor::storagetype(
self : Cursor,
) -> @wasm_types.StorageType[@ast.Ident] raise ReadError {
match self.peek() {
Some(n) if is_id(n, "i8") => {
self.i += 1
Packed(I8)
}
Some(n) if is_id(n, "i16") => {
self.i += 1
Packed(I16)
}
_ => Value(self.valtype())
}
}
///|
/// A storage type with an optional `mut`.
fn Cursor::muttype(
self : Cursor,
) -> @wasm_types.MutType[@wasm_types.StorageType[@ast.Ident]] raise ReadError {
let mut_ = self.eat_id("mut")
{ mut_, typ: self.storagetype(), }
}
///|
/// An optional `-> t` or `-> (a, b)` result list.
fn Cursor::results(
self : Cursor,
) -> Array[@wasm_types.ValType[@ast.Ident]] raise ReadError {
if !self.eat_op("->") {
return []
}
let here = self.here()
match self.peek() {
None =>
fail_at("expected a result type after `->`", here, source=self.r.src)
Some(n) =>
match n.it {
Parens(gs) => {
self.i += 1
let out = []
for g in gs {
out.push(Cursor::new(children(g), self.r).valtype())
}
out
}
_ => [self.valtype()]
}
}
}
///|
/// A parameter list: `(i32, x :: i32)`, where a name is optional because
/// wasm's is.
fn Reader::params(
self : Reader,
gs : Array[@sh.Node],
) -> Array[
@basic.Annotated[
(@ast.Ident?, @wasm_types.ValType[@ast.Ident]),
@basic.Location,
],
] raise ReadError {
let out : Array[
@basic.Annotated[
(@ast.Ident?, @wasm_types.ValType[@ast.Ident]),
@basic.Location,
],
] = []
for g in gs {
let ts = children(g)
let at = self.loc(terms_span(ts[:]))
if ts.length() >= 2 && as_id(ts[0]) is Some(name) && is_op(ts[1], "::") {
let typ = Cursor::new(ts[2:].to_owned(), self).valtype()
out.push({
desc: (Some(self.ident(name, node_span(ts[0]))), typ),
info: at,
})
} else {
let typ = Cursor::new(ts, self).valtype()
out.push({ desc: (None, typ), info: at, })
}
}
out
}
///|
/// A whole function signature: the parameter list and the results.
fn Reader::signature(
self : Reader,
params : @sh.Node,
c : Cursor,
) -> @ast.FuncType raise ReadError {
let ps = match params.it {
Parens(gs) => self.params(gs)
_ =>
fail_at("expected a parameter list", node_span(params), source=self.src)
}
{ params: ps, results: c.results(), }
}