tailwind_ast/ast/
parse.rs

1use nom::error::{ErrorKind, ParseError};
2
3use super::*;
4
5impl<'a> AstGroup<'a> {
6    /// `v:a?(a(a b))[!]?`
7    #[inline]
8    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
9        let (rest, (head, children, important)) =
10            tuple((AstStyle::parse, Self::parse_pair, opt(char('!'))))(input)?;
11        Ok((rest, Self { important: important.is_some(), head, children }))
12    }
13    #[inline]
14    fn parse_pair(input: &'a str) -> IResult<&'a str, Vec<AstGroupItem>> {
15        let (rest, paired) = delimited_paired('(', ')')(input)?;
16        Ok((rest, AstGroupItem::parse_many(paired.trim())?.1))
17    }
18}
19
20impl<'a> AstGroupItem<'a> {
21    /// [`AstGroup`] or [`AstStyle`]
22    #[inline]
23    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
24        alt((Self::maybe_group, Self::maybe_style))(input)
25    }
26    #[inline]
27    fn parse_many(input: &'a str) -> IResult<&'a str, Vec<Self>> {
28        let head = AstGroupItem::parse;
29        let rest = many0(tuple((multispace1, AstGroupItem::parse)));
30        let (rest, (first, other)) = tuple((head, rest))(input)?;
31        let mut out = vec![first];
32        out.extend(other.into_iter().map(|s| s.1));
33        Ok((rest, out))
34    }
35    fn maybe_group(input: &'a str) -> IResult<&'a str, Self> {
36        let (rest, o) = AstGroup::parse(input)?;
37        Ok((rest, Self::Grouped(o)))
38    }
39    fn maybe_style(input: &'a str) -> IResult<&'a str, Self> {
40        let (rest, o) = AstStyle::parse(input)?;
41        Ok((rest, Self::Styled(o)))
42    }
43}
44
45impl<'a> AstStyle<'a> {
46    /// `v:v::-?a-a-a-[A]`
47    #[inline]
48    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
49        let (rest, (variants, negative, elements, arbitrary, important)) = tuple((
50            many0(ASTVariant::parse),
51            opt(char('-')),
52            opt(AstElements::parse),
53            opt(AstArbitrary::parse),
54            opt(char('!')),
55        ))(input)?;
56
57        Ok((
58            rest,
59            Self {
60                important: important.is_some(),
61                negative: negative.is_some(),
62                variants,
63                elements: elements.unwrap_or_default().elements,
64                arbitrary: arbitrary.map(|s| s.arbitrary),
65            },
66        ))
67    }
68}
69
70impl<'a> AstElements<'a> {
71    /// `a(-a)*`
72    #[inline]
73    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
74        let (rest, (first, other)) = tuple((Self::parse_head, many0(Self::parse_rest)))(input)?;
75        let mut out = vec![first];
76        out.extend(other.into_iter());
77        Ok((rest, Self { elements: out }))
78    }
79    #[inline]
80    fn parse_head(input: &'a str) -> IResult<&'a str, &'a str> {
81        let stop = |c: char| -> bool {
82            // space
83            matches!(c, ' ' | '\n' | '\r' | '-' | '[' | ']' | '(' | ')')
84        };
85        take_till1(stop)(input)
86    }
87    #[inline]
88    fn parse_rest(input: &'a str) -> IResult<&'a str, &'a str> {
89        let (rest, (_, out)) = tuple((char('-'), Self::parse_head))(input)?;
90        Ok((rest, out))
91    }
92}
93
94impl<'a> ASTVariant<'a> {
95    /// `(not-)?variant:pseudo::`
96    ///
97    /// ## Reference
98    /// -
99    #[inline]
100    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
101        let (rest, (mut v, s)) = tuple((Self::parse_one, alt((tag("::"), tag(":")))))(input)?;
102        if s == "::" {
103            v.pseudo = true
104        }
105        else {
106            v.pseudo = Self::check_pseudo(&v.names.iter().map(<_>::as_ref).collect::<Vec<_>>());
107        }
108        Ok((rest, v))
109    }
110    /// `(not-)?(ALPHA)(-ALPHA)*`
111    ///
112    /// eg:
113    /// - `not-focus`
114    /// - `not-last-child`
115    #[inline]
116    fn parse_one(input: &'a str) -> IResult<&'a str, Self> {
117        let not = opt(tuple((tag("not"), tag("-"))));
118        let vs = separated_list0(tag("-"), alphanumeric1);
119        let (rest, (not, names)) = tuple((not, vs))(input)?;
120        Ok((rest, Self { not: not.is_some(), pseudo: false, names }))
121    }
122    /// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements#index
123    #[rustfmt::skip] #[inline]
124    fn check_pseudo(names: &[&str]) -> bool {
125        matches!(names
126            , ["after"]
127            | ["before"]
128            | ["backdrop"]
129            | ["marker"]
130            | ["placeholder"]
131            | ["selection"]
132            | ["first", "line"]
133            | ["first", "litter"]
134            | ["first", "selector", "button"]
135            | ["target", "text"]
136        )
137    }
138}
139
140impl<'a> AstArbitrary<'a> {
141    /// `-[ANY+]`
142    #[inline]
143    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
144        let pair = delimited(char('['), take_till1(|c| c == ']'), char(']'));
145        let (rest, (_, arbitrary)) = tuple((char('-'), pair))(input)?;
146        Ok((rest, Self { arbitrary }))
147    }
148}
149
150impl AstReference {
151    /// `&`
152    #[inline]
153    pub fn parse(input: &str) -> IResult<&str, Self> {
154        let (rest, _) = char('&')(input)?;
155        Ok((rest, Self {}))
156    }
157}
158
159fn delimited_paired(opening: char, closing: char) -> impl Fn(&str) -> IResult<&str, &str> {
160    move |input: &str| {
161        delimited(char(opening), take_until_unbalanced(opening, closing), char(closing))(input)
162    }
163}
164
165/// https://stackoverflow.com/questions/70630556/parse-allowing-nested-parentheses-in-nom
166fn take_until_unbalanced(
167    opening_bracket: char,
168    closing_bracket: char,
169) -> impl Fn(&str) -> IResult<&str, &str> {
170    move |i: &str| {
171        let mut index = 0;
172        let mut bracket_counter = 0;
173        while let Some(n) = &i[index..].find(&[opening_bracket, closing_bracket, '\\'][..]) {
174            index += n;
175            let mut it = i[index..].chars();
176            match it.next().unwrap_or_default() {
177                c if c == '\\' => {
178                    // Skip the escape char `\`.
179                    index += '\\'.len_utf8();
180                    // Skip also the following char.
181                    if let Some(c) = it.next() {
182                        index += c.len_utf8();
183                    }
184                }
185                c if c == opening_bracket => {
186                    bracket_counter += 1;
187                    index += opening_bracket.len_utf8();
188                }
189                c if c == closing_bracket => {
190                    // Closing bracket.
191                    bracket_counter -= 1;
192                    index += closing_bracket.len_utf8();
193                }
194                // Can not happen.
195                _ => unreachable!(),
196            };
197            // We found the unmatched closing bracket.
198            if bracket_counter == -1 {
199                // We do not consume it.
200                index -= closing_bracket.len_utf8();
201                return Ok((&i[index..], &i[0..index]));
202            };
203        }
204
205        if bracket_counter == 0 {
206            Ok(("", i))
207        }
208        else {
209            Err(Err::Error(Error::from_error_kind(i, ErrorKind::TakeUntil)))
210        }
211    }
212}