FEATURE: Add the Span trait and add a StrIter implementation.

This commit is contained in:
Jeremy Wall 2018-09-11 20:54:20 -05:00
parent ed908e7c13
commit beca789911
4 changed files with 104 additions and 39 deletions

View File

@ -648,24 +648,25 @@ macro_rules! text_token {
#[macro_export] #[macro_export]
macro_rules! until { macro_rules! until {
($i:expr, $term:ident!( $( $args:tt )* ) ) => {{ ($i:expr, $term:ident!( $( $args:tt )* ) ) => {{
use $crate::{Result, Offsetable}; use $crate::{Result, Offsetable, Span, SpanRange};
let mut acc = Vec::new(); let start_offset = $i.get_offset();
let mut _i = $i.clone(); let mut _i = $i.clone();
let pfn = || { let pfn = || {
loop { loop {
match $term!(_i.clone(), $($args)*) { match $term!(_i.clone(), $($args)*) {
Result::Complete(_, _) => return Result::Complete(_i, acc), Result::Complete(_, _) => {
let range = SpanRange::Range(start_offset.._i.get_offset());
return Result::Complete(_i, $i.span(range));
},
Result::Abort(e) => return Result::Abort(e), Result::Abort(e) => return Result::Abort(e),
Result::Incomplete(offset) => return Result::Incomplete(offset), Result::Incomplete(offset) => return Result::Incomplete(offset),
Result::Fail(_) => { Result::Fail(_) => {
// noop // noop
} }
} }
let item = match _i.next() { if let None = _i.next() {
Some(it) => it, return Result::Incomplete(_i.get_offset());
None => return Result::Incomplete(_i.get_offset()), }
};
acc.push(item);
} }
}; };
pfn() pfn()
@ -674,24 +675,4 @@ macro_rules! until {
($i:expr, $term:ident) => { ($i:expr, $term:ident) => {
consume_until!($i, run!($term)) consume_until!($i, run!($term))
}; };
}
/// Maps a Result of type Vec<&u8> to a Result of type String.
pub fn must_string<'a, I, E>(matched: Result<I, Vec<&'a u8>>, msg: E) -> Result<I, String>
where
I: InputIter<Item=&'a u8>,
E: Into<String>,
{
match matched {
Result::Complete(i, mut o) => {
let new_string = String::from_utf8(o.drain(0..).map(|b| *b).collect());
match new_string {
Ok(s) => Result::Complete(i, s),
Err(_) => Result::Abort(Error::new(msg, &i)),
}
},
Result::Incomplete(offset) => Result::Incomplete(offset),
Result::Abort(e) => Result::Abort(e),
Result::Fail(e) => Result::Fail(e),
}
} }

View File

@ -2,7 +2,7 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::iter::Iterator; use std::iter::Iterator;
use super::{InputIter, Offsetable}; use super::{InputIter, Offsetable, Span, SpanRange};
/// Implements `InputIter` for any slice of T. /// Implements `InputIter` for any slice of T.
#[derive(Debug)] #[derive(Debug)]
@ -21,10 +21,6 @@ impl<'a, T: Debug + 'a> SliceIter<'a, T> {
} }
} }
fn str_iter<'a>(input: &'a str) -> SliceIter<'a, u8> {
SliceIter::new(input.as_bytes())
}
impl<'a, T: Debug + 'a> Iterator for SliceIter<'a, T> { impl<'a, T: Debug + 'a> Iterator for SliceIter<'a, T> {
type Item = &'a T; type Item = &'a T;
@ -56,10 +52,20 @@ impl<'a, T: Debug + 'a> Clone for SliceIter<'a, T> {
impl<'a, T: Debug + 'a> InputIter for SliceIter<'a, T> {} impl<'a, T: Debug + 'a> InputIter for SliceIter<'a, T> {}
impl<'a, T: Debug + 'a> Span<&'a [T]> for SliceIter<'a, T> {
fn span(&self, idx: SpanRange) -> &'a [T] {
match idx {
SpanRange::Range(r) => self.source.index(r),
SpanRange::RangeTo(r) => self.source.index(r),
SpanRange::RangeFrom(r) => self.source.index(r),
SpanRange::RangeFull(r) => self.source.index(r),
}
}
}
impl<'a> From<&'a str> for SliceIter<'a, u8> { impl<'a> From<&'a str> for SliceIter<'a, u8> {
fn from(source: &'a str) -> Self { fn from(source: &'a str) -> Self {
str_iter(source) SliceIter::new(source.as_bytes())
} }
} }
@ -73,4 +79,71 @@ impl <'a, T: Debug> From<&'a Vec<T>> for SliceIter<'a, T> {
fn from(source: &'a Vec<T>) -> Self { fn from(source: &'a Vec<T>) -> Self {
SliceIter::new(source.as_slice()) SliceIter::new(source.as_slice())
} }
}
/// Implements `InputIter` for any slice of T.
#[derive(Debug)]
pub struct StrIter<'a> {
source: &'a str,
offset: usize,
}
impl<'a> StrIter<'a> {
/// new constructs a StrIter from a Slice of T.
pub fn new(source: &'a str) -> Self {
StrIter {
source: source,
offset: 0,
}
}
}
impl<'a> Iterator for StrIter<'a> {
type Item = &'a u8;
fn next(&mut self) -> Option<Self::Item> {
match self.source.as_bytes().get(self.offset) {
Some(item) => {
self.offset += 1;
Some(item)
}
None => None,
}
}
}
impl<'a> Offsetable for StrIter<'a> {
fn get_offset(&self) -> usize {
self.offset
}
}
impl<'a> Clone for StrIter<'a> {
fn clone(&self) -> Self {
StrIter {
source: self.source,
offset: self.offset,
}
}
}
impl<'a> InputIter for StrIter<'a> {}
impl<'a> From<&'a str> for StrIter<'a> {
fn from(source: &'a str) -> Self {
Self::new(source)
}
}
use std::ops::Index;
impl<'a> Span<&'a str> for StrIter<'a> {
fn span(&self, idx: SpanRange) -> &'a str {
match idx {
SpanRange::Range(r) => self.source.index(r),
SpanRange::RangeTo(r) => self.source.index(r),
SpanRange::RangeFrom(r) => self.source.index(r),
SpanRange::RangeFull(r) => self.source.index(r),
}
}
} }

