use crate::ast::{ self, convert::{Context, Convert}, }; use proc_macro2::Span; use syn::{ ext::IdentExt, parse::{Parse, ParseStream}, punctuated::{Pair, Punctuated}, token, Result, Token, }; pub type Epsilon = Token![_]; impl Convert for Epsilon { fn convert(self, _: &Context) -> ast::Term { ast::Term::Epsilon(self) } } type Ident = syn::Ident; impl Convert for Ident { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let name = self.to_string(); if let Some(variable) = context.get(&name) { Term::Variable(ast::Variable::new(self, variable)) } else { let span = self.span(); Term::Call(ast::Call::new(self, Vec::new(), span)) } } } type Literal = syn::LitStr; impl Convert for Literal { fn convert(self, _context: &Context) -> ast::Term { ast::Term::Literal(self) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Call { name: Ident, paren_tok: token::Paren, args: Punctuated, } impl Call { fn span(&self) -> Span { self.name.span().join(self.paren_tok.span).unwrap_or_else(Span::call_site) } } impl Parse for Call { fn parse(input: ParseStream<'_>) -> Result { let args; let name = input.call(Ident::parse_any)?; let paren_tok = syn::parenthesized!(args in input); let args = args.call(Punctuated::parse_terminated)?; Ok(Self { name, paren_tok, args, }) } } impl Convert for Call { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let span = self.span(); Term::Call(ast::Call::new( self.name, self.args .into_pairs() .map(|pair| match pair { Pair::Punctuated(t, _) => t.convert(context), Pair::End(t) => t.convert(context), }) .collect(), span, )) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Fix { bracket_token: token::Bracket, arg: Ident, paren_token: token::Paren, expr: Expression, } impl Fix { fn span(&self) -> Span { self.bracket_token.span.join(self.paren_token.span).unwrap_or_else(Span::call_site) } } impl Parse for Fix { fn parse(input: ParseStream<'_>) -> Result { let arg; let bracket_token = syn::bracketed!(arg in input); let arg = arg.call(Ident::parse_any)?; let expr; let paren_token = syn::parenthesized!(expr in input); let expr = expr.parse()?; Ok(Self { bracket_token, arg, paren_token, expr, }) } } impl Convert for Fix { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let span = self.span(); let expr = self.expr; let arg_name = self.arg.to_string(); Term::Fix(ast::Fix::new( self.arg, context.push(arg_name, |c| expr.convert(c)), span, )) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct ParenExpression { paren_tok: token::Paren, expr: Expression, } impl ParenExpression { fn span(&self) -> Span { self.paren_tok.span } } impl Parse for ParenExpression { fn parse(input: ParseStream<'_>) -> Result { let expr; let paren_tok = syn::parenthesized!(expr in input); let expr = expr.parse()?; Ok(Self { paren_tok, expr }) } } impl Convert for ParenExpression { fn convert(self, context: &Context) -> ast::Term { self.expr.convert(context) } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum Term { Epsilon(Epsilon), Ident(Ident), Literal(Literal), Call(Call), Fix(Fix), Parens(ParenExpression), } impl Term { fn span(&self) -> Span { match self { Self::Epsilon(eps) => eps.span, Self::Ident(ident) => ident.span(), Self::Literal(lit) => lit.span(), Self::Call(call) => call.span(), Self::Fix(fix) => fix.span(), Self::Parens(paren) => paren.span(), } } } impl Parse for Term { fn parse(input: ParseStream<'_>) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(Token![_]) { input.parse().map(Self::Epsilon) } else if lookahead.peek(syn::LitStr) { input.parse().map(Self::Literal) } else if lookahead.peek(token::Bracket) { input.parse().map(Self::Fix) } else if lookahead.peek(token::Paren) { let content; let paren_tok = syn::parenthesized!(content in input); content .parse() .map(|expr| Self::Parens(ParenExpression { paren_tok, expr })) } else if lookahead.peek(Ident::peek_any) { let name = input.call(Ident::parse_any)?; if input.peek(token::Paren) { let args; let paren_tok = syn::parenthesized!(args in input); args.call(Punctuated::parse_terminated).map(|args| { Self::Call(Call { name, paren_tok, args, }) }) }else { Ok(Self::Ident(name)) } } else { Err(lookahead.error()) } } } impl Convert for Term { fn convert(self, context: &Context) -> ast::Term { match self { Self::Epsilon(e) => e.convert(context), Self::Ident(i) => i.convert(context), Self::Literal(l) => l.convert(context), Self::Call(c) => c.convert(context), Self::Fix(f) => f.convert(context), Self::Parens(e) => e.convert(context), } } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Cat { terms: Punctuated, } impl Cat { fn span(&self) -> Span { let mut pairs = self.terms.pairs(); let mut span = pairs.next().and_then(|pair| match pair { Pair::Punctuated(term, punct) => term.span().join(punct.span), Pair::End(term) => Some(term.span()), }); for pair in pairs { span = span.and_then(|span| match pair { Pair::Punctuated(term, punct) => span.join(term.span()).and_then(|s| s.join(punct.span)), Pair::End(term) => span.join(term.span()), }) } span.unwrap_or_else(Span::call_site) } } impl Parse for Cat { fn parse(input: ParseStream<'_>) -> Result { Ok(Self { terms: input.call(Punctuated::parse_separated_nonempty)?, }) } } impl Convert for Cat { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let mut iter = self.terms.into_pairs(); let init = match iter.next_back().unwrap() { Pair::Punctuated(_, _) => unreachable!(), Pair::End(t) => t.convert(context), }; iter.rfold(init, |term, pair| match pair { Pair::Punctuated(t, p) => Term::Cat(ast::Cat::new(t.convert(context), p, term)), Pair::End(_) => unreachable!(), }) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Alt { cats: Punctuated, } impl Alt { fn span(&self) -> Span { let mut pairs = self.cats.pairs(); let mut span = pairs.next().and_then(|pair| match pair { Pair::Punctuated(cat, punct) => cat.span().join(punct.span), Pair::End(cat) => Some(cat.span()), }); for pair in pairs { span = span.and_then(|span| match pair { Pair::Punctuated(cat, punct) => span.join(cat.span()).and_then(|s| s.join(punct.span)), Pair::End(cat) => span.join(cat.span()), }) } span.unwrap_or_else(Span::call_site) } } impl Parse for Alt { fn parse(input: ParseStream<'_>) -> Result { Ok(Self { cats: input.call(Punctuated::parse_separated_nonempty)?, }) } } impl Convert for Alt { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let mut iter = self.cats.into_pairs(); let init = match iter.next_back().unwrap() { Pair::Punctuated(_, _) => unreachable!(), Pair::End(t) => t.convert(context), }; iter.rfold(init, |term, pair| match pair { Pair::Punctuated(t, p) => Term::Alt(ast::Alt::new(t.convert(context), p, term)), Pair::End(_) => unreachable!(), }) } } pub type Expression = Alt; #[cfg(test)] mod tests { use super::{Epsilon, Ident, Literal}; use syn::parse_str; #[test] fn parse_epsilon() { assert!(parse_str::("_").is_ok()); } #[test] fn parse_ident() { assert_eq!(parse_str::("x").unwrap().to_string(), "x"); assert_eq!(parse_str::("x_yz").unwrap().to_string(), "x_yz"); assert_eq!(parse_str::("a123").unwrap().to_string(), "a123"); assert_eq!(parse_str::("𝒢𝒢").unwrap().to_string(), "𝒢𝒢"); assert!(parse_str::("1").is_err()); assert!(parse_str::("_").is_err()); } #[test] fn parse_literal() { assert_eq!(parse_str::(r#""hello""#).unwrap().value(), "hello") } }