diff --git a/CHANGELOG.md b/CHANGELOG.md index 544c008b7ac..551870d6dd7 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. diff --git a/Cargo.lock b/Cargo.lock index eb69230f1e6..293766ffed1 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" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 4dc01538c25..7ab8ef1cabd 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] 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 d8b82f588e7..d0c8cab8ee7 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, @@ -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/binary.rs b/lib/interface-types/src/decoders/binary.rs index e77238e7f2d..6beafc681f2 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::{ @@ -432,7 +432,8 @@ fn interfaces<'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 0ac9d881dd2..399bb896e21 100644 --- a/lib/interface-types/src/decoders/mod.rs +++ b/lib/interface-types/src/decoders/mod.rs @@ -1,4 +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/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs new file mode 100644 index 00000000000..7ef1d547250 --- /dev/null +++ b/lib/interface-types/src/decoders/wat.rs @@ -0,0 +1,881 @@ +//! 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::{self, Cursor, Parse, Parser, Peek, Result}, + Id, LParen, +}; + +mod keyword { + pub use wast::{ + custom_keyword, + 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. +impl Parse<'_> for InterfaceType { + fn parse(parser: Parser<'_>) -> Result { + let mut lookahead = parser.lookahead1(); + + 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()) + } + } +} + +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 { + fn peek(cursor: Cursor<'_>) -> bool { + cursor.reserved().map(|(string, _)| string) == Some("@interface") + } + + fn display() -> &'static str { + "`@interface`" + } +} + +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`")) + }) + } +} + +#[derive(PartialEq, Debug)] +enum FunctionType { + Input(Vec), + Output(Vec), +} + +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>), + #[allow(dead_code)] + 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, + output_types, + }) + } +} + +impl<'a> Parse<'a> for Import<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + parser.parse::()?; + + let (namespace, name) = parser.parens(|parser| { + parser.parse::()?; + + Ok((parser.parse()?, 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(Import { + namespace, + name, + input_types, + output_types, + }) + } +} + +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![]; + let mut instructions = vec![]; + + while !parser.is_empty() { + 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), + } + } else { + instructions.push(parser.parse()?); + } + } + + Ok(match kind { + AdapterKind::Import => Adapter::Import { + namespace, + name, + input_types, + output_types, + instructions, + }, + + AdapterKind::Export => Adapter::Export { + name, + input_types, + output_types, + instructions, + }, + + _ => 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) + } +} + +/// 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; + + fn buffer(input: &str) -> Buffer { + Buffer::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", + ]; + 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 (input, output) in inputs.iter().zip(outputs.iter()) { + assert_eq!( + &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 + ); + } + } + + #[test] + fn test_param_empty() { + let input = buffer("(param)"); + let output = FunctionType::Input(vec![]); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_param() { + let input = buffer("(param i32 string)"); + let output = FunctionType::Input(vec![InterfaceType::I32, InterfaceType::String]); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_result_empty() { + let input = buffer("(result)"); + let output = FunctionType::Output(vec![]); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_result() { + let input = buffer("(result i32 string)"); + let output = FunctionType::Output(vec![InterfaceType::I32, InterfaceType::String]); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + + #[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] + fn test_export_with_some_param_no_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_export_with_no_param_some_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_export_with_some_param_some_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_export_escaped_name() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_import_with_no_param_no_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_import_with_some_param_no_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_import_with_no_param_some_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_import_with_some_param_some_result() { + 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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_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!(parser::parse::(&input).unwrap(), output); + } + + #[test] + fn test_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) + arg.get 42) + +(@interface adapt (export "bar") + arg.get 42) + +(@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![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!(parser::parse::(&input).unwrap(), output); + } +} diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 792c91e8973..e7ef7f1b150 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