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