use mumu::{
parser::types::FunctionValue::RustClosure,
parser::types::Value,
Interpreter,
};
use std::sync::{Arc, Mutex};
pub fn array_range_bridge(
_interp: &mut Interpreter,
mut args: Vec<Value>
) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(None, None)),
1 => {
let first = args.remove(0);
match first {
Value::Placeholder => {
Ok(make_two_arg_partial(None, None))
}
val => {
Ok(make_two_arg_partial(Some(val), None))
}
}
}
2 => {
let start_val = args.remove(0);
let end_val = args.remove(0);
let s_pl = matches!(start_val, Value::Placeholder);
let e_pl = matches!(end_val, Value::Placeholder);
if !s_pl && !e_pl {
do_range_final(start_val, end_val)
} else {
Ok(make_two_arg_partial(
if s_pl { None } else { Some(start_val) },
if e_pl { None } else { Some(end_val) },
))
}
}
n => Err(format!("array:range => expected up to 2 arguments, got {}", n)),
}
}
fn do_range_final(start_val: Value, end_val: Value) -> Result<Value, String> {
let s = match coerce_int(&start_val)? {
Some(i) => i,
None => return Ok(Value::IntArray(vec![])), };
let e = match coerce_int(&end_val)? {
Some(j) => j,
None => return Ok(Value::IntArray(vec![])),
};
if s >= e {
return Ok(Value::IntArray(vec![]));
}
let length = (e - s) as usize;
let mut out = Vec::with_capacity(length);
for i in s..e {
out.push(i);
}
Ok(Value::IntArray(out))
}
fn coerce_int(val: &Value) -> Result<Option<i32>, String> {
match val {
Value::Placeholder => {
Err("array:range => found placeholder in do_range_final".to_string())
}
Value::Int(i) => Ok(Some(*i)),
Value::IntArray(arr) if arr.len() == 1 => Ok(Some(arr[0])),
Value::Float(_) | Value::Long(_) => Err(format!("array:range => requires i32, got {:?}", val)),
_ => Err(format!("array:range => not an int or single-element IntArray, got {:?}", val)),
}
}
fn make_two_arg_partial(
start_opt: Option<Value>,
end_opt: Option<Value>
) -> Value {
Value::Function(Box::new(RustClosure(
"array:range-partial".to_string(),
Arc::new(Mutex::new(move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut s_current = start_opt.clone();
let mut e_current = end_opt.clone();
for arg in new_args {
if s_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
s_current = Some(arg);
}
continue;
}
if e_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
e_current = Some(arg);
}
continue;
}
return Err("array:range => partial => too many arguments".to_string());
}
if s_current.is_some() && e_current.is_some() {
do_range_final(
s_current.as_ref().unwrap().clone(),
e_current.as_ref().unwrap().clone()
)
} else {
Ok(make_two_arg_partial(s_current, e_current))
}
})),
0,
)))
}