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}