diff --git a/src/api/type_names.rs b/src/api/type_names.rs index ea76ffa4..de6961c1 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -86,7 +86,7 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { } #[cfg(not(feature = "no_float"))] - if name == type_name::() { + if name == type_name::>() { return if shorthands { "range" } else { @@ -94,7 +94,7 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { }; } #[cfg(feature = "decimal")] - if name == type_name::() { + if name == type_name::>() { return if shorthands { "range" } else { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 7e8b6af8..5627c834 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,34 +1,59 @@ use crate::eval::calc_index; use crate::plugin::*; -use crate::types::dynamic::Variant; use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + any::type_name, + cmp::Ordering, + fmt::Debug, iter::{ExactSizeIterator, FusedIterator}, ops::{Range, RangeInclusive}, }; #[cfg(not(feature = "unchecked"))] -use num_traits::{CheckedAdd as Add, CheckedSub as Sub}; - -#[cfg(feature = "unchecked")] -use std::ops::{Add, Sub}; +#[inline(always)] +fn std_add(x: T, y: T) -> Option +where + T: Debug + Copy + PartialOrd + num_traits::CheckedAdd, +{ + x.checked_add(&y) +} +#[inline(always)] +fn regular_add(x: T, y: T) -> Option +where + T: Debug + Copy + PartialOrd + std::ops::Add, +{ + Some(x + y) +} // Range iterator with step -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub struct StepRange(T, T, T) -where - T: Variant + Copy + PartialOrd + Add + Sub; +#[derive(Clone, Copy, Hash, Eq, PartialEq)] +pub struct StepRange { + pub from: T, + pub to: T, + pub step: T, + pub add: fn(T, T) -> Option, + pub dir: i8, +} -impl StepRange -where - T: Variant + Copy + PartialOrd + Add + Sub, -{ - pub fn new(from: T, to: T, step: T) -> RhaiResultOf { - #[cfg(not(feature = "unchecked"))] - if let Some(r) = from.checked_add(&step) { - if r == from { +impl Debug for StepRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple(&format!("StepRange<{}>", type_name::())) + .field(&self.from) + .field(&self.to) + .field(&self.step) + .finish() + } +} + +impl StepRange { + pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option) -> RhaiResultOf { + let mut dir = 0; + + if let Some(n) = add(from, step) { + #[cfg(not(feature = "unchecked"))] + if n == from { return Err(crate::ERR::ErrorInFunctionCall( "range".to_string(), String::new(), @@ -41,77 +66,53 @@ where ) .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)) + Ok(Self { + from, + to, + step, + add, + dir, + }) } } -impl Iterator for StepRange -where - T: Variant + Copy + PartialOrd + Add + Sub, -{ +impl Iterator for StepRange { type Item = T; fn next(&mut self) -> Option { - if self.0 == self.1 { - None - } else if self.0 < self.1 { - #[cfg(not(feature = "unchecked"))] - let diff1 = self.1.checked_sub(&self.0)?; - #[cfg(feature = "unchecked")] - let diff1 = self.1 - self.0; + if self.dir == 0 { + return None; + } - let v = self.0; + let v = self.from; - #[cfg(not(feature = "unchecked"))] - let n = self.0.checked_add(&self.2)?; - #[cfg(feature = "unchecked")] - let n = self.0 + self.2; + self.from = (self.add)(self.from, self.step)?; - #[cfg(not(feature = "unchecked"))] - let diff2 = self.1.checked_sub(&n)?; - #[cfg(feature = "unchecked")] - let diff2 = self.1 - n; - - if diff2 >= diff1 { - None - } else { - self.0 = if n >= self.1 { self.1 } else { n }; - Some(v) + if self.dir > 0 { + if self.from >= self.to { + self.dir = 0; + } + } else if self.dir < 0 { + if self.from <= self.to { + self.dir = 0; } } else { - #[cfg(not(feature = "unchecked"))] - let diff1 = self.0.checked_sub(&self.1)?; - #[cfg(feature = "unchecked")] - let diff1 = self.0 - self.1; - - let v = self.0; - - #[cfg(not(feature = "unchecked"))] - let n = self.0.checked_add(&self.2)?; - #[cfg(feature = "unchecked")] - let n = self.0 + self.2; - - #[cfg(not(feature = "unchecked"))] - let diff2 = n.checked_sub(&self.1)?; - #[cfg(feature = "unchecked")] - let diff2 = n - self.1; - - if diff2 >= diff1 { - None - } else { - self.0 = if n <= self.1 { self.1 } else { n }; - Some(v) - } + unreachable!(); } + + Some(v) } } -impl FusedIterator for StepRange where - T: Variant + Copy + PartialOrd + Add + Sub -{ -} +impl FusedIterator for StepRange {} // Bit-field iterator with step #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] @@ -237,134 +238,6 @@ impl ExactSizeIterator for CharsStream { } } -#[cfg(not(feature = "no_float"))] -pub mod float { - use super::*; - use crate::FLOAT; - - #[derive(Debug, Clone, Copy, PartialEq)] - pub struct StepFloatRange(FLOAT, FLOAT, FLOAT); - - impl StepFloatRange { - pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> RhaiResultOf { - #[cfg(not(feature = "unchecked"))] - if step == 0.0 { - return Err(crate::ERR::ErrorInFunctionCall( - "range".to_string(), - "".to_string(), - crate::ERR::ErrorArithmetic( - "step value cannot be zero".to_string(), - Position::NONE, - ) - .into(), - Position::NONE, - ) - .into()); - } - - Ok(Self(from, to, step)) - } - } - - impl Iterator for StepFloatRange { - type Item = FLOAT; - - fn next(&mut self) -> Option { - if self.0 == self.1 { - None - } else if self.0 < self.1 { - #[cfg(not(feature = "unchecked"))] - if self.2 < 0.0 { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n >= self.1 { self.1 } else { n }; - Some(v) - } else { - #[cfg(not(feature = "unchecked"))] - if self.2 > 0.0 { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n <= self.1 { self.1 } else { n }; - Some(v) - } - } - } - - impl FusedIterator for StepFloatRange {} -} - -#[cfg(feature = "decimal")] -pub mod decimal { - use super::*; - use rust_decimal::Decimal; - - #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] - pub struct StepDecimalRange(Decimal, Decimal, Decimal); - - impl StepDecimalRange { - pub fn new(from: Decimal, to: Decimal, step: Decimal) -> RhaiResultOf { - #[cfg(not(feature = "unchecked"))] - if step.is_zero() { - return Err(crate::ERR::ErrorInFunctionCall( - "range".to_string(), - "".to_string(), - crate::ERR::ErrorArithmetic( - "step value cannot be zero".to_string(), - Position::NONE, - ) - .into(), - Position::NONE, - ) - .into()); - } - - Ok(Self(from, to, step)) - } - } - - impl Iterator for StepDecimalRange { - type Item = Decimal; - - fn next(&mut self) -> Option { - if self.0 == self.1 { - None - } else if self.0 < self.1 { - #[cfg(not(feature = "unchecked"))] - if self.2.is_sign_negative() { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n >= self.1 { self.1 } else { n }; - Some(v) - } else { - #[cfg(not(feature = "unchecked"))] - if self.2.is_sign_positive() { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n <= self.1 { self.1 } else { n }; - Some(v) - } - } - } - - impl FusedIterator for StepDecimalRange {} -} - macro_rules! reg_range { ($lib:ident | $x:expr => $( $y:ty ),*) => { $( @@ -377,11 +250,13 @@ macro_rules! reg_range { concat!("to: ", stringify!($y)), concat!("Iterator"), ], [ - "/// Return an iterator over the range of `from..to`.", + "/// Return an iterator over the exclusive range of `from..to`.", + "/// The value `to` is never included.", "///", "/// # Example", "///", "/// ```rhai", + "/// // prints all values from 8 to 17", "/// for n in range(8, 18) {", "/// print(n);", "/// }", @@ -392,9 +267,15 @@ macro_rules! reg_range { )* }; ($lib:ident | step $x:expr => $( $y:ty ),*) => { + #[cfg(not(feature = "unchecked"))] + reg_range!($lib | step(std_add) $x => $( $y ),*); + #[cfg(feature = "unchecked")] + reg_range!($lib | step(regular_add) $x => $( $y ),*); + }; + ($lib:ident | step ( $add:ident ) $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step)); + let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step, $add)); #[cfg(feature = "metadata")] $lib.update_fn_metadata_with_comments(_hash, [ @@ -403,17 +284,22 @@ macro_rules! reg_range { concat!("step: ", stringify!($y)), concat!("Iterator") ], [ - "/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.", + "/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.", + "/// The value `to` is never included.", "///", "/// If `from` > `to` and `step` < 0, the iteration goes backwards.", "///", + "/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned.", + "///", "/// # Example", "///", "/// ```rhai", + "/// // prints all values from 8 to 17 in steps of 3", "/// for n in range(8, 18, 3) {", "/// print(n);", "/// }", "///", + "/// // prints all values down from 18 to 9 in steps of -3", "/// for n in range(18, 8, -3) {", "/// print(n);", "/// }", @@ -436,7 +322,6 @@ def_package! { reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); #[cfg(not(target_family = "wasm"))] - reg_range!(lib | "range" => i128, u128); } @@ -448,63 +333,22 @@ def_package! { reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); #[cfg(not(target_family = "wasm"))] - reg_range!(lib | step "range" => i128, u128); } #[cfg(not(feature = "no_float"))] - { - lib.set_iterator::(); - - let _hash = lib.set_native_fn("range", float::StepFloatRange::new); - #[cfg(feature = "metadata")] - lib.update_fn_metadata_with_comments( - _hash, - ["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator"], - [ - "/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.", - "///", - "/// If `from` > `to` and `step` < 0, the iteration goes backwards.", - "///", - "/// # Example", - "///", - "/// ```rhai", - "/// for n in range(8.0, 18.0, 3.0) {", - "/// print(n);", - "/// }", - "///", - "/// for n in range(18.0, 8.0, -3.0) {", - "/// print(n);", - "/// }", - "/// ```" - ] - ); - } + reg_range!(lib | step(regular_add) "range" => crate::FLOAT); #[cfg(feature = "decimal")] - { - lib.set_iterator::(); - - let _hash = lib.set_native_fn("range", decimal::StepDecimalRange::new); - #[cfg(feature = "metadata")] - lib.update_fn_metadata_with_comments( - _hash, - ["from: Decimal", "to: Decimal", "step: Decimal", "Iterator"], - [ - "/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.", - "///", - "/// If `from` > `to` and `step` < 0, the iteration goes backwards.", - ] - ); - } + reg_range!(lib | step "range" => rust_decimal::Decimal); // Register string iterator lib.set_iterator::(); #[cfg(feature = "metadata")] let (range_type, range_inclusive_type) = ( - format!("range: Range<{}>", std::any::type_name::()), - format!("range: RangeInclusive<{}>", std::any::type_name::()), + format!("range: Range<{}>", type_name::()), + format!("range: RangeInclusive<{}>", type_name::()), ); let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| {