oxc_regular_expression/ast_impl/
visit.rs1#![expect(clippy::trivially_copy_pass_by_ref)]
2use oxc_span::{GetSpan, Span};
5
6use crate::ast::{
7 Alternative, BoundaryAssertion, CapturingGroup, Character, CharacterClass,
8 CharacterClassContents, CharacterClassEscape, CharacterClassRange, ClassString,
9 ClassStringDisjunction, Disjunction, Dot, IgnoreGroup, IndexedReference, LookAroundAssertion,
10 NamedReference, Pattern, Quantifier, Term, UnicodePropertyEscape,
11};
12
13use walk::{walk_pattern, *};
14
15#[derive(Copy, Clone, Debug)]
16pub enum RegExpAstKind<'a> {
17 Pattern(&'a Pattern<'a>),
18 Disjunction(&'a Disjunction<'a>),
19 Alternative(&'a Alternative<'a>),
20 Term(&'a Term<'a>),
21 LookAroundAssertion(&'a LookAroundAssertion<'a>),
22 Quantifier(&'a Quantifier<'a>),
23 CapturingGroup(&'a CapturingGroup<'a>),
24 IgnoreGroup(&'a IgnoreGroup<'a>),
25 BoundaryAssertion(&'a BoundaryAssertion),
26 Character(&'a Character),
27 Dot(&'a Dot),
28 CharacterClassEscape(&'a CharacterClassEscape),
29 UnicodePropertyEscape(&'a UnicodePropertyEscape<'a>),
30 CharacterClass(&'a CharacterClass<'a>),
31 CharacterClassContents(&'a CharacterClassContents<'a>),
32 CharacterClassRange(&'a CharacterClassRange),
33 CharacterClassStringDisjunction(&'a ClassStringDisjunction<'a>),
34 CharacterClassString(&'a ClassString<'a>),
35 IndexedReference(&'a IndexedReference),
36 NamedReference(&'a NamedReference<'a>),
37}
38
39impl GetSpan for RegExpAstKind<'_> {
40 #[inline]
41 fn span(&self) -> Span {
42 match self {
43 Self::Pattern(it) => it.span,
44 Self::Disjunction(it) => it.span,
45 Self::Alternative(it) => it.span,
46 Self::Term(it) => GetSpan::span(*it),
47 Self::LookAroundAssertion(it) => it.span,
48 Self::Quantifier(it) => it.span,
49 Self::CapturingGroup(it) => it.span,
50 Self::IgnoreGroup(it) => it.span,
51 Self::BoundaryAssertion(it) => it.span,
52 Self::Character(it) => it.span,
53 Self::Dot(it) => it.span,
54 Self::CharacterClassEscape(it) => it.span,
55 Self::UnicodePropertyEscape(it) => it.span,
56 Self::CharacterClass(it) => it.span,
57 Self::CharacterClassContents(it) => GetSpan::span(*it),
58 Self::CharacterClassRange(it) => it.span,
59 Self::CharacterClassStringDisjunction(it) => it.span,
60 Self::CharacterClassString(it) => it.span,
61 Self::IndexedReference(it) => it.span,
62 Self::NamedReference(it) => it.span,
63 }
64 }
65}
66
67pub trait Visit<'a>: Sized {
69 #[inline]
70 fn enter_node(&mut self, _kind: RegExpAstKind<'a>) {}
71
72 #[inline]
73 fn leave_node(&mut self, _kind: RegExpAstKind<'a>) {}
74
75 #[inline]
76 fn alloc<T>(&self, t: &T) -> &'a T {
77 unsafe { std::mem::transmute(t) }
81 }
82
83 #[inline]
84 fn visit_pattern(&mut self, it: &Pattern<'a>) {
85 walk_pattern(self, it);
86 }
87
88 #[inline]
89 fn visit_disjunction(&mut self, it: &Disjunction<'a>) {
90 walk_disjunction(self, it);
91 }
92
93 #[inline]
94 fn visit_alternative(&mut self, it: &Alternative<'a>) {
95 walk_alternative(self, it);
96 }
97
98 #[inline]
99 fn visit_term(&mut self, it: &Term<'a>) {
100 walk_term(self, it);
101 }
102
103 #[inline]
104 fn visit_lookaround_assertion(&mut self, it: &LookAroundAssertion<'a>) {
105 walk_lookaround_assertion(self, it);
106 }
107
108 #[inline]
109 fn visit_quantifier(&mut self, it: &Quantifier<'a>) {
110 walk_quantifier(self, it);
111 }
112
113 #[inline]
114 fn visit_capturing_group(&mut self, it: &CapturingGroup<'a>) {
115 walk_capturing_group(self, it);
116 }
117
118 #[inline]
119 fn visit_ignore_group(&mut self, it: &IgnoreGroup<'a>) {
120 walk_ignore_group(self, it);
121 }
122
123 #[inline]
124 fn visit_boundary_assertion(&mut self, it: &BoundaryAssertion) {
125 walk_boundary_assertion(self, it);
126 }
127
128 #[inline]
129 fn visit_character(&mut self, it: &Character) {
130 walk_character(self, it);
131 }
132
133 #[inline]
134 fn visit_dot(&mut self, it: &Dot) {
135 walk_dot(self, it);
136 }
137
138 #[inline]
139 fn visit_character_class_escape(&mut self, it: &CharacterClassEscape) {
140 walk_character_class_escape(self, it);
141 }
142
143 #[inline]
144 fn visit_unicode_property_escape(&mut self, it: &UnicodePropertyEscape<'a>) {
145 walk_unicode_property_escape(self, it);
146 }
147
148 #[inline]
149 fn visit_character_class(&mut self, it: &CharacterClass<'a>) {
150 walk_character_class(self, it);
151 }
152
153 #[inline]
154 fn visit_character_class_contents(&mut self, it: &CharacterClassContents<'a>) {
155 walk_character_class_contents(self, it);
156 }
157
158 #[inline]
159 fn visit_character_class_range(&mut self, it: &CharacterClassRange) {
160 walk_character_class_range(self, it);
161 }
162
163 #[inline]
164 fn visit_character_class_string_disjunction(&mut self, it: &ClassStringDisjunction<'a>) {
165 walk_character_class_string_disjunction(self, it);
166 }
167
168 #[inline]
169 fn visit_character_class_string(&mut self, it: &ClassString<'a>) {
170 walk_character_class_string(self, it);
171 }
172
173 #[inline]
174 fn visit_indexed_reference(&mut self, it: &IndexedReference) {
175 walk_indexed_reference(self, it);
176 }
177
178 #[inline]
179 fn visit_named_reference(&mut self, it: &NamedReference<'a>) {
180 walk_named_reference(self, it);
181 }
182}
183
184pub mod walk {
185 use super::*;
186
187 #[inline]
188 pub fn walk_pattern<'a, V: Visit<'a>>(visitor: &mut V, it: &Pattern<'a>) {
189 let kind = RegExpAstKind::Pattern(visitor.alloc(it));
190 visitor.enter_node(kind);
191 visitor.visit_disjunction(&it.body);
192 visitor.leave_node(kind);
193 }
194
195 #[inline]
196 pub fn walk_disjunction<'a, V: Visit<'a>>(visitor: &mut V, it: &Disjunction<'a>) {
197 let kind = RegExpAstKind::Disjunction(visitor.alloc(it));
198 visitor.enter_node(kind);
199 for alt in &it.body {
200 visitor.visit_alternative(alt);
201 }
202 visitor.leave_node(kind);
203 }
204
205 #[inline]
206 pub fn walk_alternative<'a, V: Visit<'a>>(visitor: &mut V, it: &Alternative<'a>) {
207 let kind = RegExpAstKind::Alternative(visitor.alloc(it));
208 visitor.enter_node(kind);
209 for term in &it.body {
210 visitor.visit_term(term);
211 }
212 visitor.leave_node(kind);
213 }
214
215 #[inline]
216 pub fn walk_term<'a, V: Visit<'a>>(visitor: &mut V, it: &Term<'a>) {
217 let kind = RegExpAstKind::Term(visitor.alloc(it));
218 visitor.enter_node(kind);
219 match it {
220 Term::LookAroundAssertion(lookaround) => {
221 visitor.visit_lookaround_assertion(lookaround);
222 }
223 Term::Quantifier(quant) => {
224 visitor.visit_quantifier(quant);
225 }
226 Term::CapturingGroup(group) => {
227 visitor.visit_capturing_group(group);
228 }
229 Term::IgnoreGroup(group) => {
230 visitor.visit_ignore_group(group);
231 }
232 Term::BoundaryAssertion(boundary_assertion) => {
233 visitor.visit_boundary_assertion(boundary_assertion);
234 }
235 Term::Character(character) => {
236 visitor.visit_character(character);
237 }
238 Term::Dot(dot) => {
239 visitor.visit_dot(dot);
240 }
241 Term::CharacterClassEscape(character_class_escape) => {
242 visitor.visit_character_class_escape(character_class_escape);
243 }
244 Term::UnicodePropertyEscape(unicode_property_escape) => {
245 visitor.visit_unicode_property_escape(unicode_property_escape);
246 }
247 Term::CharacterClass(character_class) => {
248 visitor.visit_character_class(character_class);
249 }
250 Term::IndexedReference(indexed_reference) => {
251 visitor.visit_indexed_reference(indexed_reference);
252 }
253 Term::NamedReference(named_reference) => {
254 visitor.visit_named_reference(named_reference);
255 }
256 }
257 visitor.leave_node(kind);
258 }
259
260 #[inline]
261 pub fn walk_lookaround_assertion<'a, V: Visit<'a>>(
262 visitor: &mut V,
263 it: &LookAroundAssertion<'a>,
264 ) {
265 let kind = RegExpAstKind::LookAroundAssertion(visitor.alloc(it));
266 visitor.enter_node(kind);
267 visitor.visit_disjunction(&it.body);
268 visitor.leave_node(kind);
269 }
270
271 #[inline]
272 pub fn walk_quantifier<'a, V: Visit<'a>>(visitor: &mut V, it: &Quantifier<'a>) {
273 let kind = RegExpAstKind::Quantifier(visitor.alloc(it));
274 visitor.enter_node(kind);
275 visitor.visit_term(&it.body);
276 visitor.leave_node(kind);
277 }
278
279 #[inline]
280 pub fn walk_capturing_group<'a, V: Visit<'a>>(visitor: &mut V, it: &CapturingGroup<'a>) {
281 let kind = RegExpAstKind::CapturingGroup(visitor.alloc(it));
282 visitor.enter_node(kind);
283 visitor.visit_disjunction(&it.body);
284 visitor.leave_node(kind);
285 }
286
287 #[inline]
288 pub fn walk_ignore_group<'a, V: Visit<'a>>(visitor: &mut V, it: &IgnoreGroup<'a>) {
289 let kind = RegExpAstKind::IgnoreGroup(visitor.alloc(it));
290 visitor.enter_node(kind);
291 visitor.visit_disjunction(&it.body);
292 visitor.leave_node(kind);
293 }
294
295 #[inline]
296 pub fn walk_boundary_assertion<'a, V: Visit<'a>>(visitor: &mut V, it: &BoundaryAssertion) {
297 let kind = RegExpAstKind::BoundaryAssertion(visitor.alloc(it));
298 visitor.enter_node(kind);
299 visitor.leave_node(kind);
300 }
301
302 #[inline]
303 pub fn walk_character<'a, V: Visit<'a>>(visitor: &mut V, it: &Character) {
304 let kind = RegExpAstKind::Character(visitor.alloc(it));
305 visitor.enter_node(kind);
306 visitor.leave_node(kind);
307 }
308
309 #[inline]
310 pub fn walk_dot<'a, V: Visit<'a>>(visitor: &mut V, it: &Dot) {
311 let kind = RegExpAstKind::Dot(visitor.alloc(it));
312 visitor.enter_node(kind);
313 visitor.leave_node(kind);
314 }
315
316 #[inline]
317 pub fn walk_character_class_escape<'a, V: Visit<'a>>(
318 visitor: &mut V,
319 it: &CharacterClassEscape,
320 ) {
321 let kind = RegExpAstKind::CharacterClassEscape(visitor.alloc(it));
322 visitor.enter_node(kind);
323 visitor.leave_node(kind);
324 }
325
326 #[inline]
327 pub fn walk_unicode_property_escape<'a, V: Visit<'a>>(
328 visitor: &mut V,
329 it: &UnicodePropertyEscape<'a>,
330 ) {
331 let kind = RegExpAstKind::UnicodePropertyEscape(visitor.alloc(it));
332 visitor.enter_node(kind);
333 visitor.leave_node(kind);
334 }
335
336 #[inline]
337 pub fn walk_character_class<'a, V: Visit<'a>>(visitor: &mut V, it: &CharacterClass<'a>) {
338 let kind = RegExpAstKind::CharacterClass(visitor.alloc(it));
339 visitor.enter_node(kind);
340 for content in &it.body {
341 visitor.visit_character_class_contents(content);
342 }
343 visitor.leave_node(kind);
344 }
345
346 #[inline]
347 pub fn walk_character_class_contents<'a, V: Visit<'a>>(
348 visitor: &mut V,
349 it: &CharacterClassContents<'a>,
350 ) {
351 let kind = RegExpAstKind::CharacterClassContents(visitor.alloc(it));
352 visitor.enter_node(kind);
353 match it {
354 CharacterClassContents::CharacterClassRange(character_class_range) => {
355 visitor.visit_character_class_range(character_class_range);
356 }
357 CharacterClassContents::CharacterClassEscape(character_class_escape) => {
358 visitor.visit_character_class_escape(character_class_escape);
359 }
360 CharacterClassContents::UnicodePropertyEscape(unicode_property_escape) => {
361 visitor.visit_unicode_property_escape(unicode_property_escape);
362 }
363 CharacterClassContents::Character(character) => {
364 visitor.visit_character(character);
365 }
366 CharacterClassContents::NestedCharacterClass(character_class) => {
367 visitor.visit_character_class(character_class);
368 }
369 CharacterClassContents::ClassStringDisjunction(class_string_disjunction) => {
370 visitor.visit_character_class_string_disjunction(class_string_disjunction);
371 }
372 }
373 visitor.leave_node(kind);
374 }
375
376 #[inline]
377 pub fn walk_character_class_range<'a, V: Visit<'a>>(visitor: &mut V, it: &CharacterClassRange) {
378 let kind = RegExpAstKind::CharacterClassRange(visitor.alloc(it));
379 visitor.enter_node(kind);
380 visitor.visit_character(&it.min);
381 visitor.visit_character(&it.max);
382 visitor.leave_node(kind);
383 }
384
385 #[inline]
386 pub fn walk_character_class_string_disjunction<'a, V: Visit<'a>>(
387 visitor: &mut V,
388 it: &ClassStringDisjunction<'a>,
389 ) {
390 let kind = RegExpAstKind::CharacterClassStringDisjunction(visitor.alloc(it));
391 visitor.enter_node(kind);
392 for string in &it.body {
393 visitor.visit_character_class_string(string);
394 }
395 visitor.leave_node(kind);
396 }
397
398 #[inline]
399 pub fn walk_character_class_string<'a, V: Visit<'a>>(visitor: &mut V, it: &ClassString<'a>) {
400 let kind = RegExpAstKind::CharacterClassString(visitor.alloc(it));
401 visitor.enter_node(kind);
402 for character in &it.body {
403 visitor.visit_character(character);
404 }
405 visitor.leave_node(kind);
406 }
407
408 #[inline]
409 pub fn walk_indexed_reference<'a, V: Visit<'a>>(visitor: &mut V, it: &IndexedReference) {
410 let kind = RegExpAstKind::IndexedReference(visitor.alloc(it));
411 visitor.enter_node(kind);
412 visitor.leave_node(kind);
413 }
414
415 #[inline]
416 pub fn walk_named_reference<'a, V: Visit<'a>>(visitor: &mut V, it: &NamedReference<'a>) {
417 let kind = RegExpAstKind::NamedReference(visitor.alloc(it));
418 visitor.enter_node(kind);
419 visitor.leave_node(kind);
420 }
421}