diff --git a/Cargo.toml b/Cargo.toml index 5ee344f..a8eae12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1" [dev-dependencies] -anyhow = "1" pretty_assertions = "0.7" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..35f2467 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,2 @@ +pub type Error = anyhow::Error; +pub type Result = std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index 2d06385..f862ab1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +mod error; +mod reader; mod scanner; mod token; diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 0000000..3176e27 --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,205 @@ +use std::collections::VecDeque; + +use anyhow::bail; + +use crate::error::Result; + +#[derive(Debug)] +struct Reader { + src: I, + buffer: VecDeque, + column: usize, + mark: usize, + + reset_column: bool, +} + +impl Reader +where + I: Iterator>, +{ + pub fn new(src: I) -> Self { + Self { + src, + buffer: VecDeque::new(), + column: 0, + mark: 0, + + reset_column: false, + } + } + + /// Number of chars seen so far + pub fn mark(&self) -> usize { + self.mark + } + + /// How deep into a line we currently are + pub fn column(&self) -> usize { + self.column + } + + /// Reserve amt chars, returning an error if it could not reserve the + /// requested amount + pub fn reserve_exact(&mut self, amt: usize) -> Result<()> { + let reserved = self.reserve(amt)?; + + if reserved != amt { + bail!("only reserved {}/{} chars @{}", reserved, amt, self.mark()); + } + + Ok(()) + } + + /// Attempt to reserve up to amt chars, returning the actual number added + pub fn reserve(&mut self, amt: usize) -> Result { + let mut done = 0; + + while done < amt { + match self.src.next() { + Some(Ok(c)) => self.buffer.push_front(c), + Some(Err(e)) => return Err(e), + None => return Ok(done), + } + + done += 1; + } + + Ok(done) + } + + fn next_char(&mut self) -> Result> { + match self.read_one()? { + Some(c) => { + // We hit a line end previously, reset column size + if self.reset_column { + self.reset_column = false; + self.column = 0; + } + + // We just hit a line end, arm column reset + if c == '\n' { + self.reset_column = true; + } + + self.column += 1; + self.mark += 1; + + Ok(Some(c)) + } + None => Ok(None), + } + } + + fn read_one(&mut self) -> Result> { + match self.buffer.is_empty() { + true => self.src.next().transpose(), + false => Ok(self.buffer.pop_back()), + } + } +} + +impl Iterator for Reader +where + I: Iterator>, +{ + type Item = Result; + + fn next(&mut self) -> Option { + self.next_char().transpose() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::{assert_eq, assert_ne}; + + macro_rules! data { + ($data:expr) => { + $data.chars().map(Ok) + }; + () => { + "1234567890".chars().map(Ok) + }; + } + + #[test] + fn amount() { + let data = data!(); + let r = Reader::new(data); + + assert_eq!(r.into_iter().count(), 10); + } + + #[test] + fn reserve() { + let data = data!(); + let mut r = Reader::new(data); + + let amount = r.reserve(20).expect("impossible to error"); + + assert_ne!(amount, 20); + assert_eq!(amount, 10); + } + + #[test] + fn reserve_partial() { + let data = data!(); + let mut r = Reader::new(data); + + let amount = r.reserve(5).expect("impossible to error"); + + assert_eq!(amount, 5); + } + + #[test] + fn reserve_one() { + let data = data!(); + let mut r = Reader::new(data); + + let amount = r.reserve(1).expect("impossible to error"); + + assert_eq!(amount, 1); + } + + #[test] + fn reserve_zero() { + let data = data!(); + let mut r = Reader::new(data); + + let amount = r.reserve(0).expect("impossible to error"); + + assert_eq!(amount, 0); + } + + #[test] + fn column() { + let data = data!("abc\nefg\nhijkl"); + let expected = vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; + let mut r = Reader::new(data); + + assert_eq!(r.column(), 0); + + for indent in expected { + let c = r.next().unwrap().expect("impossible to error"); + + assert_eq!(r.column(), indent, "@ char: {}, mark: {}", c, r.mark()); + } + } + + #[test] + fn mark() { + let data = data!(); + let expected = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let mut r = Reader::new(data); + + assert_eq!(r.mark(), 0); + + for mark in expected { + let c = r.next().unwrap().expect("impossible to error"); + + assert_eq!(r.mark(), mark, "@ char: {}, mark: {}", c, r.mark()); + } + } +}