View File

@ -13,6 +13,18 @@ impl Offsetable for usize {
} }
} }
pub enum SpanRange {
Range(std::ops::Range<usize>),
RangeTo(std::ops::RangeTo<usize>),
RangeFrom(std::ops::RangeFrom<usize>),
RangeFull(std::ops::RangeFull),
}
// An input that can provide a span of a range of the input.
pub trait Span<O> {
fn span(&self, idx: SpanRange) -> O;
}
/// A Cloneable Iterator that can report an offset as a count of processed Items. /// A Cloneable Iterator that can report an offset as a count of processed Items.
pub trait InputIter: Iterator + Clone + Offsetable {} pub trait InputIter: Iterator + Clone + Offsetable {}

View File

@ -1,8 +1,7 @@
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use super::{InputIter, Offsetable, Result}; use super::{InputIter, Offsetable, Result};
use iter::SliceIter; use iter::{StrIter, SliceIter};
use combinators::must_string;
#[test] #[test]
fn test_slice_iter() { fn test_slice_iter() {
@ -354,13 +353,13 @@ fn test_repeat_abort() {
#[test] #[test]
fn test_until() { fn test_until() {
let input_str = "foo; "; let input_str = "foo; ";
let iter = SliceIter::new(input_str.as_bytes()); let iter = StrIter::new(input_str);
let result = must_string(until!(iter, text_token!("; ")), "AAAHHH!".to_string()); let result = until!(iter, text_token!("; "));
assert!(result.is_complete()); assert!(result.is_complete());
if let Result::Complete(i, o) = result { if let Result::Complete(i, o) = result {
assert_eq!(i.get_offset(), 3); assert_eq!(i.get_offset(), 3);
assert_eq!(o.len(), 3); assert_eq!(o.len(), 3);
assert_eq!(&o, "foo"); assert_eq!(o, "foo");
} }
} }