1use self::state::ParserState;
2use crate::{
3 ParserOptions,
4 ast::{
5 Dimension, Ident, InterpolableIdentStaticPart, InterpolableStrStaticPart,
6 InterpolableUrlStaticPart, Str,
7 },
8 config::Syntax,
9 error::{Error, ErrorKind, PResult},
10 pos::Span,
11 tokenizer::{Token, TokenWithSpan, Tokenizer, token},
12 util,
13};
14pub use builder::ParserBuilder;
15use oxc_allocator::{Allocator, Vec as ArenaVec};
16
17mod at_rule;
18mod builder;
19mod convert;
20mod less;
21mod postcss_simple_vars;
22mod sass;
23mod selector;
24mod state;
25mod stmt;
26mod token_seq;
27mod value;
28
29pub trait Parse<'a>: Sized {
30 fn parse(input: &mut Parser<'a>) -> PResult<Self>;
31}
32
33pub(in crate::parser) struct ParserCursor<'a> {
34 tokenizer: Tokenizer<'a>,
35 cached_token: Option<TokenWithSpan<'a>>,
36}
37
38impl<'a> ParserCursor<'a> {
39 #[inline]
40 fn new(tokenizer: Tokenizer<'a>) -> Self {
41 Self { tokenizer, cached_token: None }
42 }
43
44 #[inline]
45 fn bump(&mut self) -> PResult<TokenWithSpan<'a>> {
46 match self.cached_token.take() {
47 Some(token_with_span) => Ok(token_with_span),
48 None => self.tokenizer.bump(),
49 }
50 }
51
52 #[inline]
53 fn peek(&mut self) -> PResult<&TokenWithSpan<'a>> {
54 if self.cached_token.is_none() {
55 let token = self.tokenizer.bump()?;
56 self.cached_token = Some(token);
57 }
58 match self.cached_token.as_ref() {
59 Some(token_with_span) => Ok(token_with_span),
60 None => unreachable!(),
61 }
62 }
63
64 #[inline]
65 fn eat_token<T>(
66 &mut self,
67 extract: impl FnOnce(Token<'a>) -> Result<T, Token<'a>>,
68 ) -> PResult<Option<(T, Span)>> {
69 let TokenWithSpan { token, span } = self.bump()?;
70 match extract(token) {
71 Ok(token) => Ok(Some((token, span))),
72 Err(token) => {
73 self.cached_token = Some(TokenWithSpan { token, span });
74 Ok(None)
75 }
76 }
77 }
78
79 #[inline]
80 fn expect_token<T>(
81 &mut self,
82 expected: &'static str,
83 extract: impl FnOnce(Token<'a>) -> Result<T, Token<'a>>,
84 ) -> PResult<(T, Span)> {
85 let TokenWithSpan { token, span } = self.bump()?;
86 match extract(token) {
87 Ok(token) => Ok((token, span)),
88 Err(token) => {
89 Err(Error { kind: ErrorKind::Unexpected(expected, token.symbol()), span })
90 }
91 }
92 }
93
94 #[inline]
95 fn expect_ampersand(&mut self) -> PResult<(token::Ampersand, Span)> {
96 self.expect_token("&", |token| match token {
97 Token::Ampersand(token) => Ok(token),
98 token => Err(token),
99 })
100 }
101
102 #[inline]
103 fn expect_at(&mut self) -> PResult<(token::At, Span)> {
104 self.expect_token("@", |token| match token {
105 Token::At(token) => Ok(token),
106 token => Err(token),
107 })
108 }
109
110 #[inline]
111 fn expect_at_keyword(&mut self) -> PResult<(token::AtKeyword<'a>, Span)> {
112 self.expect_token("<at-keyword>", |token| match token {
113 Token::AtKeyword(token) => Ok(token),
114 token => Err(token),
115 })
116 }
117
118 #[inline]
119 fn expect_at_l_brace_var(&mut self) -> PResult<(token::AtLBraceVar<'a>, Span)> {
120 self.expect_token("@{", |token| match token {
121 Token::AtLBraceVar(token) => Ok(token),
122 token => Err(token),
123 })
124 }
125
126 #[inline]
127 fn expect_backtick_code(&mut self) -> PResult<(token::BacktickCode<'a>, Span)> {
128 self.expect_token("<backtick code>", |token| match token {
129 Token::BacktickCode(token) => Ok(token),
130 token => Err(token),
131 })
132 }
133
134 #[inline]
135 fn expect_bar(&mut self) -> PResult<(token::Bar, Span)> {
136 self.expect_token("|", |token| match token {
137 Token::Bar(token) => Ok(token),
138 token => Err(token),
139 })
140 }
141
142 #[inline]
143 fn expect_colon(&mut self) -> PResult<(token::Colon, Span)> {
144 self.expect_token(":", |token| match token {
145 Token::Colon(token) => Ok(token),
146 token => Err(token),
147 })
148 }
149
150 #[inline]
151 fn expect_colon_colon(&mut self) -> PResult<(token::ColonColon, Span)> {
152 self.expect_token("::", |token| match token {
153 Token::ColonColon(token) => Ok(token),
154 token => Err(token),
155 })
156 }
157
158 #[inline]
159 fn expect_comma(&mut self) -> PResult<(token::Comma, Span)> {
160 self.expect_token(",", |token| match token {
161 Token::Comma(token) => Ok(token),
162 token => Err(token),
163 })
164 }
165
166 #[inline]
167 fn expect_dimension(&mut self) -> PResult<(token::Dimension<'a>, Span)> {
168 self.expect_token("<dimension>", |token| match token {
169 Token::Dimension(token) => Ok(token),
170 token => Err(token),
171 })
172 }
173
174 #[inline]
175 fn expect_dollar_l_brace_var(&mut self) -> PResult<(token::DollarLBraceVar<'a>, Span)> {
176 self.expect_token("${", |token| match token {
177 Token::DollarLBraceVar(token) => Ok(token),
178 token => Err(token),
179 })
180 }
181
182 #[inline]
183 fn expect_dollar_var(&mut self) -> PResult<(token::DollarVar<'a>, Span)> {
184 self.expect_token("$var", |token| match token {
185 Token::DollarVar(token) => Ok(token),
186 token => Err(token),
187 })
188 }
189
190 #[inline]
191 fn expect_dot(&mut self) -> PResult<(token::Dot, Span)> {
192 self.expect_token(".", |token| match token {
193 Token::Dot(token) => Ok(token),
194 token => Err(token),
195 })
196 }
197
198 #[inline]
199 fn expect_eof(&mut self) -> PResult<(token::Eof, Span)> {
200 self.expect_token("<eof>", |token| match token {
201 Token::Eof(token) => Ok(token),
202 token => Err(token),
203 })
204 }
205
206 #[inline]
207 fn expect_exclamation(&mut self) -> PResult<(token::Exclamation, Span)> {
208 self.expect_token("!", |token| match token {
209 Token::Exclamation(token) => Ok(token),
210 token => Err(token),
211 })
212 }
213
214 #[inline]
215 fn expect_hash(&mut self) -> PResult<(token::Hash<'a>, Span)> {
216 self.expect_token("<hash>", |token| match token {
217 Token::Hash(token) => Ok(token),
218 token => Err(token),
219 })
220 }
221
222 #[inline]
223 fn expect_hash_l_brace(&mut self) -> PResult<(token::HashLBrace, Span)> {
224 self.expect_token("#{", |token| match token {
225 Token::HashLBrace(token) => Ok(token),
226 token => Err(token),
227 })
228 }
229
230 #[inline]
231 fn expect_ident(&mut self) -> PResult<(token::Ident<'a>, Span)> {
232 self.expect_token("<ident>", |token| match token {
233 Token::Ident(token) => Ok(token),
234 token => Err(token),
235 })
236 }
237
238 #[inline]
239 fn expect_l_brace(&mut self) -> PResult<(token::LBrace, Span)> {
240 self.expect_token("{", |token| match token {
241 Token::LBrace(token) => Ok(token),
242 token => Err(token),
243 })
244 }
245
246 #[inline]
247 fn expect_l_bracket(&mut self) -> PResult<(token::LBracket, Span)> {
248 self.expect_token("[", |token| match token {
249 Token::LBracket(token) => Ok(token),
250 token => Err(token),
251 })
252 }
253
254 #[inline]
255 fn expect_linebreak(&mut self) -> PResult<(token::Linebreak, Span)> {
256 self.expect_token("<linebreak>", |token| match token {
257 Token::Linebreak(token) => Ok(token),
258 token => Err(token),
259 })
260 }
261
262 #[inline]
263 fn expect_l_paren(&mut self) -> PResult<(token::LParen, Span)> {
264 self.expect_token("(", |token| match token {
265 Token::LParen(token) => Ok(token),
266 token => Err(token),
267 })
268 }
269
270 #[inline]
271 fn expect_minus(&mut self) -> PResult<(token::Minus, Span)> {
272 self.expect_token("-", |token| match token {
273 Token::Minus(token) => Ok(token),
274 token => Err(token),
275 })
276 }
277
278 #[inline]
279 fn expect_number(&mut self) -> PResult<(token::Number<'a>, Span)> {
280 self.expect_token("<number>", |token| match token {
281 Token::Number(token) => Ok(token),
282 token => Err(token),
283 })
284 }
285
286 #[inline]
287 fn expect_percent(&mut self) -> PResult<(token::Percent, Span)> {
288 self.expect_token("%", |token| match token {
289 Token::Percent(token) => Ok(token),
290 token => Err(token),
291 })
292 }
293
294 #[inline]
295 fn expect_percentage(&mut self) -> PResult<(token::Percentage<'a>, Span)> {
296 self.expect_token("<percentage>", |token| match token {
297 Token::Percentage(token) => Ok(token),
298 token => Err(token),
299 })
300 }
301
302 #[inline]
303 fn expect_placeholder(&mut self) -> PResult<(token::Placeholder<'a>, Span)> {
304 self.expect_token("<placeholder>", |token| match token {
305 Token::Placeholder(token) => Ok(token),
306 token => Err(token),
307 })
308 }
309
310 #[inline]
311 fn expect_r_brace(&mut self) -> PResult<(token::RBrace, Span)> {
312 self.expect_token("}", |token| match token {
313 Token::RBrace(token) => Ok(token),
314 token => Err(token),
315 })
316 }
317
318 #[inline]
319 fn expect_r_bracket(&mut self) -> PResult<(token::RBracket, Span)> {
320 self.expect_token("]", |token| match token {
321 Token::RBracket(token) => Ok(token),
322 token => Err(token),
323 })
324 }
325
326 #[inline]
327 fn expect_r_paren(&mut self) -> PResult<(token::RParen, Span)> {
328 self.expect_token(")", |token| match token {
329 Token::RParen(token) => Ok(token),
330 token => Err(token),
331 })
332 }
333
334 #[inline]
335 fn expect_semicolon(&mut self) -> PResult<(token::Semicolon, Span)> {
336 self.expect_token(";", |token| match token {
337 Token::Semicolon(token) => Ok(token),
338 token => Err(token),
339 })
340 }
341
342 #[inline]
343 fn expect_solidus(&mut self) -> PResult<(token::Solidus, Span)> {
344 self.expect_token("/", |token| match token {
345 Token::Solidus(token) => Ok(token),
346 token => Err(token),
347 })
348 }
349
350 #[inline]
351 fn expect_str(&mut self) -> PResult<(token::Str<'a>, Span)> {
352 self.expect_token("<string>", |token| match token {
353 Token::Str(token) => Ok(token),
354 token => Err(token),
355 })
356 }
357
358 #[inline]
359 fn expect_str_template(&mut self) -> PResult<(token::StrTemplate<'a>, Span)> {
360 self.expect_token("<string template>", |token| match token {
361 Token::StrTemplate(token) => Ok(token),
362 token => Err(token),
363 })
364 }
365
366 #[inline]
367 fn expect_tilde(&mut self) -> PResult<(token::Tilde, Span)> {
368 self.expect_token("~", |token| match token {
369 Token::Tilde(token) => Ok(token),
370 token => Err(token),
371 })
372 }
373
374 #[inline]
375 fn expect_without_ws_or_comments<T>(
376 &mut self,
377 expected: &'static str,
378 extract: impl FnOnce(Token<'a>) -> Result<T, Token<'a>>,
379 ) -> PResult<(T, Span)> {
380 debug_assert!(self.cached_token.is_none());
381 let TokenWithSpan { token, span } = self.tokenizer.bump_without_ws_or_comments()?;
382 match extract(token) {
383 Ok(token) => Ok((token, span)),
384 Err(token) => {
385 Err(Error { kind: ErrorKind::Unexpected(expected, token.symbol()), span })
386 }
387 }
388 }
389
390 #[inline]
391 fn expect_ident_without_ws_or_comments(
392 &mut self,
393 allow_leading_digit: bool,
394 ) -> PResult<(token::Ident<'a>, Span)> {
395 debug_assert!(self.cached_token.is_none());
396 if self.tokenizer.is_start_of_ident()
397 || (allow_leading_digit && self.tokenizer.is_start_of_digit())
398 {
399 self.tokenizer.scan_ident_sequence(allow_leading_digit)
400 } else {
401 let TokenWithSpan { token, span } = self.tokenizer.bump_without_ws_or_comments()?;
402 Err(Error { kind: ErrorKind::Unexpected("<ident>", token.symbol()), span })
403 }
404 }
405
406 #[inline]
407 fn expect_asterisk_without_ws_or_comments(&mut self) -> PResult<(token::Asterisk, Span)> {
408 self.expect_without_ws_or_comments("*", |token| match token {
409 Token::Asterisk(token) => Ok(token),
410 token => Err(token),
411 })
412 }
413
414 #[inline]
415 fn expect_l_paren_without_ws_or_comments(&mut self) -> PResult<(token::LParen, Span)> {
416 self.expect_without_ws_or_comments("(", |token| match token {
417 Token::LParen(token) => Ok(token),
418 token => Err(token),
419 })
420 }
421
422 #[inline]
423 fn expect_solidus_without_ws_or_comments(&mut self) -> PResult<(token::Solidus, Span)> {
424 self.expect_without_ws_or_comments("/", |token| match token {
425 Token::Solidus(token) => Ok(token),
426 token => Err(token),
427 })
428 }
429
430 #[inline]
431 fn eat_bar(&mut self) -> PResult<Option<(token::Bar, Span)>> {
432 self.eat_token(|token| match token {
433 Token::Bar(token) => Ok(token),
434 token => Err(token),
435 })
436 }
437
438 #[inline]
439 fn eat_colon(&mut self) -> PResult<Option<(token::Colon, Span)>> {
440 self.eat_token(|token| match token {
441 Token::Colon(token) => Ok(token),
442 token => Err(token),
443 })
444 }
445
446 #[inline]
447 fn eat_comma(&mut self) -> PResult<Option<(token::Comma, Span)>> {
448 self.eat_token(|token| match token {
449 Token::Comma(token) => Ok(token),
450 token => Err(token),
451 })
452 }
453
454 #[inline]
455 fn eat_dot_dot_dot(&mut self) -> PResult<Option<(token::DotDotDot, Span)>> {
456 self.eat_token(|token| match token {
457 Token::DotDotDot(token) => Ok(token),
458 token => Err(token),
459 })
460 }
461
462 #[inline]
463 fn eat_exclamation(&mut self) -> PResult<Option<(token::Exclamation, Span)>> {
464 self.eat_token(|token| match token {
465 Token::Exclamation(token) => Ok(token),
466 token => Err(token),
467 })
468 }
469
470 #[inline]
471 fn eat_greater_than(&mut self) -> PResult<Option<(token::GreaterThan, Span)>> {
472 self.eat_token(|token| match token {
473 Token::GreaterThan(token) => Ok(token),
474 token => Err(token),
475 })
476 }
477
478 #[inline]
479 fn eat_ident(&mut self) -> PResult<Option<(token::Ident<'a>, Span)>> {
480 self.eat_token(|token| match token {
481 Token::Ident(token) => Ok(token),
482 token => Err(token),
483 })
484 }
485
486 #[inline]
487 fn eat_indent(&mut self) -> PResult<Option<(token::Indent, Span)>> {
488 self.eat_token(|token| match token {
489 Token::Indent(token) => Ok(token),
490 token => Err(token),
491 })
492 }
493
494 #[inline]
495 fn eat_linebreak(&mut self) -> PResult<Option<(token::Linebreak, Span)>> {
496 self.eat_token(|token| match token {
497 Token::Linebreak(token) => Ok(token),
498 token => Err(token),
499 })
500 }
501
502 #[inline]
503 fn eat_l_paren(&mut self) -> PResult<Option<(token::LParen, Span)>> {
504 self.eat_token(|token| match token {
505 Token::LParen(token) => Ok(token),
506 token => Err(token),
507 })
508 }
509
510 #[inline]
511 fn eat_r_paren(&mut self) -> PResult<Option<(token::RParen, Span)>> {
512 self.eat_token(|token| match token {
513 Token::RParen(token) => Ok(token),
514 token => Err(token),
515 })
516 }
517
518 #[inline]
519 fn eat_semicolon(&mut self) -> PResult<Option<(token::Semicolon, Span)>> {
520 self.eat_token(|token| match token {
521 Token::Semicolon(token) => Ok(token),
522 token => Err(token),
523 })
524 }
525
526 #[inline]
527 fn eat_tilde(&mut self) -> PResult<Option<(token::Tilde, Span)>> {
528 self.eat_token(|token| match token {
529 Token::Tilde(token) => Ok(token),
530 token => Err(token),
531 })
532 }
533}
534
535pub struct Parser<'a> {
537 allocator: &'a Allocator,
538 source: &'a str,
539 syntax: Syntax,
540 options: ParserOptions,
541 cursor: ParserCursor<'a>,
542 state: ParserState,
543 recoverable_errors: Vec<Error>,
544 sass_pending_indents: u32,
550}
551
552impl<'a> Parser<'a> {
553 pub fn new(allocator: &'a Allocator, source: &'a str, syntax: Syntax) -> Self {
556 let source = source.strip_prefix('\u{feff}').unwrap_or(source);
557 Parser {
558 allocator,
559 source,
560 syntax,
561 options: Default::default(),
562 cursor: ParserCursor::new(Tokenizer::new(allocator, source, syntax, None, false)),
563 state: Default::default(),
564 recoverable_errors: vec![],
565 sass_pending_indents: 0,
566 }
567 }
568
569 pub fn parse<T>(&mut self) -> PResult<T>
571 where
572 T: Parse<'a>,
573 {
574 T::parse(self)
575 }
576
577 #[inline]
579 pub fn recoverable_errors(&self) -> &[Error] {
580 &self.recoverable_errors
581 }
582
583 #[inline]
585 pub fn comments(&self) -> &[crate::tokenizer::token::Comment<'a>] {
586 self.cursor.tokenizer.comments()
587 }
588
589 #[inline]
590 pub(crate) fn alloc<T>(&self, value: T) -> oxc_allocator::Box<'a, T> {
591 oxc_allocator::Box::new_in(value, &self.allocator)
592 }
593
594 #[inline]
595 pub(crate) fn vec<T>(&self) -> ArenaVec<'a, T> {
596 ArenaVec::new_in(&self.allocator)
597 }
598
599 #[inline]
600 pub(crate) fn vec1<T>(&self, value: T) -> ArenaVec<'a, T> {
601 let mut vec = self.vec_with_capacity(1);
602 vec.push(value);
603 vec
604 }
605
606 #[inline]
607 pub(crate) fn vec_with_capacity<T>(&self, capacity: usize) -> ArenaVec<'a, T> {
608 ArenaVec::with_capacity_in(capacity, &self.allocator)
609 }
610
611 #[inline]
612 pub(crate) fn ident(&self, token: token::Ident<'a>, span: crate::Span) -> Ident<'a> {
613 Ident { name: self.ident_name(&token), raw: token.raw, span }
614 }
615
616 pub(super) fn parse_dollar_var_ident(&mut self) -> PResult<(Ident<'a>, Span)> {
619 let (dollar_var, span) = self.cursor.expect_dollar_var()?;
620 let name = self.ident(dollar_var.ident, Span { start: span.start + 1, end: span.end });
621 Ok((name, span))
622 }
623
624 pub(crate) fn dimension(
625 &self,
626 token: token::Dimension<'a>,
627 span: crate::Span,
628 ) -> PResult<Dimension<'a>> {
629 let value_span = crate::Span { start: span.start, end: span.start + token.value.raw.len() };
630 let unit_span = crate::Span { start: span.start + token.value.raw.len(), end: span.end };
631 let value = (token.value, value_span).try_into()?;
632 let unit = self.ident(token.unit, unit_span);
633 let kind = convert::dimension_kind(unit.name);
634 Ok(Dimension { value, unit, kind, span })
635 }
636
637 #[inline]
638 pub(crate) fn interpolable_ident_static_part(
639 &self,
640 token: token::Ident<'a>,
641 span: crate::Span,
642 ) -> InterpolableIdentStaticPart<'a> {
643 InterpolableIdentStaticPart { value: self.ident_name(&token), raw: token.raw, span }
644 }
645
646 #[inline]
647 pub(crate) fn str(&self, token: token::Str<'a>, span: crate::Span) -> Str<'a> {
648 let raw_without_quotes = unsafe { token.raw.get_unchecked(1..token.raw.len() - 1) };
649 let value = if token.escaped {
650 util::handle_escape_in(raw_without_quotes, self.allocator)
651 } else {
652 raw_without_quotes
653 };
654 Str { value, raw: token.raw, span }
655 }
656
657 #[inline]
658 pub(crate) fn interpolable_str_static_part(
659 &self,
660 token: token::StrTemplate<'a>,
661 span: crate::Span,
662 ) -> InterpolableStrStaticPart<'a> {
663 let raw_without_quotes = if token.tail {
664 unsafe { token.raw.get_unchecked(0..token.raw.len() - 1) }
665 } else if token.head {
666 unsafe { token.raw.get_unchecked(1..token.raw.len()) }
667 } else {
668 token.raw
669 };
670 let value = if token.escaped {
671 util::handle_escape_in(raw_without_quotes, self.allocator)
672 } else {
673 raw_without_quotes
674 };
675 InterpolableStrStaticPart { value, raw: token.raw, span }
676 }
677
678 #[inline]
679 pub(crate) fn interpolable_url_static_part(
680 &self,
681 token: token::UrlTemplate<'a>,
682 span: crate::Span,
683 ) -> InterpolableUrlStaticPart<'a> {
684 let value = if token.escaped {
685 util::handle_escape_in(token.raw, self.allocator)
686 } else {
687 token.raw
688 };
689 InterpolableUrlStaticPart { value, raw: token.raw, span }
690 }
691
692 #[inline]
693 fn ident_name(&self, token: &token::Ident<'a>) -> &'a str {
694 if token.escaped { util::handle_escape_in(token.raw, self.allocator) } else { token.raw }
695 }
696
697 fn try_parse<R, F: FnOnce(&mut Self) -> PResult<R>>(&mut self, f: F) -> PResult<R> {
698 let tokenizer_state = self.cursor.tokenizer.state.clone();
699 let comments_count = self.cursor.tokenizer.comments_count();
700 let recoverable_errors_count = self.recoverable_errors.len();
701 let cached_token = self.cursor.cached_token.clone();
702 let sass_pending_indents = self.sass_pending_indents;
703 let result = f(self);
704 if result.is_err() {
705 self.cursor.tokenizer.state = tokenizer_state;
706 self.cursor.tokenizer.truncate_comments(comments_count);
707 self.recoverable_errors.truncate(recoverable_errors_count);
708 self.cursor.cached_token = cached_token;
709 self.sass_pending_indents = sass_pending_indents;
710 }
711 result
712 }
713}