feat(interface-types) Introduce the record type.

This patch updates the `Type` type to be an enum with 2 variants:
`Function` and `Record`, resp. to represent:

1. `(@interface type (func (param i32 i32) (result string)))`
2. `(@interface type (record string i32))`

This patch updates the binary encoder and decoder, along with the WAT
encoder and decoder.
This commit is contained in:
Ivan Enderlin 2020-03-24 16:29:29 +01:00
parent 63f824a240
commit c3c6fcbfdd
5 changed files with 143 additions and 32 deletions

View File

@ -50,18 +50,41 @@ pub enum InterfaceType {
I64,
}
/// Represents a type signature.
///
/// ```wasm,ignore
/// (@interface type (param i32 i32) (result string))
/// ```
/// Represents the kind of type.
#[derive(PartialEq, Debug)]
pub struct Type {
/// Types for the parameters (`(param …)`).
pub inputs: Vec<InterfaceType>,
pub enum TypeKind {
/// A function type.
Function,
/// Types for the results (`(result …)`).
pub outputs: Vec<InterfaceType>,
/// A record type.
Record,
}
/// Represents a type.
#[derive(PartialEq, Debug)]
pub enum Type {
/// A function type, like:
///
/// ```wasm,ignore
/// (@interface type (func (param i32 i32) (result string)))
/// ```
Function {
/// Types for the parameters (`(param …)`).
inputs: Vec<InterfaceType>,
/// Types for the results (`(result …)`).
outputs: Vec<InterfaceType>,
},
/// A record type, like:
///
/// ```wasm,ignore
/// (@interface type (record string i32))
/// ```
Record {
/// Types representing the fields.
fields: Vec<InterfaceType>,
},
}
/// Represents an imported function.

View File

@ -32,6 +32,19 @@ impl TryFrom<u8> for InterfaceType {
}
}
/// Parse a type kind.
impl TryFrom<u8> for TypeKind {
type Error = &'static str;
fn try_from(code: u8) -> Result<Self, Self::Error> {
Ok(match code {
0x00 => Self::Function,
0x01 => Self::Record,
_ => return Err("Unknown type kind code."),
})
}
}
/// Parse an interface kind.
impl TryFrom<u8> for InterfaceKind {
type Error = &'static str;
@ -234,10 +247,25 @@ fn types<'input, E: ParseError<&'input [u8]>>(
let mut types = Vec::with_capacity(number_of_types as usize);
for _ in 0..number_of_types {
consume!((input, inputs) = list(input, ty)?);
consume!((input, outputs) = list(input, ty)?);
consume!((input, type_kind) = byte(input)?);
types.push(Type { inputs, outputs });
let type_kind = TypeKind::try_from(type_kind)
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?;
match type_kind {
TypeKind::Function => {
consume!((input, inputs) = list(input, ty)?);
consume!((input, outputs) = list(input, ty)?);
types.push(Type::Function { inputs, outputs });
}
TypeKind::Record => {
consume!((input, fields) = list(input, ty)?);
types.push(Type::Record { fields });
}
}
}
Ok((input, types))

View File

@ -13,6 +13,7 @@ mod keyword {
// New keywords.
custom_keyword!(implement);
custom_keyword!(r#type = "type");
custom_keyword!(record);
// New types.
custom_keyword!(s8);
@ -401,25 +402,48 @@ impl<'a> Parse<'a> for Type {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::r#type>()?;
let (inputs, outputs) = parser.parens(|parser| {
parser.parse::<keyword::func>()?;
let ty = parser.parens(|parser| {
let mut lookahead = parser.lookahead1();
let mut input_types = vec![];
let mut output_types = vec![];
if lookahead.peek::<keyword::func>() {
parser.parse::<keyword::func>()?;
while !parser.is_empty() {
let function_type = parser.parse::<FunctionType>()?;
let mut input_types = vec![];
let mut output_types = vec![];
match function_type {
FunctionType::Input(mut inputs) => input_types.append(&mut inputs),
FunctionType::Output(mut outputs) => output_types.append(&mut outputs),
while !parser.is_empty() {
let function_type = parser.parse::<FunctionType>()?;
match function_type {
FunctionType::Input(mut inputs) => input_types.append(&mut inputs),
FunctionType::Output(mut outputs) => output_types.append(&mut outputs),
}
}
}
Ok((input_types, output_types))
Ok(Type::Function {
inputs: input_types,
outputs: output_types,
})
} else if lookahead.peek::<keyword::record>() {
parser.parse::<keyword::record>()?;
let fields = parser.parens(|parser| {
let mut fields = vec![];
while !parser.is_empty() {
fields.push(parser.parse()?);
}
Ok(fields)
})?;
Ok(Type::Record { fields })
} else {
Err(lookahead.error())
}
})?;
Ok(Type { inputs, outputs })
Ok(ty)
}
}

View File

@ -112,6 +112,19 @@ where
}
}
/// Encode a `TypeKind` into bytes.
impl<W> ToBytes<W> for TypeKind
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
TypeKind::Function => 0x00_u8.to_bytes(writer),
TypeKind::Record => 0x01_u8.to_bytes(writer),
}
}
}
/// Encode an `InterfaceKind` into bytes.
impl<W> ToBytes<W> for InterfaceKind
where
@ -136,8 +149,18 @@ where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.inputs.to_bytes(writer)?;
self.outputs.to_bytes(writer)?;
match self {
Type::Function { inputs, outputs } => {
TypeKind::Function.to_bytes(writer)?;
inputs.to_bytes(writer)?;
outputs.to_bytes(writer)?;
}
Type::Record { fields } => {
TypeKind::Record.to_bytes(writer)?;
fields.to_bytes(writer)?;
}
}
Ok(())
}

View File

@ -167,11 +167,24 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String {
/// Encode a `Type` into a string.
impl<'input> ToString for &Type {
fn to_string(&self) -> String {
format!(
r#"(@interface type (func{inputs}{outputs}))"#,
inputs = input_types_to_param(&self.inputs),
outputs = output_types_to_result(&self.outputs),
)
match self {
Type::Function { inputs, outputs } => format!(
r#"(@interface type (func{inputs}{outputs}))"#,
inputs = input_types_to_param(&inputs),
outputs = output_types_to_result(&outputs),
),
Type::Record { fields } => format!(
r#"(@interface type (record {fields}))"#,
fields = fields
.iter()
.fold(String::new(), |mut accumulator, interface_type| {
accumulator.push(' ');
accumulator.push_str(&interface_type.to_string());
accumulator
}),
),
}
}
}