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#[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#[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#[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 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 lib.set_iterator::<CharsStream>();
384
385 lib.set_iterator::<BitRange>();
387
388 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 #[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 #[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 #[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 #[rhai_fn(name = "chars")]
459 pub fn chars_from_start(string: &str, start: INT) -> CharsStream {
460 CharsStream::new(string, start, INT::MAX)
461 }
462 #[rhai_fn(name = "chars")]
472 pub fn chars(string: &str) -> CharsStream {
473 CharsStream::new(string, 0, INT::MAX)
474 }
475 #[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 #[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 #[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 #[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 #[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 #[rhai_fn(name = "bits", return_raw)]
572 pub fn bits(value: INT) -> RhaiResultOf<BitRange> {
573 BitRange::new(value, 0, INT::MAX)
574 }
575 #[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 #[rhai_fn(get = "start", name = "start", pure)]
597 pub fn start(range: &mut ExclusiveRange) -> INT {
598 range.start
599 }
600 #[rhai_fn(get = "end", name = "end", pure)]
602 pub fn end(range: &mut ExclusiveRange) -> INT {
603 range.end
604 }
605 #[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 #[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 #[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 #[rhai_fn(name = "contains")]
625 pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool {
626 range.contains(&value)
627 }
628
629 #[rhai_fn(get = "start", name = "start", pure)]
631 pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
632 *range.start()
633 }
634 #[rhai_fn(get = "end", name = "end", pure)]
636 pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
637 *range.end()
638 }
639 #[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 #[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 #[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 #[rhai_fn(name = "contains")]
658 pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool {
659 range.contains(&value)
660 }
661}