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