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
15impl<'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 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 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 _ => 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 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 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 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 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 _ => 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 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 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 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 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 _ => 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 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 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 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 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 _ => 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 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 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 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 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
1015impl<'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 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
1384impl<'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(..) | Token::AtLBraceVar(..)
1593 | Token::NumberSign(..)
1594 | Token::HashLBrace(..)
1595 | Token::Placeholder(..), 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}