noa_parser/
recognizer.rs

1//! Defines how to recognize an object.
2
3use crate::errors::{ParseError, ParseResult};
4use crate::matcher::{Match, MatchSize};
5use crate::scanner::Scanner;
6
7/// A trait that defines how to recognize an object.
8///
9/// # Type Parameters
10/// * `V` - The type of the object to recognize
11/// * `T` - The type of the data to scan
12/// * `'a` - The lifetime of the data to scan
13pub trait Recognizable<'a, T, V>: MatchSize {
14    /// Try to recognize the object for the given scanner.
15    ///
16    /// # Type Parameters
17    /// V - The type of the object to recognize
18    ///
19    /// # Arguments
20    /// * `scanner` - The scanner to recognize the object for.
21    ///
22    /// # Returns
23    /// * `Ok(Some(V))` if the object was recognized,
24    /// * `Ok(None)` if the object was not recognized,
25    /// * `Err(ParseError)` if an error occurred
26    ///
27    fn recognize(self, scanner: &mut Scanner<'a, T>) -> ParseResult<Option<V>>;
28}
29
30/// Recognize an object for the given scanner.
31///
32/// # Type Parameters
33/// * `V` - The type of the object to recognize
34/// * `R` - The type of the recognizable object
35///
36/// # Arguments
37/// * `recognizable` - The recognizable object to use for recognition
38/// * `scanner` - The scanner to recognize the object for
39///
40/// # Returns
41/// * `Ok(V)` if the object was recognized,
42/// * `Err(ParseError)` if an error occurred
43///
44/// This function calls the `recognize` method of the recognizable object and
45/// returns its result. If the recognizable object was not recognized, an
46/// `Err(ParseError::UnexpectedToken)` is returned. If the scanner is at the end
47/// of its input and the recognizable object is longer than the remaining input,
48/// an `Err(ParseError::UnexpectedEndOfInput)` is returned.
49pub fn recognize<'a, T, V, R: Recognizable<'a, T, V>>(
50    recognizable: R,
51    scanner: &mut Scanner<'a, T>,
52) -> ParseResult<V> {
53    if recognizable.size() > scanner.remaining().len() {
54        return Err(ParseError::UnexpectedEndOfInput);
55    }
56    recognizable
57        .recognize(scanner)?
58        .ok_or(ParseError::UnexpectedToken)
59}
60
61/// Recognize an object for the given scanner.
62/// Return a slice of the recognized object.
63impl<'a, T, M: Match<T> + MatchSize> Recognizable<'a, T, &'a [T]> for M {
64    fn recognize(self, scanner: &mut Scanner<'a, T>) -> ParseResult<Option<&'a [T]>> {
65        if scanner.is_empty() {
66            return Err(ParseError::UnexpectedEndOfInput);
67        }
68
69        let data = scanner.remaining();
70
71        let (result, size) = self.matcher(data);
72        if !result {
73            return Ok(None);
74        }
75        let curent_position = scanner.current_position();
76        if !scanner.is_empty() {
77            scanner.bump_by(size);
78        }
79        Ok(Some(
80            &scanner.data()[curent_position..curent_position + size],
81        ))
82    }
83}
84
85/// A `Recognizer` is a type that wraps a `Scanner` and holds a successfully
86/// recognized value.
87///
88/// When a value is successfully recognized, the `Recognizer` stores the value in
89/// its `data` field and returns itself. If a value is not recognized, the
90/// `Recognizer` rewinds the scanner to the previous position and returns itself.
91///
92/// # Type Parameters
93///
94/// * `T` - The type of the data to scan.
95/// * `U` - The type of the value to recognize.
96/// * `'a` - The lifetime of the data to scan.
97/// * `'container` - The lifetime of the `Recognizer`.
98pub struct Recognizer<'a, 'container, T, U> {
99    data: Option<U>,
100    scanner: &'container mut Scanner<'a, T>,
101}
102
103impl<'a, 'b, T, U> Recognizer<'a, 'b, T, U> {
104    /// Create a new `Recognizer` with the given scanner.
105    ///
106    /// # Arguments
107    ///
108    /// * `scanner` - The scanner to use when recognizing input.
109    ///
110    /// # Returns
111    ///
112    /// A new `Recognizer` that uses the given scanner.
113    pub fn new(scanner: &'b mut Scanner<'a, T>) -> Self {
114        Recognizer {
115            data: None,
116            scanner,
117        }
118    }
119
120    /// Attempt to recognize a `U` using the given `element`, and return the
121    /// current recognizer if it fails.
122    ///
123    /// # Arguments
124    ///
125    /// * `element` - A `Recognizable` that recognizes a `U`.
126    ///
127    /// # Returns
128    ///
129    /// If the `U` is successfully recognized, returns the current recognizer with
130    /// the resulting value in `data`. If the `U` is not successfully recognized,
131    /// returns the current recognizer with the current position of the scanner
132    /// rewound to the position at which the `U` was attempted, and `data` is left
133    /// `None`.
134    pub fn try_or<R: Recognizable<'a, T, U>>(
135        mut self,
136        element: R,
137    ) -> ParseResult<Recognizer<'a, 'b, T, U>> {
138        // Propagate result
139        if self.data.is_some() {
140            return Ok(self);
141        }
142        // Or apply current recognizer
143        if let Some(found) = element.recognize(self.scanner)? {
144            self.data = Some(found);
145        }
146        Ok(self)
147    }
148
149    /// Consume the recognizer and return the `U` that was recognized if the
150    /// recognizer was successful.
151    ///
152    /// # Returns
153    ///
154    /// If the recognizer was successful (i.e., `data` is `Some`), returns the
155    /// `U` that was recognized. Otherwise, returns `None`.
156    pub fn finish(self) -> Option<U> {
157        self.data
158    }
159
160    /// Consume the recognizer and return the `U` that was recognized if the
161    /// recognizer was successful, or run the given closure if the recognizer was
162    /// not successful.
163    ///
164    /// # Arguments
165    ///
166    /// * `closure` - A function that takes the `Scanner` and returns a
167    ///   `ParseResult<U>`.
168    ///
169    /// # Returns
170    ///
171    /// If the recognizer was successful (i.e., `data` is `Some`), returns the
172    /// `U` that was recognized. If the recognizer was not successful, the
173    /// `closure` is called with the `Scanner` and the result of the closure is
174    /// returned.
175    pub fn finish_with<F>(self, closure: F) -> ParseResult<U>
176    where
177        F: FnOnce(&mut Scanner<'a, T>) -> ParseResult<U>,
178    {
179        match self.data {
180            None => closure(self.scanner),
181            Some(token) => Ok(token),
182        }
183    }
184}