Skip to main content

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::{Cow, ToOwned};
14use alloc::boxed::Box;
15use alloc::collections::BTreeSet;
16use alloc::rc::Rc;
17use alloc::string::String;
18use alloc::sync::Arc;
19use alloc::vec;
20use alloc::vec::Vec;
21use core::fmt::{Debug, Display, Formatter};
22use core::num::NonZeroUsize;
23use core::ops::Deref; // used in BorrowedOrRc.as_str
24use core::ops::Range;
25use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
26
27use crate::RuleType;
28use crate::error::{Error, ErrorVariant};
29use crate::iterators::pairs::new;
30use crate::iterators::{QueueableToken, pairs};
31use crate::position::Position;
32use crate::span::Span;
33use crate::stack::Stack;
34
35/// The current lookahead status of a [`ParserState`].
36///
37/// [`ParserState`]: struct.ParserState.html
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub enum Lookahead {
40    /// The positive predicate, written as an ampersand &,
41    /// attempts to match its inner expression.
42    /// If the inner expression succeeds, parsing continues,
43    /// but at the same position as the predicate —
44    /// &foo ~ bar is thus a kind of "AND" statement:
45    /// "the input string must match foo AND bar".
46    /// If the inner expression fails,
47    /// the whole expression fails too.
48    Positive,
49    /// The negative predicate, written as an exclamation mark !,
50    /// attempts to match its inner expression.
51    /// If the inner expression fails, the predicate succeeds
52    /// and parsing continues at the same position as the predicate.
53    /// If the inner expression succeeds, the predicate fails —
54    /// !foo ~ bar is thus a kind of "NOT" statement:
55    /// "the input string must match bar but NOT foo".
56    Negative,
57    /// No lookahead (i.e. it will consume input).
58    None,
59}
60
61/// The current atomicity of a [`ParserState`].
62///
63/// [`ParserState`]: struct.ParserState.html
64#[derive(Clone, Copy, Debug, Eq, PartialEq)]
65pub enum Atomicity {
66    /// prevents implicit whitespace: inside an atomic rule,
67    /// the tilde ~ means "immediately followed by",
68    /// and repetition operators (asterisk * and plus sign +)
69    /// have no implicit separation. In addition, all other rules
70    /// called from an atomic rule are also treated as atomic.
71    /// (interior matching rules are silent)
72    Atomic,
73    /// The same as atomic, but inner tokens are produced as normal.
74    CompoundAtomic,
75    /// implicit whitespace is enabled
76    NonAtomic,
77}
78
79/// Type alias to simplify specifying the return value of chained closures.
80pub type ParseResult<S> = Result<S, S>;
81
82/// Match direction for the stack. Used in `PEEK[a..b]`/`stack_match_peek_slice`.
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum MatchDir {
85    /// from the bottom to the top of the stack
86    BottomToTop,
87    /// from the top to the bottom of the stack
88    TopToBottom,
89}
90
91static CALL_LIMIT: AtomicUsize = AtomicUsize::new(0);
92
93/// Sets the maximum cumulative number of counted parser-state calls.
94///
95/// Invocations of [`ParserState::rule`], [`ParserState::sequence`],
96/// [`ParserState::repeat`], [`ParserState::optional`], [`ParserState::lookahead`],
97/// [`ParserState::atomic`], and [`ParserState::stack_push`] consume this budget.
98/// Calls are not refunded after returning or backtracking. Repetition counts
99/// its entry, not each iteration of its closure. Primitive matches and
100/// [`ParserState::restore_on_err`] do not consume this cumulative budget.
101/// A refused entry fails the parse with "call limit reached", even if an
102/// alternative or optional expression would otherwise recover.
103///
104/// This process-wide setting is captured when each [`ParserState`] is created.
105/// `None` disables the cumulative budget. Available native stack space is
106/// checked automatically in `std` builds on the [default native targets].
107/// Other targets require the `stack-guard` feature (which enables `std`).
108/// These checks are independent of this setting; insufficient space fails
109/// parsing with "stack limit reached".
110/// Stack checking is best-effort when native stack bounds cannot be determined
111/// and is not available in `no_std` builds. It cannot protect arbitrary user
112/// callbacks or work outside the parser's guarded entry points.
113/// It is not an elapsed-time or memory limit: individual operations and
114/// user closures may perform work without additional counted calls.
115/// See the [resource-limit guidance] for enforcing deadlines.
116///
117/// [resource-limit guidance]: https://github.com/pest-parser/pest/blob/master/SECURITY.md#parsing-untrusted-input
118/// [default native targets]: https://github.com/pest-parser/pest/blob/master/README.md#native-stack-guard
119pub fn set_call_limit(limit: Option<NonZeroUsize>) {
120    CALL_LIMIT.store(
121        limit.map(|limit| limit.get()).unwrap_or(0),
122        Ordering::Relaxed,
123    );
124}
125
126static ERROR_DETAIL: AtomicBool = AtomicBool::new(false);
127
128/// Sets whether information for more error details
129/// should be collected. This is useful for debugging
130/// parser errors (as it leads to more comprehensive
131/// error messages), but it has a higher performance cost.
132/// (hence, it's off by default)
133///
134/// # Arguments
135///
136/// * `enabled` - Whether to enable the collection for
137///   more error details.
138pub fn set_error_detail(enabled: bool) {
139    ERROR_DETAIL.store(enabled, Ordering::Relaxed);
140}
141
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143enum LimitKind {
144    Call,
145    Stack,
146}
147
148const REQUIRED_STACK_SPACE: usize = 64 * 1024;
149
150#[inline]
151fn remaining_stack() -> Option<usize> {
152    #[cfg(feature = "std")]
153    return pest_stack_guard::remaining_stack();
154    #[cfg(not(feature = "std"))]
155    return None;
156}
157
158#[derive(Debug)]
159struct CallLimitTracker {
160    current_call_limit: Option<(usize, usize)>,
161    limit_reached: Option<LimitKind>,
162}
163
164impl Default for CallLimitTracker {
165    fn default() -> Self {
166        let call_limit = CALL_LIMIT.load(Ordering::Relaxed);
167        Self {
168            current_call_limit: (call_limit > 0).then_some((0, call_limit)),
169            limit_reached: None,
170        }
171    }
172}
173
174impl CallLimitTracker {
175    fn limit_reached(&self) -> bool {
176        self.limit_reached.is_some()
177    }
178
179    fn error_message(&self) -> Option<&'static str> {
180        self.limit_reached.map(|cause| match cause {
181            LimitKind::Call => "call limit reached",
182            LimitKind::Stack => "stack limit reached",
183        })
184    }
185
186    fn enter(&mut self, count_call: bool) -> bool {
187        if self.limit_reached() {
188            return false;
189        }
190        if count_call
191            && self
192                .current_call_limit
193                .is_some_and(|(current, limit)| current >= limit)
194        {
195            self.limit_reached = Some(LimitKind::Call);
196            return false;
197        }
198        let remaining = remaining_stack();
199        self.enter_with_stack(count_call, remaining)
200    }
201
202    fn enter_with_stack(&mut self, count_call: bool, stack_space: Option<usize>) -> bool {
203        if self.limit_reached() {
204            return false;
205        }
206        let cause = if count_call
207            && self
208                .current_call_limit
209                .is_some_and(|(current, limit)| current >= limit)
210        {
211            Some(LimitKind::Call)
212        } else if stack_space.is_some_and(|remaining| remaining < REQUIRED_STACK_SPACE) {
213            Some(LimitKind::Stack)
214        } else {
215            None
216        };
217        if let Some(cause) = cause {
218            self.limit_reached = Some(cause);
219            return false;
220        }
221        if count_call && let Some((current, _)) = &mut self.current_call_limit {
222            *current += 1;
223        }
224        true
225    }
226}
227
228/// Number of call stacks that may result from a sequence of rules parsing.
229const CALL_STACK_INITIAL_CAPACITY: usize = 20;
230/// Max (un)expected number of tokens that we may see on the parsing error position.
231const EXPECTED_TOKENS_INITIAL_CAPACITY: usize = 30;
232/// Max rule children number for which we'll extend calls stacks.
233///
234/// In case rule we're working with has too many children rules that failed in parsing,
235/// we don't want to store long stacks for all of them. If rule has more than this number
236/// of failed children, they all will be collapsed in a parent rule.
237const CALL_STACK_CHILDREN_THRESHOLD: usize = 4;
238
239/// Structure tracking errored parsing call (associated with specific `ParserState` function).
240#[derive(Debug, Hash, PartialEq, Eq, Clone, PartialOrd, Ord)]
241pub enum ParseAttempt<R> {
242    /// Call of `rule` errored.
243    Rule(R),
244    /// Call of token element (e.g., `match_string` or `match_insensitive`) errored.
245    /// Works as indicator of that leaf node is not a rule. In order to get the token value we
246    /// can address `ParseAttempts` `(un)expected_tokens`.
247    Token,
248}
249
250impl<R> ParseAttempt<R> {
251    pub fn get_rule(&self) -> Option<&R> {
252        match self {
253            ParseAttempt::Rule(r) => Some(r),
254            ParseAttempt::Token => None,
255        }
256    }
257}
258
259/// Rules call stack.
260/// Contains sequence of rule calls that resulted in new parsing attempt.
261#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
262pub struct RulesCallStack<R> {
263    /// Deepest rule caused a parsing error (ParseAttempt::Token transformed into a rule).
264    pub deepest: ParseAttempt<R>,
265    /// Most top rule covering `deepest`.
266    pub parent: Option<R>,
267}
268
269impl<R> RulesCallStack<R> {
270    fn new(deepest: ParseAttempt<R>) -> RulesCallStack<R> {
271        RulesCallStack {
272            deepest,
273            parent: None,
274        }
275    }
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
279pub enum ParsingToken {
280    Sensitive { token: String },
281    Insensitive { token: String },
282    Range { start: char, end: char },
283    BuiltInRule,
284}
285
286impl Display for ParsingToken {
287    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
288        match self {
289            ParsingToken::Sensitive { token } => write!(f, "{token}"),
290            ParsingToken::Insensitive { token } => write!(f, "{}", token.to_uppercase()),
291            ParsingToken::Range { start, end } => write!(f, "{start}..{end}"),
292            ParsingToken::BuiltInRule => write!(f, "BUILTIN_RULE"),
293        }
294    }
295}
296/// A helper that provides efficient string handling without unnecessary copying.
297/// We use `Arc<String>` instead of `Cow<'i, str>` to avoid copying strings when cloning the `Owned` variant, since
298/// `Arc::clone` only increments a reference count. [SpanOrLiteral] needs to be [Send] and [Sync]`, so we use [Arc]
299/// instead of [Rc].
300///
301/// (We need to clone this struct to detach it from the `&self` borrow in [SpanOrLiteral::as_borrowed_or_rc], so that
302/// we can then call `self.match_string` (a `mut self` method).
303#[derive(Debug, Clone)]
304enum BorrowedOrArc<'i> {
305    Borrowed(&'i str),
306    Owned(Arc<String>),
307}
308
309/// A holder for a literal string, for use in `push_literal`. This is typically a `&'static str`, but is an owned
310/// `Rc<String>` for the pest vm.
311impl<'i> BorrowedOrArc<'i> {
312    fn as_str<'a: 'i>(&'a self) -> &'a str {
313        match self {
314            BorrowedOrArc::Borrowed(s) => s,
315            BorrowedOrArc::Owned(s) => s.deref(),
316        }
317    }
318}
319
320impl From<Cow<'static, str>> for BorrowedOrArc<'_> {
321    fn from(value: Cow<'static, str>) -> Self {
322        match value {
323            Cow::Borrowed(s) => Self::Borrowed(s),
324            Cow::Owned(s) => Self::Owned(Arc::new(s)),
325        }
326    }
327}
328
329#[derive(Debug, Clone)]
330enum SpanOrLiteral<'i> {
331    Span(Span<'i>),
332    Literal(BorrowedOrArc<'i>),
333}
334
335impl<'i> SpanOrLiteral<'i> {
336    #[inline]
337    fn as_borrowed_or_rc(&self) -> BorrowedOrArc<'i> {
338        match self {
339            Self::Span(s) => BorrowedOrArc::Borrowed(s.as_str()),
340            Self::Literal(s) => s.clone(),
341        }
342    }
343}
344
345/// Structure that tracks all the parsing attempts made on the max position.
346/// We want to give an error hint about parsing rules that succeeded
347/// at the farthest input position.
348/// The intuition is such rules will be most likely the query user initially wanted to write.
349#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
350pub struct ParseAttempts<R> {
351    /// Indicates whether the parsing attempts are tracked.
352    enabled: bool,
353    /// Vec of rule calls sequences awaiting tokens at the same `max_position`.
354    /// If there are several stacks in vec, it means all those rule stacks are "equal"
355    /// because their attempts occurred on the same position.
356    pub call_stacks: Vec<RulesCallStack<R>>,
357    /// Tokens that could be putted at `max_position`
358    /// in order to get a valid grammar query.
359    expected_tokens: Vec<ParsingToken>,
360    /// Tokens that we've prohibited to be putted at `max_position`
361    /// in order to get a valid grammar query.
362    unexpected_tokens: Vec<ParsingToken>,
363    /// Max position at which we were expecting to see one of `expected_tokens`.
364    pub max_position: usize,
365}
366
367impl<R: RuleType> ParseAttempts<R> {
368    /// Create new `ParseAttempts` instance with `call_stacks` and `expected_tokens`
369    /// initialized with capacity.
370    pub fn new() -> Self {
371        Self {
372            call_stacks: Vec::with_capacity(CALL_STACK_INITIAL_CAPACITY),
373            expected_tokens: Vec::with_capacity(EXPECTED_TOKENS_INITIAL_CAPACITY),
374            unexpected_tokens: Vec::with_capacity(EXPECTED_TOKENS_INITIAL_CAPACITY),
375            max_position: 0,
376            enabled: ERROR_DETAIL.load(Ordering::Relaxed),
377        }
378    }
379
380    /// Get number of currently present call stacks.
381    fn call_stacks_number(&self) -> usize {
382        self.call_stacks.len()
383    }
384
385    pub fn expected_tokens(&self) -> Vec<ParsingToken> {
386        self.expected_tokens
387            .iter()
388            .cloned()
389            .collect::<BTreeSet<_>>()
390            .into_iter()
391            .collect()
392    }
393
394    pub fn unexpected_tokens(&self) -> Vec<ParsingToken> {
395        self.unexpected_tokens
396            .iter()
397            .cloned()
398            .collect::<BTreeSet<_>>()
399            .into_iter()
400            .collect()
401    }
402
403    /// Retrieve call stacks.
404    pub fn call_stacks(&self) -> Vec<RulesCallStack<R>> {
405        self.call_stacks
406            .iter()
407            .cloned()
408            .collect::<BTreeSet<_>>()
409            .into_iter()
410            .collect()
411    }
412
413    /// In case we've tried to parse a rule, which start position is bigger than previous
414    /// `max_position` it means that we've advanced in our parsing and found better candidate.
415    ///
416    /// `start_index` is:
417    /// * Number of call stacks present in state at the moment current `rule` was called. The idea
418    ///   is that we'd like to update only those stacks that originated from the current `rule` and
419    ///   not from those that were called previously.
420    /// * 0 in case we've successfully parsed some token since the moment `rule` was called.
421    fn try_add_new_stack_rule(&mut self, rule: R, start_index: usize) {
422        let mut non_token_call_stacks = Vec::new();
423        let mut token_call_stack_met = false;
424        for call_stack in self.call_stacks.iter().skip(start_index) {
425            if matches!(call_stack.deepest, ParseAttempt::Token) {
426                token_call_stack_met = true;
427            } else {
428                non_token_call_stacks.push(call_stack.clone())
429            }
430        }
431        if token_call_stack_met && non_token_call_stacks.is_empty() {
432            // If `non_token_call_stacks` is not empty we wouldn't like to add a new standalone
433            // `RulesCallStack::new(ParseAttempt::Token)` (that will later be transformed into a
434            // rule) as soon as it doesn't give us any useful additional info.
435            non_token_call_stacks.push(RulesCallStack::new(ParseAttempt::Token));
436        }
437        self.call_stacks
438            .splice(start_index.., non_token_call_stacks);
439
440        let children_number_over_threshold =
441            self.call_stacks_number() - start_index >= CALL_STACK_CHILDREN_THRESHOLD;
442        if children_number_over_threshold {
443            self.call_stacks.truncate(start_index);
444            self.call_stacks
445                .push(RulesCallStack::new(ParseAttempt::Rule(rule)));
446        } else {
447            for call_stack in self.call_stacks.iter_mut().skip(start_index) {
448                if matches!(call_stack.deepest, ParseAttempt::Token) {
449                    call_stack.deepest = ParseAttempt::Rule(rule);
450                } else {
451                    call_stack.parent = Some(rule);
452                }
453            }
454        }
455    }
456
457    /// If `expected` flag is set to false, it means we've successfully parsed token being in the
458    /// state of negative lookahead and want to track `token` in the `unexpected_tokens`. Otherwise,
459    /// we want to track it the `expected_tokens`. Let's call chosen vec a `target_vec`.
460    ///
461    /// In case `position` is:
462    /// * Equal to `max_position`, add `token` to `target_vec`,
463    /// * Bigger than `max_position`, set `token` as the only new element of `target_vec`.
464    #[allow(clippy::comparison_chain)]
465    fn try_add_new_token(
466        &mut self,
467        token: ParsingToken,
468        start_position: usize,
469        position: usize,
470        negative_lookahead: bool,
471    ) {
472        let target_vec_push_token = |attempts: &mut ParseAttempts<R>| {
473            let target_vec = if negative_lookahead {
474                &mut attempts.unexpected_tokens
475            } else {
476                &mut attempts.expected_tokens
477            };
478            target_vec.push(token);
479        };
480
481        if position > self.max_position {
482            if negative_lookahead && start_position > self.max_position {
483                // We encountered a sequence under negative lookahead.
484                // We would like to track only first failed token in this sequence (which
485                // `start_position` should be equal to `self.max_position`).
486                return;
487            }
488            target_vec_push_token(self);
489
490            if negative_lookahead {
491                // In case of successful parsing of token under negative lookahead the only
492                // thing we'd like to do is to track the token in the `unexpected_tokens`.
493                return;
494            }
495            self.max_position = position;
496            self.expected_tokens.clear();
497            self.unexpected_tokens.clear();
498            self.call_stacks.clear();
499            self.call_stacks
500                .push(RulesCallStack::new(ParseAttempt::Token));
501        } else if position == self.max_position {
502            target_vec_push_token(self);
503            self.call_stacks
504                .push(RulesCallStack::new(ParseAttempt::Token));
505        }
506    }
507
508    /// Reset state in case we've successfully parsed some token in
509    /// `match_string` or `match_insensitive`.
510    fn nullify_expected_tokens(&mut self, new_max_position: usize) {
511        self.call_stacks.clear();
512        self.expected_tokens.clear();
513        self.unexpected_tokens.clear();
514        self.max_position = new_max_position;
515    }
516}
517
518impl<R: RuleType> Default for ParseAttempts<R> {
519    fn default() -> Self {
520        Self::new()
521    }
522}
523
524/// The complete state of a [`Parser`].
525///
526/// [`Parser`]: trait.Parser.html
527#[derive(Debug)]
528pub struct ParserState<'i, R: RuleType> {
529    /// Current position from which we try to apply some parser function.
530    /// Initially is 0.
531    /// E.g., we are parsing `create user 'Bobby'` query, we parsed "create" via `match_insensitive`
532    /// and switched our `position` from 0 to the length of "create".
533    ///
534    /// E.g., see `match_string` -> `self.position.match_string(string)` which updates `self.pos`.
535    position: Position<'i>,
536    /// Queue representing rules partially (`QueueableToken::Start`) and
537    /// totally (`QueueableToken::End`) parsed. When entering rule we put it in the queue in a state
538    /// of `Start` and after all its sublogic (subrules or strings) are parsed, we change it to
539    /// `End` state.
540    queue: Vec<QueueableToken<'i, R>>,
541    /// Status set in case specific lookahead logic is used in grammar.
542    /// See `Lookahead` for more information.
543    lookahead: Lookahead,
544    /// Rules that we HAVE expected, tried to parse, but failed.
545    pos_attempts: Vec<R>,
546    /// Rules that we have NOT expected, tried to parse, but failed.
547    neg_attempts: Vec<R>,
548    /// Max position in the query from which we've tried to parse some rule but failed.
549    attempt_pos: usize,
550    /// Current atomicity status. For more information see `Atomicity`.
551    atomicity: Atomicity,
552    /// Helper structure tracking `Stack` status (used in case grammar contains stack PUSH/POP
553    /// invocations).
554    stack: Stack<SpanOrLiteral<'i>>,
555    /// Tracks cumulative parser-state calls and call/stack limit exhaustion.
556    call_tracker: CallLimitTracker,
557    /// Together with tracking of `pos_attempts` and `attempt_pos`
558    /// as a pair of (list of rules that we've tried to parse but failed, max parsed position)
559    /// we track those rules (which we've tried to parse at the same max pos) at this helper struct.
560    ///
561    /// Note, that we may try to parse several rules on different positions. We want to track only
562    /// those rules, which attempt position is bigger, because we consider that it's nearer to the
563    /// query that user really wanted to pass.
564    ///
565    /// E.g. we have a query `create user "Bobby"` and two root rules:
566    /// * CreateUser  = { "create" ~ "user"  ~ Name }
567    /// * CreateTable = { "create" ~ "table" ~ Name }
568    /// * Name = { SOME_DEFINITION }
569    ///
570    /// While parsing the query we'll update tracker position to the start of "Bobby", because we'd
571    /// successfully parse "create" + "user" (and not "table").
572    parse_attempts: ParseAttempts<R>,
573}
574
575/// Creates a `ParserState` from a `&str`, supplying it to a closure `f`.
576///
577/// # Examples
578///
579/// ```
580/// # use pest;
581/// let input = "";
582/// pest::state::<(), _>(input, |s| Ok(s)).unwrap();
583/// ```
584#[allow(clippy::perf)]
585pub fn state<'i, R: RuleType, F>(input: &'i str, f: F) -> Result<pairs::Pairs<'i, R>, Error<R>>
586where
587    F: FnOnce(Box<ParserState<'i, R>>) -> ParseResult<Box<ParserState<'i, R>>>,
588{
589    let state = ParserState::new(input);
590
591    match f(state) {
592        Ok(state) if !state.call_tracker.limit_reached() => {
593            let len = state.queue.len();
594            Ok(new(Rc::new(state.queue), input, None, 0, len))
595        }
596        Ok(mut state) | Err(mut state) => {
597            let variant = if let Some(message) = state.call_tracker.error_message() {
598                ErrorVariant::CustomError {
599                    message: message.to_owned(),
600                }
601            } else {
602                state.pos_attempts.sort();
603                state.pos_attempts.dedup();
604                state.neg_attempts.sort();
605                state.neg_attempts.dedup();
606                ErrorVariant::ParsingError {
607                    positives: state.pos_attempts.clone(),
608                    negatives: state.neg_attempts.clone(),
609                }
610            };
611
612            if state.parse_attempts.enabled {
613                Err(Error::new_from_pos_with_parsing_attempts(
614                    variant,
615                    Position::new_internal(input, state.attempt_pos),
616                    state.parse_attempts.clone(),
617                ))
618            } else {
619                Err(Error::new_from_pos(
620                    variant,
621                    Position::new_internal(input, state.attempt_pos),
622                ))
623            }
624        }
625    }
626}
627
628impl<'i, R: RuleType> ParserState<'i, R> {
629    /// Allocates a fresh `ParserState` object to the heap and returns the owned `Box`. This `Box`
630    /// will be passed from closure to closure based on the needs of the specified `Parser`.
631    ///
632    /// # Examples
633    ///
634    /// ```
635    /// # use pest;
636    /// let input = "";
637    /// let state: Box<pest::ParserState<&str>> = pest::ParserState::new(input);
638    /// ```
639    pub fn new(input: &'i str) -> Box<Self> {
640        Box::new(ParserState {
641            position: Position::from_start(input),
642            queue: vec![],
643            lookahead: Lookahead::None,
644            pos_attempts: vec![],
645            neg_attempts: vec![],
646            attempt_pos: 0,
647            atomicity: Atomicity::NonAtomic,
648            stack: Stack::new(),
649            call_tracker: Default::default(),
650            parse_attempts: ParseAttempts::new(),
651        })
652    }
653
654    /// Get all parse attempts after process of parsing is finished.
655    pub fn get_parse_attempts(&self) -> &ParseAttempts<R> {
656        &self.parse_attempts
657    }
658
659    /// Returns a reference to the current `Position` of the `ParserState`.
660    ///
661    /// # Examples
662    ///
663    /// ```
664    /// # use pest;
665    /// # #[allow(non_camel_case_types)]
666    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
667    /// enum Rule {
668    ///     ab
669    /// }
670    ///
671    /// let input = "ab";
672    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
673    /// let position = state.position();
674    /// assert_eq!(position.pos(), 0);
675    /// ```
676    pub fn position(&self) -> &Position<'i> {
677        &self.position
678    }
679
680    /// Returns the current atomicity of the `ParserState`.
681    ///
682    /// # Examples
683    ///
684    /// ```
685    /// # use pest;
686    /// # use pest::Atomicity;
687    /// # #[allow(non_camel_case_types)]
688    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
689    /// enum Rule {
690    ///     ab
691    /// }
692    ///
693    /// let input = "ab";
694    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
695    /// let atomicity = state.atomicity();
696    /// assert_eq!(atomicity, Atomicity::NonAtomic);
697    /// ```
698    pub fn atomicity(&self) -> Atomicity {
699        self.atomicity
700    }
701
702    #[inline]
703    fn inc_call_check_limit(mut self: Box<Self>) -> ParseResult<Box<Self>> {
704        if !self.call_tracker.enter(true) {
705            return Err(self);
706        }
707        Ok(self)
708    }
709
710    /// Checks native stack headroom without consuming a counted parser call.
711    #[doc(hidden)]
712    #[inline]
713    pub fn check_stack_limit(mut self: Box<Self>) -> ParseResult<Box<Self>> {
714        if !self.call_tracker.enter(false) {
715            return Err(self);
716        }
717        Ok(self)
718    }
719
720    /// Wrapper needed to generate tokens. This will associate the `R` type rule to the closure
721    /// meant to match the rule.
722    ///
723    /// # Examples
724    ///
725    /// ```
726    /// # use pest;
727    /// # #[allow(non_camel_case_types)]
728    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
729    /// enum Rule {
730    ///     a
731    /// }
732    ///
733    /// let input = "a";
734    /// let pairs: Vec<_> = pest::state(input, |state| {
735    ///     state.rule(Rule::a, |s| Ok(s))
736    /// }).unwrap().collect();
737    ///
738    /// assert_eq!(pairs.len(), 1);
739    /// ```
740    #[inline]
741    pub fn rule<F>(mut self: Box<Self>, rule: R, f: F) -> ParseResult<Box<Self>>
742    where
743        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
744    {
745        self = self.inc_call_check_limit()?;
746        // Position from which this `rule` starts parsing.
747        let actual_pos = self.position.pos();
748        // Remember index of the `self.queue` element that will be associated with this `rule`.
749        let index = self.queue.len();
750
751        let (pos_attempts_index, neg_attempts_index) = if actual_pos == self.attempt_pos {
752            (self.pos_attempts.len(), self.neg_attempts.len())
753        } else {
754            // Attempts have not been cleared yet since the attempt_pos is older.
755            (0, 0)
756        };
757
758        if self.lookahead == Lookahead::None && self.atomicity != Atomicity::Atomic {
759            // Pair's position will only be known after running the closure.
760            self.queue.push(QueueableToken::Start {
761                end_token_index: 0,
762                input_pos: actual_pos,
763            });
764        }
765
766        // Remember attempts number before `f` call.
767        // In `track` using this variable we can say, how many attempts were added
768        // during children rules traversal.
769        let attempts = self.attempts_at(actual_pos);
770        // Number of call stacks present in `self.parse_attempts` before `f` call.
771        // We need to remember this number only in case there wasn't found any farther attempt.
772        // E.g. we are handling rule, on start position of which may be tested two
773        // children rules. At the moment we'll return from `f` call below,
774        // there will be two more children rules in `self.parse_attempts` that we'll
775        // consider to be the children of current `rule`.
776        let mut remember_call_stacks_number = self.parse_attempts.call_stacks_number();
777        // Max parsing attempt position at the moment of `rule` handling.
778        // It case it's raised during children rules handling, it means
779        // we've made a parsing progress.
780        let remember_max_position = self.parse_attempts.max_position;
781
782        let result = f(self);
783
784        let mut try_add_rule_to_stack = |new_state: &mut Box<ParserState<'_, R>>| {
785            if new_state.parse_attempts.max_position > remember_max_position {
786                // It means that one of `match_string` or e.g. `match_insensitive` function calls
787                // have already erased `self.parse_attempts.call_stacks` and that previously
788                // remembered values are not valid anymore.
789                remember_call_stacks_number = 0;
790            }
791            if !matches!(new_state.atomicity, Atomicity::Atomic) {
792                new_state
793                    .parse_attempts
794                    .try_add_new_stack_rule(rule, remember_call_stacks_number);
795            }
796        };
797
798        match result {
799            Ok(mut new_state) => {
800                if new_state.lookahead == Lookahead::Negative {
801                    new_state.track(
802                        rule,
803                        actual_pos,
804                        pos_attempts_index,
805                        neg_attempts_index,
806                        attempts,
807                    );
808                }
809
810                if new_state.lookahead == Lookahead::None
811                    && new_state.atomicity != Atomicity::Atomic
812                {
813                    // Index of `QueueableToken::End` token added below
814                    // that corresponds to previously added `QueueableToken::Start` token.
815                    let new_index = new_state.queue.len();
816                    match new_state.queue[index] {
817                        QueueableToken::Start {
818                            ref mut end_token_index,
819                            ..
820                        } => *end_token_index = new_index,
821                        _ => unreachable!(),
822                    };
823
824                    let new_pos = new_state.position.pos();
825
826                    new_state.queue.push(QueueableToken::End {
827                        start_token_index: index,
828                        rule,
829                        tag: None,
830                        input_pos: new_pos,
831                    });
832                }
833
834                // Note, that we need to count positive parsing results too, because we can fail in
835                // optional rule call inside which may lie the farthest
836                // parsed token.
837                if new_state.parse_attempts.enabled {
838                    try_add_rule_to_stack(&mut new_state);
839                }
840                Ok(new_state)
841            }
842            Err(mut new_state) => {
843                if new_state.lookahead != Lookahead::Negative {
844                    new_state.track(
845                        rule,
846                        actual_pos,
847                        pos_attempts_index,
848                        neg_attempts_index,
849                        attempts,
850                    );
851                    if new_state.parse_attempts.enabled {
852                        try_add_rule_to_stack(&mut new_state);
853                    }
854                }
855
856                if new_state.lookahead == Lookahead::None
857                    && new_state.atomicity != Atomicity::Atomic
858                {
859                    new_state.queue.truncate(index);
860                }
861
862                Err(new_state)
863            }
864        }
865    }
866
867    /// Tag current node
868    ///
869    /// # Examples
870    ///
871    /// Try to recognize the one specified in a set of characters
872    ///
873    /// ```
874    /// use pest::{state, ParseResult, ParserState, iterators::Pair};
875    /// #[allow(non_camel_case_types)]
876    /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
877    /// enum Rule {
878    ///     character,
879    /// }
880    /// fn mark_c(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
881    ///     state.sequence(|state| {
882    ///         character(state)
883    ///             .and_then(|state| character(state))
884    ///             .and_then(|state| character(state))
885    ///             .and_then(|state| state.tag_node("c"))
886    ///             .and_then(|state| character(state))
887    ///     })
888    /// }
889    /// fn character(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
890    ///     state.rule(Rule::character, |state| state.match_range('a'..'z'))
891    /// }
892    ///
893    /// let input = "abcd";
894    /// let pairs = state(input, mark_c).unwrap();
895    /// // find all node tag as `c`
896    /// let find: Vec<Pair<Rule>> = pairs.filter(|s| s.as_node_tag() == Some("c")).collect();
897    /// assert_eq!(find[0].as_str(), "c")
898    /// ```
899    #[inline]
900    pub fn tag_node(mut self: Box<Self>, tag: &'i str) -> ParseResult<Box<Self>> {
901        if self.lookahead != Lookahead::None {
902            return Ok(self);
903        }
904        if let Some(QueueableToken::End { tag: old, .. }) = self.queue.last_mut() {
905            *old = Some(tag)
906        }
907        Ok(self)
908    }
909
910    /// Get number of allowed rules attempts + prohibited rules attempts.
911    fn attempts_at(&self, pos: usize) -> usize {
912        if self.attempt_pos == pos {
913            self.pos_attempts.len() + self.neg_attempts.len()
914        } else {
915            0
916        }
917    }
918
919    fn track(
920        &mut self,
921        rule: R,
922        pos: usize,
923        pos_attempts_index: usize,
924        neg_attempts_index: usize,
925        prev_attempts: usize,
926    ) {
927        if self.atomicity == Atomicity::Atomic {
928            return;
929        }
930
931        // If nested rules made no progress, there is no use to report them; it's only useful to
932        // track the current rule, the exception being when only one attempt has been made during
933        // the children rules.
934        let curr_attempts = self.attempts_at(pos);
935        if curr_attempts > prev_attempts && curr_attempts - prev_attempts == 1 {
936            return;
937        }
938
939        if pos == self.attempt_pos {
940            self.pos_attempts.truncate(pos_attempts_index);
941            self.neg_attempts.truncate(neg_attempts_index);
942        }
943
944        if pos > self.attempt_pos {
945            self.pos_attempts.clear();
946            self.neg_attempts.clear();
947            self.attempt_pos = pos;
948        }
949
950        let attempts = if self.lookahead != Lookahead::Negative {
951            &mut self.pos_attempts
952        } else {
953            &mut self.neg_attempts
954        };
955
956        if pos == self.attempt_pos {
957            attempts.push(rule);
958        }
959    }
960
961    /// Starts a sequence of transformations provided by `f` from the `Box<ParserState>`. Returns
962    /// the same `Result` returned by `f` in the case of an `Ok`, or `Err` with the current
963    /// `Box<ParserState>` otherwise.
964    ///
965    /// This method is useful to parse sequences that only match together which usually come in the
966    /// form of chained `Result`s with
967    /// [`Result::and_then`](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then).
968    ///
969    ///
970    /// # Examples
971    ///
972    /// ```
973    /// # use pest;
974    /// # #[allow(non_camel_case_types)]
975    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
976    /// enum Rule {
977    ///     a
978    /// }
979    ///
980    /// let input = "a";
981    /// let pairs: Vec<_> = pest::state(input, |state| {
982    ///     state.sequence(|s| {
983    ///         s.rule(Rule::a, |s| Ok(s)).and_then(|s| {
984    ///             s.match_string("b")
985    ///         })
986    ///     }).or_else(|s| {
987    ///         Ok(s)
988    ///     })
989    /// }).unwrap().collect();
990    ///
991    /// assert_eq!(pairs.len(), 0);
992    /// ```
993    #[inline]
994    pub fn sequence<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
995    where
996        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
997    {
998        self = self.inc_call_check_limit()?;
999        let token_index = self.queue.len();
1000        let initial_pos = self.position;
1001
1002        let result = f(self.checkpoint());
1003
1004        match result {
1005            Ok(new_state) => Ok(new_state.checkpoint_ok()),
1006            Err(mut new_state) => {
1007                // Restore the initial position and truncate the token queue.
1008                new_state.position = initial_pos;
1009                new_state.queue.truncate(token_index);
1010                Err(new_state.restore())
1011            }
1012        }
1013    }
1014
1015    /// Repeatedly applies the transformation provided by `f` from the `Box<ParserState>`. Returns
1016    /// `Ok` with the updated `Box<ParserState>` returned by `f` wrapped up in an `Err`.
1017    ///
1018    /// # Examples
1019    ///
1020    /// ```
1021    /// # use pest;
1022    /// # #[allow(non_camel_case_types)]
1023    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1024    /// enum Rule {
1025    ///     ab
1026    /// }
1027    ///
1028    /// let input = "aab";
1029    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1030    /// let mut result = state.repeat(|s| {
1031    ///     s.match_string("a")
1032    /// });
1033    /// assert!(result.is_ok());
1034    /// assert_eq!(result.unwrap().position().pos(), 2);
1035    ///
1036    /// state = pest::ParserState::new(input);
1037    /// result = state.repeat(|s| {
1038    ///     s.match_string("b")
1039    /// });
1040    /// assert!(result.is_ok());
1041    /// assert_eq!(result.unwrap().position().pos(), 0);
1042    /// ```
1043    #[inline]
1044    pub fn repeat<F>(mut self: Box<Self>, mut f: F) -> ParseResult<Box<Self>>
1045    where
1046        F: FnMut(Box<Self>) -> ParseResult<Box<Self>>,
1047    {
1048        self = self.inc_call_check_limit()?;
1049        let mut result = f(self);
1050
1051        loop {
1052            match result {
1053                Ok(state) => result = f(state),
1054                Err(state) if state.call_tracker.limit_reached() => return Err(state),
1055                Err(state) => return Ok(state),
1056            };
1057        }
1058    }
1059
1060    /// Optionally applies the transformation provided by `f` from the `Box<ParserState>`. Returns
1061    /// `Ok` with the updated `Box<ParserState>` returned by `f` regardless of the `Result`.
1062    ///
1063    /// # Examples
1064    ///
1065    /// ```
1066    /// # use pest;
1067    /// # #[allow(non_camel_case_types)]
1068    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1069    /// enum Rule {
1070    ///     ab
1071    /// }
1072    ///
1073    /// let input = "ab";
1074    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1075    /// let result = state.optional(|s| {
1076    ///     s.match_string("ab")
1077    /// });
1078    /// assert!(result.is_ok());
1079    ///
1080    /// state = pest::ParserState::new(input);
1081    /// let result = state.optional(|s| {
1082    ///     s.match_string("ac")
1083    /// });
1084    /// assert!(result.is_ok());
1085    /// ```
1086    #[inline]
1087    pub fn optional<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1088    where
1089        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1090    {
1091        self = self.inc_call_check_limit()?;
1092        match f(self) {
1093            Err(state) if state.call_tracker.limit_reached() => Err(state),
1094            Ok(state) | Err(state) => Ok(state),
1095        }
1096    }
1097
1098    /// Generic function to handle result of char/string/range parsing
1099    /// in order to track (un)expected tokens.
1100    fn handle_token_parse_result(
1101        &mut self,
1102        start_position: usize,
1103        token: ParsingToken,
1104        parse_succeeded: bool,
1105    ) {
1106        // New position after tracked parsed element for case of `parse_succeeded` is true.
1107        // Position of parsing failure otherwise.
1108        let current_pos = self.position.pos();
1109
1110        if parse_succeeded {
1111            if self.lookahead == Lookahead::Negative {
1112                self.parse_attempts
1113                    .try_add_new_token(token, start_position, current_pos, true);
1114            } else if current_pos > self.parse_attempts.max_position {
1115                self.parse_attempts.nullify_expected_tokens(current_pos);
1116            }
1117        } else if self.lookahead != Lookahead::Negative {
1118            self.parse_attempts
1119                .try_add_new_token(token, start_position, current_pos, false);
1120        }
1121    }
1122
1123    /// Attempts to match a single character based on a filter function. Returns `Ok` with the
1124    /// updated `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>`
1125    /// otherwise.
1126    ///
1127    /// # Examples
1128    ///
1129    /// ```
1130    /// # use pest;
1131    /// # #[allow(non_camel_case_types)]
1132    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1133    /// enum Rule {}
1134    ///
1135    /// let input = "ab";
1136    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1137    /// let result = state.match_char_by(|c| c.is_ascii());
1138    /// assert!(result.is_ok());
1139    /// assert_eq!(result.unwrap().position().pos(), 1);
1140    ///
1141    /// let input = "❤";
1142    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1143    /// let result = state.match_char_by(|c| c.is_ascii());
1144    /// assert!(result.is_err());
1145    /// assert_eq!(result.unwrap_err().position().pos(), 0);
1146    /// ```
1147    #[inline]
1148    pub fn match_char_by<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1149    where
1150        F: FnOnce(char) -> bool,
1151    {
1152        let start_position = self.position.pos();
1153        let succeeded = self.position.match_char_by(f);
1154        if self.parse_attempts.enabled {
1155            let token = ParsingToken::BuiltInRule;
1156            self.handle_token_parse_result(start_position, token, succeeded);
1157        }
1158        if succeeded { Ok(self) } else { Err(self) }
1159    }
1160
1161    /// Attempts to match the given string. Returns `Ok` with the updated `Box<ParserState>` if
1162    /// successful, or `Err` with the updated `Box<ParserState>` otherwise.
1163    ///
1164    /// # Examples
1165    ///
1166    /// ```
1167    /// # use pest;
1168    /// # #[allow(non_camel_case_types)]
1169    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1170    /// enum Rule {}
1171    ///
1172    /// let input = "ab";
1173    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1174    /// let mut result = state.match_string("ab");
1175    /// assert!(result.is_ok());
1176    /// assert_eq!(result.unwrap().position().pos(), 2);
1177    ///
1178    /// state = pest::ParserState::new(input);
1179    /// result = state.match_string("ac");
1180    /// assert!(result.is_err());
1181    /// assert_eq!(result.unwrap_err().position().pos(), 0);
1182    /// ```
1183    #[inline]
1184    pub fn match_string(mut self: Box<Self>, string: &str) -> ParseResult<Box<Self>> {
1185        let start_position = self.position.pos();
1186        let succeeded = self.position.match_string(string);
1187        if self.parse_attempts.enabled {
1188            let token = ParsingToken::Sensitive {
1189                token: String::from(string),
1190            };
1191            self.handle_token_parse_result(start_position, token, succeeded);
1192        }
1193        if succeeded { Ok(self) } else { Err(self) }
1194    }
1195
1196    /// Pushes the given literal to the stack, and always returns `Ok(Box<ParserState>)`.
1197    ///
1198    /// # Examples
1199    ///
1200    /// ```
1201    /// # use pest;
1202    /// # #[allow(non_camel_case_types)]
1203    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1204    /// enum Rule {}
1205    ///
1206    /// let input = "ab";
1207    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1208    ///
1209    /// let mut result = state.stack_push_literal("a");
1210    /// assert!(result.is_ok());
1211    /// assert_eq!(result.as_ref().unwrap().position().pos(), 0);
1212    ///
1213    /// let mut result = result.unwrap().stack_pop();
1214    /// assert!(result.is_ok());
1215    /// assert_eq!(result.unwrap().position().pos(), 1);
1216    /// ```
1217    #[inline]
1218    pub fn stack_push_literal(
1219        mut self: Box<Self>,
1220        string: impl Into<Cow<'static, str>>,
1221    ) -> ParseResult<Box<Self>> {
1222        // convert string into a Literal, and then into a BorrowedOrRc
1223        self.stack
1224            .push(SpanOrLiteral::Literal(string.into().into()));
1225        Ok(self)
1226    }
1227
1228    /// Attempts to case-insensitively match the given string. Returns `Ok` with the updated
1229    /// `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>` otherwise.
1230    ///
1231    /// # Examples
1232    ///
1233    /// ```
1234    /// # use pest;
1235    /// # #[allow(non_camel_case_types)]
1236    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1237    /// enum Rule {}
1238    ///
1239    /// let input = "ab";
1240    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1241    /// let mut result = state.match_insensitive("AB");
1242    /// assert!(result.is_ok());
1243    /// assert_eq!(result.unwrap().position().pos(), 2);
1244    ///
1245    /// state = pest::ParserState::new(input);
1246    /// result = state.match_insensitive("AC");
1247    /// assert!(result.is_err());
1248    /// assert_eq!(result.unwrap_err().position().pos(), 0);
1249    /// ```
1250    #[inline]
1251    pub fn match_insensitive(mut self: Box<Self>, string: &str) -> ParseResult<Box<Self>> {
1252        let start_position: usize = self.position().pos();
1253        let succeeded = self.position.match_insensitive(string);
1254        if self.parse_attempts.enabled {
1255            let token = ParsingToken::Insensitive {
1256                token: String::from(string),
1257            };
1258            self.handle_token_parse_result(start_position, token, succeeded);
1259        }
1260        if succeeded { Ok(self) } else { Err(self) }
1261    }
1262
1263    /// Attempts to match a single character from the given range. Returns `Ok` with the updated
1264    /// `Box<ParserState>` if successful, or `Err` with the updated `Box<ParserState>` otherwise.
1265    ///
1266    /// # Caution
1267    /// The provided `range` is interpreted as inclusive.
1268    ///
1269    /// # Examples
1270    ///
1271    /// ```
1272    /// # use pest;
1273    /// # #[allow(non_camel_case_types)]
1274    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1275    /// enum Rule {}
1276    ///
1277    /// let input = "ab";
1278    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1279    /// let mut result = state.match_range('a'..'z');
1280    /// assert!(result.is_ok());
1281    /// assert_eq!(result.unwrap().position().pos(), 1);
1282    ///
1283    /// state = pest::ParserState::new(input);
1284    /// result = state.match_range('A'..'Z');
1285    /// assert!(result.is_err());
1286    /// assert_eq!(result.unwrap_err().position().pos(), 0);
1287    /// ```
1288    #[inline]
1289    pub fn match_range(mut self: Box<Self>, range: Range<char>) -> ParseResult<Box<Self>> {
1290        let start_position = self.position().pos();
1291        let token = ParsingToken::Range {
1292            start: range.start,
1293            end: range.end,
1294        };
1295        let succeeded = self.position.match_range(range);
1296        if self.parse_attempts.enabled {
1297            self.handle_token_parse_result(start_position, token, succeeded);
1298        }
1299        if succeeded { Ok(self) } else { Err(self) }
1300    }
1301
1302    /// Attempts to skip `n` characters forward. Returns `Ok` with the updated `Box<ParserState>`
1303    /// if successful, or `Err` with the updated `Box<ParserState>` otherwise.
1304    ///
1305    /// # Examples
1306    ///
1307    /// ```
1308    /// # use pest;
1309    /// # #[allow(non_camel_case_types)]
1310    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1311    /// enum Rule {}
1312    ///
1313    /// let input = "ab";
1314    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1315    /// let mut result = state.skip(1);
1316    /// assert!(result.is_ok());
1317    /// assert_eq!(result.unwrap().position().pos(), 1);
1318    ///
1319    /// state = pest::ParserState::new(input);
1320    /// result = state.skip(3);
1321    /// assert!(result.is_err());
1322    /// assert_eq!(result.unwrap_err().position().pos(), 0);
1323    /// ```
1324    #[inline]
1325    pub fn skip(mut self: Box<Self>, n: usize) -> ParseResult<Box<Self>> {
1326        if self.position.skip(n) {
1327            Ok(self)
1328        } else {
1329            Err(self)
1330        }
1331    }
1332
1333    /// Attempts to skip forward until one of the given strings is found. Returns `Ok` with the
1334    /// updated `Box<ParserState>` whether or not one of the strings is found.
1335    ///
1336    /// # Examples
1337    ///
1338    /// ```
1339    /// # use pest;
1340    /// # #[allow(non_camel_case_types)]
1341    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1342    /// enum Rule {}
1343    ///
1344    /// let input = "abcd";
1345    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1346    /// let mut result = state.skip_until(&["c", "d"]);
1347    /// assert!(result.is_ok());
1348    /// assert_eq!(result.unwrap().position().pos(), 2);
1349    /// ```
1350    #[inline]
1351    pub fn skip_until(mut self: Box<Self>, strings: &[&str]) -> ParseResult<Box<Self>> {
1352        self.position.skip_until(strings);
1353        Ok(self)
1354    }
1355
1356    /// Attempts to match the start of the input. Returns `Ok` with the current `Box<ParserState>`
1357    /// if the parser has not yet advanced, or `Err` with the current `Box<ParserState>` otherwise.
1358    ///
1359    /// # Examples
1360    ///
1361    /// ```
1362    /// # use pest;
1363    /// # #[allow(non_camel_case_types)]
1364    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1365    /// enum Rule {}
1366    ///
1367    /// let input = "ab";
1368    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1369    /// let mut result = state.start_of_input();
1370    /// assert!(result.is_ok());
1371    ///
1372    /// state = pest::ParserState::new(input);
1373    /// state = state.match_string("ab").unwrap();
1374    /// result = state.start_of_input();
1375    /// assert!(result.is_err());
1376    /// ```
1377    #[inline]
1378    pub fn start_of_input(self: Box<Self>) -> ParseResult<Box<Self>> {
1379        if self.position.at_start() {
1380            Ok(self)
1381        } else {
1382            Err(self)
1383        }
1384    }
1385
1386    /// Attempts to match the end of the input. Returns `Ok` with the current `Box<ParserState>` if
1387    /// there is no input remaining, or `Err` with the current `Box<ParserState>` otherwise.
1388    ///
1389    /// # Examples
1390    ///
1391    /// ```
1392    /// # use pest;
1393    /// # #[allow(non_camel_case_types)]
1394    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1395    /// enum Rule {}
1396    ///
1397    /// let input = "ab";
1398    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1399    /// let mut result = state.end_of_input();
1400    /// assert!(result.is_err());
1401    ///
1402    /// state = pest::ParserState::new(input);
1403    /// state = state.match_string("ab").unwrap();
1404    /// result = state.end_of_input();
1405    /// assert!(result.is_ok());
1406    /// ```
1407    #[inline]
1408    pub fn end_of_input(self: Box<Self>) -> ParseResult<Box<Self>> {
1409        if self.position.at_end() {
1410            Ok(self)
1411        } else {
1412            Err(self)
1413        }
1414    }
1415
1416    /// Starts a lookahead transformation provided by `f` from the `Box<ParserState>`. It returns
1417    /// `Ok` with the current `Box<ParserState>` if `f` also returns an `Ok`, or `Err` with the current
1418    /// `Box<ParserState>` otherwise. If `is_positive` is `false`, it swaps the `Ok` and `Err`
1419    /// together, negating the `Result`.
1420    ///
1421    /// # Examples
1422    ///
1423    /// ```
1424    /// # use pest;
1425    /// # #[allow(non_camel_case_types)]
1426    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1427    /// enum Rule {
1428    ///     a
1429    /// }
1430    ///
1431    /// let input = "a";
1432    /// let pairs: Vec<_> = pest::state(input, |state| {
1433    ///     state.lookahead(true, |state| {
1434    ///         state.rule(Rule::a, |s| Ok(s))
1435    ///     })
1436    /// }).unwrap().collect();
1437    ///
1438    /// assert_eq!(pairs.len(), 0);
1439    /// ```
1440    #[inline]
1441    pub fn lookahead<F>(mut self: Box<Self>, is_positive: bool, f: F) -> ParseResult<Box<Self>>
1442    where
1443        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1444    {
1445        self = self.inc_call_check_limit()?;
1446        let initial_lookahead = self.lookahead;
1447
1448        self.lookahead = if is_positive {
1449            match initial_lookahead {
1450                Lookahead::None | Lookahead::Positive => Lookahead::Positive,
1451                Lookahead::Negative => Lookahead::Negative,
1452            }
1453        } else {
1454            match initial_lookahead {
1455                Lookahead::None | Lookahead::Positive => Lookahead::Negative,
1456                Lookahead::Negative => Lookahead::Positive,
1457            }
1458        };
1459
1460        let initial_pos = self.position;
1461
1462        let result = f(self.checkpoint());
1463
1464        let result_state = match result {
1465            Ok(mut new_state) => {
1466                new_state.position = initial_pos;
1467                new_state.lookahead = initial_lookahead;
1468                Ok(new_state.restore())
1469            }
1470            Err(mut new_state) => {
1471                new_state.position = initial_pos;
1472                new_state.lookahead = initial_lookahead;
1473                Err(new_state.restore())
1474            }
1475        };
1476
1477        let exhausted = match &result_state {
1478            Ok(state) | Err(state) => state.call_tracker.limit_reached(),
1479        };
1480        if exhausted {
1481            return Err(result_state.unwrap_or_else(|state| state));
1482        }
1483        if is_positive {
1484            result_state
1485        } else {
1486            match result_state {
1487                Ok(state) => Err(state),
1488                Err(state) => Ok(state),
1489            }
1490        }
1491    }
1492
1493    /// Transformation which stops `Token`s from being generated according to `is_atomic`.
1494    /// Used as wrapper over `rule` (or even another `atomic`) call.
1495    ///
1496    /// # Examples
1497    ///
1498    /// ```
1499    /// # use pest::{self, Atomicity};
1500    /// # #[allow(non_camel_case_types)]
1501    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1502    /// enum Rule {
1503    ///     a
1504    /// }
1505    ///
1506    /// let input = "a";
1507    /// let pairs: Vec<_> = pest::state(input, |state| {
1508    ///     state.atomic(Atomicity::Atomic, |s| {
1509    ///         s.rule(Rule::a, |s| Ok(s))
1510    ///     })
1511    /// }).unwrap().collect();
1512    ///
1513    /// assert_eq!(pairs.len(), 0);
1514    /// ```
1515    #[inline]
1516    pub fn atomic<F>(mut self: Box<Self>, atomicity: Atomicity, f: F) -> ParseResult<Box<Self>>
1517    where
1518        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1519    {
1520        self = self.inc_call_check_limit()?;
1521        // In case child parsing call is another `atomic` it will have its own atomicity status.
1522        let initial_atomicity = self.atomicity;
1523        // In case child atomicity is the same as we've demanded, we shouldn't do nothing.
1524        // E.g. we have the following rules:
1525        // * RootRule = @{ InnerRule }
1526        // * InnerRule = @{ ... }
1527        let should_toggle = self.atomicity != atomicity;
1528
1529        // Note that we take atomicity of the top rule and not of the leaf (inner).
1530        if should_toggle {
1531            self.atomicity = atomicity;
1532        }
1533
1534        let result = f(self);
1535
1536        match result {
1537            Ok(mut new_state) => {
1538                if should_toggle {
1539                    new_state.atomicity = initial_atomicity;
1540                }
1541                Ok(new_state)
1542            }
1543            Err(mut new_state) => {
1544                if should_toggle {
1545                    new_state.atomicity = initial_atomicity;
1546                }
1547                Err(new_state)
1548            }
1549        }
1550    }
1551
1552    /// Evaluates the result of closure `f` and pushes the span of the input consumed from before
1553    /// `f` is called to after `f` is called to the stack. Returns `Ok(Box<ParserState>)` if `f` is
1554    /// called successfully, or `Err(Box<ParserState>)` otherwise.
1555    ///
1556    /// # Examples
1557    ///
1558    /// ```
1559    /// # use pest;
1560    /// # #[allow(non_camel_case_types)]
1561    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1562    /// enum Rule {}
1563    ///
1564    /// let input = "ab";
1565    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1566    /// let mut result = state.stack_push(|state| state.match_string("a"));
1567    /// assert!(result.is_ok());
1568    /// assert_eq!(result.unwrap().position().pos(), 1);
1569    /// ```
1570    #[inline]
1571    pub fn stack_push<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1572    where
1573        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1574    {
1575        self = self.inc_call_check_limit()?;
1576        let start = self.position;
1577
1578        let result = f(self);
1579
1580        match result {
1581            Ok(mut state) => {
1582                let end = state.position;
1583                state.stack.push(SpanOrLiteral::Span(start.span(&end)));
1584                Ok(state)
1585            }
1586            Err(state) => Err(state),
1587        }
1588    }
1589
1590    /// Peeks the top of the stack and attempts to match the string. Returns `Ok(Box<ParserState>)`
1591    /// if the string is matched successfully, or `Err(Box<ParserState>)` otherwise.
1592    ///
1593    /// # Panics
1594    ///
1595    /// Panics if the stack is empty.
1596    ///
1597    /// # Examples
1598    ///
1599    /// ```
1600    /// # use pest;
1601    /// # #[allow(non_camel_case_types)]
1602    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1603    /// enum Rule {}
1604    ///
1605    /// let input = "aa";
1606    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1607    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1608    ///     |state| state.stack_peek()
1609    /// );
1610    /// assert!(result.is_ok());
1611    /// assert_eq!(result.unwrap().position().pos(), 2);
1612    /// ```
1613    #[inline]
1614    pub fn stack_peek(self: Box<Self>) -> ParseResult<Box<Self>> {
1615        let string = self
1616            .stack
1617            .peek()
1618            .expect("peek was called on empty stack")
1619            .as_borrowed_or_rc();
1620        self.match_string(string.as_str())
1621    }
1622
1623    /// Pops the top of the stack and attempts to match the string. Returns `Ok(Box<ParserState>)`
1624    /// if the string is matched successfully, or `Err(Box<ParserState>)` otherwise.
1625    ///
1626    /// # Panics
1627    ///
1628    /// Panics if the stack is empty.
1629    ///
1630    /// # Examples
1631    ///
1632    /// ```
1633    /// # use pest;
1634    /// # #[allow(non_camel_case_types)]
1635    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1636    /// enum Rule {}
1637    ///
1638    /// let input = "aa";
1639    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1640    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1641    ///     |state| state.stack_pop()
1642    /// );
1643    /// assert!(result.is_ok());
1644    /// assert_eq!(result.unwrap().position().pos(), 2);
1645    /// ```
1646    #[inline]
1647    pub fn stack_pop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1648        let string = self
1649            .stack
1650            .pop()
1651            .expect("pop was called on empty stack")
1652            .as_borrowed_or_rc();
1653        self.match_string(string.as_str())
1654    }
1655
1656    /// Matches part of the state of the stack.
1657    ///
1658    /// # Examples
1659    ///
1660    /// ```
1661    /// # use pest::{self, MatchDir};
1662    /// # #[allow(non_camel_case_types)]
1663    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1664    /// enum Rule {}
1665    ///
1666    /// let input = "abcd cd cb";
1667    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1668    /// let mut result = state
1669    ///     .stack_push(|state| state.match_string("a"))
1670    ///     .and_then(|state| state.stack_push(|state| state.match_string("b")))
1671    ///     .and_then(|state| state.stack_push(|state| state.match_string("c")))
1672    ///     .and_then(|state| state.stack_push(|state| state.match_string("d")))
1673    ///     .and_then(|state| state.match_string(" "))
1674    ///     .and_then(|state| state.stack_match_peek_slice(2, None, MatchDir::BottomToTop))
1675    ///     .and_then(|state| state.match_string(" "))
1676    ///     .and_then(|state| state.stack_match_peek_slice(1, Some(-1), MatchDir::TopToBottom));
1677    /// assert!(result.is_ok());
1678    /// assert_eq!(result.unwrap().position().pos(), 10);
1679    /// ```
1680    #[inline]
1681    pub fn stack_match_peek_slice(
1682        mut self: Box<Self>,
1683        start: i32,
1684        end: Option<i32>,
1685        match_dir: MatchDir,
1686    ) -> ParseResult<Box<Self>> {
1687        let range = match constrain_idxs(start, end, self.stack.len()) {
1688            Some(r) => r,
1689            None => return Err(self),
1690        };
1691        // return true if an empty sequence is requested
1692        if range.end <= range.start {
1693            return Ok(self);
1694        }
1695
1696        let mut position = self.position;
1697        let result = {
1698            let mut iter_b2t = self.stack[range].iter();
1699            let matcher =
1700                |span: &SpanOrLiteral<'_>| position.match_string(span.as_borrowed_or_rc().as_str());
1701            match match_dir {
1702                MatchDir::BottomToTop => iter_b2t.all(matcher),
1703                MatchDir::TopToBottom => iter_b2t.rev().all(matcher),
1704            }
1705        };
1706        if result {
1707            self.position = position;
1708            Ok(self)
1709        } else {
1710            Err(self)
1711        }
1712    }
1713
1714    /// Matches the full state of the stack.
1715    ///
1716    /// # Examples
1717    ///
1718    /// ```
1719    /// # use pest;
1720    /// # #[allow(non_camel_case_types)]
1721    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1722    /// enum Rule {}
1723    ///
1724    /// let input = "abba";
1725    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1726    /// let mut result = state
1727    ///     .stack_push(|state| state.match_string("a"))
1728    ///     .and_then(|state| { state.stack_push(|state| state.match_string("b")) })
1729    ///     .and_then(|state| state.stack_match_peek());
1730    /// assert!(result.is_ok());
1731    /// assert_eq!(result.unwrap().position().pos(), 4);
1732    /// ```
1733    #[inline]
1734    pub fn stack_match_peek(self: Box<Self>) -> ParseResult<Box<Self>> {
1735        self.stack_match_peek_slice(0, None, MatchDir::TopToBottom)
1736    }
1737
1738    /// Matches the full state of the stack. This method will clear the stack as it evaluates.
1739    ///
1740    /// # Examples
1741    ///
1742    /// ```
1743    /// /// # use pest;
1744    /// # #[allow(non_camel_case_types)]
1745    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1746    /// enum Rule {}
1747    ///
1748    /// let input = "aaaa";
1749    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1750    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(|state| {
1751    ///     state.stack_push(|state| state.match_string("a"))
1752    /// }).and_then(|state| state.stack_match_peek());
1753    /// assert!(result.is_ok());
1754    /// assert_eq!(result.unwrap().position().pos(), 4);
1755    /// ```
1756    #[inline]
1757    pub fn stack_match_pop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1758        let mut position = self.position;
1759        let mut result = true;
1760        while let Some(span) = self.stack.pop() {
1761            result = position.match_string(span.as_borrowed_or_rc().as_str());
1762            if !result {
1763                break;
1764            }
1765        }
1766
1767        if result {
1768            self.position = position;
1769            Ok(self)
1770        } else {
1771            Err(self)
1772        }
1773    }
1774
1775    /// Drops the top of the stack. Returns `Ok(Box<ParserState>)` if there was a value to drop, or
1776    /// `Err(Box<ParserState>)` otherwise.
1777    ///
1778    /// # Examples
1779    ///
1780    /// ```
1781    /// # use pest;
1782    /// # #[allow(non_camel_case_types)]
1783    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1784    /// enum Rule {}
1785    ///
1786    /// let input = "aa";
1787    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1788    /// let mut result = state.stack_push(|state| state.match_string("a")).and_then(
1789    ///     |state| state.stack_drop()
1790    /// );
1791    /// assert!(result.is_ok());
1792    /// assert_eq!(result.unwrap().position().pos(), 1);
1793    /// ```
1794    #[inline]
1795    pub fn stack_drop(mut self: Box<Self>) -> ParseResult<Box<Self>> {
1796        match self.stack.pop() {
1797            Some(_) => Ok(self),
1798            None => Err(self),
1799        }
1800    }
1801
1802    /// Restores the original state of the `ParserState` when `f` returns an `Err`. Currently,
1803    /// this method only restores the stack.
1804    ///
1805    /// # Examples
1806    ///
1807    /// ```
1808    /// # use pest;
1809    /// # #[allow(non_camel_case_types)]
1810    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1811    /// enum Rule {}
1812    ///
1813    /// let input = "ab";
1814    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input);
1815    /// let mut result = state.restore_on_err(|state| state.stack_push(|state|
1816    ///     state.match_string("a")).and_then(|state| state.match_string("a"))
1817    /// );
1818    ///
1819    /// assert!(result.is_err());
1820    ///
1821    /// // Since the the rule doesn't match, the "a" pushed to the stack will be removed.
1822    /// let catch_panic = std::panic::catch_unwind(|| result.unwrap_err().stack_pop());
1823    /// assert!(catch_panic.is_err());
1824    /// ```
1825    #[inline]
1826    pub fn restore_on_err<F>(mut self: Box<Self>, f: F) -> ParseResult<Box<Self>>
1827    where
1828        F: FnOnce(Box<Self>) -> ParseResult<Box<Self>>,
1829    {
1830        self = self.check_stack_limit()?;
1831        match f(self.checkpoint()) {
1832            Ok(state) => Ok(state.checkpoint_ok()),
1833            Err(state) => Err(state.restore()),
1834        }
1835    }
1836
1837    // Mark the current state as a checkpoint and return the `Box`.
1838    #[inline]
1839    pub(crate) fn checkpoint(mut self: Box<Self>) -> Box<Self> {
1840        self.stack.snapshot();
1841        self
1842    }
1843
1844    // The checkpoint was cleared successfully
1845    // so remove it without touching other stack state.
1846    #[inline]
1847    pub(crate) fn checkpoint_ok(mut self: Box<Self>) -> Box<Self> {
1848        self.stack.clear_snapshot();
1849        self
1850    }
1851
1852    // Restore the current state to the most recent checkpoint.
1853    #[inline]
1854    pub(crate) fn restore(mut self: Box<Self>) -> Box<Self> {
1855        self.stack.restore();
1856        self
1857    }
1858}
1859
1860/// Helper function used only in case stack operations (PUSH/POP) are used in grammar.
1861fn constrain_idxs(start: i32, end: Option<i32>, len: usize) -> Option<Range<usize>> {
1862    let start_norm = normalize_index(start, len)?;
1863    let end_norm = end.map_or(Some(len), |e| normalize_index(e, len))?;
1864    Some(start_norm..end_norm)
1865}
1866
1867/// `constrain_idxs` helper function.
1868/// Normalizes the index using its sequence’s length.
1869/// Returns `None` if the normalized index is OOB.
1870fn normalize_index(i: i32, len: usize) -> Option<usize> {
1871    if i > len as i32 {
1872        None
1873    } else if i >= 0 {
1874        Some(i as usize)
1875    } else {
1876        let real_i = len as i32 + i;
1877        if real_i >= 0 {
1878            Some(real_i as usize)
1879        } else {
1880            None
1881        }
1882    }
1883}
1884
1885#[cfg(test)]
1886mod test {
1887    use super::*;
1888
1889    type TestState = Box<ParserState<'static, ()>>;
1890    type TestResult = ParseResult<TestState>;
1891    type Combinator = fn(TestState, fn(TestState) -> TestResult) -> TestResult;
1892
1893    const COMBINATORS: [(&str, Combinator); 9] = [
1894        ("rule", |state, inner| state.rule((), inner)),
1895        ("sequence", ParserState::sequence),
1896        ("repeat", ParserState::repeat),
1897        ("optional", ParserState::optional),
1898        ("positive lookahead", |state, inner| {
1899            state.lookahead(true, inner)
1900        }),
1901        ("negative lookahead", |state, inner| {
1902            state.lookahead(false, inner)
1903        }),
1904        ("atomic", |state, inner| {
1905            state.atomic(Atomicity::Atomic, inner)
1906        }),
1907        ("stack_push", ParserState::stack_push),
1908        ("restore_on_err", ParserState::restore_on_err),
1909    ];
1910
1911    fn tracker_at_calls(calls: usize, limit: usize) -> CallLimitTracker {
1912        CallLimitTracker {
1913            current_call_limit: Some((calls, limit)),
1914            limit_reached: None,
1915        }
1916    }
1917
1918    #[test]
1919    fn stack_limit_probe_boundary_and_unknown_bounds() {
1920        for stack_space in [
1921            None,
1922            Some(REQUIRED_STACK_SPACE),
1923            Some(REQUIRED_STACK_SPACE - 1),
1924        ] {
1925            let mut tracker = tracker_at_calls(0, 10);
1926            let allowed = stack_space != Some(REQUIRED_STACK_SPACE - 1);
1927            assert_eq!(tracker.enter_with_stack(true, stack_space), allowed);
1928            assert_eq!(tracker.current_call_limit, Some((usize::from(allowed), 10)));
1929            if !allowed {
1930                assert_eq!(tracker.error_message(), Some("stack limit reached"));
1931                assert!(!tracker.enter_with_stack(true, None));
1932            }
1933        }
1934    }
1935
1936    #[test]
1937    fn call_limit_counts_shallow_work() {
1938        let result = state::<(), _>("xxxxxxxxxxxxxxxx", |mut state| {
1939            state.call_tracker = CallLimitTracker {
1940                current_call_limit: Some((0, 3)),
1941                limit_reached: None,
1942            };
1943            state.repeat(|state| state.sequence(|state| state.match_string("x")))
1944        });
1945        assert_eq!(
1946            result.unwrap_err().variant,
1947            ErrorVariant::CustomError {
1948                message: "call limit reached".to_owned()
1949            }
1950        );
1951    }
1952
1953    #[test]
1954    fn call_limit_preserves_combinator_charges() {
1955        for (name, combinator) in COMBINATORS {
1956            for input in ["x", "y"] {
1957                for initial_calls in [0, 2] {
1958                    let mut state = ParserState::new(input);
1959                    state.call_tracker = tracker_at_calls(initial_calls, 10);
1960                    let result = combinator(state, |state| state.match_string("x"));
1961                    let should_succeed = match name {
1962                        "repeat" | "optional" => true,
1963                        "negative lookahead" => input != "x",
1964                        _ => input == "x",
1965                    };
1966                    assert_eq!(result.is_ok(), should_succeed, "{name}");
1967                    let state = result.unwrap_or_else(|state| state);
1968                    assert_eq!(
1969                        state.call_tracker.current_call_limit,
1970                        Some((initial_calls + usize::from(name != "restore_on_err"), 10)),
1971                        "{name} on {input:?}"
1972                    );
1973                    assert!(!state.call_tracker.limit_reached(), "{name}");
1974                    assert_eq!(state.lookahead, Lookahead::None, "{name}");
1975                    assert_eq!(state.atomicity, Atomicity::NonAtomic, "{name}");
1976                }
1977            }
1978        }
1979    }
1980
1981    #[test]
1982    fn call_limit_boundary() {
1983        for (name, combinator) in COMBINATORS {
1984            if name == "restore_on_err" {
1985                continue;
1986            }
1987            for initial_calls in [0, 2] {
1988                for remaining in [1, 2] {
1989                    let limit = initial_calls + remaining;
1990                    let mut state = ParserState::new("x");
1991                    state.call_tracker = tracker_at_calls(initial_calls, limit);
1992                    let result = combinator(state, |state| {
1993                        state.rule((), |state| state.match_string("x"))
1994                    });
1995                    let state = result.unwrap_or_else(|state| state);
1996                    assert_eq!(
1997                        state.call_tracker.current_call_limit,
1998                        Some((limit, limit)),
1999                        "{name}"
2000                    );
2001                    let exhausted = remaining == 1 || name == "repeat";
2002                    assert_eq!(state.call_tracker.limit_reached(), exhausted, "{name}");
2003                    if exhausted {
2004                        let state = combinator(state, |_| {
2005                            panic!("exhausted state must not reenter a helper")
2006                        })
2007                        .unwrap_err();
2008                        assert_eq!(
2009                            state.call_tracker.current_call_limit,
2010                            Some((limit, limit)),
2011                            "{name}"
2012                        );
2013                    }
2014                }
2015            }
2016        }
2017    }
2018
2019    #[test]
2020    fn call_limit_restore_on_err_checkpoints() {
2021        for limit in [1, 2] {
2022            for succeeds in [false, true] {
2023                let mut state = ParserState::<()>::new("xy");
2024                state.call_tracker = tracker_at_calls(0, limit);
2025                state
2026                    .stack
2027                    .push(SpanOrLiteral::Span(Span::new("xy", 0, 1).unwrap()));
2028                let state = state.checkpoint();
2029                let result = state.restore_on_err(|mut state| {
2030                    state.stack.pop();
2031                    state.restore_on_err(|state| {
2032                        state.sequence(|mut state| {
2033                            state
2034                                .stack
2035                                .push(SpanOrLiteral::Span(Span::new("xy", 1, 2).unwrap()));
2036                            let state = state.match_string("x").unwrap();
2037                            if succeeds { Ok(state) } else { Err(state) }
2038                        })
2039                    })
2040                });
2041                assert_eq!(result.is_ok(), succeeds);
2042                let state = result.unwrap_or_else(|state| state);
2043                assert_eq!(state.call_tracker.current_call_limit, Some((1, limit)));
2044                assert!(!state.call_tracker.limit_reached());
2045                assert_eq!(state.position.pos(), usize::from(succeeds));
2046                let top = state.stack.peek().unwrap().as_borrowed_or_rc();
2047                assert_eq!(top.as_str(), if succeeds { "y" } else { "x" });
2048                let state = state.restore();
2049                assert_eq!(state.stack.len(), 1);
2050                assert_eq!(
2051                    state.stack.peek().unwrap().as_borrowed_or_rc().as_str(),
2052                    "x"
2053                );
2054            }
2055        }
2056    }
2057
2058    #[test]
2059    fn call_limit_accumulates_on_backtracking() {
2060        let input = "xxxxxxxxxxxxxxxx";
2061        let mut state = ParserState::<()>::new(input);
2062        state.call_tracker = tracker_at_calls(0, 100);
2063        let state = state
2064            .repeat(|state| {
2065                state
2066                    .sequence(|state| state.match_string("y"))
2067                    .or_else(|state| state.sequence(|state| state.match_string("x")))
2068            })
2069            .unwrap();
2070        assert_eq!(state.position.pos(), input.len());
2071        assert_eq!(state.call_tracker.current_call_limit, Some((35, 100)));
2072        assert!(!state.call_tracker.limit_reached());
2073    }
2074
2075    #[test]
2076    fn call_limit_cannot_be_swallowed() {
2077        for (name, combinator) in COMBINATORS {
2078            for error_detail in [false, true] {
2079                let result = state::<(), _>("", |mut state| {
2080                    state.call_tracker = tracker_at_calls(0, 1);
2081                    state.parse_attempts.enabled = error_detail;
2082                    combinator(state, |state| {
2083                        state.rule((), |state| {
2084                            state.rule((), |_| panic!("over-limit closure must not run"))
2085                        })
2086                    })
2087                    .or_else(Ok)
2088                });
2089                assert_eq!(
2090                    result.expect_err(name).variant,
2091                    ErrorVariant::CustomError {
2092                        message: "call limit reached".to_owned(),
2093                    },
2094                    "{name}, error_detail={error_detail}"
2095                );
2096            }
2097        }
2098    }
2099
2100    #[test]
2101    fn call_limit_stops_recovering_combinator_continuations() {
2102        for (name, combinator) in COMBINATORS {
2103            if !matches!(name, "optional" | "repeat" | "negative lookahead") {
2104                continue;
2105            }
2106            let mut state = ParserState::new("");
2107            state.call_tracker = tracker_at_calls(0, 1);
2108            let result: TestResult = combinator(state, |state| {
2109                state.rule((), |_| panic!("over-budget body must not run"))
2110            })
2111            .map(|_| panic!("fatal resource rejection must stop continuations"));
2112            assert!(result.is_err(), "{name}");
2113        }
2114    }
2115
2116    #[test]
2117    fn normalize_index_pos() {
2118        assert_eq!(normalize_index(4, 6), Some(4));
2119        assert_eq!(normalize_index(5, 5), Some(5));
2120        assert_eq!(normalize_index(6, 3), None);
2121    }
2122
2123    #[test]
2124    fn normalize_index_neg() {
2125        assert_eq!(normalize_index(-4, 6), Some(2));
2126        assert_eq!(normalize_index(-5, 5), Some(0));
2127        assert_eq!(normalize_index(-6, 3), None);
2128    }
2129}