use nom::error::{ErrorKind, ParseError};
use super::*;
impl<'a> AstGroup<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (rest, (head, children, important)) =
tuple((AstStyle::parse, Self::parse_pair, opt(char('!'))))(input)?;
Ok((rest, Self { important: important.is_some(), head, children }))
}
#[inline]
fn parse_pair(input: &'a str) -> IResult<&'a str, Vec<AstGroupItem>> {
let (rest, paired) = delimited_paired('(', ')')(input)?;
Ok((rest, AstGroupItem::parse_many(paired.trim())?.1))
}
}
impl<'a> AstGroupItem<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
alt((Self::maybe_group, Self::maybe_style))(input)
}
#[inline]
fn parse_many(input: &'a str) -> IResult<&'a str, Vec<Self>> {
let head = AstGroupItem::parse;
let rest = many0(tuple((multispace1, AstGroupItem::parse)));
let (rest, (first, other)) = tuple((head, rest))(input)?;
let mut out = vec![first];
out.extend(other.into_iter().map(|s| s.1));
Ok((rest, out))
}
fn maybe_group(input: &'a str) -> IResult<&'a str, Self> {
let (rest, o) = AstGroup::parse(input)?;
Ok((rest, Self::Grouped(o)))
}
fn maybe_style(input: &'a str) -> IResult<&'a str, Self> {
let (rest, o) = AstStyle::parse(input)?;
Ok((rest, Self::Styled(o)))
}
}
impl<'a> AstStyle<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (rest, (variants, negative, elements, arbitrary, important)) = tuple((
many0(ASTVariant::parse),
opt(char('-')),
opt(AstElements::parse),
opt(AstArbitrary::parse),
opt(char('!')),
))(input)?;
Ok((
rest,
Self {
important: important.is_some(),
negative: negative.is_some(),
variants,
elements: elements.unwrap_or_default().elements,
arbitrary: arbitrary.map(|s| s.arbitrary),
},
))
}
}
impl<'a> AstElements<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (rest, (first, other)) = tuple((Self::parse_head, many0(Self::parse_rest)))(input)?;
let mut out = vec![first];
out.extend(other.into_iter());
Ok((rest, Self { elements: out }))
}
#[inline]
fn parse_head(input: &'a str) -> IResult<&'a str, &'a str> {
let stop = |c: char| -> bool {
matches!(c, ' ' | '\n' | '\r' | '-' | '[' | ']' | '(' | ')')
};
take_till1(stop)(input)
}
#[inline]
fn parse_rest(input: &'a str) -> IResult<&'a str, &'a str> {
let (rest, (_, out)) = tuple((char('-'), Self::parse_head))(input)?;
Ok((rest, out))
}
}
impl<'a> ASTVariant<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (rest, (mut v, s)) = tuple((Self::parse_one, alt((tag("::"), tag(":")))))(input)?;
if s == "::" {
v.pseudo = true
}
else {
v.pseudo = Self::check_pseudo(&v.names.iter().map(<_>::as_ref).collect::<Vec<_>>());
}
Ok((rest, v))
}
#[inline]
fn parse_one(input: &'a str) -> IResult<&'a str, Self> {
let not = opt(tuple((tag("not"), tag("-"))));
let vs = separated_list0(tag("-"), alphanumeric1);
let (rest, (not, names)) = tuple((not, vs))(input)?;
Ok((rest, Self { not: not.is_some(), pseudo: false, names }))
}
#[rustfmt::skip] #[inline]
fn check_pseudo(names: &[&str]) -> bool {
matches!(names
, ["after"]
| ["before"]
| ["backdrop"]
| ["marker"]
| ["placeholder"]
| ["selection"]
| ["first", "line"]
| ["first", "litter"]
| ["first", "selector", "button"]
| ["target", "text"]
)
}
}
impl<'a> AstArbitrary<'a> {
#[inline]
pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
let pair = delimited(char('['), take_till1(|c| c == ']'), char(']'));
let (rest, (_, arbitrary)) = tuple((char('-'), pair))(input)?;
Ok((rest, Self { arbitrary }))
}
}
impl AstReference {
#[inline]
pub fn parse(input: &str) -> IResult<&str, Self> {
let (rest, _) = char('&')(input)?;
Ok((rest, Self {}))
}
}
fn delimited_paired(opening: char, closing: char) -> impl Fn(&str) -> IResult<&str, &str> {
move |input: &str| {
delimited(char(opening), take_until_unbalanced(opening, closing), char(closing))(input)
}
}
fn take_until_unbalanced(
opening_bracket: char,
closing_bracket: char,
) -> impl Fn(&str) -> IResult<&str, &str> {
move |i: &str| {
let mut index = 0;
let mut bracket_counter = 0;
while let Some(n) = &i[index..].find(&[opening_bracket, closing_bracket, '\\'][..]) {
index += n;
let mut it = i[index..].chars();
match it.next().unwrap_or_default() {
c if c == '\\' => {
index += '\\'.len_utf8();
if let Some(c) = it.next() {
index += c.len_utf8();
}
}
c if c == opening_bracket => {
bracket_counter += 1;
index += opening_bracket.len_utf8();
}
c if c == closing_bracket => {
bracket_counter -= 1;
index += closing_bracket.len_utf8();
}
_ => unreachable!(),
};
if bracket_counter == -1 {
index -= closing_bracket.len_utf8();
return Ok((&i[index..], &i[0..index]));
};
}
if bracket_counter == 0 {
Ok(("", i))
}
else {
Err(Err::Error(Error::from_error_kind(i, ErrorKind::TakeUntil)))
}
}
}