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 { paren_token: Paren, args: Punctuated, } impl ArgList { 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 IntoIterator for ArgList { type Item = T; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.args.into_iter() } } impl Parse for ArgList { fn parse(input: ParseStream<'_>) -> syn::Result { let args; let paren_token = parenthesized!(args in input); let args = args.call(Punctuated::parse_terminated)?; Ok(Self { paren_token, args }) } } #[derive(Clone)] pub struct Call { pub name: Ident, pub args: ArgList, } impl Call { pub fn span(&self) -> Option { self.name.span().join(self.args.span()) } } impl Parse for Call { fn parse(input: ParseStream<'_>) -> syn::Result { 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 { self.bracket_token.span.join(self.paren_token.span) } } impl Parse for Fix { fn parse(input: ParseStream<'_>) -> syn::Result { 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, }) } } #[derive(Clone)] pub struct ParenExpression { paren_token: Paren, pub expr: Expression, } impl Parse for ParenExpression { fn parse(input: ParseStream<'_>) -> syn::Result { let expr; let paren_token = parenthesized!(expr in input); let expr = expr.parse()?; Ok(Self { paren_token, expr }) } } #[derive(Clone)] pub enum Term { Epsilon(Epsilon), Ident(Ident), Literal(Literal), Call(Call), Fix(Fix), Parens(ParenExpression), } impl Term { pub fn span(&self) -> Option { 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 { 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()) } } } #[derive(Clone)] pub struct Cat(pub Punctuated); impl Parse for Cat { fn parse(input: ParseStream<'_>) -> syn::Result { input.call(Punctuated::parse_separated_nonempty).map(Self) } } impl Cat { pub fn span(&self) -> Option { 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)), }) } } #[derive(Clone)] pub struct Label { colon_tok: Token![:], pub label: Ident, } impl Label { pub fn span(&self) -> Option { self.colon_tok.span.join(self.label.span()) } } impl Parse for Label { fn parse(input: ParseStream<'_>) -> syn::Result { let colon_tok = input.parse()?; let label = input.call(Ident::parse_any)?; Ok(Self { colon_tok, label }) } } #[derive(Clone)] pub struct Labelled { pub cat: Cat, pub label: Option