use mumu::parser::types::{FunctionValue, Value};
use mumu::parser::interpreter::{Interpreter, apply_n_ary_function_value};
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(&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(interp, aa, bb)
} else {
Ok(make_two_arg_partial(finalize, a, b))
}
};
Value::Function(Box::new(RustClosure(
"array-sort-2arg-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn array_sort_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
match args.len() {
1 => sort_default(&args.remove(0)),
2 => {
let cmp = args.remove(0);
let arr = args.remove(0);
if is_placeholder(&cmp) && is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_with_comparator, None, None))
} else if is_placeholder(&cmp) {
Ok(make_two_arg_partial(sort_with_comparator, None, Some(arr)))
} else if is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_with_comparator, Some(cmp), None))
} else {
sort_with_comparator(interp, cmp, arr)
}
}
0 => Ok(make_two_arg_partial(sort_with_comparator, None, None)),
n => Err(format!("array:sort expects 1 or 2 arguments (optional comparator, array), got {}", n)),
}
}
pub fn array_sort_by_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
match args.len() {
2 => {
let fn_val = args.remove(0);
let arr = args.remove(0);
if is_placeholder(&fn_val) && is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_by_finalize, None, None))
} else if is_placeholder(&fn_val) {
Ok(make_two_arg_partial(sort_by_finalize, None, Some(arr)))
} else if is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_by_finalize, Some(fn_val), None))
} else {
sort_by_finalize(interp, fn_val, arr)
}
}
1 => Ok(make_two_arg_partial(sort_by_finalize, Some(args.remove(0)), None)),
0 => Ok(make_two_arg_partial(sort_by_finalize, None, None)),
n => Err(format!("array:sortBy expects 2 arguments (fn, array), got {}", n)),
}
}
fn sort_by_finalize(interp: &mut Interpreter, fn_val: Value, arr: Value) -> Result<Value, String> {
let fn_val = match fn_val {
Value::Function(fb) => fb,
other => return Err(format!("array:sortBy => first arg must be function, got {:?}", other)),
};
let arr_vec = to_mixed_vec(&arr)?;
let mut out = arr_vec.clone();
out.sort_by(|a, b| {
let ka = apply_n_ary_function_value(interp, fn_val.clone(), vec![a.clone()]);
let kb = apply_n_ary_function_value(interp, fn_val.clone(), vec![b.clone()]);
match (ka, kb) {
(Ok(Value::Int(va)), Ok(Value::Int(vb))) => va.cmp(&vb),
(Ok(Value::Float(va)), Ok(Value::Float(vb))) => va.partial_cmp(&vb).unwrap_or(std::cmp::Ordering::Equal),
(Ok(Value::SingleString(sa)), Ok(Value::SingleString(sb))) => sa.cmp(&sb),
_ => std::cmp::Ordering::Equal,
}
});
Ok(Value::MixedArray(out))
}
pub fn array_sort_with_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
match args.len() {
2 => {
let fns = args.remove(0);
let arr = args.remove(0);
if is_placeholder(&fns) && is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_with_finalize, None, None))
} else if is_placeholder(&fns) {
Ok(make_two_arg_partial(sort_with_finalize, None, Some(arr)))
} else if is_placeholder(&arr) {
Ok(make_two_arg_partial(sort_with_finalize, Some(fns), None))
} else {
sort_with_finalize(interp, fns, arr)
}
}
1 => Ok(make_two_arg_partial(sort_with_finalize, Some(args.remove(0)), None)),
0 => Ok(make_two_arg_partial(sort_with_finalize, None, None)),
n => Err(format!("array:sortWith expects 2 arguments (fns_array, array), got {}", n)),
}
}
fn sort_with_finalize(interp: &mut Interpreter, fns: Value, arr: Value) -> Result<Value, String> {
let fns = match fns {
Value::MixedArray(vs) => vs,
Value::Function(f) => vec![Value::Function(f)],
other => return Err(format!("array:sortWith => first arg must be array of functions, got {:?}", other)),
};
let arr_vec = to_mixed_vec(&arr)?;
let mut out = arr_vec.clone();
out.sort_by(|a, b| {
for fn_val in &fns {
let cmp_fn = match fn_val {
Value::Function(fb) => fb,
_ => continue,
};
let res = apply_n_ary_function_value(interp, cmp_fn.clone(), vec![a.clone(), b.clone()]);
match res {
Ok(Value::Int(i)) => {
if i < 0 { return std::cmp::Ordering::Less; }
if i > 0 { return std::cmp::Ordering::Greater; }
}
Ok(Value::Float(f)) => {
if f < 0.0 { return std::cmp::Ordering::Less; }
if f > 0.0 { return std::cmp::Ordering::Greater; }
}
_ => {}
}
}
std::cmp::Ordering::Equal
});
Ok(Value::MixedArray(out))
}
fn sort_default(arr: &Value) -> Result<Value, String> {
match arr {
Value::IntArray(xs) => {
let mut out = xs.clone();
out.sort();
Ok(Value::IntArray(out))
}
Value::FloatArray(xs) => {
let mut out = xs.clone();
out.sort_by(|a, b| a.partial_cmp(b).unwrap());
Ok(Value::FloatArray(out))
}
Value::StrArray(xs) => {
let mut out = xs.clone();
out.sort();
Ok(Value::StrArray(out))
}
Value::MixedArray(xs) => {
if xs.iter().all(|v| matches!(v, Value::Int(_))) {
let mut ints: Vec<i32> = xs.iter().filter_map(|v| if let Value::Int(x) = v { Some(*x) } else { None }).collect();
ints.sort();
Ok(Value::MixedArray(ints.into_iter().map(Value::Int).collect()))
} else if xs.iter().all(|v| matches!(v, Value::Float(_))) {
let mut floats: Vec<f64> = xs.iter().filter_map(|v| if let Value::Float(x) = v { Some(*x) } else { None }).collect();
floats.sort_by(|a, b| a.partial_cmp(b).unwrap());
Ok(Value::MixedArray(floats.into_iter().map(Value::Float).collect()))
} else if xs.iter().all(|v| matches!(v, Value::SingleString(_))) {
let mut ss: Vec<String> = xs.iter().filter_map(|v| if let Value::SingleString(s) = v { Some(s.clone()) } else { None }).collect();
ss.sort();
Ok(Value::MixedArray(ss.into_iter().map(Value::SingleString).collect()))
} else {
Err("array:sort(MixedArray) => can only sort arrays of all numbers or all strings".to_string())
}
}
_ => Err("array:sort => unsupported array type".to_string()),
}
}
fn sort_with_comparator(
interp: &mut Interpreter,
cmp: Value,
arr: Value
) -> Result<Value, String> {
let arr_vec = to_mixed_vec(&arr)?;
let mut arr_cloned = arr_vec.clone();
let cmp_fn = match cmp {
Value::Function(fb) => fb,
_ => return Err("array:sort: comparator must be a function".to_string()),
};
arr_cloned.sort_by(|a, b| {
let res = apply_n_ary_function_value(interp, cmp_fn.clone(), vec![a.clone(), b.clone()]);
match res {
Ok(Value::Int(i)) => {
if i < 0 { std::cmp::Ordering::Less }
else if i == 0 { std::cmp::Ordering::Equal }
else { std::cmp::Ordering::Greater }
}
Ok(Value::Float(f)) => {
if f < 0.0 { std::cmp::Ordering::Less }
else if f == 0.0 { std::cmp::Ordering::Equal }
else { std::cmp::Ordering::Greater }
}
_ => std::cmp::Ordering::Equal,
}
});
Ok(Value::MixedArray(arr_cloned))
}
fn to_mixed_vec(arr: &Value) -> Result<Vec<Value>, String> {
match arr {
Value::MixedArray(xs) => Ok(xs.clone()),
Value::IntArray(xs) => Ok(xs.iter().map(|&x| Value::Int(x)).collect()),
Value::FloatArray(xs) => Ok(xs.iter().map(|&x| Value::Float(x)).collect()),
Value::StrArray(xs) => Ok(xs.iter().map(|x| Value::SingleString(x.clone())).collect()),
_ => Err("array:sortWith/sortBy => unsupported array type".to_string()),
}
}