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