raffia/parser/
stmt.rs

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