Skip to main content

spo_rhai/packages/
iter_basic.rs

1use crate::eval::calc_index;
2use crate::module::ModuleFlags;
3use crate::plugin::*;
4use crate::FuncRegistration;
5use crate::{
6    def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT, INT_BITS, MAX_USIZE_INT,
7};
8#[cfg(feature = "no_std")]
9use std::prelude::v1::*;
10use std::{
11    any::type_name,
12    cmp::Ordering,
13    fmt::Debug,
14    iter::{ExactSizeIterator, FusedIterator},
15    ops::{Range, RangeInclusive},
16    vec::IntoIter,
17};
18
19#[cfg(not(feature = "no_float"))]
20use crate::FLOAT;
21
22#[cfg(feature = "decimal")]
23use rust_decimal::Decimal;
24
25#[cfg(not(feature = "unchecked"))]
26#[inline(always)]
27#[allow(clippy::needless_pass_by_value)]
28fn std_add<T>(x: T, y: T) -> Option<T>
29where
30    T: num_traits::CheckedAdd<Output = T>,
31{
32    x.checked_add(&y)
33}
34#[inline(always)]
35#[allow(dead_code)]
36#[allow(clippy::unnecessary_wraps, clippy::needless_pass_by_value)]
37fn regular_add<T>(x: T, y: T) -> Option<T>
38where
39    T: std::ops::Add<Output = T>,
40{
41    Some(x + y)
42}
43
44// Range iterator with step
45#[derive(Clone, Hash, Eq, PartialEq)]
46pub struct StepRange<T> {
47    pub from: T,
48    pub to: T,
49    pub step: T,
50    pub add: fn(T, T) -> Option<T>,
51    pub dir: i8,
52}
53
54impl<T: Debug> Debug for StepRange<T> {
55    #[cold]
56    #[inline(never)]
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        f.debug_tuple(&format!("StepRange<{}>", type_name::<T>()))
59            .field(&self.from)
60            .field(&self.to)
61            .field(&self.step)
62            .finish()
63    }
64}
65
66impl<T: Copy + PartialOrd> StepRange<T> {
67    pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option<T>) -> RhaiResultOf<Self> {
68        let mut dir = 0;
69
70        if let Some(n) = add(from, step) {
71            #[cfg(not(feature = "unchecked"))]
72            if n == from {
73                return Err(ERR::ErrorInFunctionCall(
74                    "range".to_string(),
75                    String::new(),
76                    ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)
77                        .into(),
78                    Position::NONE,
79                )
80                .into());
81            }
82
83            match from.partial_cmp(&to).unwrap_or(Ordering::Equal) {
84                Ordering::Less if n > from => dir = 1,
85                Ordering::Greater if n < from => dir = -1,
86                _ => (),
87            }
88        }
89
90        Ok(Self {
91            from,
92            to,
93            step,
94            add,
95            dir,
96        })
97    }
98}
99
100impl<T: Copy + PartialOrd> Iterator for StepRange<T> {
101    type Item = T;
102
103    fn next(&mut self) -> Option<T> {
104        if self.dir == 0 {
105            return None;
106        }
107
108        let v = self.from;
109
110        self.from = (self.add)(self.from, self.step)?;
111
112        match self.dir.cmp(&0) {
113            Ordering::Greater if self.from >= self.to => self.dir = 0,
114            Ordering::Less if self.from <= self.to => self.dir = 0,
115            Ordering::Equal => unreachable!("`dir` != 0"),
116            _ => (),
117        }
118
119        Some(v)
120    }
121}
122
123impl<T: Copy + PartialOrd> FusedIterator for StepRange<T> {}
124
125// Bit-field iterator with step
126#[derive(Debug, Clone, Hash, Eq, PartialEq)]
127pub struct BitRange(INT, usize);
128
129impl BitRange {
130    pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
131        let from = calc_index(INT_BITS, from, true, || {
132            ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
133        })?;
134
135        #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
136        let len = if len < 0 {
137            0
138        } else if from + (len as usize) > INT_BITS {
139            INT_BITS - from
140        } else {
141            len as usize
142        };
143
144        Ok(Self(value >> from, len))
145    }
146}
147
148impl Iterator for BitRange {
149    type Item = bool;
150
151    fn next(&mut self) -> Option<Self::Item> {
152        if self.1 == 0 {
153            None
154        } else {
155            let r = (self.0 & 0x0001) != 0;
156            self.0 >>= 1;
157            self.1 -= 1;
158            Some(r)
159        }
160    }
161
162    #[inline(always)]
163    fn size_hint(&self) -> (usize, Option<usize>) {
164        (self.1, Some(self.1))
165    }
166}
167
168impl FusedIterator for BitRange {}
169
170impl ExactSizeIterator for BitRange {
171    #[inline(always)]
172    fn len(&self) -> usize {
173        self.1
174    }
175}
176
177// String iterator over characters
178#[derive(Debug, Clone)]
179pub struct CharsStream(IntoIter<char>);
180
181impl CharsStream {
182    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
183    pub fn new(string: &str, from: INT, len: INT) -> Self {
184        if len <= 0 || from > MAX_USIZE_INT {
185            return Self(Vec::new().into_iter());
186        }
187        let len = len.min(MAX_USIZE_INT) as usize;
188
189        if from >= 0 {
190            return Self(
191                string
192                    .chars()
193                    .skip(from as usize)
194                    .take(len)
195                    .collect::<Vec<_>>()
196                    .into_iter(),
197            );
198        }
199
200        let abs_from = from.unsigned_abs() as usize;
201        let num_chars = string.chars().count();
202        let offset = if num_chars < abs_from {
203            0
204        } else {
205            num_chars - abs_from
206        };
207        Self(
208            string
209                .chars()
210                .skip(offset)
211                .take(len)
212                .collect::<Vec<_>>()
213                .into_iter(),
214        )
215    }
216}
217
218impl Iterator for CharsStream {
219    type Item = char;
220
221    #[inline(always)]
222    fn next(&mut self) -> Option<Self::Item> {
223        self.0.next()
224    }
225
226    #[inline(always)]
227    fn size_hint(&self) -> (usize, Option<usize>) {
228        self.0.size_hint()
229    }
230}
231
232impl FusedIterator for CharsStream {}
233
234impl ExactSizeIterator for CharsStream {
235    #[inline(always)]
236    fn len(&self) -> usize {
237        self.0.len()
238    }
239}
240
241macro_rules! reg_range {
242    ($lib:ident | $x:expr => $( $y:ty ),*) => {
243        $(
244            $lib.set_iterator::<Range<$y>>();
245
246            let f = FuncRegistration::new($x);
247
248            #[cfg(feature = "metadata")]
249            let f = f.with_params_info([
250                concat!("from: ", stringify!($y)),
251                concat!("to: ", stringify!($y)),
252                concat!("Iterator<", stringify!($y), ">"),
253            ]).with_comments(["\
254                /// Return an iterator over the exclusive range of `from..to`.\n\
255                /// The value `to` is never included.\n\
256                ///\n\
257                /// # Example\n\
258                ///\n\
259                /// ```rhai\n\
260                /// // prints all values from 8 to 17\n\
261                /// for n in range(8, 18) {\n\
262                ///     print(n);\n\
263                /// }\n\
264                /// ```"
265            ]);
266
267            f.set_into_module($lib, |from: $y, to: $y| from..to);
268
269            $lib.set_iterator::<RangeInclusive<$y>>();
270        )*
271    };
272    ($lib:ident | step $x:expr => $( $y:ty ),*) => {
273        #[cfg(not(feature = "unchecked"))]
274        reg_range!($lib | step(std_add) $x => $( $y ),*);
275        #[cfg(feature = "unchecked")]
276        reg_range!($lib | step(regular_add) $x => $( $y ),*);
277    };
278    ($lib:ident | step ( $add:ident ) $x:expr => $( $y:ty ),*) => {
279        $(
280            $lib.set_iterator::<StepRange<$y>>();
281
282            let f = FuncRegistration::new($x);
283
284            #[cfg(feature = "metadata")]
285            let f = f.with_params_info([
286                concat!("from: ", stringify!($y)),
287                concat!("to: ", stringify!($y)),
288                concat!("step: ", stringify!($y)),
289                concat!("Iterator<", stringify!($y), ">")
290            ]).with_comments(["\
291                /// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.\n\
292                /// The value `to` is never included.\n\
293                ///\n\
294                /// If `from` > `to` and `step` < 0, iteration goes backwards.\n\
295                ///\n\
296                /// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned.\n\
297                ///\n\
298                /// # Example\n\
299                ///\n\
300                /// ```rhai\n\
301                /// // prints all values from 8 to 17 in steps of 3\n\
302                /// for n in range(8, 18, 3) {\n\
303                ///     print(n);\n\
304                /// }\n\
305                ///\n\
306                /// // prints all values down from 18 to 9 in steps of -3\n\
307                /// for n in range(18, 8, -3) {\n\
308                ///     print(n);\n\
309                /// }\n\
310                /// ```"
311            ]);
312
313            f.set_into_module($lib, |from: $y, to: $y, step: $y| StepRange::new(from, to, step, $add));
314
315            let f = FuncRegistration::new($x);
316
317            #[cfg(feature = "metadata")]
318            let f = f.with_params_info([
319                concat!("range: Range<", stringify!($y), ">"),
320                concat!("step: ", stringify!($y)),
321                concat!("Iterator<", stringify!($y), ">")
322            ]).with_comments(["\
323                /// Return an iterator over an exclusive range, each iteration increasing by `step`.\n\
324                ///\n\
325                /// If `range` is reversed and `step` < 0, iteration goes backwards.\n\
326                ///\n\
327                /// Otherwise, if `range` is empty, an empty iterator is returned.\n\
328                ///\n\
329                /// # Example\n\
330                ///\n\
331                /// ```rhai\n\
332                /// // prints all values from 8 to 17 in steps of 3\n\
333                /// for n in range(8..18, 3) {\n\
334                ///     print(n);\n\
335                /// }\n\
336                ///\n\
337                /// // prints all values down from 18 to 9 in steps of -3\n\
338                /// for n in range(18..8, -3) {\n\
339                ///     print(n);\n\
340                /// }\n\
341                /// ```"
342            ]);
343
344            f.set_into_module($lib, |range: std::ops::Range<$y>, step: $y| StepRange::new(range.start, range.end, step, $add));
345        )*
346    };
347}
348
349def_package! {
350    /// Package of basic range iterators
351    pub BasicIteratorPackage(lib) {
352        lib.flags |= ModuleFlags::STANDARD_LIB;
353
354        reg_range!(lib | "range" => INT);
355
356        #[cfg(not(feature = "only_i32"))]
357        #[cfg(not(feature = "only_i64"))]
358        {
359            reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
360
361            #[cfg(not(target_family = "wasm"))]
362            reg_range!(lib | "range" => i128, u128);
363        }
364
365        reg_range!(lib | step "range" => INT);
366
367        #[cfg(not(feature = "only_i32"))]
368        #[cfg(not(feature = "only_i64"))]
369        {
370            reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
371
372            #[cfg(not(target_family = "wasm"))]
373            reg_range!(lib | step "range" => i128, u128);
374        }
375
376        #[cfg(not(feature = "no_float"))]
377        reg_range!(lib | step(regular_add) "range" => FLOAT);
378
379        #[cfg(feature = "decimal")]
380        reg_range!(lib | step "range" => Decimal);
381
382        // Register string iterator
383        lib.set_iterator::<CharsStream>();
384
385        // Register bit-field iterator
386        lib.set_iterator::<BitRange>();
387
388        // Register iterator functions
389        combine_with_exported_module!(lib, "iterator", iterator_functions);
390        combine_with_exported_module!(lib, "range", range_functions);
391    }
392}
393
394#[export_module]
395mod iterator_functions {
396    /// Return an iterator over an exclusive range of characters in the string.
397    ///
398    /// # Example
399    ///
400    /// ```rhai
401    /// for ch in "hello, world!".chars(2..5) {
402    ///     print(ch);
403    /// }
404    /// ```
405    #[rhai_fn(name = "chars")]
406    pub fn chars_from_exclusive_range(string: &str, range: ExclusiveRange) -> CharsStream {
407        let from = INT::max(range.start, 0);
408        let to = INT::max(range.end, from);
409        CharsStream::new(string, from, to - from)
410    }
411    /// Return an iterator over an inclusive range of characters in the string.
412    ///
413    /// # Example
414    ///
415    /// ```rhai
416    /// for ch in "hello, world!".chars(2..=6) {
417    ///     print(ch);
418    /// }
419    /// ```
420    #[rhai_fn(name = "chars")]
421    pub fn chars_from_inclusive_range(string: &str, range: InclusiveRange) -> CharsStream {
422        let from = INT::max(*range.start(), 0);
423        let to = INT::max(*range.end(), from - 1);
424        CharsStream::new(string, from, to - from + 1)
425    }
426    /// Return an iterator over a portion of characters in the string.
427    ///
428    /// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
429    /// * If `start` < -length of string, position counts from the beginning of the string.
430    /// * If `start` ≥ length of string, an empty iterator is returned.
431    /// * If `len` ≤ 0, an empty iterator is returned.
432    /// * If `start` position + `len` ≥ length of string, all characters of the string after the `start` position are iterated.
433    ///
434    /// # Example
435    ///
436    /// ```rhai
437    /// for ch in "hello, world!".chars(2, 4) {
438    ///     print(ch);
439    /// }
440    /// ```
441    #[rhai_fn(name = "chars")]
442    pub fn chars_from_start_len(string: &str, start: INT, len: INT) -> CharsStream {
443        CharsStream::new(string, start, len)
444    }
445    /// Return an iterator over the characters in the string starting from the `start` position.
446    ///
447    /// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
448    /// * If `start` < -length of string, position counts from the beginning of the string.
449    /// * If `start` ≥ length of string, an empty iterator is returned.
450    ///
451    /// # Example
452    ///
453    /// ```rhai
454    /// for ch in "hello, world!".chars(2) {
455    ///     print(ch);
456    /// }
457    /// ```
458    #[rhai_fn(name = "chars")]
459    pub fn chars_from_start(string: &str, start: INT) -> CharsStream {
460        CharsStream::new(string, start, INT::MAX)
461    }
462    /// Return an iterator over the characters in the string.
463    ///
464    /// # Example
465    ///
466    /// ```rhai
467    /// for ch in "hello, world!".chars() {
468    ///     print(ch);
469    /// }
470    /// ```
471    #[rhai_fn(name = "chars")]
472    pub fn chars(string: &str) -> CharsStream {
473        CharsStream::new(string, 0, INT::MAX)
474    }
475    /// Return an iterator over all the characters in the string.
476    ///
477    /// # Example
478    ///
479    /// ```rhai
480    /// for ch in "hello, world!".chars {"
481    ///     print(ch);
482    /// }
483    /// ```
484    #[cfg(not(feature = "no_object"))]
485    #[rhai_fn(get = "chars")]
486    pub fn get_chars(string: &str) -> CharsStream {
487        CharsStream::new(string, 0, INT::MAX)
488    }
489
490    /// Return an iterator over an exclusive range of bits in the number.
491    ///
492    /// # Example
493    ///
494    /// ```rhai
495    /// let x = 123456;
496    ///
497    /// for bit in x.bits(10..24) {
498    ///     print(bit);
499    /// }
500    /// ```
501    #[rhai_fn(name = "bits", return_raw)]
502    pub fn bits_from_exclusive_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<BitRange> {
503        let from = INT::max(range.start, 0);
504        let to = INT::max(range.end, from);
505        BitRange::new(value, from, to - from)
506    }
507    /// Return an iterator over an inclusive range of bits in the number.
508    ///
509    /// # Example
510    ///
511    /// ```rhai
512    /// let x = 123456;
513    ///
514    /// for bit in x.bits(10..=23) {
515    ///     print(bit);
516    /// }
517    /// ```
518    #[rhai_fn(name = "bits", return_raw)]
519    pub fn bits_from_inclusive_range(value: INT, range: InclusiveRange) -> RhaiResultOf<BitRange> {
520        let from = INT::max(*range.start(), 0);
521        let to = INT::max(*range.end(), from - 1);
522        BitRange::new(value, from, to - from + 1)
523    }
524    /// Return an iterator over a portion of bits in the number.
525    ///
526    /// * If `start` < 0, position counts from the MSB (Most Significant Bit)>.
527    /// * If `len` ≤ 0, an empty iterator is returned.
528    /// * If `start` position + `len` ≥ length of string, all bits of the number after the `start` position are iterated.
529    ///
530    /// # Example
531    ///
532    /// ```rhai
533    /// let x = 123456;
534    ///
535    /// for bit in x.bits(10, 8) {
536    ///     print(bit);
537    /// }
538    /// ```
539    #[rhai_fn(name = "bits", return_raw)]
540    pub fn bits_from_start_and_len(value: INT, from: INT, len: INT) -> RhaiResultOf<BitRange> {
541        BitRange::new(value, from, len)
542    }
543    /// Return an iterator over the bits in the number starting from the specified `start` position.
544    ///
545    /// If `start` < 0, position counts from the MSB (Most Significant Bit)>.
546    ///
547    /// # Example
548    ///
549    /// ```rhai
550    /// let x = 123456;
551    ///
552    /// for bit in x.bits(10) {
553    ///     print(bit);
554    /// }
555    /// ```
556    #[rhai_fn(name = "bits", return_raw)]
557    pub fn bits_from_start(value: INT, from: INT) -> RhaiResultOf<BitRange> {
558        BitRange::new(value, from, INT::MAX)
559    }
560    /// Return an iterator over all the bits in the number.
561    ///
562    /// # Example
563    ///
564    /// ```rhai
565    /// let x = 123456;
566    ///
567    /// for bit in x.bits() {
568    ///     print(bit);
569    /// }
570    /// ```
571    #[rhai_fn(name = "bits", return_raw)]
572    pub fn bits(value: INT) -> RhaiResultOf<BitRange> {
573        BitRange::new(value, 0, INT::MAX)
574    }
575    /// Return an iterator over all the bits in the number.
576    ///
577    /// # Example
578    ///
579    /// ```rhai
580    /// let x = 123456;
581    ///
582    /// for bit in x.bits {
583    ///     print(bit);
584    /// }
585    /// ```
586    #[cfg(not(feature = "no_object"))]
587    #[rhai_fn(get = "bits", return_raw)]
588    pub fn get_bits(value: INT) -> RhaiResultOf<BitRange> {
589        BitRange::new(value, 0, INT::MAX)
590    }
591}
592
593#[export_module]
594mod range_functions {
595    /// Return the start of the exclusive range.
596    #[rhai_fn(get = "start", name = "start", pure)]
597    pub fn start(range: &mut ExclusiveRange) -> INT {
598        range.start
599    }
600    /// Return the end of the exclusive range.
601    #[rhai_fn(get = "end", name = "end", pure)]
602    pub fn end(range: &mut ExclusiveRange) -> INT {
603        range.end
604    }
605    /// Return `true` if the range is inclusive.
606    #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
607    pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
608        let _ = range;
609        false
610    }
611    /// Return `true` if the range is exclusive.
612    #[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
613    pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
614        let _ = range;
615        true
616    }
617    /// Return true if the range contains no items.
618    #[rhai_fn(get = "is_empty", name = "is_empty", pure)]
619    #[allow(unstable_name_collisions)]
620    pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
621        range.is_empty()
622    }
623    /// Return `true` if the range contains a specified value.
624    #[rhai_fn(name = "contains")]
625    pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool {
626        range.contains(&value)
627    }
628
629    /// Return the start of the inclusive range.
630    #[rhai_fn(get = "start", name = "start", pure)]
631    pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
632        *range.start()
633    }
634    /// Return the end of the inclusive range.
635    #[rhai_fn(get = "end", name = "end", pure)]
636    pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
637        *range.end()
638    }
639    /// Return `true` if the range is inclusive.
640    #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
641    pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
642        let _ = range;
643        true
644    }
645    /// Return `true` if the range is exclusive.
646    #[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
647    pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
648        let _ = range;
649        false
650    }
651    /// Return true if the range contains no items.
652    #[rhai_fn(get = "is_empty", name = "is_empty", pure)]
653    pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool {
654        range.is_empty()
655    }
656    /// Return `true` if the range contains a specified value.
657    #[rhai_fn(name = "contains")]
658    pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool {
659        range.contains(&value)
660    }
661}