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
49/// Parser config for parsing without tokens (default).
50///
51/// See [`ParserConfig`] for more details.
52#[derive(Copy, Clone, Default)]
53pub struct NoTokensParserConfig;
54
55impl ParserConfig for NoTokensParserConfig {
56 type LexerConfig = NoTokensLexerConfig;
57
58 #[inline(always)]
59 fn lexer_config(&self) -> NoTokensLexerConfig {
60 NoTokensLexerConfig
61 }
62}
63
64/// Parser config for parsing with tokens.
65///
66/// See [`ParserConfig`] for more details.
67#[derive(Copy, Clone, Default)]
68pub struct TokensParserConfig;
69
70impl ParserConfig for TokensParserConfig {
71 type LexerConfig = TokensLexerConfig;
72
73 #[inline(always)]
74 fn lexer_config(&self) -> TokensLexerConfig {
75 TokensLexerConfig
76 }
77}
78
79/// Parser config for parsing with / without tokens, decided at runtime.
80///
81/// See [`ParserConfig`] for more details.
82#[derive(Copy, Clone, Default)]
83#[repr(transparent)]
84pub struct RuntimeParserConfig {
85 lexer_config: RuntimeLexerConfig,
86}
87
88impl RuntimeParserConfig {
89 #[inline(always)]
90 pub fn new(tokens: bool) -> Self {
91 Self { lexer_config: RuntimeLexerConfig::new(tokens) }
92 }
93}
94
95impl ParserConfig for RuntimeParserConfig {
96 type LexerConfig = RuntimeLexerConfig;
97
98 #[inline(always)]
99 fn lexer_config(&self) -> RuntimeLexerConfig {
100 self.lexer_config
101 }
102}
103
104/// Lexer config.
105///
106/// See [`ParserConfig`] for more details.
107///
108/// We have to define a different byte handler table for each config, as byte handler functions
109/// are generic over the [`LexerConfig`].
110///
111/// Byte handler tables are defined within lexer (`lexer/byte_handlers.rs`).
112/// We need them to be defined as `static` items, for performance.
113/// 1. `static` ensures that only 1 copy of each byte handler table appears in the binary
114/// (even if `Lexer::handle_byte` is inlined in multiple places).
115/// 2. Indexing into byte handlers table is an extremely hot path, and even 1 extra indirection (pointer chasing)
116/// has a sizeable impact on performance.
117///
118/// An associated type and `byte_handlers` method is the only way to make this work with Rust's type system
119/// and borrow checker.
120pub trait LexerConfig: Default {
121 /// Byte handlers table type.
122 type ByteHandlers: Index<usize, Output = ByteHandler<Self>>;
123
124 /// `true` if [`tokens`] method returns a static value.
125 ///
126 /// This const is useful in situations where code can be more efficient
127 /// when the return value of [`tokens`] method is known at compile time.
128 ///
129 /// Implementors of this trait MUST only set this const to `true` if [`tokens`] method returns a static value.
130 ///
131 /// [`tokens`]: LexerConfig::tokens
132 const TOKENS_METHOD_IS_STATIC: bool;
133
134 /// Returns `true` if tokens are enabled.
135 fn tokens(&self) -> bool;
136
137 /// Get byte handlers table to use in lexer with this config.
138 fn byte_handlers(&self) -> &Self::ByteHandlers;
139}
140
141/// Lexer config for lexing without tokens.
142#[derive(Copy, Clone, Default)]
143pub struct NoTokensLexerConfig;
144
145impl LexerConfig for NoTokensLexerConfig {
146 type ByteHandlers = ByteHandlers<Self>;
147
148 const TOKENS_METHOD_IS_STATIC: bool = true;
149
150 #[inline(always)]
151 fn tokens(&self) -> bool {
152 false
153 }
154
155 #[inline(always)]
156 fn byte_handlers(&self) -> &Self::ByteHandlers {
157 &byte_handler_tables::NO_TOKENS
158 }
159}
160
161/// Lexer config for parsing with tokens.
162#[derive(Copy, Clone, Default)]
163pub struct TokensLexerConfig;
164
165impl LexerConfig for TokensLexerConfig {
166 type ByteHandlers = ByteHandlers<Self>;
167
168 const TOKENS_METHOD_IS_STATIC: bool = true;
169
170 #[inline(always)]
171 fn tokens(&self) -> bool {
172 true
173 }
174
175 #[inline(always)]
176 fn byte_handlers(&self) -> &Self::ByteHandlers {
177 &byte_handler_tables::WITH_TOKENS
178 }
179}
180
181/// Lexer config for lexing with / without tokens, decided at runtime.
182#[derive(Copy, Clone, Default)]
183#[repr(transparent)]
184pub struct RuntimeLexerConfig {
185 tokens: bool,
186}
187
188impl RuntimeLexerConfig {
189 #[inline(always)]
190 pub fn new(tokens: bool) -> Self {
191 Self { tokens }
192 }
193}
194
195impl LexerConfig for RuntimeLexerConfig {
196 type ByteHandlers = ByteHandlers<Self>;
197
198 const TOKENS_METHOD_IS_STATIC: bool = false;
199
200 #[inline(always)]
201 fn tokens(&self) -> bool {
202 self.tokens
203 }
204
205 #[inline(always)]
206 fn byte_handlers(&self) -> &Self::ByteHandlers {
207 &byte_handler_tables::RUNTIME_TOKENS
208 }
209}