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}