Skip to main content

oxc_css_parser/parser/
mod.rs

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
535/// Create a parser with some source code, then parse it.
536pub 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    /// Indented syntax only: `Indent` tokens consumed as mid-statement line
545    /// continuations (`@for $i\n  from 1...`). The statement's own block then
546    /// starts "virtually" at that depth (see [`SimpleBlock`]'s parse), and any
547    /// unconsumed levels are drained against `Dedent` tokens after the
548    /// statement.
549    sass_pending_indents: u32,
550}
551
552impl<'a> Parser<'a> {
553    /// Create a parser with the given source code and specified syntax.
554    /// If you need to control more options, please use [`ParserBuilder`].
555    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    /// Start to parse.
570    pub fn parse<T>(&mut self) -> PResult<T>
571    where
572        T: Parse<'a>,
573    {
574        T::parse(self)
575    }
576
577    /// Retrieve recoverable errors.
578    #[inline]
579    pub fn recoverable_errors(&self) -> &[Error] {
580        &self.recoverable_errors
581    }
582
583    /// Retrieve collected comments.
584    #[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    // A `$`-prefixed variable name: '$' <ident>  (Sass variables, Less property
617    // accessors, postcss-simple-vars).
618    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}