Skip to main content

oxc_parser/
config.rs

1// All methods are `#[inline(always)]` to ensure compiler removes dead code resulting from static values
2#![expect(clippy::inline_always)]
3
4use std::ops::Index;
5
6use crate::lexer::{ByteHandler, ByteHandlers, byte_handler_tables};
7
8/// Parser config.
9///
10/// The purpose of parser config (as opposed to `ParseOptions`) is to allow setting options at either
11/// compile time or runtime.
12///
13/// 3 configs are provided:
14/// * [`NoTokensParserConfig`]: Parse without tokens, static (default)
15/// * [`TokensParserConfig`]: Parse with tokens, static
16/// * [`RuntimeParserConfig`]: Parse with or without tokens, decided at runtime
17///
18/// The trade-off is:
19///
20/// * The 2 static configs will produce better performance, because compiler can remove code that relates
21///   to the other option as dead code, and remove branches.
22///
23/// * The runtime config will produce a smaller binary than using 2 different configs in the same application,
24///   which would cause 2 polymorphic variants of the parser to be compiled.
25///
26/// Advised usage:
27/// * If your application uses only a specific set of options, use a static config.
28/// * If your application uses multiple sets of options, probably a runtime config is preferable.
29///
30/// At present the only option controlled by `ParserConfig` is whether to parse with or without tokens.
31/// Other options will be added in future.
32///
33/// You can also create your own config by implementing [`ParserConfig`] on a type.
34///
35/// Implementors must be `'static` because `Parser`'s entry points dispatch via [`Any`]
36/// to non-generic helpers for the known configs. This avoids per-consuming-crate
37/// monomorphization of the parser body - see comments on [`Parser::parse`].
38///
39/// [`Any`]: std::any::Any
40/// [`Parser::parse`]: crate::Parser::parse
41pub trait ParserConfig: Default + 'static {
42    /// Type of [`LexerConfig`] this [`ParserConfig`] uses.
43    type LexerConfig: LexerConfig;
44
45    /// Get [`LexerConfig`] for this [`ParserConfig`].
46    fn lexer_config(&self) -> Self::LexerConfig;
47
48    /// Whether a host-embedded markup grammar may be registered (see `parse_with_markup`).
49    /// Every markup hook site is compiled out when this is `false`.
50    const MARKUP: bool = false;
51}
52
53/// Parser config for [`crate::Parser::parse_with_markup`]: no tokens, markup hooks compiled in.
54#[derive(Copy, Clone, Default)]
55pub(crate) struct MarkupParserConfig;
56
57impl ParserConfig for MarkupParserConfig {
58    type LexerConfig = NoTokensLexerConfig;
59
60    const MARKUP: bool = true;
61
62    #[inline(always)]
63    fn lexer_config(&self) -> NoTokensLexerConfig {
64        NoTokensLexerConfig
65    }
66}
67
68/// Parser config for parsing without tokens (default).
69///
70/// See [`ParserConfig`] for more details.
71#[derive(Copy, Clone, Default)]
72pub struct NoTokensParserConfig;
73
74impl ParserConfig for NoTokensParserConfig {
75    type LexerConfig = NoTokensLexerConfig;
76
77    #[inline(always)]
78    fn lexer_config(&self) -> NoTokensLexerConfig {
79        NoTokensLexerConfig
80    }
81}
82
83/// Parser config for parsing with tokens.
84///
85/// See [`ParserConfig`] for more details.
86#[derive(Copy, Clone, Default)]
87pub struct TokensParserConfig;
88
89impl ParserConfig for TokensParserConfig {
90    type LexerConfig = TokensLexerConfig;
91
92    #[inline(always)]
93    fn lexer_config(&self) -> TokensLexerConfig {
94        TokensLexerConfig
95    }
96}
97
98/// Parser config for parsing with / without tokens, decided at runtime.
99///
100/// See [`ParserConfig`] for more details.
101#[derive(Copy, Clone, Default)]
102#[repr(transparent)]
103pub struct RuntimeParserConfig {
104    lexer_config: RuntimeLexerConfig,
105}
106
107impl RuntimeParserConfig {
108    #[inline(always)]
109    pub fn new(tokens: bool) -> Self {
110        Self { lexer_config: RuntimeLexerConfig::new(tokens) }
111    }
112}
113
114impl ParserConfig for RuntimeParserConfig {
115    type LexerConfig = RuntimeLexerConfig;
116
117    #[inline(always)]
118    fn lexer_config(&self) -> RuntimeLexerConfig {
119        self.lexer_config
120    }
121}
122
123/// Lexer config.
124///
125/// See [`ParserConfig`] for more details.
126///
127/// We have to define a different byte handler table for each config, as byte handler functions
128/// are generic over the [`LexerConfig`].
129///
130/// Byte handler tables are defined within lexer (`lexer/byte_handlers.rs`).
131/// We need them to be defined as `static` items, for performance.
132/// 1. `static` ensures that only 1 copy of each byte handler table appears in the binary
133///    (even if `Lexer::handle_byte` is inlined in multiple places).
134/// 2. Indexing into byte handlers table is an extremely hot path, and even 1 extra indirection (pointer chasing)
135///    has a sizeable impact on performance.
136///
137/// An associated type and `byte_handlers` method is the only way to make this work with Rust's type system
138/// and borrow checker.
139pub trait LexerConfig: Default {
140    /// Byte handlers table type.
141    type ByteHandlers: Index<usize, Output = ByteHandler<Self>>;
142
143    /// `true` if [`tokens`] method returns a static value.
144    ///
145    /// This const is useful in situations where code can be more efficient
146    /// when the return value of [`tokens`] method is known at compile time.
147    ///
148    /// Implementors of this trait MUST only set this const to `true` if [`tokens`] method returns a static value.
149    ///
150    /// [`tokens`]: LexerConfig::tokens
151    const TOKENS_METHOD_IS_STATIC: bool;
152
153    /// Returns `true` if tokens are enabled.
154    fn tokens(&self) -> bool;
155
156    /// Get byte handlers table to use in lexer with this config.
157    fn byte_handlers(&self) -> &Self::ByteHandlers;
158}
159
160/// Lexer config for lexing without tokens.
161#[derive(Copy, Clone, Default)]
162pub struct NoTokensLexerConfig;
163
164impl LexerConfig for NoTokensLexerConfig {
165    type ByteHandlers = ByteHandlers<Self>;
166
167    const TOKENS_METHOD_IS_STATIC: bool = true;
168
169    #[inline(always)]
170    fn tokens(&self) -> bool {
171        false
172    }
173
174    #[inline(always)]
175    fn byte_handlers(&self) -> &Self::ByteHandlers {
176        &byte_handler_tables::NO_TOKENS
177    }
178}
179
180/// Lexer config for parsing with tokens.
181#[derive(Copy, Clone, Default)]
182pub struct TokensLexerConfig;
183
184impl LexerConfig for TokensLexerConfig {
185    type ByteHandlers = ByteHandlers<Self>;
186
187    const TOKENS_METHOD_IS_STATIC: bool = true;
188
189    #[inline(always)]
190    fn tokens(&self) -> bool {
191        true
192    }
193
194    #[inline(always)]
195    fn byte_handlers(&self) -> &Self::ByteHandlers {
196        &byte_handler_tables::WITH_TOKENS
197    }
198}
199
200/// Lexer config for lexing with / without tokens, decided at runtime.
201#[derive(Copy, Clone, Default)]
202#[repr(transparent)]
203pub struct RuntimeLexerConfig {
204    tokens: bool,
205}
206
207impl RuntimeLexerConfig {
208    #[inline(always)]
209    pub fn new(tokens: bool) -> Self {
210        Self { tokens }
211    }
212}
213
214impl LexerConfig for RuntimeLexerConfig {
215    type ByteHandlers = ByteHandlers<Self>;
216
217    const TOKENS_METHOD_IS_STATIC: bool = false;
218
219    #[inline(always)]
220    fn tokens(&self) -> bool {
221        self.tokens
222    }
223
224    #[inline(always)]
225    fn byte_handlers(&self) -> &Self::ByteHandlers {
226        &byte_handler_tables::RUNTIME_TOKENS
227    }
228}