From c3c6fcbfdd3bebd3a8cf37878a03282eb8dcf627 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 24 Mar 2020 16:29:29 +0100 Subject: [PATCH] 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. --- src/ast.rs | 43 ++++++++++++++++++++++++++++--------- src/decoders/binary.rs | 34 +++++++++++++++++++++++++++--- src/decoders/wat.rs | 48 +++++++++++++++++++++++++++++++----------- src/encoders/binary.rs | 27 ++++++++++++++++++++++-- src/encoders/wat.rs | 23 +++++++++++++++----- 5 files changed, 143 insertions(+), 32 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 9828043..431a3b4 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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, +pub enum TypeKind { + /// A function type. + Function, - /// Types for the results (`(result …)`). - pub outputs: Vec, + /// 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, + + /// Types for the results (`(result …)`). + outputs: Vec, + }, + + /// A record type, like: + /// + /// ```wasm,ignore + /// (@interface type (record string i32)) + /// ``` + Record { + /// Types representing the fields. + fields: Vec, + }, } /// Represents an imported function. diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index f1f7f77..3d53d1f 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -32,6 +32,19 @@ impl TryFrom for InterfaceType { } } +/// Parse a type kind. +impl TryFrom for TypeKind { + type Error = &'static str; + + fn try_from(code: u8) -> Result { + Ok(match code { + 0x00 => Self::Function, + 0x01 => Self::Record, + _ => return Err("Unknown type kind code."), + }) + } +} + /// Parse an interface kind. impl TryFrom 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)) diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index 3e41d51..3d1b149 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -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 { parser.parse::()?; - let (inputs, outputs) = parser.parens(|parser| { - parser.parse::()?; + let ty = parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); - let mut input_types = vec![]; - let mut output_types = vec![]; + if lookahead.peek::() { + parser.parse::()?; - while !parser.is_empty() { - let function_type = parser.parse::()?; + 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::()?; + + 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::() { + parser.parse::()?; + + 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) } } diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index 7a24669..e016ac0 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -112,6 +112,19 @@ where } } +/// Encode a `TypeKind` into bytes. +impl ToBytes 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 ToBytes 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(()) } diff --git a/src/encoders/wat.rs b/src/encoders/wat.rs index 7856e59..a67ce5b 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -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 + }), + ), + } } }