use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{Interpreter, DynamicFnInfo};
use mumu::parser::types::{Value, FunctionValue};
use mumu::parser::interpreter::apply_n_ary_function_value;
pub fn register_flow_compose(interp: &mut Interpreter) {
let f = Arc::new(Mutex::new(flow_compose_bridge_fn));
let info = DynamicFnInfo::new(f, true);
interp.register_dynamic_function_ex("flow:compose", info);
interp.set_variable(
"flow:compose",
Value::Function(Box::new(FunctionValue::Named("flow:compose".to_string())))
);
}
fn flow_compose_bridge_fn(_intp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() {
Ok(make_compose_multi_partial(vec![]))
} else {
let mut placeholders_found = false;
let mut all_fns = true;
for v in &args {
if is_placeholder(v) {
placeholders_found = true;
} else if !matches!(v, Value::Function(_)) {
all_fns = false;
}
}
if placeholders_found || !all_fns {
Ok(make_compose_multi_partial(args))
} else {
Ok(build_final_composition(args))
}
}
}
fn make_compose_multi_partial(existing_args: Vec<Value>) -> Value {
use mumu::parser::types::FunctionValue::RustClosure;
let st = ComposeMultiPartialState { arg_list: existing_args };
Value::Function(Box::new(RustClosure(
"flow:compose-multi-partial".to_string(),
Arc::new(Mutex::new(move |_intp: &mut Interpreter, new_args: Vec<Value>| {
let mut combined = st.arg_list.clone();
for na in &new_args {
combined.push(na.clone());
}
let mut placeholders_found = false;
let mut all_fns = true;
for v in &combined {
if is_placeholder(v) {
placeholders_found = true;
} else if !matches!(v, Value::Function(_)) {
all_fns = false;
}
}
if placeholders_found || !all_fns {
Ok(make_compose_multi_partial(combined))
} else {
Ok(build_final_composition(combined))
}
})),
0
)))
}
fn build_final_composition(fn_args: Vec<Value>) -> Value {
let chain_env = Arc::new(ComposeMultiEnv { chain: fn_args });
make_multi_final(chain_env)
}
fn make_multi_final(chain_env: Arc<ComposeMultiEnv>) -> Value {
use mumu::parser::types::FunctionValue::RustClosure;
let clos = RustClosure(
"flow:compose-final-nary".to_string(),
Arc::new(Mutex::new(move |_intp: &mut Interpreter, incoming: Vec<Value>| {
if incoming.is_empty() {
return Ok(make_multi_final(Arc::clone(&chain_env)));
}
if incoming.len() > 1 {
return Err(format!("flow:compose => final => got {} args, expected 0 or 1", incoming.len()));
}
let data_val = incoming[0].clone();
let mut cur_val = data_val;
for funcv in chain_env.chain.iter().rev() {
if let Value::Function(fb) = funcv {
cur_val = apply_n_ary_function_value(_intp, fb.clone(), vec![cur_val])?;
} else {
return Err(format!("flow:compose => in final chain, got non-function: {:?}", funcv));
}
}
Ok(cur_val)
})),
0
);
Value::Function(Box::new(clos))
}
#[derive(Clone)]
struct ComposeMultiEnv {
chain: Vec<Value>,
}
#[derive(Clone)]
struct ComposeMultiPartialState {
arg_list: Vec<Value>,
}
fn is_placeholder(v: &Value) -> bool {
match v {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
_ => false,
}
}