diff options
Diffstat (limited to 'chewed')
-rw-r--r-- | chewed/Cargo.toml | 9 | ||||
-rw-r--r-- | chewed/src/error.rs | 58 | ||||
-rw-r--r-- | chewed/src/lib.rs | 5 | ||||
-rw-r--r-- | chewed/src/parse.rs | 82 |
4 files changed, 154 insertions, 0 deletions
diff --git a/chewed/Cargo.toml b/chewed/Cargo.toml new file mode 100644 index 0000000..87053bc --- /dev/null +++ b/chewed/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chewed" +version = "0.1.0" +authors = ["Greg Brown <gmb60@cam.ac.uk>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/chewed/src/error.rs b/chewed/src/error.rs new file mode 100644 index 0000000..cb2cc4b --- /dev/null +++ b/chewed/src/error.rs @@ -0,0 +1,58 @@ +use std::{error::Error, fmt}; + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum TakeError { + BadBranch(char, &'static [char]), + BadString(String, &'static str), + EndOfStream, +} + +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)?; + + if expected.is_empty() { + write!(f, "Expected end of input.") + } else if expected.len() == 1 { + write!(f, "Expected character {:?}.", expected[0]) + } else { + let mut iter = expected.iter(); + write!(f, "Expected one of {:?}", iter.next().unwrap())?; + + for c in iter { + write!(f, ", {:?}", c)?; + } + + Ok(()) + } + } + Self::BadString(got, expected) => write!(f, "Unexpected string {:?}. Expected {:?}", got, expected), + Self::EndOfStream => write!(f, "Unexpected end of input"), + } + } +} + +impl Error for TakeError {} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum ParseError { + TakeError(TakeError), + InputContinues, +} + +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"), + } + } +} + +impl From<TakeError> for ParseError { + fn from(e: TakeError) -> Self { + Self::TakeError(e) + } +} diff --git a/chewed/src/lib.rs b/chewed/src/lib.rs new file mode 100644 index 0000000..341af9d --- /dev/null +++ b/chewed/src/lib.rs @@ -0,0 +1,5 @@ +mod error; +mod parse; + +pub use error::*; +pub use parse::*; diff --git a/chewed/src/parse.rs b/chewed/src/parse.rs new file mode 100644 index 0000000..0687ea5 --- /dev/null +++ b/chewed/src/parse.rs @@ -0,0 +1,82 @@ +use super::error::{ParseError, TakeError}; + +pub trait Parser: Iterator<Item = char> { + fn peek(&mut self) -> Option<char>; + + fn take<P: Parse>(&mut self) -> Result<P, TakeError> { + P::take(self) + } + + fn parse<P: Parse>(self) -> Result<P, ParseError> + where + Self: Sized, + { + P::parse(self) + } + + fn take_str(&mut self, s: &'static str) -> Result<(), TakeError> { + let mut count = 0; + + for exp in s.chars() { + if let Some(got) = self.peek() { + if got == exp { + self.next(); + count += 1 + } else { + let mut out = String::from(&s[..count]); + out.push(got); + + return Err(TakeError::BadString(out, s)); + } + } else { + return Err(TakeError::EndOfStream); + } + } + + Ok(()) + } +} + +pub trait Parse: Sized { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError>; + + fn parse<P: Parser>(mut input: P) -> Result<Self, ParseError> { + let res = Self::take(&mut input)?; + + if input.peek().is_some() { + Err(ParseError::InputContinues) + } else { + Ok(res) + } + } +} + +impl Parse for () { + fn take<P: Parser + ?Sized>(_: &mut P) -> Result<Self, TakeError> { + Ok(()) + } +} + +impl<A: Parse, B: Parse> Parse for (A, B) { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { + let a = input.take()?; + let b = input.take()?; + Ok((a, b)) + } + + fn parse<P: Parser>(mut input: P) -> Result<Self, ParseError> { + let a = A::take(&mut input)?; + let b = B::parse(input)?; + Ok((a, b)) + } +} + +impl<T: Parse + Sized> Parse for Box<T> { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { + Ok(Box::new(input.take()?)) + } + + fn parse<P: Parser>(input: P) -> Result<Self, ParseError> { + Ok(Box::new(input.parse()?)) + } +} |