use std::fmt::Write;
use lutra_bin::ir;
use crate::rust::Context;
use crate::rust::types;
#[rustfmt::skip::macros(writeln)]
#[rustfmt::skip::macros(write)]
pub fn write_functions(
w: &mut impl Write,
functions: &[(&String, ir::TyFunction)],
ctx: &mut Context,
) -> Result<(), std::fmt::Error> {
if functions.is_empty() {
return Ok(());
}
let lutra_bin = &ctx.options.lutra_bin_path;
writeln!(w, "pub trait Functions {{")?;
for (name, func) in functions {
writeln!(w, " fn {name}(")?;
for (param_i, param) in func.params.iter().enumerate() {
write!(w, " arg{param_i}: ")?;
types::write_ty_ref(w, param, false, ctx)?;
writeln!(w, ",")?;
}
let body = &func.body;
write!(w, " ) -> ")?;
types::write_ty_ref(w, body, false, ctx)?;
writeln!(w, ";")?;
}
writeln!(w, "}}\n")?;
writeln!(w, "pub trait NativeFunctions {{")?;
for (name, _func) in functions {
writeln!(w, " fn {name}(")?;
writeln!(w, " interpreter: &mut ::lutra_interpreter::Interpreter,")?;
writeln!(w, " layout_args: &[u32],")?;
writeln!(w, " args: Vec<::lutra_interpreter::Cell>,")?;
writeln!(w, " ) -> Result<::lutra_interpreter::Cell, ::lutra_interpreter::EvalError>;")?;
}
writeln!(w, "}}\n")?;
writeln!(w, "impl <T: Functions> NativeFunctions for T {{")?;
for (name, func) in functions {
let args = if func.params.is_empty() {
"_args"
} else {
"args"
};
writeln!(w, " fn {name}(")?;
writeln!(w, " _interpreter: &mut ::lutra_interpreter::Interpreter,")?;
writeln!(w, " _layout_args: &[u32],")?;
writeln!(w, " {args}: Vec<::lutra_interpreter::Cell>,")?;
writeln!(w, " ) -> Result<::lutra_interpreter::Cell, ::lutra_interpreter::EvalError> {{")?;
if !func.params.is_empty() {
writeln!(w, " use {lutra_bin}::Decode;", )?;
writeln!(w, " let mut args = args.into_iter();")?;
writeln!(w)?;
for (param_i, param) in func.params.iter().enumerate() {
writeln!(w, " let arg{param_i} = args.next().unwrap();")?;
writeln!(w, " let arg{param_i} = arg{param_i}.into_data().unwrap_or_else(|_| panic!());")?;
write!(w, " let arg{param_i} = ")?;
types::write_ty_ref(w, param, true, ctx)?;
writeln!(w, "::decode(&arg{param_i}.flatten()).unwrap();")?;
writeln!(w)?;
}
} else {
writeln!(w, " use {lutra_bin}::Encode;", )?;
}
writeln!(w, " let res = <T as Functions>::{name}(")?;
for (param_i, _param) in func.params.iter().enumerate() {
writeln!(w, " arg{param_i},")?;
}
writeln!(w, " );")?;
writeln!(w, " let buf = {lutra_bin}::Encode::encode(&res);")?;
writeln!(w, " Ok(::lutra_interpreter::Cell::Data(::lutra_interpreter::Data::new(buf.to_vec())))")?;
writeln!(w, " }}")?;
}
writeln!(w, "}}\n")?;
writeln!(w, "pub struct Wrapper<T>(pub T);\n")?;
writeln!(w, "impl <T: NativeFunctions + 'static + Sync> ::lutra_interpreter::NativeModule for Wrapper<T> {{")?;
writeln!(w, " fn lookup_native_symbol(&self, id: &str) -> Option<::lutra_interpreter::NativeFunction> {{")?;
writeln!(w, " match id {{")?;
for (name, _func) in functions {
writeln!(w, " \"{name}\" => Some(&T::{name}),")?;
}
writeln!(w, " _ => None,")?;
writeln!(w, " }}")?;
writeln!(w, " }}")?;
writeln!(w, "}}\n")?;
Ok(())
}