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