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