Skip to content

Commit

Permalink
feat: Add the range_of parser
Browse files Browse the repository at this point in the history
The `range_of` parser makes it possible to "match" on the input parsed
by a non-range parser and extract the range of input which was parsed.
This makes range parsing much more expressive with only a small cost of
an additional method on `RangeStream`

Closes #83

BREAKING CHANGE

`distance` must be defined on `RangeStream`
  • Loading branch information
Marwes committed Jul 29, 2017
1 parent 5afcf40 commit c29643d
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,11 @@ where
false
})
}

#[inline]
fn distance(&self, end: &Self) -> usize {
self.input.distance(&end.input)
}
}

/// A type alias over the specific `Result` type used by parsers to indicate wether they were
Expand Down Expand Up @@ -804,6 +809,15 @@ pub trait RangeStream: Stream {
fn uncons_while<F>(&mut self, f: F) -> Result<Self::Range, Error<Self::Item, Self::Range>>
where
F: FnMut(Self::Item) -> bool;

/// Returns the distance between `self` and `end`. The returned `usize` must be so that
///
/// ```
/// let start = stream.clone();
/// stream.uncons_range(distance);
/// start.distance() == distance
/// ```
fn distance(&self, end: &Self) -> usize;
}

/// Removes items from the input while `predicate` returns `true`.
Expand Down Expand Up @@ -880,6 +894,11 @@ impl<'a> RangeStream for &'a str {
Err(Error::end_of_input())
}
}

#[inline]
fn distance(&self, end: &Self) -> usize {
end.position().0 - self.position().0
}
}

impl<'a> Range for &'a str {
Expand Down Expand Up @@ -910,6 +929,7 @@ where
Err(Error::end_of_input())
}
}

#[inline]
fn uncons_while<F>(&mut self, mut f: F) -> Result<&'a [T], Error<T, &'a [T]>>
where
Expand All @@ -920,6 +940,11 @@ where
*self = remaining;
Ok(result)
}

#[inline]
fn distance(&self, end: &Self) -> usize {
end.position().0 - self.position().0
}
}

impl<'a> StreamOnce for &'a str {
Expand Down Expand Up @@ -1030,6 +1055,11 @@ where
self.0 = rest;
Ok(range)
}

#[inline]
fn distance(&self, end: &Self) -> usize {
self.0.distance(&end.0)
}
}

/// Wrapper around iterators which allows them to be treated as a stream.
Expand Down
46 changes: 45 additions & 1 deletion src/range.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::marker::PhantomData;

use primitives::{ConsumedResult, Error, Info, ParseError, Parser, RangeStream, StreamError};
use primitives::{ConsumedResult, Error, Info, ParseError, Parser, RangeStream, StreamError, StreamOnce};
use primitives::FastResult::*;

pub struct Range<I>(I::Range)
Expand Down Expand Up @@ -34,6 +34,50 @@ where
}
}

pub struct RangeOf<P>(P);

impl<P> Parser for RangeOf<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 StreamError<Self::Input>) {
self.0.add_error(errors)
}
}

/// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
/// range.
///
/// ```
/// # extern crate combine;
/// # use combine::range::range_of;
/// # use combine::char::letter;
/// # use combine::*;
/// # fn main() {
/// let mut parser = range_of(skip_many1(letter()));
/// assert_eq!(parser.parse("hello world"), Ok(("hello", " world")));
/// assert!(parser.parse("!").is_err());
/// # }
/// ```
#[inline(always)]
pub fn range_of<P>(parser: P) -> RangeOf<P>
where
P: Parser
{
RangeOf(parser)
}

/// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
/// range.
///
Expand Down

0 comments on commit c29643d

Please sign in to comment.