parser_combinators/
char.rs

1use primitives::{Consumed, Parser, ParseError, Error, State, Stream};
2use combinator::{Expected, satisfy, Satisfy, skip_many, SkipMany, token, Token, ParserExt, With};
3use std::marker::PhantomData;
4
5pub type ParseResult<O, I> = ::primitives::ParseResult<O, I, char>;
6
7macro_rules! impl_char_parser {
8    ($name: ident ($($ty_var: ident),*), $inner_type: ty) => {
9    #[derive(Clone)]
10    pub struct $name<I $(,$ty_var)*>($inner_type, PhantomData<fn (I) -> I>)
11        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)*;
12    impl <I $(,$ty_var)*> Parser for $name<I $(,$ty_var)*>
13        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)* {
14        type Input = I;
15        type Output = <$inner_type as Parser>::Output;
16        fn parse_lazy(&mut self, input: State<<Self as Parser>::Input>) -> ParseResult<<Self as Parser>::Output, <Self as Parser>::Input> {
17            self.0.parse_lazy(input)
18        }
19        fn add_error(&mut self, errors: &mut ParseError<<Self::Input as Stream>::Item>) {
20            self.0.add_error(errors)
21        }
22    }
23}
24}
25
26///Parses a character and succeeds if the characther is equal to `c`
27///
28/// ```
29/// # extern crate parser_combinators as pc;
30/// # use pc::*;
31/// # fn main() {
32/// let result = char('!')
33///     .parse("!")
34///     .map(|x| x.0);
35/// assert_eq!(result, Ok('!'));
36/// # }
37/// ```
38pub fn char<I>(c: char) -> Token<I>
39    where I: Stream<Item=char> {
40    token(c)
41}
42
43impl_char_parser! { Digit(), Expected<Satisfy<I, fn (char) -> bool>> }
44///Parses a digit from a stream containing characters
45pub fn digit<I>() -> Digit<I>
46    where I: Stream<Item=char> {
47    Digit(satisfy(static_fn!((c, char) -> bool { c.is_digit(10) }))
48         .expected("digit"), PhantomData)
49}
50
51impl_char_parser! { Space(), Expected<Satisfy<I, fn (char) -> bool>> }
52///Parses whitespace
53pub fn space<I>() -> Space<I>
54    where I: Stream<Item=char> {
55    let f: fn (char) -> bool = char::is_whitespace;
56    Space(satisfy(f)
57        .expected("whitespace"), PhantomData)
58}
59impl_char_parser! { Spaces(), Expected<SkipMany<Space<I>>> }
60///Skips over zero or more spaces
61pub fn spaces<I>() -> Spaces<I>
62    where I: Stream<Item=char> {
63    Spaces(skip_many(space())
64          .expected("whitespaces"), PhantomData)
65}
66
67impl_char_parser! { NewLine(), Expected<Satisfy<I, fn (char) -> bool>> }
68///Parses a newline character
69pub fn newline<I>() -> NewLine<I>
70    where I: Stream<Item=char> {
71    NewLine(satisfy(static_fn!((ch, char) -> bool { ch == '\n' }))
72           .expected("lf newline"), PhantomData)
73}
74
75impl_char_parser! { CrLf(), Expected<With<Satisfy<I, fn (char) -> bool>, NewLine<I>>> }
76///Parses carriage return and newline, returning the newline character.
77pub fn crlf<I>() -> CrLf<I>
78    where I: Stream<Item=char> {
79    CrLf(satisfy(static_fn!((ch, char) -> bool { ch == '\r' }))
80        .with(newline())
81        .expected("crlf newline"), PhantomData)
82}
83
84impl_char_parser! { Tab(), Expected<Satisfy<I, fn (char) -> bool>> }
85///Parses a tab character
86pub fn tab<I>() -> Tab<I>
87    where I: Stream<Item=char> {
88    Tab(satisfy(static_fn!((ch, char) -> bool { ch == '\t' }))
89       .expected("tab"), PhantomData)
90}
91
92impl_char_parser! { Upper(), Expected<Satisfy<I, fn (char) -> bool>> }
93///Parses an uppercase letter
94pub fn upper<I>() -> Upper<I>
95    where I: Stream<Item=char> {
96    Upper(satisfy(static_fn!((ch, char) -> bool { ch.is_uppercase()}))
97         .expected("uppercase letter"), PhantomData)
98}
99
100impl_char_parser! { Lower(), Expected<Satisfy<I, fn (char) -> bool>> }
101///Parses an lowercase letter
102pub fn lower<I>() -> Lower<I>
103    where I: Stream<Item=char> {
104    Lower(satisfy(static_fn!((ch, char) -> bool { ch.is_lowercase() }))
105         .expected("lowercase letter"), PhantomData)
106}
107
108impl_char_parser! { AlphaNum(), Expected<Satisfy<I, fn (char) -> bool>> }
109///Parses either an alphabet letter or digit
110pub fn alpha_num<I>() -> AlphaNum<I>
111    where I: Stream<Item=char> {
112    AlphaNum(satisfy(static_fn!((ch, char) -> bool { ch.is_alphanumeric() }))
113            .expected("letter or digit"), PhantomData)
114}
115
116impl_char_parser! { Letter(), Expected<Satisfy<I, fn (char) -> bool>> }
117///Parses an alphabet letter
118pub fn letter<I>() -> Letter<I>
119    where I: Stream<Item=char> {
120    Letter(satisfy(static_fn!((ch, char) -> bool { ch.is_alphabetic() }))
121          .expected("letter"), PhantomData)
122}
123
124impl_char_parser! { OctDigit(), Expected<Satisfy<I, fn (char) -> bool>> }
125///Parses an octal digit
126pub fn oct_digit<I>() -> OctDigit<I>
127    where I: Stream<Item=char> {
128    OctDigit(satisfy(static_fn!((ch, char) -> bool { ch.is_digit(8) }))
129            .expected("octal digit"), PhantomData)
130}
131
132impl_char_parser! { HexDigit(), Expected<Satisfy<I, fn (char) -> bool>> }
133///Parses a hexdecimal digit with uppercase and lowercase
134pub fn hex_digit<I>() -> HexDigit<I>
135    where I: Stream<Item=char> {
136    HexDigit(satisfy(static_fn!((ch, char) -> bool { ch.is_digit(0x10) }))
137            .expected("hexadecimal digit"), PhantomData)
138}
139
140
141#[derive(Clone)]
142pub struct String<I>(&'static str, PhantomData<I>);
143impl <I> Parser for String<I>
144    where I: Stream<Item=char> {
145    type Input = I;
146    type Output = &'static str;
147    fn parse_lazy(&mut self, mut input: State<I>) -> ParseResult<&'static str, I> {
148        let start = input.position;
149        let mut consumed = false;
150        for c in self.0.chars() {
151            match input.uncons() {
152                Ok((other, rest)) => {
153                    if c != other {
154                        return Err(if consumed {
155                            let errors = vec![Error::Unexpected(other), Error::Expected(self.0.into())];
156                            let error = ParseError::from_errors(start, errors);
157                            Consumed::Consumed(error)
158                        } else {
159                            Consumed::Empty(ParseError::empty(start))
160                        })
161                    }
162                    consumed = true;
163                    input = rest.into_inner();
164                }
165                Err(error) => {
166                    return error.combine(|mut error| {
167                        error.position = start;
168                        Err(if consumed { Consumed::Consumed(error) } else { Consumed::Empty(error) })
169                    })
170                }
171            }
172        }
173        Ok((self.0, if consumed { Consumed::Consumed(input) } else { Consumed::Empty(input) }))
174    }
175    fn add_error(&mut self, errors: &mut ParseError<<Self::Input as Stream>::Item>) {
176        errors.add_error(Error::Expected(self.0.into()));
177    }
178}
179
180///Parses the string `s`
181///
182/// ```
183/// # extern crate parser_combinators as pc;
184/// # use pc::*;
185/// # fn main() {
186/// let result = string("rust")
187///     .parse("rust")
188///     .map(|x| x.0);
189/// assert_eq!(result, Ok("rust"));
190/// # }
191/// ```
192pub fn string<I>(s: &'static str) -> String<I>
193    where I: Stream<Item=char> {
194    String(s, PhantomData)
195}
196
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use primitives::{Error, ParseError, Parser, SourcePosition};
202
203    #[test]
204    fn space_error() {
205        let result = space()
206            .parse("");
207        assert!(result.is_err());
208        assert_eq!(result.unwrap_err().errors, vec![Error::Message("End of input".into()), Error::Expected("whitespace".into())]);
209
210    }
211
212    #[test]
213    fn string_consumed() {
214        let result = string("a").parse("b");
215        assert!(result.is_err());
216        assert_eq!(result.unwrap_err().position, SourcePosition { line: 1, column: 1 });
217    }
218
219    #[test]
220    fn string_error() {
221        let result = string("abc").parse("bc");
222        assert_eq!(result, Err(ParseError {
223            position: SourcePosition { line: 1, column: 1 },
224            errors: vec![Error::Unexpected('b'), Error::Expected("abc".into())]
225        }));
226    }
227}