Skip to main content

oxc_css_parser/parser/
selector.rs

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