raffia/parser/
value.rs

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