Skip to main content

oxc_css_parser/parser/
value.rs

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