summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-01-06 16:36:46 +0000
committerGreg Brown <gmb60@cam.ac.uk>2021-01-06 16:36:46 +0000
commiteb280a903f8f20d0b0c0ef5acae955a20929d100 (patch)
tree5e6f38fb41c8c630d3b1e3a1990249f8a98047b8
parentdc10a278cca74d737e4af0fe034a1caa8abb291d (diff)
Create Chewed, the consumer crate.
-rw-r--r--Cargo.toml6
-rw-r--r--chewed/Cargo.toml9
-rw-r--r--chewed/src/error.rs58
-rw-r--r--chewed/src/lib.rs5
-rw-r--r--chewed/src/parse.rs82
-rw-r--r--examples/autochomp/main.rs3
-rw-r--r--src/lower/rust.rs92
7 files changed, 183 insertions, 72 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ae907a5..f25ec0c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
}
}