• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Module containing parsers specialized on character streams.
2 
3 use crate::{
4     error::ParseError,
5     parser::{
6         combinator::no_partial,
7         repeat::skip_many,
8         token::{satisfy, token, tokens_cmp, Token},
9     },
10     stream::Stream,
11     Parser,
12 };
13 
14 /// Parses a character and succeeds if the character is equal to `c`.
15 ///
16 /// ```
17 /// use combine::Parser;
18 /// use combine::parser::char::char;
19 /// assert_eq!(char('!').parse("!"), Ok(('!', "")));
20 /// assert!(char('A').parse("!").is_err());
21 /// ```
char<Input>(c: char) -> Token<Input> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,22 pub fn char<Input>(c: char) -> Token<Input>
23 where
24     Input: Stream<Token = char>,
25     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
26 {
27     token(c)
28 }
29 
30 parser! {
31     #[derive(Copy, Clone)]
32     pub struct Digit;
33     /// Parses a base-10 digit.
34     ///
35     /// ```
36     /// use combine::Parser;
37     /// use combine::parser::char::digit;
38     /// assert_eq!(digit().parse("9"), Ok(('9', "")));
39     /// assert!(digit().parse("A").is_err());
40     /// ```
41     pub fn digit[Input]()(Input) -> char
42     where
43         [Input: Stream<Token = char>,]
44     {
45         satisfy(|c: char| c.is_digit(10)).expected("digit")
46     }
47 }
48 
49 /// Parse a single whitespace according to [`std::char::is_whitespace`].
50 ///
51 /// This includes space characters, tabs and newlines.
52 ///
53 /// [`std::char::is_whitespace`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_whitespace
54 ///
55 /// ```
56 /// use combine::Parser;
57 /// use combine::parser::char::space;
58 /// assert_eq!(space().parse(" "), Ok((' ', "")));
59 /// assert_eq!(space().parse("  "), Ok((' ', " ")));
60 /// assert!(space().parse("!").is_err());
61 /// assert!(space().parse("").is_err());
62 /// ```
space<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,63 pub fn space<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
64 where
65     Input: Stream<Token = char>,
66     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
67 {
68     let f: fn(char) -> bool = char::is_whitespace;
69     satisfy(f).expected("whitespace")
70 }
71 
72 /// Skips over zero or more spaces according to [`std::char::is_whitespace`].
73 ///
74 /// This includes space characters, tabs and newlines.
75 ///
76 /// [`std::char::is_whitespace`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_whitespace
77 ///
78 /// ```
79 /// use combine::Parser;
80 /// use combine::parser::char::spaces;
81 /// assert_eq!(spaces().parse(""), Ok(((), "")));
82 /// assert_eq!(spaces().parse("   "), Ok(((), "")));
83 /// ```
spaces<Input>() -> impl Parser<Input, Output = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,84 pub fn spaces<Input>() -> impl Parser<Input, Output = ()>
85 where
86     Input: Stream<Token = char>,
87     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
88 {
89     skip_many(space()).expected("whitespaces")
90 }
91 
92 /// Parses a newline character (`'\n'`).
93 ///
94 /// ```
95 /// use combine::Parser;
96 /// use combine::parser::char::newline;
97 /// assert_eq!(newline().parse("\n"), Ok(('\n', "")));
98 /// assert!(newline().parse("\r").is_err());
99 /// ```
newline<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,100 pub fn newline<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
101 where
102     Input: Stream<Token = char>,
103     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
104 {
105     satisfy(|ch: char| ch == '\n').expected("lf newline")
106 }
107 
108 /// Parses carriage return and newline (`"\r\n"`), returning the newline character.
109 ///
110 /// ```
111 /// use combine::Parser;
112 /// use combine::parser::char::crlf;
113 /// assert_eq!(crlf().parse("\r\n"), Ok(('\n', "")));
114 /// assert!(crlf().parse("\r").is_err());
115 /// assert!(crlf().parse("\n").is_err());
116 /// ```
crlf<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,117 pub fn crlf<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
118 where
119     Input: Stream<Token = char>,
120     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
121 {
122     no_partial(satisfy(|ch: char| ch == '\r').with(newline())).expected("crlf newline")
123 }
124 
125 /// Parses a tab character (`'\t'`).
126 ///
127 /// ```
128 /// use combine::Parser;
129 /// use combine::parser::char::tab;
130 /// assert_eq!(tab().parse("\t"), Ok(('\t', "")));
131 /// assert!(tab().parse(" ").is_err());
132 /// ```
tab<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,133 pub fn tab<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
134 where
135     Input: Stream<Token = char>,
136     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
137 {
138     satisfy(|ch: char| ch == '\t').expected("tab")
139 }
140 
141 /// Parses an uppercase letter according to [`std::char::is_uppercase`].
142 ///
143 /// [`std::char::is_uppercase`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_uppercase
144 ///
145 /// ```
146 /// use combine::Parser;
147 /// use combine::parser::char::upper;
148 /// assert_eq!(upper().parse("A"), Ok(('A', "")));
149 /// assert!(upper().parse("a").is_err());
150 /// ```
upper<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,151 pub fn upper<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
152 where
153     Input: Stream<Token = char>,
154     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
155 {
156     satisfy(|ch: char| ch.is_uppercase()).expected("uppercase letter")
157 }
158 
159 /// Parses an lowercase letter according to [`std::char::is_lowercase`].
160 ///
161 /// [`std::char::is_lowercase`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_lowercase
162 ///
163 /// ```
164 /// use combine::Parser;
165 /// use combine::parser::char::lower;
166 /// assert_eq!(lower().parse("a"), Ok(('a', "")));
167 /// assert!(lower().parse("A").is_err());
168 /// ```
lower<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,169 pub fn lower<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
170 where
171     Input: Stream<Token = char>,
172     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
173 {
174     satisfy(|ch: char| ch.is_lowercase()).expected("lowercase letter")
175 }
176 
177 /// Parses either an alphabet letter or digit according to [`std::char::is_alphanumeric`].
178 ///
179 /// [`std::char::is_alphanumeric`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_alphanumeric
180 ///
181 /// ```
182 /// use combine::Parser;
183 /// use combine::parser::char::alpha_num;
184 /// assert_eq!(alpha_num().parse("A"), Ok(('A', "")));
185 /// assert_eq!(alpha_num().parse("1"), Ok(('1', "")));
186 /// assert!(alpha_num().parse("!").is_err());
187 /// ```
alpha_num<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,188 pub fn alpha_num<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
189 where
190     Input: Stream<Token = char>,
191     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
192 {
193     satisfy(|ch: char| ch.is_alphanumeric()).expected("letter or digit")
194 }
195 
196 /// Parses an alphabet letter according to [`std::char::is_alphabetic`].
197 ///
198 /// [`std::char::is_alphabetic`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_alphabetic
199 ///
200 /// ```
201 /// use combine::Parser;
202 /// use combine::parser::char::letter;
203 /// assert_eq!(letter().parse("a"), Ok(('a', "")));
204 /// assert_eq!(letter().parse("A"), Ok(('A', "")));
205 /// assert!(letter().parse("9").is_err());
206 /// ```
letter<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,207 pub fn letter<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
208 where
209     Input: Stream<Token = char>,
210     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
211 {
212     satisfy(|ch: char| ch.is_alphabetic()).expected("letter")
213 }
214 
215 /// Parses an octal digit.
216 ///
217 /// ```
218 /// use combine::Parser;
219 /// use combine::parser::char::oct_digit;
220 /// assert_eq!(oct_digit().parse("7"), Ok(('7', "")));
221 /// assert!(oct_digit().parse("8").is_err());
222 /// ```
oct_digit<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,223 pub fn oct_digit<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
224 where
225     Input: Stream<Token = char>,
226     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
227 {
228     satisfy(|ch: char| ch.is_digit(8)).expected("octal digit")
229 }
230 
231 /// Parses a hexdecimal digit with uppercase and lowercase.
232 ///
233 /// ```
234 /// use combine::Parser;
235 /// use combine::parser::char::hex_digit;
236 /// assert_eq!(hex_digit().parse("F"), Ok(('F', "")));
237 /// assert!(hex_digit().parse("H").is_err());
238 /// ```
hex_digit<Input>() -> impl Parser<Input, Output = char, PartialState = ()> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,239 pub fn hex_digit<Input>() -> impl Parser<Input, Output = char, PartialState = ()>
240 where
241     Input: Stream<Token = char>,
242     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
243 {
244     satisfy(|ch: char| ch.is_digit(0x10)).expected("hexadecimal digit")
245 }
246 
247 /// Parses the string `s`.
248 ///
249 /// ```
250 /// # extern crate combine;
251 /// # use combine::*;
252 /// # use combine::parser::char::string;
253 /// # fn main() {
254 /// let result = string("rust")
255 ///     .parse("rust")
256 ///     .map(|x| x.0);
257 /// assert_eq!(result, Ok("rust"));
258 /// # }
259 /// ```
string<'a, Input>(s: &'static str) -> impl Parser<Input, Output = &'a str> where Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,260 pub fn string<'a, Input>(s: &'static str) -> impl Parser<Input, Output = &'a str>
261 where
262     Input: Stream<Token = char>,
263     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
264 {
265     string_cmp(s, |l, r| l == r)
266 }
267 
268 /// Parses the string `s`, using `cmp` to compare each character.
269 ///
270 /// ```
271 /// # extern crate combine;
272 /// # use combine::*;
273 /// # use combine::parser::char::string_cmp;
274 /// # fn main() {
275 /// let result = string_cmp("rust", |l, r| l.eq_ignore_ascii_case(&r))
276 ///     .parse("RusT")
277 ///     .map(|x| x.0);
278 /// assert_eq!(result, Ok("rust"));
279 /// # }
280 /// ```
string_cmp<'a, C, Input>(s: &'static str, cmp: C) -> impl Parser<Input, Output = &'a str> where C: FnMut(char, char) -> bool, Input: Stream<Token = char>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,281 pub fn string_cmp<'a, C, Input>(s: &'static str, cmp: C) -> impl Parser<Input, Output = &'a str>
282 where
283     C: FnMut(char, char) -> bool,
284     Input: Stream<Token = char>,
285     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
286 {
287     tokens_cmp(s.chars(), cmp).map(move |_| s).expected(s)
288 }
289 
290 #[cfg(all(feature = "std", test))]
291 mod tests {
292 
293     use crate::{
294         parser::EasyParser,
295         stream::{
296             easy::{Error, Errors},
297             position::{self, SourcePosition},
298         },
299     };
300 
301     use super::*;
302 
303     #[test]
space_error()304     fn space_error() {
305         let result = space().easy_parse("");
306         assert!(result.is_err());
307         assert_eq!(
308             result.unwrap_err().errors,
309             vec![Error::end_of_input(), Error::Expected("whitespace".into())]
310         );
311     }
312 
313     #[test]
string_committed()314     fn string_committed() {
315         let result = string("a").easy_parse(position::Stream::new("b"));
316         assert!(result.is_err());
317         assert_eq!(
318             result.unwrap_err().position,
319             SourcePosition { line: 1, column: 1 }
320         );
321     }
322 
323     #[test]
string_error()324     fn string_error() {
325         let result = string("abc").easy_parse(position::Stream::new("bc"));
326         assert_eq!(
327             result,
328             Err(Errors {
329                 position: SourcePosition { line: 1, column: 1 },
330                 errors: vec![Error::Unexpected('b'.into()), Error::Expected("abc".into())],
331             })
332         );
333     }
334 }
335