use mumu::{
parser::types::FunctionValue,
parser::types::Value,
Interpreter,
};
use std::sync::{Arc, Mutex};
pub fn array_nth_bridge(
_interp: &mut Interpreter,
args: Vec<Value>
) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(None, None)),
1 => {
let first = &args[0];
match first {
Value::Placeholder => {
Ok(make_two_arg_partial(None, None))
}
val => {
Ok(make_two_arg_partial(Some(val.clone()), None))
}
}
}
2 => {
let index_val = &args[0];
let data_val = &args[1];
let i_is_pl = matches!(index_val, Value::Placeholder);
let d_is_pl = matches!(data_val, Value::Placeholder);
if !i_is_pl && !d_is_pl {
do_nth_final(index_val.clone(), data_val.clone())
} else {
let maybe_idx = if i_is_pl { None } else { Some(index_val.clone()) };
let maybe_dat = if d_is_pl { None } else { Some(data_val.clone()) };
Ok(make_two_arg_partial(maybe_idx, maybe_dat))
}
}
n => Err(format!("array:nth => expected up to 2 arguments, got {}", n)),
}
}
fn do_nth_final(
index_val: Value,
data_val: Value
) -> Result<Value, String> {
let idx_i32 = match index_val {
Value::Int(i) => i,
Value::IntArray(ref arr) if arr.len() == 1 => arr[0],
other => {
return Err(format!(
"array:nth => index must be int or single-element IntArray, got {:?}",
other
))
}
};
match data_val {
Value::IntArray(xs) => nth_from_int_array(xs, idx_i32),
Value::StrArray(ss) => nth_from_str_array(ss, idx_i32),
Value::BoolArray(bb) => nth_from_bool_array(bb, idx_i32),
Value::FloatArray(ffs) => nth_from_float_array(ffs, idx_i32),
other => Err(format!(
"array:nth => second argument must be an array, got {:?}",
other
)),
}
}
fn nth_from_int_array(xs: Vec<i32>, idx: i32) -> Result<Value, String> {
let len = xs.len() as i32;
let real = if idx < 0 { len + idx } else { idx };
if real < 0 || real >= len {
Ok(Value::Placeholder)
} else {
Ok(Value::Int(xs[real as usize]))
}
}
fn nth_from_str_array(ss: Vec<String>, idx: i32) -> Result<Value, String> {
let len = ss.len() as i32;
let real = if idx < 0 { len + idx } else { idx };
if real < 0 || real >= len {
Ok(Value::Placeholder)
} else {
Ok(Value::SingleString(ss[real as usize].clone()))
}
}
fn nth_from_bool_array(bb: Vec<bool>, idx: i32) -> Result<Value, String> {
let len = bb.len() as i32;
let real = if idx < 0 { len + idx } else { idx };
if real < 0 || real >= len {
Ok(Value::Placeholder)
} else {
Ok(Value::Bool(bb[real as usize]))
}
}
fn nth_from_float_array(ffs: Vec<f64>, idx: i32) -> Result<Value, String> {
let len = ffs.len() as i32;
let real = if idx < 0 { len + idx } else { idx };
if real < 0 || real >= len {
Ok(Value::Placeholder)
} else {
Ok(Value::Float(ffs[real as usize]))
}
}
fn make_two_arg_partial(
index_opt: Option<Value>,
data_opt: Option<Value>
) -> Value {
Value::Function(Box::new(FunctionValue::RustClosure(
"array:nth-partial".to_string(),
Arc::new(Mutex::new(move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut i_current = index_opt.clone();
let mut d_current = data_opt.clone();
for arg in new_args {
if i_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
i_current = Some(arg);
}
continue;
}
if d_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
d_current = Some(arg);
}
continue;
}
return Err("array:nth => partial => too many arguments".to_string());
}
if i_current.is_some() && d_current.is_some() {
do_nth_final(
i_current.as_ref().unwrap().clone(),
d_current.as_ref().unwrap().clone()
)
} else {
Ok(make_two_arg_partial(i_current, d_current))
}
})),
0,
)))
}