Skip to main content

oxc_css_parser/parser/
value.rs

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                    // while this syntax is weird, Bootstrap is actually using it
368                    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        // caller of `parse_src_url` should make sure there're no whitespaces before paren
433        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(/* allow_comparison */ true)
598            }
599            Syntax::Less => input.parse_less_operation(/* allow_mixin_call */ true),
600        }
601    }
602}
603
604impl<'a> Parse<'a> for ComponentValues<'a> {
605    /// This is for public-use only. For internal code of oxc-css-parser, **DO NOT** use.
606    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        // A css-in-js placeholder stands in for an interpolated ident anywhere one
739        // is expected (id selector `#${x}`, attribute value `[a=${x}]`, ...).
740        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                // Less variable interpolation is disallowed in declaration value
749                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, // `)` is consumed, but span excludes it
850            };
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, // `)` is consumed, but span excludes it
860                    };
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}