use crate::eval::calc_index;
use crate::plugin::*;
use crate::{
def_package, ExclusiveRange, ImmutableString, InclusiveRange, Position, RhaiResultOf, ERR, INT,
INT_BITS,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
any::{type_name, TypeId},
cmp::Ordering,
convert::TryFrom,
fmt::Debug,
iter::{ExactSizeIterator, FusedIterator},
ops::{Range, RangeInclusive},
vec::IntoIter,
};
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
#[cfg(feature = "decimal")]
use rust_decimal::Decimal;
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
#[allow(clippy::needless_pass_by_value)]
fn std_add<T>(x: T, y: T) -> Option<T>
where
T: num_traits::CheckedAdd<Output = T>,
{
x.checked_add(&y)
}
#[inline(always)]
#[allow(dead_code)]
#[allow(clippy::unnecessary_wraps, clippy::needless_pass_by_value)]
fn regular_add<T>(x: T, y: T) -> Option<T>
where
T: std::ops::Add<Output = T>,
{
Some(x + y)
}
#[allow(unpredictable_function_pointer_comparisons)]
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct StepRange<T> {
pub from: T,
pub to: T,
pub step: T,
pub add: fn(T, T) -> Option<T>,
pub dir: i8,
}
impl<T: Debug> Debug for StepRange<T> {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("StepRange<{}>", type_name::<T>()))
.field(&self.from)
.field(&self.to)
.field(&self.step)
.finish()
}
}
impl<T: Copy + PartialOrd> StepRange<T> {
pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option<T>) -> RhaiResultOf<Self> {
let mut dir = 0;
if let Some(n) = add(from, step) {
#[cfg(not(feature = "unchecked"))]
if n == from {
return Err(ERR::ErrorInFunctionCall(
"range".to_string(),
String::new(),
ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)
.into(),
Position::NONE,
)
.into());
}
match from.partial_cmp(&to).unwrap_or(Ordering::Equal) {
Ordering::Less if n > from => dir = 1,
Ordering::Greater if n < from => dir = -1,
_ => (),
}
}
Ok(Self {
from,
to,
step,
add,
dir,
})
}
}
impl<T: Copy + PartialOrd> Iterator for StepRange<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.dir == 0 {
return None;
}
let v = self.from;
self.from = (self.add)(self.from, self.step)?;
match self.dir.cmp(&0) {
Ordering::Greater if self.from >= self.to => self.dir = 0,
Ordering::Less if self.from <= self.to => self.dir = 0,
Ordering::Equal => unreachable!("`dir` != 0"),
_ => (),
}
Some(v)
}
}
impl<T: Copy + PartialOrd> FusedIterator for StepRange<T> {}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct BitRange(INT, usize);
impl BitRange {
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
let from = calc_index(INT_BITS, from, true, || {
ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
})?;
let len = if len < 0 {
0
} else if let Ok(len) = usize::try_from(len) {
if len.checked_add(from).map_or(true, |x| x > INT_BITS) {
INT_BITS - from
} else {
len
}
} else {
INT_BITS - from
};
Ok(Self(value >> from, len))
}
}
impl Iterator for BitRange {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.1 == 0 {
None
} else {
let r = (self.0 & 0x0001) != 0;
self.0 >>= 1;
self.1 -= 1;
Some(r)
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.1, Some(self.1))
}
}
impl FusedIterator for BitRange {}
impl ExactSizeIterator for BitRange {
#[inline(always)]
fn len(&self) -> usize {
self.1
}
}
#[derive(Debug, Clone)]
pub struct CharsStream(IntoIter<char>);
impl CharsStream {
pub fn new(string: &str, from: INT, len: INT) -> Self {
if len <= 0 {
return Self(Vec::new().into_iter());
}
let len = usize::try_from(len).unwrap_or(usize::MAX);
if from >= 0 {
let from = usize::try_from(from).unwrap_or(usize::MAX);
return Self(
string
.chars()
.skip(from)
.take(len)
.collect::<Vec<_>>()
.into_iter(),
);
}
let abs_from = usize::try_from(from.unsigned_abs()).unwrap_or(usize::MAX);
let num_chars = string.chars().count();
let offset = num_chars.saturating_sub(abs_from);
Self(
string
.chars()
.skip(offset)
.take(len)
.collect::<Vec<_>>()
.into_iter(),
)
}
}
impl Iterator for CharsStream {
type Item = char;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl FusedIterator for CharsStream {}
impl ExactSizeIterator for CharsStream {
#[inline(always)]
fn len(&self) -> usize {
self.0.len()
}
}
macro_rules! reg_range {
($lib:ident => $( $arg_type:ty ),*) => {
$({
$lib.set_iterator::<Range<$arg_type>>();
#[export_module]
mod range_function {
pub const fn range (from: $arg_type, to: $arg_type) -> Range<$arg_type> {
from..to
}
}
combine_with_exported_module!($lib, stringify!($arg_type), range_function);
$lib.set_iterator::<RangeInclusive<$arg_type>>();
})*
};
($lib:ident |> $( $arg_type:ty ),*) => {
#[cfg(not(feature = "unchecked"))]
reg_range!($lib |> std_add => $( $arg_type ),*);
#[cfg(feature = "unchecked")]
reg_range!($lib |> regular_add => $( $arg_type ),*);
};
($lib:ident |> $add:ident => $( $arg_type:ty ),*) => {
$({
$lib.set_iterator::<StepRange<$arg_type>>();
#[export_module]
mod range_functions {
#[rhai_fn(name = "range", return_raw)]
pub fn range_from_to_stepped (from: $arg_type, to: $arg_type, step: $arg_type) -> RhaiResultOf<StepRange<$arg_type>> {
StepRange::new(from, to, step, $add)
}
#[rhai_fn(name = "range", return_raw)]
pub fn range_stepped (range: std::ops::Range<$arg_type>, step: $arg_type) -> RhaiResultOf<StepRange<$arg_type>> {
StepRange::new(range.start, range.end, step, $add)
}
}
combine_with_exported_module!($lib, stringify!($arg_type), range_functions);
})*
};
}
def_package! {
pub BasicIteratorPackage(lib) {
lib.set_standard_lib(true);
#[cfg(not(feature = "no_index"))]
{
lib.set_iterable::<crate::Array>();
lib.set_iterable::<crate::Blob>();
}
lib.set_iter(TypeId::of::<ImmutableString>(), |value| Box::new(
CharsStream::new(value.cast::<ImmutableString>().as_str(), 0, INT::MAX).map(Into::into)
));
lib.set_iterator::<CharsStream>();
lib.set_iterator::<BitRange>();
reg_range!(lib => INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_range!(lib => i8, u8, i16, u16, i32, u32, i64, u64);
#[cfg(not(target_family = "wasm"))]
reg_range!(lib => i128, u128);
}
reg_range!(lib |> INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_range!(lib |> i8, u8, i16, u16, i32, u32, i64, u64);
#[cfg(not(target_family = "wasm"))]
reg_range!(lib |> i128, u128);
}
#[cfg(not(feature = "no_float"))]
reg_range!(lib |> regular_add => FLOAT);
#[cfg(feature = "decimal")]
reg_range!(lib |> Decimal);
combine_with_exported_module!(lib, "iterator", iterator_functions);
combine_with_exported_module!(lib, "range", range_functions);
}
}
#[export_module]
mod iterator_functions {
#[rhai_fn(name = "chars")]
pub fn chars_from_exclusive_range(string: &str, range: ExclusiveRange) -> CharsStream {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
CharsStream::new(string, from, to - from)
}
#[rhai_fn(name = "chars")]
pub fn chars_from_inclusive_range(string: &str, range: InclusiveRange) -> CharsStream {
let from = INT::max(*range.start(), 0);
let to = INT::min(INT::max(*range.end(), from - 1), INT::MAX - 1);
CharsStream::new(string, from, to - from + 1)
}
#[rhai_fn(name = "chars")]
pub fn chars_from_start_len(string: &str, start: INT, len: INT) -> CharsStream {
CharsStream::new(string, start, len)
}
#[rhai_fn(name = "chars")]
pub fn chars_from_start(string: &str, start: INT) -> CharsStream {
CharsStream::new(string, start, INT::MAX)
}
#[rhai_fn(name = "chars")]
pub fn chars(string: &str) -> CharsStream {
CharsStream::new(string, 0, INT::MAX)
}
#[cfg(not(feature = "no_object"))]
#[rhai_fn(get = "chars")]
pub fn get_chars(string: &str) -> CharsStream {
CharsStream::new(string, 0, INT::MAX)
}
#[rhai_fn(name = "bits", return_raw)]
pub fn bits_from_exclusive_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<BitRange> {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
BitRange::new(value, from, to - from)
}
#[rhai_fn(name = "bits", return_raw)]
pub fn bits_from_inclusive_range(value: INT, range: InclusiveRange) -> RhaiResultOf<BitRange> {
let from = INT::max(*range.start(), 0);
let to = INT::min(INT::max(*range.end(), from - 1), INT::MAX - 1);
BitRange::new(value, from, to - from + 1)
}
#[rhai_fn(name = "bits", return_raw)]
pub fn bits_from_start_and_len(value: INT, from: INT, len: INT) -> RhaiResultOf<BitRange> {
BitRange::new(value, from, len)
}
#[rhai_fn(name = "bits", return_raw)]
pub fn bits_from_start(value: INT, from: INT) -> RhaiResultOf<BitRange> {
BitRange::new(value, from, INT::MAX)
}
#[rhai_fn(name = "bits", return_raw)]
pub fn bits(value: INT) -> RhaiResultOf<BitRange> {
BitRange::new(value, 0, INT::MAX)
}
#[cfg(not(feature = "no_object"))]
#[rhai_fn(get = "bits", return_raw)]
pub fn get_bits(value: INT) -> RhaiResultOf<BitRange> {
BitRange::new(value, 0, INT::MAX)
}
}
#[export_module]
mod range_functions {
#[rhai_fn(get = "start", name = "start", pure)]
pub fn start(range: &mut ExclusiveRange) -> INT {
range.start
}
#[rhai_fn(get = "end", name = "end", pure)]
pub fn end(range: &mut ExclusiveRange) -> INT {
range.end
}
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
let _ = range;
false
}
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
let _ = range;
true
}
#[rhai_fn(get = "is_empty", name = "is_empty", pure)]
#[allow(unstable_name_collisions)]
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
range.is_empty()
}
#[rhai_fn(name = "contains")]
pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool {
range.contains(&value)
}
#[rhai_fn(get = "start", name = "start", pure)]
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
*range.start()
}
#[rhai_fn(get = "end", name = "end", pure)]
pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
*range.end()
}
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
let _ = range;
true
}
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
let _ = range;
false
}
#[rhai_fn(get = "is_empty", name = "is_empty", pure)]
pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool {
range.is_empty()
}
#[rhai_fn(name = "contains")]
pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool {
range.contains(&value)
}
}