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