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