diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index b966f010e..28c8682b1 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,6 +1,6 @@ use std::str; -#[derive(PartialEq, Clone, Copy, Debug)] +#[derive(PartialEq, Debug)] pub enum InterfaceType { Int, Float, diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 560489f40..eafca5713 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,7 +1,7 @@ -use crate::ast::{Export, Instruction, InterfaceType}; +use crate::ast::{Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Type}; -impl From for String { - fn from(interface_type: InterfaceType) -> Self { +impl From<&InterfaceType> for String { + fn from(interface_type: &InterfaceType) -> Self { match interface_type { InterfaceType::Int => "Int".into(), InterfaceType::Float => "Float".into(), @@ -17,14 +17,14 @@ impl From for String { } } -impl<'input> From> for String { - fn from(instruction: Instruction) -> Self { +impl<'input> From<&Instruction<'input>> for String { + fn from(instruction: &Instruction) -> Self { match instruction { Instruction::ArgumentGet(index) => format!("arg.get {}", index), Instruction::Call(index) => format!("call {}", index), - Instruction::CallExport(export_name) => format!("call-export \"{}\"", export_name), + Instruction::CallExport(export_name) => format!(r#"call-export "{}""#, export_name), Instruction::ReadUtf8 => "read-utf8".into(), - Instruction::WriteUtf8(string) => format!("write-utf8 \"{}\"", string), + Instruction::WriteUtf8(string) => format!(r#"write-utf8 "{}""#, string), Instruction::AsWasm(interface_type) => { format!("as-wasm {}", String::from(interface_type)) } @@ -48,41 +48,124 @@ impl<'input> From> for String { } } -impl<'input> From> for String { - fn from(export: Export) -> Self { +fn input_types_to_param(input_types: &Vec) -> String { + if input_types.is_empty() { + "".into() + } else { format!( - "(@interface export \"{}\"{}{})", - export.name, - if export.input_types.is_empty() { - "".into() - } else { - format!( - " (param{})", - export.input_types.iter().fold( - String::new(), - |mut accumulator, interface_type| { - accumulator.push(' '); - accumulator.push_str(&String::from(*interface_type)); - accumulator - } - ) - ) - }, - if export.output_types.is_empty() { - "".into() - } else { - format!( - " (result{})", - export.output_types.iter().fold( - String::new(), - |mut accumulator, interface_type| { - accumulator.push(' '); - accumulator.push_str(&String::from(*interface_type)); - accumulator - } - ) - ) - }, + "\n (param{})", + input_types + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(interface_type)); + accumulator + }) + ) + } +} + +fn output_types_to_result(output_types: &Vec) -> String { + if output_types.is_empty() { + "".into() + } else { + format!( + "\n (result{})", + output_types + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(interface_type)); + accumulator + }) + ) + } +} + +impl<'input> From<&Export<'input>> for String { + fn from(export: &Export) -> Self { + format!( + r#"(@interface export "{name}"{inputs}{outputs})"#, + name = export.name, + inputs = input_types_to_param(&export.input_types), + outputs = output_types_to_result(&export.output_types), + ) + } +} + +impl<'input> From<&Type<'input>> for String { + fn from(_ty: &Type) -> Self { + unimplemented!() + } +} + +impl<'input> From<&ImportedFunction<'input>> for String { + fn from(imported_function: &ImportedFunction) -> Self { + format!( + r#"(@interface func ${namespace}_{name} (import "{namespace}" "{name}"){inputs}{outputs})"#, + namespace = imported_function.namespace, + name = imported_function.name, + inputs = input_types_to_param(&imported_function.input_types), + outputs = output_types_to_result(&imported_function.output_types), + ) + } +} + +impl<'input> From<&Adapter<'input>> for String { + fn from(adapter: &Adapter) -> Self { + match adapter { + Adapter::Import { + namespace, + name, + input_types, + output_types, + instructions, + } => format!( + r#"(@interface adapt (import "{namespace}" "{name}"){inputs}{outputs}{instructions})"#, + namespace = namespace, + name = name, + inputs = input_types_to_param(&input_types), + outputs = output_types_to_result(&output_types), + instructions = instructions.iter().fold( + String::new(), + |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + } + ), + ), + + Adapter::Export { + name, + input_types, + output_types, + instructions, + } => format!( + r#"(@interface adapt (export "{name}"){inputs}{outputs}{instructions})"#, + name = name, + inputs = input_types_to_param(&input_types), + outputs = output_types_to_result(&output_types), + instructions = instructions.iter().fold( + String::new(), + |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + } + ), + ), + + _ => unimplemented!(), + } + } +} + +impl<'input> From<&Forward<'input>> for String { + fn from(forward: &Forward) -> Self { + format!( + r#"(@interface forward (export "{name}"))"#, + name = forward.name, ) } } @@ -94,16 +177,16 @@ mod tests { #[test] fn test_interface_types() { let inputs: Vec = vec![ - InterfaceType::Int.into(), - InterfaceType::Float.into(), - InterfaceType::Any.into(), - InterfaceType::String.into(), - InterfaceType::Seq.into(), - InterfaceType::I32.into(), - InterfaceType::I64.into(), - InterfaceType::F32.into(), - InterfaceType::F64.into(), - InterfaceType::AnyRef.into(), + (&InterfaceType::Int).into(), + (&InterfaceType::Float).into(), + (&InterfaceType::Any).into(), + (&InterfaceType::String).into(), + (&InterfaceType::Seq).into(), + (&InterfaceType::I32).into(), + (&InterfaceType::I64).into(), + (&InterfaceType::F32).into(), + (&InterfaceType::F64).into(), + (&InterfaceType::AnyRef).into(), ]; let outputs = vec![ "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", @@ -115,27 +198,27 @@ mod tests { #[test] fn test_instructions() { let inputs: Vec = vec![ - Instruction::ArgumentGet(7).into(), - Instruction::Call(7).into(), - Instruction::CallExport("foo").into(), - Instruction::ReadUtf8.into(), - Instruction::WriteUtf8("foo").into(), - Instruction::AsWasm(InterfaceType::Int).into(), - Instruction::AsInterface(InterfaceType::AnyRef).into(), - Instruction::TableRefAdd.into(), - Instruction::TableRefGet.into(), - Instruction::CallMethod(7).into(), - Instruction::MakeRecord(InterfaceType::Int).into(), - Instruction::GetField(InterfaceType::Int, 7).into(), - Instruction::Const(InterfaceType::I32, 7).into(), - Instruction::FoldSeq(7).into(), + (&Instruction::ArgumentGet(7)).into(), + (&Instruction::Call(7)).into(), + (&Instruction::CallExport("foo")).into(), + (&Instruction::ReadUtf8).into(), + (&Instruction::WriteUtf8("foo")).into(), + (&Instruction::AsWasm(InterfaceType::Int)).into(), + (&Instruction::AsInterface(InterfaceType::AnyRef)).into(), + (&Instruction::TableRefAdd).into(), + (&Instruction::TableRefGet).into(), + (&Instruction::CallMethod(7)).into(), + (&Instruction::MakeRecord(InterfaceType::Int)).into(), + (&Instruction::GetField(InterfaceType::Int, 7)).into(), + (&Instruction::Const(InterfaceType::I32, 7)).into(), + (&Instruction::FoldSeq(7)).into(), ]; let outputs = vec![ "arg.get 7", "call 7", - "call-export \"foo\"", + r#"call-export "foo""#, "read-utf8", - "write-utf8 \"foo\"", + r#"write-utf8 "foo""#, "as-wasm Int", "as-interface anyref", "table-ref-add", @@ -152,14 +235,184 @@ mod tests { #[test] fn test_exports() { - let inputs: Vec = vec![Export { - name: "foo", - input_types: vec![InterfaceType::I32, InterfaceType::F32], - output_types: vec![InterfaceType::I32], - } - .into()]; - let outputs = vec!["(@interface export \"foo\" (param i32 f32) (result i32))"]; + let inputs: Vec = vec![ + (&Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![], + output_types: vec![], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface export "foo" + (param i32 f32) + (result i32))"#, + r#"(@interface export "foo" + (param i32))"#, + r#"(@interface export "foo" + (result i32))"#, + r#"(@interface export "foo")"#, + ]; assert_eq!(inputs, outputs); } + + #[test] + fn test_imported_functions() { + let inputs: Vec = vec![ + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::Int, InterfaceType::String], + output_types: vec![InterfaceType::String], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::String], + output_types: vec![], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::String], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface func $ns_foo (import "ns" "foo") + (param Int String) + (result String))"#, + r#"(@interface func $ns_foo (import "ns" "foo") + (param String))"#, + r#"(@interface func $ns_foo (import "ns" "foo") + (result String))"#, + r#"(@interface func $ns_foo (import "ns" "foo"))"#, + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_adapters() { + let inputs: Vec = vec![ + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + instructions: vec![ + Instruction::ArgumentGet(0), + Instruction::WriteUtf8("hello"), + Instruction::CallExport("f"), + ], + }) + .into(), + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + instructions: vec![ + Instruction::ArgumentGet(0), + Instruction::WriteUtf8("hello"), + Instruction::CallExport("f"), + ], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface adapt (import "ns" "foo") + (param i32 f32) + (result i32) + arg.get 0 + write-utf8 "hello" + call-export "f")"#, + r#"(@interface adapt (import "ns" "foo") + (param i32) + call-export "f")"#, + r#"(@interface adapt (import "ns" "foo") + (result i32) + call-export "f")"#, + r#"(@interface adapt (export "foo") + (param i32 f32) + (result i32) + arg.get 0 + write-utf8 "hello" + call-export "f")"#, + r#"(@interface adapt (export "foo") + (param i32) + call-export "f")"#, + r#"(@interface adapt (export "foo") + (result i32) + call-export "f")"#, + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_forward() { + let input: String = (&Forward { name: "main" }).into(); + let output = r#"(@interface forward (export "main"))"#; + + assert_eq!(input, output); + } }