use crate::apply::apply_n_ary_function_value;
use mumu::parser::types::{Value, FunctionValue};
use mumu::parser::interpreter::Interpreter;
use std::collections::HashSet;
pub fn array_flatten_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:flatten expects exactly one argument".to_string());
}
let v = &args[0];
match v {
Value::MixedArray(xs) => {
let mut out = Vec::new();
for x in xs {
match x {
Value::MixedArray(inner) => out.extend(inner.clone()),
_ => out.push(x.clone()),
}
}
Ok(Value::MixedArray(out))
}
Value::IntArray(_) | Value::FloatArray(_) | Value::StrArray(_) | Value::BoolArray(_) => Ok(v.clone()),
_ => Err("array:flatten expects a MixedArray or array".to_string()),
}
}
pub fn array_flatten_deep_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:flatten_deep expects exactly one argument".to_string());
}
Ok(flatten_deep(&args[0]))
}
fn flatten_deep(v: &Value) -> Value {
match v {
Value::MixedArray(xs) => {
let mut out = Vec::new();
for x in xs {
match x {
Value::MixedArray(inner) => {
let deep = flatten_deep(&Value::MixedArray(inner.clone()));
match deep {
Value::MixedArray(inner_deep) => out.extend(inner_deep),
_ => out.push(deep),
}
}
_ => out.push(x.clone()),
}
}
Value::MixedArray(out)
}
_ => v.clone(),
}
}
pub fn array_uniq_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:uniq expects exactly one argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => {
let mut seen = HashSet::new();
let mut out = Vec::new();
for &x in xs {
if seen.insert(x) {
out.push(x);
}
}
Ok(Value::IntArray(out))
}
Value::BoolArray(xs) => {
let mut seen = HashSet::new();
let mut out = Vec::new();
for &x in xs {
if seen.insert(x) {
out.push(x);
}
}
Ok(Value::BoolArray(out))
}
Value::StrArray(xs) => {
let mut seen = HashSet::new();
let mut out = Vec::new();
for x in xs {
if seen.insert(x) {
out.push(x.clone());
}
}
Ok(Value::StrArray(out))
}
Value::MixedArray(xs) => {
let mut seen = Vec::new();
let mut out = Vec::new();
for x in xs {
if !seen.iter().any(|y| y == x) {
seen.push(x.clone());
out.push(x.clone());
}
}
Ok(Value::MixedArray(out))
}
Value::FloatArray(xs) => {
let mut seen: Vec<f64> = Vec::new();
let mut out = Vec::new();
for &x in xs {
if !seen.iter().any(|y: &f64| (*y - x).abs() < std::f64::EPSILON) {
seen.push(x);
out.push(x);
}
}
Ok(Value::FloatArray(out))
}
_ => Err("array:uniq => unsupported type".to_string()),
}
}
pub fn array_compact_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:compact expects exactly one argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => Ok(Value::IntArray(xs.iter().filter(|&&x| x != 0).copied().collect())),
Value::FloatArray(xs) => Ok(Value::FloatArray(xs.iter().filter(|&&x| x != 0.0).copied().collect())),
Value::BoolArray(xs) => Ok(Value::BoolArray(xs.iter().filter(|&&x| x).copied().collect())),
Value::StrArray(xs) => Ok(Value::StrArray(xs.iter().filter(|x| !x.is_empty()).cloned().collect())),
Value::MixedArray(xs) => Ok(Value::MixedArray(xs.iter().filter(|x| !is_falsy(x)).cloned().collect())),
_ => Err("array:compact => unsupported type".to_string()),
}
}
fn is_falsy(v: &Value) -> bool {
match v {
Value::Bool(false) => true,
Value::Int(0) => true,
Value::Float(x) if *x == 0.0 => true,
Value::SingleString(s) if s.is_empty() => true,
Value::Placeholder => true,
_ => false,
}
}
pub fn array_reverse_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:reverse expects exactly one argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => {
let mut out = xs.clone();
out.reverse();
Ok(Value::IntArray(out))
}
Value::FloatArray(xs) => {
let mut out = xs.clone();
out.reverse();
Ok(Value::FloatArray(out))
}
Value::BoolArray(xs) => {
let mut out = xs.clone();
out.reverse();
Ok(Value::BoolArray(out))
}
Value::StrArray(xs) => {
let mut out = xs.clone();
out.reverse();
Ok(Value::StrArray(out))
}
Value::MixedArray(xs) => {
let mut out = xs.clone();
out.reverse();
Ok(Value::MixedArray(out))
}
_ => Err("array:reverse => unsupported type".to_string()),
}
}
pub fn array_shuffle_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
use rand::seq::SliceRandom;
use rand::thread_rng;
if args.len() != 1 {
return Err("array:shuffle expects exactly one argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => {
let mut out = xs.clone();
out.shuffle(&mut thread_rng());
Ok(Value::IntArray(out))
}
Value::FloatArray(xs) => {
let mut out = xs.clone();
out.shuffle(&mut thread_rng());
Ok(Value::FloatArray(out))
}
Value::BoolArray(xs) => {
let mut out = xs.clone();
out.shuffle(&mut thread_rng());
Ok(Value::BoolArray(out))
}
Value::StrArray(xs) => {
let mut out = xs.clone();
out.shuffle(&mut thread_rng());
Ok(Value::StrArray(out))
}
Value::MixedArray(xs) => {
let mut out = xs.clone();
out.shuffle(&mut thread_rng());
Ok(Value::MixedArray(out))
}
_ => Err("array:shuffle => unsupported type".to_string()),
}
}
pub fn array_partition_bridge(
interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 2 {
return Err("array:partition => expects (fn, array)".to_string());
}
let pred_fn = match &args[0] {
Value::Function(f) => f,
other => return Err(format!("array:partition => first argument must be a function, got {:?}", other)),
};
match &args[1] {
Value::IntArray(xs) => {
let (yes, no) = partition_values(interp, &**pred_fn, xs.iter().map(|&x| Value::Int(x)).collect());
Ok(Value::MixedArray(vec![Value::MixedArray(yes), Value::MixedArray(no)]))
}
Value::FloatArray(xs) => {
let (yes, no) = partition_values(interp, &**pred_fn, xs.iter().map(|&x| Value::Float(x)).collect());
Ok(Value::MixedArray(vec![Value::MixedArray(yes), Value::MixedArray(no)]))
}
Value::StrArray(xs) => {
let (yes, no) = partition_values(interp, &**pred_fn, xs.iter().map(|x| Value::SingleString(x.clone())).collect());
Ok(Value::MixedArray(vec![Value::MixedArray(yes), Value::MixedArray(no)]))
}
Value::BoolArray(xs) => {
let (yes, no) = partition_values(interp, &**pred_fn, xs.iter().map(|&x| Value::Bool(x)).collect());
Ok(Value::MixedArray(vec![Value::MixedArray(yes), Value::MixedArray(no)]))
}
Value::MixedArray(xs) => {
let (yes, no) = partition_values(interp, &**pred_fn, xs.clone());
Ok(Value::MixedArray(vec![Value::MixedArray(yes), Value::MixedArray(no)]))
}
other => Err(format!("array:partition => unsupported array type: {:?}", other)),
}
}
fn partition_values(
interp: &mut Interpreter,
pred: &FunctionValue,
items: Vec<Value>,
) -> (Vec<Value>, Vec<Value>) {
let mut yes = Vec::new();
let mut no = Vec::new();
for v in items {
let res = apply_n_ary_function_value(interp, Box::new(pred.clone()), vec![v.clone()]);
match res {
Ok(Value::Bool(true)) => yes.push(v),
Ok(Value::Bool(false)) => no.push(v),
_ => no.push(v),
}
}
(yes, no)
}
pub fn array_head_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:head => expects 1 argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => xs.get(0).map(|&x| Value::Int(x)).ok_or("_".to_string()),
Value::FloatArray(xs) => xs.get(0).map(|&x| Value::Float(x)).ok_or("_".to_string()),
Value::BoolArray(xs) => xs.get(0).map(|&x| Value::Bool(x)).ok_or("_".to_string()),
Value::StrArray(xs) => xs.get(0).map(|x| Value::SingleString(x.clone())).ok_or("_".to_string()),
Value::MixedArray(xs) => xs.get(0).cloned().ok_or("_".to_string()),
Value::SingleString(s) => s.chars().next().map(|c| Value::SingleString(c.to_string())).ok_or("_".to_string()),
_ => Err("array:head => unsupported type".to_string()),
}
}
pub fn array_last_bridge(
_interp: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, String> {
if args.len() != 1 {
return Err("array:last => expects 1 argument".to_string());
}
let v = &args[0];
match v {
Value::IntArray(xs) => xs.last().map(|&x| Value::Int(x)).ok_or("_".to_string()),
Value::FloatArray(xs) => xs.last().map(|&x| Value::Float(x)).ok_or("_".to_string()),
Value::BoolArray(xs) => xs.last().map(|&x| Value::Bool(x)).ok_or("_".to_string()),
Value::StrArray(xs) => xs.last().map(|x| Value::SingleString(x.clone())).ok_or("_".to_string()),
Value::MixedArray(xs) => xs.last().cloned().ok_or("_".to_string()),
Value::SingleString(s) => s.chars().last().map(|c| Value::SingleString(c.to_string())).ok_or("_".to_string()),
_ => Err("array:last => unsupported type".to_string()),
}
}