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