Skip to main content

oxc_css_parser/parser/
selector.rs

1use super::Parser;
2use crate::{
3    Parse, Syntax, arena_vec,
4    ast::*,
5    bump, eat,
6    error::{Error, ErrorKind, PResult},
7    expect, expect_without_ws_or_comments, peek,
8    pos::{Span, Spanned},
9    tokenizer::{Token, TokenWithSpan, token},
10    util,
11};
12
13// https://www.w3.org/TR/css-syntax-3/#the-anb-type
14impl<'a> Parse<'a> for AnPlusB {
15    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
16        match peek!(input) {
17            TokenWithSpan { token: Token::Dimension(..), .. } => {
18                let (token::Dimension { value, unit }, span) = expect!(input, Dimension);
19                let value_span = Span { start: span.start, end: span.start + value.raw.len() };
20                let unit_name = unit.name();
21                if unit_name.eq_ignore_ascii_case("n") {
22                    match &peek!(input).token {
23                        // syntax: <n-dimension> ['+' | '-'] <signless-integer>
24                        // examples: '1n + 1', '1n - 1', '1n+ 1'
25                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
26                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
27                            bump!(input);
28                            let (number, number_span) = expect_unsigned_int(input)?;
29                            let span = Span { start: span.start, end: number_span.end };
30                            Ok(AnPlusB {
31                                a: value
32                                    .try_into()
33                                    .map_err(|kind| Error { kind, span: value_span })?,
34                                b: sign
35                                    * i32::try_from(number)
36                                        .map_err(|kind| Error { kind, span: number_span })?,
37                                span,
38                            })
39                        }
40
41                        // syntax: <n-dimension> <signed-integer>
42                        // examples: '1n +1', '1n -1'
43                        Token::Number(..) => {
44                            let (number, number_span) = expect!(input, Number);
45                            let span = Span { start: span.start, end: number_span.end };
46                            Ok(AnPlusB {
47                                a: value
48                                    .try_into()
49                                    .map_err(|kind| Error { kind, span: value_span })?,
50                                b: number
51                                    .try_into()
52                                    .map_err(|kind| Error { kind, span: number_span })?,
53                                span,
54                            })
55                        }
56
57                        // syntax: <n-dimension>
58                        // examples: '1n'
59                        _ => Ok(AnPlusB {
60                            a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
61                            b: 0,
62                            span,
63                        }),
64                    }
65                } else if unit_name.eq_ignore_ascii_case("n-") {
66                    // syntax: <ndash-dimension> <signless-integer>
67                    // examples: '1n- 1'
68                    let (number, number_span) = expect_unsigned_int(input)?;
69                    let span = Span { start: span.start, end: number_span.end };
70                    Ok(AnPlusB {
71                        a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
72                        b: -i32::try_from(number)
73                            .map_err(|kind| Error { kind, span: number_span })?,
74                        span,
75                    })
76                } else if let Some(digits) = unit_name.strip_prefix("n-") {
77                    // syntax: <ndashdigit-dimension>
78                    // examples: '1n-1'
79                    if digits.chars().any(|c| !c.is_ascii_digit()) {
80                        return Err(Error {
81                            kind: ErrorKind::ExpectUnsignedInteger,
82                            span: Span { start: span.start + value.raw.len() + 2, end: span.end },
83                        });
84                    }
85                    let b = digits.parse::<i32>().map_err(|_| Error {
86                        kind: ErrorKind::ExpectInteger,
87                        span: Span { start: span.start + value.raw.len() + 2, end: span.end },
88                    })?;
89                    Ok(AnPlusB {
90                        a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
91                        b: -b,
92                        span,
93                    })
94                } else {
95                    Err(Error { kind: ErrorKind::InvalidAnPlusB, span })
96                }
97            }
98
99            TokenWithSpan { token: Token::Plus(..), .. } => {
100                let plus_span = bump!(input).span;
101                let (ident, ident_span) = expect_without_ws_or_comments!(input, Ident);
102                let ident_name = ident.name();
103                if ident_name.eq_ignore_ascii_case("n") {
104                    match &peek!(input).token {
105                        // syntax: +n ['+' | '-'] <signless-integer>
106                        // examples: '+n + 1', '+n - 1', '+n+ 1'
107                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
108                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
109                            bump!(input);
110                            let (number, number_span) = expect_unsigned_int(input)?;
111                            let span = Span { start: plus_span.start, end: number_span.end };
112                            Ok(AnPlusB {
113                                a: 1,
114                                b: sign
115                                    * i32::try_from(number)
116                                        .map_err(|kind| Error { kind, span: number_span })?,
117                                span,
118                            })
119                        }
120
121                        // syntax: +n <signed-integer>
122                        // examples: '+n +1', '+n -1'
123                        Token::Number(..) => {
124                            let (number, number_span) = expect!(input, Number);
125                            let span = Span { start: plus_span.start, end: number_span.end };
126                            Ok(AnPlusB {
127                                a: 1,
128                                b: number
129                                    .try_into()
130                                    .map_err(|kind| Error { kind, span: number_span })?,
131                                span,
132                            })
133                        }
134
135                        // syntax: +n
136                        _ => Ok(AnPlusB {
137                            a: 1,
138                            b: 0,
139                            span: Span { start: plus_span.start, end: ident_span.end },
140                        }),
141                    }
142                } else if ident_name.eq_ignore_ascii_case("n-") {
143                    // syntax: +n- <signless-integer>
144                    // examples: '+n- 1'
145                    let (number, number_span) = expect_unsigned_int(input)?;
146                    let span = Span { start: plus_span.start, end: number_span.end };
147                    Ok(AnPlusB {
148                        a: 1,
149                        b: -i32::try_from(number)
150                            .map_err(|kind| Error { kind, span: number_span })?,
151                        span,
152                    })
153                } else if let Some(digits) = ident_name.strip_prefix("n-") {
154                    // syntax: +<ndashdigit-ident>
155                    // examples: '+n-1'
156                    if digits.chars().any(|c| !c.is_ascii_digit()) {
157                        return Err(Error {
158                            kind: ErrorKind::ExpectUnsignedInteger,
159                            span: Span { start: ident_span.start + 2, end: ident_span.end },
160                        });
161                    }
162                    let b = digits.parse::<i32>().map_err(|_| Error {
163                        kind: ErrorKind::ExpectInteger,
164                        span: Span { start: ident_span.start + 2, end: ident_span.end },
165                    })?;
166                    Ok(AnPlusB {
167                        a: 1,
168                        b: -b,
169                        span: Span { start: plus_span.start, end: ident_span.end },
170                    })
171                } else {
172                    Err(Error {
173                        kind: ErrorKind::InvalidAnPlusB,
174                        span: Span { start: plus_span.start, end: ident_span.end },
175                    })
176                }
177            }
178
179            TokenWithSpan { token: Token::Ident(..), .. } => {
180                let (ident, ident_span) = expect!(input, Ident);
181                let ident_name = ident.name();
182                if ident_name.eq_ignore_ascii_case("n") {
183                    match &peek!(input).token {
184                        // syntax: n ['+' | '-'] <signless-integer>
185                        // examples: 'n + 1', 'n - 1', 'n+ 1'
186                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
187                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
188                            bump!(input);
189                            let (number, number_span) = expect_unsigned_int(input)?;
190                            let span = Span { start: ident_span.start, end: number_span.end };
191                            Ok(AnPlusB {
192                                a: 1,
193                                b: sign
194                                    * i32::try_from(number)
195                                        .map_err(|kind| Error { kind, span: number_span })?,
196                                span,
197                            })
198                        }
199
200                        // syntax: n <signed-integer>
201                        // examples: 'n +1', 'n -1'
202                        Token::Number(..) => {
203                            let (number, number_span) = expect!(input, Number);
204                            let span = Span { start: ident_span.start, end: number_span.end };
205                            Ok(AnPlusB {
206                                a: 1,
207                                b: number
208                                    .try_into()
209                                    .map_err(|kind| Error { kind, span: number_span })?,
210                                span,
211                            })
212                        }
213
214                        // syntax: n
215                        _ => Ok(AnPlusB { a: 1, b: 0, span: ident_span }),
216                    }
217                } else if ident_name.eq_ignore_ascii_case("n-") {
218                    // syntax: n- <signless-integer>
219                    // examples: 'n- 1'
220                    let (number, number_span) = expect_unsigned_int(input)?;
221                    let span = Span { start: ident_span.start, end: number_span.end };
222                    Ok(AnPlusB {
223                        a: 1,
224                        b: -i32::try_from(number)
225                            .map_err(|kind| Error { kind, span: number_span })?,
226                        span,
227                    })
228                } else if let Some(digits) = ident_name.strip_prefix("n-") {
229                    // syntax: <ndashdigit-ident>
230                    // examples: 'n-1'
231                    if digits.chars().any(|c| !c.is_ascii_digit()) {
232                        return Err(Error {
233                            kind: ErrorKind::ExpectUnsignedInteger,
234                            span: Span { start: ident_span.start + 2, end: ident_span.end },
235                        });
236                    }
237                    let b = digits.parse::<i32>().map_err(|_| Error {
238                        kind: ErrorKind::ExpectInteger,
239                        span: Span { start: ident_span.start + 2, end: ident_span.end },
240                    })?;
241                    Ok(AnPlusB { a: 1, b: -b, span: ident_span })
242                } else if ident_name.eq_ignore_ascii_case("-n") {
243                    match &peek!(input).token {
244                        // syntax: -n ['+' | '-'] <signless-integer>
245                        // examples: '-n + 1', '-n - 1', '-n+ 1'
246                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
247                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
248                            bump!(input);
249                            let (number, number_span) = expect_unsigned_int(input)?;
250                            let span = Span { start: ident_span.start, end: number_span.end };
251                            Ok(AnPlusB {
252                                a: -1,
253                                b: sign
254                                    * i32::try_from(number)
255                                        .map_err(|kind| Error { kind, span: number_span })?,
256                                span,
257                            })
258                        }
259
260                        // syntax: -n <signed-integer>
261                        // examples: '-n +1', '-n -1'
262                        Token::Number(..) => {
263                            let (number, number_span) = expect!(input, Number);
264                            let span = Span { start: ident_span.start, end: number_span.end };
265                            Ok(AnPlusB {
266                                a: -1,
267                                b: number
268                                    .try_into()
269                                    .map_err(|kind| Error { kind, span: number_span })?,
270                                span,
271                            })
272                        }
273
274                        // syntax: -n
275                        _ => Ok(AnPlusB { a: -1, b: 0, span: ident_span }),
276                    }
277                } else if ident_name.eq_ignore_ascii_case("-n-") {
278                    // syntax: -n- <signless-integer>
279                    // examples: '-n- 1'
280                    let (number, number_span) = expect_unsigned_int(input)?;
281                    let span = Span { start: ident_span.start, end: number_span.end };
282                    Ok(AnPlusB {
283                        a: -1,
284                        b: -i32::try_from(number)
285                            .map_err(|kind| Error { kind, span: number_span })?,
286                        span,
287                    })
288                } else if let Some(digits) = ident_name.strip_prefix("-n-") {
289                    // syntax: -n-<ndashdigit-ident>
290                    // examples: '-n-1'
291                    if digits.chars().any(|c| !c.is_ascii_digit()) {
292                        return Err(Error {
293                            kind: ErrorKind::ExpectUnsignedInteger,
294                            span: Span { start: ident_span.start + 3, end: ident_span.end },
295                        });
296                    }
297                    let b = digits.parse::<i32>().map_err(|_| Error {
298                        kind: ErrorKind::ExpectInteger,
299                        span: Span { start: ident_span.start + 3, end: ident_span.end },
300                    })?;
301                    Ok(AnPlusB { a: -1, b: -b, span: ident_span })
302                } else {
303                    Err(Error { kind: ErrorKind::InvalidAnPlusB, span: ident_span })
304                }
305            }
306
307            TokenWithSpan { span, .. } => {
308                Err(Error { kind: ErrorKind::InvalidAnPlusB, span: span.clone() })
309            }
310        }
311    }
312}
313
314impl<'a> Parse<'a> for AttributeSelector<'a> {
315    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
316        let start = expect!(input, LBracket).1.start;
317
318        let name = match peek!(input) {
319            TokenWithSpan {
320                token: Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..),
321                ..
322            } => {
323                let ident = input.parse::<InterpolableIdent>()?;
324                let ident_span = ident.span();
325                if let Some((_, bar_token_span)) = eat!(input, Bar) {
326                    let name = input.parse::<InterpolableIdent>()?;
327                    let name_span = name.span();
328
329                    let start = ident_span.start;
330                    let end = name_span.end;
331                    WqName {
332                        name,
333                        prefix: Some(NsPrefix {
334                            kind: Some(NsPrefixKind::Ident(ident)),
335                            span: Span { start, end: bar_token_span.end },
336                        }),
337                        span: Span { start, end },
338                    }
339                } else {
340                    let span = ident_span.clone();
341                    WqName { name: ident, prefix: None, span }
342                }
343            }
344            TokenWithSpan { token: Token::Asterisk(..), .. } => {
345                let asterisk_span = bump!(input).span;
346                let bar_token_span = expect!(input, Bar).1;
347                let name = input.parse::<InterpolableIdent>()?;
348
349                let start = asterisk_span.start;
350                let end = name.span().end;
351                WqName {
352                    name,
353                    prefix: Some(NsPrefix {
354                        kind: Some(NsPrefixKind::Universal(NsPrefixUniversal {
355                            span: asterisk_span,
356                        })),
357                        span: Span { start, end: bar_token_span.end },
358                    }),
359                    span: Span { start, end },
360                }
361            }
362            TokenWithSpan { token: Token::Bar(..), .. } => {
363                let bar_token_span = bump!(input).span;
364                let name = input.parse::<InterpolableIdent>()?;
365
366                let start = bar_token_span.start;
367                let end = name.span().end;
368                WqName {
369                    name,
370                    prefix: Some(NsPrefix {
371                        kind: None,
372                        span: Span { start, end: bar_token_span.end },
373                    }),
374                    span: Span { start, end },
375                }
376            }
377            TokenWithSpan { span, .. } => {
378                return Err(Error { kind: ErrorKind::ExpectWqName, span: span.clone() });
379            }
380        };
381
382        let matcher = match peek!(input) {
383            TokenWithSpan { token: Token::RBracket(..), .. } => None,
384            TokenWithSpan { token: Token::Equal(..), .. } => Some(AttributeSelectorMatcher {
385                kind: AttributeSelectorMatcherKind::Exact,
386                span: bump!(input).span,
387            }),
388            TokenWithSpan { token: Token::TildeEqual(..), .. } => Some(AttributeSelectorMatcher {
389                kind: AttributeSelectorMatcherKind::MatchWord,
390                span: bump!(input).span,
391            }),
392            TokenWithSpan { token: Token::BarEqual(..), .. } => Some(AttributeSelectorMatcher {
393                kind: AttributeSelectorMatcherKind::ExactOrPrefixThenHyphen,
394                span: bump!(input).span,
395            }),
396            TokenWithSpan { token: Token::CaretEqual(..), .. } => Some(AttributeSelectorMatcher {
397                kind: AttributeSelectorMatcherKind::Prefix,
398                span: bump!(input).span,
399            }),
400            TokenWithSpan { token: Token::DollarEqual(..), .. } => Some(AttributeSelectorMatcher {
401                kind: AttributeSelectorMatcherKind::Suffix,
402                span: bump!(input).span,
403            }),
404            TokenWithSpan { token: Token::AsteriskEqual(..), .. } => {
405                Some(AttributeSelectorMatcher {
406                    kind: AttributeSelectorMatcherKind::Substring,
407                    span: bump!(input).span,
408                })
409            }
410            TokenWithSpan { span, .. } => {
411                return Err(Error {
412                    kind: ErrorKind::ExpectAttributeSelectorMatcher,
413                    span: span.clone(),
414                });
415            }
416        };
417
418        let value = if matcher.is_some() {
419            match peek!(input) {
420                TokenWithSpan {
421                    token:
422                        Token::Ident(..)
423                        | Token::HashLBrace(..)
424                        | Token::AtLBraceVar(..)
425                        | Token::Placeholder(..),
426                    ..
427                } => Some(AttributeSelectorValue::Ident(input.parse()?)),
428                TokenWithSpan { token: Token::Str(..) | Token::StrTemplate(..), .. } => {
429                    Some(AttributeSelectorValue::Str(input.parse()?))
430                }
431                // Unquoted numeric values such as `[size=1]` or `[size=1px]` are
432                // technically non-conforming (Selectors wants an ident or string), but
433                // browsers accept them and they appear in real CSS (incl. UA stylesheets).
434                TokenWithSpan { token: Token::Number(..), .. } => {
435                    Some(AttributeSelectorValue::Number(input.parse()?))
436                }
437                TokenWithSpan { token: Token::Dimension(..), .. } => {
438                    Some(AttributeSelectorValue::Dimension(input.parse()?))
439                }
440                TokenWithSpan { token: Token::Percentage(..), .. }
441                    if input.syntax == Syntax::Less =>
442                {
443                    Some(AttributeSelectorValue::Percentage(input.parse()?))
444                }
445                TokenWithSpan { token: Token::Tilde(..), .. } if input.syntax == Syntax::Less => {
446                    Some(AttributeSelectorValue::LessEscapedStr(input.parse()?))
447                }
448                TokenWithSpan { token: Token::RBracket(..), span } => {
449                    input.recoverable_errors.push(Error {
450                        kind: ErrorKind::ExpectAttributeSelectorValue,
451                        span: span.clone(),
452                    });
453                    None
454                }
455                token_with_span => {
456                    return Err(Error {
457                        kind: ErrorKind::ExpectAttributeSelectorValue,
458                        span: token_with_span.span.clone(),
459                    });
460                }
461            }
462        } else {
463            None
464        };
465
466        let modifier = if value.is_some() {
467            match &peek!(input).token {
468                Token::Ident(..) | Token::HashLBrace(..) => {
469                    let ident = input.parse::<InterpolableIdent>()?;
470                    let span = ident.span().clone();
471                    Some(AttributeSelectorModifier { ident, span })
472                }
473                _ => None,
474            }
475        } else {
476            None
477        };
478
479        let end = expect!(input, RBracket).1.end;
480        Ok(AttributeSelector { name, matcher, value, modifier, span: Span { start, end } })
481    }
482}
483
484impl<'a> Parse<'a> for ClassSelector<'a> {
485    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
486        let (_, dot_span) = expect!(input, Dot);
487        let start = dot_span.start;
488        let end;
489        // Detect an adjacent placeholder without `peek!`: `peek!` skips
490        // whitespace and caches a token, which would both break the no-ws rule
491        // (the name must immediately follow the dot) and trip the empty-cache
492        // assertion in the `expect_without_ws_or_comments!` fallback. `scan_placeholder`
493        // returns `None` (leaving the tokenizer untouched) unless a placeholder
494        // begins exactly here, so the fallback paths run with an empty cache.
495        let placeholder = if input.options.template_placeholder.is_some() {
496            input.tokenizer.scan_placeholder()
497        } else {
498            None
499        };
500        let name = if let Some(TokenWithSpan { token: Token::Placeholder(placeholder), span }) =
501            placeholder
502        {
503            end = span.end;
504            InterpolableIdent::Placeholder((placeholder, span).into())
505        } else if input.syntax == Syntax::Css {
506            let (ident, ident_span) = expect_without_ws_or_comments!(input, Ident);
507            end = ident_span.end;
508            InterpolableIdent::Literal(input.ident(ident, ident_span))
509        } else {
510            let ident = input.parse::<InterpolableIdent>()?;
511            let ident_span = ident.span();
512            util::assert_no_ws_or_comment(&dot_span, ident_span)?;
513            end = ident_span.end;
514            ident
515        };
516
517        Ok(ClassSelector { name, span: Span { start, end } })
518    }
519}
520
521impl<'a> Parse<'a> for ComplexSelector<'a> {
522    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
523        let mut children = input.vec_with_capacity(3);
524
525        let (span, first, mut is_previous_combinator) = if let Token::GreaterThan(..)
526        | Token::Plus(..)
527        | Token::Tilde(..)
528        | Token::BarBar(..) = peek!(input).token
529        {
530            let end = input.tokenizer.current_offset();
531            if let Some(combinator) = input.parse_combinator(end)? {
532                (combinator.span.clone(), ComplexSelectorChild::Combinator(combinator), true)
533            } else {
534                return Err(Error {
535                    kind: ErrorKind::ExpectSimpleSelector,
536                    span: bump!(input).span,
537                });
538            }
539        } else {
540            let compound_selector = input.parse::<CompoundSelector>()?;
541            (
542                compound_selector.span.clone(),
543                ComplexSelectorChild::CompoundSelector(compound_selector),
544                false,
545            )
546        };
547        let Span { start, mut end } = span;
548
549        children.push(first);
550        let is_less = input.syntax == Syntax::Less;
551        while !matches!(
552            peek!(input).token,
553            Token::LBrace(..) | Token::Indent(..) | Token::Linebreak(..)
554        ) {
555            if is_previous_combinator {
556                let compound_selector = input.parse::<CompoundSelector>()?;
557                end = compound_selector.span.end;
558                children.push(ComplexSelectorChild::CompoundSelector(compound_selector));
559            } else if let Some(combinator) = input.parse_combinator(end)? {
560                if is_less && combinator.kind == CombinatorKind::Descendant {
561                    match &peek!(input).token {
562                        Token::Ident(ident) if ident.raw == "when" => break,
563                        _ => {}
564                    }
565                }
566                children.push(ComplexSelectorChild::Combinator(combinator));
567            } else {
568                break;
569            }
570            is_previous_combinator = !is_previous_combinator;
571        }
572
573        Ok(ComplexSelector { children, span: Span { start, end } })
574    }
575}
576
577impl<'a> Parse<'a> for CompoundSelector<'a> {
578    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
579        let first = input.parse::<SimpleSelector>()?;
580        let first_span = first.span();
581        let start = first_span.start;
582        let mut end = first_span.end;
583
584        let mut children = input.vec_with_capacity(2);
585        children.push(first);
586        loop {
587            use token::*;
588            match peek!(input) {
589                TokenWithSpan {
590                    token:
591                        Token::Dot(..)
592                        | Token::Hash(..)
593                        | Token::NumberSign(..)
594                        | Token::LBracket(..)
595                        | Token::Colon(..)
596                        | Token::ColonColon(..)
597                        | Token::Ident(..)
598                        | Token::Asterisk(..)
599                        | Token::HashLBrace(..)
600                        | Token::Bar(..)
601                        | Token::Ampersand(..)
602                        | Token::AtLBraceVar(..),
603                    span,
604                } if !util::has_ws(input.source, end, span.start) => {
605                    let child = input.parse::<SimpleSelector>()?;
606                    end = child.span().end;
607                    children.push(child);
608                }
609                TokenWithSpan { token: Token::Percent(..), span }
610                    if matches!(input.syntax, Syntax::Scss | Syntax::Sass)
611                        && !util::has_ws(input.source, end, span.start) =>
612                {
613                    let child = input.parse::<SimpleSelector>()?;
614                    end = child.span().end;
615                    children.push(child);
616                }
617                TokenWithSpan { token: Token::Placeholder(..), span }
618                    if !util::has_ws(input.source, end, span.start) =>
619                {
620                    let child = input.parse::<SimpleSelector>()?;
621                    end = child.span().end;
622                    children.push(child);
623                }
624                _ => break,
625            }
626        }
627
628        Ok(CompoundSelector { children, span: Span { start, end } })
629    }
630}
631
632impl<'a> Parse<'a> for CompoundSelectorList<'a> {
633    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
634        let first = input.parse::<CompoundSelector>()?;
635        let mut span = first.span.clone();
636
637        let mut selectors = arena_vec!(input; first);
638        let mut comma_spans = arena_vec!(input);
639        while let Some((_, comma_span)) = eat!(input, Comma) {
640            comma_spans.push(comma_span);
641            selectors.push(input.parse()?);
642        }
643
644        // SAFETY: it has at least one element.
645        span.end = unsafe {
646            let index = selectors.len() - 1;
647            selectors.get_unchecked(index).span().end
648        };
649        Ok(CompoundSelectorList { selectors, comma_spans, span })
650    }
651}
652
653impl<'a> Parse<'a> for IdSelector<'a> {
654    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
655        match bump!(input) {
656            TokenWithSpan { token: Token::Hash(token), span } => {
657                let first_span = Span { start: span.start + 1, end: span.end };
658                let raw = token.raw;
659                if raw.starts_with(|c: char| c.is_ascii_digit())
660                    || matches!(raw.as_bytes(), [b'-'] | [b'-', b'0'..=b'9', ..])
661                {
662                    input
663                        .recoverable_errors
664                        .push(Error { kind: ErrorKind::InvalidIdSelectorName, span: span.clone() });
665                }
666                let value = if token.escaped {
667                    util::handle_escape_in(raw, input.allocator())
668                } else {
669                    raw
670                };
671                let first = Ident { name: value, raw: token.raw, span: first_span };
672                let name = match peek!(input) {
673                    TokenWithSpan { token: Token::HashLBrace(..), span }
674                        if matches!(input.syntax, Syntax::Scss | Syntax::Sass)
675                            && first.span.end == span.start =>
676                    {
677                        match input.parse()? {
678                            InterpolableIdent::SassInterpolated(mut interpolation) => {
679                                interpolation.elements.insert(
680                                    0,
681                                    SassInterpolatedIdentElement::Static(
682                                        InterpolableIdentStaticPart {
683                                            value: first.name,
684                                            raw: first.raw,
685                                            span: first.span,
686                                        },
687                                    ),
688                                );
689                                InterpolableIdent::SassInterpolated(interpolation)
690                            }
691                            _ => unreachable!(),
692                        }
693                    }
694                    _ => InterpolableIdent::Literal(first),
695                };
696                let span = Span { start: span.start, end: name.span().end };
697                Ok(IdSelector { name, span })
698            }
699            TokenWithSpan { token: Token::NumberSign(..), span } => {
700                let name = input.parse::<InterpolableIdent>()?;
701                let span = Span { start: span.start, end: name.span().end };
702                Ok(IdSelector { name, span })
703            }
704            TokenWithSpan { span, .. } => Err(Error { kind: ErrorKind::ExpectIdSelector, span }),
705        }
706    }
707}
708
709impl<'a> Parse<'a> for LanguageRange<'a> {
710    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
711        match &peek!(input).token {
712            Token::Str(..) | Token::StrTemplate(..) => input.parse().map(LanguageRange::Str),
713            _ => input.parse().map(LanguageRange::Ident),
714        }
715    }
716}
717
718impl<'a> Parse<'a> for LanguageRangeList<'a> {
719    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
720        let first = input.parse::<LanguageRange>()?;
721        let mut span = first.span().clone();
722
723        let mut ranges = arena_vec!(input; first);
724        let mut comma_spans = arena_vec!(input);
725        while let Some((_, comma_span)) = eat!(input, Comma) {
726            comma_spans.push(comma_span);
727            ranges.push(input.parse()?);
728        }
729        debug_assert_eq!(comma_spans.len() + 1, ranges.len());
730
731        if let Some(end) = ranges.last() {
732            span.end = end.span().end;
733        }
734        Ok(LanguageRangeList { ranges, comma_spans, span })
735    }
736}
737
738impl<'a> Parse<'a> for NestingSelector<'a> {
739    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
740        let (_, mut span) = expect!(input, Ampersand);
741        let suffix = match input.syntax {
742            Syntax::Css => {
743                if let Some((ident, ident_span)) = input.tokenizer.scan_ident_template()? {
744                    span.end = ident_span.end;
745                    Some(InterpolableIdent::Literal(input.ident(ident, ident_span)))
746                } else {
747                    None
748                }
749            }
750            Syntax::Scss | Syntax::Sass => {
751                let start = span.end;
752                let elements = input.parse_sass_interpolated_ident_rest(&mut span.end)?;
753                if elements.is_empty() {
754                    None
755                } else {
756                    Some(InterpolableIdent::SassInterpolated(SassInterpolatedIdent {
757                        elements,
758                        span: Span { start, end: span.end },
759                    }))
760                }
761            }
762            Syntax::Less => {
763                let start = span.end;
764                let elements = input.parse_less_interpolated_ident_rest(&mut span.end)?;
765                if elements.is_empty() {
766                    None
767                } else {
768                    Some(InterpolableIdent::LessInterpolated(LessInterpolatedIdent {
769                        elements,
770                        span: Span { start, end: span.end },
771                    }))
772                }
773            }
774        };
775        Ok(NestingSelector { suffix, span })
776    }
777}
778
779// https://drafts.csswg.org/selectors-4/#the-nth-child-pseudo
780impl<'a> Parse<'a> for Nth<'a> {
781    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
782        let index = input.parse::<NthIndex>()?;
783        let mut span = index.span().clone();
784        let matcher = match &peek!(input).token {
785            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("of") => {
786                let matcher = input.parse::<NthMatcher>()?;
787                span.end = matcher.span.end;
788                Some(matcher)
789            }
790            _ => None,
791        };
792
793        Ok(Nth { index, matcher, span })
794    }
795}
796
797impl<'a> Parse<'a> for NthIndex<'a> {
798    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
799        match &peek!(input).token {
800            Token::Ident(ident) => {
801                let name = ident.name();
802                if name.eq_ignore_ascii_case("odd") {
803                    input.parse().map(NthIndex::Odd)
804                } else if name.eq_ignore_ascii_case("even") {
805                    input.parse().map(NthIndex::Even)
806                } else {
807                    input.parse().map(NthIndex::AnPlusB)
808                }
809            }
810            Token::Number(..) => {
811                let number = input.parse::<Number>()?;
812                if number.value.fract() == 0.0 {
813                    Ok(NthIndex::Integer(number))
814                } else {
815                    Err(Error { kind: ErrorKind::ExpectInteger, span: number.span })
816                }
817            }
818            _ => input.parse().map(NthIndex::AnPlusB),
819        }
820    }
821}
822
823impl<'a> Parse<'a> for NthMatcher<'a> {
824    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
825        let (ident, mut span) = expect!(input, Ident);
826        if !ident.name().eq_ignore_ascii_case("of") {
827            return Err(Error { kind: ErrorKind::ExpectNthOf, span });
828        }
829
830        let selector = if matches!(&peek!(input).token, Token::RParen(..)) {
831            None
832        } else {
833            let selector = input.parse::<SelectorList>()?;
834            span.end = selector.span.end;
835            Some(selector)
836        };
837
838        Ok(NthMatcher { selector, span })
839    }
840}
841
842impl<'a> Parse<'a> for PseudoClassSelector<'a> {
843    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
844        let (_, colon_span) = expect!(input, Colon);
845        let name = input.parse::<InterpolableIdent>()?;
846        let name_span = name.span();
847        util::assert_no_ws(input.source, &colon_span, name_span)?;
848
849        let mut end = name_span.end;
850
851        let arg = match peek!(input) {
852            TokenWithSpan { token: Token::LParen(..), span: l_paren } if l_paren.start == end => {
853                let l_paren = l_paren.clone();
854                bump!(input);
855                let kind = match &name {
856                    InterpolableIdent::Literal(Ident { name, .. })
857                        if name.eq_ignore_ascii_case("nth-child")
858                            || name.eq_ignore_ascii_case("nth-last-child") =>
859                    {
860                        if input.syntax == Syntax::Css {
861                            input.parse().map(PseudoClassSelectorArgKind::Nth)?
862                        } else if let Ok(nth) = input.try_parse(Nth::parse) {
863                            PseudoClassSelectorArgKind::Nth(nth)
864                        } else {
865                            input
866                                .parse_tokens_in_parens()
867                                .map(PseudoClassSelectorArgKind::TokenSeq)?
868                        }
869                    }
870                    InterpolableIdent::Literal(Ident { name, .. })
871                        if name.eq_ignore_ascii_case("nth-of-type")
872                            || name.eq_ignore_ascii_case("nth-last-of-type")
873                            || name.eq_ignore_ascii_case("nth-col")
874                            || name.eq_ignore_ascii_case("nth-last-col") =>
875                    'pseudo_arg: {
876                        let nth = if input.syntax == Syntax::Css {
877                            input.parse()?
878                        } else if let Ok(nth) = input.try_parse(Nth::parse) {
879                            nth
880                        } else {
881                            break 'pseudo_arg input
882                                .parse_tokens_in_parens()
883                                .map(PseudoClassSelectorArgKind::TokenSeq)?;
884                        };
885                        if let Some(NthMatcher { span, .. }) = &nth.matcher {
886                            input.recoverable_errors.push(Error {
887                                kind: ErrorKind::UnexpectedNthMatcher,
888                                span: span.clone(),
889                            });
890                        }
891                        PseudoClassSelectorArgKind::Nth(nth)
892                    }
893                    InterpolableIdent::Literal(Ident { name, .. })
894                        if name.eq_ignore_ascii_case("not")
895                            || name.eq_ignore_ascii_case("is")
896                            || name.eq_ignore_ascii_case("where")
897                            || name.eq_ignore_ascii_case("matches")
898                            || name.eq_ignore_ascii_case("global") =>
899                    {
900                        input.parse().map(PseudoClassSelectorArgKind::SelectorList)?
901                    }
902                    InterpolableIdent::Literal(Ident { name, .. })
903                        if name.eq_ignore_ascii_case("has") =>
904                    {
905                        input.parse().map(PseudoClassSelectorArgKind::RelativeSelectorList)?
906                    }
907                    InterpolableIdent::Literal(Ident { name, .. })
908                        if name.eq_ignore_ascii_case("dir") =>
909                    {
910                        input.parse().map(PseudoClassSelectorArgKind::Ident)?
911                    }
912                    InterpolableIdent::Literal(Ident { name, .. })
913                        if name.eq_ignore_ascii_case("lang") =>
914                    {
915                        input.parse().map(PseudoClassSelectorArgKind::LanguageRangeList)?
916                    }
917                    InterpolableIdent::Literal(Ident { name, .. })
918                        if name.eq_ignore_ascii_case("-moz-any")
919                            || name.eq_ignore_ascii_case("-webkit-any")
920                            || name.eq_ignore_ascii_case("current")
921                            || name.eq_ignore_ascii_case("past")
922                            || name.eq_ignore_ascii_case("future") =>
923                    {
924                        input.parse().map(PseudoClassSelectorArgKind::CompoundSelectorList)?
925                    }
926                    InterpolableIdent::Literal(Ident { name, .. })
927                        if name.eq_ignore_ascii_case("host")
928                            || name.eq_ignore_ascii_case("host-context") =>
929                    {
930                        input.parse().map(PseudoClassSelectorArgKind::CompoundSelector)?
931                    }
932                    InterpolableIdent::Literal(Ident { name, .. })
933                        if input.syntax == Syntax::Less && *name == "extend" =>
934                    {
935                        input.parse().map(PseudoClassSelectorArgKind::LessExtendList)?
936                    }
937                    _ => {
938                        input.parse_tokens_in_parens().map(PseudoClassSelectorArgKind::TokenSeq)?
939                    }
940                };
941
942                let r_paren = expect!(input, RParen).1;
943                end = r_paren.end;
944                let span = Span { start: l_paren.start, end: r_paren.end };
945                Some(PseudoClassSelectorArg { kind, l_paren, r_paren, span })
946            }
947            _ => None,
948        };
949
950        let span = Span { start: colon_span.start, end };
951        Ok(PseudoClassSelector { name, arg, span })
952    }
953}
954
955impl<'a> Parse<'a> for PseudoElementSelector<'a> {
956    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
957        let (_, colon_colon_span) = expect!(input, ColonColon);
958        let mut end;
959        let name = if input.syntax == Syntax::Css {
960            let (ident, ident_span) = expect!(input, Ident);
961            end = ident_span.end;
962            util::assert_no_ws(input.source, &colon_colon_span, &ident_span)?;
963            InterpolableIdent::Literal(input.ident(ident, ident_span))
964        } else {
965            let name = input.parse::<InterpolableIdent>()?;
966            let name_span = name.span();
967            end = name_span.end;
968            util::assert_no_ws(input.source, &colon_colon_span, name_span)?;
969            name
970        };
971
972        let arg = match peek!(input) {
973            TokenWithSpan { token: Token::LParen(..), span: l_paren } if l_paren.start == end => {
974                let l_paren = l_paren.clone();
975                bump!(input);
976                let kind = match &name {
977                    InterpolableIdent::Literal(Ident { name, .. })
978                        if name.eq_ignore_ascii_case("part") =>
979                    {
980                        input.parse().map(PseudoElementSelectorArgKind::Ident)?
981                    }
982                    InterpolableIdent::Literal(Ident { name, .. })
983                        if name.eq_ignore_ascii_case("cue")
984                            || name.eq_ignore_ascii_case("cue-region")
985                            || name.eq_ignore_ascii_case("slotted") =>
986                    {
987                        input.parse().map(PseudoElementSelectorArgKind::CompoundSelector)?
988                    }
989                    _ => input
990                        .parse_tokens_in_parens()
991                        .map(PseudoElementSelectorArgKind::TokenSeq)?,
992                };
993
994                let r_paren = expect!(input, RParen).1;
995                end = r_paren.end;
996                let span = Span { start: l_paren.start, end: r_paren.end };
997                Some(PseudoElementSelectorArg { kind, l_paren, r_paren, span })
998            }
999            _ => None,
1000        };
1001
1002        let span = Span { start: colon_colon_span.start, end };
1003        Ok(PseudoElementSelector { name, arg, span })
1004    }
1005}
1006
1007impl<'a> Parse<'a> for RelativeSelector<'a> {
1008    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1009        let pos = input.tokenizer.current_offset();
1010        let combinator = match input.parse_combinator(pos)? {
1011            Some(Combinator { kind: CombinatorKind::Descendant, .. }) => None,
1012            combinator => combinator,
1013        };
1014        let complex_selector = input.parse::<ComplexSelector>()?;
1015        let mut span = complex_selector.span.clone();
1016        if let Some(combinator) = &combinator {
1017            span.start = combinator.span.start;
1018        }
1019        Ok(RelativeSelector { combinator, complex_selector, span })
1020    }
1021}
1022
1023impl<'a> Parse<'a> for RelativeSelectorList<'a> {
1024    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1025        let first = input.parse::<RelativeSelector>()?;
1026        let mut span = first.span.clone();
1027
1028        let mut selectors = arena_vec!(input; first);
1029        let mut comma_spans = arena_vec!(input);
1030        while let Some((_, comma_span)) = eat!(input, Comma) {
1031            comma_spans.push(comma_span);
1032            selectors.push(input.parse()?);
1033        }
1034
1035        // SAFETY: it has at least one element.
1036        span.end = unsafe {
1037            let index = selectors.len() - 1;
1038            selectors.get_unchecked(index).span().end
1039        };
1040        Ok(RelativeSelectorList { selectors, comma_spans, span })
1041    }
1042}
1043
1044impl<'a> Parse<'a> for SelectorList<'a> {
1045    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1046        let first = input.parse::<ComplexSelector>()?;
1047        let mut span = first.span.clone();
1048
1049        let mut selectors = input.vec_with_capacity(2);
1050        selectors.push(first);
1051        let mut comma_spans = arena_vec!(input);
1052
1053        let is_css = input.syntax == Syntax::Css;
1054        while let Some((_, comma_span)) = eat!(input, Comma) {
1055            span.end = comma_span.end;
1056            comma_spans.push(comma_span);
1057            if !is_css
1058                && matches!(
1059                    peek!(input).token,
1060                    Token::LBrace(..) | Token::Indent(..) | Token::Linebreak(..)
1061                )
1062            {
1063                break;
1064            }
1065
1066            let selector = input.parse::<ComplexSelector>()?;
1067            span.end = selector.span.end;
1068            selectors.push(selector);
1069        }
1070
1071        debug_assert!(if is_css {
1072            selectors.len() - comma_spans.len() == 1
1073        } else {
1074            selectors.len() - comma_spans.len() <= 1
1075        });
1076
1077        Ok(SelectorList { selectors, comma_spans, span })
1078    }
1079}
1080
1081// https://www.w3.org/TR/selectors-4/#ref-for-typedef-simple-selector
1082impl<'a> Parse<'a> for SimpleSelector<'a> {
1083    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1084        match peek!(input) {
1085            TokenWithSpan { token: Token::Dot(..), .. } => input.parse().map(SimpleSelector::Class),
1086            TokenWithSpan { token: Token::Hash(..) | Token::NumberSign(..), .. } => {
1087                input.parse().map(SimpleSelector::Id)
1088            }
1089            TokenWithSpan { token: Token::LBracket(..), .. } => {
1090                input.parse().map(SimpleSelector::Attribute)
1091            }
1092            TokenWithSpan { token: Token::Colon(..), .. } => {
1093                input.parse().map(SimpleSelector::PseudoClass)
1094            }
1095            TokenWithSpan { token: Token::ColonColon(..), .. } => {
1096                input.parse().map(SimpleSelector::PseudoElement)
1097            }
1098            TokenWithSpan {
1099                token:
1100                    Token::Ident(..)
1101                    | Token::Asterisk(..)
1102                    | Token::HashLBrace(..)
1103                    | Token::Bar(..)
1104                    | Token::AtLBraceVar(..),
1105                ..
1106            } => input.parse().map(SimpleSelector::Type),
1107            TokenWithSpan { token: Token::Ampersand(..), .. } => {
1108                input.parse().map(SimpleSelector::Nesting)
1109            }
1110            TokenWithSpan { token: Token::Percent(..), .. }
1111                if matches!(input.syntax, Syntax::Scss | Syntax::Sass) =>
1112            {
1113                input.parse().map(SimpleSelector::SassPlaceholder)
1114            }
1115            TokenWithSpan { token: Token::Placeholder(..), .. } => {
1116                let name = input.parse::<InterpolableIdent>()?;
1117                let span = name.span().clone();
1118                Ok(SimpleSelector::Type(TypeSelector::TagName(TagNameSelector {
1119                    name: WqName { name, prefix: None, span: span.clone() },
1120                    span,
1121                })))
1122            }
1123            token_with_span => Err(Error {
1124                kind: ErrorKind::ExpectSimpleSelector,
1125                span: token_with_span.span.clone(),
1126            }),
1127        }
1128    }
1129}
1130
1131impl<'a> Parse<'a> for TypeSelector<'a> {
1132    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1133        enum IdentOrAsterisk<'a> {
1134            Ident(InterpolableIdent<'a>),
1135            Asterisk(Span),
1136        }
1137
1138        let ident_or_asterisk = match &peek!(input).token {
1139            Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
1140                input.parse().map(IdentOrAsterisk::Ident).map(Some)?
1141            }
1142            Token::Asterisk(..) => Some(IdentOrAsterisk::Asterisk(bump!(input).span)),
1143            Token::Bar(..) => None,
1144            _ => unreachable!(),
1145        };
1146
1147        match peek!(input) {
1148            TokenWithSpan { token: Token::Bar(..), span }
1149                if ident_or_asterisk
1150                    .as_ref()
1151                    .map(|t| match t {
1152                        IdentOrAsterisk::Ident(ident) => {
1153                            !util::has_ws(input.source, ident.span().end, span.start)
1154                        }
1155                        IdentOrAsterisk::Asterisk(asterisk_span) => {
1156                            !util::has_ws(input.source, asterisk_span.end, span.start)
1157                        }
1158                    })
1159                    .unwrap_or(true) =>
1160            {
1161                let bar_token_span = bump!(input).span;
1162
1163                let prefix = match ident_or_asterisk {
1164                    Some(IdentOrAsterisk::Ident(ident)) => {
1165                        let mut span = ident.span().clone();
1166                        span.end = bar_token_span.end;
1167                        NsPrefix { kind: Some(NsPrefixKind::Ident(ident)), span }
1168                    }
1169                    Some(IdentOrAsterisk::Asterisk(asterisk_span)) => {
1170                        let mut span = asterisk_span.clone();
1171                        span.end = bar_token_span.end;
1172                        NsPrefix {
1173                            kind: Some(NsPrefixKind::Universal(NsPrefixUniversal {
1174                                span: asterisk_span,
1175                            })),
1176                            span,
1177                        }
1178                    }
1179                    None => NsPrefix { kind: None, span: bar_token_span },
1180                };
1181
1182                match peek!(input) {
1183                    TokenWithSpan { token: Token::Ident(..) | Token::HashLBrace(..), .. } => {
1184                        let name = input.parse::<InterpolableIdent>()?;
1185                        let name_span = name.span();
1186                        util::assert_no_ws(input.source, &prefix.span, name_span)?;
1187                        let span = Span { start: prefix.span.start, end: name_span.end };
1188                        Ok(TypeSelector::TagName(TagNameSelector {
1189                            name: WqName { name, prefix: Some(prefix), span: span.clone() },
1190                            span,
1191                        }))
1192                    }
1193                    TokenWithSpan { token: Token::Asterisk(..), .. } => {
1194                        let asterisk_span = bump!(input).span;
1195                        util::assert_no_ws(input.source, &prefix.span, &asterisk_span)?;
1196                        let span = Span { start: prefix.span.start, end: asterisk_span.end };
1197                        Ok(TypeSelector::Universal(UniversalSelector {
1198                            prefix: Some(prefix),
1199                            span,
1200                        }))
1201                    }
1202                    TokenWithSpan { span, .. } => {
1203                        Err(Error { kind: ErrorKind::ExpectTypeSelector, span: span.clone() })
1204                    }
1205                }
1206            }
1207
1208            _ => match ident_or_asterisk {
1209                Some(IdentOrAsterisk::Ident(ident)) => {
1210                    let span = ident.span().clone();
1211                    Ok(TypeSelector::TagName(TagNameSelector {
1212                        name: WqName { name: ident, prefix: None, span: span.clone() },
1213                        span,
1214                    }))
1215                }
1216                Some(IdentOrAsterisk::Asterisk(span)) => {
1217                    Ok(TypeSelector::Universal(UniversalSelector { prefix: None, span }))
1218                }
1219                None => unreachable!(),
1220            },
1221        }
1222    }
1223}
1224
1225impl<'a> Parser<'a> {
1226    fn parse_combinator(&mut self, pos: usize) -> PResult<Option<Combinator>> {
1227        match peek!(self) {
1228            TokenWithSpan {
1229                token:
1230                    Token::Ident(..)
1231                    | Token::Dot(..)
1232                    | Token::Hash(..)
1233                    | Token::Colon(..)
1234                    | Token::ColonColon(..)
1235                    | Token::LBracket(..)
1236                    | Token::Asterisk(..)
1237                    | Token::Ampersand(..)
1238                    | Token::Bar(..) // selector like `|type` (with <ns-prefix>)
1239                    | Token::AtLBraceVar(..)
1240                    | Token::NumberSign(..)
1241                    | Token::HashLBrace(..)
1242                    | Token::Placeholder(..), // `${a} ${b}` descendant
1243                span,
1244            } if pos < span.start => Ok(Some(Combinator {
1245                kind: CombinatorKind::Descendant,
1246                span: Span {
1247                    start: pos,
1248                    end: span.start,
1249                },
1250            })),
1251            TokenWithSpan {
1252                token: Token::GreaterThan(..),
1253                ..
1254            } => Ok(Some(Combinator {
1255                kind: CombinatorKind::Child,
1256                span: bump!(self).span,
1257            })),
1258            TokenWithSpan {
1259                token: Token::Plus(..),
1260                ..
1261            } => Ok(Some(Combinator {
1262                kind: CombinatorKind::NextSibling,
1263                span: bump!(self).span,
1264            })),
1265            TokenWithSpan {
1266                token: Token::Tilde(..),
1267                ..
1268            } => Ok(Some(Combinator {
1269                kind: CombinatorKind::LaterSibling,
1270                span: bump!(self).span,
1271            })),
1272            TokenWithSpan {
1273                token: Token::BarBar(..),
1274                ..
1275            } => Ok(Some(Combinator {
1276                kind: CombinatorKind::Column,
1277                span: bump!(self).span,
1278            })),
1279            _ => Ok(None),
1280        }
1281    }
1282}
1283
1284fn expect_unsigned_int<'a>(input: &mut Parser<'a>) -> PResult<(token::Number<'a>, Span)> {
1285    let (number, span) = expect!(input, Number);
1286    if number.raw.chars().any(|c| !c.is_ascii_digit()) {
1287        Err(Error { kind: ErrorKind::ExpectUnsignedInteger, span })
1288    } else {
1289        Ok((number, span))
1290    }
1291}