606 lines
20 KiB
Rust
Raw Normal View History

use crate::{
ast::{Adapter, Export, Forward, ImportedFunction, InterfaceType, Interfaces, Type},
instructions::Instruction,
};
impl From<&InterfaceType> for String {
fn from(interface_type: &InterfaceType) -> Self {
match interface_type {
InterfaceType::Int => "Int".into(),
InterfaceType::Float => "Float".into(),
InterfaceType::Any => "Any".into(),
InterfaceType::String => "String".into(),
InterfaceType::Seq => "Seq".into(),
InterfaceType::I32 => "i32".into(),
InterfaceType::I64 => "i64".into(),
InterfaceType::F32 => "f32".into(),
InterfaceType::F64 => "f64".into(),
InterfaceType::AnyRef => "anyref".into(),
}
}
}
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!(r#"call-export "{}""#, export_name),
Instruction::ReadUtf8 => "read-utf8".into(),
Instruction::WriteUtf8(string) => format!(r#"write-utf8 "{}""#, string),
Instruction::AsWasm(interface_type) => {
format!("as-wasm {}", String::from(interface_type))
}
Instruction::AsInterface(interface_type) => {
format!("as-interface {}", String::from(interface_type))
}
Instruction::TableRefAdd => "table-ref-add".into(),
Instruction::TableRefGet => "table-ref-get".into(),
Instruction::CallMethod(index) => format!("call-method {}", index),
Instruction::MakeRecord(interface_type) => {
format!("make-record {}", String::from(interface_type))
}
Instruction::GetField(interface_type, field_index) => {
format!("get-field {} {}", String::from(interface_type), field_index)
}
Instruction::Const(interface_type, value) => {
format!("const {} {}", String::from(interface_type), value)
}
Instruction::FoldSeq(import_index) => format!("fold-seq {}", import_index),
Instruction::Add(interface_type) => format!("add {}", String::from(interface_type)),
Instruction::MemToSeq(interface_type, memory) => format!(
r#"mem-to-seq {} "{}""#,
String::from(interface_type),
memory
),
Instruction::Load(interface_type, memory) => {
format!(r#"load {} "{}""#, String::from(interface_type), memory)
}
Instruction::SeqNew(interface_type) => {
format!("seq.new {}", String::from(interface_type))
}
Instruction::ListPush => "list.push".into(),
Instruction::RepeatWhile(condition_index, step_index) => {
format!("repeat-while {} {}", condition_index, step_index)
}
}
}
}
fn input_types_to_param(input_types: &Vec<InterfaceType>) -> String {
if input_types.is_empty() {
"".into()
} else {
format!(
"\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<InterfaceType>) -> 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,
)
}
}
impl<'input> From<&Interfaces<'input>> for String {
fn from(interfaces: &Interfaces) -> Self {
let mut output = String::from(";; Interfaces");
let exports = interfaces
.exports
.iter()
.fold(String::new(), |mut accumulator, export| {
accumulator.push_str(&format!("\n\n;; Interface, Export {}\n", export.name));
accumulator.push_str(&String::from(export));
accumulator
});
let types = interfaces
.types
.iter()
.fold(String::new(), |mut accumulator, ty| {
accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name));
accumulator.push_str(&String::from(ty));
accumulator
});
let imported_functions = interfaces.imported_functions.iter().fold(
String::new(),
|mut accumulator, imported_function| {
accumulator.push_str(&format!(
"\n\n;; Interface, Imported function {}.{}\n",
imported_function.namespace, imported_function.name
));
accumulator.push_str(&String::from(imported_function));
accumulator
},
);
let adapters =
interfaces
.adapters
.iter()
.fold(String::new(), |mut accumulator, adapter| {
match adapter {
Adapter::Import {
namespace, name, ..
} => accumulator.push_str(&format!(
"\n\n;; Interface, Adapter {}.{}\n",
namespace, name
)),
Adapter::Export { name, .. } => {
accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name))
}
_ => unimplemented!(),
}
accumulator.push_str(&String::from(adapter));
accumulator
});
let forwards =
interfaces
.forwards
.iter()
.fold(String::new(), |mut accumulator, forward| {
accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name));
accumulator.push_str(&String::from(forward));
accumulator
});
output.push_str(&exports);
output.push_str(&types);
output.push_str(&imported_functions);
output.push_str(&adapters);
output.push_str(&forwards);
output
}
}
#[cfg(test)]
mod tests {
use crate::{ast::*, instructions::Instruction};
#[test]
fn test_interface_types() {
let inputs: Vec<String> = 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(),
];
let outputs = vec![
"Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref",
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_instructions() {
let inputs: Vec<String> = 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::Add(InterfaceType::Int)).into(),
(&Instruction::MemToSeq(InterfaceType::Int, "foo")).into(),
(&Instruction::Load(InterfaceType::Int, "foo")).into(),
(&Instruction::SeqNew(InterfaceType::Int)).into(),
(&Instruction::ListPush).into(),
(&Instruction::RepeatWhile(1, 2)).into(),
];
let outputs = 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-while 1 2",
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_exports() {
let inputs: Vec<String> = 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<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);
}
#[test]
fn test_interfaces() {
let input: String = (&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![],
imported_functions: vec![
ImportedFunction {
namespace: "ns",
name: "foo",
input_types: vec![],
output_types: vec![InterfaceType::I32],
},
ImportedFunction {
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(42)],
},
Adapter::Export {
name: "bar",
input_types: vec![],
output_types: vec![],
instructions: vec![Instruction::ArgumentGet(42)],
},
],
forwards: vec![Forward { name: "main" }],
})
.into();
let output = r#";; Interfaces
;; Interface, Export foo
(@interface export "foo"
(param i32))
;; Interface, Export bar
(@interface export "bar")
;; Interface, Imported function ns.foo
(@interface func $ns_foo (import "ns" "foo")
(result i32))
;; Interface, Imported function ns.bar
(@interface func $ns_bar (import "ns" "bar"))
;; Interface, Adapter ns.foo
(@interface adapt (import "ns" "foo")
(param i32)
arg.get 42)
;; Interface, Adapter bar
(@interface adapt (export "bar")
arg.get 42)
;; Interface, Forward main
(@interface forward (export "main"))"#;
assert_eq!(input, output);
}
}