use crate::apply::apply_n_ary_function_value;
use mumu::parser::types::{FunctionValue, Value};
use mumu::parser::interpreter::Interpreter;
use std::sync::{Arc, Mutex};
fn is_placeholder(val: &Value) -> bool {
match val {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(arr) if arr.len() == 1 && arr[0] == "_" => true,
_ => false,
}
}
fn make_two_arg_partial(
finalize_fn: fn(&mut Interpreter, Value, Value) -> Result<Value, String>,
a_opt: Option<Value>,
b_opt: Option<Value>
) -> Value {
use FunctionValue::RustClosure;
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_fn(interp, aa, bb)
} else {
Ok(make_two_arg_partial(finalize_fn, a, b))
}
};
Value::Function(Box::new(RustClosure(
"array-search-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn array_find_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(do_find, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(do_find, None, None))
} else {
Ok(make_two_arg_partial(do_find, Some(args[0].clone()), None))
}
}
2 => {
let a1_pl = is_placeholder(&args[0]);
let a2_pl = is_placeholder(&args[1]);
if !a1_pl && !a2_pl {
do_find(interp, args[0].clone(), args[1].clone())
} else {
Ok(make_two_arg_partial(
do_find,
if a1_pl { None } else { Some(args[0].clone()) },
if a2_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("array:find expects up to 2 arguments, got {}", n)),
}
}
fn do_find(interp: &mut Interpreter, pred: Value, arr: Value) -> Result<Value, String> {
let pred_fn = match pred {
Value::Function(fb) => fb,
_ => return Err("array:find => first argument must be a function".to_string()),
};
let iter = match arr {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::FloatArray(xs) => xs.into_iter().map(Value::Float).collect(),
Value::BoolArray(xs) => xs.into_iter().map(Value::Bool).collect(),
Value::StrArray(xs) => xs.into_iter().map(Value::SingleString).collect(),
Value::MixedArray(xs) => xs,
_ => return Err("array:find => second argument must be an array".to_string()),
};
for item in iter {
let res = apply_n_ary_function_value(interp, pred_fn.clone(), vec![item.clone()]);
if let Ok(Value::Bool(true)) = res {
return Ok(item);
}
}
Ok(Value::Placeholder)
}
pub fn array_find_index_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(do_find_index, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(do_find_index, None, None))
} else {
Ok(make_two_arg_partial(do_find_index, Some(args[0].clone()), None))
}
}
2 => {
let a1_pl = is_placeholder(&args[0]);
let a2_pl = is_placeholder(&args[1]);
if !a1_pl && !a2_pl {
do_find_index(interp, args[0].clone(), args[1].clone())
} else {
Ok(make_two_arg_partial(
do_find_index,
if a1_pl { None } else { Some(args[0].clone()) },
if a2_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("array:findIndex expects up to 2 arguments, got {}", n)),
}
}
fn do_find_index(interp: &mut Interpreter, pred: Value, arr: Value) -> Result<Value, String> {
let pred_fn = match pred {
Value::Function(fb) => fb,
_ => return Err("array:findIndex => first argument must be a function".to_string()),
};
let iter = match arr {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::FloatArray(xs) => xs.into_iter().map(Value::Float).collect(),
Value::BoolArray(xs) => xs.into_iter().map(Value::Bool).collect(),
Value::StrArray(xs) => xs.into_iter().map(Value::SingleString).collect(),
Value::MixedArray(xs) => xs,
_ => return Err("array:findIndex => second argument must be an array".to_string()),
};
for (idx, item) in iter.into_iter().enumerate() {
let res = apply_n_ary_function_value(interp, pred_fn.clone(), vec![item]);
if let Ok(Value::Bool(true)) = res {
return Ok(Value::Int(idx as i32));
}
}
Ok(Value::Int(-1))
}
pub fn array_includes_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(do_includes, None, None)),
1 => Ok(make_two_arg_partial(do_includes, Some(args[0].clone()), None)),
2 => {
let a1_pl = is_placeholder(&args[0]);
let a2_pl = is_placeholder(&args[1]);
if !a1_pl && !a2_pl {
do_includes(_interp, args[0].clone(), args[1].clone())
} else {
Ok(make_two_arg_partial(
do_includes,
if a1_pl { None } else { Some(args[0].clone()) },
if a2_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("array:includes expects up to 2 arguments, got {}", n)),
}
}
fn do_includes(_interp: &mut Interpreter, item: Value, arr: Value) -> Result<Value, String> {
let iter = match arr {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::FloatArray(xs) => xs.into_iter().map(Value::Float).collect(),
Value::BoolArray(xs) => xs.into_iter().map(Value::Bool).collect(),
Value::StrArray(xs) => xs.into_iter().map(Value::SingleString).collect(),
Value::MixedArray(xs) => xs,
_ => return Err("array:includes => second argument must be an array".to_string()),
};
for v in iter {
if v == item {
return Ok(Value::Bool(true));
}
}
Ok(Value::Bool(false))
}
pub fn array_every_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(do_every, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(do_every, None, None))
} else {
Ok(make_two_arg_partial(do_every, Some(args[0].clone()), None))
}
}
2 => {
let a1_pl = is_placeholder(&args[0]);
let a2_pl = is_placeholder(&args[1]);
if !a1_pl && !a2_pl {
do_every(interp, args[0].clone(), args[1].clone())
} else {
Ok(make_two_arg_partial(
do_every,
if a1_pl { None } else { Some(args[0].clone()) },
if a2_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("array:every expects up to 2 arguments, got {}", n)),
}
}
fn do_every(interp: &mut Interpreter, pred: Value, arr: Value) -> Result<Value, String> {
let pred_fn = match pred {
Value::Function(fb) => fb,
_ => return Err("array:every => first argument must be a function".to_string()),
};
let iter = match arr {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::FloatArray(xs) => xs.into_iter().map(Value::Float).collect(),
Value::BoolArray(xs) => xs.into_iter().map(Value::Bool).collect(),
Value::StrArray(xs) => xs.into_iter().map(Value::SingleString).collect(),
Value::MixedArray(xs) => xs,
_ => return Err("array:every => second argument must be an array".to_string()),
};
for v in iter {
let res = apply_n_ary_function_value(interp, pred_fn.clone(), vec![v]);
if !matches!(res, Ok(Value::Bool(true))) {
return Ok(Value::Bool(false));
}
}
Ok(Value::Bool(true))
}
pub fn array_some_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(do_some, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(do_some, None, None))
} else {
Ok(make_two_arg_partial(do_some, Some(args[0].clone()), None))
}
}
2 => {
let a1_pl = is_placeholder(&args[0]);
let a2_pl = is_placeholder(&args[1]);
if !a1_pl && !a2_pl {
do_some(interp, args[0].clone(), args[1].clone())
} else {
Ok(make_two_arg_partial(
do_some,
if a1_pl { None } else { Some(args[0].clone()) },
if a2_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("array:some expects up to 2 arguments, got {}", n)),
}
}
fn do_some(interp: &mut Interpreter, pred: Value, arr: Value) -> Result<Value, String> {
let pred_fn = match pred {
Value::Function(fb) => fb,
_ => return Err("array:some => first argument must be a function".to_string()),
};
let iter = match arr {
Value::IntArray(xs) => xs.into_iter().map(Value::Int).collect(),
Value::FloatArray(xs) => xs.into_iter().map(Value::Float).collect(),
Value::BoolArray(xs) => xs.into_iter().map(Value::Bool).collect(),
Value::StrArray(xs) => xs.into_iter().map(Value::SingleString).collect(),
Value::MixedArray(xs) => xs,
_ => return Err("array:some => second argument must be an array".to_string()),
};
for v in iter {
let res = apply_n_ary_function_value(interp, pred_fn.clone(), vec![v]);
if matches!(res, Ok(Value::Bool(true))) {
return Ok(Value::Bool(true));
}
}
Ok(Value::Bool(false))
}