diff options
author | Greg Brown <gmb60@cam.ac.uk> | 2021-01-08 18:00:34 +0000 |
---|---|---|
committer | Greg Brown <gmb60@cam.ac.uk> | 2021-01-08 18:00:34 +0000 |
commit | 0a837400e0ffa7fca1a1902b34f375d0dc5b5f6b (patch) | |
tree | f6a71331efcd8e2fbdb3427d7fa0ae5289174f7b | |
parent | e1452227b8bd9ad3805480f8a5a66a75fb8370dd (diff) |
Add positions to chewed errors.
-rw-r--r-- | chewed/src/error.rs | 24 | ||||
-rw-r--r-- | chewed/src/lib.rs | 2 | ||||
-rw-r--r-- | chewed/src/parse.rs | 73 | ||||
-rw-r--r-- | chewed/src/position.rs | 21 |
4 files changed, 103 insertions, 17 deletions
diff --git a/chewed/src/error.rs b/chewed/src/error.rs index 420a376..afe65c0 100644 --- a/chewed/src/error.rs +++ b/chewed/src/error.rs @@ -1,17 +1,19 @@ use std::{error::Error, fmt}; +use super::position::LineCol; + #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum TakeError { - BadBranch(char, &'static [char]), - BadString(String, &'static str), - EndOfStream, + BadBranch(LineCol, char, &'static [char]), + BadString(LineCol, String, &'static str), + EndOfStream(LineCol), } impl fmt::Display for TakeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::BadBranch(got, expected) => { - write!(f, "Unexpected character {:?}. ", got)?; + Self::BadBranch(pos, got, expected) => { + write!(f, "{}: Unexpected character {:?}. ", pos, got)?; if expected.is_empty() { write!(f, "Expected end of input.") @@ -28,8 +30,12 @@ impl fmt::Display for TakeError { Ok(()) } } - Self::BadString(got, expected) => write!(f, "Unexpected string {:?}. Expected {:?}", got, expected), - Self::EndOfStream => write!(f, "Unexpected end of input"), + Self::BadString(pos, got, expected) => write!( + f, + "{}: Unexpected string {:?}. Expected {:?}", + pos, got, expected + ), + Self::EndOfStream(pos) => write!(f, "{}: Unexpected end of input", pos), } } } @@ -39,14 +45,14 @@ impl Error for TakeError {} #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum ParseError { TakeError(TakeError), - InputContinues, + InputContinues(LineCol), } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::TakeError(e) => e.fmt(f), - Self::InputContinues => write!(f, "Expected end of input"), + Self::InputContinues(pos) => write!(f, "{}: Expected end of input", pos), } } } diff --git a/chewed/src/lib.rs b/chewed/src/lib.rs index 341af9d..a2bdd0a 100644 --- a/chewed/src/lib.rs +++ b/chewed/src/lib.rs @@ -1,5 +1,7 @@ mod error; mod parse; +mod position; pub use error::*; pub use parse::*; +pub use position::*; diff --git a/chewed/src/parse.rs b/chewed/src/parse.rs index f6bbd66..58bb94c 100644 --- a/chewed/src/parse.rs +++ b/chewed/src/parse.rs @@ -1,10 +1,15 @@ -use std::iter::Peekable; +use super::{ + error::{ParseError, TakeError}, + position::LineCol, +}; -use super::error::{ParseError, TakeError}; +pub trait Parser { + fn next(&mut self) -> Option<char>; -pub trait Parser: Iterator<Item = char> { fn peek(&mut self) -> Option<char>; + fn pos(&self) -> LineCol; + fn take<P: Parse>(&mut self) -> Result<P, TakeError> { P::take(self) } @@ -28,10 +33,10 @@ pub trait Parser: Iterator<Item = char> { let mut out = String::from(&s[..count]); out.push(got); - return Err(TakeError::BadString(out, s)); + return Err(TakeError::BadString(self.pos(), out, s)); } } else { - return Err(TakeError::EndOfStream); + return Err(TakeError::EndOfStream(self.pos())); } } @@ -39,9 +44,61 @@ pub trait Parser: Iterator<Item = char> { } } -impl<I: Iterator<Item = char>> Parser for Peekable<I> { +pub struct IterWrapper<T: ?Sized> { + pos: LineCol, + next: Option<char>, + iter: T, +} + +impl<T: Iterator<Item = char>> IterWrapper<T> { + pub fn new(iter: T) -> Self { + Self { + pos: LineCol::default(), + next: None, + iter, + } + } +} + +impl<I: ?Sized + Iterator<Item = char>> Parser for IterWrapper<I> { + fn next(&mut self) -> Option<char> { + match self.next.take().or_else(|| self.iter.next()) { + x @ Some('\n') + | x @ Some('\x0B') + | x @ Some('\x0C') + | x @ Some('\u{85}') + | x @ Some('\u{2028}') + | x @ Some('\u{2029}') => { + self.pos.line += 1; + self.pos.col = 0; + x + } + Some('\x0D') => { + if self.peek() == Some('\n') { + self.pos.col += 1; + } else { + self.pos.line += 1; + self.pos.col = 0; + } + Some('\x0D') + } + x => { + self.pos.col += 1; + x + } + } + } + fn peek(&mut self) -> Option<char> { - Peekable::peek(self).copied() + if self.next.is_none() { + self.next = self.iter.next(); + } + + self.next.as_ref().copied() + } + + fn pos(&self) -> LineCol { + self.pos } } @@ -52,7 +109,7 @@ pub trait Parse: Sized { let res = Self::take(&mut input)?; if input.peek().is_some() { - Err(ParseError::InputContinues) + Err(ParseError::InputContinues(input.pos())) } else { Ok(res) } diff --git a/chewed/src/position.rs b/chewed/src/position.rs new file mode 100644 index 0000000..62d9b5a --- /dev/null +++ b/chewed/src/position.rs @@ -0,0 +1,21 @@ +use std::fmt; + +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub struct LineCol { + /// One-indexed line. + pub line: usize, + /// Zero-indexed column. + pub col: usize, +} + +impl Default for LineCol { + fn default() -> Self { + Self { line: 1, col: 0 } + } +} + +impl fmt::Display for LineCol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.line, self.col) + } +} |