1use super::{state::QualifiedRuleContext, Parser};
2use crate::{
3 ast::*,
4 bump, eat,
5 error::{Error, ErrorKind, PResult},
6 expect, peek,
7 pos::{Span, Spanned},
8 tokenizer::{Token, TokenWithSpan},
9 util, Parse, Syntax,
10};
11use std::borrow::Cow;
12
13const PRECEDENCE_MULTIPLY: u8 = 2;
14const PRECEDENCE_PLUS: u8 = 1;
15
16impl<'cmt, 's: 'cmt> Parser<'cmt, 's> {
17 pub(in crate::parser) fn parse_calc_expr(&mut self) -> PResult<ComponentValue<'s>> {
18 self.parse_calc_expr_recursively(0)
19 }
20
21 fn parse_calc_expr_recursively(&mut self, precedence: u8) -> PResult<ComponentValue<'s>> {
22 let mut left = if precedence >= PRECEDENCE_MULTIPLY {
23 if eat!(self, LParen).is_some() {
24 let expr = self.parse_calc_expr()?;
25 expect!(self, RParen);
26 expr
27 } else if self.syntax == Syntax::Less {
28 if matches!(peek!(self).token, Token::Minus(..)) {
29 ComponentValue::LessNegativeValue(self.parse()?)
30 } else {
31 self.parse_component_value_atom()?
32 }
33 } else {
34 self.parse_component_value_atom()?
35 }
36 } else {
37 self.parse_calc_expr_recursively(precedence + 1)?
38 };
39
40 loop {
41 let operator = match &peek!(self).token {
42 Token::Asterisk(..) if precedence == PRECEDENCE_MULTIPLY => CalcOperator {
43 kind: CalcOperatorKind::Multiply,
44 span: bump!(self).span,
45 },
46 Token::Solidus(..) if precedence == PRECEDENCE_MULTIPLY => CalcOperator {
47 kind: CalcOperatorKind::Division,
48 span: bump!(self).span,
49 },
50 Token::Plus(..) if precedence == PRECEDENCE_PLUS => CalcOperator {
51 kind: CalcOperatorKind::Plus,
52 span: bump!(self).span,
53 },
54 Token::Minus(..) if precedence == PRECEDENCE_PLUS => CalcOperator {
55 kind: CalcOperatorKind::Minus,
56 span: bump!(self).span,
57 },
58 _ => break,
59 };
60
61 let right = self.parse_calc_expr_recursively(precedence + 1)?;
62 let span = Span {
63 start: left.span().start,
64 end: right.span().end,
65 };
66 left = ComponentValue::Calc(Calc {
67 left: Box::new(left),
68 op: operator,
69 right: Box::new(right),
70 span,
71 });
72 }
73
74 Ok(left)
75 }
76
77 pub(super) fn parse_component_value_atom(&mut self) -> PResult<ComponentValue<'s>> {
78 let token_with_span = peek!(self);
79 match &token_with_span.token {
80 Token::Ident(token) => {
81 if token.name().eq_ignore_ascii_case("url") {
82 match self.try_parse(Url::parse) {
83 Ok(url) => return Ok(ComponentValue::Url(url)),
84 Err(Error {
85 kind: ErrorKind::TryParseError,
86 ..
87 }) => {}
88 Err(error) => {
89 return if matches!(self.syntax, Syntax::Scss | Syntax::Sass) {
90 let function_name = expect!(self, Ident).into();
91 self.parse_function(InterpolableIdent::Literal(function_name))
92 .map(ComponentValue::Function)
93 .map_err(|_| error)
94 } else {
95 Err(error)
96 };
97 }
98 }
99 }
100 let ident = self.parse::<InterpolableIdent>()?;
101 let ident_end = ident.span().end;
102 match peek!(self) {
103 TokenWithSpan {
104 token: Token::LParen(..),
105 span,
106 } if span.start == ident_end => {
107 return match ident {
108 InterpolableIdent::Literal(ident)
109 if ident.name.eq_ignore_ascii_case("src") =>
110 {
111 self.parse_src_url(ident).map(ComponentValue::Url)
112 }
113 ident => self.parse_function(ident).map(ComponentValue::Function),
114 };
115 }
116 TokenWithSpan {
117 token: Token::Dot(..),
118 span,
119 } if matches!(self.syntax, Syntax::Scss | Syntax::Sass)
120 && span.start == ident_end =>
121 {
122 if let InterpolableIdent::Literal(module) = ident {
123 let name = self.parse_sass_qualified_name(module)?;
124 return if let SassQualifiedName {
125 member: SassModuleMemberName::Ident(..),
126 ..
127 } = name
128 {
129 let (_, lparen_span) = expect!(self, LParen);
130 util::assert_no_ws_or_comment(&name.span, &lparen_span)?;
131 let args = self.parse_function_args()?;
132 let (_, Span { end, .. }) = expect!(self, RParen);
133 let span = Span {
134 start: name.span.start,
135 end,
136 };
137 Ok(ComponentValue::Function(Function {
138 name: FunctionName::SassQualifiedName(Box::new(name)),
139 args,
140 span,
141 }))
142 } else {
143 Ok(ComponentValue::SassQualifiedName(name))
144 };
145 }
146 }
147 _ => {}
148 }
149 match ident {
150 InterpolableIdent::Literal(ident) if ident.raw.eq_ignore_ascii_case("u") => {
151 match peek!(self) {
152 TokenWithSpan {
153 token: Token::Plus(..),
154 span,
155 } if span.start == ident_end => self
156 .parse_unicode_range(ident)
157 .map(ComponentValue::UnicodeRange),
158 TokenWithSpan {
159 token: Token::Number(token),
160 span,
161 } if token.raw.starts_with('+') && span.start == ident_end => self
162 .parse_unicode_range(ident)
163 .map(ComponentValue::UnicodeRange),
164 TokenWithSpan {
165 token: Token::Dimension(token),
166 span,
167 } if token.value.raw.starts_with('+') && span.start == ident_end => {
168 self.parse_unicode_range(ident)
169 .map(ComponentValue::UnicodeRange)
170 }
171 _ => Ok(ComponentValue::InterpolableIdent(
172 InterpolableIdent::Literal(ident),
173 )),
174 }
175 }
176 _ => Ok(ComponentValue::InterpolableIdent(ident)),
177 }
178 }
179 Token::Solidus(..) | Token::Comma(..) => self.parse().map(ComponentValue::Delimiter),
180 Token::Number(..) => self.parse().map(ComponentValue::Number),
181 Token::Dimension(..) => self.parse().map(ComponentValue::Dimension),
182 Token::Percentage(..) => self.parse().map(ComponentValue::Percentage),
183 Token::Hash(..) => {
184 if self.syntax == Syntax::Less {
185 self.parse_maybe_hex_color_or_less_mixin_call()
186 } else {
187 self.parse().map(ComponentValue::HexColor)
188 }
189 }
190 Token::Str(..) => self
191 .parse()
192 .map(InterpolableStr::Literal)
193 .map(ComponentValue::InterpolableStr),
194 Token::LBracket(..) => self.parse().map(ComponentValue::BracketBlock),
195 Token::DollarVar(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
196 self.parse().map(ComponentValue::SassVariable)
197 }
198 Token::LParen(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
199 match self.try_parse(SassParenthesizedExpression::parse) {
200 Ok(expr) => Ok(ComponentValue::SassParenthesizedExpression(expr)),
201 Err(err) => self.parse().map(ComponentValue::SassMap).map_err(|_| err),
202 }
203 }
204 Token::HashLBrace(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
205 let ident = self.parse_sass_interpolated_ident()?;
206 match peek!(self) {
207 TokenWithSpan {
208 token: Token::LParen(..),
209 span,
210 } if span.start == ident.span().end => {
211 self.parse_function(ident).map(ComponentValue::Function)
212 }
213 _ => Ok(ComponentValue::InterpolableIdent(ident)),
214 }
215 }
216 Token::StrTemplate(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => self
217 .parse()
218 .map(InterpolableStr::SassInterpolated)
219 .map(ComponentValue::InterpolableStr),
220 Token::Ampersand(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
221 self.parse().map(ComponentValue::SassParentSelector)
222 }
223 Token::LBrace(..)
224 if self.syntax == Syntax::Scss
225 && matches!(
226 self.state.qualified_rule_ctx,
227 Some(QualifiedRuleContext::DeclarationValue)
228 ) =>
229 {
230 self.parse().map(ComponentValue::SassNestingDeclaration)
231 }
232 Token::Indent(..)
233 if self.syntax == Syntax::Sass
234 && matches!(
235 self.state.qualified_rule_ctx,
236 Some(QualifiedRuleContext::DeclarationValue)
237 ) =>
238 {
239 self.parse().map(ComponentValue::SassNestingDeclaration)
240 }
241 Token::AtKeyword(..) if self.syntax == Syntax::Less => {
242 self.parse_less_maybe_variable_or_with_lookups()
243 }
244 Token::Dot(..) if self.syntax == Syntax::Less => {
245 self.parse_less_maybe_mixin_call_or_with_lookups()
246 }
247 Token::StrTemplate(..) if self.syntax == Syntax::Less => self
248 .parse()
249 .map(InterpolableStr::LessInterpolated)
250 .map(ComponentValue::InterpolableStr),
251 Token::At(..) if self.syntax == Syntax::Less => {
252 self.parse().map(ComponentValue::LessVariableVariable)
253 }
254 Token::DollarVar(..) if self.syntax == Syntax::Less => {
255 self.parse().map(ComponentValue::LessPropertyVariable)
256 }
257 Token::Tilde(..) if self.syntax == Syntax::Less => {
258 if let Ok(list_function_call) = self.try_parse(Function::parse) {
259 Ok(ComponentValue::Function(list_function_call))
260 } else if let Ok(less_escaped_str) = self.try_parse(LessEscapedStr::parse) {
261 Ok(ComponentValue::LessEscapedStr(less_escaped_str))
262 } else {
263 self.parse().map(ComponentValue::LessJavaScriptSnippet)
264 }
265 }
266 Token::Percent(..) if self.syntax == Syntax::Less => self
267 .try_parse(Function::parse)
268 .map(ComponentValue::Function)
269 .or_else(|_| self.parse().map(ComponentValue::LessPercentKeyword)),
270 Token::BacktickCode(..) if self.syntax == Syntax::Less => {
271 self.parse().map(ComponentValue::LessJavaScriptSnippet)
272 }
273 _ => Err(Error {
274 kind: ErrorKind::ExpectComponentValue,
275 span: token_with_span.span.clone(),
276 }),
277 }
278 }
279
280 pub(super) fn parse_dashed_ident(&mut self) -> PResult<InterpolableIdent<'s>> {
281 let ident = self.parse()?;
282 match &ident {
283 InterpolableIdent::Literal(ident) if !ident.name.starts_with("--") => {
284 self.recoverable_errors.push(Error {
285 kind: ErrorKind::ExpectDashedIdent,
286 span: ident.span.clone(),
287 });
288 }
289 _ => {}
290 }
291 Ok(ident)
292 }
293
294 pub(super) fn parse_function(&mut self, name: InterpolableIdent<'s>) -> PResult<Function<'s>> {
295 expect!(self, LParen);
296 let args = if let Token::RParen(..) = &peek!(self).token {
297 vec![]
298 } else {
299 match &name {
300 InterpolableIdent::Literal(ident)
301 if ident.name.eq_ignore_ascii_case("calc")
302 || ident.name.eq_ignore_ascii_case("-webkit-calc")
303 || ident.name.eq_ignore_ascii_case("-moz-calc")
304 || ident.name.eq_ignore_ascii_case("min")
305 || ident.name.eq_ignore_ascii_case("max")
306 || ident.name.eq_ignore_ascii_case("clamp")
307 || ident.name.eq_ignore_ascii_case("sin")
308 || ident.name.eq_ignore_ascii_case("cos")
309 || ident.name.eq_ignore_ascii_case("tan")
310 || ident.name.eq_ignore_ascii_case("asin")
311 || ident.name.eq_ignore_ascii_case("acos")
312 || ident.name.eq_ignore_ascii_case("atan")
313 || ident.name.eq_ignore_ascii_case("sqrt")
314 || ident.name.eq_ignore_ascii_case("exp")
315 || ident.name.eq_ignore_ascii_case("abs")
316 || ident.name.eq_ignore_ascii_case("sign")
317 || ident.name.eq_ignore_ascii_case("hypot")
318 || ident.name.eq_ignore_ascii_case("round")
319 || ident.name.eq_ignore_ascii_case("mod")
320 || ident.name.eq_ignore_ascii_case("rem")
321 || ident.name.eq_ignore_ascii_case("atan2")
322 || ident.name.eq_ignore_ascii_case("pow")
323 || ident.name.eq_ignore_ascii_case("log") =>
324 {
325 let mut values = Vec::with_capacity(1);
326 loop {
327 match peek!(self) {
328 TokenWithSpan {
329 token: Token::RParen(..),
330 ..
331 } => break,
332 TokenWithSpan {
333 token: Token::Comma(..),
334 ..
335 } => {
336 values.push(ComponentValue::Delimiter(self.parse()?));
337 }
338 TokenWithSpan {
339 token: Token::DotDotDot(..),
340 ..
341 } if matches!(self.syntax, Syntax::Scss | Syntax::Sass)
342 && values.len() == 1 =>
343 {
344 let TokenWithSpan {
345 span: Span { end, .. },
346 ..
347 } = bump!(self);
348 let value = values.remove(0);
349 let span = Span {
350 start: value.span().start,
351 end,
352 };
353 values.push(ComponentValue::SassArbitraryArgument(
354 SassArbitraryArgument {
355 value: Box::new(value),
356 span,
357 },
358 ));
359 break;
360 }
361 _ => values.push(self.parse_calc_expr()?),
362 }
363 }
364 values
365 }
366 InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("element") => {
367 vec![self.parse().map(ComponentValue::IdSelector)?]
368 }
369 InterpolableIdent::Literal(Ident {
370 raw: "boolean" | "if",
371 ..
372 }) if self.syntax == Syntax::Less => {
373 let condition =
374 ComponentValue::LessCondition(Box::new(self.parse_less_condition(false)?));
375 let mut args = self.parse_function_args()?;
376 args.insert(0, condition);
377 args
378 }
379 _ => self.parse_function_args()?,
380 }
381 };
382 let end = expect!(self, RParen).1.end;
383 let span = Span {
384 start: name.span().start,
385 end,
386 };
387 Ok(Function {
388 name: FunctionName::Ident(name),
389 args,
390 span,
391 })
392 }
393
394 pub(super) fn parse_function_args(&mut self) -> PResult<Vec<ComponentValue<'s>>> {
395 let mut values = Vec::with_capacity(4);
396 loop {
397 match &peek!(self).token {
398 Token::RParen(..) | Token::Eof(..) => break,
399 Token::Semicolon(..) => {
400 values.push(self.parse().map(ComponentValue::Delimiter)?);
401 }
402 Token::Exclamation(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
403 values.push(self.parse().map(ComponentValue::ImportantAnnotation)?);
405 }
406 Token::LBrace(..) if self.syntax == Syntax::Less => {
407 values.push(self.parse().map(ComponentValue::LessDetachedRuleset)?);
408 }
409 _ => {
410 let value = if let Ok(value) = self.try_parse(ComponentValue::parse) {
411 value
412 } else {
413 values.push(ComponentValue::TokenWithSpan(bump!(self)));
414 continue;
415 };
416 if matches!(self.syntax, Syntax::Scss | Syntax::Sass) {
417 if let Some((_, mut span)) = eat!(self, DotDotDot) {
418 span.start = value.span().start;
419 values.push(ComponentValue::SassArbitraryArgument(
420 SassArbitraryArgument {
421 value: Box::new(value),
422 span,
423 },
424 ));
425 } else if let ComponentValue::SassVariable(sass_var) = value {
426 if let Some((_, colon_span)) = eat!(self, Colon) {
427 let value = self.parse::<ComponentValue>()?;
428 let span = Span {
429 start: sass_var.span.start,
430 end: value.span().end,
431 };
432 values.push(ComponentValue::SassKeywordArgument(
433 SassKeywordArgument {
434 name: sass_var,
435 colon_span,
436 value: Box::new(value),
437 span,
438 },
439 ));
440 } else {
441 values.push(ComponentValue::SassVariable(sass_var));
442 }
443 } else {
444 values.push(value);
445 }
446 } else {
447 values.push(value);
448 }
449 }
450 }
451 }
452 Ok(values)
453 }
454
455 pub(super) fn parse_ratio(&mut self, numerator: Number<'s>) -> PResult<Ratio<'s>> {
456 let (_, solidus_span) = expect!(self, Solidus);
457 let denominator = self.parse::<Number>()?;
458 if denominator.value <= 0.0 {
459 self.recoverable_errors.push(Error {
460 kind: ErrorKind::InvalidRatioDenominator,
461 span: denominator.span.clone(),
462 });
463 }
464
465 let span = Span {
466 start: numerator.span.start,
467 end: denominator.span.end,
468 };
469 Ok(Ratio {
470 numerator,
471 solidus_span,
472 denominator,
473 span,
474 })
475 }
476
477 fn parse_src_url(&mut self, name: Ident<'s>) -> PResult<Url<'s>> {
478 expect!(self, LParen);
480 let value = match &peek!(self).token {
481 Token::Str(..) | Token::StrTemplate(..) => {
482 Some(UrlValue::Str(self.parse::<InterpolableStr>()?))
483 }
484 _ => None,
485 };
486 let modifiers = match &peek!(self).token {
487 Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
488 let mut modifiers = Vec::with_capacity(1);
489 loop {
490 modifiers.push(self.parse()?);
491 if let Token::RParen(..) = &peek!(self).token {
492 break;
493 }
494 }
495 modifiers
496 }
497 _ => vec![],
498 };
499 let end = expect!(self, RParen).1.end;
500 let span = Span {
501 start: name.span.start,
502 end,
503 };
504 Ok(Url {
505 name,
506 value,
507 modifiers,
508 span,
509 })
510 }
511
512 fn parse_unicode_range(&mut self, prefix_ident: Ident<'s>) -> PResult<UnicodeRange<'s>> {
513 let prefix = prefix_ident.raw.chars().next().unwrap();
514 let (span_start, span_end) = match bump!(self) {
515 TokenWithSpan {
516 token: Token::Plus(..),
517 span: plus_token_span,
518 } => {
519 let start = plus_token_span.start;
520 let mut end = match self.tokenizer.bump_without_ws_or_comments()? {
521 TokenWithSpan {
522 token: Token::Ident(..) | Token::Question(..),
523 span,
524 } => span.end,
525 TokenWithSpan { token, span } => {
526 return Err(Error {
527 kind: ErrorKind::Unexpected("?", token.symbol()),
528 span,
529 });
530 }
531 };
532 loop {
533 match peek!(self) {
534 TokenWithSpan {
535 token: Token::Question(..),
536 span,
537 } if span.start == end => {
538 end = bump!(self).span.end;
539 }
540 _ => break,
541 }
542 }
543 (start, end)
544 }
545 TokenWithSpan {
546 token: Token::Dimension(..),
547 span: dimension_token_span,
548 } => {
549 let start = dimension_token_span.start;
550 let mut end = dimension_token_span.end;
551 loop {
552 match peek!(self) {
553 TokenWithSpan {
554 token: Token::Question(..),
555 span,
556 } if span.start == end => {
557 end = bump!(self).span.end;
558 }
559 _ => break,
560 }
561 }
562 (start, end)
563 }
564 TokenWithSpan {
565 token: Token::Number(..),
566 span: number_token_span,
567 } => {
568 let start = number_token_span.start;
569 let mut end = number_token_span.end;
570 match &peek!(self).token {
571 Token::Question(..) => {
572 end = bump!(self).span.end;
573 loop {
574 match peek!(self) {
575 TokenWithSpan {
576 token: Token::Question(..),
577 span,
578 } if span.start == end => {
579 end = bump!(self).span.end;
580 }
581 _ => break,
582 }
583 }
584 }
585 Token::Dimension(..) | Token::Number(..) => {
586 end = bump!(self).span.end;
587 }
588 _ => {}
589 }
590 (start, end)
591 }
592 TokenWithSpan { span, .. } => {
593 return Err(Error {
594 kind: ErrorKind::InvalidUnicodeRange,
595 span,
596 });
597 }
598 };
599
600 let source = self.source.get(span_start + 1..span_end).ok_or(Error {
601 kind: ErrorKind::InvalidUnicodeRange,
602 span: Span {
603 start: span_start + 1,
604 end: span_end,
605 },
606 })?;
607 let span = Span {
608 start: prefix_ident.span.start,
609 end: span_end,
610 };
611 let unicode_range = if let Some((left, right)) = source.split_once('-') {
612 if left.len() > 6 || !left.chars().all(|c| c.is_ascii_hexdigit()) {
613 return Err(Error {
614 kind: ErrorKind::InvalidUnicodeRange,
615 span,
616 });
617 }
618 if right.len() > 6
619 || !right
620 .trim_end_matches('?')
621 .chars()
622 .all(|c| c.is_ascii_hexdigit())
623 {
624 return Err(Error {
625 kind: ErrorKind::InvalidUnicodeRange,
626 span,
627 });
628 }
629 let start = u32::from_str_radix(left, 16).map_err(|_| Error {
630 kind: ErrorKind::InvalidUnicodeRange,
631 span: span.clone(),
632 })?;
633 let end = u32::from_str_radix(&right.replace('?', "F"), 16).map_err(|_| Error {
634 kind: ErrorKind::InvalidUnicodeRange,
635 span: span.clone(),
636 })?;
637 UnicodeRange {
638 prefix,
639 start,
640 start_raw: left,
641 end,
642 end_raw: Some(right),
643 span,
644 }
645 } else {
646 if source.len() > 6
647 || !source
648 .trim_end_matches('?')
649 .chars()
650 .all(|c| c.is_ascii_hexdigit())
651 {
652 return Err(Error {
653 kind: ErrorKind::InvalidUnicodeRange,
654 span,
655 });
656 }
657 let start = u32::from_str_radix(&source.replace('?', "0"), 16).map_err(|_| Error {
658 kind: ErrorKind::InvalidUnicodeRange,
659 span: span.clone(),
660 })?;
661 let end = u32::from_str_radix(&source.replace('?', "F"), 16).map_err(|_| Error {
662 kind: ErrorKind::InvalidUnicodeRange,
663 span: span.clone(),
664 })?;
665 UnicodeRange {
666 prefix,
667 start,
668 start_raw: source,
669 end,
670 end_raw: None,
671 span,
672 }
673 };
674 if unicode_range.end > 0x10ffff {
675 self.recoverable_errors.push(Error {
676 kind: ErrorKind::MaxCodePointExceeded,
677 span: unicode_range.span.clone(),
678 });
679 }
680 if unicode_range.start > unicode_range.end {
681 self.recoverable_errors.push(Error {
682 kind: ErrorKind::UnicodeRangeStartGreaterThanEnd,
683 span: unicode_range.span.clone(),
684 });
685 }
686 Ok(unicode_range)
687 }
688}
689
690impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for BracketBlock<'s> {
691 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
692 let start = expect!(input, LBracket).1.start;
693 let mut value = Vec::with_capacity(3);
694 loop {
695 match &peek!(input).token {
696 Token::RBracket(..) => break,
697 _ => value.push(input.parse()?),
698 }
699 }
700 let end = expect!(input, RBracket).1.end;
701 Ok(BracketBlock {
702 value,
703 span: Span { start, end },
704 })
705 }
706}
707
708impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ComponentValue<'s> {
709 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
710 match input.syntax {
711 Syntax::Css => input.parse_component_value_atom(),
712 Syntax::Scss | Syntax::Sass => {
713 input.parse_sass_bin_expr(true)
714 }
715 Syntax::Less => input.parse_less_operation(true),
716 }
717 }
718}
719
720impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ComponentValues<'s> {
721 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
723 let first = input.parse::<ComponentValue>()?;
724 let mut span = first.span().clone();
725
726 let mut values = Vec::with_capacity(4);
727 values.push(first);
728 loop {
729 match &peek!(input).token {
730 Token::Eof(..) => break,
731 Token::Semicolon(..) => {
732 values.push(input.parse().map(ComponentValue::Delimiter)?);
733 }
734 _ => values.push(input.parse()?),
735 }
736 }
737
738 if let Some(value) = values.last() {
739 span.end = value.span().end;
740 }
741 Ok(ComponentValues { values, span })
742 }
743}
744
745impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Delimiter {
746 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
747 use crate::tokenizer::token::*;
748 match bump!(input) {
749 TokenWithSpan {
750 token: Token::Solidus(..),
751 span,
752 } => Ok(Delimiter {
753 kind: DelimiterKind::Solidus,
754 span,
755 }),
756 TokenWithSpan {
757 token: Token::Comma(..),
758 span,
759 } => Ok(Delimiter {
760 kind: DelimiterKind::Comma,
761 span,
762 }),
763 TokenWithSpan {
764 token: Token::Semicolon(..),
765 span,
766 } => Ok(Delimiter {
767 kind: DelimiterKind::Semicolon,
768 span,
769 }),
770 _ => unreachable!(),
771 }
772 }
773}
774
775impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Dimension<'s> {
776 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
777 expect!(input, Dimension).try_into()
778 }
779}
780
781impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Function<'s> {
782 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
783 let name = input.parse::<FunctionName>()?;
784 match peek!(input) {
785 TokenWithSpan {
786 token: Token::LParen(..),
787 span,
788 } => {
789 util::assert_no_ws_or_comment(name.span(), span)?;
790 match name {
791 FunctionName::Ident(name) => input.parse_function(name),
792 name => {
793 bump!(input);
794 let args = input.parse_function_args()?;
795 let (_, Span { end, .. }) = expect!(input, RParen);
796 let span = Span {
797 start: name.span().start,
798 end,
799 };
800 Ok(Function { name, args, span })
801 }
802 }
803 }
804 TokenWithSpan { token, span } => {
805 use crate::{token::LParen, tokenizer::TokenSymbol};
806 Err(Error {
807 kind: ErrorKind::Unexpected(LParen::symbol(), token.symbol()),
808 span: span.clone(),
809 })
810 }
811 }
812 }
813}
814
815impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for FunctionName<'s> {
816 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
817 match peek!(input).token {
818 Token::Ident(..) => {
819 let ident = input.parse::<Ident>()?;
820 match (&peek!(input).token, input.syntax) {
821 (Token::Dot(..), Syntax::Scss | Syntax::Sass) => {
822 bump!(input);
823 let member = input.parse::<Ident>()?;
824 let span = Span {
825 start: ident.span.start,
826 end: member.span.end,
827 };
828 Ok(FunctionName::SassQualifiedName(Box::new(
829 SassQualifiedName {
830 module: ident,
831 member: SassModuleMemberName::Ident(member),
832 span,
833 },
834 )))
835 }
836 _ => Ok(FunctionName::Ident(InterpolableIdent::Literal(ident))),
837 }
838 }
839 Token::Percent(..) if input.syntax == Syntax::Less => {
840 input.parse().map(FunctionName::LessFormatFunction)
841 }
842 Token::Tilde(..) if input.syntax == Syntax::Less => {
843 input.parse().map(FunctionName::LessListFunction)
844 }
845 _ => {
846 use crate::{token::Ident, tokenizer::TokenSymbol};
847 let TokenWithSpan { token, span } = bump!(input);
848 Err(Error {
849 kind: ErrorKind::Unexpected(Ident::symbol(), token.symbol()),
850 span,
851 })
852 }
853 }
854 }
855}
856
857impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for HexColor<'s> {
858 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
859 let (token, span) = expect!(input, Hash);
860 let raw = token.raw;
861 let value = if token.escaped {
862 util::handle_escape(raw)
863 } else {
864 Cow::from(raw)
865 };
866 Ok(HexColor { value, raw, span })
867 }
868}
869
870impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Ident<'s> {
871 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
872 Ok(expect!(input, Ident).into())
873 }
874}
875
876impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for InterpolableIdent<'s> {
877 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
878 match input.syntax {
879 Syntax::Css => input.parse().map(InterpolableIdent::Literal),
880 Syntax::Scss | Syntax::Sass => input.parse_sass_interpolated_ident(),
881 Syntax::Less => {
882 if matches!(
884 input.state.qualified_rule_ctx,
885 Some(QualifiedRuleContext::DeclarationValue)
886 ) {
887 input.parse().map(InterpolableIdent::Literal)
888 } else {
889 input.parse_less_interpolated_ident()
890 }
891 }
892 }
893 }
894}
895
896impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for InterpolableStr<'s> {
897 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
898 match peek!(input) {
899 TokenWithSpan {
900 token: Token::Str(..),
901 ..
902 } => input.parse().map(InterpolableStr::Literal),
903 TokenWithSpan {
904 token: Token::StrTemplate(..),
905 span,
906 } => match input.syntax {
907 Syntax::Scss | Syntax::Sass => input.parse().map(InterpolableStr::SassInterpolated),
908 Syntax::Less => input.parse().map(InterpolableStr::LessInterpolated),
909 Syntax::Css => Err(Error {
910 kind: ErrorKind::UnexpectedTemplateInCss,
911 span: span.clone(),
912 }),
913 },
914 TokenWithSpan { span, .. } => Err(Error {
915 kind: ErrorKind::ExpectString,
916 span: span.clone(),
917 }),
918 }
919 }
920}
921
922impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Number<'s> {
923 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
924 let (number, span) = expect!(input, Number);
925 number
926 .raw
927 .parse()
928 .map_err(|_| Error {
929 kind: ErrorKind::InvalidNumber,
930 span: span.clone(),
931 })
932 .map(|value| Self {
933 value,
934 raw: number.raw,
935 span,
936 })
937 }
938}
939
940impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Percentage<'s> {
941 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
942 let (token, span) = expect!(input, Percentage);
943 Ok(Percentage {
944 value: (
945 token.value,
946 Span {
947 start: span.start,
948 end: span.end - 1,
949 },
950 )
951 .try_into()?,
952 span,
953 })
954 }
955}
956
957impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Str<'s> {
958 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
959 Ok(expect!(input, Str).into())
960 }
961}
962
963impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Url<'s> {
964 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
965 let (prefix, prefix_span) = expect!(input, Ident);
966 if !prefix.name().eq_ignore_ascii_case("url") {
967 return Err(Error {
968 kind: ErrorKind::ExpectUrl,
969 span: prefix_span,
970 });
971 }
972
973 match peek!(input) {
974 TokenWithSpan {
975 token: Token::LParen(..),
976 span,
977 } if prefix_span.end == span.start => {
978 bump!(input);
979 }
980 TokenWithSpan { span, .. } => {
981 return Err(Error {
982 kind: ErrorKind::TryParseError,
983 span: span.clone(),
984 });
985 }
986 }
987
988 if input.tokenizer.is_start_of_url_string() {
989 let value = input.parse()?;
990 let modifiers = match &peek!(input).token {
991 Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
992 let mut modifiers = Vec::with_capacity(1);
993 loop {
994 modifiers.push(input.parse()?);
995 if let Token::RParen(..) = &peek!(input).token {
996 break;
997 }
998 }
999 modifiers
1000 }
1001 _ => vec![],
1002 };
1003 let end = expect!(input, RParen).1.end;
1004 let span = Span {
1005 start: prefix_span.start,
1006 end,
1007 };
1008 Ok(Url {
1009 name: (prefix, prefix_span).into(),
1010 value: Some(UrlValue::Str(value)),
1011 modifiers,
1012 span,
1013 })
1014 } else if let Ok(value) = input.try_parse(UrlRaw::parse) {
1015 let span = Span {
1016 start: prefix_span.start,
1017 end: value.span.end + 1, };
1019 Ok(Url {
1020 name: (prefix, prefix_span).into(),
1021 value: Some(UrlValue::Raw(value)),
1022 modifiers: vec![],
1023 span,
1024 })
1025 } else {
1026 match input.syntax {
1027 Syntax::Css => Err(Error {
1028 kind: ErrorKind::InvalidUrl,
1029 span: bump!(input).span,
1030 }),
1031 Syntax::Scss | Syntax::Sass => {
1032 let value = input.parse::<SassInterpolatedUrl>()?;
1033 let span = Span {
1034 start: prefix_span.start,
1035 end: value.span.end + 1, };
1037 Ok(Url {
1038 name: (prefix, prefix_span).into(),
1039 value: Some(UrlValue::SassInterpolated(value)),
1040 modifiers: vec![],
1041 span,
1042 })
1043 }
1044 Syntax::Less => {
1045 let value = UrlValue::LessEscapedStr(input.parse()?);
1046 let (_, Span { end, .. }) = expect!(input, RParen);
1047 let span = Span {
1048 start: prefix_span.start,
1049 end,
1050 };
1051 Ok(Url {
1052 name: (prefix, prefix_span).into(),
1053 value: Some(value),
1054 modifiers: vec![],
1055 span,
1056 })
1057 }
1058 }
1059 }
1060 }
1061}
1062
1063impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for UrlModifier<'s> {
1064 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
1065 let ident = input.parse::<InterpolableIdent>()?;
1066 match peek!(input) {
1067 TokenWithSpan {
1068 token: Token::LParen(..),
1069 span,
1070 } if ident.span().end == span.start => {
1071 input.parse_function(ident).map(UrlModifier::Function)
1072 }
1073 _ => Ok(UrlModifier::Ident(ident)),
1074 }
1075 }
1076}
1077
1078impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for UrlRaw<'s> {
1079 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
1080 match input.tokenizer.scan_url_raw_or_template()? {
1081 TokenWithSpan {
1082 token: Token::UrlRaw(url),
1083 span,
1084 } => {
1085 let value = if url.escaped {
1086 util::handle_escape(url.raw)
1087 } else {
1088 Cow::from(url.raw)
1089 };
1090 Ok(UrlRaw {
1091 value,
1092 raw: url.raw,
1093 span,
1094 })
1095 }
1096 TokenWithSpan { token, span } => Err(Error {
1097 kind: ErrorKind::Unexpected("<url>", token.symbol()),
1098 span,
1099 }),
1100 }
1101 }
1102}