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