noa_parser/
recognizer.rs

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