From d07542fbb49ebd7ad57c1bf6ec2cabfb402e22ba Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 19 Feb 2020 17:20:18 +0100 Subject: [PATCH] feat(interface-types) Parse `Import` in the WAT decoders. --- src/decoders/wat.rs | 198 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 23 deletions(-) diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index 581f4d5..bf57501 100644 --- a/src/decoders/wat.rs +++ b/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))); + } }