use mumu::parser::interpreter::apply_n_ary_function_value;
use mumu::parser::types::Value;
use mumu::parser::interpreter::Interpreter;
use std::sync::{Arc, Mutex};
pub fn array_apply_bridge(
interp: &mut Interpreter,
mut args: Vec<Value>
) -> Result<Value, String> {
match args.len() {
0 => {
Ok(make_apply_partial(None, None))
}
1 => {
let first = args.remove(0);
match first {
Value::Placeholder => {
Ok(make_apply_partial(None, None))
}
Value::SingleString(s) if s == "_" => {
Ok(make_apply_partial(None, None))
}
Value::StrArray(ref arr) if arr.len() == 1 && arr[0] == "_" => {
Ok(make_apply_partial(None, None))
}
other => {
Ok(make_apply_partial(Some(other), None))
}
}
}
2 => {
let a1 = args.remove(0);
let a2 = args.remove(0);
let first_is_placeholder = match &a1 {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len()==1 && ss[0] == "_" => true,
_ => false,
};
let second_is_placeholder = match &a2 {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len()==1 && ss[0] == "_" => true,
_ => false,
};
if first_is_placeholder || second_is_placeholder {
let first_opt = if first_is_placeholder { None } else { Some(a1) };
let second_opt = if second_is_placeholder { None } else { Some(a2) };
Ok(make_apply_partial(first_opt, second_opt))
} else {
do_apply(interp, a1, a2)
}
}
n => Err(format!("array:apply => expected up to 2 arguments, got {}", n)),
}
}
fn make_apply_partial(fn_opt: Option<Value>, arr_opt: Option<Value>) -> Value {
use mumu::parser::types::FunctionValue::RustClosure;
let closure = move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut current_fn = fn_opt.clone();
let mut current_arr = arr_opt.clone();
for arg in new_args {
if current_fn.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
current_fn = Some(arg);
}
continue;
}
if current_arr.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
current_arr = Some(arg);
}
continue;
}
return Err("array:apply => partial => too many arguments".to_string());
}
if current_fn.is_some() && current_arr.is_some() {
do_apply(interp, current_fn.unwrap(), current_arr.unwrap())
} else {
Ok(make_apply_partial(current_fn, current_arr))
}
};
Value::Function(Box::new(RustClosure(
"array:apply-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
fn do_apply(interp: &mut Interpreter, fn_val: Value, arr_val: Value) -> Result<Value, String> {
let func_box = match fn_val {
Value::Function(fb) => fb,
other => {
return Err(format!("array:apply => first argument must be Function(...), got {:?}", other));
}
};
let elements = match arr_val {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::StrArray(ss) => ss.into_iter().map(Value::SingleString).collect(),
Value::BoolArray(bb) => bb.into_iter().map(Value::Bool).collect(),
Value::FloatArray(ff) => ff.into_iter().map(Value::Float).collect(),
Value::Placeholder => {
return Err("array:apply => second argument cannot be just '_'; it must be an actual array".to_string());
}
other => {
return Err(format!("array:apply => second argument must be an array, got {:?}", other));
}
};
apply_n_ary_function_value(interp, func_box, elements)
}