pub mod adaptors;
pub mod generators;
pub mod peekable;
use crate::{KIteratorOutput as Output, Result, derive::*, prelude::*};
static MODULE_NAME: &str = "core.iterator";
pub fn make_module() -> KMap {
let result = KMap::with_type(MODULE_NAME);
result.add_fn("advance", |ctx| {
let expected_error = "|Iterator, Number >= 0|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(KValue::Iterator(iterator), [KValue::Number(n)]) if *n >= 0.0 => {
let mut iterator = iterator.clone();
let mut remaining = usize::from(n);
while remaining > 0 {
match iterator.next() {
Some(Output::Error(error)) => return Err(error),
Some(_) => remaining -= 1,
None => break,
}
}
Ok(remaining.into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("all", |ctx| {
let expected_error = "|Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
for output in ctx.vm.make_iterator(iterable)? {
let predicate_result = match output {
Output::Value(value) => ctx.vm.call_function(predicate.clone(), value),
Output::ValuePair(a, b) => ctx
.vm
.call_function(predicate.clone(), CallArgs::AsTuple(&[a, b])),
Output::Error(error) => return Err(error),
};
match predicate_result {
Ok(KValue::Bool(result)) => {
if !result {
return Ok(false.into());
}
}
Ok(unexpected) => {
return unexpected_type(
"a Bool to be returned from the predicate",
&unexpected,
);
}
error @ Err(_) => return error,
}
}
Ok(true.into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("any", |ctx| {
let expected_error = "|Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
for output in ctx.vm.make_iterator(iterable)? {
let predicate_result = match output {
Output::Value(value) => ctx.vm.call_function(predicate.clone(), value),
Output::ValuePair(a, b) => ctx
.vm
.call_function(predicate.clone(), CallArgs::AsTuple(&[a, b])),
Output::Error(error) => return Err(error),
};
match predicate_result {
Ok(KValue::Bool(result)) => {
if result {
return Ok(true.into());
}
}
Ok(unexpected) => {
return unexpected_type(
"a Bool to be returned from the predicate",
&unexpected,
);
}
Err(error) => return Err(error),
}
}
Ok(false.into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("chain", |ctx| {
let expected_error = "|Iterable, Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable_a, [iterable_b]) if iterable_b.is_iterable() => {
let iterable_a = iterable_a.clone();
let iterable_b = iterable_b.clone();
let result = KIterator::new(adaptors::Chain::new(
ctx.vm.make_iterator(iterable_a)?,
ctx.vm.make_iterator(iterable_b)?,
));
Ok(KValue::Iterator(result))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("chunks", |ctx| {
let expected_error = "|Iterable, Number|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [KValue::Number(n)]) => {
let iterable = iterable.clone();
let n = *n;
match adaptors::Chunks::new(ctx.vm.make_iterator(iterable)?, n.into()) {
Ok(result) => Ok(KIterator::new(result).into()),
Err(e) => runtime_error!("iterator.chunks: {}", e),
}
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("consume", |ctx| {
let expected_error = "|Iterable|, or |Iterable, |Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
for output in ctx.vm.make_iterator(iterable)? {
if let Output::Error(error) = output {
return Err(error);
}
}
Ok(KValue::Null)
}
(iterable, [f]) if f.is_callable() => {
let iterable = iterable.clone();
let f = f.clone();
for output in ctx.vm.make_iterator(iterable)? {
match output {
Output::Value(value) => {
ctx.vm.call_function(f.clone(), value)?;
}
Output::ValuePair(a, b) => {
ctx.vm
.call_function(f.clone(), CallArgs::AsTuple(&[a, b]))?;
}
Output::Error(error) => return Err(error),
}
}
Ok(KValue::Null)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("count", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let mut result = 0;
for output in ctx.vm.make_iterator(iterable)? {
if let Output::Error(error) = output {
return Err(error);
}
result += 1;
}
Ok(KValue::Number(result.into()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("cycle", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let result = adaptors::Cycle::new(ctx.vm.make_iterator(iterable)?);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("each", |ctx| {
let expected_error = "|Iterable, |Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [f]) if f.is_callable() => {
let iterable = iterable.clone();
let f = f.clone();
let result = adaptors::Each::new(ctx.vm.make_iterator(iterable)?, f, ctx.vm);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("enumerate", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let result = adaptors::Enumerate::new(ctx.vm.make_iterator(iterable)?);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("find", |ctx| {
let expected_error = "|Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
for output in ctx.vm.make_iterator(iterable)?.map(collect_pair) {
match output {
Output::Value(value) => {
match ctx.vm.call_function(predicate.clone(), value.clone()) {
Ok(KValue::Bool(result)) => {
if result {
return Ok(value);
}
}
Ok(unexpected) => {
return unexpected_type(
"a Bool to be returned from the predicate",
&unexpected,
);
}
Err(error) => return Err(error),
}
}
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(KValue::Null)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("flatten", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let result = adaptors::Flatten::new(ctx.vm.make_iterator(iterable)?, ctx.vm);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("fold", |ctx| {
let expected_error = "|Iterable, Any, |Any, Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [result, f]) if f.is_callable() => {
let iterable = iterable.clone();
let result = result.clone();
let f = f.clone();
let mut iter = ctx.vm.make_iterator(iterable)?;
match iter
.borrow_internals(|iterator| {
let mut fold_result = result.clone();
for value in iterator.map(collect_pair) {
match value {
Output::Value(value) => {
match ctx.vm.call_function(f.clone(), &[fold_result, value]) {
Ok(result) => fold_result = result,
Err(error) => return Some(Output::Error(error)),
}
}
Output::Error(error) => return Some(Output::Error(error)),
_ => unreachable!(),
}
}
Some(Output::Value(fold_result))
})
.unwrap()
{
Output::Value(result) => Ok(result),
Output::Error(error) => Err(error),
_ => unreachable!(),
}
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("generate", |ctx| {
let instance = ctx.instance();
if !(matches!(instance, &KValue::Null) || instance.type_as_string() == MODULE_NAME) {
return runtime_error!("iterator.generate must be used as a standalone function");
}
match ctx.args() {
[f] if f.is_callable() => {
let result = generators::Generate::new(f.clone(), ctx.vm);
Ok(KIterator::new(result).into())
}
[f, KValue::Number(n)] if *n >= 0 && f.is_callable() => {
let result = generators::GenerateN::new(n.into(), f.clone(), ctx.vm);
Ok(KIterator::new(result).into())
}
unexpected => unexpected_args(
"|generator: || -> Any|, or |generator: || -> Any, n: Number >= 0|",
unexpected,
),
}
});
result.add_fn("intersperse", |ctx| {
let expected_error = "|Iterable, Value|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [separator_fn]) if separator_fn.is_callable() => {
let iterable = iterable.clone();
let separator_fn = separator_fn.clone();
let result = adaptors::IntersperseWith::new(
ctx.vm.make_iterator(iterable)?,
separator_fn,
ctx.vm,
);
Ok(KIterator::new(result).into())
}
(iterable, [separator]) => {
let iterable = iterable.clone();
let separator = separator.clone();
let result = adaptors::Intersperse::new(ctx.vm.make_iterator(iterable)?, separator);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("iter", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
Ok(KValue::Iterator(ctx.vm.make_iterator(iterable)?))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("keep", |ctx| {
let expected_error = "|Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
let result =
adaptors::Keep::new(ctx.vm.make_iterator(iterable)?, predicate, ctx.vm);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("last", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let mut result = KValue::Null;
let mut iter = ctx.vm.make_iterator(iterable)?.map(collect_pair);
for output in &mut iter {
match output {
Output::Value(value) => result = value,
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(result)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("max", |ctx| {
let expected_error = "|Iterable|, or |Iterable, |Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
run_iterator_comparison(ctx.vm, iterable, InvertResult::Yes)
}
(iterable, [key_fn]) if key_fn.is_callable() => {
let iterable = iterable.clone();
let key_fn = key_fn.clone();
run_iterator_comparison_by_key(ctx.vm, iterable, key_fn, InvertResult::Yes)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("min", |ctx| {
let expected_error = "|Iterable|, or |Iterable, |Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
run_iterator_comparison(ctx.vm, iterable, InvertResult::No)
}
(iterable, [key_fn]) if key_fn.is_callable() => {
let iterable = iterable.clone();
let key_fn = key_fn.clone();
run_iterator_comparison_by_key(ctx.vm, iterable, key_fn, InvertResult::No)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("min_max", |ctx| {
let expected_error = "|Iterable|, or |Iterable, |Any| -> Any|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let mut result = None;
for iter_output in ctx.vm.make_iterator(iterable)?.map(collect_pair) {
match iter_output {
Output::Value(value) => {
result = Some(match result {
Some((min, max)) => (
compare_values(ctx.vm, min, value.clone(), InvertResult::No)?,
compare_values(ctx.vm, max, value, InvertResult::Yes)?,
),
None => (value.clone(), value),
})
}
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(result.map_or(KValue::Null, |(min, max)| {
KValue::Tuple(vec![min, max].into())
}))
}
(iterable, [key_fn]) if key_fn.is_callable() => {
let iterable = iterable.clone();
let key_fn = key_fn.clone();
let mut result = None;
for iter_output in ctx.vm.make_iterator(iterable)?.map(collect_pair) {
match iter_output {
Output::Value(value) => {
let key = ctx.vm.call_function(key_fn.clone(), value.clone())?;
let value_and_key = (value, key);
result = Some(match result {
Some((min_and_key, max_and_key)) => (
compare_values_with_key(
ctx.vm,
min_and_key,
value_and_key.clone(),
InvertResult::No,
)?,
compare_values_with_key(
ctx.vm,
max_and_key,
value_and_key,
InvertResult::Yes,
)?,
),
None => (value_and_key.clone(), value_and_key),
})
}
Output::Error(error) => return Err(error),
_ => unreachable!(), }
}
Ok(result.map_or(KValue::Null, |((min, _), (max, _))| {
KValue::Tuple(vec![min, max].into())
}))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("next", |ctx| {
let expected_error = "|Iterable|";
let mut iter = match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(KValue::Iterator(i), []) => i.clone(),
(iterable, []) if iterable.is_iterable() => ctx.vm.make_iterator(iterable.clone())?,
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
};
let output = match iter_output_to_result(iter.next())? {
None => KValue::Null,
Some(output) => IteratorOutput::from(output).into(),
};
Ok(output)
});
result.add_fn("next_back", |ctx| {
let expected_error = "|Iterable|";
let mut iter = match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(KValue::Iterator(i), []) => i.clone(),
(iterable, []) if iterable.is_iterable() => ctx.vm.make_iterator(iterable.clone())?,
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
};
let output = match iter_output_to_result(iter.next_back())? {
None => KValue::Null,
Some(output) => IteratorOutput::from(output).into(),
};
Ok(output)
});
result.add_fn("once", |ctx| {
let instance = ctx.instance();
if !(matches!(instance, &KValue::Null) || instance.type_as_string() == MODULE_NAME) {
return runtime_error!("iterator.once must be used as a standalone function");
}
match ctx.args() {
[value] => Ok(KIterator::new(generators::Once::new(value.clone())).into()),
unexpected => unexpected_args("|Any|", unexpected),
}
});
result.add_fn("peekable", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
Ok(peekable::Peekable::make_value(
ctx.vm.make_iterator(iterable)?,
))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("position", |ctx| {
let expected_error = "|Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
for (i, output) in ctx.vm.make_iterator(iterable)?.enumerate() {
let predicate_result = match output {
Output::Value(value) => ctx.vm.call_function(predicate.clone(), value),
Output::ValuePair(a, b) => ctx
.vm
.call_function(predicate.clone(), CallArgs::AsTuple(&[a, b])),
Output::Error(error) => return Err(error),
};
match predicate_result {
Ok(KValue::Bool(result)) => {
if result {
return Ok(i.into());
}
}
Ok(unexpected) => {
return unexpected_type(
"a Bool to be returned from the predicate",
&unexpected,
);
}
Err(error) => return Err(error),
}
}
Ok(KValue::Null)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("product", |ctx| {
let (iterable, initial_value) = {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => (iterable.clone(), KValue::Number(1.into())),
(iterable, [initial_value]) => (iterable.clone(), initial_value.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
}
};
fold_with_operator(ctx.vm, iterable, initial_value, BinaryOp::Multiply)
});
result.add_fn("repeat", |ctx| {
let instance = ctx.instance();
if !matches!(instance, &KValue::Null) && instance.type_as_string() != MODULE_NAME {
return runtime_error!("iterator.repeat must be used as a standalone function");
}
match ctx.args() {
[value] => {
let result = generators::Repeat::new(value.clone());
Ok(KIterator::new(result).into())
}
[value, KValue::Number(n)] if *n >= 0.0 => {
let result = generators::RepeatN::new(value.clone(), n.into());
Ok(KIterator::new(result).into())
}
unexpected => unexpected_args("|Any|, or |Any, Number >= 0|", unexpected),
}
});
result.add_fn("reversed", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
match adaptors::Reversed::new(ctx.vm.make_iterator(iterable)?) {
Ok(result) => Ok(KIterator::new(result).into()),
Err(e) => runtime_error!("iterator.reversed: {}", e),
}
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("skip", |ctx| {
let expected_error = "|Iterable, Number >= 0|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [KValue::Number(n)]) if *n >= 0.0 => {
let iterable = iterable.clone();
let n = *n;
let result = adaptors::Skip::new(ctx.vm.make_iterator(iterable)?, n.into());
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("step", |ctx| {
let expected_error = "|Iterable, Number|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [KValue::Number(n)]) => {
if *n > 0 {
let iterable = iterable.clone();
let step_size = n.into();
match adaptors::Step::new(ctx.vm.make_iterator(iterable)?, step_size) {
Ok(result) => Ok(KIterator::new(result).into()),
Err(e) => runtime_error!("iterator.step: {}", e),
}
} else {
runtime_error!("expected a non-negative number")
}
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("sum", |ctx| {
let (iterable, initial_value) = {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => (iterable.clone(), KValue::Number(0.into())),
(iterable, [initial_value]) => (iterable.clone(), initial_value.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
}
};
fold_with_operator(ctx.vm, iterable, initial_value, BinaryOp::Add)
});
result.add_fn("take", |ctx| {
let expected_error = "|Iterable, Number >= 0|, or |Iterable, |Any| -> Bool|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [KValue::Number(n)]) if *n >= 0.0 => {
let iterable = iterable.clone();
let n = *n;
let result = adaptors::Take::new(ctx.vm.make_iterator(iterable)?, n.into());
Ok(KIterator::new(result).into())
}
(iterable, [predicate]) if predicate.is_callable() => {
let iterable = iterable.clone();
let predicate = predicate.clone();
let result =
adaptors::TakeWhile::new(ctx.vm.make_iterator(iterable)?, predicate, ctx.vm);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("to_list", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let iterator = ctx.vm.make_iterator(iterable)?;
let (size_hint, _) = iterator.size_hint();
let mut result = ValueVec::with_capacity(size_hint);
for output in iterator.map(collect_pair) {
match output {
Output::Value(value) => result.push(value),
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(KValue::List(KList::with_data(result)))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("to_map", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let iterator = ctx.vm.make_iterator(iterable)?;
let (size_hint, _) = iterator.size_hint();
let mut result = ValueMap::with_capacity(size_hint);
for output in iterator {
let (key, value) = match output {
Output::ValuePair(key, value) => (key, value),
Output::Value(KValue::Tuple(t)) if t.len() == 2 => {
let key = t[0].clone();
let value = t[1].clone();
(key, value)
}
Output::Value(value) => (value, KValue::Null),
Output::Error(error) => return Err(error),
};
result.insert(ValueKey::try_from(key)?, value);
}
Ok(KValue::Map(KMap::with_data(result)))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("to_string", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let iterator = ctx.vm.make_iterator(iterable)?;
let (size_hint, _) = iterator.size_hint();
let mut display_context = DisplayContext::with_vm_and_capacity(ctx.vm, size_hint);
for output in iterator.map(collect_pair) {
match output {
Output::Value(KValue::Str(s)) => display_context.append(s),
Output::Value(value) => value.display(&mut display_context)?,
Output::Error(error) => return Err(error),
_ => unreachable!(),
};
}
Ok(display_context.result().into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("to_tuple", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, []) => {
let iterable = iterable.clone();
let iterator = ctx.vm.make_iterator(iterable)?;
let (size_hint, _) = iterator.size_hint();
let mut result = Vec::with_capacity(size_hint);
for output in iterator.map(collect_pair) {
match output {
Output::Value(value) => result.push(value),
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(KValue::Tuple(result.into()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("windows", |ctx| {
let expected_error = "|Iterable, Number|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable, [KValue::Number(n)]) => {
let iterable = iterable.clone();
let n = *n;
match adaptors::Windows::new(ctx.vm.make_iterator(iterable)?, n.into()) {
Ok(result) => Ok(KIterator::new(result).into()),
Err(e) => runtime_error!("iterator.windows: {}", e),
}
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("zip", |ctx| {
let expected_error = "|Iterable|";
match ctx.instance_and_args(KValue::is_iterable, expected_error)? {
(iterable_a, [iterable_b]) if iterable_b.is_iterable() => {
let iterable_a = iterable_a.clone();
let iterable_b = iterable_b.clone();
let result = adaptors::Zip::new(
ctx.vm.make_iterator(iterable_a)?,
ctx.vm.make_iterator(iterable_b)?,
);
Ok(KIterator::new(result).into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result
}
pub(crate) fn collect_pair(iterator_output: Output) -> Output {
match iterator_output {
Output::ValuePair(first, second) => {
Output::Value(KValue::Tuple(vec![first, second].into()))
}
_ => iterator_output,
}
}
pub(crate) fn iter_output_to_result(iterator_output: Option<Output>) -> Result<Option<KValue>> {
let output = match iterator_output {
Some(Output::Value(value)) => Some(value),
Some(Output::ValuePair(first, second)) => Some(KValue::Tuple(vec![first, second].into())),
Some(Output::Error(error)) => return Err(error),
None => None,
};
Ok(output)
}
#[derive(Clone, KotoCopy, KotoType)]
pub struct IteratorOutput(KValue);
#[koto_impl(runtime = crate)]
impl IteratorOutput {
#[koto_method]
pub fn get(&self) -> KValue {
self.0.clone()
}
}
impl KotoObject for IteratorOutput {
fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
ctx.append(Self::type_static());
ctx.append('(');
let mut wrapped_ctx = DisplayContext::default();
self.0.display(&mut wrapped_ctx)?;
ctx.append(wrapped_ctx.result());
ctx.append(')');
Ok(())
}
}
impl From<KValue> for IteratorOutput {
fn from(value: KValue) -> Self {
Self(value)
}
}
impl From<IteratorOutput> for KValue {
fn from(output: IteratorOutput) -> Self {
KObject::from(output).into()
}
}
fn fold_with_operator(
vm: &mut KotoVm,
iterable: KValue,
initial_value: KValue,
operator: BinaryOp,
) -> Result<KValue> {
let mut result = initial_value;
for output in vm.make_iterator(iterable)?.map(collect_pair) {
match output {
Output::Value(rhs_value) => {
result = vm.run_binary_op(operator, result, rhs_value)?;
}
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(result)
}
fn run_iterator_comparison(
vm: &mut KotoVm,
iterable: KValue,
invert_result: InvertResult,
) -> Result<KValue> {
let mut result: Option<KValue> = None;
for iter_output in vm.make_iterator(iterable)?.map(collect_pair) {
match iter_output {
Output::Value(value) => {
result = Some(match result {
Some(result) => {
compare_values(vm, result.clone(), value.clone(), invert_result)?
}
None => value,
})
}
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(result.unwrap_or_default())
}
fn run_iterator_comparison_by_key(
vm: &mut KotoVm,
iterable: KValue,
key_fn: KValue,
invert_result: InvertResult,
) -> Result<KValue> {
let mut result_and_key: Option<(KValue, KValue)> = None;
for iter_output in vm.make_iterator(iterable)?.map(collect_pair) {
match iter_output {
Output::Value(value) => {
let key = vm.call_function(key_fn.clone(), value.clone())?;
let value_and_key = (value, key);
result_and_key = Some(match result_and_key {
Some(result_and_key) => {
compare_values_with_key(vm, result_and_key, value_and_key, invert_result)?
}
None => value_and_key,
});
}
Output::Error(error) => return Err(error),
_ => unreachable!(),
}
}
Ok(result_and_key.map_or(KValue::Null, |(value, _)| value))
}
fn compare_values(
vm: &mut KotoVm,
a: KValue,
b: KValue,
invert_result: InvertResult,
) -> Result<KValue> {
use InvertResult::*;
use KValue::Bool;
let comparison_result = vm.run_binary_op(BinaryOp::Less, a.clone(), b.clone())?;
match (comparison_result, invert_result) {
(Bool(true), No) => Ok(a),
(Bool(false), No) => Ok(b),
(Bool(true), Yes) => Ok(b),
(Bool(false), Yes) => Ok(a),
(other, _) => runtime_error!(
"Expected Bool from '<' comparison, found '{}'",
other.type_as_string()
),
}
}
fn compare_values_with_key(
vm: &mut KotoVm,
a_and_key: (KValue, KValue),
b_and_key: (KValue, KValue),
invert_result: InvertResult,
) -> Result<(KValue, KValue)> {
use InvertResult::*;
use KValue::Bool;
let comparison_result =
vm.run_binary_op(BinaryOp::Less, a_and_key.1.clone(), b_and_key.1.clone())?;
match (comparison_result, invert_result) {
(Bool(true), No) => Ok(a_and_key),
(Bool(false), No) => Ok(b_and_key),
(Bool(true), Yes) => Ok(b_and_key),
(Bool(false), Yes) => Ok(a_and_key),
(other, _) => runtime_error!(
"Expected Bool from '<' comparison, found '{}'",
other.type_as_string()
),
}
}
#[derive(Clone, Copy)]
enum InvertResult {
Yes,
No,
}