use crate::{ error::{ParseError, ParseResult, StreamError}, lib::fmt, stream::{ IteratorStream, Positioned, RangeStreamOnce, ResetStream, SliceStream, StreamErrorFor, StreamOnce, }, }; #[cfg(feature = "std")] use crate::stream::read; /// Trait for tracking the current position of a `Stream`. pub trait Positioner { /// The type which keeps track of the position type Position: Clone + Ord; type Checkpoint: Clone; /// Returns the current position fn position(&self) -> Self::Position; /// Updates the position given that `token` has been taken from the stream fn update(&mut self, token: &Item); fn checkpoint(&self) -> Self::Checkpoint; fn reset(&mut self, checkpoint: Self::Checkpoint); } /// Trait for tracking the current position of a `RangeStream`. pub trait RangePositioner: Positioner { /// Updates the position given that `range` has been taken from the stream fn update_range(&mut self, range: &Range); } /// Defines a default `Positioner` type for a particular `Stream` type. pub trait DefaultPositioned { type Positioner: Default; } impl<'a> DefaultPositioned for &'a str { type Positioner = SourcePosition; } impl<'a, T> DefaultPositioned for &'a [T] { type Positioner = IndexPositioner; } impl<'a, T> DefaultPositioned for SliceStream<'a, T> { type Positioner = IndexPositioner; } impl DefaultPositioned for IteratorStream { type Positioner = IndexPositioner; } #[cfg(feature = "std")] impl DefaultPositioned for read::Stream { type Positioner = IndexPositioner; } /// The `Stream` struct maintains the current position in the stream `Input` using /// the `Positioner` trait to track the position. /// /// ``` /// # #![cfg(feature = "std")] /// # extern crate combine; /// # use combine::*; /// # use combine::stream::easy; /// # use combine::stream::position; /// # fn main() { /// let result = token(b'9') /// .message("Not a nine") /// .easy_parse(position::Stream::new(&b"8"[..])); /// assert_eq!(result, Err(easy::Errors { /// position: 0, /// errors: vec![ /// easy::Error::Unexpected(b'8'.into()), /// easy::Error::Expected(b'9'.into()), /// easy::Error::Message("Not a nine".into()) /// ] /// })); /// # } /// ``` #[derive(Clone, Debug, PartialEq)] pub struct Stream { /// The input stream used when items are requested pub input: Input, /// The positioner used to update the current position pub positioner: X, } impl Stream where Input: StreamOnce, X: Positioner, { /// Creates a new `Stream` from an input stream and a positioner. pub fn with_positioner(input: Input, positioner: X) -> Stream { Stream { input, positioner } } } impl Stream where Input: StreamOnce + DefaultPositioned, Input::Positioner: Positioner, { /// Creates a new `Stream` from an input stream and its default positioner. pub fn new(input: Input) -> Stream { Stream::with_positioner(input, Input::Positioner::default()) } } impl Positioned for Stream where Input: StreamOnce, X: Positioner, S: StreamError, Input::Error: ParseError, Input::Error: ParseError, { #[inline] fn position(&self) -> Self::Position { self.positioner.position() } } impl StreamOnce for Stream where Input: StreamOnce, X: Positioner, S: StreamError, Input::Error: ParseError, Input::Error: ParseError, { type Token = Input::Token; type Range = Input::Range; type Position = X::Position; type Error = Input::Error; #[inline] fn uncons(&mut self) -> Result> { self.input.uncons().map(|c| { self.positioner.update(&c); c }) } fn is_partial(&self) -> bool { self.input.is_partial() } } impl Positioner for &'_ mut T where Item: Clone, T: ?Sized + Positioner, { type Position = T::Position; type Checkpoint = T::Checkpoint; #[inline] fn position(&self) -> T::Position { (**self).position() } #[inline] fn update(&mut self, item: &Item) { (**self).update(item) } #[inline] fn checkpoint(&self) -> Self::Checkpoint { (**self).checkpoint() } #[inline] fn reset(&mut self, checkpoint: Self::Checkpoint) { (**self).reset(checkpoint) } } impl RangePositioner for &'_ mut T where Item: Clone, Range: Clone + crate::stream::Range, T: ?Sized + RangePositioner, { fn update_range(&mut self, range: &Range) { (**self).update_range(range); } } /// The `IndexPositioner` struct maintains the current index into the stream `Input`. The /// initial index is index 0. Each `Item` committed increments the index by 1; each `range` committed /// increments the position by `range.len()`. #[derive(Clone, Debug, Default, PartialEq)] pub struct IndexPositioner(usize); impl Positioner for IndexPositioner where Item: Clone, { type Position = usize; type Checkpoint = Self; #[inline] fn position(&self) -> usize { self.0 } #[inline] fn update(&mut self, _item: &Item) { self.0 += 1 } #[inline] fn checkpoint(&self) -> Self::Checkpoint { self.clone() } #[inline] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint; } } impl IndexPositioner { pub fn new() -> IndexPositioner { IndexPositioner::new_with_position(0) } pub fn new_with_position(position: usize) -> IndexPositioner { IndexPositioner(position) } } impl RangePositioner for IndexPositioner where Item: Clone, Range: Clone + crate::stream::Range, { fn update_range(&mut self, range: &Range) { self.0 += range.len() } } /// Struct which represents a position in a source file. #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct SourcePosition { /// Current line of the input pub line: i32, /// Current column of the input pub column: i32, } impl Default for SourcePosition { fn default() -> Self { SourcePosition { line: 1, column: 1 } } } impl fmt::Display for SourcePosition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "line: {}, column: {}", self.line, self.column) } } impl SourcePosition { pub fn new() -> Self { SourcePosition::default() } } impl Positioner for SourcePosition { type Position = SourcePosition; type Checkpoint = Self; #[inline] fn position(&self) -> SourcePosition { *self } #[inline] fn update(&mut self, token: &char) { self.column += 1; if *token == '\n' { self.column = 1; self.line += 1; } } #[inline] fn checkpoint(&self) -> Self::Checkpoint { *self } #[inline] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint; } } impl Positioner for SourcePosition { type Position = SourcePosition; type Checkpoint = Self; #[inline] fn position(&self) -> SourcePosition { *self } #[inline] fn update(&mut self, token: &u8) { self.column += 1; if *token == b'\n' { self.column = 1; self.line += 1; } } #[inline] fn checkpoint(&self) -> Self::Checkpoint { *self } #[inline] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint; } } impl<'a> RangePositioner for SourcePosition { fn update_range(&mut self, range: &&'a str) { for c in range.chars() { self.update(&c); } } } impl RangeStreamOnce for Stream where Input: RangeStreamOnce, X: RangePositioner, S: StreamError, Input::Error: ParseError, Input::Error: ParseError, Input::Position: Clone + Ord, { #[inline] fn uncons_range(&mut self, size: usize) -> Result> { self.input.uncons_range(size).map(|range| { self.positioner.update_range(&range); range }) } #[inline] fn uncons_while(&mut self, mut predicate: F) -> Result> where F: FnMut(Input::Token) -> bool, { let positioner = &mut self.positioner; self.input.uncons_while(|t| { if predicate(t.clone()) { positioner.update(&t); true } else { false } }) } #[inline] fn uncons_while1( &mut self, mut predicate: F, ) -> ParseResult> where F: FnMut(Self::Token) -> bool, { let positioner = &mut self.positioner; self.input.uncons_while1(|t| { if predicate(t.clone()) { positioner.update(&t); true } else { false } }) } #[inline] fn distance(&self, end: &Self::Checkpoint) -> usize { self.input.distance(&end.input) } fn range(&self) -> Self::Range { self.input.range() } } impl ResetStream for Stream where Input: ResetStream, X: Positioner, S: StreamError, Input::Error: ParseError, Input::Error: ParseError, { type Checkpoint = Stream; fn checkpoint(&self) -> Self::Checkpoint { Stream { input: self.input.checkpoint(), positioner: self.positioner.checkpoint(), } } fn reset(&mut self, checkpoint: Self::Checkpoint) -> Result<(), Self::Error> { self.input.reset(checkpoint.input)?; self.positioner.reset(checkpoint.positioner); Ok(()) } } #[cfg(all(feature = "std", test))] mod tests { use crate::Parser; use super::*; #[test] fn test_positioner() { let input = ["a".to_string(), "b".to_string()]; let mut parser = crate::any(); let result = parser.parse(Stream::new(&input[..])); assert_eq!( result, Ok(( "a".to_string(), Stream::with_positioner( &["b".to_string()][..], IndexPositioner::new_with_position(1) ) )) ); } #[test] fn test_range_positioner() { let input = ["a".to_string(), "b".to_string(), "c".to_string()]; let mut parser = crate::parser::range::take(2); let result = parser.parse(Stream::new(&input[..])); assert_eq!( result, Ok(( &["a".to_string(), "b".to_string()][..], Stream::with_positioner( &["c".to_string()][..], IndexPositioner::new_with_position(2) ) )) ); } }