1use super::{Parser, state::QualifiedRuleContext};
2use crate::{
3 Parse, Syntax,
4 ast::*,
5 error::{Error, ErrorKind, PResult},
6 pos::Span,
7 tokenizer::{Token, TokenWithSpan},
8 util,
9};
10
11const PRECEDENCE_MULTIPLY: u8 = 2;
12const PRECEDENCE_PLUS: u8 = 1;
13
14fn unvendored(name: &str) -> &str {
17 name.strip_prefix('-').and_then(|rest| rest.split_once('-')).map_or(name, |(_, base)| base)
18}
19
20fn is_special_typed_or_raw_function(name: &str) -> bool {
27 let base = unvendored(name);
28 let vendored = base.len() != name.len();
29 base.eq_ignore_ascii_case("element")
30 || (!vendored && (base.eq_ignore_ascii_case("type") || base.eq_ignore_ascii_case("if")))
31 || (vendored && (base.eq_ignore_ascii_case("calc") || base.eq_ignore_ascii_case("url")))
32}
33
34impl<'a> Parser<'a> {
35 pub(in crate::parser) fn parse_calc_expr(
36 &mut self,
37 allow_modulo: bool,
38 ) -> PResult<ComponentValue<'a>> {
39 self.parse_calc_expr_recursively(0, allow_modulo)
40 }
41
42 fn parse_calc_expr_recursively(
50 &mut self,
51 precedence: u8,
52 allow_modulo: bool,
53 ) -> PResult<ComponentValue<'a>> {
54 let mut left = if precedence >= PRECEDENCE_MULTIPLY {
55 if self.cursor.eat_l_paren()?.is_some() {
56 let expr = self.parse_calc_expr(allow_modulo)?;
57 self.cursor.expect_r_paren()?;
58 expr
59 } else if matches!(self.syntax, Syntax::Scss | Syntax::Sass)
60 && matches!(&self.cursor.peek()?.token, Token::Minus(..) | Token::Plus(..))
61 && {
62 let span = &self.cursor.peek()?.span;
63 self.source.as_bytes().get(span.end) == Some(&b'(')
64 }
65 {
66 let op = match &self.cursor.peek()?.token {
70 Token::Minus(..) => SassUnaryOperator {
71 kind: SassUnaryOperatorKind::Minus,
72 span: self.cursor.bump()?.span,
73 },
74 _ => SassUnaryOperator {
75 kind: SassUnaryOperatorKind::Plus,
76 span: self.cursor.bump()?.span,
77 },
78 };
79 let expr = self.parse_calc_expr_recursively(PRECEDENCE_MULTIPLY, allow_modulo)?;
80 let span = Span { start: op.span.start, end: expr.span().end };
81 ComponentValue::SassUnaryExpression(SassUnaryExpression {
82 expr: self.alloc(expr),
83 op,
84 span,
85 })
86 } else if self.syntax == Syntax::Less {
87 if matches!(self.cursor.peek()?.token, Token::Minus(..)) {
88 ComponentValue::LessNegativeValue(self.parse()?)
89 } else {
90 self.parse_component_value_atom()?
91 }
92 } else {
93 self.parse_component_value_atom()?
94 }
95 } else {
96 self.parse_calc_expr_recursively(precedence + 1, allow_modulo)?
97 };
98
99 loop {
100 let operator = match &self.cursor.peek()?.token {
101 Token::Asterisk(..) if precedence == PRECEDENCE_MULTIPLY => CalcOperator {
102 kind: CalcOperatorKind::Multiply,
103 span: self.cursor.bump()?.span,
104 },
105 Token::Solidus(..) if precedence == PRECEDENCE_MULTIPLY => CalcOperator {
106 kind: CalcOperatorKind::Division,
107 span: self.cursor.bump()?.span,
108 },
109 Token::Percent(..) if precedence == PRECEDENCE_MULTIPLY && allow_modulo => {
113 CalcOperator { kind: CalcOperatorKind::Modulo, span: self.cursor.bump()?.span }
114 }
115 Token::Plus(..) if precedence == PRECEDENCE_PLUS => {
116 CalcOperator { kind: CalcOperatorKind::Plus, span: self.cursor.bump()?.span }
117 }
118 Token::Minus(..) if precedence == PRECEDENCE_PLUS => {
119 CalcOperator { kind: CalcOperatorKind::Minus, span: self.cursor.bump()?.span }
120 }
121 _ => break,
122 };
123
124 let right = self.parse_calc_expr_recursively(precedence + 1, allow_modulo)?;
125 let span = Span { start: left.span().start, end: right.span().end };
126 left = ComponentValue::Calc(Calc {
127 left: self.alloc(left),
128 op: operator,
129 right: self.alloc(right),
130 span,
131 });
132 }
133
134 Ok(left)
135 }
136
137 pub(super) fn parse_component_value_atom(&mut self) -> PResult<ComponentValue<'a>> {
141 let token_with_span = self.cursor.peek()?;
142 match &token_with_span.token {
143 Token::Ident(token) => {
144 if unvendored(&token.name()).eq_ignore_ascii_case("url") {
145 match self.try_parse(Url::parse) {
146 Ok(url) => return Ok(ComponentValue::Url(self.alloc(url))),
147 Err(Error { kind: ErrorKind::TryParseError, .. }) => {}
148 Err(error) => {
149 let (function_name, function_name_span) = self.cursor.expect_ident()?;
156 let function_name = self.ident(function_name, function_name_span);
157 return self
158 .parse_function_typed_or_raw(function_name)
159 .map(ComponentValue::Function)
160 .map_err(|_| error);
161 }
162 }
163 }
164 let ident = self.parse::<InterpolableIdent>()?;
165 let ident_end = ident.span().end;
166 match self.cursor.peek()? {
167 TokenWithSpan { token: Token::LParen(..), span } if span.start == ident_end => {
168 return match ident {
169 InterpolableIdent::Literal(ident)
170 if ident.name.eq_ignore_ascii_case("src") =>
171 {
172 self.parse_src_url(ident)
173 .map(|url| ComponentValue::Url(self.alloc(url)))
174 }
175 InterpolableIdent::Literal(ident)
176 if unvendored(ident.name).eq_ignore_ascii_case("expression") =>
177 {
178 self.parse_raw_function(InterpolableIdent::Literal(ident))
181 .map(ComponentValue::Function)
182 }
183 InterpolableIdent::Literal(ident)
184 if is_special_typed_or_raw_function(ident.name) =>
185 {
186 self.parse_function_typed_or_raw(ident)
187 .map(ComponentValue::Function)
188 }
189 ident => self.parse_function(ident).map(ComponentValue::Function),
190 };
191 }
192 TokenWithSpan { token: Token::Colon(..), span }
197 if span.start == ident_end
198 && matches!(
199 &ident,
200 InterpolableIdent::Literal(id)
201 if unvendored(id.name).eq_ignore_ascii_case("progid")
202 ) =>
203 {
204 if let InterpolableIdent::Literal(ident) = ident {
205 return self.parse_progid_function(ident).map(ComponentValue::Function);
206 }
207 unreachable!("guard matched a literal ident");
208 }
209 TokenWithSpan { token: Token::Dot(..), span }
210 if matches!(self.syntax, Syntax::Scss | Syntax::Sass)
211 && span.start == ident_end =>
212 {
213 if let InterpolableIdent::Literal(module) = &ident {
214 let module = Ident {
215 name: module.name,
216 raw: module.raw,
217 span: module.span.clone(),
218 };
219 let qualified = self.try_parse(|parser| {
222 let name = parser.parse_sass_qualified_name(module)?;
223 if let SassQualifiedName {
224 member: SassModuleMemberName::Ident(..),
225 ..
226 } = name
227 {
228 let (_, lparen_span) = parser.cursor.expect_l_paren()?;
229 util::assert_no_ws_or_comment(&name.span, &lparen_span)?;
230 let args = parser.parse_function_args()?;
231 let (_, Span { end, .. }) = parser.cursor.expect_r_paren()?;
232 let span = Span { start: name.span.start, end };
233 Ok(ComponentValue::Function(Function {
234 name: FunctionName::SassQualifiedName(parser.alloc(name)),
235 args,
236 span,
237 }))
238 } else {
239 Ok(ComponentValue::SassQualifiedName(parser.alloc(name)))
240 }
241 });
242 return match qualified {
243 Ok(value) => Ok(value),
244 Err(_) => Ok(ComponentValue::InterpolableIdent(ident)),
252 };
253 }
254 }
255 _ => {}
256 }
257 match ident {
258 InterpolableIdent::Literal(ident) if ident.raw.eq_ignore_ascii_case("u") => {
259 match self.cursor.peek()? {
260 TokenWithSpan { token: Token::Plus(..), span }
261 if span.start == ident_end =>
262 {
263 self.parse_unicode_range(ident).map(ComponentValue::UnicodeRange)
264 }
265 TokenWithSpan { token: Token::Number(token), span }
266 if token.raw.starts_with('+') && span.start == ident_end =>
267 {
268 self.parse_unicode_range(ident).map(ComponentValue::UnicodeRange)
269 }
270 TokenWithSpan { token: Token::Dimension(token), span }
271 if token.value.raw.starts_with('+') && span.start == ident_end =>
272 {
273 self.parse_unicode_range(ident).map(ComponentValue::UnicodeRange)
274 }
275 _ => Ok(ComponentValue::InterpolableIdent(InterpolableIdent::Literal(
276 ident,
277 ))),
278 }
279 }
280 _ => Ok(ComponentValue::InterpolableIdent(ident)),
281 }
282 }
283 Token::Solidus(..) | Token::Comma(..) => self.parse().map(ComponentValue::Delimiter),
284 Token::Number(..) => self.parse().map(ComponentValue::Number),
285 Token::Dimension(..) => self.parse().map(ComponentValue::Dimension),
286 Token::Percentage(..) => self.parse().map(ComponentValue::Percentage),
287 Token::Hash(..) => {
288 if self.syntax == Syntax::Less {
289 self.parse_maybe_hex_color_or_less_mixin_call()
290 } else {
291 self.parse().map(ComponentValue::HexColor)
292 }
293 }
294 Token::Str(..) => {
295 self.parse().map(InterpolableStr::Literal).map(ComponentValue::InterpolableStr)
296 }
297 Token::LBracket(..) => self.parse().map(ComponentValue::BracketBlock),
298 Token::DollarVar(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
299 self.parse().map(ComponentValue::SassVariable)
300 }
301 Token::DollarVar(..)
302 if self.syntax == Syntax::Css && self.options.allow_postcss_simple_vars =>
303 {
304 self.parse().map(ComponentValue::PostcssSimpleVar)
305 }
306 Token::LParen(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
307 match self.try_parse(SassParenthesizedExpression::parse) {
308 Ok(expr) => Ok(ComponentValue::SassParenthesizedExpression(expr)),
309 Err(err) => self.parse().map(ComponentValue::SassMap).map_err(|_| err),
310 }
311 }
312 Token::HashLBrace(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
313 let ident = self.parse_sass_interpolated_ident()?;
314 match self.cursor.peek()? {
315 TokenWithSpan { token: Token::LParen(..), span }
316 if span.start == ident.span().end =>
317 {
318 self.parse_function(ident).map(ComponentValue::Function)
319 }
320 _ => Ok(ComponentValue::InterpolableIdent(ident)),
321 }
322 }
323 Token::StrTemplate(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => self
324 .parse()
325 .map(InterpolableStr::SassInterpolated)
326 .map(ComponentValue::InterpolableStr),
327 Token::Ampersand(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
328 self.parse().map(ComponentValue::SassParentSelector)
329 }
330 Token::LBrace(..)
331 if self.syntax == Syntax::Scss
332 && matches!(
333 self.state.qualified_rule_ctx,
334 Some(QualifiedRuleContext::DeclarationValue)
335 ) =>
336 {
337 self.parse().map(ComponentValue::SassNestingDeclaration)
338 }
339 Token::Indent(..)
340 if self.syntax == Syntax::Sass
341 && matches!(
342 self.state.qualified_rule_ctx,
343 Some(QualifiedRuleContext::DeclarationValue)
344 ) =>
345 {
346 self.parse().map(ComponentValue::SassNestingDeclaration)
347 }
348 Token::AtKeyword(..) if self.syntax == Syntax::Less => {
349 self.parse_less_maybe_variable_or_with_lookups()
350 }
351 Token::Dot(..) if self.syntax == Syntax::Less => {
352 self.parse_less_maybe_mixin_call_or_with_lookups()
353 }
354 Token::Dot(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
360 let dot = self.cursor.bump()?;
361 match self.cursor.peek()? {
362 TokenWithSpan { token: Token::Ident(..), span }
363 if span.start == dot.span.end =>
364 {
365 Ok(ComponentValue::TokenWithSpan(dot))
366 }
367 _ => Err(Error { kind: ErrorKind::ExpectComponentValue, span: dot.span }),
368 }
369 }
370 Token::StrTemplate(..) if self.syntax == Syntax::Less => self
371 .parse()
372 .map(InterpolableStr::LessInterpolated)
373 .map(ComponentValue::InterpolableStr),
374 Token::At(..) if self.syntax == Syntax::Less => {
375 self.parse().map(ComponentValue::LessVariableVariable)
376 }
377 Token::DollarVar(..) if self.syntax == Syntax::Less => {
378 self.parse().map(ComponentValue::LessPropertyVariable)
379 }
380 Token::Tilde(..) if self.syntax == Syntax::Less => {
381 if let Ok(list_function_call) = self.try_parse(Function::parse) {
382 Ok(ComponentValue::Function(list_function_call))
383 } else if let Ok(less_escaped_str) = self.try_parse(LessEscapedStr::parse) {
384 Ok(ComponentValue::LessEscapedStr(less_escaped_str))
385 } else {
386 self.parse().map(ComponentValue::LessJavaScriptSnippet)
387 }
388 }
389 Token::Percent(..) if self.syntax == Syntax::Less => self
390 .try_parse(Function::parse)
391 .map(ComponentValue::Function)
392 .or_else(|_| self.parse().map(ComponentValue::LessPercentKeyword)),
393 Token::BacktickCode(..) if self.syntax == Syntax::Less => {
394 self.parse().map(ComponentValue::LessJavaScriptSnippet)
395 }
396 Token::Placeholder(..) => {
397 let (placeholder, span) = self.cursor.expect_placeholder()?;
398 Ok(ComponentValue::Placeholder((placeholder, span).into()))
399 }
400 _ => Err(Error {
401 kind: ErrorKind::ExpectComponentValue,
402 span: token_with_span.span.clone(),
403 }),
404 }
405 }
406
407 pub(super) fn parse_dashed_ident(&mut self) -> PResult<InterpolableIdent<'a>> {
409 let ident = self.parse()?;
410 match &ident {
411 InterpolableIdent::Literal(ident) if !ident.name.starts_with("--") => {
412 self.recoverable_errors
413 .push(Error { kind: ErrorKind::ExpectDashedIdent, span: ident.span.clone() });
414 }
415 _ => {}
416 }
417 Ok(ident)
418 }
419
420 pub(super) fn parse_function(&mut self, name: InterpolableIdent<'a>) -> PResult<Function<'a>> {
422 self.cursor.expect_l_paren()?;
423 let args = if let Token::RParen(..) = &self.cursor.peek()?.token {
424 self.vec()
425 } else {
426 match &name {
427 InterpolableIdent::Literal(ident)
428 if ident.name.eq_ignore_ascii_case("calc")
429 || ident.name.eq_ignore_ascii_case("-webkit-calc")
430 || ident.name.eq_ignore_ascii_case("-moz-calc")
431 || ident.name.eq_ignore_ascii_case("min")
432 || ident.name.eq_ignore_ascii_case("max")
433 || ident.name.eq_ignore_ascii_case("clamp")
434 || ident.name.eq_ignore_ascii_case("sin")
435 || ident.name.eq_ignore_ascii_case("cos")
436 || ident.name.eq_ignore_ascii_case("tan")
437 || ident.name.eq_ignore_ascii_case("asin")
438 || ident.name.eq_ignore_ascii_case("acos")
439 || ident.name.eq_ignore_ascii_case("atan")
440 || ident.name.eq_ignore_ascii_case("sqrt")
441 || ident.name.eq_ignore_ascii_case("exp")
442 || ident.name.eq_ignore_ascii_case("abs")
443 || ident.name.eq_ignore_ascii_case("sign")
444 || ident.name.eq_ignore_ascii_case("hypot")
445 || ident.name.eq_ignore_ascii_case("round")
446 || ident.name.eq_ignore_ascii_case("mod")
447 || ident.name.eq_ignore_ascii_case("rem")
448 || ident.name.eq_ignore_ascii_case("atan2")
449 || ident.name.eq_ignore_ascii_case("pow")
450 || ident.name.eq_ignore_ascii_case("log") =>
451 {
452 let allow_modulo = matches!(self.syntax, Syntax::Scss | Syntax::Sass)
455 && (ident.name.eq_ignore_ascii_case("min")
456 || ident.name.eq_ignore_ascii_case("max"));
457 if !matches!(self.syntax, Syntax::Scss | Syntax::Sass) {
464 self.parse_calc_args(allow_modulo)?
465 } else {
466 let typed = self.try_parse(|p| {
467 let values = p.parse_calc_args(allow_modulo)?;
468 if matches!(&p.cursor.peek()?.token, Token::RParen(..)) {
469 Ok(values)
470 } else {
471 let span = p.cursor.peek()?.span.clone();
472 Err(Error { kind: ErrorKind::TryParseError, span })
473 }
474 });
475 match typed {
476 Ok(values) => values,
477 Err(error) => {
478 let (args, comma_spans) = self.parse_sass_invocation_args()?;
479 if !args.iter().any(|arg| {
484 matches!(arg, ComponentValue::SassKeywordArgument(..))
485 }) {
486 return Err(error);
487 }
488 let mut values = self.vec_with_capacity(args.len() * 2);
489 let mut comma_spans = comma_spans.into_iter();
490 for (i, arg) in args.into_iter().enumerate() {
491 if i > 0
492 && let Some(span) = comma_spans.next()
493 {
494 values.push(ComponentValue::Delimiter(Delimiter {
495 kind: DelimiterKind::Comma,
496 span,
497 }));
498 }
499 values.push(arg);
500 }
501 values
502 }
503 }
504 }
505 }
506 InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("element") => {
507 let id_selector = self.parse().map(ComponentValue::IdSelector)?;
508 self.vec1(id_selector)
509 }
510 InterpolableIdent::Literal(Ident { raw: "boolean" | "if", .. })
511 if self.syntax == Syntax::Less =>
512 {
513 let less_condition = self.parse_less_condition(false)?;
514 let condition = ComponentValue::LessCondition(self.alloc(less_condition));
515 let mut args = self.parse_function_args()?;
516 args.insert(0, condition);
517 args
518 }
519 _ => self.parse_function_args()?,
520 }
521 };
522 let end = self.cursor.expect_r_paren()?.1.end;
523 let span = Span { start: name.span().start, end };
524 Ok(Function { name: FunctionName::Ident(name), args, span })
525 }
526
527 fn parse_calc_args(
531 &mut self,
532 allow_modulo: bool,
533 ) -> PResult<oxc_allocator::Vec<'a, ComponentValue<'a>>> {
534 let mut values = self.vec_with_capacity(1);
535 loop {
536 match self.cursor.peek()? {
537 TokenWithSpan { token: Token::RParen(..), .. } => break,
538 TokenWithSpan { token: Token::Comma(..), .. } => {
539 values.push(ComponentValue::Delimiter(self.parse()?));
540 }
541 TokenWithSpan { token: Token::DotDotDot(..), .. }
544 if allow_modulo
545 && matches!(self.syntax, Syntax::Scss | Syntax::Sass)
546 && !values.is_empty()
547 && !values
548 .iter()
549 .any(|v| matches!(v, ComponentValue::SassArbitraryArgument(..))) =>
550 {
551 let TokenWithSpan { span: Span { end, .. }, .. } = self.cursor.bump()?;
552 let value = values.pop().unwrap();
553 let span = Span { start: value.span().start, end };
554 values.push(ComponentValue::SassArbitraryArgument(SassArbitraryArgument {
555 value: self.alloc(value),
556 span,
557 }));
558 }
559 _ => values.push(self.parse_calc_expr(allow_modulo)?),
560 }
561 }
562 Ok(values)
563 }
564
565 pub(super) fn parse_function_typed_or_raw(&mut self, name: Ident<'a>) -> PResult<Function<'a>> {
569 let name_copy = Ident { name: name.name, raw: name.raw, span: name.span.clone() };
570 match self.try_parse(|p| p.parse_function(InterpolableIdent::Literal(name))) {
571 Ok(function) => Ok(function),
572 Err(_) => self.parse_raw_function(InterpolableIdent::Literal(name_copy)),
573 }
574 }
575
576 pub(in crate::parser) fn parse_raw_function(
580 &mut self,
581 name: InterpolableIdent<'a>,
582 ) -> PResult<Function<'a>> {
583 self.cursor.expect_l_paren()?;
584 let mut args = self.vec_with_capacity(4);
585 self.parse_raw_function_args_into(&mut args)?;
586 let end = self.cursor.expect_r_paren()?.1.end;
587 let span = Span { start: name.span().start, end };
588 Ok(Function { name: FunctionName::Ident(name), args, span })
589 }
590
591 fn parse_progid_function(&mut self, name: Ident<'a>) -> PResult<Function<'a>> {
595 let mut args = self.vec_with_capacity(4);
596 loop {
597 match &self.cursor.peek()?.token {
598 Token::LParen(..)
599 | Token::Semicolon(..)
600 | Token::RBrace(..)
601 | Token::RParen(..)
602 | Token::Eof(..)
603 | Token::Indent(..)
604 | Token::Dedent(..)
605 | Token::Linebreak(..) => break,
606 _ => args.push(ComponentValue::TokenWithSpan(self.cursor.bump()?)),
607 }
608 }
609 self.cursor.expect_l_paren()?;
610 self.parse_raw_function_args_into(&mut args)?;
611 let end = self.cursor.expect_r_paren()?.1.end;
612 let span = Span { start: name.span.start, end };
613 Ok(Function { name: FunctionName::Ident(InterpolableIdent::Literal(name)), args, span })
614 }
615
616 fn parse_raw_function_args_into(
620 &mut self,
621 values: &mut oxc_allocator::Vec<'a, ComponentValue<'a>>,
622 ) -> PResult<()> {
623 let mut pairs: Vec<util::PairedToken> = Vec::with_capacity(1);
624 loop {
625 match &self.cursor.peek()?.token {
626 Token::Eof(..) => break,
627 Token::StrTemplate(..) => {
630 values.push(ComponentValue::InterpolableStr(self.parse()?));
631 continue;
632 }
633 token => {
634 if !util::track_paired_token(token, &mut pairs) {
635 break;
636 }
637 }
638 }
639 values.push(ComponentValue::TokenWithSpan(self.cursor.bump()?));
640 }
641 Ok(())
642 }
643
644 pub(super) fn parse_function_args(
647 &mut self,
648 ) -> PResult<oxc_allocator::Vec<'a, ComponentValue<'a>>> {
649 let mut values = self.vec_with_capacity(4);
650 loop {
651 match &self.cursor.peek()?.token {
652 Token::RParen(..) | Token::Eof(..) => break,
653 Token::Semicolon(..) => {
654 values.push(self.parse().map(ComponentValue::Delimiter)?);
655 }
656 Token::Exclamation(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
657 values.push(self.parse().map(ComponentValue::ImportantAnnotation)?);
659 }
660 Token::LBrace(..) if self.syntax == Syntax::Less => {
661 values.push(self.parse().map(ComponentValue::LessDetachedRuleset)?);
662 }
663 Token::Dot(..) | Token::NumberSign(..) if self.syntax == Syntax::Less => {
664 if let Ok(mixin) = self.try_parse(Parser::parse_less_anonymous_mixin) {
665 values.push(ComponentValue::LessAnonymousMixin(mixin));
666 } else if let Ok(value) = self.try_parse(ComponentValue::parse) {
667 values.push(value);
668 } else {
669 values.push(ComponentValue::TokenWithSpan(self.cursor.bump()?));
670 }
671 }
672 Token::Indent(..) | Token::Dedent(..) | Token::Linebreak(..) => {
673 self.cursor.bump()?;
674 }
675 Token::Unknown(..) if self.syntax != Syntax::Css => {
679 let span = self.cursor.peek()?.span.clone();
680 return Err(Error { kind: ErrorKind::UnknownToken, span });
681 }
682 _ => {
683 let value = if let Ok(value) = self.try_parse(ComponentValue::parse) {
684 value
685 } else {
686 values.push(ComponentValue::TokenWithSpan(self.cursor.bump()?));
687 continue;
688 };
689 if matches!(self.syntax, Syntax::Scss | Syntax::Sass) {
690 if let Some((_, mut span)) = self.cursor.eat_dot_dot_dot()? {
691 span.start = value.span().start;
692 values.push(ComponentValue::SassArbitraryArgument(
693 SassArbitraryArgument { value: self.alloc(value), span },
694 ));
695 } else if let ComponentValue::SassVariable(sass_var) = value {
696 if let Some((_, colon_span)) = self.cursor.eat_colon()? {
697 let value = self.parse::<ComponentValue>()?;
698 let span =
699 Span { start: sass_var.span.start, end: value.span().end };
700 values.push(ComponentValue::SassKeywordArgument(
701 SassKeywordArgument {
702 name: sass_var,
703 colon_span,
704 value: self.alloc(value),
705 span,
706 },
707 ));
708 } else {
709 values.push(ComponentValue::SassVariable(sass_var));
710 }
711 } else {
712 values.push(value);
713 }
714 } else {
715 values.push(value);
716 }
717 }
718 }
719 }
720 Ok(values)
721 }
722
723 pub(super) fn parse_ratio(&mut self, numerator: Number<'a>) -> PResult<Ratio<'a>> {
726 let (_, solidus_span) = self.cursor.expect_solidus()?;
727 let denominator = self.parse::<Number>()?;
728 if denominator.value <= 0.0 {
729 self.recoverable_errors.push(Error {
730 kind: ErrorKind::InvalidRatioDenominator,
731 span: denominator.span.clone(),
732 });
733 }
734
735 let span = Span { start: numerator.span.start, end: denominator.span.end };
736 Ok(Ratio { numerator, solidus_span, denominator, span })
737 }
738
739 fn parse_src_url(&mut self, name: Ident<'a>) -> PResult<Url<'a>> {
742 self.cursor.expect_l_paren()?;
744 let value = match &self.cursor.peek()?.token {
745 Token::Str(..) | Token::StrTemplate(..) => {
746 Some(UrlValue::Str(self.parse::<InterpolableStr>()?))
747 }
748 _ => None,
749 };
750 let modifiers = match &self.cursor.peek()?.token {
751 Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
752 let mut modifiers = self.vec_with_capacity(1);
753 loop {
754 modifiers.push(self.parse()?);
755 if let Token::RParen(..) = &self.cursor.peek()?.token {
756 break;
757 }
758 }
759 modifiers
760 }
761 _ => self.vec(),
762 };
763 let end = self.cursor.expect_r_paren()?.1.end;
764 let span = Span { start: name.span.start, end };
765 Ok(Url { name, value, modifiers, span })
766 }
767
768 fn parse_unicode_range(&mut self, prefix_ident: Ident<'a>) -> PResult<UnicodeRange<'a>> {
771 let prefix = prefix_ident.raw.chars().next().unwrap();
772 let (span_start, span_end) = match self.cursor.bump()? {
773 TokenWithSpan { token: Token::Plus(..), span: plus_token_span } => {
774 let start = plus_token_span.start;
775 let mut end = match self.cursor.tokenizer.bump_without_ws_or_comments()? {
776 TokenWithSpan { token: Token::Ident(..) | Token::Question(..), span } => {
777 span.end
778 }
779 TokenWithSpan { token, span } => {
780 return Err(Error {
781 kind: ErrorKind::Unexpected("?", token.symbol()),
782 span,
783 });
784 }
785 };
786 loop {
787 match self.cursor.peek()? {
788 TokenWithSpan { token: Token::Question(..), span } if span.start == end => {
789 end = self.cursor.bump()?.span.end;
790 }
791 _ => break,
792 }
793 }
794 (start, end)
795 }
796 TokenWithSpan { token: Token::Dimension(..), span: dimension_token_span } => {
797 let start = dimension_token_span.start;
798 let mut end = dimension_token_span.end;
799 loop {
800 match self.cursor.peek()? {
801 TokenWithSpan { token: Token::Question(..), span } if span.start == end => {
802 end = self.cursor.bump()?.span.end;
803 }
804 _ => break,
805 }
806 }
807 (start, end)
808 }
809 TokenWithSpan { token: Token::Number(..), span: number_token_span } => {
810 let start = number_token_span.start;
811 let mut end = number_token_span.end;
812 match &self.cursor.peek()?.token {
813 Token::Question(..) => {
814 end = self.cursor.bump()?.span.end;
815 loop {
816 match self.cursor.peek()? {
817 TokenWithSpan { token: Token::Question(..), span }
818 if span.start == end =>
819 {
820 end = self.cursor.bump()?.span.end;
821 }
822 _ => break,
823 }
824 }
825 }
826 Token::Dimension(..) | Token::Number(..) => {
827 end = self.cursor.bump()?.span.end;
828 }
829 _ => {}
830 }
831 (start, end)
832 }
833 TokenWithSpan { span, .. } => {
834 return Err(Error { kind: ErrorKind::InvalidUnicodeRange, span });
835 }
836 };
837
838 let source = self.source.get(span_start + 1..span_end).ok_or(Error {
839 kind: ErrorKind::InvalidUnicodeRange,
840 span: Span { start: span_start + 1, end: span_end },
841 })?;
842 let span = Span { start: prefix_ident.span.start, end: span_end };
843 let unicode_range = if let Some((left, right)) = source.split_once('-') {
844 if left.len() > 6 || !left.chars().all(|c| c.is_ascii_hexdigit()) {
845 return Err(Error { kind: ErrorKind::InvalidUnicodeRange, span });
846 }
847 if right.len() > 6
848 || !right.trim_end_matches('?').chars().all(|c| c.is_ascii_hexdigit())
849 {
850 return Err(Error { kind: ErrorKind::InvalidUnicodeRange, span });
851 }
852 let start = u32::from_str_radix(left, 16)
853 .map_err(|_| Error { kind: ErrorKind::InvalidUnicodeRange, span: span.clone() })?;
854 let end = u32::from_str_radix(&replace_unicode_range_wildcards(right, 'F'), 16)
855 .map_err(|_| Error { kind: ErrorKind::InvalidUnicodeRange, span: span.clone() })?;
856 UnicodeRange { prefix, start, start_raw: left, end, end_raw: Some(right), span }
857 } else {
858 if source.len() > 6
859 || !source.trim_end_matches('?').chars().all(|c| c.is_ascii_hexdigit())
860 {
861 return Err(Error { kind: ErrorKind::InvalidUnicodeRange, span });
862 }
863 let start = u32::from_str_radix(&replace_unicode_range_wildcards(source, '0'), 16)
864 .map_err(|_| Error { kind: ErrorKind::InvalidUnicodeRange, span: span.clone() })?;
865 let end = u32::from_str_radix(&replace_unicode_range_wildcards(source, 'F'), 16)
866 .map_err(|_| Error { kind: ErrorKind::InvalidUnicodeRange, span: span.clone() })?;
867 UnicodeRange { prefix, start, start_raw: source, end, end_raw: None, span }
868 };
869 Ok(unicode_range)
874 }
875}
876
877fn replace_unicode_range_wildcards(source: &str, replacement: char) -> String {
878 source.chars().map(|c| if c == '?' { replacement } else { c }).collect()
879}
880
881impl<'a> Parse<'a> for BracketBlock<'a> {
884 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
885 let start = input.cursor.expect_l_bracket()?.1.start;
886 let mut value = input.vec_with_capacity(3);
887 loop {
888 match &input.cursor.peek()?.token {
889 Token::RBracket(..) => break,
890 _ => value.push(input.parse()?),
891 }
892 }
893 let end = input.cursor.expect_r_bracket()?.1.end;
894 Ok(BracketBlock { value, span: Span { start, end } })
895 }
896}
897
898impl<'a> Parse<'a> for ComponentValue<'a> {
903 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
904 match input.syntax {
905 Syntax::Css => input.parse_component_value_atom(),
906 Syntax::Scss | Syntax::Sass => {
907 input.parse_sass_bin_expr(true)
908 }
909 Syntax::Less => input.parse_less_operation(true),
910 }
911 }
912}
913
914impl<'a> Parse<'a> for ComponentValues<'a> {
916 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
918 let first = input.parse::<ComponentValue>()?;
919 let mut span = first.span().clone();
920
921 let mut values = input.vec_with_capacity(4);
922 values.push(first);
923 loop {
924 match &input.cursor.peek()?.token {
925 Token::Eof(..) => break,
926 Token::Semicolon(..) => {
927 values.push(input.parse().map(ComponentValue::Delimiter)?);
928 }
929 _ => values.push(input.parse()?),
930 }
931 }
932
933 if let Some(value) = values.last() {
934 span.end = value.span().end;
935 }
936 Ok(ComponentValues { values, span })
937 }
938}
939
940impl<'a> Parse<'a> for Delimiter {
942 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
943 use crate::tokenizer::token::*;
944 match input.cursor.bump()? {
945 TokenWithSpan { token: Token::Solidus(..), span } => {
946 Ok(Delimiter { kind: DelimiterKind::Solidus, span })
947 }
948 TokenWithSpan { token: Token::Comma(..), span } => {
949 Ok(Delimiter { kind: DelimiterKind::Comma, span })
950 }
951 TokenWithSpan { token: Token::Semicolon(..), span } => {
952 Ok(Delimiter { kind: DelimiterKind::Semicolon, span })
953 }
954 _ => unreachable!(),
955 }
956 }
957}
958
959impl<'a> Parse<'a> for Dimension<'a> {
961 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
962 let (dimension, span) = input.cursor.expect_dimension()?;
963 input.dimension(dimension, span)
964 }
965}
966
967impl<'a> Parse<'a> for Function<'a> {
971 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
972 let name = input.parse::<FunctionName>()?;
973 match input.cursor.peek()? {
974 TokenWithSpan { token: Token::LParen(..), span } => {
975 util::assert_no_ws_or_comment(name.span(), span)?;
976 match name {
977 FunctionName::Ident(name) => input.parse_function(name),
978 name => {
979 input.cursor.bump()?;
980 let args = input.parse_function_args()?;
981 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
982 let span = Span { start: name.span().start, end };
983 Ok(Function { name, args, span })
984 }
985 }
986 }
987 TokenWithSpan { token, span } => {
988 Err(Error { kind: ErrorKind::Unexpected("(", token.symbol()), span: span.clone() })
989 }
990 }
991 }
992}
993
994impl<'a> Parse<'a> for FunctionName<'a> {
997 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
998 match input.cursor.peek()?.token {
999 Token::Ident(..) => {
1000 let ident = input.parse::<Ident>()?;
1001 match (&input.cursor.peek()?.token, input.syntax) {
1002 (Token::Dot(..), Syntax::Scss | Syntax::Sass) => {
1003 input.cursor.bump()?;
1004 let member = input.parse::<Ident>()?;
1005 let span = Span { start: ident.span.start, end: member.span.end };
1006 Ok(FunctionName::SassQualifiedName(input.alloc(SassQualifiedName {
1007 module: ident,
1008 member: SassModuleMemberName::Ident(member),
1009 span,
1010 })))
1011 }
1012 _ => Ok(FunctionName::Ident(InterpolableIdent::Literal(ident))),
1013 }
1014 }
1015 Token::Percent(..) if input.syntax == Syntax::Less => {
1016 input.parse().map(FunctionName::LessFormatFunction)
1017 }
1018 Token::Tilde(..) if input.syntax == Syntax::Less => {
1019 input.parse().map(FunctionName::LessListFunction)
1020 }
1021 _ => {
1022 let TokenWithSpan { token, span } = input.cursor.bump()?;
1023 Err(Error { kind: ErrorKind::Unexpected("<ident>", token.symbol()), span })
1024 }
1025 }
1026 }
1027}
1028
1029impl<'a> Parse<'a> for HexColor<'a> {
1031 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1032 let (token, span) = input.cursor.expect_hash()?;
1033 let raw = token.raw;
1034 let value = if token.escaped { util::handle_escape_in(raw, input.allocator) } else { raw };
1035 Ok(HexColor { value, raw, span })
1036 }
1037}
1038
1039impl<'a> Parse<'a> for Ident<'a> {
1041 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1042 let (ident, span) = input.cursor.expect_ident()?;
1043 Ok(input.ident(ident, span))
1044 }
1045}
1046
1047impl<'a> Parse<'a> for InterpolableIdent<'a> {
1050 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1051 if let Token::Placeholder(..) = input.cursor.peek()?.token {
1054 let (placeholder, span) = input.cursor.expect_placeholder()?;
1055 return Ok(InterpolableIdent::Placeholder((placeholder, span).into()));
1056 }
1057 match input.syntax {
1058 Syntax::Css => input.parse().map(InterpolableIdent::Literal),
1059 Syntax::Scss | Syntax::Sass => input.parse_sass_interpolated_ident(),
1060 Syntax::Less => {
1061 if matches!(
1063 input.state.qualified_rule_ctx,
1064 Some(QualifiedRuleContext::DeclarationValue)
1065 ) {
1066 input.parse().map(InterpolableIdent::Literal)
1067 } else {
1068 input.parse_less_interpolated_ident()
1069 }
1070 }
1071 }
1072 }
1073}
1074
1075impl<'a> Parse<'a> for InterpolableStr<'a> {
1077 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1078 match input.cursor.peek()? {
1079 TokenWithSpan { token: Token::Str(..), .. } => {
1080 input.parse().map(InterpolableStr::Literal)
1081 }
1082 TokenWithSpan { token: Token::StrTemplate(..), span } => match input.syntax {
1083 Syntax::Scss | Syntax::Sass => input.parse().map(InterpolableStr::SassInterpolated),
1084 Syntax::Less => input.parse().map(InterpolableStr::LessInterpolated),
1085 Syntax::Css => {
1086 Err(Error { kind: ErrorKind::UnexpectedTemplateInCss, span: span.clone() })
1087 }
1088 },
1089 TokenWithSpan { span, .. } => {
1090 Err(Error { kind: ErrorKind::ExpectString, span: span.clone() })
1091 }
1092 }
1093 }
1094}
1095
1096impl<'a> Parse<'a> for Number<'a> {
1098 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1099 let (number, span) = input.cursor.expect_number()?;
1100 number
1101 .raw
1102 .parse()
1103 .map_err(|_| Error { kind: ErrorKind::InvalidNumber, span: span.clone() })
1104 .map(|value| Self { value, raw: number.raw, span })
1105 }
1106}
1107
1108impl<'a> Parse<'a> for Percentage<'a> {
1110 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1111 let (token, span) = input.cursor.expect_percentage()?;
1112 Ok(Percentage {
1113 value: (token.value, Span { start: span.start, end: span.end - 1 }).try_into()?,
1114 span,
1115 })
1116 }
1117}
1118
1119impl<'a> Parse<'a> for Str<'a> {
1121 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1122 let (str, span) = input.cursor.expect_str()?;
1123 Ok(input.str(str, span))
1124 }
1125}
1126
1127impl<'a> Parse<'a> for Url<'a> {
1132 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1133 let (prefix, prefix_span) = input.cursor.expect_ident()?;
1134 let prefix_name = prefix.name();
1138 let base_name = unvendored(&prefix_name);
1139 if !base_name.eq_ignore_ascii_case("url")
1140 && !base_name.eq_ignore_ascii_case("url-prefix")
1141 && !base_name.eq_ignore_ascii_case("domain")
1142 {
1143 return Err(Error { kind: ErrorKind::ExpectUrl, span: prefix_span });
1144 }
1145 let prefix_start = prefix_span.start;
1146 let name = input.ident(prefix, prefix_span.clone());
1147
1148 match input.cursor.peek()? {
1149 TokenWithSpan { token: Token::LParen(..), span } if prefix_span.end == span.start => {
1150 input.cursor.bump()?;
1151 }
1152 TokenWithSpan { span, .. } => {
1153 return Err(Error { kind: ErrorKind::TryParseError, span: span.clone() });
1154 }
1155 }
1156
1157 if input.cursor.tokenizer.is_start_of_url_string() {
1158 let value = input.parse()?;
1159 let modifiers = match &input.cursor.peek()?.token {
1160 Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
1161 let mut modifiers = input.vec_with_capacity(1);
1162 loop {
1163 modifiers.push(input.parse()?);
1164 if let Token::RParen(..) = &input.cursor.peek()?.token {
1165 break;
1166 }
1167 }
1168 modifiers
1169 }
1170 _ => input.vec(),
1171 };
1172 let end = input.cursor.expect_r_paren()?.1.end;
1173 let span = Span { start: prefix_start, end };
1174 Ok(Url { name, value: Some(UrlValue::Str(value)), modifiers, span })
1175 } else if let Ok(value) = input.try_parse(UrlRaw::parse) {
1176 let span = Span {
1177 start: prefix_start,
1178 end: value.span.end + 1, };
1180 Ok(Url { name, value: Some(UrlValue::Raw(value)), modifiers: input.vec(), span })
1181 } else {
1182 match input.syntax {
1183 Syntax::Css => {
1184 Err(Error { kind: ErrorKind::InvalidUrl, span: input.cursor.bump()?.span })
1185 }
1186 Syntax::Scss | Syntax::Sass => {
1187 let value = input.parse::<SassInterpolatedUrl>()?;
1188 let span = Span {
1189 start: prefix_start,
1190 end: value.span.end + 1, };
1192 Ok(Url {
1193 name,
1194 value: Some(UrlValue::SassInterpolated(value)),
1195 modifiers: input.vec(),
1196 span,
1197 })
1198 }
1199 Syntax::Less => {
1200 let value = UrlValue::LessEscapedStr(input.parse()?);
1201 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
1202 let span = Span { start: prefix_start, end };
1203 Ok(Url { name, value: Some(value), modifiers: input.vec(), span })
1204 }
1205 }
1206 }
1207 }
1208}
1209
1210impl<'a> Parse<'a> for UrlModifier<'a> {
1212 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1213 let ident = input.parse::<InterpolableIdent>()?;
1214 match input.cursor.peek()? {
1215 TokenWithSpan { token: Token::LParen(..), span } if ident.span().end == span.start => {
1216 input.parse_function(ident).map(UrlModifier::Function)
1217 }
1218 _ => Ok(UrlModifier::Ident(ident)),
1219 }
1220 }
1221}
1222
1223impl<'a> Parse<'a> for UrlRaw<'a> {
1225 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1226 match input.cursor.tokenizer.scan_url_raw_or_template()? {
1227 TokenWithSpan { token: Token::UrlRaw(url), span } => {
1228 let value = if url.escaped {
1229 util::handle_escape_in(url.raw, input.allocator)
1230 } else {
1231 url.raw
1232 };
1233 Ok(UrlRaw { value, raw: url.raw, span })
1234 }
1235 TokenWithSpan { token, span } => {
1236 Err(Error { kind: ErrorKind::Unexpected("<url>", token.symbol()), span })
1237 }
1238 }
1239 }
1240}