summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-04-30 14:36:54 +0100
committerGreg Brown <gmb60@cam.ac.uk>2021-04-30 14:36:54 +0100
commitd93ba45b0a952dea06f5cc5326eefb0818525912 (patch)
tree01cffb12342546cd95e13ee2e9a944295264971e
parent449695dcf87d26b0d06a51ca27bbc8214338f954 (diff)
Move items in `nibble::cst` to `nibble`
-rw-r--r--autochomp/benches/parse.rs2
-rw-r--r--autochomp/tests/compare/main.rs2
-rw-r--r--chomp-macro/src/lib.rs2
-rw-r--r--src/main.rs2
-rw-r--r--src/nibble/convert.rs2
-rw-r--r--src/nibble/cst.rs472
-rw-r--r--src/nibble/mod.rs474
7 files changed, 478 insertions, 478 deletions
diff --git a/autochomp/benches/parse.rs b/autochomp/benches/parse.rs
index f185ef0..a42543a 100644
--- a/autochomp/benches/parse.rs
+++ b/autochomp/benches/parse.rs
@@ -149,7 +149,7 @@ fn parse_autochomp(input: &str) -> Result<(Vec<Function>, NamedExpression), Box<
}
fn parse_chomp(input: &str) -> Result<(Vec<Function>, NamedExpression), Box<dyn Error>> {
- syn::parse_str::<nibble::cst::File>(input)
+ syn::parse_str::<nibble::File>(input)
.map_err(|e| Box::new(e) as Box<dyn Error>)
.and_then(|file| file.convert().map_err(|e| Box::new(e) as Box<dyn Error>))
}
diff --git a/autochomp/tests/compare/main.rs b/autochomp/tests/compare/main.rs
index 5149881..681014a 100644
--- a/autochomp/tests/compare/main.rs
+++ b/autochomp/tests/compare/main.rs
@@ -2,7 +2,7 @@ use chewed::{IterWrapper, Parser};
use chomp::{chomp::ast::{Function, NamedExpression}, nibble};
fn chomp(input: &str) -> (Vec<Function>, NamedExpression) {
- syn::parse_str::<nibble::cst::File>(&input).unwrap().convert().unwrap()
+ syn::parse_str::<nibble::File>(&input).unwrap().convert().unwrap()
}
fn autonibble(input: &str) -> (Vec<Function>, NamedExpression) {
diff --git a/chomp-macro/src/lib.rs b/chomp-macro/src/lib.rs
index 91e1527..67964ec 100644
--- a/chomp-macro/src/lib.rs
+++ b/chomp-macro/src/lib.rs
@@ -9,7 +9,7 @@ use chomp::{
visit::Visitable,
},
lower::RustBackend,
- nibble::cst::File,
+ nibble::File,
};
use proc_macro::TokenStream;
use syn::Error;
diff --git a/src/main.rs b/src/main.rs
index 10156e6..0e7193e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,7 +15,7 @@ use chomp::{
visit::Visitable,
},
lower::RustBackend,
- nibble::cst::File,
+ nibble::File,
};
fn main() {
diff --git a/src/nibble/convert.rs b/src/nibble/convert.rs
index cef3159..afebafe 100644
--- a/src/nibble/convert.rs
+++ b/src/nibble/convert.rs
@@ -8,7 +8,7 @@ use crate::chomp::{
Name,
};
-use super::cst::{Alt, Call, Cat, Fix, Ident, Labelled, ParenExpression, Term};
+use super::{Alt, Call, Cat, Fix, Ident, Labelled, ParenExpression, Term};
#[derive(Clone, Copy, Debug)]
pub enum Binding {
diff --git a/src/nibble/cst.rs b/src/nibble/cst.rs
deleted file mode 100644
index d8b71b7..0000000
--- a/src/nibble/cst.rs
+++ /dev/null
@@ -1,472 +0,0 @@
-use std::fmt;
-
-use proc_macro2::Span;
-use syn::{
- bracketed,
- ext::IdentExt,
- parenthesized,
- parse::{Parse, ParseStream},
- punctuated::{Pair, Punctuated},
- token::{Bracket, Comma, Let, Match, Paren},
- LitStr, Token,
-};
-
-use crate::chomp::{ast, Name};
-
-use super::convert::{Context, Convert, ConvertError};
-
-pub type Epsilon = Token![_];
-
-pub type Ident = syn::Ident;
-
-pub type Literal = LitStr;
-
-#[derive(Clone)]
-pub struct ArgList<T> {
- paren_token: Paren,
- args: Punctuated<T, Comma>,
-}
-
-impl<T> ArgList<T> {
- pub fn span(&self) -> Span {
- self.paren_token.span
- }
-
- pub fn len(&self) -> usize {
- self.args.len()
- }
-
- pub fn is_empty(&self) -> bool {
- self.args.is_empty()
- }
-}
-
-impl<T> IntoIterator for ArgList<T> {
- type Item = T;
-
- type IntoIter = <Punctuated<T, Comma> as IntoIterator>::IntoIter;
-
- fn into_iter(self) -> Self::IntoIter {
- self.args.into_iter()
- }
-}
-
-impl<T: Parse> Parse for ArgList<T> {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let args;
- let paren_token = parenthesized!(args in input);
- let args = args.call(Punctuated::parse_terminated)?;
- Ok(Self { paren_token, args })
- }
-}
-
-impl<T: fmt::Debug> fmt::Debug for ArgList<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ArgList")?;
- f.debug_list().entries(self.args.iter()).finish()
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Call {
- pub name: Ident,
- pub args: ArgList<Expression>,
-}
-
-impl Call {
- pub fn span(&self) -> Option<Span> {
- self.name.span().join(self.args.span())
- }
-}
-
-impl Parse for Call {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let name = input.call(Ident::parse_any)?;
- let args = input.parse()?;
- Ok(Self { name, args })
- }
-}
-
-#[derive(Clone)]
-pub struct Fix {
- bracket_token: Bracket,
- pub arg: Ident,
- paren_token: Paren,
- pub expr: Expression,
-}
-
-impl Fix {
- pub fn span(&self) -> Option<Span> {
- self.bracket_token.span.join(self.paren_token.span)
- }
-}
-
-impl Parse for Fix {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let arg;
- let bracket_token = bracketed!(arg in input);
- let arg = arg.call(Ident::parse_any)?;
- let expr;
- let paren_token = parenthesized!(expr in input);
- let expr = expr.parse()?;
- Ok(Self {
- bracket_token,
- arg,
- paren_token,
- expr,
- })
- }
-}
-
-impl fmt::Debug for Fix {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Fix")
- .field("arg", &self.arg)
- .field("expr", &self.expr)
- .finish()
- }
-}
-
-#[derive(Clone)]
-pub struct ParenExpression {
- paren_token: Paren,
- pub expr: Expression,
-}
-
-impl Parse for ParenExpression {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let expr;
- let paren_token = parenthesized!(expr in input);
- let expr = expr.parse()?;
- Ok(Self { paren_token, expr })
- }
-}
-
-impl fmt::Debug for ParenExpression {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("ParenExpression")
- .field("expr", &self.expr)
- .finish()
- }
-}
-
-#[derive(Clone)]
-pub enum Term {
- Epsilon(Epsilon),
- Ident(Ident),
- Literal(Literal),
- Call(Call),
- Fix(Fix),
- Parens(ParenExpression),
-}
-
-impl Term {
- pub fn span(&self) -> Option<Span> {
- match self {
- Self::Epsilon(e) => Some(e.span),
- Self::Ident(i) => Some(i.span()),
- Self::Literal(l) => Some(l.span()),
- Self::Call(c) => c.span(),
- Self::Fix(f) => f.span(),
- Self::Parens(p) => Some(p.paren_token.span),
- }
- }
-}
-
-impl Parse for Term {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let lookahead = input.lookahead1();
-
- if lookahead.peek(Token![_]) {
- input.parse().map(Self::Epsilon)
- } else if lookahead.peek(LitStr) {
- input.parse().map(Self::Literal)
- } else if lookahead.peek(Bracket) {
- input.parse().map(Self::Fix)
- } else if lookahead.peek(Paren) {
- input.parse().map(Self::Parens)
- } else if lookahead.peek(Ident::peek_any) {
- let name = input.call(Ident::parse_any)?;
-
- if input.peek(Paren) {
- input.parse().map(|args| Self::Call(Call { name, args }))
- } else {
- Ok(Self::Ident(name))
- }
- } else {
- Err(lookahead.error())
- }
- }
-}
-
-impl fmt::Debug for Term {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Term::Epsilon(_) => write!(f, "Term::Epsilon"),
- Term::Ident(i) => write!(f, "Term::Ident({:?})", i),
- Term::Literal(l) => write!(f, "Term::Literal({:?})", l.value()),
- Term::Call(c) => write!(f, "Term::Call({:?})", c),
- Term::Fix(x) => write!(f, "Term::Fix({:?})", x),
- Term::Parens(p) => write!(f, "Term::Parens({:?})", p),
- }
- }
-}
-
-#[derive(Clone)]
-pub struct Cat(pub Punctuated<Term, Token![.]>);
-
-impl Parse for Cat {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- input.call(Punctuated::parse_separated_nonempty).map(Self)
- }
-}
-
-impl Cat {
- pub fn span(&self) -> Option<Span> {
- let mut iter = self.0.pairs();
- let span = match iter.next()? {
- Pair::Punctuated(t, p) => t.span().and_then(|s| s.join(p.span)),
- Pair::End(t) => t.span(),
- }?;
-
- iter.try_fold(span, |span, pair| match pair {
- Pair::Punctuated(t, p) => t
- .span()
- .and_then(|s| span.join(s))
- .and_then(|s| s.join(p.span)),
- Pair::End(t) => t.span().and_then(|s| span.join(s)),
- })
- }
-}
-
-impl fmt::Debug for Cat {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "Cat")?;
- f.debug_list().entries(self.0.iter()).finish()
- }
-}
-
-#[derive(Clone)]
-pub struct Label {
- colon_tok: Token![:],
- pub label: Ident,
-}
-
-impl Label {
- pub fn span(&self) -> Option<Span> {
- self.colon_tok.span.join(self.label.span())
- }
-}
-
-impl Parse for Label {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let colon_tok = input.parse()?;
- let label = input.call(Ident::parse_any)?;
- Ok(Self { colon_tok, label })
- }
-}
-
-impl fmt::Debug for Label {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Label").field("label", &self.label).finish()
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Labelled {
- pub cat: Cat,
- pub label: Option<Label>,
-}
-
-impl Labelled {
- pub fn span(&self) -> Option<Span> {
- self.cat.span().and_then(|s| {
- self.label
- .as_ref()
- .and_then(|l| l.span().and_then(|t| s.join(t)))
- })
- }
-}
-
-impl Parse for Labelled {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let cat = input.parse()?;
- let label = if input.peek(Token![:]) {
- Some(input.parse()?)
- } else {
- None
- };
- Ok(Self { cat, label })
- }
-}
-
-#[derive(Clone)]
-pub struct Alt(pub Punctuated<Labelled, Token![|]>);
-
-impl Alt {
- pub fn span(&self) -> Option<Span> {
- let mut iter = self.0.pairs();
- let span = match iter.next()? {
- Pair::Punctuated(t, p) => t.span().and_then(|s| s.join(p.span)),
- Pair::End(t) => t.span(),
- }?;
-
- iter.try_fold(span, |span, pair| match pair {
- Pair::Punctuated(t, p) => t
- .span()
- .and_then(|s| span.join(s))
- .and_then(|s| s.join(p.span)),
- Pair::End(t) => t.span().and_then(|s| span.join(s)),
- })
- }
-}
-
-impl Parse for Alt {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- input.call(Punctuated::parse_separated_nonempty).map(Self)
- }
-}
-
-impl fmt::Debug for Alt {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "Alt")?;
- f.debug_list().entries(self.0.iter()).finish()
- }
-}
-
-pub type Expression = Alt;
-
-#[derive(Clone)]
-pub struct LetStatement {
- let_token: Token![let],
- name: Ident,
- args: Option<ArgList<Ident>>,
- eq_token: Token![=],
- expr: Expression,
- semi_token: Token![;],
-}
-
-impl LetStatement {
- pub fn span(&self) -> Option<Span> {
- self.let_token.span.join(self.semi_token.span)
- }
-}
-
-impl Parse for LetStatement {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let let_token = input.parse()?;
- let name = input.call(Ident::parse_any)?;
- let args = if input.peek(Paren) {
- Some(input.parse()?)
- } else {
- None
- };
- let eq_token = input.parse()?;
- let expr = input.parse()?;
- let semi_token = input.parse()?;
-
- Ok(Self {
- let_token,
- name,
- args,
- eq_token,
- expr,
- semi_token,
- })
- }
-}
-
-impl fmt::Debug for LetStatement {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("LetStatement")
- .field("name", &self.name)
- .field("args", &self.args)
- .field("expr", &self.expr)
- .finish()
- }
-}
-
-#[derive(Clone)]
-pub struct GoalStatement {
- match_token: Token![match],
- expr: Expression,
- semi_token: Token![;],
-}
-
-impl Parse for GoalStatement {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let match_token = input.parse()?;
- let expr = input.parse()?;
- let semi_token = input.parse()?;
-
- Ok(Self {
- match_token,
- expr,
- semi_token,
- })
- }
-}
-
-impl fmt::Debug for GoalStatement {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("GoalStatement")
- .field("expr", &self.expr)
- .finish()
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct File {
- lets: Vec<LetStatement>,
- goal: GoalStatement,
-}
-
-impl File {
- pub fn convert(self) -> Result<(Vec<ast::Function>, ast::NamedExpression), ConvertError> {
- let mut names = Vec::new();
- let mut map = Vec::new();
- for stmt in self.lets {
- let span = stmt.span();
- let name: Name = stmt.name.into();
- let params = stmt
- .args
- .into_iter()
- .flat_map(ArgList::into_iter)
- .map(Name::from);
- let mut context = Context::new(&names, params.clone());
- let mut expr = stmt.expr.convert(&mut context)?;
- names.push(name.clone());
- expr.name = Some(name.clone());
- map.push(ast::Function {
- name,
- params: params.map(Some).collect(),
- expr,
- span,
- });
- }
-
- let mut context = Context::new(&names, Vec::new());
- let goal = self.goal.expr.convert(&mut context)?;
- Ok((map, goal))
- }
-}
-
-impl Parse for File {
- fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
- let mut lets = Vec::new();
- let mut lookahead = input.lookahead1();
-
- while lookahead.peek(Let) {
- lets.push(input.parse()?);
- lookahead = input.lookahead1();
- }
-
- let goal = if lookahead.peek(Match) {
- input.parse()?
- } else {
- return Err(lookahead.error());
- };
-
- Ok(Self { lets, goal })
- }
-}
diff --git a/src/nibble/mod.rs b/src/nibble/mod.rs
index 41e895c..3f5c892 100644
--- a/src/nibble/mod.rs
+++ b/src/nibble/mod.rs
@@ -1,2 +1,474 @@
pub mod convert;
-pub mod cst;
+
+use std::fmt;
+
+use proc_macro2::Span;
+use syn::{
+ bracketed,
+ ext::IdentExt,
+ parenthesized,
+ parse::{Parse, ParseStream},
+ punctuated::{Pair, Punctuated},
+ token::{Bracket, Comma, Let, Match, Paren},
+ LitStr, Token,
+};
+
+use crate::chomp::{ast, Name};
+
+use convert::{Context, Convert, ConvertError};
+
+pub type Epsilon = Token![_];
+
+pub type Ident = syn::Ident;
+
+pub type Literal = LitStr;
+
+#[derive(Clone)]
+pub struct ArgList<T> {
+ paren_token: Paren,
+ args: Punctuated<T, Comma>,
+}
+
+impl<T> ArgList<T> {
+ pub fn span(&self) -> Span {
+ self.paren_token.span
+ }
+
+ pub fn len(&self) -> usize {
+ self.args.len()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.args.is_empty()
+ }
+}
+
+impl<T> IntoIterator for ArgList<T> {
+ type Item = T;
+
+ type IntoIter = <Punctuated<T, Comma> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.args.into_iter()
+ }
+}
+
+impl<T: Parse> Parse for ArgList<T> {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let args;
+ let paren_token = parenthesized!(args in input);
+ let args = args.call(Punctuated::parse_terminated)?;
+ Ok(Self { paren_token, args })
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for ArgList<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ArgList")?;
+ f.debug_list().entries(self.args.iter()).finish()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Call {
+ pub name: Ident,
+ pub args: ArgList<Expression>,
+}
+
+impl Call {
+ pub fn span(&self) -> Option<Span> {
+ self.name.span().join(self.args.span())
+ }
+}
+
+impl Parse for Call {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let name = input.call(Ident::parse_any)?;
+ let args = input.parse()?;
+ Ok(Self { name, args })
+ }
+}
+
+#[derive(Clone)]
+pub struct Fix {
+ bracket_token: Bracket,
+ pub arg: Ident,
+ paren_token: Paren,
+ pub expr: Expression,
+}
+
+impl Fix {
+ pub fn span(&self) -> Option<Span> {
+ self.bracket_token.span.join(self.paren_token.span)
+ }
+}
+
+impl Parse for Fix {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let arg;
+ let bracket_token = bracketed!(arg in input);
+ let arg = arg.call(Ident::parse_any)?;
+ let expr;
+ let paren_token = parenthesized!(expr in input);
+ let expr = expr.parse()?;
+ Ok(Self {
+ bracket_token,
+ arg,
+ paren_token,
+ expr,
+ })
+ }
+}
+
+impl fmt::Debug for Fix {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Fix")
+ .field("arg", &self.arg)
+ .field("expr", &self.expr)
+ .finish()
+ }
+}
+
+#[derive(Clone)]
+pub struct ParenExpression {
+ paren_token: Paren,
+ pub expr: Expression,
+}
+
+impl Parse for ParenExpression {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let expr;
+ let paren_token = parenthesized!(expr in input);
+ let expr = expr.parse()?;
+ Ok(Self { paren_token, expr })
+ }
+}
+
+impl fmt::Debug for ParenExpression {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ParenExpression")
+ .field("expr", &self.expr)
+ .finish()
+ }
+}
+
+#[derive(Clone)]
+pub enum Term {
+ Epsilon(Epsilon),
+ Ident(Ident),
+ Literal(Literal),
+ Call(Call),
+ Fix(Fix),
+ Parens(ParenExpression),
+}
+
+impl Term {
+ pub fn span(&self) -> Option<Span> {
+ match self {
+ Self::Epsilon(e) => Some(e.span),
+ Self::Ident(i) => Some(i.span()),
+ Self::Literal(l) => Some(l.span()),
+ Self::Call(c) => c.span(),
+ Self::Fix(f) => f.span(),
+ Self::Parens(p) => Some(p.paren_token.span),
+ }
+ }
+}
+
+impl Parse for Term {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+
+ if lookahead.peek(Token![_]) {
+ input.parse().map(Self::Epsilon)
+ } else if lookahead.peek(LitStr) {
+ input.parse().map(Self::Literal)
+ } else if lookahead.peek(Bracket) {
+ input.parse().map(Self::Fix)
+ } else if lookahead.peek(Paren) {
+ input.parse().map(Self::Parens)
+ } else if lookahead.peek(Ident::peek_any) {
+ let name = input.call(Ident::parse_any)?;
+
+ if input.peek(Paren) {
+ input.parse().map(|args| Self::Call(Call { name, args }))
+ } else {
+ Ok(Self::Ident(name))
+ }
+ } else {
+ Err(lookahead.error())
+ }
+ }
+}
+
+impl fmt::Debug for Term {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Term::Epsilon(_) => write!(f, "Term::Epsilon"),
+ Term::Ident(i) => write!(f, "Term::Ident({:?})", i),
+ Term::Literal(l) => write!(f, "Term::Literal({:?})", l.value()),
+ Term::Call(c) => write!(f, "Term::Call({:?})", c),
+ Term::Fix(x) => write!(f, "Term::Fix({:?})", x),
+ Term::Parens(p) => write!(f, "Term::Parens({:?})", p),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct Cat(pub Punctuated<Term, Token![.]>);
+
+impl Parse for Cat {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ input.call(Punctuated::parse_separated_nonempty).map(Self)
+ }
+}
+
+impl Cat {
+ pub fn span(&self) -> Option<Span> {
+ let mut iter = self.0.pairs();
+ let span = match iter.next()? {
+ Pair::Punctuated(t, p) => t.span().and_then(|s| s.join(p.span)),
+ Pair::End(t) => t.span(),
+ }?;
+
+ iter.try_fold(span, |span, pair| match pair {
+ Pair::Punctuated(t, p) => t
+ .span()
+ .and_then(|s| span.join(s))
+ .and_then(|s| s.join(p.span)),
+ Pair::End(t) => t.span().and_then(|s| span.join(s)),
+ })
+ }
+}
+
+impl fmt::Debug for Cat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Cat")?;
+ f.debug_list().entries(self.0.iter()).finish()
+ }
+}
+
+#[derive(Clone)]
+pub struct Label {
+ colon_tok: Token![:],
+ pub label: Ident,
+}
+
+impl Label {
+ pub fn span(&self) -> Option<Span> {
+ self.colon_tok.span.join(self.label.span())
+ }
+}
+
+impl Parse for Label {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let colon_tok = input.parse()?;
+ let label = input.call(Ident::parse_any)?;
+ Ok(Self { colon_tok, label })
+ }
+}
+
+impl fmt::Debug for Label {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Label").field("label", &self.label).finish()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Labelled {
+ pub cat: Cat,
+ pub label: Option<Label>,
+}
+
+impl Labelled {
+ pub fn span(&self) -> Option<Span> {
+ self.cat.span().and_then(|s| {
+ self.label
+ .as_ref()
+ .and_then(|l| l.span().and_then(|t| s.join(t)))
+ })
+ }
+}
+
+impl Parse for Labelled {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let cat = input.parse()?;
+ let label = if input.peek(Token![:]) {
+ Some(input.parse()?)
+ } else {
+ None
+ };
+ Ok(Self { cat, label })
+ }
+}
+
+#[derive(Clone)]
+pub struct Alt(pub Punctuated<Labelled, Token![|]>);
+
+impl Alt {
+ pub fn span(&self) -> Option<Span> {
+ let mut iter = self.0.pairs();
+ let span = match iter.next()? {
+ Pair::Punctuated(t, p) => t.span().and_then(|s| s.join(p.span)),
+ Pair::End(t) => t.span(),
+ }?;
+
+ iter.try_fold(span, |span, pair| match pair {
+ Pair::Punctuated(t, p) => t
+ .span()
+ .and_then(|s| span.join(s))
+ .and_then(|s| s.join(p.span)),
+ Pair::End(t) => t.span().and_then(|s| span.join(s)),
+ })
+ }
+}
+
+impl Parse for Alt {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ input.call(Punctuated::parse_separated_nonempty).map(Self)
+ }
+}
+
+impl fmt::Debug for Alt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Alt")?;
+ f.debug_list().entries(self.0.iter()).finish()
+ }
+}
+
+pub type Expression = Alt;
+
+#[derive(Clone)]
+pub struct LetStatement {
+ let_token: Token![let],
+ name: Ident,
+ args: Option<ArgList<Ident>>,
+ eq_token: Token![=],
+ expr: Expression,
+ semi_token: Token![;],
+}
+
+impl LetStatement {
+ pub fn span(&self) -> Option<Span> {
+ self.let_token.span.join(self.semi_token.span)
+ }
+}
+
+impl Parse for LetStatement {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let let_token = input.parse()?;
+ let name = input.call(Ident::parse_any)?;
+ let args = if input.peek(Paren) {
+ Some(input.parse()?)
+ } else {
+ None
+ };
+ let eq_token = input.parse()?;
+ let expr = input.parse()?;
+ let semi_token = input.parse()?;
+
+ Ok(Self {
+ let_token,
+ name,
+ args,
+ eq_token,
+ expr,
+ semi_token,
+ })
+ }
+}
+
+impl fmt::Debug for LetStatement {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("LetStatement")
+ .field("name", &self.name)
+ .field("args", &self.args)
+ .field("expr", &self.expr)
+ .finish()
+ }
+}
+
+#[derive(Clone)]
+pub struct GoalStatement {
+ match_token: Token![match],
+ expr: Expression,
+ semi_token: Token![;],
+}
+
+impl Parse for GoalStatement {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let match_token = input.parse()?;
+ let expr = input.parse()?;
+ let semi_token = input.parse()?;
+
+ Ok(Self {
+ match_token,
+ expr,
+ semi_token,
+ })
+ }
+}
+
+impl fmt::Debug for GoalStatement {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("GoalStatement")
+ .field("expr", &self.expr)
+ .finish()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct File {
+ lets: Vec<LetStatement>,
+ goal: GoalStatement,
+}
+
+impl File {
+ pub fn convert(self) -> Result<(Vec<ast::Function>, ast::NamedExpression), ConvertError> {
+ let mut names = Vec::new();
+ let mut map = Vec::new();
+ for stmt in self.lets {
+ let span = stmt.span();
+ let name: Name = stmt.name.into();
+ let params = stmt
+ .args
+ .into_iter()
+ .flat_map(ArgList::into_iter)
+ .map(Name::from);
+ let mut context = Context::new(&names, params.clone());
+ let mut expr = stmt.expr.convert(&mut context)?;
+ names.push(name.clone());
+ expr.name = Some(name.clone());
+ map.push(ast::Function {
+ name,
+ params: params.map(Some).collect(),
+ expr,
+ span,
+ });
+ }
+
+ let mut context = Context::new(&names, Vec::new());
+ let goal = self.goal.expr.convert(&mut context)?;
+ Ok((map, goal))
+ }
+}
+
+impl Parse for File {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let mut lets = Vec::new();
+ let mut lookahead = input.lookahead1();
+
+ while lookahead.peek(Let) {
+ lets.push(input.parse()?);
+ lookahead = input.lookahead1();
+ }
+
+ let goal = if lookahead.peek(Match) {
+ input.parse()?
+ } else {
+ return Err(lookahead.error());
+ };
+
+ Ok(Self { lets, goal })
+ }
+}