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