use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{Interpreter, DynamicFnInfo};
use mumu::parser::interpreter::apply::apply_n_ary_function_value;
use mumu::parser::types::{Value, FunctionValue, IteratorHandle, IteratorKind};
pub fn register_flow_slice(interp: &mut Interpreter) {
let f = Arc::new(Mutex::new(flow_slice_bridge_fn));
let info = DynamicFnInfo::new(f, true);
interp.register_dynamic_function_ex("flow:slice", info);
interp.set_variable(
"flow:slice",
Value::Function(Box::new(FunctionValue::Named("flow:slice".to_string())))
);
}
fn flow_slice_bridge_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
make_slice_partial(args)
}
fn make_slice_partial(existing_args: Vec<Value>) -> Result<Value, String> {
use mumu::parser::types::FunctionValue::RustClosure;
let store = Arc::new(SlicePartialState { arg_list: existing_args });
let clos = RustClosure(
"flow:slice-partial".to_string(),
Arc::new(Mutex::new(move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut combined = store.arg_list.clone();
for na in &new_args {
combined.push(na.clone());
}
finalize_or_partial(interp, combined)
})),
0
);
Ok(Value::Function(Box::new(clos)))
}
fn finalize_or_partial(_interp: &mut Interpreter, combined: Vec<Value>) -> Result<Value, String> {
if combined.len() < 3 {
return make_slice_partial(combined);
}
if combined.len() > 3 {
return Err(format!("flow:slice => expected up to 3 arguments, got {}", combined.len()));
}
let skip_val = &combined[0];
let count_val = &combined[1];
let source_val = &combined[2];
if is_placeholder(skip_val) || is_placeholder(count_val) || is_placeholder(source_val) {
return make_slice_partial(combined);
}
let skip_int = match skip_val {
Value::Int(i) => *i,
_ => return Err(format!("flow:slice => 'skip' must be int or placeholder, got {:?}", skip_val)),
};
let count_int = match count_val {
Value::Int(i) => *i,
_ => return Err(format!("flow:slice => 'count' must be int or placeholder, got {:?}", count_val)),
};
match source_val {
Value::Iterator(h) => Ok(Value::Function(build_slice_transform(
skip_int,
count_int,
SourceKind::Iterator(h.clone()),
))),
Value::Function(fb) => Ok(Value::Function(build_slice_transform(
skip_int,
count_int,
SourceKind::Transform(fb.clone()),
))),
_ => Err(format!(
"flow:slice => third arg must be Iterator or transform Function, got {:?}",
source_val
)),
}
}
enum SourceKind {
Iterator(IteratorHandle),
Transform(Box<FunctionValue>),
}
fn build_slice_transform(skip: i32, count: i32, source: SourceKind) -> Box<FunctionValue> {
use mumu::parser::types::FunctionValue::RustClosure;
let data = Arc::new(Mutex::new(SliceData {
skip_so_far: 0,
taken_so_far: 0,
skip_limit: skip.max(0),
take_limit: count.max(0),
source_kind: source,
done: false,
}));
Box::new(RustClosure(
"flow:slice-transform".to_string(),
Arc::new(Mutex::new(move |interp: &mut Interpreter, _args: Vec<Value>| {
let mut locked = data.lock().map_err(|_| "flow:slice => lock error".to_string())?;
if locked.done || locked.taken_so_far >= locked.take_limit {
return Err("NO_MORE_DATA".to_string());
}
while locked.skip_so_far < locked.skip_limit {
let reached_end = match &mut locked.source_kind {
SourceKind::Iterator(handle) => {
match &mut handle.kind {
IteratorKind::Core(state_arc) => {
let mut g = state_arc
.lock()
.map_err(|_| "flow:slice => Iterator lock error".to_string())?;
if g.done || g.current >= g.end {
true
} else {
g.current += 1;
if g.current >= g.end {
g.done = true;
}
false
}
}
IteratorKind::Plugin(plugin_arc) => {
let mut plugin = plugin_arc
.lock()
.map_err(|_| "flow:slice => plugin lock error".to_string())?;
match plugin.next_value() {
Ok(_) => false,
Err(e) if e == "NO_MORE_DATA" => true,
Err(e) => return Err(e),
}
}
}
}
SourceKind::Transform(fb) => {
match apply_n_ary_function_value(interp, fb.clone(), vec![]) {
Ok(_) => false,
Err(e) if e == "NO_MORE_DATA" => true,
Err(e) => return Err(e),
}
}
};
if reached_end {
locked.done = true;
return Err("NO_MORE_DATA".to_string());
}
locked.skip_so_far += 1;
}
let next_val = match &mut locked.source_kind {
SourceKind::Iterator(handle) => match &mut handle.kind {
IteratorKind::Core(state_arc) => {
let mut g = state_arc
.lock()
.map_err(|_| "flow:slice => Iterator lock error".to_string())?;
if g.done || g.current >= g.end {
None
} else {
let cur_item = g.current;
g.current += 1;
if g.current >= g.end {
g.done = true;
}
Some(Value::Int(cur_item))
}
}
IteratorKind::Plugin(plugin_arc) => {
let mut plugin = plugin_arc
.lock()
.map_err(|_| "flow:slice => plugin lock error".to_string())?;
match plugin.next_value() {
Ok(v) => Some(v),
Err(e) if e == "NO_MORE_DATA" => None,
Err(e) => return Err(e),
}
}
},
SourceKind::Transform(fb) => {
match apply_n_ary_function_value(interp, fb.clone(), vec![]) {
Ok(v) => Some(v),
Err(e) if e == "NO_MORE_DATA" => None,
Err(e) => return Err(e),
}
}
};
match next_val {
None => {
locked.done = true;
Err("NO_MORE_DATA".to_string())
}
Some(val) => {
locked.taken_so_far += 1;
if locked.taken_so_far >= locked.take_limit {
locked.done = true;
}
Ok(val)
}
}
})),
0,
))
}
struct SliceData {
skip_so_far: i32,
taken_so_far: i32,
skip_limit: i32,
take_limit: i32,
source_kind: SourceKind,
done: bool,
}
struct SlicePartialState {
arg_list: Vec<Value>,
}
fn is_placeholder(v: &Value) -> bool {
match v {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
_ => false,
}
}