1 | //! Character specific parsers and combinators |
2 | //! |
3 | //! Functions recognizing specific characters |
4 | |
5 | #[cfg (test)] |
6 | mod tests; |
7 | |
8 | use crate::lib::std::ops::{Add, Shl}; |
9 | |
10 | use crate::combinator::alt; |
11 | use crate::combinator::cut_err; |
12 | use crate::combinator::opt; |
13 | use crate::combinator::trace; |
14 | use crate::error::ParserError; |
15 | use crate::error::{ErrMode, ErrorKind, Needed}; |
16 | use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial}; |
17 | use crate::stream::{Compare, CompareResult}; |
18 | use crate::token::one_of; |
19 | use crate::token::take_till; |
20 | use crate::token::take_while; |
21 | use crate::PResult; |
22 | use crate::Parser; |
23 | |
24 | /// Mark a value as case-insensitive for ASCII characters |
25 | /// |
26 | /// # Example |
27 | /// ```rust |
28 | /// # use winnow::prelude::*; |
29 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; |
30 | /// # use winnow::ascii::Caseless; |
31 | /// |
32 | /// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
33 | /// Caseless("hello" ).parse_next(s) |
34 | /// } |
35 | /// |
36 | /// assert_eq!(parser.parse_peek("Hello, World!" ), Ok((", World!" , "Hello" ))); |
37 | /// assert_eq!(parser.parse_peek("hello, World!" ), Ok((", World!" , "hello" ))); |
38 | /// assert_eq!(parser.parse_peek("HeLlo, World!" ), Ok((", World!" , "HeLlo" ))); |
39 | /// assert_eq!(parser.parse_peek("Some" ), Err(ErrMode::Backtrack(InputError::new("Some" , ErrorKind::Tag)))); |
40 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Tag)))); |
41 | /// ``` |
42 | #[derive (Copy, Clone, Debug)] |
43 | pub struct Caseless<T>(pub T); |
44 | |
45 | impl Caseless<&str> { |
46 | /// Get the byte-representation of this case-insensitive value |
47 | #[inline (always)] |
48 | pub fn as_bytes(&self) -> Caseless<&[u8]> { |
49 | Caseless(self.0.as_bytes()) |
50 | } |
51 | } |
52 | |
53 | /// Recognizes the string `"\r\n"`. |
54 | /// |
55 | /// *Complete version*: Will return an error if there's not enough input data. |
56 | /// |
57 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
58 | /// |
59 | /// # Example |
60 | /// |
61 | /// ``` |
62 | /// # use winnow::prelude::*; |
63 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; |
64 | /// # use winnow::ascii::crlf; |
65 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
66 | /// crlf.parse_next(input) |
67 | /// } |
68 | /// |
69 | /// assert_eq!(parser.parse_peek(" \r\nc" ), Ok(("c" , " \r\n" ))); |
70 | /// assert_eq!(parser.parse_peek("ab \r\nc" ), Err(ErrMode::Backtrack(InputError::new("ab \r\nc" , ErrorKind::Tag)))); |
71 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Tag)))); |
72 | /// ``` |
73 | /// |
74 | /// ``` |
75 | /// # use winnow::prelude::*; |
76 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
77 | /// # use winnow::Partial; |
78 | /// # use winnow::ascii::crlf; |
79 | /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new(" \r\nc" )), Ok((Partial::new("c" ), " \r\n" ))); |
80 | /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("ab \r\nc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab \r\nc" ), ErrorKind::Tag)))); |
81 | /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(2)))); |
82 | /// ``` |
83 | #[inline (always)] |
84 | pub fn crlf<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
85 | where |
86 | I: StreamIsPartial, |
87 | I: Stream, |
88 | I: Compare<&'static str>, |
89 | { |
90 | trace(name:"crlf" , parser:" \r\n" ).parse_next(input) |
91 | } |
92 | |
93 | /// Recognizes a string of any char except `"\r\n"` or `"\n"`. |
94 | /// |
95 | /// *Complete version*: Will return an error if there's not enough input data. |
96 | /// |
97 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
98 | /// |
99 | /// # Example |
100 | /// |
101 | /// ``` |
102 | /// # use winnow::prelude::*; |
103 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
104 | /// # use winnow::ascii::till_line_ending; |
105 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
106 | /// till_line_ending.parse_next(input) |
107 | /// } |
108 | /// |
109 | /// assert_eq!(parser.parse_peek("ab \r\nc" ), Ok((" \r\nc" , "ab" ))); |
110 | /// assert_eq!(parser.parse_peek("ab \nc" ), Ok((" \nc" , "ab" ))); |
111 | /// assert_eq!(parser.parse_peek("abc" ), Ok(("" , "abc" ))); |
112 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
113 | /// assert_eq!(parser.parse_peek("a \rb \nc" ), Err(ErrMode::Backtrack(InputError::new(" \rb \nc" , ErrorKind::Tag )))); |
114 | /// assert_eq!(parser.parse_peek("a \rbc" ), Err(ErrMode::Backtrack(InputError::new(" \rbc" , ErrorKind::Tag )))); |
115 | /// ``` |
116 | /// |
117 | /// ``` |
118 | /// # use winnow::prelude::*; |
119 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
120 | /// # use winnow::Partial; |
121 | /// # use winnow::ascii::till_line_ending; |
122 | /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab \r\nc" )), Ok((Partial::new(" \r\nc" ), "ab" ))); |
123 | /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
124 | /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
125 | /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a \rb \nc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new(" \rb \nc" ), ErrorKind::Tag )))); |
126 | /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a \rbc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new(" \rbc" ), ErrorKind::Tag )))); |
127 | /// ``` |
128 | #[inline (always)] |
129 | pub fn till_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
130 | where |
131 | I: StreamIsPartial, |
132 | I: Stream, |
133 | I: Compare<&'static str>, |
134 | <I as Stream>::Token: AsChar + Clone, |
135 | { |
136 | traceimpl Parser::Slice, …>(name:"till_line_ending" , parser:move |input: &mut I| { |
137 | if <I as StreamIsPartial>::is_partial_supported() { |
138 | till_line_ending_::<_, _, true>(input) |
139 | } else { |
140 | till_line_ending_::<_, _, false>(input) |
141 | } |
142 | }) |
143 | .parse_next(input) |
144 | } |
145 | |
146 | /// Deprecated, replaced with [`till_line_ending`] |
147 | #[deprecated (since = "0.5.35" , note = "Replaced with `till_line_ending`" )] |
148 | #[inline (always)] |
149 | pub fn not_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
150 | where |
151 | I: StreamIsPartial, |
152 | I: Stream, |
153 | I: Compare<&'static str>, |
154 | <I as Stream>::Token: AsChar + Clone, |
155 | { |
156 | till_line_ending(input) |
157 | } |
158 | |
159 | fn till_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>( |
160 | input: &mut I, |
161 | ) -> PResult<<I as Stream>::Slice, E> |
162 | where |
163 | I: StreamIsPartial, |
164 | I: Stream, |
165 | I: Compare<&'static str>, |
166 | <I as Stream>::Token: AsChar + Clone, |
167 | { |
168 | let res: ::Slice = take_till(range:0.., (' \r' , ' \n' )).parse_next(input)?; |
169 | if input.compare(" \r" ) == CompareResult::Ok { |
170 | let comp: CompareResult = input.compare(" \r\n" ); |
171 | match comp { |
172 | //FIXME: calculate the right index |
173 | CompareResult::Ok => {} |
174 | CompareResult::Incomplete if PARTIAL && input.is_partial() => { |
175 | return Err(ErrMode::Incomplete(Needed::Unknown)); |
176 | } |
177 | CompareResult::Incomplete | CompareResult::Error => { |
178 | let e: ErrorKind = ErrorKind::Tag; |
179 | return Err(ErrMode::from_error_kind(input, kind:e)); |
180 | } |
181 | } |
182 | } |
183 | Ok(res) |
184 | } |
185 | |
186 | /// Recognizes an end of line (both `"\n"` and `"\r\n"`). |
187 | /// |
188 | /// *Complete version*: Will return an error if there's not enough input data. |
189 | /// |
190 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
191 | /// |
192 | /// # Example |
193 | /// |
194 | /// ``` |
195 | /// # use winnow::prelude::*; |
196 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
197 | /// # use winnow::ascii::line_ending; |
198 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
199 | /// line_ending.parse_next(input) |
200 | /// } |
201 | /// |
202 | /// assert_eq!(parser.parse_peek(" \r\nc" ), Ok(("c" , " \r\n" ))); |
203 | /// assert_eq!(parser.parse_peek("ab \r\nc" ), Err(ErrMode::Backtrack(InputError::new("ab \r\nc" , ErrorKind::Tag)))); |
204 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Tag)))); |
205 | /// ``` |
206 | /// |
207 | /// ``` |
208 | /// # use winnow::prelude::*; |
209 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
210 | /// # use winnow::Partial; |
211 | /// # use winnow::ascii::line_ending; |
212 | /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new(" \r\nc" )), Ok((Partial::new("c" ), " \r\n" ))); |
213 | /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab \r\nc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab \r\nc" ), ErrorKind::Tag)))); |
214 | /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
215 | /// ``` |
216 | #[inline (always)] |
217 | pub fn line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
218 | where |
219 | I: StreamIsPartial, |
220 | I: Stream, |
221 | I: Compare<&'static str>, |
222 | { |
223 | trace(name:"line_ending" , parser:alt((" \n" , " \r\n" ))).parse_next(input) |
224 | } |
225 | |
226 | /// Matches a newline character `'\n'`. |
227 | /// |
228 | /// *Complete version*: Will return an error if there's not enough input data. |
229 | /// |
230 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
231 | /// |
232 | /// # Example |
233 | /// |
234 | /// ``` |
235 | /// # use winnow::prelude::*; |
236 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
237 | /// # use winnow::ascii::newline; |
238 | /// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> { |
239 | /// newline.parse_next(input) |
240 | /// } |
241 | /// |
242 | /// assert_eq!(parser.parse_peek(" \nc" ), Ok(("c" , ' \n' ))); |
243 | /// assert_eq!(parser.parse_peek(" \r\nc" ), Err(ErrMode::Backtrack(InputError::new(" \r\nc" , ErrorKind::Verify)))); |
244 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Token)))); |
245 | /// ``` |
246 | /// |
247 | /// ``` |
248 | /// # use winnow::prelude::*; |
249 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
250 | /// # use winnow::Partial; |
251 | /// # use winnow::ascii::newline; |
252 | /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new(" \nc" )), Ok((Partial::new("c" ), ' \n' ))); |
253 | /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new(" \r\nc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new(" \r\nc" ), ErrorKind::Verify)))); |
254 | /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
255 | /// ``` |
256 | #[inline (always)] |
257 | pub fn newline<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> |
258 | where |
259 | I: StreamIsPartial, |
260 | I: Stream, |
261 | <I as Stream>::Token: AsChar + Clone, |
262 | { |
263 | trace(name:"newline" , parser:' \n' .map(AsChar::as_char)).parse_next(input) |
264 | } |
265 | |
266 | /// Matches a tab character `'\t'`. |
267 | /// |
268 | /// *Complete version*: Will return an error if there's not enough input data. |
269 | /// |
270 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
271 | /// |
272 | /// # Example |
273 | /// |
274 | /// ``` |
275 | /// # use winnow::prelude::*; |
276 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
277 | /// # use winnow::ascii::tab; |
278 | /// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> { |
279 | /// tab.parse_next(input) |
280 | /// } |
281 | /// |
282 | /// assert_eq!(parser.parse_peek(" \tc" ), Ok(("c" , ' \t' ))); |
283 | /// assert_eq!(parser.parse_peek(" \r\nc" ), Err(ErrMode::Backtrack(InputError::new(" \r\nc" , ErrorKind::Verify)))); |
284 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Token)))); |
285 | /// ``` |
286 | /// |
287 | /// ``` |
288 | /// # use winnow::prelude::*; |
289 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
290 | /// # use winnow::Partial; |
291 | /// # use winnow::ascii::tab; |
292 | /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new(" \tc" )), Ok((Partial::new("c" ), ' \t' ))); |
293 | /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new(" \r\nc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new(" \r\nc" ), ErrorKind::Verify)))); |
294 | /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
295 | /// ``` |
296 | #[inline (always)] |
297 | pub fn tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> |
298 | where |
299 | I: StreamIsPartial, |
300 | I: Stream, |
301 | <I as Stream>::Token: AsChar + Clone, |
302 | { |
303 | trace(name:"tab" , parser:' \t' .map(AsChar::as_char)).parse_next(input) |
304 | } |
305 | |
306 | /// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` |
307 | /// |
308 | /// *Complete version*: Will return the whole input if no terminating token is found (a non |
309 | /// alphabetic character). |
310 | /// |
311 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
312 | /// or if no terminating token is found (a non alphabetic character). |
313 | /// |
314 | /// # Example |
315 | /// |
316 | /// ``` |
317 | /// # use winnow::prelude::*; |
318 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
319 | /// # use winnow::ascii::alpha0; |
320 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
321 | /// alpha0.parse_next(input) |
322 | /// } |
323 | /// |
324 | /// assert_eq!(parser.parse_peek("ab1c" ), Ok(("1c" , "ab" ))); |
325 | /// assert_eq!(parser.parse_peek("1c" ), Ok(("1c" , "" ))); |
326 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
327 | /// ``` |
328 | /// |
329 | /// ``` |
330 | /// # use winnow::prelude::*; |
331 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
332 | /// # use winnow::Partial; |
333 | /// # use winnow::ascii::alpha0; |
334 | /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("ab1c" )), Ok((Partial::new("1c" ), "ab" ))); |
335 | /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("1c" )), Ok((Partial::new("1c" ), "" ))); |
336 | /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
337 | /// ``` |
338 | #[inline (always)] |
339 | pub fn alpha0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
340 | where |
341 | I: StreamIsPartial, |
342 | I: Stream, |
343 | <I as Stream>::Token: AsChar, |
344 | { |
345 | trace(name:"alpha0" , parser:take_while(range:0.., list:AsChar::is_alpha)).parse_next(input) |
346 | } |
347 | |
348 | /// Recognizes one or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` |
349 | /// |
350 | /// *Complete version*: Will return an error if there's not enough input data, |
351 | /// or the whole input if no terminating token is found (a non alphabetic character). |
352 | /// |
353 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
354 | /// or if no terminating token is found (a non alphabetic character). |
355 | /// |
356 | /// # Example |
357 | /// |
358 | /// ``` |
359 | /// # use winnow::prelude::*; |
360 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
361 | /// # use winnow::ascii::alpha1; |
362 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
363 | /// alpha1.parse_next(input) |
364 | /// } |
365 | /// |
366 | /// assert_eq!(parser.parse_peek("aB1c" ), Ok(("1c" , "aB" ))); |
367 | /// assert_eq!(parser.parse_peek("1c" ), Err(ErrMode::Backtrack(InputError::new("1c" , ErrorKind::Slice)))); |
368 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
369 | /// ``` |
370 | /// |
371 | /// ``` |
372 | /// # use winnow::prelude::*; |
373 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
374 | /// # use winnow::Partial; |
375 | /// # use winnow::ascii::alpha1; |
376 | /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("aB1c" )), Ok((Partial::new("1c" ), "aB" ))); |
377 | /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("1c" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("1c" ), ErrorKind::Slice)))); |
378 | /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
379 | /// ``` |
380 | #[inline (always)] |
381 | pub fn alpha1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
382 | where |
383 | I: StreamIsPartial, |
384 | I: Stream, |
385 | <I as Stream>::Token: AsChar, |
386 | { |
387 | trace(name:"alpha1" , parser:take_while(range:1.., list:AsChar::is_alpha)).parse_next(input) |
388 | } |
389 | |
390 | /// Recognizes zero or more ASCII numerical characters: `'0'..='9'` |
391 | /// |
392 | /// *Complete version*: Will return an error if there's not enough input data, |
393 | /// or the whole input if no terminating token is found (a non digit character). |
394 | /// |
395 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
396 | /// or if no terminating token is found (a non digit character). |
397 | /// |
398 | /// # Example |
399 | /// |
400 | /// ``` |
401 | /// # use winnow::prelude::*; |
402 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
403 | /// # use winnow::ascii::digit0; |
404 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
405 | /// digit0.parse_next(input) |
406 | /// } |
407 | /// |
408 | /// assert_eq!(parser.parse_peek("21c" ), Ok(("c" , "21" ))); |
409 | /// assert_eq!(parser.parse_peek("21" ), Ok(("" , "21" ))); |
410 | /// assert_eq!(parser.parse_peek("a21c" ), Ok(("a21c" , "" ))); |
411 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
412 | /// ``` |
413 | /// |
414 | /// ``` |
415 | /// # use winnow::prelude::*; |
416 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
417 | /// # use winnow::Partial; |
418 | /// # use winnow::ascii::digit0; |
419 | /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("21c" )), Ok((Partial::new("c" ), "21" ))); |
420 | /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("a21c" )), Ok((Partial::new("a21c" ), "" ))); |
421 | /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
422 | /// ``` |
423 | #[inline (always)] |
424 | pub fn digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
425 | where |
426 | I: StreamIsPartial, |
427 | I: Stream, |
428 | <I as Stream>::Token: AsChar, |
429 | { |
430 | trace(name:"digit0" , parser:take_while(range:0.., list:AsChar::is_dec_digit)).parse_next(input) |
431 | } |
432 | |
433 | /// Recognizes one or more ASCII numerical characters: `'0'..='9'` |
434 | /// |
435 | /// *Complete version*: Will return an error if there's not enough input data, |
436 | /// or the whole input if no terminating token is found (a non digit character). |
437 | /// |
438 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
439 | /// or if no terminating token is found (a non digit character). |
440 | /// |
441 | /// # Example |
442 | /// |
443 | /// ``` |
444 | /// # use winnow::prelude::*; |
445 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
446 | /// # use winnow::ascii::digit1; |
447 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
448 | /// digit1.parse_next(input) |
449 | /// } |
450 | /// |
451 | /// assert_eq!(parser.parse_peek("21c" ), Ok(("c" , "21" ))); |
452 | /// assert_eq!(parser.parse_peek("c1" ), Err(ErrMode::Backtrack(InputError::new("c1" , ErrorKind::Slice)))); |
453 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
454 | /// ``` |
455 | /// |
456 | /// ``` |
457 | /// # use winnow::prelude::*; |
458 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
459 | /// # use winnow::Partial; |
460 | /// # use winnow::ascii::digit1; |
461 | /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("21c" )), Ok((Partial::new("c" ), "21" ))); |
462 | /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("c1" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("c1" ), ErrorKind::Slice)))); |
463 | /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
464 | /// ``` |
465 | /// |
466 | /// ## Parsing an integer |
467 | /// |
468 | /// You can use `digit1` in combination with [`Parser::try_map`] to parse an integer: |
469 | /// |
470 | /// ``` |
471 | /// # use winnow::prelude::*; |
472 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, Parser}; |
473 | /// # use winnow::ascii::digit1; |
474 | /// fn parser<'s>(input: &mut &'s str) -> PResult<u32, InputError<&'s str>> { |
475 | /// digit1.try_map(str::parse).parse_next(input) |
476 | /// } |
477 | /// |
478 | /// assert_eq!(parser.parse_peek("416" ), Ok(("" , 416))); |
479 | /// assert_eq!(parser.parse_peek("12b" ), Ok(("b" , 12))); |
480 | /// assert!(parser.parse_peek("b" ).is_err()); |
481 | /// ``` |
482 | #[inline (always)] |
483 | pub fn digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
484 | where |
485 | I: StreamIsPartial, |
486 | I: Stream, |
487 | <I as Stream>::Token: AsChar, |
488 | { |
489 | trace(name:"digit1" , parser:take_while(range:1.., list:AsChar::is_dec_digit)).parse_next(input) |
490 | } |
491 | |
492 | /// Recognizes zero or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, |
493 | /// `'a'..='f'` |
494 | /// |
495 | /// *Complete version*: Will return the whole input if no terminating token is found (a non hexadecimal digit character). |
496 | /// |
497 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
498 | /// or if no terminating token is found (a non hexadecimal digit character). |
499 | /// |
500 | /// # Example |
501 | /// |
502 | /// ``` |
503 | /// # use winnow::prelude::*; |
504 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
505 | /// # use winnow::ascii::hex_digit0; |
506 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
507 | /// hex_digit0.parse_next(input) |
508 | /// } |
509 | /// |
510 | /// assert_eq!(parser.parse_peek("21cZ" ), Ok(("Z" , "21c" ))); |
511 | /// assert_eq!(parser.parse_peek("Z21c" ), Ok(("Z21c" , "" ))); |
512 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
513 | /// ``` |
514 | /// |
515 | /// ``` |
516 | /// # use winnow::prelude::*; |
517 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
518 | /// # use winnow::Partial; |
519 | /// # use winnow::ascii::hex_digit0; |
520 | /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ" )), Ok((Partial::new("Z" ), "21c" ))); |
521 | /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c" )), Ok((Partial::new("Z21c" ), "" ))); |
522 | /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
523 | /// ``` |
524 | #[inline (always)] |
525 | pub fn hex_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
526 | where |
527 | I: StreamIsPartial, |
528 | I: Stream, |
529 | <I as Stream>::Token: AsChar, |
530 | { |
531 | trace(name:"hex_digit0" , parser:take_while(range:0.., list:AsChar::is_hex_digit)).parse_next(input) |
532 | } |
533 | |
534 | /// Recognizes one or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, |
535 | /// `'a'..='f'` |
536 | /// |
537 | /// *Complete version*: Will return an error if there's not enough input data, |
538 | /// or the whole input if no terminating token is found (a non hexadecimal digit character). |
539 | /// |
540 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
541 | /// or if no terminating token is found (a non hexadecimal digit character). |
542 | /// |
543 | /// # Example |
544 | /// |
545 | /// ``` |
546 | /// # use winnow::prelude::*; |
547 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
548 | /// # use winnow::ascii::hex_digit1; |
549 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
550 | /// hex_digit1.parse_next(input) |
551 | /// } |
552 | /// |
553 | /// assert_eq!(parser.parse_peek("21cZ" ), Ok(("Z" , "21c" ))); |
554 | /// assert_eq!(parser.parse_peek("H2" ), Err(ErrMode::Backtrack(InputError::new("H2" , ErrorKind::Slice)))); |
555 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
556 | /// ``` |
557 | /// |
558 | /// ``` |
559 | /// # use winnow::prelude::*; |
560 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
561 | /// # use winnow::Partial; |
562 | /// # use winnow::ascii::hex_digit1; |
563 | /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ" )), Ok((Partial::new("Z" ), "21c" ))); |
564 | /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2" ), ErrorKind::Slice)))); |
565 | /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
566 | /// ``` |
567 | #[inline (always)] |
568 | pub fn hex_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
569 | where |
570 | I: StreamIsPartial, |
571 | I: Stream, |
572 | <I as Stream>::Token: AsChar, |
573 | { |
574 | trace(name:"hex_digit1" , parser:take_while(range:1.., list:AsChar::is_hex_digit)).parse_next(input) |
575 | } |
576 | |
577 | /// Recognizes zero or more octal characters: `'0'..='7'` |
578 | /// |
579 | /// *Complete version*: Will return the whole input if no terminating token is found (a non octal |
580 | /// digit character). |
581 | /// |
582 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
583 | /// or if no terminating token is found (a non octal digit character). |
584 | /// |
585 | /// # Example |
586 | /// |
587 | /// ``` |
588 | /// # use winnow::prelude::*; |
589 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
590 | /// # use winnow::ascii::oct_digit0; |
591 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
592 | /// oct_digit0.parse_next(input) |
593 | /// } |
594 | /// |
595 | /// assert_eq!(parser.parse_peek("21cZ" ), Ok(("cZ" , "21" ))); |
596 | /// assert_eq!(parser.parse_peek("Z21c" ), Ok(("Z21c" , "" ))); |
597 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
598 | /// ``` |
599 | /// |
600 | /// ``` |
601 | /// # use winnow::prelude::*; |
602 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
603 | /// # use winnow::Partial; |
604 | /// # use winnow::ascii::oct_digit0; |
605 | /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ" )), Ok((Partial::new("cZ" ), "21" ))); |
606 | /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c" )), Ok((Partial::new("Z21c" ), "" ))); |
607 | /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
608 | /// ``` |
609 | #[inline (always)] |
610 | pub fn oct_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
611 | where |
612 | I: StreamIsPartial, |
613 | I: Stream, |
614 | <I as Stream>::Token: AsChar, |
615 | { |
616 | trace(name:"oct_digit0" , parser:take_while(range:0.., list:AsChar::is_oct_digit)).parse_next(input) |
617 | } |
618 | |
619 | /// Recognizes one or more octal characters: `'0'..='7'` |
620 | /// |
621 | /// *Complete version*: Will return an error if there's not enough input data, |
622 | /// or the whole input if no terminating token is found (a non octal digit character). |
623 | /// |
624 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
625 | /// or if no terminating token is found (a non octal digit character). |
626 | /// |
627 | /// # Example |
628 | /// |
629 | /// ``` |
630 | /// # use winnow::prelude::*; |
631 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
632 | /// # use winnow::ascii::oct_digit1; |
633 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
634 | /// oct_digit1.parse_next(input) |
635 | /// } |
636 | /// |
637 | /// assert_eq!(parser.parse_peek("21cZ" ), Ok(("cZ" , "21" ))); |
638 | /// assert_eq!(parser.parse_peek("H2" ), Err(ErrMode::Backtrack(InputError::new("H2" , ErrorKind::Slice)))); |
639 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
640 | /// ``` |
641 | /// |
642 | /// ``` |
643 | /// # use winnow::prelude::*; |
644 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
645 | /// # use winnow::Partial; |
646 | /// # use winnow::ascii::oct_digit1; |
647 | /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ" )), Ok((Partial::new("cZ" ), "21" ))); |
648 | /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2" ), ErrorKind::Slice)))); |
649 | /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
650 | /// ``` |
651 | #[inline (always)] |
652 | pub fn oct_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
653 | where |
654 | I: StreamIsPartial, |
655 | I: Stream, |
656 | <I as Stream>::Token: AsChar, |
657 | { |
658 | trace(name:"oct_digit0" , parser:take_while(range:1.., list:AsChar::is_oct_digit)).parse_next(input) |
659 | } |
660 | |
661 | /// Recognizes zero or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` |
662 | /// |
663 | /// *Complete version*: Will return the whole input if no terminating token is found (a non |
664 | /// alphanumerical character). |
665 | /// |
666 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
667 | /// or if no terminating token is found (a non alphanumerical character). |
668 | /// |
669 | /// # Example |
670 | /// |
671 | /// ``` |
672 | /// # use winnow::prelude::*; |
673 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
674 | /// # use winnow::ascii::alphanumeric0; |
675 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
676 | /// alphanumeric0.parse_next(input) |
677 | /// } |
678 | /// |
679 | /// assert_eq!(parser.parse_peek("21cZ%1" ), Ok(("%1" , "21cZ" ))); |
680 | /// assert_eq!(parser.parse_peek("&Z21c" ), Ok(("&Z21c" , "" ))); |
681 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
682 | /// ``` |
683 | /// |
684 | /// ``` |
685 | /// # use winnow::prelude::*; |
686 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
687 | /// # use winnow::Partial; |
688 | /// # use winnow::ascii::alphanumeric0; |
689 | /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1" )), Ok((Partial::new("%1" ), "21cZ" ))); |
690 | /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("&Z21c" )), Ok((Partial::new("&Z21c" ), "" ))); |
691 | /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
692 | /// ``` |
693 | #[inline (always)] |
694 | pub fn alphanumeric0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
695 | where |
696 | I: StreamIsPartial, |
697 | I: Stream, |
698 | <I as Stream>::Token: AsChar, |
699 | { |
700 | trace(name:"alphanumeric0" , parser:take_while(range:0.., list:AsChar::is_alphanum)).parse_next(input) |
701 | } |
702 | |
703 | /// Recognizes one or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` |
704 | /// |
705 | /// *Complete version*: Will return an error if there's not enough input data, |
706 | /// or the whole input if no terminating token is found (a non alphanumerical character). |
707 | /// |
708 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
709 | /// or if no terminating token is found (a non alphanumerical character). |
710 | /// |
711 | /// # Example |
712 | /// |
713 | /// ``` |
714 | /// # use winnow::prelude::*; |
715 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
716 | /// # use winnow::ascii::alphanumeric1; |
717 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
718 | /// alphanumeric1.parse_next(input) |
719 | /// } |
720 | /// |
721 | /// assert_eq!(parser.parse_peek("21cZ%1" ), Ok(("%1" , "21cZ" ))); |
722 | /// assert_eq!(parser.parse_peek("&H2" ), Err(ErrMode::Backtrack(InputError::new("&H2" , ErrorKind::Slice)))); |
723 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
724 | /// ``` |
725 | /// |
726 | /// ``` |
727 | /// # use winnow::prelude::*; |
728 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
729 | /// # use winnow::Partial; |
730 | /// # use winnow::ascii::alphanumeric1; |
731 | /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1" )), Ok((Partial::new("%1" ), "21cZ" ))); |
732 | /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("&H2" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("&H2" ), ErrorKind::Slice)))); |
733 | /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
734 | /// ``` |
735 | #[inline (always)] |
736 | pub fn alphanumeric1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
737 | where |
738 | I: StreamIsPartial, |
739 | I: Stream, |
740 | <I as Stream>::Token: AsChar, |
741 | { |
742 | trace(name:"alphanumeric1" , parser:take_while(range:1.., list:AsChar::is_alphanum)).parse_next(input) |
743 | } |
744 | |
745 | /// Recognizes zero or more spaces and tabs. |
746 | /// |
747 | /// *Complete version*: Will return the whole input if no terminating token is found (a non space |
748 | /// character). |
749 | /// |
750 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
751 | /// or if no terminating token is found (a non space character). |
752 | /// |
753 | /// # Example |
754 | /// |
755 | /// ``` |
756 | /// # use winnow::prelude::*; |
757 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
758 | /// # use winnow::Partial; |
759 | /// # use winnow::ascii::space0; |
760 | /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new(" \t21c" )), Ok((Partial::new("21c" ), " \t" ))); |
761 | /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("Z21c" )), Ok((Partial::new("Z21c" ), "" ))); |
762 | /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
763 | /// ``` |
764 | #[inline (always)] |
765 | pub fn space0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
766 | where |
767 | I: StreamIsPartial, |
768 | I: Stream, |
769 | <I as Stream>::Token: AsChar + Clone, |
770 | { |
771 | trace(name:"space0" , parser:take_while(range:0.., list:AsChar::is_space)).parse_next(input) |
772 | } |
773 | |
774 | /// Recognizes zero or more spaces and tabs. |
775 | /// |
776 | /// *Complete version*: Will return the whole input if no terminating token is found (a non space |
777 | /// character). |
778 | /// |
779 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
780 | /// or if no terminating token is found (a non space character). |
781 | /// |
782 | /// # Example |
783 | /// |
784 | /// ``` |
785 | /// # use winnow::prelude::*; |
786 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
787 | /// # use winnow::ascii::space1; |
788 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
789 | /// space1.parse_next(input) |
790 | /// } |
791 | /// |
792 | /// assert_eq!(parser.parse_peek(" \t21c" ), Ok(("21c" , " \t" ))); |
793 | /// assert_eq!(parser.parse_peek("H2" ), Err(ErrMode::Backtrack(InputError::new("H2" , ErrorKind::Slice)))); |
794 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
795 | /// ``` |
796 | /// |
797 | /// ``` |
798 | /// # use winnow::prelude::*; |
799 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
800 | /// # use winnow::Partial; |
801 | /// # use winnow::ascii::space1; |
802 | /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new(" \t21c" )), Ok((Partial::new("21c" ), " \t" ))); |
803 | /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("H2" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2" ), ErrorKind::Slice)))); |
804 | /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
805 | /// ``` |
806 | #[inline (always)] |
807 | pub fn space1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
808 | where |
809 | I: StreamIsPartial, |
810 | I: Stream, |
811 | <I as Stream>::Token: AsChar + Clone, |
812 | { |
813 | trace(name:"space1" , parser:take_while(range:1.., list:AsChar::is_space)).parse_next(input) |
814 | } |
815 | |
816 | /// Recognizes zero or more spaces, tabs, carriage returns and line feeds. |
817 | /// |
818 | /// *Complete version*: will return the whole input if no terminating token is found (a non space |
819 | /// character). |
820 | /// |
821 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
822 | /// or if no terminating token is found (a non space character). |
823 | /// |
824 | /// # Example |
825 | /// |
826 | /// ``` |
827 | /// # use winnow::prelude::*; |
828 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
829 | /// # use winnow::ascii::multispace0; |
830 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
831 | /// multispace0.parse_next(input) |
832 | /// } |
833 | /// |
834 | /// assert_eq!(parser.parse_peek(" \t\n\r21c" ), Ok(("21c" , " \t\n\r" ))); |
835 | /// assert_eq!(parser.parse_peek("Z21c" ), Ok(("Z21c" , "" ))); |
836 | /// assert_eq!(parser.parse_peek("" ), Ok(("" , "" ))); |
837 | /// ``` |
838 | /// |
839 | /// ``` |
840 | /// # use winnow::prelude::*; |
841 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
842 | /// # use winnow::Partial; |
843 | /// # use winnow::ascii::multispace0; |
844 | /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c" )), Ok((Partial::new("21c" ), " \t\n\r" ))); |
845 | /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("Z21c" )), Ok((Partial::new("Z21c" ), "" ))); |
846 | /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
847 | /// ``` |
848 | #[inline (always)] |
849 | pub fn multispace0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
850 | where |
851 | I: StreamIsPartial, |
852 | I: Stream, |
853 | <I as Stream>::Token: AsChar + Clone, |
854 | { |
855 | trace(name:"multispace0" , parser:take_while(range:0.., (' ' , ' \t' , ' \r' , ' \n' ))).parse_next(input) |
856 | } |
857 | |
858 | /// Recognizes one or more spaces, tabs, carriage returns and line feeds. |
859 | /// |
860 | /// *Complete version*: will return an error if there's not enough input data, |
861 | /// or the whole input if no terminating token is found (a non space character). |
862 | /// |
863 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, |
864 | /// or if no terminating token is found (a non space character). |
865 | /// |
866 | /// # Example |
867 | /// |
868 | /// ``` |
869 | /// # use winnow::prelude::*; |
870 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
871 | /// # use winnow::ascii::multispace1; |
872 | /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { |
873 | /// multispace1.parse_next(input) |
874 | /// } |
875 | /// |
876 | /// assert_eq!(parser.parse_peek(" \t\n\r21c" ), Ok(("21c" , " \t\n\r" ))); |
877 | /// assert_eq!(parser.parse_peek("H2" ), Err(ErrMode::Backtrack(InputError::new("H2" , ErrorKind::Slice)))); |
878 | /// assert_eq!(parser.parse_peek("" ), Err(ErrMode::Backtrack(InputError::new("" , ErrorKind::Slice)))); |
879 | /// ``` |
880 | /// |
881 | /// ``` |
882 | /// # use winnow::prelude::*; |
883 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
884 | /// # use winnow::Partial; |
885 | /// # use winnow::ascii::multispace1; |
886 | /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c" )), Ok((Partial::new("21c" ), " \t\n\r" ))); |
887 | /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("H2" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2" ), ErrorKind::Slice)))); |
888 | /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
889 | /// ``` |
890 | #[inline (always)] |
891 | pub fn multispace1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
892 | where |
893 | I: StreamIsPartial, |
894 | I: Stream, |
895 | <I as Stream>::Token: AsChar + Clone, |
896 | { |
897 | trace(name:"multispace1" , parser:take_while(range:1.., (' ' , ' \t' , ' \r' , ' \n' ))).parse_next(input) |
898 | } |
899 | |
900 | /// Decode a decimal unsigned integer (e.g. [`u32`]) |
901 | /// |
902 | /// *Complete version*: can parse until the end of input. |
903 | /// |
904 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
905 | #[doc (alias = "u8" )] |
906 | #[doc (alias = "u16" )] |
907 | #[doc (alias = "u32" )] |
908 | #[doc (alias = "u64" )] |
909 | #[doc (alias = "u128" )] |
910 | pub fn dec_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> |
911 | where |
912 | I: StreamIsPartial, |
913 | I: Stream, |
914 | <I as Stream>::Token: AsChar + Clone, |
915 | O: Uint, |
916 | { |
917 | trace("dec_uint" , move |input: &mut I| { |
918 | if input.eof_offset() == 0 { |
919 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
920 | return Err(ErrMode::Incomplete(Needed::new(1))); |
921 | } else { |
922 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
923 | } |
924 | } |
925 | |
926 | let mut value = O::default(); |
927 | for (offset, c) in input.iter_offsets() { |
928 | match c.as_char().to_digit(10) { |
929 | Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| { |
930 | let d = d as u8; |
931 | v.checked_add(d, sealed::SealedMarker) |
932 | }) { |
933 | None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)), |
934 | Some(v) => value = v, |
935 | }, |
936 | None => { |
937 | if offset == 0 { |
938 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
939 | } else { |
940 | let _ = input.next_slice(offset); |
941 | return Ok(value); |
942 | } |
943 | } |
944 | } |
945 | } |
946 | |
947 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
948 | Err(ErrMode::Incomplete(Needed::new(1))) |
949 | } else { |
950 | let _ = input.finish(); |
951 | Ok(value) |
952 | } |
953 | }) |
954 | .parse_next(input) |
955 | } |
956 | |
957 | /// Metadata for parsing unsigned integers, see [`dec_uint`] |
958 | pub trait Uint: Default { |
959 | #[doc (hidden)] |
960 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>; |
961 | #[doc (hidden)] |
962 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>; |
963 | } |
964 | |
965 | impl Uint for u8 { |
966 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
967 | self.checked_mul(by as Self) |
968 | } |
969 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
970 | self.checked_add(by as Self) |
971 | } |
972 | } |
973 | |
974 | impl Uint for u16 { |
975 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
976 | self.checked_mul(by as Self) |
977 | } |
978 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
979 | self.checked_add(by as Self) |
980 | } |
981 | } |
982 | |
983 | impl Uint for u32 { |
984 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
985 | self.checked_mul(by as Self) |
986 | } |
987 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
988 | self.checked_add(by as Self) |
989 | } |
990 | } |
991 | |
992 | impl Uint for u64 { |
993 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
994 | self.checked_mul(by as Self) |
995 | } |
996 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
997 | self.checked_add(by as Self) |
998 | } |
999 | } |
1000 | |
1001 | impl Uint for u128 { |
1002 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1003 | self.checked_mul(by as Self) |
1004 | } |
1005 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1006 | self.checked_add(by as Self) |
1007 | } |
1008 | } |
1009 | |
1010 | /// Deprecated since v0.5.17 |
1011 | impl Uint for i8 { |
1012 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1013 | self.checked_mul(by as Self) |
1014 | } |
1015 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1016 | self.checked_add(by as Self) |
1017 | } |
1018 | } |
1019 | |
1020 | /// Deprecated since v0.5.17 |
1021 | impl Uint for i16 { |
1022 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1023 | self.checked_mul(by as Self) |
1024 | } |
1025 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1026 | self.checked_add(by as Self) |
1027 | } |
1028 | } |
1029 | |
1030 | /// Deprecated since v0.5.17 |
1031 | impl Uint for i32 { |
1032 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1033 | self.checked_mul(by as Self) |
1034 | } |
1035 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1036 | self.checked_add(by as Self) |
1037 | } |
1038 | } |
1039 | |
1040 | /// Deprecated since v0.5.17 |
1041 | impl Uint for i64 { |
1042 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1043 | self.checked_mul(by as Self) |
1044 | } |
1045 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1046 | self.checked_add(by as Self) |
1047 | } |
1048 | } |
1049 | |
1050 | /// Deprecated since v0.5.17 |
1051 | impl Uint for i128 { |
1052 | fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1053 | self.checked_mul(by as Self) |
1054 | } |
1055 | fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1056 | self.checked_add(by as Self) |
1057 | } |
1058 | } |
1059 | |
1060 | /// Decode a decimal signed integer (e.g. [`i32`]) |
1061 | /// |
1062 | /// *Complete version*: can parse until the end of input. |
1063 | /// |
1064 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. |
1065 | #[doc (alias = "i8" )] |
1066 | #[doc (alias = "i16" )] |
1067 | #[doc (alias = "i32" )] |
1068 | #[doc (alias = "i64" )] |
1069 | #[doc (alias = "i128" )] |
1070 | pub fn dec_int<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> |
1071 | where |
1072 | I: StreamIsPartial, |
1073 | I: Stream, |
1074 | <I as Stream>::Token: AsChar + Clone, |
1075 | O: Int, |
1076 | { |
1077 | trace("dec_int" , move |input: &mut I| { |
1078 | fn sign(token: impl AsChar) -> bool { |
1079 | let token = token.as_char(); |
1080 | token == '+' || token == '-' |
1081 | } |
1082 | let sign = opt(crate::token::one_of(sign).map(AsChar::as_char)) |
1083 | .map(|c| c != Some('-' )) |
1084 | .parse_next(input)?; |
1085 | |
1086 | if input.eof_offset() == 0 { |
1087 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
1088 | return Err(ErrMode::Incomplete(Needed::new(1))); |
1089 | } else { |
1090 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
1091 | } |
1092 | } |
1093 | |
1094 | let mut value = O::default(); |
1095 | for (offset, c) in input.iter_offsets() { |
1096 | match c.as_char().to_digit(10) { |
1097 | Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| { |
1098 | let d = d as u8; |
1099 | if sign { |
1100 | v.checked_add(d, sealed::SealedMarker) |
1101 | } else { |
1102 | v.checked_sub(d, sealed::SealedMarker) |
1103 | } |
1104 | }) { |
1105 | None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)), |
1106 | Some(v) => value = v, |
1107 | }, |
1108 | None => { |
1109 | if offset == 0 { |
1110 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
1111 | } else { |
1112 | let _ = input.next_slice(offset); |
1113 | return Ok(value); |
1114 | } |
1115 | } |
1116 | } |
1117 | } |
1118 | |
1119 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
1120 | Err(ErrMode::Incomplete(Needed::new(1))) |
1121 | } else { |
1122 | let _ = input.finish(); |
1123 | Ok(value) |
1124 | } |
1125 | }) |
1126 | .parse_next(input) |
1127 | } |
1128 | |
1129 | /// Metadata for parsing signed integers, see [`dec_int`] |
1130 | pub trait Int: Uint { |
1131 | #[doc (hidden)] |
1132 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>; |
1133 | } |
1134 | |
1135 | impl Int for i8 { |
1136 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1137 | self.checked_sub(by as Self) |
1138 | } |
1139 | } |
1140 | |
1141 | impl Int for i16 { |
1142 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1143 | self.checked_sub(by as Self) |
1144 | } |
1145 | } |
1146 | |
1147 | impl Int for i32 { |
1148 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1149 | self.checked_sub(by as Self) |
1150 | } |
1151 | } |
1152 | |
1153 | impl Int for i64 { |
1154 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1155 | self.checked_sub(by as Self) |
1156 | } |
1157 | } |
1158 | |
1159 | impl Int for i128 { |
1160 | fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { |
1161 | self.checked_sub(by as Self) |
1162 | } |
1163 | } |
1164 | |
1165 | /// Decode a variable-width hexadecimal integer (e.g. [`u32`]) |
1166 | /// |
1167 | /// *Complete version*: Will parse until the end of input if it has fewer characters than the type |
1168 | /// supports. |
1169 | /// |
1170 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input |
1171 | /// is hit before a hard boundary (non-hex character, more characters than supported). |
1172 | /// |
1173 | /// # Example |
1174 | /// |
1175 | /// ```rust |
1176 | /// # use winnow::prelude::*; |
1177 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; |
1178 | /// use winnow::ascii::hex_uint; |
1179 | /// |
1180 | /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<u32, InputError<&'s [u8]>> { |
1181 | /// hex_uint(s) |
1182 | /// } |
1183 | /// |
1184 | /// assert_eq!(parser.parse_peek(&b"01AE" [..]), Ok((&b"" [..], 0x01AE))); |
1185 | /// assert_eq!(parser.parse_peek(&b"abc" [..]), Ok((&b"" [..], 0x0ABC))); |
1186 | /// assert_eq!(parser.parse_peek(&b"ggg" [..]), Err(ErrMode::Backtrack(InputError::new(&b"ggg" [..], ErrorKind::Slice)))); |
1187 | /// ``` |
1188 | /// |
1189 | /// ```rust |
1190 | /// # use winnow::prelude::*; |
1191 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
1192 | /// # use winnow::Partial; |
1193 | /// use winnow::ascii::hex_uint; |
1194 | /// |
1195 | /// fn parser<'s>(s: &mut Partial<&'s [u8]>) -> PResult<u32, InputError<Partial<&'s [u8]>>> { |
1196 | /// hex_uint(s) |
1197 | /// } |
1198 | /// |
1199 | /// assert_eq!(parser.parse_peek(Partial::new(&b"01AE;" [..])), Ok((Partial::new(&b";" [..]), 0x01AE))); |
1200 | /// assert_eq!(parser.parse_peek(Partial::new(&b"abc" [..])), Err(ErrMode::Incomplete(Needed::new(1)))); |
1201 | /// assert_eq!(parser.parse_peek(Partial::new(&b"ggg" [..])), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"ggg" [..]), ErrorKind::Slice)))); |
1202 | /// ``` |
1203 | #[inline ] |
1204 | pub fn hex_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> |
1205 | where |
1206 | I: StreamIsPartial, |
1207 | I: Stream, |
1208 | O: HexUint, |
1209 | <I as Stream>::Token: AsChar, |
1210 | <I as Stream>::Slice: AsBStr, |
1211 | { |
1212 | trace("hex_uint" , move |input: &mut I| { |
1213 | let invalid_offset = input |
1214 | .offset_for(|c| { |
1215 | let c = c.as_char(); |
1216 | !"0123456789abcdefABCDEF" .contains(c) |
1217 | }) |
1218 | .unwrap_or_else(|| input.eof_offset()); |
1219 | let max_nibbles = O::max_nibbles(sealed::SealedMarker); |
1220 | let max_offset = input.offset_at(max_nibbles); |
1221 | let offset = match max_offset { |
1222 | Ok(max_offset) => { |
1223 | if max_offset < invalid_offset { |
1224 | // Overflow |
1225 | return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); |
1226 | } else { |
1227 | invalid_offset |
1228 | } |
1229 | } |
1230 | Err(_) => { |
1231 | if <I as StreamIsPartial>::is_partial_supported() |
1232 | && input.is_partial() |
1233 | && invalid_offset == input.eof_offset() |
1234 | { |
1235 | // Only the next byte is guaranteed required |
1236 | return Err(ErrMode::Incomplete(Needed::new(1))); |
1237 | } else { |
1238 | invalid_offset |
1239 | } |
1240 | } |
1241 | }; |
1242 | if offset == 0 { |
1243 | // Must be at least one digit |
1244 | return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); |
1245 | } |
1246 | let parsed = input.next_slice(offset); |
1247 | |
1248 | let mut res = O::default(); |
1249 | for c in parsed.as_bstr() { |
1250 | let nibble = *c as char; |
1251 | let nibble = nibble.to_digit(16).unwrap_or(0) as u8; |
1252 | let nibble = O::from(nibble); |
1253 | res = (res << O::from(4)) + nibble; |
1254 | } |
1255 | |
1256 | Ok(res) |
1257 | }) |
1258 | .parse_next(input) |
1259 | } |
1260 | |
1261 | /// Metadata for parsing hex numbers, see [`hex_uint`] |
1262 | pub trait HexUint: |
1263 | Default + Shl<Self, Output = Self> + Add<Self, Output = Self> + From<u8> |
1264 | { |
1265 | #[doc (hidden)] |
1266 | fn max_nibbles(_: sealed::SealedMarker) -> usize; |
1267 | } |
1268 | |
1269 | impl HexUint for u8 { |
1270 | #[inline (always)] |
1271 | fn max_nibbles(_: sealed::SealedMarker) -> usize { |
1272 | 2 |
1273 | } |
1274 | } |
1275 | |
1276 | impl HexUint for u16 { |
1277 | #[inline (always)] |
1278 | fn max_nibbles(_: sealed::SealedMarker) -> usize { |
1279 | 4 |
1280 | } |
1281 | } |
1282 | |
1283 | impl HexUint for u32 { |
1284 | #[inline (always)] |
1285 | fn max_nibbles(_: sealed::SealedMarker) -> usize { |
1286 | 8 |
1287 | } |
1288 | } |
1289 | |
1290 | impl HexUint for u64 { |
1291 | #[inline (always)] |
1292 | fn max_nibbles(_: sealed::SealedMarker) -> usize { |
1293 | 16 |
1294 | } |
1295 | } |
1296 | |
1297 | impl HexUint for u128 { |
1298 | #[inline (always)] |
1299 | fn max_nibbles(_: sealed::SealedMarker) -> usize { |
1300 | 32 |
1301 | } |
1302 | } |
1303 | |
1304 | /// Recognizes floating point number in text format and returns a [`f32`] or [`f64`]. |
1305 | /// |
1306 | /// *Complete version*: Can parse until the end of input. |
1307 | /// |
1308 | /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. |
1309 | /// |
1310 | /// # Example |
1311 | /// |
1312 | /// ```rust |
1313 | /// # use winnow::prelude::*; |
1314 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
1315 | /// # use winnow::error::Needed::Size; |
1316 | /// use winnow::ascii::float; |
1317 | /// |
1318 | /// fn parser<'s>(s: &mut &'s str) -> PResult<f64, InputError<&'s str>> { |
1319 | /// float(s) |
1320 | /// } |
1321 | /// |
1322 | /// assert_eq!(parser.parse_peek("11e-1" ), Ok(("" , 1.1))); |
1323 | /// assert_eq!(parser.parse_peek("123E-02" ), Ok(("" , 1.23))); |
1324 | /// assert_eq!(parser.parse_peek("123K-01" ), Ok(("K-01" , 123.0))); |
1325 | /// assert_eq!(parser.parse_peek("abc" ), Err(ErrMode::Backtrack(InputError::new("abc" , ErrorKind::Tag)))); |
1326 | /// ``` |
1327 | /// |
1328 | /// ```rust |
1329 | /// # use winnow::prelude::*; |
1330 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
1331 | /// # use winnow::error::Needed::Size; |
1332 | /// # use winnow::Partial; |
1333 | /// use winnow::ascii::float; |
1334 | /// |
1335 | /// fn parser<'s>(s: &mut Partial<&'s str>) -> PResult<f64, InputError<Partial<&'s str>>> { |
1336 | /// float(s) |
1337 | /// } |
1338 | /// |
1339 | /// assert_eq!(parser.parse_peek(Partial::new("11e-1 " )), Ok((Partial::new(" " ), 1.1))); |
1340 | /// assert_eq!(parser.parse_peek(Partial::new("11e-1" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
1341 | /// assert_eq!(parser.parse_peek(Partial::new("123E-02" )), Err(ErrMode::Incomplete(Needed::new(1)))); |
1342 | /// assert_eq!(parser.parse_peek(Partial::new("123K-01" )), Ok((Partial::new("K-01" ), 123.0))); |
1343 | /// assert_eq!(parser.parse_peek(Partial::new("abc" )), Err(ErrMode::Backtrack(InputError::new(Partial::new("abc" ), ErrorKind::Tag)))); |
1344 | /// ``` |
1345 | #[inline (always)] |
1346 | #[doc (alias = "f32" )] |
1347 | #[doc (alias = "double" )] |
1348 | #[allow (clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug |
1349 | pub fn float<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E> |
1350 | where |
1351 | I: StreamIsPartial, |
1352 | I: Stream, |
1353 | I: Compare<&'static str>, |
1354 | <I as Stream>::Slice: ParseSlice<O>, |
1355 | <I as Stream>::Token: AsChar + Clone, |
1356 | <I as Stream>::IterOffsets: Clone, |
1357 | I: AsBStr, |
1358 | { |
1359 | traceimpl Parser(name:"float" , parser:move |input: &mut I| { |
1360 | let s: ::Slice = recognize_float_or_exceptions(input)?; |
1361 | s.parse_slice() |
1362 | .ok_or_else(|| ErrMode::from_error_kind(input, kind:ErrorKind::Verify)) |
1363 | }) |
1364 | .parse_next(input) |
1365 | } |
1366 | |
1367 | #[allow (clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug |
1368 | #[allow (deprecated)] |
1369 | fn recognize_float_or_exceptions<I, E: ParserError<I>>( |
1370 | input: &mut I, |
1371 | ) -> PResult<<I as Stream>::Slice, E> |
1372 | where |
1373 | I: StreamIsPartial, |
1374 | I: Stream, |
1375 | I: Compare<&'static str>, |
1376 | <I as Stream>::Token: AsChar + Clone, |
1377 | <I as Stream>::IterOffsets: Clone, |
1378 | I: AsBStr, |
1379 | { |
1380 | altimpl Parser::Slice, …>(( |
1381 | recognize_float, |
1382 | crate::token::tag_no_case(tag:"nan" ), |
1383 | ( |
1384 | opt(one_of(['+' , '-' ])), |
1385 | crate::token::tag_no_case(tag:"infinity" ), |
1386 | ) |
1387 | .recognize(), |
1388 | (opt(one_of(['+' , '-' ])), crate::token::tag_no_case(tag:"inf" )).recognize(), |
1389 | )) |
1390 | .parse_next(input) |
1391 | } |
1392 | |
1393 | #[allow (clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug |
1394 | fn recognize_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> |
1395 | where |
1396 | I: StreamIsPartial, |
1397 | I: Stream, |
1398 | I: Compare<&'static str>, |
1399 | <I as Stream>::Token: AsChar + Clone, |
1400 | <I as Stream>::IterOffsets: Clone, |
1401 | I: AsBStr, |
1402 | { |
1403 | ( |
1404 | opt(one_of(['+' , '-' ])), |
1405 | alt(( |
1406 | (digit1, opt(('.' , opt(digit1)))).map(|_| ()), |
1407 | ('.' , digit1).map(|_| ()), |
1408 | )), |
1409 | opt((one_of(['e' , 'E' ]), opt(one_of(['+' , '-' ])), cut_err(parser:digit1))), |
1410 | ) |
1411 | .recognize() |
1412 | .parse_next(input) |
1413 | } |
1414 | |
1415 | /// Matches a byte string with escaped characters. |
1416 | /// |
1417 | /// * The first argument matches the normal characters (it must not accept the control character) |
1418 | /// * The second argument is the control character (like `\` in most languages) |
1419 | /// * The third argument matches the escaped characters |
1420 | /// |
1421 | /// # Example |
1422 | /// |
1423 | /// ```rust |
1424 | /// # use winnow::prelude::*; |
1425 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; |
1426 | /// # use winnow::ascii::digit1; |
1427 | /// # use winnow::prelude::*; |
1428 | /// use winnow::ascii::escaped; |
1429 | /// use winnow::token::one_of; |
1430 | /// |
1431 | /// fn esc(s: &str) -> IResult<&str, &str> { |
1432 | /// escaped(digit1, ' \\' , one_of(['"' , 'n' , ' \\' ])).parse_peek(s) |
1433 | /// } |
1434 | /// |
1435 | /// assert_eq!(esc("123;" ), Ok((";" , "123" ))); |
1436 | /// assert_eq!(esc(r#"12\"34;"# ), Ok((";" , r#"12\"34"# ))); |
1437 | /// ``` |
1438 | /// |
1439 | /// ```rust |
1440 | /// # use winnow::prelude::*; |
1441 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; |
1442 | /// # use winnow::ascii::digit1; |
1443 | /// # use winnow::prelude::*; |
1444 | /// # use winnow::Partial; |
1445 | /// use winnow::ascii::escaped; |
1446 | /// use winnow::token::one_of; |
1447 | /// |
1448 | /// fn esc(s: Partial<&str>) -> IResult<Partial<&str>, &str> { |
1449 | /// escaped(digit1, ' \\' , one_of(['"' , 'n' , ' \\' ])).parse_peek(s) |
1450 | /// } |
1451 | /// |
1452 | /// assert_eq!(esc(Partial::new("123;" )), Ok((Partial::new(";" ), "123" ))); |
1453 | /// assert_eq!(esc(Partial::new("12 \\\"34;" )), Ok((Partial::new(";" ), "12 \\\"34" ))); |
1454 | /// ``` |
1455 | #[inline (always)] |
1456 | pub fn escaped<'a, I: 'a, Error, F, G, O1, O2>( |
1457 | mut normal: F, |
1458 | control_char: char, |
1459 | mut escapable: G, |
1460 | ) -> impl Parser<I, <I as Stream>::Slice, Error> |
1461 | where |
1462 | I: StreamIsPartial, |
1463 | I: Stream, |
1464 | <I as Stream>::Token: AsChar + Clone, |
1465 | F: Parser<I, O1, Error>, |
1466 | G: Parser<I, O2, Error>, |
1467 | Error: ParserError<I>, |
1468 | { |
1469 | trace(name:"escaped" , parser:move |input: &mut I| { |
1470 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
1471 | streaming_escaped_internal(input, &mut normal, control_char, &mut escapable) |
1472 | } else { |
1473 | complete_escaped_internal(input, &mut normal, control_char, &mut escapable) |
1474 | } |
1475 | }) |
1476 | } |
1477 | |
1478 | fn streaming_escaped_internal<I, Error, F, G, O1, O2>( |
1479 | input: &mut I, |
1480 | normal: &mut F, |
1481 | control_char: char, |
1482 | escapable: &mut G, |
1483 | ) -> PResult<<I as Stream>::Slice, Error> |
1484 | where |
1485 | I: StreamIsPartial, |
1486 | I: Stream, |
1487 | <I as Stream>::Token: AsChar + Clone, |
1488 | F: Parser<I, O1, Error>, |
1489 | G: Parser<I, O2, Error>, |
1490 | Error: ParserError<I>, |
1491 | { |
1492 | let start = input.checkpoint(); |
1493 | |
1494 | while input.eof_offset() > 0 { |
1495 | let current_len = input.eof_offset(); |
1496 | |
1497 | match opt(normal.by_ref()).parse_next(input)? { |
1498 | Some(_) => { |
1499 | if input.eof_offset() == current_len { |
1500 | let offset = input.offset_from(&start); |
1501 | input.reset(start); |
1502 | return Ok(input.next_slice(offset)); |
1503 | } |
1504 | } |
1505 | None => { |
1506 | if opt(control_char).parse_next(input)?.is_some() { |
1507 | let _ = escapable.parse_next(input)?; |
1508 | } else { |
1509 | let offset = input.offset_from(&start); |
1510 | input.reset(start); |
1511 | return Ok(input.next_slice(offset)); |
1512 | } |
1513 | } |
1514 | } |
1515 | } |
1516 | |
1517 | Err(ErrMode::Incomplete(Needed::Unknown)) |
1518 | } |
1519 | |
1520 | fn complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>( |
1521 | input: &mut I, |
1522 | normal: &mut F, |
1523 | control_char: char, |
1524 | escapable: &mut G, |
1525 | ) -> PResult<<I as Stream>::Slice, Error> |
1526 | where |
1527 | I: StreamIsPartial, |
1528 | I: Stream, |
1529 | <I as Stream>::Token: crate::stream::AsChar + Clone, |
1530 | F: Parser<I, O1, Error>, |
1531 | G: Parser<I, O2, Error>, |
1532 | Error: ParserError<I>, |
1533 | { |
1534 | let start = input.checkpoint(); |
1535 | |
1536 | while input.eof_offset() > 0 { |
1537 | let current_len = input.eof_offset(); |
1538 | |
1539 | match opt(normal.by_ref()).parse_next(input)? { |
1540 | Some(_) => { |
1541 | if input.eof_offset() == current_len { |
1542 | let offset = input.offset_from(&start); |
1543 | input.reset(start); |
1544 | return Ok(input.next_slice(offset)); |
1545 | } |
1546 | } |
1547 | None => { |
1548 | if opt(control_char).parse_next(input)?.is_some() { |
1549 | let _ = escapable.parse_next(input)?; |
1550 | } else { |
1551 | let offset = input.offset_from(&start); |
1552 | input.reset(start); |
1553 | return Ok(input.next_slice(offset)); |
1554 | } |
1555 | } |
1556 | } |
1557 | } |
1558 | |
1559 | input.reset(start); |
1560 | Ok(input.finish()) |
1561 | } |
1562 | |
1563 | /// Matches a byte string with escaped characters. |
1564 | /// |
1565 | /// * The first argument matches the normal characters (it must not match the control character) |
1566 | /// * The second argument is the control character (like `\` in most languages) |
1567 | /// * The third argument matches the escaped characters and transforms them |
1568 | /// |
1569 | /// As an example, the chain `abc\tdef` could be `abc def` (it also consumes the control character) |
1570 | /// |
1571 | /// # Example |
1572 | /// |
1573 | /// ```rust |
1574 | /// # use winnow::prelude::*; |
1575 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
1576 | /// # use std::str::from_utf8; |
1577 | /// use winnow::token::tag; |
1578 | /// use winnow::ascii::escaped_transform; |
1579 | /// use winnow::ascii::alpha1; |
1580 | /// use winnow::combinator::alt; |
1581 | /// |
1582 | /// fn parser<'s>(input: &mut &'s str) -> PResult<String, InputError<&'s str>> { |
1583 | /// escaped_transform( |
1584 | /// alpha1, |
1585 | /// ' \\' , |
1586 | /// alt(( |
1587 | /// " \\" .value(" \\" ), |
1588 | /// " \"" .value(" \"" ), |
1589 | /// "n" .value(" \n" ), |
1590 | /// )) |
1591 | /// ).parse_next(input) |
1592 | /// } |
1593 | /// |
1594 | /// assert_eq!(parser.parse_peek("ab \\\"cd" ), Ok(("" , String::from("ab \"cd" )))); |
1595 | /// assert_eq!(parser.parse_peek("ab \\ncd" ), Ok(("" , String::from("ab \ncd" )))); |
1596 | /// ``` |
1597 | /// |
1598 | /// ``` |
1599 | /// # use winnow::prelude::*; |
1600 | /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; |
1601 | /// # use std::str::from_utf8; |
1602 | /// # use winnow::Partial; |
1603 | /// use winnow::token::tag; |
1604 | /// use winnow::ascii::escaped_transform; |
1605 | /// use winnow::ascii::alpha1; |
1606 | /// use winnow::combinator::alt; |
1607 | /// |
1608 | /// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult<String, InputError<Partial<&'s str>>> { |
1609 | /// escaped_transform( |
1610 | /// alpha1, |
1611 | /// ' \\' , |
1612 | /// alt(( |
1613 | /// " \\" .value(" \\" ), |
1614 | /// " \"" .value(" \"" ), |
1615 | /// "n" .value(" \n" ), |
1616 | /// )) |
1617 | /// ).parse_next(input) |
1618 | /// } |
1619 | /// |
1620 | /// assert_eq!(parser.parse_peek(Partial::new("ab \\\"cd \"" )), Ok((Partial::new(" \"" ), String::from("ab \"cd" )))); |
1621 | /// ``` |
1622 | #[inline (always)] |
1623 | pub fn escaped_transform<I, Error, F, G, Output>( |
1624 | mut normal: F, |
1625 | control_char: char, |
1626 | mut transform: G, |
1627 | ) -> impl Parser<I, Output, Error> |
1628 | where |
1629 | I: StreamIsPartial, |
1630 | I: Stream, |
1631 | <I as Stream>::Token: crate::stream::AsChar + Clone, |
1632 | Output: crate::stream::Accumulate<<I as Stream>::Slice>, |
1633 | F: Parser<I, <I as Stream>::Slice, Error>, |
1634 | G: Parser<I, <I as Stream>::Slice, Error>, |
1635 | Error: ParserError<I>, |
1636 | { |
1637 | trace(name:"escaped_transform" , parser:move |input: &mut I| { |
1638 | if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() { |
1639 | streaming_escaped_transform_internal(input, &mut normal, control_char, &mut transform) |
1640 | } else { |
1641 | complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform) |
1642 | } |
1643 | }) |
1644 | } |
1645 | |
1646 | fn streaming_escaped_transform_internal<I, Error, F, G, Output>( |
1647 | input: &mut I, |
1648 | normal: &mut F, |
1649 | control_char: char, |
1650 | transform: &mut G, |
1651 | ) -> PResult<Output, Error> |
1652 | where |
1653 | I: StreamIsPartial, |
1654 | I: Stream, |
1655 | <I as Stream>::Token: crate::stream::AsChar + Clone, |
1656 | Output: crate::stream::Accumulate<<I as Stream>::Slice>, |
1657 | F: Parser<I, <I as Stream>::Slice, Error>, |
1658 | G: Parser<I, <I as Stream>::Slice, Error>, |
1659 | Error: ParserError<I>, |
1660 | { |
1661 | let mut res: Output = Output::initial(capacity:Some(input.eof_offset())); |
1662 | |
1663 | while input.eof_offset() > 0 { |
1664 | let current_len: usize = input.eof_offset(); |
1665 | match opt(normal.by_ref()).parse_next(input)? { |
1666 | Some(o: ::Slice) => { |
1667 | res.accumulate(acc:o); |
1668 | if input.eof_offset() == current_len { |
1669 | return Ok(res); |
1670 | } |
1671 | } |
1672 | None => { |
1673 | if opt(control_char).parse_next(input)?.is_some() { |
1674 | let o: ::Slice = transform.parse_next(input)?; |
1675 | res.accumulate(acc:o); |
1676 | } else { |
1677 | return Ok(res); |
1678 | } |
1679 | } |
1680 | } |
1681 | } |
1682 | Err(ErrMode::Incomplete(Needed::Unknown)) |
1683 | } |
1684 | |
1685 | fn complete_escaped_transform_internal<I, Error, F, G, Output>( |
1686 | input: &mut I, |
1687 | normal: &mut F, |
1688 | control_char: char, |
1689 | transform: &mut G, |
1690 | ) -> PResult<Output, Error> |
1691 | where |
1692 | I: StreamIsPartial, |
1693 | I: Stream, |
1694 | <I as Stream>::Token: crate::stream::AsChar + Clone, |
1695 | Output: crate::stream::Accumulate<<I as Stream>::Slice>, |
1696 | F: Parser<I, <I as Stream>::Slice, Error>, |
1697 | G: Parser<I, <I as Stream>::Slice, Error>, |
1698 | Error: ParserError<I>, |
1699 | { |
1700 | let mut res = Output::initial(Some(input.eof_offset())); |
1701 | |
1702 | while input.eof_offset() > 0 { |
1703 | let current_len = input.eof_offset(); |
1704 | |
1705 | match opt(normal.by_ref()).parse_next(input)? { |
1706 | Some(o) => { |
1707 | res.accumulate(o); |
1708 | if input.eof_offset() == current_len { |
1709 | return Ok(res); |
1710 | } |
1711 | } |
1712 | None => { |
1713 | if opt(control_char).parse_next(input)?.is_some() { |
1714 | let o = transform.parse_next(input)?; |
1715 | res.accumulate(o); |
1716 | } else { |
1717 | return Ok(res); |
1718 | } |
1719 | } |
1720 | } |
1721 | } |
1722 | Ok(res) |
1723 | } |
1724 | |
1725 | mod sealed { |
1726 | pub struct SealedMarker; |
1727 | } |
1728 | |