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