summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-01-08 18:00:34 +0000
committerGreg Brown <gmb60@cam.ac.uk>2021-01-08 18:00:34 +0000
commit0a837400e0ffa7fca1a1902b34f375d0dc5b5f6b (patch)
treef6a71331efcd8e2fbdb3427d7fa0ae5289174f7b
parente1452227b8bd9ad3805480f8a5a66a75fb8370dd (diff)
Add positions to chewed errors.
-rw-r--r--chewed/src/error.rs24
-rw-r--r--chewed/src/lib.rs2
-rw-r--r--chewed/src/parse.rs73
-rw-r--r--chewed/src/position.rs21
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)
+ }
+}