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