pest/
parser_state.rs

1// pest. The Elegant Parser
2// Copyright (c) 2018 Dragoș Tiselice
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10//! The core functionality of parsing grammar.
11//! State of parser during the process of rules handling.
12
13use alloc::borrow::ToOwned;
14use alloc::boxed::Box;
15use alloc::rc::Rc;
16use alloc::vec;
17use alloc::vec::Vec;
18use core::num::NonZeroUsize;
19use core::ops::Range;
20use core::sync::atomic::{AtomicUsize, Ordering};
21
22use crate::error::{Error, ErrorVariant};
23use crate::iterators::{pairs, QueueableToken};
24use crate::position::{self, Position};
25use crate::span::Span;
26use crate::stack::Stack;
27use crate::RuleType;
28
29/// The current lookahead status of a [`ParserState`].
30///
31/// [`ParserState`]: struct.ParserState.html
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub enum Lookahead {
34    /// The positive predicate, written as an ampersand &,
35    /// attempts to match its inner expression.
36    /// If the inner expression succeeds, parsing continues,
37    /// but at the same position as the predicate —
38    /// &foo ~ bar is thus a kind of "AND" statement:
39    /// "the input string must match foo AND bar".
40    /// If the inner expression fails,
41    /// the whole expression fails too.
42    Positive,
43    /// The negative predicate, written as an exclamation mark !,
44    /// attempts to match its inner expression.
45    /// If the inner expression fails, the predicate succeeds
46    /// and parsing continues at the same position as the predicate.
47    /// If the inner expression succeeds, the predicate fails —
48    /// !foo ~ bar is thus a kind of "NOT" statement:
49    /// "the input string must match bar but NOT foo".
50    Negative,
51    /// No lookahead (i.e. it will consume input).
52    None,
53}
54
55/// The current atomicity of a [`ParserState`].
56///
57/// [`ParserState`]: struct.ParserState.html
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub enum Atomicity {
60    /// prevents implicit whitespace: inside an atomic rule,
61    /// the tilde ~ means "immediately followed by",
62    /// and repetition operators (asterisk * and plus sign +)
63    /// have no implicit separation. In addition, all other rules
64    /// called from an atomic rule are also treated as atomic.
65    /// (interior matching rules are silent)
66    Atomic,
67    /// The same as atomic, but inner tokens are produced as normal.
68    CompoundAtomic,
69    /// implicit whitespace is enabled
70    NonAtomic,
71}
72
73/// Type alias to simplify specifying the return value of chained closures.
74pub type ParseResult<S> = Result<S, S>;
75
76/// Match direction for the stack. Used in `PEEK[a..b]`/`stack_match_peek_slice`.
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
78pub enum MatchDir {
79    /// from the bottom to the top of the stack
80    BottomToTop,
81    /// from the top to the bottom of the stack
82    TopToBottom,
83}
84
85static CALL_LIMIT: AtomicUsize = AtomicUsize::new(0);
86
87/// Sets the maximum call limit for the parser state
88/// to prevent stack overflows or excessive execution times
89/// in some grammars.
90/// If set, the calls are tracked as a running total
91/// over all non-terminal rules that can nest closures
92/// (which are passed to transform the parser state).
93///
94/// # Arguments
95///
96/// * `limit` - The maximum number of calls. If None,
97///             the number of calls is unlimited.
98pub fn set_call_limit(limit: Option<NonZeroUsize>) {
99    CALL_LIMIT.store(limit.map(|f| f.get()).unwrap_or(0), Ordering::Relaxed);
100}
101
102#[derive(Debug)]
103struct CallLimitTracker {
104    current_call_limit: Option<(usize, usize)>,
105}
106
107impl Default for CallLimitTracker {
108    fn default() -> Self {
109        let limit = CALL_LIMIT.load(Ordering::Relaxed);
110        let current_call_limit = if limit > 0 { Some((0, limit)) } else { None };
111        Self { current_call_limit }
112    }
113}
114
115impl CallLimitTracker {
116    fn limit_reached(&self) -> bool {
117        self.current_call_limit
118            .map_or(false, |(current, limit)| current >= limit)
119    }
120
121    fn increment_depth(&mut self) {
122        if let Some((current, _)) = &mut self.current_call_limit {
123            *current += 1;
124        }
125    }
126}
127
128/// The complete state of a [`Parser`].
129///
130/// [`Parser`]: trait.Parser.html
131#[derive(Debug)]
132pub struct ParserState<'i, R: RuleType> {
133    /// Current position from which we try to apply some parser function.
134    /// Initially is 0.
135    /// E.g., we are parsing `create user 'Bobby'` query, we parsed "create" via `match_insensitive`
136    /// and switched our `position` from 0 to the length of "create".
137    ///
138    /// E.g., see `match_string` -> `self.position.match_string(string)` which updates `self.pos`.
139    position: Position<'i>,
140    /// Queue representing rules partially (`QueueableToken::Start`) and
141    /// totally (`QueueableToken::End`) parsed. When entering rule we put it in the queue in a state
142    /// of `Start` and after all it's sublogic (subrules or strings) are parsed, we change it to
143    /// `End` state.
144    queue: Vec<QueueableToken<'i, R>>,
145    /// Status set in case specific lookahead logic is used in grammar.
146    /// See `Lookahead` for more information.
147    lookahead: Lookahead,
148    /// Rules that we HAVE expected, tried to parse, but failed.
149    pos_attempts: Vec<R>,
150    /// Rules that we have NOT expected, tried to parse, but failed.
151    neg_attempts: Vec<R>,
152    /// Max position in the query from which we've tried to parse some rule but failed.
153    attempt_pos: usize,
154    /// Current atomicity status. For more information see `Atomicity`.
155    atomicity: Atomicity,
156    /// Helper structure tracking `Stack` status (used in case grammar contains stack PUSH/POP
157    /// invocations).
158    stack: Stack<Span<'i>>,
159    /// Used for setting max parser calls limit.
160    call_tracker: CallLimitTracker,
161}
162
163/// Creates a `ParserState` from a `&str`, supplying it to a closure `f`.
164///
165/// # Examples
166///
167/// ```
168/// # use pest;
169/// let input = "";
170/// pest::state::<(), _>(input, |s| Ok(s)).unwrap();
171/// ```
172#[allow(clippy::perf)]
173pub fn state<'i, R: RuleType, F>(input: &'i str, f: F) -> Result<pairs::Pairs<'i, R>, Error<R>>
174where
175    F: FnOnce(Box<ParserState<'i, R>>) -> ParseResult<Box<ParserState<'i, R>>>,
176{
177    let state = ParserState::new(input);
178
179    match f(state) {
180        Ok(state) => {
181            let len = state.queue.len();
182            Ok(pairs::new(Rc::new(state.queue), input, None, 0, len))
183        }
184        Err(mut state) => {
185            let variant = if state.reached_call_limit() {
186                ErrorVariant::CustomError {
187                    message: "call limit reached".to_owned(),
188                }
189            } else {
190                state.pos_attempts.sort();
191                state.pos_attempts.dedup();
192                state.neg_attempts.sort();
193                state.neg_attempts.dedup();
194                ErrorVariant::ParsingError {
195                    positives: state.pos_attempts.clone(),
196                    negatives: state.neg_attempts.clone(),
197                }
198            };
199
200            Err(Error::new_from_pos(
201                variant,
202                // TODO(performance): Guarantee state.attempt_pos is a valid position
203                position::Position::new(input, state.attempt_pos).unwrap(),
204            ))
205        }
206    }
207}
208
209impl<'i, R: RuleType> ParserState<'i, R> {
210    /// Allocates a fresh `ParserState` object to the heap and returns the owned `Box`. This `Box`
211    /// will be passed from closure to closure based on the needs of the specified `Parser`.
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// # use pest;
217    /// let input = "";
218    /// let state: Box<pest::ParserState<&str>> = pest::ParserState::new(input);
219    /// ```
220    pub fn new(input: &'i str) -> Box<Self> {
221        Box::new(ParserState {
222            position: Position::from_start(input),
223            queue: vec![],
224            lookahead: Lookahead::None,
225            pos_attempts: vec![],
226            neg_attempts: vec![],
227            attempt_pos: 0,
228            atomicity: Atomicity::NonAtomic,
229            stack: Stack::new(),
230            call_tracker: Default::default(),
231        })
232    }
233
234    /// Returns a reference to the current `Position` of the `ParserState`.
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// # use pest;
240    /// # #[allow(non_camel_case_types)]
241    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
242    /// enum Rule {
243    ///     ab
244    /// }
245    ///
246    /// let input = "ab";
247    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
248    /// let position = state.position();
249    /// assert_eq!(position.pos(), 0);
250    /// ```
251    pub fn position(&self) -> &Position<'i> {
252        &self.position
253    }
254
255    /// Returns the current atomicity of the `ParserState`.
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// # use pest;
261    /// # use pest::Atomicity;
262    /// # #[allow(non_camel_case_types)]
263    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
264    /// enum Rule {
265    ///     ab
266    /// }
267    ///
268    /// let input = "ab";
269    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
270    /// let atomicity = state.atomicity();
271    /// assert_eq!(atomicity, Atomicity::NonAtomic);
272    /// ```
273    pub fn atomicity(&self) -> Atomicity {
274        self.atomicity
275    }
276
277    #[inline]
278    fn inc_call_check_limit(mut self: Box<Self>) -> ParseResult<Box<Self>> {
279        if self.call_tracker.limit_reached() {
280            return Err(self);
281        }
282        self.call_tracker.increment_depth();
283        Ok(self)
284    }
285
286    #[inline]
287    fn reached_call_limit(&self) -> bool {
288        self.call_tracker.limit_reached()
289    }
290
291    /// Wrapper needed to generate tokens. This will associate the `R` type rule to the closure
292    /// meant to match the rule.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// # use pest;
298    /// # #[allow(non_camel_case_types)]
299    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
300    /// enum Rule {
301    ///     a
302    /// }
303    ///
304    /// let input = "a";
305    /// let pairs: Vec<_> = pest::state(input, |state| {
306    ///     state.rule(Rule::a, |s| Ok(s))
307    /// }).unwrap().collect();
308    ///
309    /// assert_eq!(pairs.len(), 1);
310    /// ```
311    #[inline]
312    pub fn rule<F>(mut self: Box<Self>, rule: R, f: F) -> ParseResult<Box<Self>>
313    where
314        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
315    {
316        self = self.inc_call_check_limit()?;
317        // Position from which this `rule` starts parsing.
318        let actual_pos = self.position.pos();
319        // Remember index of the `self.queue` element that will be associated with this `rule`.
320        let index = self.queue.len();
321
322        let (pos_attempts_index, neg_attempts_index) = if actual_pos == self.attempt_pos {
323            (self.pos_attempts.len(), self.neg_attempts.len())
324        } else {
325            // Attempts have not been cleared yet since the attempt_pos is older.
326            (0, 0)
327        };
328
329        if self.lookahead == Lookahead::None && self.atomicity != Atomicity::Atomic {
330            // Pair's position will only be known after running the closure.
331            self.queue.push(QueueableToken::Start {
332                end_token_index: 0,
333                input_pos: actual_pos,
334            });
335        }
336
337        // Remember attempts number before `f` call.
338        // In `track` using this variable we can say, how many attempts were added
339        // during children rules traversal.
340        let attempts = self.attempts_at(actual_pos);
341
342        let result = f(self);
343
344        match result {
345            Ok(mut new_state) => {
346                if new_state.lookahead == Lookahead::Negative {
347                    new_state.track(
348                        rule,
349                        actual_pos,
350                        pos_attempts_index,
351                        neg_attempts_index,
352                        attempts,
353                    );
354                }
355
356                if new_state.lookahead == Lookahead::None
357                    && new_state.atomicity != Atomicity::Atomic
358                {
359                    // Index of `QueueableToken::End` token added below
360                    // that corresponds to previously added `QueueableToken::Start` token.
361                    let new_index = new_state.queue.len();
362                    match new_state.queue[index] {
363                        QueueableToken::Start {
364                            ref mut end_token_index,
365                            ..
366                        } => *end_token_index = new_index,
367                        _ => unreachable!(),
368                    };
369
370                    let new_pos = new_state.position.pos();
371
372                    new_state.queue.push(QueueableToken::End {
373                        start_token_index: index,
374                        rule,
375                        tag: None,
376                        input_pos: new_pos,
377                    });
378                }
379
380                Ok(new_state)
381            }
382            Err(mut new_state) => {
383                if new_state.lookahead != Lookahead::Negative {
384                    new_state.track(
385                        rule,
386                        actual_pos,
387                        pos_attempts_index,
388                        neg_attempts_index,
389                        attempts,
390                    );
391                }
392
393                if new_state.lookahead == Lookahead::None
394                    && new_state.atomicity != Atomicity::Atomic
395                {
396                    new_state.queue.truncate(index);
397                }
398
399                Err(new_state)
400            }
401        }
402    }
403
404    /// Tag current node
405    ///
406    /// # Examples
407    ///
408    /// Try to recognize the one specified in a set of characters
409    ///
410    /// ```
411    /// use pest::{state, ParseResult, ParserState, iterators::Pair};
412    /// #[allow(non_camel_case_types)]
413    /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
414    /// enum Rule {
415    ///     character,
416    /// }
417    /// fn mark_c(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
418    ///     state.sequence(|state| {
419    ///         character(state)
420    ///             .and_then(|state| character(state))
421    ///             .and_then(|state| character(state))
422    ///             .and_then(|state| state.tag_node("c"))
423    ///             .and_then(|state| character(state))
424    ///     })
425    /// }
426    /// fn character(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
427    ///     state.rule(Rule::character, |state| state.match_range('a'..'z'))
428    /// }
429    ///
430    /// let input = "abcd";
431    /// let pairs = state(input, mark_c).unwrap();
432    /// // find all node tag as `c`
433    /// let find: Vec<Pair<Rule>> = pairs.filter(|s| s.as_node_tag() == Some("c")).collect();
434    /// assert_eq!(find[0].as_str(), "c")
435    /// ```
436    #[inline]
437    pub fn tag_node(mut self: Box<Self>, tag: &'i str) -> ParseResult<Box<Self>> {
438        if let Some(QueueableToken::End { tag: old, .. }) = self.queue.last_mut() {
439            *old = Some(tag)
440        }
441        Ok(self)
442    }
443
444    /// Get number of allowed rules attempts + prohibited rules attempts.
445    fn attempts_at(&self, pos: usize) -> usize {
446        if self.attempt_pos == pos {
447            self.pos_attempts.len() + self.neg_attempts.len()
448        } else {
449            0
450        }
451    }
452
453    fn track(
454        &mut self,
455        rule: R,
456        pos: usize,
457        pos_attempts_index: usize,
458        neg_attempts_index: usize,
459        prev_attempts: usize,
460    ) {
461        if self.atomicity == Atomicity::Atomic {
462            return;
463        }
464
465        // If nested rules made no progress, there is no use to report them; it's only useful to
466        // track the current rule, the exception being when only one attempt has been made during
467        // the children rules.
468        let curr_attempts = self.attempts_at(pos);
469        if curr_attempts > prev_attempts && curr_attempts - prev_attempts == 1 {
470            return;
471        }
472
473        if pos == self.attempt_pos {
474            self.pos_attempts.truncate(pos_attempts_index);
475            self.neg_attempts.truncate(neg_attempts_index);
476        }
477
478        if pos > self.attempt_pos {
479            self.pos_attempts.clear();
480            self.neg_attempts.clear();
481            self.attempt_pos = pos;
482        }
483
484        let attempts = if self.lookahead != Lookahead::Negative {
485            &mut self.pos_attempts
486        } else {
487            &mut self.neg_attempts
488        };
489
490        if pos == self.attempt_pos {
491            attempts.push(rule);
492        }
493    }
494
495    /// Starts a sequence of transformations provided by `f` from the `Box<ParserState>`. Returns
496    /// the same `Result` returned by `f` in the case of an `Ok`, or `Err` with the current
497    /// `Box<ParserState>` otherwise.
498    ///
499    /// This method is useful to parse sequences that only match together which usually come in the
500    /// form of chained `Result`s with
501    /// [`Result::and_then`](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then).
502    ///
503    ///
504    /// # Examples
505    ///
506    /// ```
507    /// # use pest;
508    /// # #[allow(non_camel_case_types)]
509    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
510    /// enum Rule {
511    ///     a
512    /// }
513    ///
514    /// let input = "a";
515    /// let pairs: Vec<_> = pest::state(input, |state| {
516    ///     state.sequence(|s| {
517    ///         s.rule(Rule::a, |s| Ok(s)).and_then(|s| {
518    ///             s.match_string("b")
519    ///         })
520    ///     }).or_else(|s| {
521    ///         Ok(s)
522    ///     })
523    /// }).unwrap().collect();
524    ///
525    /// assert_eq!(pairs.len(), 0);
526    /// ```
527    #[inline]
528    pub fn sequence<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
529    where
530        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
531    {
532        self = self.inc_call_check_limit()?;
533        let token_index = self.queue.len();
534        let initial_pos = self.position;
535
536        let result = f(self);
537
538        match result {
539            Ok(new_state) => Ok(new_state),
540            Err(mut new_state) => {
541                // Restore the initial position and truncate the token queue.
542                new_state.position = initial_pos;
543                new_state.queue.truncate(token_index);
544                Err(new_state)
545            }
546        }
547    }
548
549    /// Repeatedly applies the transformation provided by `f` from the `Box<ParserState>`. Returns
550    /// `Ok` with the updated `Box<ParserState>` returned by `f` wrapped up in an `Err`.
551    ///
552    /// # Examples
553    ///
554    /// ```
555    /// # use pest;
556    /// # #[allow(non_camel_case_types)]
557    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
558    /// enum Rule {
559    ///     ab
560    /// }
561    ///
562    /// let input = "aab";
563    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
564    /// let mut result = state.repeat(|s| {
565    ///     s.match_string("a")
566    /// });
567    /// assert!(result.is_ok());
568    /// assert_eq!(result.unwrap().position().pos(), 2);
569    ///
570    /// state = pest::ParserState::new(input);
571    /// result = state.repeat(|s| {
572    ///     s.match_string("b")
573    /// });
574    /// assert!(result.is_ok());
575    /// assert_eq!(result.unwrap().position().pos(), 0);
576    /// ```
577    #[inline]
578    pub fn repeat<F>(mut self: Box<Self>, mut f: F) -> ParseResult<Box<Self>>
579    where
580        F: FnMut(Box<Self>) -> ParseResult<Box<Self>>,
581    {
582        self = self.inc_call_check_limit()?;
583        let mut result = f(self);
584
585        loop {
586            match result {
587                Ok(state) => result = f(state),
588                Err(state) => return Ok(state),
589            };
590        }
591    }
592
593    /// Optionally applies the transformation provided by `f` from the `Box<ParserState>`. Returns
594    /// `Ok` with the updated `Box<ParserState>` returned by `f` regardless of the `Result`.
595    ///
596    /// # Examples
597    ///
598    /// ```
599    /// # use pest;
600    /// # #[allow(non_camel_case_types)]
601    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
602    /// enum Rule {
603    ///     ab
604    /// }
605    ///
606    /// let input = "ab";
607    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
608    /// let result = state.optional(|s| {
609    ///     s.match_string("ab")
610    /// });
611    /// assert!(result.is_ok());
612    ///
613    /// state = pest::ParserState::new(input);
614    /// let result = state.optional(|s| {
615    ///     s.match_string("ac")
616    /// });
617    /// assert!(result.is_ok());
618    /// ```
619    #[inline]
620    pub fn optional<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
621    where
622        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
623    {
624        self = self.inc_call_check_limit()?;
625        match f(self) {
626            Ok(state) | Err(state) => Ok(state),
627        }
628    }
629
630    /// Attempts to match a single character based on a filter function. Returns `Ok` with the
631    /// updated `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>`
632    /// otherwise.
633    ///
634    /// # Examples
635    ///
636    /// ```
637    /// # use pest;
638    /// # #[allow(non_camel_case_types)]
639    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
640    /// enum Rule {}
641    ///
642    /// let input = "ab";
643    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
644    /// let result = state.match_char_by(|c| c.is_ascii());
645    /// assert!(result.is_ok());
646    /// assert_eq!(result.unwrap().position().pos(), 1);
647    ///
648    /// let input = "❤";
649    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
650    /// let result = state.match_char_by(|c| c.is_ascii());
651    /// assert!(result.is_err());
652    /// assert_eq!(result.unwrap_err().position().pos(), 0);
653    /// ```
654    #[inline]
655    pub fn match_char_by<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
656    where
657        F: FnOnce(char) -> bool,
658    {
659        if self.position.match_char_by(f) {
660            Ok(self)
661        } else {
662            Err(self)
663        }
664    }
665
666    /// Attempts to match the given string. Returns `Ok` with the updated `Box<ParserState>` if
667    /// successful, or `Err` with the updated `Box<ParserState>` otherwise.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// # use pest;
673    /// # #[allow(non_camel_case_types)]
674    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
675    /// enum Rule {}
676    ///
677    /// let input = "ab";
678    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
679    /// let mut result = state.match_string("ab");
680    /// assert!(result.is_ok());
681    /// assert_eq!(result.unwrap().position().pos(), 2);
682    ///
683    /// state = pest::ParserState::new(input);
684    /// result = state.match_string("ac");
685    /// assert!(result.is_err());
686    /// assert_eq!(result.unwrap_err().position().pos(), 0);
687    /// ```
688    #[inline]
689    pub fn match_string(mut self: Box<Self>, string: &str) -> ParseResult<Box<Self>> {
690        if self.position.match_string(string) {
691            Ok(self)
692        } else {
693            Err(self)
694        }
695    }
696
697    /// Attempts to case-insensitively match the given string. Returns `Ok` with the updated
698    /// `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>` otherwise.
699    ///
700    /// # Examples
701    ///
702    /// ```
703    /// # use pest;
704    /// # #[allow(non_camel_case_types)]
705    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
706    /// enum Rule {}
707    ///
708    /// let input = "ab";
709    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
710    /// let mut result = state.match_insensitive("AB");
711    /// assert!(result.is_ok());
712    /// assert_eq!(result.unwrap().position().pos(), 2);
713    ///
714    /// state = pest::ParserState::new(input);
715    /// result = state.match_insensitive("AC");
716    /// assert!(result.is_err());
717    /// assert_eq!(result.unwrap_err().position().pos(), 0);
718    /// ```
719    #[inline]
720    pub fn match_insensitive(mut self: Box<Self>, string: &str) -> ParseResult<Box<Self>> {
721        if self.position.match_insensitive(string) {
722            Ok(self)
723        } else {
724            Err(self)
725        }
726    }
727
728    /// Attempts to match a single character from the given range. Returns `Ok` with the updated
729    /// `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>` otherwise.
730    ///
731    /// # Caution
732    /// The provided `range` is interpreted as inclusive.
733    ///
734    /// # Examples
735    ///
736    /// ```
737    /// # use pest;
738    /// # #[allow(non_camel_case_types)]
739    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
740    /// enum Rule {}
741    ///
742    /// let input = "ab";
743    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
744    /// let mut result = state.match_range('a'..'z');
745    /// assert!(result.is_ok());
746    /// assert_eq!(result.unwrap().position().pos(), 1);
747    ///
748    /// state = pest::ParserState::new(input);
749    /// result = state.match_range('A'..'Z');
750    /// assert!(result.is_err());
751    /// assert_eq!(result.unwrap_err().position().pos(), 0);
752    /// ```
753    #[inline]
754    pub fn match_range(mut self: Box<Self>, range: Range<char>) -> ParseResult<Box<Self>> {
755        if self.position.match_range(range) {
756            Ok(self)
757        } else {
758            Err(self)
759        }
760    }
761
762    /// Attempts to skip `n` characters forward. Returns `Ok` with the updated `Box<ParserState>`
763    /// if successful, or `Err` with the updated `Box<ParserState>` otherwise.
764    ///
765    /// # Examples
766    ///
767    /// ```
768    /// # use pest;
769    /// # #[allow(non_camel_case_types)]
770    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
771    /// enum Rule {}
772    ///
773    /// let input = "ab";
774    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
775    /// let mut result = state.skip(1);
776    /// assert!(result.is_ok());
777    /// assert_eq!(result.unwrap().position().pos(), 1);
778    ///
779    /// state = pest::ParserState::new(input);
780    /// result = state.skip(3);
781    /// assert!(result.is_err());
782    /// assert_eq!(result.unwrap_err().position().pos(), 0);
783    /// ```
784    #[inline]
785    pub fn skip(mut self: Box<Self>, n: usize) -> ParseResult<Box<Self>> {
786        if self.position.skip(n) {
787            Ok(self)
788        } else {
789            Err(self)
790        }
791    }
792
793    /// Attempts to skip forward until one of the given strings is found. Returns `Ok` with the
794    /// updated `Box<ParserState>` whether or not one of the strings is found.
795    ///
796    /// # Examples
797    ///
798    /// ```
799    /// # use pest;
800    /// # #[allow(non_camel_case_types)]
801    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
802    /// enum Rule {}
803    ///
804    /// let input = "abcd";
805    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
806    /// let mut result = state.skip_until(&["c", "d"]);
807    /// assert!(result.is_ok());
808    /// assert_eq!(result.unwrap().position().pos(), 2);
809    /// ```
810    #[inline]
811    pub fn skip_until(mut self: Box<Self>, strings: &[&str]) -> ParseResult<Box<Self>> {
812        self.position.skip_until(strings);
813        Ok(self)
814    }
815
816    /// Attempts to match the start of the input. Returns `Ok` with the current `Box<ParserState>`
817    /// if the parser has not yet advanced, or `Err` with the current `Box<ParserState>` otherwise.
818    ///
819    /// # Examples
820    ///
821    /// ```
822    /// # use pest;
823    /// # #[allow(non_camel_case_types)]
824    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
825    /// enum Rule {}
826    ///
827    /// let input = "ab";
828    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
829    /// let mut result = state.start_of_input();
830    /// assert!(result.is_ok());
831    ///
832    /// state = pest::ParserState::new(input);
833    /// state = state.match_string("ab").unwrap();
834    /// result = state.start_of_input();
835    /// assert!(result.is_err());
836    /// ```
837    #[inline]
838    pub fn start_of_input(self: Box<Self>) -> ParseResult<Box<Self>> {
839        if self.position.at_start() {
840            Ok(self)
841        } else {
842            Err(self)
843        }
844    }
845
846    /// Attempts to match the end of the input. Returns `Ok` with the current `Box<ParserState>` if
847    /// there is no input remaining, or `Err` with the current `Box<ParserState>` otherwise.
848    ///
849    /// # Examples
850    ///
851    /// ```
852    /// # use pest;
853    /// # #[allow(non_camel_case_types)]
854    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
855    /// enum Rule {}
856    ///
857    /// let input = "ab";
858    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
859    /// let mut result = state.end_of_input();
860    /// assert!(result.is_err());
861    ///
862    /// state = pest::ParserState::new(input);
863    /// state = state.match_string("ab").unwrap();
864    /// result = state.end_of_input();
865    /// assert!(result.is_ok());
866    /// ```
867    #[inline]
868    pub fn end_of_input(self: Box<Self>) -> ParseResult<Box<Self>> {
869        if self.position.at_end() {
870            Ok(self)
871        } else {
872            Err(self)
873        }
874    }
875
876    /// Starts a lookahead transformation provided by `f` from the `Box<ParserState>`. It returns
877    /// `Ok` with the current `Box<ParserState>` if `f` also returns an `Ok`, or `Err` with the current
878    /// `Box<ParserState>` otherwise. If `is_positive` is `false`, it swaps the `Ok` and `Err`
879    /// together, negating the `Result`.
880    ///
881    /// # Examples
882    ///
883    /// ```
884    /// # use pest;
885    /// # #[allow(non_camel_case_types)]
886    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
887    /// enum Rule {
888    ///     a
889    /// }
890    ///
891    /// let input = "a";
892    /// let pairs: Vec<_> = pest::state(input, |state| {
893    ///     state.lookahead(true, |state| {
894    ///         state.rule(Rule::a, |s| Ok(s))
895    ///     })
896    /// }).unwrap().collect();
897    ///
898    /// assert_eq!(pairs.len(), 0);
899    /// ```
900    #[inline]
901    pub fn lookahead<F>(mut self: Box<Self>, is_positive: bool, f: F) -> ParseResult<Box<Self>>
902    where
903        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
904    {
905        self = self.inc_call_check_limit()?;
906        let initial_lookahead = self.lookahead;
907
908        self.lookahead = if is_positive {
909            match initial_lookahead {
910                Lookahead::None | Lookahead::Positive => Lookahead::Positive,
911                Lookahead::Negative => Lookahead::Negative,
912            }
913        } else {
914            match initial_lookahead {
915                Lookahead::None | Lookahead::Positive => Lookahead::Negative,
916                Lookahead::Negative => Lookahead::Positive,
917            }
918        };
919
920        let initial_pos = self.position;
921
922        let result = f(self.checkpoint());
923
924        let result_state = match result {
925            Ok(mut new_state) => {
926                new_state.position = initial_pos;
927                new_state.lookahead = initial_lookahead;
928                Ok(new_state.restore())
929            }
930            Err(mut new_state) => {
931                new_state.position = initial_pos;
932                new_state.lookahead = initial_lookahead;
933                Err(new_state.restore())
934            }
935        };
936
937        if is_positive {
938            result_state
939        } else {
940            match result_state {
941                Ok(state) => Err(state),
942                Err(state) => Ok(state),
943            }
944        }
945    }
946
947    /// Transformation which stops `Token`s from being generated according to `is_atomic`.
948    /// Used as wrapper over `rule` (or even another `atomic`) call.
949    ///
950    /// # Examples
951    ///
952    /// ```
953    /// # use pest::{self, Atomicity};
954    /// # #[allow(non_camel_case_types)]
955    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
956    /// enum Rule {
957    ///     a
958    /// }
959    ///
960    /// let input = "a";
961    /// let pairs: Vec<_> = pest::state(input, |state| {
962    ///     state.atomic(Atomicity::Atomic, |s| {
963    ///         s.rule(Rule::a, |s| Ok(s))
964    ///     })
965    /// }).unwrap().collect();
966    ///
967    /// assert_eq!(pairs.len(), 0);
968    /// ```
969    #[inline]
970    pub fn atomic<F>(mut self: Box<Self>, atomicity: Atomicity, f: F) -> ParseResult<Box<Self>>
971    where
972        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
973    {
974        self = self.inc_call_check_limit()?;
975        // In case child parsing call is another `atomic` it will have it's own atomicity status.
976        let initial_atomicity = self.atomicity;
977        // In case child atomicity is the same as we've demanded, we shouldn't do nothing.
978        // E.g. we have the following rules:
979        // * RootRule = @{ InnerRule }
980        // * InnerRule = @{ ... }
981        let should_toggle = self.atomicity != atomicity;
982
983        // Note that we take atomicity of the top rule and not of the leaf (inner).
984        if should_toggle {
985            self.atomicity = atomicity;
986        }
987
988        let result = f(self);
989
990        match result {
991            Ok(mut new_state) => {
992                if should_toggle {
993                    new_state.atomicity = initial_atomicity;
994                }
995                Ok(new_state)
996            }
997            Err(mut new_state) => {
998                if should_toggle {
999                    new_state.atomicity = initial_atomicity;
1000                }
1001                Err(new_state)
1002            }
1003        }
1004    }
1005
1006    /// Evaluates the result of closure `f` and pushes the span of the input consumed from before
1007    /// `f` is called to after `f` is called to the stack. Returns `Ok(Box<ParserState>)` if `f` is
1008    /// called successfully, or `Err(Box<ParserState>)` otherwise.
1009    ///
1010    /// # Examples
1011    ///
1012    /// ```
1013    /// # use pest;
1014    /// # #[allow(non_camel_case_types)]
1015    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1016    /// enum Rule {}
1017    ///
1018    /// let input = "ab";
1019    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1020    /// let mut result = state.stack_push(|state| state.match_string("a"));
1021    /// assert!(result.is_ok());
1022    /// assert_eq!(result.unwrap().position().pos(), 1);
1023    /// ```
1024    #[inline]
1025    pub fn stack_push<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1026    where
1027        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1028    {
1029        self = self.inc_call_check_limit()?;
1030        let start = self.position;
1031
1032        let result = f(self);
1033
1034        match result {
1035            Ok(mut state) => {
1036                let end = state.position;
1037                state.stack.push(start.span(&end));
1038                Ok(state)
1039            }
1040            Err(state) => Err(state),
1041        }
1042    }
1043
1044    /// Peeks the top of the stack and attempts to match the string. Returns `Ok(Box<ParserState>)`
1045    /// if the string is matched successfully, or `Err(Box<ParserState>)` otherwise.
1046    ///
1047    /// # Examples
1048    ///
1049    /// ```
1050    /// # use pest;
1051    /// # #[allow(non_camel_case_types)]
1052    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1053    /// enum Rule {}
1054    ///
1055    /// let input = "aa";
1056    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1057    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1058    ///     |state| state.stack_peek()
1059    /// );
1060    /// assert!(result.is_ok());
1061    /// assert_eq!(result.unwrap().position().pos(), 2);
1062    /// ```
1063    #[inline]
1064    pub fn stack_peek(self: Box<Self>) -> ParseResult<Box<Self>> {
1065        let string = self
1066            .stack
1067            .peek()
1068            .expect("peek was called on empty stack")
1069            .as_str();
1070        self.match_string(string)
1071    }
1072
1073    /// Pops the top of the stack and attempts to match the string. Returns `Ok(Box<ParserState>)`
1074    /// if the string is matched successfully, or `Err(Box<ParserState>)` otherwise.
1075    ///
1076    /// # Examples
1077    ///
1078    /// ```
1079    /// # use pest;
1080    /// # #[allow(non_camel_case_types)]
1081    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1082    /// enum Rule {}
1083    ///
1084    /// let input = "aa";
1085    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1086    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1087    ///     |state| state.stack_pop()
1088    /// );
1089    /// assert!(result.is_ok());
1090    /// assert_eq!(result.unwrap().position().pos(), 2);
1091    /// ```
1092    #[inline]
1093    pub fn stack_pop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1094        let string = self
1095            .stack
1096            .pop()
1097            .expect("pop was called on empty stack")
1098            .as_str();
1099        self.match_string(string)
1100    }
1101
1102    /// Matches part of the state of the stack.
1103    ///
1104    /// # Examples
1105    ///
1106    /// ```
1107    /// # use pest::{self, MatchDir};
1108    /// # #[allow(non_camel_case_types)]
1109    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1110    /// enum Rule {}
1111    ///
1112    /// let input = "abcd cd cb";
1113    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1114    /// let mut result = state
1115    ///     .stack_push(|state| state.match_string("a"))
1116    ///     .and_then(|state| state.stack_push(|state| state.match_string("b")))
1117    ///     .and_then(|state| state.stack_push(|state| state.match_string("c")))
1118    ///     .and_then(|state| state.stack_push(|state| state.match_string("d")))
1119    ///     .and_then(|state| state.match_string(" "))
1120    ///     .and_then(|state| state.stack_match_peek_slice(2, None, MatchDir::BottomToTop))
1121    ///     .and_then(|state| state.match_string(" "))
1122    ///     .and_then(|state| state.stack_match_peek_slice(1, Some(-1), MatchDir::TopToBottom));
1123    /// assert!(result.is_ok());
1124    /// assert_eq!(result.unwrap().position().pos(), 10);
1125    /// ```
1126    #[inline]
1127    pub fn stack_match_peek_slice(
1128        mut self: Box<Self>,
1129        start: i32,
1130        end: Option<i32>,
1131        match_dir: MatchDir,
1132    ) -> ParseResult<Box<Self>> {
1133        let range = match constrain_idxs(start, end, self.stack.len()) {
1134            Some(r) => r,
1135            None => return Err(self),
1136        };
1137        // return true if an empty sequence is requested
1138        if range.end <= range.start {
1139            return Ok(self);
1140        }
1141
1142        let mut position = self.position;
1143        let result = {
1144            let mut iter_b2t = self.stack[range].iter();
1145            let matcher = |span: &Span<'_>| position.match_string(span.as_str());
1146            match match_dir {
1147                MatchDir::BottomToTop => iter_b2t.all(matcher),
1148                MatchDir::TopToBottom => iter_b2t.rev().all(matcher),
1149            }
1150        };
1151        if result {
1152            self.position = position;
1153            Ok(self)
1154        } else {
1155            Err(self)
1156        }
1157    }
1158
1159    /// Matches the full state of the stack.
1160    ///
1161    /// # Examples
1162    ///
1163    /// ```
1164    /// # use pest;
1165    /// # #[allow(non_camel_case_types)]
1166    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1167    /// enum Rule {}
1168    ///
1169    /// let input = "abba";
1170    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1171    /// let mut result = state
1172    ///     .stack_push(|state| state.match_string("a"))
1173    ///     .and_then(|state| { state.stack_push(|state| state.match_string("b")) })
1174    ///     .and_then(|state| state.stack_match_peek());
1175    /// assert!(result.is_ok());
1176    /// assert_eq!(result.unwrap().position().pos(), 4);
1177    /// ```
1178    #[inline]
1179    pub fn stack_match_peek(self: Box<Self>) -> ParseResult<Box<Self>> {
1180        self.stack_match_peek_slice(0, None, MatchDir::TopToBottom)
1181    }
1182
1183    /// Matches the full state of the stack. This method will clear the stack as it evaluates.
1184    ///
1185    /// # Examples
1186    ///
1187    /// ```
1188    /// /// # use pest;
1189    /// # #[allow(non_camel_case_types)]
1190    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1191    /// enum Rule {}
1192    ///
1193    /// let input = "aaaa";
1194    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1195    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(|state| {
1196    ///     state.stack_push(|state| state.match_string("a"))
1197    /// }).and_then(|state| state.stack_match_peek());
1198    /// assert!(result.is_ok());
1199    /// assert_eq!(result.unwrap().position().pos(), 4);
1200    /// ```
1201    #[inline]
1202    pub fn stack_match_pop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1203        let mut position = self.position;
1204        let mut result = true;
1205        while let Some(span) = self.stack.pop() {
1206            result = position.match_string(span.as_str());
1207            if !result {
1208                break;
1209            }
1210        }
1211
1212        if result {
1213            self.position = position;
1214            Ok(self)
1215        } else {
1216            Err(self)
1217        }
1218    }
1219
1220    /// Drops the top of the stack. Returns `Ok(Box<ParserState>)` if there was a value to drop, or
1221    /// `Err(Box<ParserState>)` otherwise.
1222    ///
1223    /// # Examples
1224    ///
1225    /// ```
1226    /// # use pest;
1227    /// # #[allow(non_camel_case_types)]
1228    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1229    /// enum Rule {}
1230    ///
1231    /// let input = "aa";
1232    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1233    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1234    ///     |state| state.stack_drop()
1235    /// );
1236    /// assert!(result.is_ok());
1237    /// assert_eq!(result.unwrap().position().pos(), 1);
1238    /// ```
1239    #[inline]
1240    pub fn stack_drop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1241        match self.stack.pop() {
1242            Some(_) => Ok(self),
1243            None => Err(self),
1244        }
1245    }
1246
1247    /// Restores the original state of the `ParserState` when `f` returns an `Err`. Currently,
1248    /// this method only restores the stack.
1249    ///
1250    /// # Examples
1251    ///
1252    /// ```
1253    /// # use pest;
1254    /// # #[allow(non_camel_case_types)]
1255    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1256    /// enum Rule {}
1257    ///
1258    /// let input = "ab";
1259    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1260    /// let mut result = state.restore_on_err(|state| state.stack_push(|state|
1261    ///     state.match_string("a")).and_then(|state| state.match_string("a"))
1262    /// );
1263    ///
1264    /// assert!(result.is_err());
1265    ///
1266    /// // Since the the rule doesn't match, the "a" pushed to the stack will be removed.
1267    /// let catch_panic = std::panic::catch_unwind(|| result.unwrap_err().stack_pop());
1268    /// assert!(catch_panic.is_err());
1269    /// ```
1270    #[inline]
1271    pub fn restore_on_err<F>(self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1272    where
1273        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1274    {
1275        match f(self.checkpoint()) {
1276            Ok(state) => Ok(state.checkpoint_ok()),
1277            Err(state) => Err(state.restore()),
1278        }
1279    }
1280
1281    // Mark the current state as a checkpoint and return the `Box`.
1282    #[inline]
1283    pub(crate) fn checkpoint(mut self: Box<Self>) -> Box<Self> {
1284        self.stack.snapshot();
1285        self
1286    }
1287
1288    // The checkpoint was cleared successfully
1289    // so remove it without touching other stack state.
1290    #[inline]
1291    pub(crate) fn checkpoint_ok(mut self: Box<Self>) -> Box<Self> {
1292        self.stack.clear_snapshot();
1293        self
1294    }
1295
1296    // Restore the current state to the most recent checkpoint.
1297    #[inline]
1298    pub(crate) fn restore(mut self: Box<Self>) -> Box<Self> {
1299        self.stack.restore();
1300        self
1301    }
1302}
1303
1304/// Helper function used only in case stack operations (PUSH/POP) are used in grammar.
1305fn constrain_idxs(start: i32, end: Option<i32>, len: usize) -> Option<Range<usize>> {
1306    let start_norm = normalize_index(start, len)?;
1307    let end_norm = end.map_or(Some(len), |e| normalize_index(e, len))?;
1308    Some(start_norm..end_norm)
1309}
1310
1311/// `constrain_idxs` helper function.
1312/// Normalizes the index using its sequence’s length.
1313/// Returns `None` if the normalized index is OOB.
1314fn normalize_index(i: i32, len: usize) -> Option<usize> {
1315    if i > len as i32 {
1316        None
1317    } else if i >= 0 {
1318        Some(i as usize)
1319    } else {
1320        let real_i = len as i32 + i;
1321        if real_i >= 0 {
1322            Some(real_i as usize)
1323        } else {
1324            None
1325        }
1326    }
1327}
1328
1329#[cfg(test)]
1330mod test {
1331    use super::*;
1332
1333    #[test]
1334    fn normalize_index_pos() {
1335        assert_eq!(normalize_index(4, 6), Some(4));
1336        assert_eq!(normalize_index(5, 5), Some(5));
1337        assert_eq!(normalize_index(6, 3), None);
1338    }
1339
1340    #[test]
1341    fn normalize_index_neg() {
1342        assert_eq!(normalize_index(-4, 6), Some(2));
1343        assert_eq!(normalize_index(-5, 5), Some(0));
1344        assert_eq!(normalize_index(-6, 3), None);
1345    }
1346}