feat(interface-types) Continue the WAT encoder.

This commit is contained in:
Ivan Enderlin 2019-09-18 16:37:57 +02:00
parent 40613d3d48
commit 6279b3e915
2 changed files with 328 additions and 75 deletions

View File

@ -1,6 +1,6 @@
use std::str; use std::str;
#[derive(PartialEq, Clone, Copy, Debug)] #[derive(PartialEq, Debug)]
pub enum InterfaceType { pub enum InterfaceType {
Int, Int,
Float, Float,

View File

@ -1,7 +1,7 @@
use crate::ast::{Export, Instruction, InterfaceType}; use crate::ast::{Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Type};
impl From<InterfaceType> for String { impl From<&InterfaceType> for String {
fn from(interface_type: InterfaceType) -> Self { fn from(interface_type: &InterfaceType) -> Self {
match interface_type { match interface_type {
InterfaceType::Int => "Int".into(), InterfaceType::Int => "Int".into(),
InterfaceType::Float => "Float".into(), InterfaceType::Float => "Float".into(),
@ -17,14 +17,14 @@ impl From<InterfaceType> for String {
} }
} }
impl<'input> From<Instruction<'input>> for String { impl<'input> From<&Instruction<'input>> for String {
fn from(instruction: Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
match instruction { match instruction {
Instruction::ArgumentGet(index) => format!("arg.get {}", index), Instruction::ArgumentGet(index) => format!("arg.get {}", index),
Instruction::Call(index) => format!("call {}", 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::ReadUtf8 => "read-utf8".into(),
Instruction::WriteUtf8(string) => format!("write-utf8 \"{}\"", string), Instruction::WriteUtf8(string) => format!(r#"write-utf8 "{}""#, string),
Instruction::AsWasm(interface_type) => { Instruction::AsWasm(interface_type) => {
format!("as-wasm {}", String::from(interface_type)) format!("as-wasm {}", String::from(interface_type))
} }
@ -48,41 +48,124 @@ impl<'input> From<Instruction<'input>> for String {
} }
} }
impl<'input> From<Export<'input>> for String { fn input_types_to_param(input_types: &Vec<InterfaceType>) -> String {
fn from(export: Export) -> Self { if input_types.is_empty() {
"".into()
} else {
format!( format!(
"(@interface export \"{}\"{}{})", "\n (param{})",
export.name, input_types
if export.input_types.is_empty() { .iter()
"".into() .fold(String::new(), |mut accumulator, interface_type| {
} else { accumulator.push(' ');
format!( accumulator.push_str(&String::from(interface_type));
" (param{})", accumulator
export.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<InterfaceType>) -> String {
} if output_types.is_empty() {
) "".into()
) } else {
}, format!(
if export.output_types.is_empty() { "\n (result{})",
"".into() output_types
} else { .iter()
format!( .fold(String::new(), |mut accumulator, interface_type| {
" (result{})", accumulator.push(' ');
export.output_types.iter().fold( accumulator.push_str(&String::from(interface_type));
String::new(), accumulator
|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] #[test]
fn test_interface_types() { fn test_interface_types() {
let inputs: Vec<String> = vec![ let inputs: Vec<String> = vec![
InterfaceType::Int.into(), (&InterfaceType::Int).into(),
InterfaceType::Float.into(), (&InterfaceType::Float).into(),
InterfaceType::Any.into(), (&InterfaceType::Any).into(),
InterfaceType::String.into(), (&InterfaceType::String).into(),
InterfaceType::Seq.into(), (&InterfaceType::Seq).into(),
InterfaceType::I32.into(), (&InterfaceType::I32).into(),
InterfaceType::I64.into(), (&InterfaceType::I64).into(),
InterfaceType::F32.into(), (&InterfaceType::F32).into(),
InterfaceType::F64.into(), (&InterfaceType::F64).into(),
InterfaceType::AnyRef.into(), (&InterfaceType::AnyRef).into(),
]; ];
let outputs = vec![ let outputs = vec![
"Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref",
@ -115,27 +198,27 @@ mod tests {
#[test] #[test]
fn test_instructions() { fn test_instructions() {
let inputs: Vec<String> = vec![ let inputs: Vec<String> = vec![
Instruction::ArgumentGet(7).into(), (&Instruction::ArgumentGet(7)).into(),
Instruction::Call(7).into(), (&Instruction::Call(7)).into(),
Instruction::CallExport("foo").into(), (&Instruction::CallExport("foo")).into(),
Instruction::ReadUtf8.into(), (&Instruction::ReadUtf8).into(),
Instruction::WriteUtf8("foo").into(), (&Instruction::WriteUtf8("foo")).into(),
Instruction::AsWasm(InterfaceType::Int).into(), (&Instruction::AsWasm(InterfaceType::Int)).into(),
Instruction::AsInterface(InterfaceType::AnyRef).into(), (&Instruction::AsInterface(InterfaceType::AnyRef)).into(),
Instruction::TableRefAdd.into(), (&Instruction::TableRefAdd).into(),
Instruction::TableRefGet.into(), (&Instruction::TableRefGet).into(),
Instruction::CallMethod(7).into(), (&Instruction::CallMethod(7)).into(),
Instruction::MakeRecord(InterfaceType::Int).into(), (&Instruction::MakeRecord(InterfaceType::Int)).into(),
Instruction::GetField(InterfaceType::Int, 7).into(), (&Instruction::GetField(InterfaceType::Int, 7)).into(),
Instruction::Const(InterfaceType::I32, 7).into(), (&Instruction::Const(InterfaceType::I32, 7)).into(),
Instruction::FoldSeq(7).into(), (&Instruction::FoldSeq(7)).into(),
]; ];
let outputs = vec![ let outputs = vec![
"arg.get 7", "arg.get 7",
"call 7", "call 7",
"call-export \"foo\"", r#"call-export "foo""#,
"read-utf8", "read-utf8",
"write-utf8 \"foo\"", r#"write-utf8 "foo""#,
"as-wasm Int", "as-wasm Int",
"as-interface anyref", "as-interface anyref",
"table-ref-add", "table-ref-add",
@ -152,14 +235,184 @@ mod tests {
#[test] #[test]
fn test_exports() { fn test_exports() {
let inputs: Vec<String> = vec![Export { let inputs: Vec<String> = vec![
name: "foo", (&Export {
input_types: vec![InterfaceType::I32, InterfaceType::F32], name: "foo",
output_types: vec![InterfaceType::I32], input_types: vec![InterfaceType::I32, InterfaceType::F32],
} output_types: vec![InterfaceType::I32],
.into()]; })
let outputs = vec!["(@interface export \"foo\" (param i32 f32) (result 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); assert_eq!(inputs, outputs);
} }
#[test]
fn test_imported_functions() {
let inputs: Vec<String> = 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<String> = 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);
}
} }