From c97122899d850d7345e3640d7eac22b79daa59be Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 19 Feb 2020 16:12:06 +0100 Subject: [PATCH 01/10] feat(interface-types) Start implementing the WAT decoder. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/mod.rs | 1 + lib/interface-types/src/decoders/wat.rs | 242 ++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 lib/interface-types/src/decoders/wat.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index d8b82f588..1a9c89b69 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -5,7 +5,7 @@ use crate::interpreter::Instruction; use std::str; /// Represents the types supported by WIT. -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Clone, Copy, Debug)] pub enum InterfaceType { /// An integer. Int, diff --git a/lib/interface-types/src/decoders/mod.rs b/lib/interface-types/src/decoders/mod.rs index 0ac9d881d..1b2aaaffe 100644 --- a/lib/interface-types/src/decoders/mod.rs +++ b/lib/interface-types/src/decoders/mod.rs @@ -2,3 +2,4 @@ //! `decoders::binary` reads the AST from a binary. pub mod binary; +pub mod wat; diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs new file mode 100644 index 000000000..581f4d566 --- /dev/null +++ b/lib/interface-types/src/decoders/wat.rs @@ -0,0 +1,242 @@ +//! Parse the WIT textual representation into an AST. + +#![allow(unused)] + +use crate::ast::*; +use nom::{ + branch::alt, + bytes::complete::{escaped, tag, take_while1}, + character::complete::{alphanumeric1, char, one_of}, + combinator::{cut, opt, value}, + error::ParseError, + multi::many0, + sequence::{delimited, preceded, terminated, tuple}, + IResult, +}; + +/// Parse a whitespace. +fn whitespace<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, &'input str, E> { + let whitespaces = " \t\r\n"; + + take_while1(move |c| whitespaces.contains(c))(input) +} + +/// Parse an `InterfaceType`. +fn interface_type<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, InterfaceType, E> { + let int = value(InterfaceType::Int, tag("Int")); + let float = value(InterfaceType::Float, tag("Float")); + let any = value(InterfaceType::Any, tag("Any")); + let string = value(InterfaceType::String, tag("String")); + let seq = value(InterfaceType::Seq, tag("Seq")); + let r#i32 = value(InterfaceType::I32, tag("i32")); + let r#i64 = value(InterfaceType::I64, tag("i64")); + let r#f32 = value(InterfaceType::F32, tag("f32")); + let r#f64 = value(InterfaceType::F64, tag("f64")); + let anyref = value(InterfaceType::AnyRef, tag("anyref")); + + alt(( + int, float, any, string, seq, r#i32, r#i64, r#f32, r#f64, anyref, + ))(input) +} + +/// Parse a string. +fn string<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, &'input str, E> { + escaped(alphanumeric1, '\\', one_of(r#""\"#))(input) +} + +/// Parse a `(param …)`. +fn param<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, Vec, E> { + delimited( + char('('), + preceded( + opt(whitespace), + preceded(tag("param"), many0(preceded(whitespace, interface_type))), + ), + char(')'), + )(input) +} + +/// Parse a `(result …)`. +fn result<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, Vec, E> { + delimited( + char('('), + preceded( + opt(whitespace), + preceded(tag("result"), many0(preceded(whitespace, interface_type))), + ), + char(')'), + )(input) +} + +/// Parse an `Export`. +fn export<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, Export, E> { + //(@interface export "name") + let (remains, (export_name, input_types, output_types)): ( + &str, + (&str, Option>, Option>), + ) = delimited( + char('('), + preceded( + opt(whitespace), + preceded( + tag("@interface"), + preceded( + whitespace, + preceded( + tag("export"), + preceded( + whitespace, + tuple(( + preceded(char('"'), cut(terminated(string, char('"')))), + opt(preceded(whitespace, param)), + opt(preceded(whitespace, result)), + )), + ), + ), + ), + ), + ), + char(')'), + )(input)?; + + Ok(( + remains, + Export { + name: export_name, + input_types: input_types.unwrap_or_else(|| vec![]), + output_types: output_types.unwrap_or_else(|| vec![]), + }, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_whitespace() { + let inputs = vec![" a", " a", "\n a", "\r\n a"]; + let outputs = vec![" ", " ", "\n ", "\r\n "]; + + for (nth, input) in inputs.iter().enumerate() { + assert_eq!(whitespace::<()>(input), Ok(("a", outputs[nth]))); + } + } + + #[test] + fn test_interface_type() { + let inputs = vec![ + "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", + ]; + let outputs = vec![ + InterfaceType::Int, + InterfaceType::Float, + InterfaceType::Any, + InterfaceType::String, + InterfaceType::Seq, + InterfaceType::I32, + InterfaceType::I64, + InterfaceType::F32, + InterfaceType::F64, + InterfaceType::AnyRef, + ]; + + assert_eq!(inputs.len(), outputs.len()); + + for (nth, input) in inputs.iter().enumerate() { + assert_eq!(interface_type::<()>(input), Ok(("", outputs[nth]))); + } + } + + #[test] + fn test_param_empty() { + let input = "(param)"; + let output = vec![]; + + assert_eq!(param::<()>(input), Ok(("", output))); + } + + #[test] + fn test_param() { + let input = "(param i32 String)"; + let output = vec![InterfaceType::I32, InterfaceType::String]; + + assert_eq!(param::<()>(input), Ok(("", output))); + } + + #[test] + fn test_result_empty() { + let input = "(result)"; + let output = vec![]; + + assert_eq!(result::<()>(input), Ok(("", output))); + } + + #[test] + fn test_result() { + let input = "(result i32 String)"; + let output = vec![InterfaceType::I32, InterfaceType::String]; + + assert_eq!(result::<()>(input), Ok(("", output))); + } + + #[test] + fn test_export_with_no_param_no_result() { + let input = r#"(@interface export "foo")"#; + let output = Export { + name: "foo", + input_types: vec![], + output_types: vec![], + }; + + assert_eq!(export::<()>(input), Ok(("", output))); + } + + #[test] + fn test_export_with_some_param_no_result() { + let input = r#"(@interface export "foo" (param i32))"#; + let output = Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }; + + assert_eq!(export::<()>(input), Ok(("", output))); + } + + #[test] + fn test_export_with_no_param_some_result() { + let input = r#"(@interface export "foo" (result i32))"#; + let output = Export { + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }; + + assert_eq!(export::<()>(input), Ok(("", output))); + } + + #[test] + fn test_export_with_some_param_some_result() { + let input = r#"(@interface export "foo" (param String) (result i32 i32))"#; + let output = Export { + name: "foo", + input_types: vec![InterfaceType::String], + output_types: vec![InterfaceType::I32, InterfaceType::I32], + }; + + assert_eq!(export::<()>(input), Ok(("", output))); + } +} From 2e78cf1fc05ebb97fc521f857ae8e32c3f391052 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 19 Feb 2020 17:20:18 +0100 Subject: [PATCH 02/10] feat(interface-types) Parse `Import` in the WAT decoders. --- lib/interface-types/src/decoders/wat.rs | 198 +++++++++++++++++++++--- 1 file changed, 175 insertions(+), 23 deletions(-) diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 581f4d566..bf575018e 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -7,11 +7,11 @@ use nom::{ branch::alt, bytes::complete::{escaped, tag, take_while1}, character::complete::{alphanumeric1, char, one_of}, - combinator::{cut, opt, value}, + combinator::{cut, map, opt, value}, error::ParseError, multi::many0, sequence::{delimited, preceded, terminated, tuple}, - IResult, + AsChar, IResult, }; /// Parse a whitespace. @@ -82,24 +82,22 @@ fn result<'input, E: ParseError<&'input str>>( fn export<'input, E: ParseError<&'input str>>( input: &'input str, ) -> IResult<&'input str, Export, E> { - //(@interface export "name") - let (remains, (export_name, input_types, output_types)): ( - &str, - (&str, Option>, Option>), - ) = delimited( - char('('), - preceded( - opt(whitespace), + map( + delimited( + char('('), preceded( - tag("@interface"), + opt(whitespace), preceded( - whitespace, + tag("@interface"), preceded( - tag("export"), + whitespace, preceded( - whitespace, + tag("export"), tuple(( - preceded(char('"'), cut(terminated(string, char('"')))), + preceded( + whitespace, + preceded(char('"'), cut(terminated(string, char('"')))), + ), opt(preceded(whitespace, param)), opt(preceded(whitespace, result)), )), @@ -107,18 +105,86 @@ fn export<'input, E: ParseError<&'input str>>( ), ), ), + char(')'), ), - char(')'), - )(input)?; - - Ok(( - remains, - Export { - name: export_name, + |(name, input_types, output_types)| Export { + name, input_types: input_types.unwrap_or_else(|| vec![]), output_types: output_types.unwrap_or_else(|| vec![]), }, - )) + )(input) +} + +/// Parse a `(import …)`. +fn import_qualifier<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, (&'input str, &'input str), E> { + delimited( + char('('), + preceded( + opt(whitespace), + preceded( + tag("import"), + tuple(( + preceded( + whitespace, + preceded(char('"'), cut(terminated(string, char('"')))), + ), + preceded( + whitespace, + preceded(char('"'), cut(terminated(string, char('"')))), + ), + )), + ), + ), + char(')'), + )(input) +} + +/// Parse a `$…`. +fn index_variable<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, &'input str, E> { + preceded( + char('$'), + take_while1(move |c: char| c.is_alphanum() || c == '_'), + )(input) +} + +/// Parse an `Import`. +fn import<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, Import, E> { + map( + delimited( + char('('), + preceded( + opt(whitespace), + preceded( + tag("@interface"), + preceded( + whitespace, + preceded( + tag("func"), + tuple(( + opt(preceded(whitespace, index_variable)), + preceded(whitespace, import_qualifier), + opt(preceded(whitespace, param)), + opt(preceded(whitespace, result)), + )), + ), + ), + ), + ), + char(')'), + ), + |(_index, (namespace, name), input_types, output_types)| Import { + namespace, + name, + input_types: input_types.unwrap_or_else(|| vec![]), + output_types: output_types.unwrap_or_else(|| vec![]), + }, + )(input) } #[cfg(test)] @@ -239,4 +305,90 @@ mod tests { assert_eq!(export::<()>(input), Ok(("", output))); } + + #[test] + fn test_export_escaped_name() { + let input = r#"(@interface export "fo\"o")"#; + let output = Export { + name: r#"fo\"o"#, + input_types: vec![], + output_types: vec![], + }; + + assert_eq!(export::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_qualifier() { + let input = r#"(import "ns" "name")"#; + let output = ("ns", "name"); + + assert_eq!(import_qualifier::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_with_no_param_no_result() { + let input = r#"(@interface func $ns_foo (import "ns" "foo"))"#; + let output = Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![], + }; + + assert_eq!(import::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_with_no_index_variable_no_param_no_result() { + let input = r#"(@interface func (import "ns" "foo"))"#; + let output = Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![], + }; + + assert_eq!(import::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_with_some_param_no_result() { + let input = r#"(@interface func $ns_foo (import "ns" "foo") (param i32))"#; + let output = Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }; + + assert_eq!(import::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_with_no_param_some_result() { + let input = r#"(@interface func $ns_foo (import "ns" "foo") (result i32))"#; + let output = Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }; + + assert_eq!(import::<()>(input), Ok(("", output))); + } + + #[test] + fn test_import_with_some_param_some_result() { + let input = + r#"(@interface func $ns_foo (import "ns" "foo") (param String) (result i32 i32))"#; + let output = Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::String], + output_types: vec![InterfaceType::I32, InterfaceType::I32], + }; + + assert_eq!(import::<()>(input), Ok(("", output))); + } } From 00e36508d6b4c418fcf4b28fd83fadcda5056d53 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 19 Feb 2020 17:41:06 +0100 Subject: [PATCH 03/10] feat(interface-types) Parse `Adapter` in the WAT decoders. --- lib/interface-types/src/decoders/wat.rs | 114 +++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index bf575018e..264b6fcf5 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -115,7 +115,7 @@ fn export<'input, E: ParseError<&'input str>>( )(input) } -/// Parse a `(import …)`. +/// Parse an `(import "ns" "foo")`. fn import_qualifier<'input, E: ParseError<&'input str>>( input: &'input str, ) -> IResult<&'input str, (&'input str, &'input str), E> { @@ -141,6 +141,26 @@ fn import_qualifier<'input, E: ParseError<&'input str>>( )(input) } +/// Parse an `(export "foo")`. +fn export_qualifier<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, &'input str, E> { + delimited( + char('('), + preceded( + opt(whitespace), + preceded( + tag("export"), + preceded( + whitespace, + preceded(char('"'), cut(terminated(string, char('"')))), + ), + ), + ), + char(')'), + )(input) +} + /// Parse a `$…`. fn index_variable<'input, E: ParseError<&'input str>>( input: &'input str, @@ -187,6 +207,63 @@ fn import<'input, E: ParseError<&'input str>>( )(input) } +/// Parse an `Adapter`. +fn adapter<'input, E: ParseError<&'input str>>( + input: &'input str, +) -> IResult<&'input str, Adapter, E> { + fn adapter_import<'input, E: ParseError<&'input str>>( + input: &'input str, + ) -> IResult<&'input str, Adapter, E> { + map( + tuple(( + preceded(whitespace, import_qualifier), + opt(preceded(whitespace, param)), + opt(preceded(whitespace, result)), + )), + |((namespace, name), input_types, output_types)| Adapter::Import { + namespace, + name, + input_types: input_types.unwrap_or_else(|| vec![]), + output_types: output_types.unwrap_or_else(|| vec![]), + instructions: vec![], + }, + )(input) + } + + fn adapter_export<'input, E: ParseError<&'input str>>( + input: &'input str, + ) -> IResult<&'input str, Adapter, E> { + map( + tuple(( + preceded(whitespace, export_qualifier), + opt(preceded(whitespace, param)), + opt(preceded(whitespace, result)), + )), + |(name, input_types, output_types)| Adapter::Export { + name, + input_types: input_types.unwrap_or_else(|| vec![]), + output_types: output_types.unwrap_or_else(|| vec![]), + instructions: vec![], + }, + )(input) + } + + delimited( + char('('), + preceded( + opt(whitespace), + preceded( + tag("@interface"), + preceded( + whitespace, + preceded(tag("adapt"), alt((adapter_import, adapter_export))), + ), + ), + ), + char(')'), + )(input) +} + #[cfg(test)] mod tests { use super::*; @@ -326,6 +403,14 @@ mod tests { assert_eq!(import_qualifier::<()>(input), Ok(("", output))); } + #[test] + fn test_export_qualifier() { + let input = r#"(export "name")"#; + let output = "name"; + + assert_eq!(export_qualifier::<()>(input), Ok(("", output))); + } + #[test] fn test_import_with_no_param_no_result() { let input = r#"(@interface func $ns_foo (import "ns" "foo"))"#; @@ -391,4 +476,31 @@ mod tests { assert_eq!(import::<()>(input), Ok(("", output))); } + + #[test] + fn test_adapter_import() { + let input = r#"(@interface adapt (import "ns" "foo") (param i32 i32) (result i32))"#; + let output = Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![], + }; + + assert_eq!(adapter::<()>(input), Ok(("", output))); + } + + #[test] + fn test_adapter_export() { + let input = r#"(@interface adapt (export "foo") (param i32 i32) (result i32))"#; + let output = Adapter::Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![], + }; + + assert_eq!(adapter::<()>(input), Ok(("", output))); + } } From cda53a57b5e661abfd5f029624c2d2f46c248c62 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 20 Feb 2020 12:15:23 +0100 Subject: [PATCH 04/10] feat(interface-types) Replace nom by wast for decoding WIT's text format. --- lib/interface-types/Cargo.toml | 3 +- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/wat.rs | 743 +++++++++++++----------- 3 files changed, 420 insertions(+), 328 deletions(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index a3d247479..74a2b8e07 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -8,4 +8,5 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -nom = "5.1" \ No newline at end of file +nom = "5.1" +wast = "8.0" \ No newline at end of file diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 1a9c89b69..6bb7e9e91 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -190,7 +190,7 @@ pub struct Forward<'input> { /// Represents a set of interfaces, i.e. it entirely describes a WIT /// definition. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Default, Debug)] pub struct Interfaces<'input> { /// All the exported functions. pub exports: Vec>, diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 264b6fcf5..1c85e3f04 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -3,285 +3,331 @@ #![allow(unused)] use crate::ast::*; -use nom::{ - branch::alt, - bytes::complete::{escaped, tag, take_while1}, - character::complete::{alphanumeric1, char, one_of}, - combinator::{cut, map, opt, value}, - error::ParseError, - multi::many0, - sequence::{delimited, preceded, terminated, tuple}, - AsChar, IResult, +use wast::{ + parser::{Cursor, Parse, Parser, Peek, Result}, + Id, }; -/// Parse a whitespace. -fn whitespace<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, &'input str, E> { - let whitespaces = " \t\r\n"; +mod kw { + pub use wast::{ + custom_keyword, + kw::{anyref, export, f32, f64, func, i32, i64, import, param, result}, + }; - take_while1(move |c| whitespaces.contains(c))(input) + custom_keyword!(adapt); + custom_keyword!(forward); + custom_keyword!(int); + custom_keyword!(float); + custom_keyword!(any); + custom_keyword!(string); + custom_keyword!(seq); } -/// Parse an `InterfaceType`. -fn interface_type<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, InterfaceType, E> { - let int = value(InterfaceType::Int, tag("Int")); - let float = value(InterfaceType::Float, tag("Float")); - let any = value(InterfaceType::Any, tag("Any")); - let string = value(InterfaceType::String, tag("String")); - let seq = value(InterfaceType::Seq, tag("Seq")); - let r#i32 = value(InterfaceType::I32, tag("i32")); - let r#i64 = value(InterfaceType::I64, tag("i64")); - let r#f32 = value(InterfaceType::F32, tag("f32")); - let r#f64 = value(InterfaceType::F64, tag("f64")); - let anyref = value(InterfaceType::AnyRef, tag("anyref")); +/// Issue: Uppercased keyword aren't supported for the moment. +impl Parse<'_> for InterfaceType { + fn parse(parser: Parser<'_>) -> Result { + let mut lookahead = parser.lookahead1(); - alt(( - int, float, any, string, seq, r#i32, r#i64, r#f32, r#f64, anyref, - ))(input) + if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::Int) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::Float) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::Any) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::String) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::Seq) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::I32) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::I64) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::F32) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::F64) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::AnyRef) + } else { + Err(lookahead.error()) + } + } } -/// Parse a string. -fn string<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, &'input str, E> { - escaped(alphanumeric1, '\\', one_of(r#""\"#))(input) +struct AtInterface; + +impl Peek for AtInterface { + fn peek(cursor: Cursor<'_>) -> bool { + cursor.reserved().map(|(string, _)| string) == Some("@interface") + } + + fn display() -> &'static str { + "`@interface`" + } } -/// Parse a `(param …)`. -fn param<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, Vec, E> { - delimited( - char('('), - preceded( - opt(whitespace), - preceded(tag("param"), many0(preceded(whitespace, interface_type))), - ), - char(')'), - )(input) +impl Parse<'_> for AtInterface { + fn parse(parser: Parser<'_>) -> Result { + parser.step(|cursor| { + if let Some(("@interface", rest)) = cursor.reserved() { + return Ok((AtInterface, rest)); + } + + Err(cursor.error("expected `@interface`")) + }) + } } -/// Parse a `(result …)`. -fn result<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, Vec, E> { - delimited( - char('('), - preceded( - opt(whitespace), - preceded(tag("result"), many0(preceded(whitespace, interface_type))), - ), - char(')'), - )(input) +#[derive(PartialEq, Debug)] +enum FunctionType { + Input(Vec), + Output(Vec), } -/// Parse an `Export`. -fn export<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, Export, E> { - map( - delimited( - char('('), - preceded( - opt(whitespace), - preceded( - tag("@interface"), - preceded( - whitespace, - preceded( - tag("export"), - tuple(( - preceded( - whitespace, - preceded(char('"'), cut(terminated(string, char('"')))), - ), - opt(preceded(whitespace, param)), - opt(preceded(whitespace, result)), - )), - ), - ), - ), - ), - char(')'), - ), - |(name, input_types, output_types)| Export { +impl Parse<'_> for FunctionType { + fn parse(parser: Parser<'_>) -> Result { + parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + parser.parse::()?; + + let mut inputs = vec![]; + + while !parser.is_empty() { + inputs.push(parser.parse()?); + } + + Ok(FunctionType::Input(inputs)) + } else if lookahead.peek::() { + parser.parse::()?; + + let mut outputs = vec![]; + + while !parser.is_empty() { + outputs.push(parser.parse()?); + } + + Ok(FunctionType::Output(outputs)) + } else { + Err(lookahead.error()) + } + }) + } +} + +#[derive(PartialEq, Debug)] +enum Interface<'a> { + Export(Export<'a>), + Type(Type<'a>), + Import(Import<'a>), + Adapter(Adapter<'a>), + Forward(Forward<'a>), +} + +impl<'a> Parse<'a> for Interface<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + parser.parse::()?; + + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + Ok(Interface::Export(parser.parse()?)) + } else if lookahead.peek::() { + Ok(Interface::Import(parser.parse()?)) + } else if lookahead.peek::() { + Ok(Interface::Adapter(parser.parse()?)) + } else if lookahead.peek::() { + Ok(Interface::Forward(parser.parse()?)) + } else { + Err(lookahead.error()) + } + } else { + Err(lookahead.error()) + } + }) + } +} + +impl<'a> Parse<'a> for Export<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + let name = parser.parse()?; + + let mut input_types = vec![]; + let mut output_types = vec![]; + + 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(Export { name, - input_types: input_types.unwrap_or_else(|| vec![]), - output_types: output_types.unwrap_or_else(|| vec![]), - }, - )(input) + input_types, + output_types, + }) + } } -/// Parse an `(import "ns" "foo")`. -fn import_qualifier<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, (&'input str, &'input str), E> { - delimited( - char('('), - preceded( - opt(whitespace), - preceded( - tag("import"), - tuple(( - preceded( - whitespace, - preceded(char('"'), cut(terminated(string, char('"')))), - ), - preceded( - whitespace, - preceded(char('"'), cut(terminated(string, char('"')))), - ), - )), - ), - ), - char(')'), - )(input) -} +impl<'a> Parse<'a> for Import<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + parser.parse::()?; -/// Parse an `(export "foo")`. -fn export_qualifier<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, &'input str, E> { - delimited( - char('('), - preceded( - opt(whitespace), - preceded( - tag("export"), - preceded( - whitespace, - preceded(char('"'), cut(terminated(string, char('"')))), - ), - ), - ), - char(')'), - )(input) -} + let (namespace, name) = parser.parens(|parser| { + parser.parse::()?; -/// Parse a `$…`. -fn index_variable<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, &'input str, E> { - preceded( - char('$'), - take_while1(move |c: char| c.is_alphanum() || c == '_'), - )(input) -} + Ok((parser.parse()?, parser.parse()?)) + })?; + let mut input_types = vec![]; + let mut output_types = vec![]; -/// Parse an `Import`. -fn import<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, Import, E> { - map( - delimited( - char('('), - preceded( - opt(whitespace), - preceded( - tag("@interface"), - preceded( - whitespace, - preceded( - tag("func"), - tuple(( - opt(preceded(whitespace, index_variable)), - preceded(whitespace, import_qualifier), - opt(preceded(whitespace, param)), - opt(preceded(whitespace, result)), - )), - ), - ), - ), - ), - char(')'), - ), - |(_index, (namespace, name), input_types, output_types)| Import { + 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(Import { namespace, name, - input_types: input_types.unwrap_or_else(|| vec![]), - output_types: output_types.unwrap_or_else(|| vec![]), - }, - )(input) + input_types, + output_types, + }) + } } -/// Parse an `Adapter`. -fn adapter<'input, E: ParseError<&'input str>>( - input: &'input str, -) -> IResult<&'input str, Adapter, E> { - fn adapter_import<'input, E: ParseError<&'input str>>( - input: &'input str, - ) -> IResult<&'input str, Adapter, E> { - map( - tuple(( - preceded(whitespace, import_qualifier), - opt(preceded(whitespace, param)), - opt(preceded(whitespace, result)), - )), - |((namespace, name), input_types, output_types)| Adapter::Import { +impl<'a> Parse<'a> for Adapter<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + + let (kind, namespace, name) = parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + parser.parse::()?; + + Ok((AdapterKind::Import, parser.parse()?, parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok((AdapterKind::Export, "", parser.parse()?)) + } else { + Err(lookahead.error()) + } + })?; + let mut input_types = vec![]; + let mut output_types = vec![]; + + 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(match kind { + AdapterKind::Import => Adapter::Import { namespace, name, - input_types: input_types.unwrap_or_else(|| vec![]), - output_types: output_types.unwrap_or_else(|| vec![]), + input_types, + output_types, instructions: vec![], }, - )(input) - } - fn adapter_export<'input, E: ParseError<&'input str>>( - input: &'input str, - ) -> IResult<&'input str, Adapter, E> { - map( - tuple(( - preceded(whitespace, export_qualifier), - opt(preceded(whitespace, param)), - opt(preceded(whitespace, result)), - )), - |(name, input_types, output_types)| Adapter::Export { + AdapterKind::Export => Adapter::Export { name, - input_types: input_types.unwrap_or_else(|| vec![]), - output_types: output_types.unwrap_or_else(|| vec![]), + input_types, + output_types, instructions: vec![], }, - )(input) - } - delimited( - char('('), - preceded( - opt(whitespace), - preceded( - tag("@interface"), - preceded( - whitespace, - preceded(tag("adapt"), alt((adapter_import, adapter_export))), - ), - ), - ), - char(')'), - )(input) + _ => unimplemented!("Adapter of kind “helper” is not implemented yet."), + }) + } +} + +impl<'a> Parse<'a> for Forward<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + + let name = parser.parens(|parser| { + parser.parse::()?; + + Ok((parser.parse()?)) + })?; + + Ok(Forward { name }) + } +} + +impl<'a> Parse<'a> for Interfaces<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut interfaces: Interfaces = Default::default(); + + while !parser.is_empty() { + let interface = parser.parse::()?; + + match interface { + Interface::Export(export) => interfaces.exports.push(export), + Interface::Type(ty) => interfaces.types.push(ty), + Interface::Import(import) => interfaces.imports.push(import), + Interface::Adapter(adapter) => interfaces.adapters.push(adapter), + Interface::Forward(forward) => interfaces.forwards.push(forward), + } + } + + Ok(interfaces) + } } #[cfg(test)] mod tests { use super::*; + use wast::parser::{self, ParseBuffer}; - #[test] - fn test_whitespace() { - let inputs = vec![" a", " a", "\n a", "\r\n a"]; - let outputs = vec![" ", " ", "\n ", "\r\n "]; - - for (nth, input) in inputs.iter().enumerate() { - assert_eq!(whitespace::<()>(input), Ok(("a", outputs[nth]))); - } + fn buffer(input: &str) -> ParseBuffer { + ParseBuffer::new(input).expect("Failed to build the parser buffer.") } #[test] fn test_interface_type() { let inputs = vec![ - "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", + "int", "float", "any", "string", "seq", "i32", "i64", "f32", "f64", "anyref", ]; let outputs = vec![ InterfaceType::Int, @@ -299,208 +345,253 @@ mod tests { assert_eq!(inputs.len(), outputs.len()); for (nth, input) in inputs.iter().enumerate() { - assert_eq!(interface_type::<()>(input), Ok(("", outputs[nth]))); + assert_eq!( + parser::parse::(&buffer(input)).unwrap(), + outputs[nth] + ); } } #[test] fn test_param_empty() { - let input = "(param)"; - let output = vec![]; + let input = buffer("(param)"); + let output = FunctionType::Input(vec![]); - assert_eq!(param::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_param() { - let input = "(param i32 String)"; - let output = vec![InterfaceType::I32, InterfaceType::String]; + let input = buffer("(param i32 string)"); + let output = FunctionType::Input(vec![InterfaceType::I32, InterfaceType::String]); - assert_eq!(param::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_result_empty() { - let input = "(result)"; - let output = vec![]; + let input = buffer("(result)"); + let output = FunctionType::Output(vec![]); - assert_eq!(result::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_result() { - let input = "(result i32 String)"; - let output = vec![InterfaceType::I32, InterfaceType::String]; + let input = buffer("(result i32 string)"); + let output = FunctionType::Output(vec![InterfaceType::I32, InterfaceType::String]); - assert_eq!(result::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_export_with_no_param_no_result() { - let input = r#"(@interface export "foo")"#; - let output = Export { - name: "foo", - input_types: vec![], - output_types: vec![], - }; - - assert_eq!(export::<()>(input), Ok(("", output))); + let input = buffer(r#"(@interface export "foo")"#); } #[test] fn test_export_with_some_param_no_result() { - let input = r#"(@interface export "foo" (param i32))"#; - let output = Export { + let input = buffer(r#"(@interface export "foo" (param i32))"#); + let output = Interface::Export(Export { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - }; + }); - assert_eq!(export::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_export_with_no_param_some_result() { - let input = r#"(@interface export "foo" (result i32))"#; - let output = Export { + let input = buffer(r#"(@interface export "foo" (result i32))"#); + let output = Interface::Export(Export { name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], - }; + }); - assert_eq!(export::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_export_with_some_param_some_result() { - let input = r#"(@interface export "foo" (param String) (result i32 i32))"#; - let output = Export { + let input = buffer(r#"(@interface export "foo" (param string) (result i32 i32))"#); + let output = Interface::Export(Export { name: "foo", input_types: vec![InterfaceType::String], output_types: vec![InterfaceType::I32, InterfaceType::I32], - }; + }); - assert_eq!(export::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_export_escaped_name() { - let input = r#"(@interface export "fo\"o")"#; - let output = Export { - name: r#"fo\"o"#, + let input = buffer(r#"(@interface export "fo\"o")"#); + let output = Interface::Export(Export { + name: r#"fo"o"#, input_types: vec![], output_types: vec![], - }; + }); - assert_eq!(export::<()>(input), Ok(("", output))); - } - - #[test] - fn test_import_qualifier() { - let input = r#"(import "ns" "name")"#; - let output = ("ns", "name"); - - assert_eq!(import_qualifier::<()>(input), Ok(("", output))); - } - - #[test] - fn test_export_qualifier() { - let input = r#"(export "name")"#; - let output = "name"; - - assert_eq!(export_qualifier::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_import_with_no_param_no_result() { - let input = r#"(@interface func $ns_foo (import "ns" "foo"))"#; - let output = Import { + let input = buffer(r#"(@interface func $ns_foo (import "ns" "foo"))"#); + let output = Interface::Import(Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![], - }; + }); - assert_eq!(import::<()>(input), Ok(("", output))); - } - - #[test] - fn test_import_with_no_index_variable_no_param_no_result() { - let input = r#"(@interface func (import "ns" "foo"))"#; - let output = Import { - namespace: "ns", - name: "foo", - input_types: vec![], - output_types: vec![], - }; - - assert_eq!(import::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_import_with_some_param_no_result() { - let input = r#"(@interface func $ns_foo (import "ns" "foo") (param i32))"#; - let output = Import { + let input = buffer(r#"(@interface func $ns_foo (import "ns" "foo") (param i32))"#); + let output = Interface::Import(Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - }; + }); - assert_eq!(import::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_import_with_no_param_some_result() { - let input = r#"(@interface func $ns_foo (import "ns" "foo") (result i32))"#; - let output = Import { + let input = buffer(r#"(@interface func $ns_foo (import "ns" "foo") (result i32))"#); + let output = Interface::Import(Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], - }; + }); - assert_eq!(import::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_import_with_some_param_some_result() { - let input = - r#"(@interface func $ns_foo (import "ns" "foo") (param String) (result i32 i32))"#; - let output = Import { + let input = buffer( + r#"(@interface func $ns_foo (import "ns" "foo") (param string) (result i32 i32))"#, + ); + let output = Interface::Import(Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::String], output_types: vec![InterfaceType::I32, InterfaceType::I32], - }; + }); - assert_eq!(import::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_adapter_import() { - let input = r#"(@interface adapt (import "ns" "foo") (param i32 i32) (result i32))"#; - let output = Adapter::Import { + let input = + buffer(r#"(@interface adapt (import "ns" "foo") (param i32 i32) (result i32))"#); + let output = Interface::Adapter(Adapter::Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::I32, InterfaceType::I32], output_types: vec![InterfaceType::I32], instructions: vec![], - }; + }); - assert_eq!(adapter::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] fn test_adapter_export() { - let input = r#"(@interface adapt (export "foo") (param i32 i32) (result i32))"#; - let output = Adapter::Export { + let input = buffer(r#"(@interface adapt (export "foo") (param i32 i32) (result i32))"#); + let output = Interface::Adapter(Adapter::Export { name: "foo", input_types: vec![InterfaceType::I32, InterfaceType::I32], output_types: vec![InterfaceType::I32], instructions: vec![], + }); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + #[test] + fn test_forward() { + let input = buffer(r#"(@interface forward (export "foo"))"#); + let output = Interface::Forward(Forward { name: "foo" }); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_interfaces() { + let input = buffer( + r#"(@interface export "foo" + (param i32)) + +(@interface export "bar") + +(@interface func $ns_foo (import "ns" "foo") + (result i32)) + +(@interface func $ns_bar (import "ns" "bar")) + +(@interface adapt (import "ns" "foo") + (param i32)) + +(@interface adapt (export "bar")) + +(@interface forward (export "main"))"#, + ); + let output = Interfaces { + exports: vec![ + Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }, + Export { + name: "bar", + input_types: vec![], + output_types: vec![], + }, + ], + types: vec![], + imports: vec![ + Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }, + Import { + namespace: "ns", + name: "bar", + input_types: vec![], + output_types: vec![], + }, + ], + adapters: vec![ + Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![], + }, + Adapter::Export { + name: "bar", + input_types: vec![], + output_types: vec![], + instructions: vec![], + }, + ], + forwards: vec![Forward { name: "main" }], }; - assert_eq!(adapter::<()>(input), Ok(("", output))); + assert_eq!(parser::parse::(&input).unwrap(), output); } } From 5e00153d29c290a264053d047c6d28bc83d7b3b0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 20 Feb 2020 15:13:11 +0100 Subject: [PATCH 05/10] feat(interface-types) Decode instructions in WIT's text format. --- lib/interface-types/src/decoders/wat.rs | 219 ++++++++++++++++++++++-- 1 file changed, 204 insertions(+), 15 deletions(-) diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 1c85e3f04..b3f39960a 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -2,10 +2,10 @@ #![allow(unused)] -use crate::ast::*; +use crate::{ast::*, interpreter::Instruction}; use wast::{ parser::{Cursor, Parse, Parser, Peek, Result}, - Id, + Id, LParen, }; mod kw { @@ -14,13 +14,38 @@ mod kw { kw::{anyref, export, f32, f64, func, i32, i64, import, param, result}, }; + // New keywords. custom_keyword!(adapt); custom_keyword!(forward); + + // New types. custom_keyword!(int); custom_keyword!(float); custom_keyword!(any); custom_keyword!(string); custom_keyword!(seq); + + // Instructions. + custom_keyword!(argument_get = "arg.get"); + custom_keyword!(call); + custom_keyword!(call_export = "call-export"); + custom_keyword!(read_utf8 = "read-utf8"); + custom_keyword!(write_utf8 = "write-utf8"); + custom_keyword!(as_wasm = "as-wasm"); + custom_keyword!(as_interface = "as-interface"); + custom_keyword!(table_ref_add = "table-ref-add"); + custom_keyword!(table_ref_get = "table-ref-get"); + custom_keyword!(call_method = "call-method"); + custom_keyword!(make_record = "make-record"); + custom_keyword!(get_field = "get-field"); + custom_keyword!(r#const = "const"); + custom_keyword!(fold_seq = "fold-seq"); + custom_keyword!(add); + custom_keyword!(mem_to_seq = "mem-to-seq"); + custom_keyword!(load); + custom_keyword!(seq_new = "seq.new"); + custom_keyword!(list_push = "list.push"); + custom_keyword!(repeat_until = "repeat-until"); } /// Issue: Uppercased keyword aren't supported for the moment. @@ -74,6 +99,104 @@ impl Parse<'_> for InterfaceType { } } +impl<'a> Parse<'a> for Instruction<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ArgumentGet { + index: parser.parse()?, + }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::Call { + function_index: parser.parse::()? as usize, + }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::CallExport { + export_name: parser.parse()?, + }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ReadUtf8) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::WriteUtf8 { + allocator_name: parser.parse()?, + }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::AsWasm(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::AsInterface(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::TableRefAdd) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::TableRefGet) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::CallMethod(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::MakeRecord(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::GetField(parser.parse()?, parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::Const(parser.parse()?, parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::FoldSeq(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::Add(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::MemToSeq(parser.parse()?, parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::Load(parser.parse()?, parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::SeqNew(parser.parse()?)) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ListPush) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RepeatUntil(parser.parse()?, parser.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + struct AtInterface; impl Peek for AtInterface { @@ -250,13 +373,18 @@ impl<'a> Parse<'a> for Adapter<'a> { })?; let mut input_types = vec![]; let mut output_types = vec![]; + let mut instructions = vec![]; while !parser.is_empty() { - let function_type = parser.parse::()?; + if parser.peek::() { + 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), + match function_type { + FunctionType::Input(mut inputs) => input_types.append(&mut inputs), + FunctionType::Output(mut outputs) => output_types.append(&mut outputs), + } + } else { + instructions.push(parser.parse()?); } } @@ -266,14 +394,14 @@ impl<'a> Parse<'a> for Adapter<'a> { name, input_types, output_types, - instructions: vec![], + instructions, }, AdapterKind::Export => Adapter::Export { name, input_types, output_types, - instructions: vec![], + instructions, }, _ => unimplemented!("Adapter of kind “helper” is not implemented yet."), @@ -344,10 +472,69 @@ mod tests { assert_eq!(inputs.len(), outputs.len()); - for (nth, input) in inputs.iter().enumerate() { + for (input, output) in inputs.iter().zip(outputs.iter()) { assert_eq!( - parser::parse::(&buffer(input)).unwrap(), - outputs[nth] + &parser::parse::(&buffer(input)).unwrap(), + output + ); + } + } + + #[test] + fn test_instructions() { + let inputs = vec![ + "arg.get 7", + "call 7", + r#"call-export "foo""#, + "read-utf8", + r#"write-utf8 "foo""#, + "as-wasm int", + "as-interface anyref", + "table-ref-add", + "table-ref-get", + "call-method 7", + "make-record int", + "get-field int 7", + "const i32 7", + "fold-seq 7", + "add int", + r#"mem-to-seq int "foo""#, + r#"load int "foo""#, + "seq.new int", + "list.push", + "repeat-until 1 2", + ]; + let outputs = vec![ + Instruction::ArgumentGet { index: 7 }, + Instruction::Call { function_index: 7 }, + Instruction::CallExport { export_name: "foo" }, + Instruction::ReadUtf8, + Instruction::WriteUtf8 { + allocator_name: "foo", + }, + Instruction::AsWasm(InterfaceType::Int), + Instruction::AsInterface(InterfaceType::AnyRef), + Instruction::TableRefAdd, + Instruction::TableRefGet, + Instruction::CallMethod(7), + Instruction::MakeRecord(InterfaceType::Int), + Instruction::GetField(InterfaceType::Int, 7), + Instruction::Const(InterfaceType::I32, 7), + Instruction::FoldSeq(7), + Instruction::Add(InterfaceType::Int), + Instruction::MemToSeq(InterfaceType::Int, "foo"), + Instruction::Load(InterfaceType::Int, "foo"), + Instruction::SeqNew(InterfaceType::Int), + Instruction::ListPush, + Instruction::RepeatUntil(1, 2), + ]; + + assert_eq!(inputs.len(), outputs.len()); + + for (input, output) in inputs.iter().zip(outputs.iter()) { + assert_eq!( + &parser::parse::(&buffer(input)).unwrap(), + output ); } } @@ -540,9 +727,11 @@ mod tests { (@interface func $ns_bar (import "ns" "bar")) (@interface adapt (import "ns" "foo") - (param i32)) + (param i32) + arg.get 42) -(@interface adapt (export "bar")) +(@interface adapt (export "bar") + arg.get 42) (@interface forward (export "main"))"#, ); @@ -580,13 +769,13 @@ mod tests { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - instructions: vec![], + instructions: vec![Instruction::ArgumentGet { index: 42 }], }, Adapter::Export { name: "bar", input_types: vec![], output_types: vec![], - instructions: vec![], + instructions: vec![Instruction::ArgumentGet { index: 42 }], }, ], forwards: vec![Forward { name: "main" }], From 2ef13cdc81f62ce6d8222549d52d96ddbadbd2f7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 20 Feb 2020 15:16:35 +0100 Subject: [PATCH 06/10] fix(interface-types) Remove `#[allow(unused)]`. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/wat.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 6bb7e9e91..beb77c1e8 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -5,7 +5,7 @@ use crate::interpreter::Instruction; use std::str; /// Represents the types supported by WIT. -#[derive(PartialEq, Clone, Copy, Debug)] +#[derive(PartialEq, Clone, Debug)] pub enum InterfaceType { /// An integer. Int, diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index b3f39960a..b00025913 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -1,7 +1,5 @@ //! Parse the WIT textual representation into an AST. -#![allow(unused)] - use crate::{ast::*, interpreter::Instruction}; use wast::{ parser::{Cursor, Parse, Parser, Peek, Result}, @@ -262,6 +260,7 @@ impl Parse<'_> for FunctionType { #[derive(PartialEq, Debug)] enum Interface<'a> { Export(Export<'a>), + #[allow(dead_code)] Type(Type<'a>), Import(Import<'a>), Adapter(Adapter<'a>), @@ -416,7 +415,7 @@ impl<'a> Parse<'a> for Forward<'a> { let name = parser.parens(|parser| { parser.parse::()?; - Ok((parser.parse()?)) + Ok(parser.parse()?) })?; Ok(Forward { name }) @@ -574,6 +573,13 @@ mod tests { #[test] fn test_export_with_no_param_no_result() { let input = buffer(r#"(@interface export "foo")"#); + let output = Interface::Export(Export { + name: "foo", + input_types: vec![], + output_types: vec![], + }); + + assert_eq!(parser::parse::(&input).unwrap(), output); } #[test] From 0afd308c44d128f9b71619d1dca0516e7e7da4c1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 21 Feb 2020 12:23:20 +0100 Subject: [PATCH 07/10] feat(interface-types) Add the `parse` public API. Also rename `kw` to `keyword`. --- lib/interface-types/src/decoders/wat.rs | 257 ++++++++++++++++-------- 1 file changed, 173 insertions(+), 84 deletions(-) diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index b00025913..7ef1d5472 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -1,12 +1,13 @@ -//! Parse the WIT textual representation into an AST. +//! Parse the WIT textual representation into an [AST](crate::ast). use crate::{ast::*, interpreter::Instruction}; +pub use wast::parser::ParseBuffer as Buffer; use wast::{ - parser::{Cursor, Parse, Parser, Peek, Result}, + parser::{self, Cursor, Parse, Parser, Peek, Result}, Id, LParen, }; -mod kw { +mod keyword { pub use wast::{ custom_keyword, kw::{anyref, export, f32, f64, func, i32, i64, import, param, result}, @@ -51,44 +52,44 @@ impl Parse<'_> for InterfaceType { fn parse(parser: Parser<'_>) -> Result { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { - parser.parse::()?; + if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::Int) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::Float) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::Any) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::String) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::Seq) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::I32) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::I64) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::F32) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::F64) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(InterfaceType::AnyRef) } else { @@ -101,92 +102,92 @@ impl<'a> Parse<'a> for Instruction<'a> { fn parse(parser: Parser<'a>) -> Result { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { - parser.parse::()?; + if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::ArgumentGet { index: parser.parse()?, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::Call { function_index: parser.parse::()? as usize, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::CallExport { export_name: parser.parse()?, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::ReadUtf8) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::WriteUtf8 { allocator_name: parser.parse()?, }) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::AsWasm(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::AsInterface(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::TableRefAdd) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::TableRefGet) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::CallMethod(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::MakeRecord(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::GetField(parser.parse()?, parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::Const(parser.parse()?, parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::FoldSeq(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::Add(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::MemToSeq(parser.parse()?, parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::Load(parser.parse()?, parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::SeqNew(parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::ListPush) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok(Instruction::RepeatUntil(parser.parse()?, parser.parse()?)) } else { @@ -230,8 +231,8 @@ impl Parse<'_> for FunctionType { parser.parens(|parser| { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { - parser.parse::()?; + if lookahead.peek::() { + parser.parse::()?; let mut inputs = vec![]; @@ -240,8 +241,8 @@ impl Parse<'_> for FunctionType { } Ok(FunctionType::Input(inputs)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; let mut outputs = vec![]; @@ -277,13 +278,13 @@ impl<'a> Parse<'a> for Interface<'a> { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { + if lookahead.peek::() { Ok(Interface::Export(parser.parse()?)) - } else if lookahead.peek::() { + } else if lookahead.peek::() { Ok(Interface::Import(parser.parse()?)) - } else if lookahead.peek::() { + } else if lookahead.peek::() { Ok(Interface::Adapter(parser.parse()?)) - } else if lookahead.peek::() { + } else if lookahead.peek::() { Ok(Interface::Forward(parser.parse()?)) } else { Err(lookahead.error()) @@ -297,7 +298,7 @@ impl<'a> Parse<'a> for Interface<'a> { impl<'a> Parse<'a> for Export<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + parser.parse::()?; let name = parser.parse()?; let mut input_types = vec![]; @@ -322,11 +323,11 @@ impl<'a> Parse<'a> for Export<'a> { impl<'a> Parse<'a> for Import<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + parser.parse::()?; parser.parse::()?; let (namespace, name) = parser.parens(|parser| { - parser.parse::()?; + parser.parse::()?; Ok((parser.parse()?, parser.parse()?)) })?; @@ -353,17 +354,17 @@ impl<'a> Parse<'a> for Import<'a> { impl<'a> Parse<'a> for Adapter<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + parser.parse::()?; let (kind, namespace, name) = parser.parens(|parser| { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { - parser.parse::()?; + if lookahead.peek::() { + parser.parse::()?; Ok((AdapterKind::Import, parser.parse()?, parser.parse()?)) - } else if lookahead.peek::() { - parser.parse::()?; + } else if lookahead.peek::() { + parser.parse::()?; Ok((AdapterKind::Export, "", parser.parse()?)) } else { @@ -410,10 +411,10 @@ impl<'a> Parse<'a> for Adapter<'a> { impl<'a> Parse<'a> for Forward<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + parser.parse::()?; let name = parser.parens(|parser| { - parser.parse::()?; + parser.parse::()?; Ok(parser.parse()?) })?; @@ -442,13 +443,101 @@ impl<'a> Parse<'a> for Interfaces<'a> { } } +/// Parse a WIT definition in its textual format, and produces an +/// [AST](crate::ast) with the [`Interfaces`](crate::ast::Interfaces) +/// structure upon succesful. +/// +/// # Examples +/// +/// ```rust +/// use wasmer_interface_types::{ +/// ast::*, +/// decoders::wat::{parse, Buffer}, +/// interpreter::Instruction, +/// }; +/// +/// # fn main() { +/// let input = Buffer::new( +/// r#"(@interface export "foo" +/// (param i32)) +/// +/// (@interface export "bar") +/// +/// (@interface func $ns_foo (import "ns" "foo") +/// (result i32)) +/// +/// (@interface func $ns_bar (import "ns" "bar")) +/// +/// (@interface adapt (import "ns" "foo") +/// (param i32) +/// arg.get 42) +/// +/// (@interface adapt (export "bar") +/// arg.get 42) +/// +/// (@interface forward (export "main"))"#, +/// ) +/// .unwrap(); +/// let output = Interfaces { +/// exports: vec![ +/// Export { +/// name: "foo", +/// input_types: vec![InterfaceType::I32], +/// output_types: vec![], +/// }, +/// Export { +/// name: "bar", +/// input_types: vec![], +/// output_types: vec![], +/// }, +/// ], +/// types: vec![], +/// imports: vec![ +/// Import { +/// namespace: "ns", +/// name: "foo", +/// input_types: vec![], +/// output_types: vec![InterfaceType::I32], +/// }, +/// Import { +/// namespace: "ns", +/// name: "bar", +/// input_types: vec![], +/// output_types: vec![], +/// }, +/// ], +/// adapters: vec![ +/// Adapter::Import { +/// namespace: "ns", +/// name: "foo", +/// input_types: vec![InterfaceType::I32], +/// output_types: vec![], +/// instructions: vec![Instruction::ArgumentGet { index: 42 }], +/// }, +/// Adapter::Export { +/// name: "bar", +/// input_types: vec![], +/// output_types: vec![], +/// instructions: vec![Instruction::ArgumentGet { index: 42 }], +/// }, +/// ], +/// forwards: vec![Forward { name: "main" }], +/// }; +/// +/// assert_eq!(parse(&input).unwrap(), output); +/// # } +/// ``` +pub fn parse<'input>(input: &'input Buffer) -> Result> { + parser::parse::(&input) +} + #[cfg(test)] mod tests { use super::*; - use wast::parser::{self, ParseBuffer}; + use wast::parser; - fn buffer(input: &str) -> ParseBuffer { - ParseBuffer::new(input).expect("Failed to build the parser buffer.") + fn buffer(input: &str) -> Buffer { + Buffer::new(input).expect("Failed to build the parser buffer.") } #[test] From 80d0b5116f1abb5e909c550e4712d3b645d98542 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 21 Feb 2020 12:23:58 +0100 Subject: [PATCH 08/10] doc(interface-types) Add intra links. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/binary.rs | 5 +-- lib/interface-types/src/decoders/mod.rs | 3 +- lib/interface-types/src/interpreter/mod.rs | 4 +-- lib/interface-types/src/lib.rs | 41 ++++++++++++---------- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index beb77c1e8..d0c8cab8e 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -5,7 +5,7 @@ use crate::interpreter::Instruction; use std::str; /// Represents the types supported by WIT. -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Debug)] pub enum InterfaceType { /// An integer. Int, diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index fbe2e296a..26fa382d6 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,4 +1,4 @@ -//! Parse the WIT binary representation into an AST. +//! Parse the WIT binary representation into an [AST](crate::ast). use crate::{ast::*, interpreter::Instruction}; use nom::{ @@ -408,7 +408,8 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( } /// Parse a sequence of bytes, expecting it to be a valid WIT binary -/// representation, into an `ast::Interfaces`. +/// representation, into an [`Interfaces`](crate::ast::Interfaces) +/// structure. /// /// # Example /// diff --git a/lib/interface-types/src/decoders/mod.rs b/lib/interface-types/src/decoders/mod.rs index 1b2aaaffe..399bb896e 100644 --- a/lib/interface-types/src/decoders/mod.rs +++ b/lib/interface-types/src/decoders/mod.rs @@ -1,5 +1,6 @@ //! Reads the AST from a particular data representation; for instance, -//! `decoders::binary` reads the AST from a binary. +//! [`decoders::binary`](binary) reads the [AST](crate::ast) +//! from a binary. pub mod binary; pub mod wat; diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 792c91e89..e7ef7f1b1 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -43,8 +43,8 @@ pub(crate) type ExecutableInstruction Date: Fri, 21 Feb 2020 12:25:27 +0100 Subject: [PATCH 09/10] chore(cargo) Update `Cargo.lock`. --- Cargo.lock | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index eb69230f1..293766ffe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -665,6 +665,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" + [[package]] name = "lexical-core" version = "0.4.6" @@ -1824,6 +1830,7 @@ name = "wasmer-interface-types" version = "0.14.0" dependencies = [ "nom", + "wast", ] [[package]] @@ -2039,6 +2046,15 @@ version = "0.45.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b4eab1d9971d0803729cba3617b56eb04fcb4bd25361cb63880ed41a42f20d5" +[[package]] +name = "wast" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9df3d716118a503b2f6bbb6ff46b21997ab0cc167b01de7a188e45e4b01e8d" +dependencies = [ + "leb128", +] + [[package]] name = "winapi" version = "0.3.8" From 4b0a1ef6d65a6cfc9c16ce19ac3ea0c702a0f1b8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 21 Feb 2020 12:38:16 +0100 Subject: [PATCH 10/10] doc(changelog) Add #1232. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 544c008b7..551870d6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1232](https://github.com/wasmerio/wasmer/pull/1232) `wasmer-interface-types` has a WAT decoder. + ## 0.14.0 - 2020-02-20 - [#1233](https://github.com/wasmerio/wasmer/pull/1233) Improved Wasmer C API release artifacts.