raffia/parser/
stmt.rs

1use super::{
2    Parser,
3    state::{ParserState, QualifiedRuleContext},
4};
5use crate::{
6    Parse, Syntax,
7    ast::*,
8    bump, eat,
9    error::{Error, ErrorKind, PResult},
10    expect, peek,
11    pos::{Span, Spanned},
12    tokenizer::{Token, TokenWithSpan},
13    util::PairedToken,
14};
15
16impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Declaration<'s> {
17    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
18        let name = input
19            .with_state(ParserState {
20                qualified_rule_ctx: Some(QualifiedRuleContext::DeclarationName),
21                ..input.state
22            })
23            .parse::<InterpolableIdent>()?;
24
25        // https://tailwindcss.com/docs/theme#overriding-the-default-theme
26        let name_suffix = if let TokenWithSpan {
27            token: Token::Asterisk(..),
28            span,
29        } = peek!(input)
30            && name.span().end == span.start
31        {
32            bump!(input);
33            Some('*')
34        } else {
35            None
36        };
37
38        let less_property_merge = if input.syntax == Syntax::Less {
39            input.parse()?
40        } else {
41            None
42        };
43
44        let (_, colon_span) = expect!(input, Colon);
45        let value = {
46            let mut parser = input.with_state(ParserState {
47                qualified_rule_ctx: Some(QualifiedRuleContext::DeclarationValue),
48                ..input.state
49            });
50            match &name {
51                InterpolableIdent::Literal(ident)
52                    if ident.name.starts_with("--")
53                        || ident.name.eq_ignore_ascii_case("filter")
54                            && matches!(
55                                &peek!(parser).token,
56                                // for IE-compatibility:
57                                // filter: progid:DXImageTransform.Microsoft...
58                                Token::Ident(ident) if ident.name().eq_ignore_ascii_case("progid")
59                            ) =>
60                'value: {
61                    if parser.options.try_parsing_value_in_custom_property
62                        && let Ok(values) = parser.try_parse(Parser::parse_declaration_value)
63                    {
64                        break 'value values;
65                    }
66
67                    let mut values = Vec::with_capacity(3);
68                    let mut pairs = Vec::with_capacity(1);
69                    loop {
70                        match &peek!(parser).token {
71                            Token::Dedent(..) | Token::Linebreak(..) | Token::Eof(..) => break,
72                            Token::Semicolon(..) => {
73                                if pairs.is_empty() {
74                                    break;
75                                }
76                            }
77                            Token::LParen(..) => {
78                                pairs.push(PairedToken::Paren);
79                            }
80                            Token::RParen(..) => {
81                                if let Some(PairedToken::Paren) = pairs.pop() {
82                                } else {
83                                    break;
84                                }
85                            }
86                            Token::LBracket(..) => {
87                                pairs.push(PairedToken::Bracket);
88                            }
89                            Token::RBracket(..) => {
90                                if let Some(PairedToken::Bracket) = pairs.pop() {
91                                } else {
92                                    break;
93                                }
94                            }
95                            Token::LBrace(..) | Token::HashLBrace(..) => {
96                                pairs.push(PairedToken::Brace);
97                            }
98                            Token::RBrace(..) => {
99                                if let Some(PairedToken::Brace) = pairs.pop() {
100                                } else {
101                                    break;
102                                }
103                            }
104                            _ => {}
105                        }
106                        values.push(ComponentValue::TokenWithSpan(bump!(parser)));
107                    }
108                    values
109                }
110                _ => parser.parse_declaration_value()?,
111            }
112        };
113
114        let important = if let Token::Exclamation(..) = &peek!(input).token {
115            input.parse::<ImportantAnnotation>().map(Some)?
116        } else {
117            None
118        };
119
120        let span = Span {
121            start: name.span().start,
122            end: if let Some(important) = &important {
123                important.span.end
124            } else if let Some(last) = value.last() {
125                last.span().end
126            } else {
127                colon_span.end
128            },
129        };
130        Ok(Declaration {
131            name,
132            name_suffix,
133            colon_span,
134            value,
135            important,
136            less_property_merge,
137            span,
138        })
139    }
140}
141
142impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ImportantAnnotation<'s> {
143    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
144        let (_, span) = expect!(input, Exclamation);
145        let ident: Ident = input.parse::<Ident>()?;
146        let span = Span {
147            start: span.start,
148            end: ident.span.end,
149        };
150        if ident.name.eq_ignore_ascii_case("important") {
151            Ok(ImportantAnnotation { ident, span })
152        } else {
153            Err(Error {
154                kind: ErrorKind::ExpectImportantAnnotation,
155                span,
156            })
157        }
158    }
159}
160
161impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for QualifiedRule<'s> {
162    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
163        let selector_list = input
164            .with_state(ParserState {
165                qualified_rule_ctx: Some(QualifiedRuleContext::Selector),
166                ..input.state
167            })
168            .parse::<SelectorList>()?;
169        let block = input.parse::<SimpleBlock>()?;
170        let span = Span {
171            start: selector_list.span.start,
172            end: block.span.end,
173        };
174        Ok(QualifiedRule {
175            selector: selector_list,
176            block,
177            span,
178        })
179    }
180}
181
182impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for SimpleBlock<'s> {
183    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
184        let is_sass = input.syntax == Syntax::Sass;
185        let start = if is_sass {
186            if let Some((_, span)) = eat!(input, Indent) {
187                span.end
188            } else {
189                let offset = peek!(input).span.start;
190                return Ok(SimpleBlock {
191                    statements: vec![],
192                    span: Span {
193                        start: offset,
194                        end: offset,
195                    },
196                });
197            }
198        } else {
199            expect!(input, LBrace).1.start
200        };
201
202        let statements = input.parse_statements(/* is_top_level */ false)?;
203
204        if is_sass {
205            match bump!(input) {
206                TokenWithSpan {
207                    token: Token::Dedent(..) | Token::Eof(..),
208                    span,
209                } => {
210                    let end = statements.last().map_or(span.start, |last| last.span().end);
211                    Ok(SimpleBlock {
212                        statements,
213                        span: Span { start, end },
214                    })
215                }
216                TokenWithSpan { span, .. } => Err(Error {
217                    kind: ErrorKind::ExpectDedentOrEof,
218                    span,
219                }),
220            }
221        } else {
222            let end = expect!(input, RBrace).1.end;
223            Ok(SimpleBlock {
224                statements,
225                span: Span { start, end },
226            })
227        }
228    }
229}
230
231impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for Stylesheet<'s> {
232    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
233        let statements = input.parse_statements(/* is_top_level */ true)?;
234        expect!(input, Eof);
235        Ok(Stylesheet {
236            statements,
237            span: Span {
238                start: 0,
239                end: input.source.len(),
240            },
241        })
242    }
243}
244
245impl<'cmt, 's: 'cmt> Parser<'cmt, 's> {
246    fn parse_declaration_value(&mut self) -> PResult<Vec<ComponentValue<'s>>> {
247        let mut values = Vec::with_capacity(3);
248        loop {
249            match &peek!(self).token {
250                Token::RBrace(..)
251                | Token::RParen(..)
252                | Token::Semicolon(..)
253                | Token::Dedent(..)
254                | Token::Linebreak(..)
255                | Token::Exclamation(..)
256                | Token::Eof(..) => break,
257                _ => {
258                    let value = self.parse::<ComponentValue>()?;
259                    match &value {
260                        ComponentValue::SassNestingDeclaration(..)
261                            if matches!(self.syntax, Syntax::Scss | Syntax::Sass) =>
262                        {
263                            values.push(value);
264                            break;
265                        }
266                        _ => values.push(value),
267                    }
268                }
269            }
270        }
271        Ok(values)
272    }
273
274    fn parse_statements(&mut self, is_top_level: bool) -> PResult<Vec<Statement<'s>>> {
275        let mut statements = Vec::with_capacity(1);
276        loop {
277            let mut is_block_element = false;
278            let TokenWithSpan { token, span } = peek!(self);
279            match token {
280                Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
281                    match self.syntax {
282                        Syntax::Css => {
283                            if self.state.in_keyframes_at_rule {
284                                statements.push(Statement::KeyframeBlock(self.parse()?));
285                                is_block_element = true;
286                            } else {
287                                match self.try_parse(QualifiedRule::parse) {
288                                    Ok(rule) => {
289                                        statements.push(Statement::QualifiedRule(rule));
290                                        is_block_element = true;
291                                    }
292                                    Err(error_rule) => match self.parse::<Declaration>() {
293                                        Ok(decl) => {
294                                            if is_top_level {
295                                                self.recoverable_errors.push(Error {
296                                                    kind: ErrorKind::TopLevelDeclaration,
297                                                    span: decl.span.clone(),
298                                                });
299                                            }
300                                            statements.push(Statement::Declaration(decl));
301                                        }
302                                        Err(error_decl) => {
303                                            if is_top_level {
304                                                return Err(error_rule);
305                                            } else {
306                                                return Err(error_decl);
307                                            }
308                                        }
309                                    },
310                                }
311                            }
312                        }
313                        Syntax::Scss | Syntax::Sass => {
314                            if let Ok(sass_var_decl) =
315                                self.try_parse(SassVariableDeclaration::parse)
316                            {
317                                statements.push(Statement::SassVariableDeclaration(sass_var_decl));
318                            } else if self.state.in_keyframes_at_rule {
319                                statements.push(Statement::KeyframeBlock(self.parse()?));
320                                is_block_element = true;
321                            } else {
322                                match self.try_parse(QualifiedRule::parse) {
323                                    Ok(rule) => {
324                                        statements.push(Statement::QualifiedRule(rule));
325                                        is_block_element = true;
326                                    }
327                                    Err(error_rule) => match self.parse::<Declaration>() {
328                                        Ok(decl) => {
329                                            is_block_element = matches!(
330                                                decl.value.last(),
331                                                Some(ComponentValue::SassNestingDeclaration(..))
332                                            );
333                                            if is_top_level {
334                                                self.recoverable_errors.push(Error {
335                                                    kind: ErrorKind::TopLevelDeclaration,
336                                                    span: decl.span.clone(),
337                                                });
338                                            }
339                                            statements.push(Statement::Declaration(decl));
340                                        }
341                                        Err(error_decl) => {
342                                            if is_top_level {
343                                                return Err(error_rule);
344                                            } else {
345                                                return Err(error_decl);
346                                            }
347                                        }
348                                    },
349                                }
350                            }
351                        }
352                        Syntax::Less => {
353                            if let Ok(stmt) = self.try_parse(Parser::parse_less_qualified_rule) {
354                                statements.push(stmt);
355                                is_block_element = true;
356                            } else if let Ok(decl) = self.try_parse(|parser| {
357                                if is_top_level {
358                                    Err(Error {
359                                        kind: ErrorKind::TryParseError,
360                                        span: bump!(parser).span,
361                                    })
362                                } else {
363                                    parser.parse()
364                                }
365                            }) {
366                                statements.push(Statement::Declaration(decl));
367                            } else if self.state.in_keyframes_at_rule {
368                                statements.push(Statement::KeyframeBlock(self.parse()?));
369                                is_block_element = true;
370                            } else {
371                                let fn_call = self.parse::<Function>()?;
372                                is_block_element = matches!(
373                                    fn_call.args.last(),
374                                    Some(ComponentValue::LessDetachedRuleset(..))
375                                );
376                                statements.push(Statement::LessFunctionCall(fn_call));
377                            }
378                        }
379                    }
380                }
381                Token::Dot(..) | Token::Hash(..) if self.syntax == Syntax::Less => {
382                    let stmt = if let Ok(stmt) = self.try_parse(Parser::parse_less_qualified_rule) {
383                        is_block_element = true;
384                        stmt
385                    } else if let Ok(mixin_def) = self.try_parse(LessMixinDefinition::parse) {
386                        is_block_element = true;
387                        Statement::LessMixinDefinition(mixin_def)
388                    } else {
389                        self.parse().map(Statement::LessMixinCall)?
390                    };
391                    statements.push(stmt);
392                }
393                Token::Dot(..) | Token::Hash(..) if !self.state.in_keyframes_at_rule => {
394                    statements.push(Statement::QualifiedRule(self.parse()?));
395                    is_block_element = true;
396                }
397                Token::Ampersand(..)
398                | Token::LBracket(..)
399                | Token::Colon(..)
400                | Token::ColonColon(..)
401                | Token::Asterisk(..)
402                | Token::Bar(..)
403                | Token::NumberSign(..)
404                    if !self.state.in_keyframes_at_rule =>
405                {
406                    if self.syntax == Syntax::Less {
407                        if let Ok(extend_rule) = self.try_parse(LessExtendRule::parse) {
408                            statements.push(Statement::LessExtendRule(extend_rule));
409                        } else {
410                            statements.push(self.parse_less_qualified_rule()?);
411                            is_block_element = true;
412                        }
413                    } else {
414                        statements.push(Statement::QualifiedRule(self.parse()?));
415                        is_block_element = true;
416                    }
417                }
418                Token::AtKeyword(at_keyword) => match self.syntax {
419                    Syntax::Css => {
420                        let at_rule = self.parse::<AtRule>()?;
421                        is_block_element = at_rule.block.is_some();
422                        statements.push(Statement::AtRule(at_rule));
423                    }
424                    Syntax::Scss | Syntax::Sass => {
425                        let at_keyword_name = at_keyword.ident.name();
426                        match &*at_keyword_name {
427                            "if" => {
428                                statements.push(Statement::SassIfAtRule(self.parse()?));
429                                is_block_element = true;
430                            }
431                            "else" => {
432                                return Err(Error {
433                                    kind: ErrorKind::UnexpectedSassElseAtRule,
434                                    span: bump!(self).span,
435                                });
436                            }
437                            _ => {
438                                let at_rule = self.parse::<AtRule>()?;
439                                is_block_element = at_rule.block.is_some();
440                                statements.push(Statement::AtRule(at_rule));
441                            }
442                        }
443                    }
444                    Syntax::Less => {
445                        if let Ok(less_variable_declaration) =
446                            self.try_parse(LessVariableDeclaration::parse)
447                        {
448                            is_block_element = matches!(
449                                less_variable_declaration.value,
450                                ComponentValue::LessDetachedRuleset(..)
451                            );
452                            statements.push(Statement::LessVariableDeclaration(
453                                less_variable_declaration,
454                            ));
455                        } else if let Ok(variable_call) = self.try_parse(LessVariableCall::parse) {
456                            statements.push(Statement::LessVariableCall(variable_call));
457                        } else {
458                            let at_rule = self.parse::<AtRule>()?;
459                            is_block_element = at_rule.block.is_some();
460                            statements.push(Statement::AtRule(at_rule));
461                        }
462                    }
463                },
464                Token::Percent(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
465                    statements.push(Statement::QualifiedRule(self.parse()?));
466                    is_block_element = true;
467                }
468                Token::DollarVar(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
469                    statements.push(Statement::SassVariableDeclaration(self.parse()?));
470                }
471                Token::GreaterThan(..) | Token::Plus(..) | Token::Tilde(..) | Token::BarBar(..) => {
472                    if self.syntax == Syntax::Less {
473                        statements.push(self.parse_less_qualified_rule()?);
474                    } else {
475                        statements.push(Statement::QualifiedRule(self.parse()?));
476                    }
477                    is_block_element = true;
478                }
479                Token::DollarLBraceVar(..) if self.syntax == Syntax::Less => {
480                    statements.push(self.parse().map(Statement::Declaration)?);
481                }
482                Token::Cdo(..) | Token::Cdc(..) => {
483                    bump!(self);
484                    continue;
485                }
486                Token::At(..) if matches!(self.syntax, Syntax::Scss | Syntax::Sass) => {
487                    let unknown_sass_at_rule = self.parse::<UnknownSassAtRule>()?;
488                    is_block_element = unknown_sass_at_rule.block.is_some();
489                    statements.push(Statement::UnknownSassAtRule(Box::new(unknown_sass_at_rule)));
490                }
491                Token::Percentage(..)
492                    if self.state.in_keyframes_at_rule
493                        || self.state.sass_ctx & super::state::SASS_CTX_ALLOW_KEYFRAME_BLOCK
494                            != 0
495                        || self.state.less_ctx & super::state::LESS_CTX_ALLOW_KEYFRAME_BLOCK
496                            != 0 =>
497                {
498                    statements.push(Statement::KeyframeBlock(self.parse()?));
499                    is_block_element = true;
500                }
501                Token::RBrace(..) | Token::Eof(..) | Token::Dedent(..) => break,
502                Token::Semicolon(..) | Token::Linebreak(..) => {
503                    bump!(self);
504                    continue;
505                }
506                _ => {
507                    return Err(Error {
508                        kind: if self.state.in_keyframes_at_rule {
509                            ErrorKind::ExpectKeyframeBlock
510                        } else {
511                            ErrorKind::ExpectRule
512                        },
513                        span: span.clone(),
514                    });
515                }
516            };
517            match &peek!(self).token {
518                Token::RBrace(..) | Token::Eof(..) | Token::Dedent(..) => break,
519                _ => {
520                    if self.syntax == Syntax::Sass {
521                        if is_block_element {
522                            eat!(self, Linebreak);
523                        } else if self.options.tolerate_semicolon_in_sass {
524                            if let Some((_, span)) = eat!(self, Semicolon) {
525                                self.recoverable_errors.push(Error {
526                                    kind: ErrorKind::UnexpectedSemicolonInSass,
527                                    span,
528                                });
529                            }
530                        } else {
531                            expect!(self, Linebreak);
532                        }
533                    } else if is_block_element {
534                        eat!(self, Semicolon);
535                    } else {
536                        expect!(self, Semicolon);
537                    }
538                }
539            }
540        }
541        Ok(statements)
542    }
543}