use std::marker::PhantomData;
use primitives::{ConsumedResult, Error, Info, ParseError, Parser, RangeStream, StreamError,
StreamOnce, Tracked};
use primitives::FastResult::*;
pub struct Range<I>(I::Range)
where
I: RangeStream;
impl<I> Parser for Range<I>
where
I: RangeStream,
I::Range: PartialEq + ::primitives::Range,
{
type Input = I;
type Output = I::Range;
#[inline]
fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
use primitives::Range;
let position = input.position();
match input.uncons_range(self.0.len()) {
Ok(other) => if other == self.0 {
ConsumedOk((other, input))
} else {
EmptyErr(ParseError::empty(position).into())
},
Err(err) => EmptyErr(ParseError::new(position, err).into()),
}
}
fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
errors
.error
.add_error(Error::Expected(Info::Range(self.0.clone())));
}
}
pub struct Recognize<P>(P);
impl<P> Parser for Recognize<P>
where
P: Parser,
P::Input: RangeStream,
<P::Input as StreamOnce>::Range: ::primitives::Range,
{
type Input = P::Input;
type Output = <P::Input as StreamOnce>::Range;
#[inline]
fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
let (_, new_input) = ctry!(self.0.parse_lazy(input.clone()));
let distance = input.distance(&new_input.into_inner());
take(distance).parse_lazy(input)
}
fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
self.0.add_error(errors)
}
}
#[inline(always)]
pub fn recognize<P>(parser: P) -> Recognize<P>
where
P: Parser,
{
Recognize(parser)
}
#[inline(always)]
pub fn range<I>(i: I::Range) -> Range<I>
where
I: RangeStream,
I::Range: PartialEq + ::primitives::Range,
{
Range(i)
}
pub struct Take<I>(usize, PhantomData<fn(I) -> I>);
impl<I> Parser for Take<I>
where
I: RangeStream,
I::Range: ::primitives::Range,
{
type Input = I;
type Output = I::Range;
#[inline]
fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
let position = input.position();
match input.uncons_range(self.0) {
Ok(x) => ConsumedOk((x, input)),
Err(err) => EmptyErr(ParseError::new(position, err).into()),
}
}
}
#[inline(always)]
pub fn take<I>(n: usize) -> Take<I>
where
I: RangeStream,
I::Range: ::primitives::Range,
{
Take(n, PhantomData)
}
pub struct TakeWhile<I, F>(F, PhantomData<fn(I) -> I>);
impl<I, F> Parser for TakeWhile<I, F>
where
I: RangeStream,
I::Range: ::primitives::Range,
F: FnMut(I::Item) -> bool,
{
type Input = I;
type Output = I::Range;
#[inline]
fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
::primitives::uncons_while(input, &mut self.0)
}
}
#[inline(always)]
pub fn take_while<I, F>(f: F) -> TakeWhile<I, F>
where
I: RangeStream,
F: FnMut(I::Item) -> bool,
{
TakeWhile(f, PhantomData)
}
pub struct TakeWhile1<I, F>(F, PhantomData<fn(I) -> I>);
impl<I, F> Parser for TakeWhile1<I, F>
where
I: RangeStream,
I::Range: ::primitives::Range,
F: FnMut(I::Item) -> bool,
{
type Input = I;
type Output = I::Range;
#[inline]
fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
match ::primitives::uncons_while(input, &mut self.0) {
ConsumedOk((v, input)) => ConsumedOk((v, input)),
EmptyOk((_, input)) => {
let position = input.position();
EmptyErr(ParseError::empty(position).into())
}
EmptyErr(err) => EmptyErr(err),
ConsumedErr(err) => ConsumedErr(err),
}
}
}
#[inline(always)]
pub fn take_while1<I, F>(f: F) -> TakeWhile1<I, F>
where
I: RangeStream,
I::Range: ::primitives::Range,
F: FnMut(I::Item) -> bool,
{
TakeWhile1(f, PhantomData)
}
pub struct TakeUntilRange<I>(I::Range)
where
I: RangeStream;
impl<I> Parser for TakeUntilRange<I>
where
I: RangeStream,
I::Range: PartialEq + ::primitives::Range,
{
type Input = I;
type Output = I::Range;
#[inline]
fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
use primitives::Range;
let len = self.0.len();
let mut to_consume = 0;
let mut look_ahead_input = input.clone();
loop {
match look_ahead_input.clone().uncons_range(len) {
Ok(xs) => {
if xs == self.0 {
if let Ok(consumed) = input.uncons_range(to_consume) {
if to_consume == 0 {
return EmptyOk((consumed, input));
} else {
return ConsumedOk((consumed, input));
}
}
unreachable!();
} else {
to_consume += 1;
if look_ahead_input.uncons().is_err() {
unreachable!();
}
}
}
Err(e) => return EmptyErr(ParseError::new(look_ahead_input.position(), e).into()),
};
}
}
}
#[inline(always)]
pub fn take_until_range<I>(r: I::Range) -> TakeUntilRange<I>
where
I: RangeStream,
{
TakeUntilRange(r)
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::Parser;
#[test]
fn take_while_test() {
let result = take_while(|c: char| c.is_digit(10)).parse("123abc");
assert_eq!(result, Ok(("123", "abc")));
let result = take_while(|c: char| c.is_digit(10)).parse("abc");
assert_eq!(result, Ok(("", "abc")));
}
#[test]
fn take_while1_test() {
let result = take_while1(|c: char| c.is_digit(10)).parse("123abc");
assert_eq!(result, Ok(("123", "abc")));
let result = take_while1(|c: char| c.is_digit(10)).parse("abc");
assert!(result.is_err());
}
#[test]
fn range_string_no_char_boundary_error() {
let mut parser = range("hello");
let result = parser.parse("hell\u{00EE} world");
assert!(result.is_err());
}
}