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