diff options
author | Greg Brown <gmb60@cam.ac.uk> | 2021-01-06 16:36:46 +0000 |
---|---|---|
committer | Greg Brown <gmb60@cam.ac.uk> | 2021-01-06 16:36:46 +0000 |
commit | eb280a903f8f20d0b0c0ef5acae955a20929d100 (patch) | |
tree | 5e6f38fb41c8c630d3b1e3a1990249f8a98047b8 | |
parent | dc10a278cca74d737e4af0fe034a1caa8abb291d (diff) |
Create Chewed, the consumer crate.
-rw-r--r-- | Cargo.toml | 6 | ||||
-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 | ||||
-rw-r--r-- | examples/autochomp/main.rs | 3 | ||||
-rw-r--r-- | src/lower/rust.rs | 92 |
7 files changed, 183 insertions, 72 deletions
@@ -4,6 +4,9 @@ version = "0.1.0" authors = ["Greg Brown <gmb60@cam.ac.uk>"] edition = "2018" +[workspace] +members = ["chewed"] + [dependencies] quote = "1.0.7" @@ -14,3 +17,6 @@ features = ["span-locations"] [dependencies.syn] version = "1.0.48" features = ["extra-traits"] + +[dev-dependencies] +chewed = {path = "chewed"} 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()?)) + } +} diff --git a/examples/autochomp/main.rs b/examples/autochomp/main.rs new file mode 100644 index 0000000..afa489f --- /dev/null +++ b/examples/autochomp/main.rs @@ -0,0 +1,3 @@ +pub fn main() { + todo!("Create AutoChomp") +} diff --git a/src/lower/rust.rs b/src/lower/rust.rs index e47fa8a..c236bdd 100644 --- a/src/lower/rust.rs +++ b/src/lower/rust.rs @@ -46,13 +46,7 @@ impl Backend for RustBackend { None => { let id = self.data.len(); let ty = quote! { () }; - let tokens = quote! { - impl Parse for () { - fn parse<P: Parser>(_input: &mut P) -> Result<Self> { - Ok(()) - } - } - }; + let tokens = TokenStream::new(); self.data.push((ty, tokens, BTreeSet::new())); id } @@ -75,7 +69,7 @@ impl Backend for RustBackend { pub struct #name; impl Parse for #name { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { input.take_str(#lit).map(|()| #name) } } @@ -145,10 +139,10 @@ impl Backend for RustBackend { quote! { impl Parse for #name { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { match input.peek() { - #(Some(#iter))|* => input.parse().map(Self::Right), - _ => input.parse().map(Self::Left), + #(Some(#iter))|* => input.take().map(Self::Right), + _ => input.take().map(Self::Left), } } } @@ -158,10 +152,10 @@ impl Backend for RustBackend { quote! { impl Parse for #name { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { match input.peek() { - #(Some(#iter))|* => input.parse().map(Self::Left), - _ => input.parse().map(Self::Right), + #(Some(#iter))|* => input.take().map(Self::Left), + _ => input.take().map(Self::Right), } } } @@ -172,11 +166,11 @@ impl Backend for RustBackend { quote! { impl Parse for #name { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { - match input.peek().ok_or(Error::EndOfStream)? { - #(#iter_left)|* => input.parse().map(Self::Left), - #(#iter_right)|* => input.parse().map(Self::Right), - c => input.error(Error::BadBranch(c, &[#(#iter_first),*])) + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { + match input.peek().ok_or(TakeError::EndOfStream)? { + #(#iter_left)|* => input.take().map(Self::Left), + #(#iter_right)|* => input.take().map(Self::Right), + c => input.error(TakeError::BadBranch(c, &[#(#iter_first),*])) } } } @@ -213,7 +207,7 @@ impl Backend for RustBackend { pub struct #name(#inner_ty); impl Parse for #name { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { + fn take<P: Parser + ?Sized>(input: &mut P) -> Result<Self, TakeError> { input.parse().map(Self) } } @@ -239,10 +233,13 @@ impl Backend for RustBackend { } fn emit_code(self, id: Self::Id) -> Self::Code { - let root = self.data[id].clone(); - let mut tokens = root.1; + let mut tokens = quote! { + use ::chewed::*; + }; + + let (root_ty, root_impl, mut todo) = self.data[id].clone(); + tokens.extend(root_impl); let mut completed = [id].iter().cloned().collect::<BTreeSet<_>>(); - let mut todo = root.2; while !todo.is_empty() { let mut next = BTreeSet::new(); @@ -257,59 +254,10 @@ impl Backend for RustBackend { todo = next; } - let root_ty = root.0; tokens.extend(quote! { pub type Ast = #root_ty; - - pub enum Error { - BadBranch(char, &'static [char]), - EndOfStream, - } - - pub type Result<T> = std::result::Result<T, Error>; - - pub trait Parser: Iterator<Item = char> { - fn peek(&mut self) -> Option<char>; - - fn parse<T: Parse>(&mut self) -> Result<T> where Self: Sized { - T::parse(self) - } - - fn take_str(&mut self, s: &str) -> Result<()>; - - fn error<T>(&mut self, e: Error) -> Result<T>; - } - - pub trait Parse: Sized { - fn parse<P: Parser>(input: &mut P) -> Result<Self>; - } - }); - // Good enough guess of whether we need concatenation rules - if !self.cat_map.is_empty() { - tokens.extend(quote! { - impl<A: Parse, B: Parse> Parse for (A, B) { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { - let a = input.parse()?; - let b = input.parse()?; - Ok((a, b)) - } - } - }); - } - - // Good enough guess of whether we need variable rules - if !self.fix_map.is_empty() { - tokens.extend(quote! { - impl<T: Parse + Sized> Parse for Box<T> { - fn parse<P: Parser>(input: &mut P) -> Result<Self> { - input.parse().map(Box::new) - } - } - }); - } - tokens } } |