use mumu::parser::types::{Value, FunctionValue};
use mumu::parser::interpreter::Interpreter;
use std::sync::{Arc, Mutex};
pub fn is_placeholder(val: &Value) -> bool {
matches!(val, Value::Placeholder)
|| matches!(val, Value::SingleString(s) if s == "_")
|| matches!(val, Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_")
}
pub fn make_two_arg_partial(
finalize: fn(&mut Interpreter, Value, Value) -> Result<Value, String>,
a_opt: Option<Value>,
b_opt: Option<Value>,
label: &'static str,
) -> Value {
let closure = move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut a = a_opt.clone();
let mut b = b_opt.clone();
for arg in new_args {
if a.is_none() {
if !is_placeholder(&arg) { a = Some(arg); }
continue;
}
if b.is_none() {
if !is_placeholder(&arg) { b = Some(arg); }
continue;
}
return Err("Too many arguments for partial function".to_string());
}
if let (Some(aa), Some(bb)) = (a.clone(), b.clone()) {
finalize(interp, aa, bb)
} else {
Ok(make_two_arg_partial(finalize, a, b, label))
}
};
Value::Function(Box::new(FunctionValue::RustClosure(
label.to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn make_three_arg_partial(
finalize: fn(&mut Interpreter, Value, Value, Value) -> Result<Value, String>,
a_opt: Option<Value>,
b_opt: Option<Value>,
c_opt: Option<Value>,
label: &'static str,
) -> Value {
let closure = move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut a = a_opt.clone();
let mut b = b_opt.clone();
let mut c = c_opt.clone();
for arg in new_args {
if a.is_none() {
if !is_placeholder(&arg) { a = Some(arg); }
continue;
}
if b.is_none() {
if !is_placeholder(&arg) { b = Some(arg); }
continue;
}
if c.is_none() {
if !is_placeholder(&arg) { c = Some(arg); }
continue;
}
return Err("Too many arguments for partial function".to_string());
}
if let (Some(aa), Some(bb), Some(cc)) = (a.clone(), b.clone(), c.clone()) {
finalize(interp, aa, bb, cc)
} else {
Ok(make_three_arg_partial(finalize, a, b, c, label))
}
};
Value::Function(Box::new(FunctionValue::RustClosure(
label.to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}