diff --git a/src/combinators.rs b/src/combinators.rs index a6455d5..bd76baf 100644 --- a/src/combinators.rs +++ b/src/combinators.rs @@ -759,3 +759,12 @@ macro_rules! make_fn { }; } + +#[macro_export] +macro_rules! pos { + ($i:expr) => {{ + let _i = $i.clone(); + use $crate::TextPositionTracker; + $crate::Result::Complete($i, (_i.line(), _i.column())) + }} +} \ No newline at end of file diff --git a/src/iter.rs b/src/iter.rs index b28edc1..72dbc40 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use std::iter::Iterator; -use super::{InputIter, Offsetable, Span, SpanRange}; +use super::{InputIter, Offsetable, Span, SpanRange, TextPositionTracker}; /// Implements `InputIter` for any slice of T. #[derive(Debug)] @@ -86,6 +86,8 @@ impl<'a, T: Debug> From<&'a Vec> for SliceIter<'a, T> { pub struct StrIter<'a> { source: &'a str, offset: usize, + line: usize, + column: usize, } impl<'a> StrIter<'a> { @@ -94,6 +96,8 @@ impl<'a> StrIter<'a> { StrIter { source: source, offset: 0, + line: 1, + column: 1, } } } @@ -103,8 +107,15 @@ impl<'a> Iterator for StrIter<'a> { fn next(&mut self) -> Option { match self.source.as_bytes().get(self.offset) { + // TODO count lines and columns. Some(item) => { self.offset += 1; + if *item == b'\n' { + self.line += 1; + self.column = 1; + } else { + self.column += 1; + } Some(item) } None => None, @@ -118,11 +129,23 @@ impl<'a> Offsetable for StrIter<'a> { } } +impl<'a> TextPositionTracker for StrIter<'a> { + fn line(&self) -> usize { + self.line + } + + fn column(&self) -> usize { + self.column + } +} + impl<'a> Clone for StrIter<'a> { fn clone(&self) -> Self { StrIter { source: self.source, offset: self.offset, + line: self.line, + column: self.column, } } } diff --git a/src/lib.rs b/src/lib.rs index b5fbc33..3a34c63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,6 +61,12 @@ impl Offsetable for usize { } } +/// Trait for Inputs that can track lines and columns in a text input. +pub trait TextPositionTracker { + fn line(&self) -> usize; + fn column(&self) -> usize; +} + pub enum SpanRange { Range(std::ops::Range), RangeTo(std::ops::RangeTo), diff --git a/src/test.rs b/src/test.rs index 7e1b355..e43f6a0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -462,3 +462,21 @@ fn test_ascii_ws_carriage_return() { let result = ascii_ws(iter); assert!(result.is_complete()); } + +use super::TextPositionTracker; +#[test] +fn test_position_tracking_striter() { + let input_str = "\n"; + let mut iter = StrIter::new(input_str); + assert_eq!(iter.line(), 1); + assert_eq!(iter.column(), 1); + iter.next(); + assert_eq!(iter.line(), 2); + assert_eq!(iter.column(), 1); + let pos_result = pos!(iter); + assert!(pos_result.is_complete()); + if let Result::Complete(_, (line, column)) = pos_result { + assert_eq!(line, 2); + assert_eq!(column, 1); + } +}