use combinator::{satisfy, skip_many, token, tokens, Expected, Satisfy, SkipMany, Token};
use error::{ConsumedResult, ParseError, Tracked};
use lib::marker::PhantomData;
use parser::sequence::With;
use stream::{Stream, StreamOnce};
use Parser;
#[inline(always)]
pub fn char<I>(c: char) -> Token<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
token(c)
}
parser! {
#[derive(Copy, Clone)]
pub struct Digit;
pub fn digit[I]()(I) -> char
where
[I: Stream<Item = char>,]
{
satisfy(|c: char| c.is_digit(10)).expected("digit")
}
}
impl_token_parser! { Space(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn space<I>() -> Space<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
let f: fn(char) -> bool = char::is_whitespace;
Space(satisfy(f).expected("whitespace"), PhantomData)
}
impl_token_parser! { Spaces(), char, Expected<SkipMany<Space<I>>> }
#[inline(always)]
pub fn spaces<I>() -> Spaces<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Spaces(skip_many(space()).expected("whitespaces"), PhantomData)
}
impl_token_parser! { Newline(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn newline<I>() -> Newline<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Newline(
satisfy(static_fn!((ch, char) -> bool { ch == '\n' })).expected("lf newline"),
PhantomData,
)
}
impl_token_parser! { CrLf(), char, Expected<With<Satisfy<I, fn (char) -> bool>, Newline<I>>> }
#[inline(always)]
pub fn crlf<I>() -> CrLf<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
CrLf(
satisfy(static_fn!((ch, char) -> bool { ch == '\r' }))
.with(newline())
.expected("crlf newline"),
PhantomData,
)
}
impl_token_parser! { Tab(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn tab<I>() -> Tab<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Tab(
satisfy(static_fn!((ch, char) -> bool { ch == '\t' })).expected("tab"),
PhantomData,
)
}
impl_token_parser! { Upper(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn upper<I>() -> Upper<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Upper(
satisfy(static_fn!((ch, char) -> bool { ch.is_uppercase()})).expected("uppercase letter"),
PhantomData,
)
}
impl_token_parser! { Lower(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn lower<I>() -> Lower<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Lower(
satisfy(static_fn!((ch, char) -> bool { ch.is_lowercase() })).expected("lowercase letter"),
PhantomData,
)
}
impl_token_parser! { AlphaNum(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn alpha_num<I>() -> AlphaNum<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
AlphaNum(
satisfy(static_fn!((ch, char) -> bool { ch.is_alphanumeric() }))
.expected("letter or digit"),
PhantomData,
)
}
impl_token_parser! { Letter(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn letter<I>() -> Letter<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Letter(
satisfy(static_fn!((ch, char) -> bool { ch.is_alphabetic() })).expected("letter"),
PhantomData,
)
}
impl_token_parser! { OctDigit(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn oct_digit<I>() -> OctDigit<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
OctDigit(
satisfy(static_fn!((ch, char) -> bool { ch.is_digit(8) })).expected("octal digit"),
PhantomData,
)
}
impl_token_parser! { HexDigit(), char, Expected<Satisfy<I, fn (char) -> bool>> }
#[inline(always)]
pub fn hex_digit<I>() -> HexDigit<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
HexDigit(
satisfy(static_fn!((ch, char) -> bool { ch.is_digit(0x10) })).expected("hexadecimal digit"),
PhantomData,
)
}
fn eq(l: char, r: char) -> bool {
l == r
}
#[derive(Copy, Clone)]
pub struct Str<I>(&'static str, PhantomData<fn(I) -> I>)
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>;
impl<I> Parser for Str<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
type Input = I;
type Output = &'static str;
type PartialState = ();
#[inline]
fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
tokens(eq, self.0.into(), self.0.chars())
.parse_lazy(input)
.map(|_| self.0)
}
fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
tokens::<_, _, I>(eq, self.0.into(), self.0.chars()).add_error(errors)
}
}
#[inline(always)]
pub fn string<I>(s: &'static str) -> Str<I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
Str(s, PhantomData)
}
#[derive(Copy, Clone)]
pub struct StrCmp<C, I>(&'static str, C, PhantomData<fn(I) -> I>)
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>;
impl<C, I> Parser for StrCmp<C, I>
where
C: FnMut(char, char) -> bool,
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
type Input = I;
type Output = &'static str;
type PartialState = ();
#[inline]
fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
tokens(&mut self.1, self.0.into(), self.0.chars())
.parse_lazy(input)
.map(|_| self.0)
}
fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
tokens::<_, _, I>(&mut self.1, self.0.into(), self.0.chars()).add_error(errors)
}
}
#[inline(always)]
pub fn string_cmp<C, I>(s: &'static str, cmp: C) -> StrCmp<C, I>
where
C: FnMut(char, char) -> bool,
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
StrCmp(s, cmp, PhantomData)
}
#[cfg(all(feature = "std", test))]
mod tests {
use super::*;
use stream::easy::{Error, Errors};
use stream::state::{SourcePosition, State};
#[test]
fn space_error() {
let result = space().easy_parse("");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().errors,
vec![Error::end_of_input(), Error::Expected("whitespace".into())]
);
}
#[test]
fn string_consumed() {
let result = string("a").easy_parse(State::new("b"));
assert!(result.is_err());
assert_eq!(
result.unwrap_err().position,
SourcePosition { line: 1, column: 1 }
);
}
#[test]
fn string_error() {
let result = string("abc").easy_parse(State::new("bc"));
assert_eq!(
result,
Err(Errors {
position: SourcePosition { line: 1, column: 1 },
errors: vec![Error::Unexpected('b'.into()), Error::Expected("abc".into())],
})
);
}
}