use ast::span::Spanned;
use chumsky::{
combinator::{Map, OrNot, SeparatedBy, Then},
prelude::*,
primitive::Just,
Error,
};
use tokens::Token;
use uuid::Uuid;
pub(crate) fn parse_comment() -> impl Parser<Token, String, Error = Simple<Token>> + Clone {
filter_map(|span, token| match token {
Token::Comment(text) => Ok(text),
_ => Err(Simple::custom(span, "expected comment")),
})
}
pub(crate) fn parse_ident() -> impl Parser<Token, String, Error = Simple<Token>> + Clone {
filter_map(|span, token| match token {
Token::Ident(ident) => Ok(ident),
_ => Err(Simple::custom(span, "expected identifier")),
})
}
pub(crate) fn parse_uuid() -> impl Parser<Token, Uuid, Error = Simple<Token>> + Clone {
filter_map(|span, token| match token {
Token::Uuid(ident) => Ok(ident),
_ => Err(Simple::custom(span, "expected uuid")),
})
}
pub(crate) fn parse_op<U, O>(
op: impl Parser<Token, U, Error = Simple<Token>> + Clone,
item: impl Parser<Token, Spanned<O>, Error = Simple<Token>> + Clone,
) -> impl Parser<Token, Vec<Spanned<O>>, Error = Simple<Token>> + Clone {
op.ignore_then(item.separated_by_comma())
}
pub(crate) fn parse_function<A, O, U>(
op: impl Parser<Token, U, Error = Simple<Token>> + Clone,
args: impl Parser<Token, Spanned<A>, Error = Simple<Token>> + Clone,
arrow: impl Parser<Token, U, Error = Simple<Token>> + Clone,
output: impl Parser<Token, Spanned<O>, Error = Simple<Token>> + Clone,
) -> impl Parser<Token, (Vec<Spanned<A>>, Spanned<O>), Error = Simple<Token>> + Clone {
op.ignore_then(args.separated_by(just(Token::Comma)))
.then_ignore(arrow)
.then(output)
}
pub(crate) fn parse_collection<T>(
begin: Token,
item: impl Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
end: Token,
) -> impl Parser<Token, Vec<Spanned<T>>, Error = Simple<Token>> + Clone {
item.separated_by(just(Token::Comma))
.delimited_by(just(begin), just(end))
}
pub(crate) fn parse_typed<I, T>(
item: impl Parser<Token, Spanned<I>, Error = Simple<Token>> + Clone,
ty: impl Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
) -> impl Parser<Token, (Spanned<I>, Spanned<T>), Error = Simple<Token>> + Clone {
just(Token::FromHere)
.ignore_then(item)
.then_ignore(just(Token::TypeAnnotation))
.then(ty)
}
pub(crate) fn parse_attr<I, T>(
attr: impl Parser<Token, Spanned<I>, Error = Simple<Token>> + Clone,
item: impl Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
) -> impl Parser<Token, (Spanned<I>, Spanned<T>), Error = Simple<Token>> + Clone {
just(Token::Attribute).ignore_then(attr).in_().then(item)
}
type In<T, E, O> =
Map<Then<T, OrNot<Just<Token, Token, E>>>, fn((O, Option<Token>)) -> O, (O, Option<Token>)>;
type SeparatedByComma<T, E, O> = Map<
OrNot<
Map<
Then<SeparatedBy<T, Just<Token, Token, E>, Token>, OrNot<Just<Token, Token, E>>>,
fn((Vec<O>, Option<Token>)) -> Vec<O>,
(Vec<O>, Option<Token>),
>,
>,
fn(Option<Vec<O>>) -> Vec<O>,
Option<Vec<O>>,
>;
type SeparatedByCommaAtLeastOne<T, E, O> = Map<
Then<SeparatedBy<T, Just<Token, Token, E>, Token>, OrNot<Just<Token, Token, E>>>,
fn((Vec<O>, Option<Token>)) -> Vec<O>,
(Vec<O>, Option<Token>),
>;
pub(crate) trait ParserExt<O>
where
Self: Parser<Token, O> + Sized,
{
fn in_(self) -> In<Self, Self::Error, O>;
fn separated_by_comma(self) -> SeparatedByComma<Self, Self::Error, O>;
fn separated_by_comma_at_least_one(self) -> SeparatedByCommaAtLeastOne<Self, Self::Error, O>;
}
impl<T: Parser<Token, O, Error = E>, O, E: Error<Token>> ParserExt<O> for T {
fn in_(self) -> In<Self, Self::Error, O>
where
Self: Sized,
{
self.then_ignore(just(Token::In).or_not())
}
fn separated_by_comma(self) -> SeparatedByComma<Self, Self::Error, O> {
self.separated_by_comma_at_least_one()
.or_not()
.map(|option| option.unwrap_or_default())
}
fn separated_by_comma_at_least_one(self) -> SeparatedByCommaAtLeastOne<Self, Self::Error, O> {
self.separated_by(just(Token::Comma))
.at_least(1)
.then_ignore(just(Token::Dot).or_not())
}
}