oxc_regular_expression/ast_impl/
visit.rs

1#![expect(clippy::trivially_copy_pass_by_ref)]
2// NOTE: For now, this file is implemented by hand for convenience.
3// But like `oxc_ast`, this should be generated by `tasks/ast_tools` in the future.
4use 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
67/// RegEx syntax tree traversal
68pub 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        // SAFETY:
78        // This should be safe as long as `src` is an reference from the allocator.
79        // But honestly, I'm not really sure if this is safe.
80        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}