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