Skip to content

Instantly share code, notes, and snippets.

@m4rw3r
Created February 10, 2016 13:23
Show Gist options
  • Save m4rw3r/1f43559dcd73bf46e845 to your computer and use it in GitHub Desktop.
Save m4rw3r/1f43559dcd73bf46e845 to your computer and use it in GitHub Desktop.
Just a quick implementation of line-numbering where a parser is wrapped to keep track of the number of lines it has parsed so far
use std::marker::PhantomData;
pub trait NumberingType {
type Token;
type Position;
fn update(&mut self, &[Self::Token]);
fn position(&self) -> Self::Position;
}
pub struct LineNumber(u64);
impl LineNumber {
pub fn new() -> Self { LineNumber(1) }
}
impl NumberingType for LineNumber {
type Token = u8;
type Position = u64;
fn update(&mut self, b: &[Self::Token]) {
self.0 = self.0 + b.iter().filter(|&&c| c == b'\n').count() as u64
}
fn position(&self) -> Self::Position {
self.0
}
}
pub struct Numbering<'i, T, P, R, E>
where T: NumberingType,
P: FnMut(Input<'i, T::Token>) -> ParseResult<'i, T::Token, R, E>,
R: 'i,
E: 'i,
<T as NumberingType>::Token: 'i {
parser: P,
numbering: T,
_re: PhantomData<&'i (R, E)>,
}
impl<'i, N, P, R, E> Numbering<'i, N, P, R, E>
where N: NumberingType,
P: FnMut(Input<'i, N::Token>) -> ParseResult<'i, N::Token, R, E>,
R: 'i,
E: 'i,
<N as NumberingType>::Token: 'i {
pub fn new(n: N, p: P) -> Self {
Numbering {
parser: p,
numbering: n,
_re: PhantomData,
}
}
pub fn parse(&mut self, i: Input<'i, N::Token>) -> ParseResult<'i, N::Token, (N::Position, R), E> {
use primitives::InputBuffer;
use primitives::InputClone;
use primitives::IntoInner;
use primitives::State;
let buf = i.clone();
let pos = self.numbering.position();
match (self.parser)(i.clone()).into_inner() {
State::Data(remainder, t) => {
self.numbering.update(&buf.buffer()[..buf.buffer().len() - remainder.buffer().len()]);
remainder.ret((pos, t))
},
State::Error(remainder, e) => {
self.numbering.update(&buf.buffer()[..buf.buffer().len() - remainder.len()]);
buf.replace(remainder).err(e)
},
State::Incomplete(n) => buf.incomplete(n)
}
}
}
#[test]
fn line_numbering() {
use take;
use std::cell::Cell;
use buffer::{IntoStream, Stream, StreamError};
let mut data = b"abc\nc\n\ndef".into_stream();
// Just some state to make sure we are called the correct number of times:
let i = Cell::new(0);
let p = |d| {
i.set(i.get() + 1);
take(d, 2)
};
let mut n = Numbering::new(LineNumber::new(), p);
// If we could implement FnMut for Numbering then we would be good, but we need to wrap now:
let mut m = |i| n.parse(i);
assert_eq!(data.parse(&mut m), Ok((1, &b"ab"[..])));
assert_eq!(i.get(), 1);
assert_eq!(data.parse(&mut m), Ok((1, &b"c\n"[..])));
assert_eq!(i.get(), 2);
assert_eq!(data.parse(&mut m), Ok((2, &b"c\n"[..])));
assert_eq!(i.get(), 3);
assert_eq!(data.parse(&mut m), Ok((3, &b"\nd"[..])));
assert_eq!(i.get(), 4);
assert_eq!(data.parse(&mut m), Ok((4, &b"ef"[..])));
assert_eq!(i.get(), 5);
assert_eq!(data.parse(&mut m), Err(StreamError::EndOfInput));
assert_eq!(i.get(), 5);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment