rslint_regex/
lib.rs

1//! This crate provides a RegEx parser which targets the [RegEx syntax] specified
2//! by [EcmaScript]
3//!
4//! [EcmaScript]: https://tc39.es/ecma262
5//! [RegEx syntax]: https://tc39.es/ecma262/#sec-patterns
6
7#![deny(rust_2018_idioms)]
8
9mod ir;
10#[allow(clippy::range_plus_one)]
11mod parser;
12#[cfg(test)]
13mod tests;
14mod unicode;
15
16pub use parser::*;
17
18pub use ir::*;
19use std::ops::Range;
20pub use unicode::EcmaVersion;
21
22pub type Result<T, E = Error> = std::result::Result<T, E>;
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct Span {
26    /// The offset in the whole file to calculate the absolute position.
27    pub offset: usize,
28    /// The relative start of this `Span` inside a pattern.
29    pub start: usize,
30    /// The relative end of this `Span` inside a pattern.
31    pub end: usize,
32}
33
34impl Span {
35    /// Create a new `Span`
36    pub fn new(offset: usize, start: usize, end: usize) -> Self {
37        Self { offset, start, end }
38    }
39
40    /// Calculates the absolute start using `self.offset + self.start`.
41    pub fn abs_start(&self) -> usize {
42        self.offset + self.start
43    }
44
45    /// Calculates the absolute end using `self.offset + self.end`.
46    pub fn abs_end(&self) -> usize {
47        self.offset + self.end
48    }
49
50    pub fn as_range(&self) -> Range<usize> {
51        self.abs_start()..self.abs_end()
52    }
53}
54
55impl From<Range<usize>> for Span {
56    fn from(range: Range<usize>) -> Self {
57        Span::new(0, range.start, range.end)
58    }
59}
60
61#[cfg(feature = "rslint_errors")]
62impl rslint_errors::Span for Span {
63    fn as_range(&self) -> Range<usize> {
64        self.abs_start()..self.abs_end()
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub struct Error {
70    pub span: Span,
71    pub message: String,
72}
73
74impl Error {
75    pub fn new(message: impl ToString, span: Span) -> Self {
76        Self {
77            span,
78            message: message.to_string(),
79        }
80    }
81
82    pub(crate) fn primary(self, span: impl Into<Span>, _msg: &str) -> Self {
83        Self {
84            span: span.into(),
85            message: self.message,
86        }
87    }
88}
89
90/// A visitor trait for [`Regex`]
91///
92/// ⚠️ overriding functions may cause inner nodes to not be visited,
93/// to avoid this, use `VisitAll` instead.
94#[allow(unused_variables)]
95pub trait Visit {
96    fn visit_regex(&mut self, regex: &Regex) {
97        self.visit_node(&regex.node);
98    }
99
100    fn visit_node(&mut self, node: &Node) {
101        match node {
102            Node::Empty => self.visit_empty_node(),
103            Node::Disjunction(span, nodes) => self.visit_disjunction(span, nodes),
104            Node::Assertion(span, kind) => self.visit_assertion(span, kind),
105            Node::Alternative(span, nodes) => self.visit_alternative(span, nodes),
106            Node::Literal(span, literal, _) => self.visit_literal(span, *literal),
107            Node::PerlClass(span, kind, negated) => {
108                self.visit_perl_class(span, kind.to_owned(), *negated)
109            }
110            Node::BackReference(span, reference) => self.visit_backreference(span, *reference),
111            Node::Dot(span) => self.visit_dot(span),
112            Node::CharacterClass(span, class) => self.visit_character_class(span, class),
113            Node::Group(span, group) => self.visit_group(span, group),
114            Node::Quantifier(span, node, kind, lazy) => {
115                self.visit_quantifier(span, node, kind.to_owned(), *lazy)
116            }
117            Node::NamedBackReference(span, backreference) => {
118                self.visit_named_backreference(span, backreference)
119            }
120        }
121    }
122
123    fn visit_empty_node(&mut self) {}
124
125    fn visit_disjunction(&mut self, span: &Span, nodes: &[Node]) {
126        for node in nodes {
127            self.visit_node(node);
128        }
129    }
130
131    fn visit_assertion(&mut self, span: &Span, kind: &AssertionKind) {
132        match kind {
133            AssertionKind::Lookahead(node)
134            | AssertionKind::Lookbehind(node)
135            | AssertionKind::NegativeLookahead(node)
136            | AssertionKind::NegativeLookbehind(node) => self.visit_node(node),
137            _ => {}
138        }
139    }
140
141    fn visit_alternative(&mut self, span: &Span, nodes: &[Node]) {
142        for node in nodes {
143            self.visit_node(node);
144        }
145    }
146
147    fn visit_literal(&mut self, span: &Span, literal: char) {}
148
149    fn visit_perl_class(&mut self, span: &Span, kind: ClassPerlKind, negated: bool) {}
150
151    fn visit_backreference(&mut self, span: &Span, reference: u32) {}
152
153    fn visit_dot(&mut self, span: &Span) {}
154
155    fn visit_character_class(&mut self, span: &Span, class: &CharacterClass) {
156        for member in &class.members {
157            match member {
158                CharacterClassMember::Range(l, r) => {
159                    self.visit_node(l);
160                    self.visit_node(r);
161                }
162                CharacterClassMember::Single(n) => self.visit_node(n),
163            }
164        }
165    }
166
167    fn visit_group(&mut self, span: &Span, group: &Group) {
168        self.visit_node(&group.inner)
169    }
170
171    fn visit_quantifier(&mut self, span: &Span, node: &Node, kind: QuantifierKind, lazy: bool) {
172        self.visit_node(node);
173    }
174
175    fn visit_named_backreference(&mut self, span: &Span, backreference: &str) {}
176}
177
178#[allow(unused_variables)]
179pub trait VisitMut {
180    fn visit_regex(&mut self, regex: &mut Regex) {
181        self.visit_node(&mut regex.node);
182    }
183
184    fn visit_node(&mut self, node: &mut Node) {
185        match node {
186            Node::Empty => self.visit_empty_node(),
187            Node::Disjunction(span, nodes) => self.visit_disjunction(span, nodes),
188            Node::Assertion(span, kind) => self.visit_assertion(span, kind),
189            Node::Alternative(span, nodes) => self.visit_alternative(span, nodes),
190            Node::Literal(span, literal, _) => self.visit_literal(span, literal),
191            Node::PerlClass(span, kind, negated) => self.visit_perl_class(span, kind, negated),
192            Node::BackReference(span, reference) => self.visit_backreference(span, reference),
193            Node::Dot(span) => self.visit_dot(span),
194            Node::CharacterClass(span, class) => self.visit_character_class(span, class),
195            Node::Group(span, group) => self.visit_group(span, group),
196            Node::Quantifier(span, node, kind, lazy) => {
197                self.visit_quantifier(span, node, kind, lazy)
198            }
199            Node::NamedBackReference(span, backreference) => {
200                self.visit_named_backreference(span, backreference)
201            }
202        }
203    }
204
205    fn visit_empty_node(&mut self) {}
206
207    fn visit_disjunction(&mut self, span: &Span, nodes: &mut [Node]) {
208        for node in nodes {
209            self.visit_node(node);
210        }
211    }
212
213    fn visit_assertion(&mut self, span: &Span, kind: &mut AssertionKind) {
214        match kind {
215            AssertionKind::Lookahead(node)
216            | AssertionKind::Lookbehind(node)
217            | AssertionKind::NegativeLookahead(node)
218            | AssertionKind::NegativeLookbehind(node) => self.visit_node(node),
219            _ => {}
220        }
221    }
222
223    fn visit_alternative(&mut self, span: &Span, nodes: &mut [Node]) {
224        for node in nodes {
225            self.visit_node(node);
226        }
227    }
228
229    fn visit_literal(&mut self, span: &Span, literal: &mut char) {}
230
231    fn visit_perl_class(&mut self, span: &Span, kind: &mut ClassPerlKind, negated: &mut bool) {}
232
233    fn visit_backreference(&mut self, span: &Span, reference: &mut u32) {}
234
235    fn visit_dot(&mut self, span: &Span) {}
236
237    fn visit_character_class(&mut self, span: &Span, class: &mut CharacterClass) {
238        for member in &mut class.members {
239            match member {
240                CharacterClassMember::Range(l, r) => {
241                    self.visit_node(l);
242                    self.visit_node(r);
243                }
244                CharacterClassMember::Single(n) => self.visit_node(n),
245            }
246        }
247    }
248
249    fn visit_group(&mut self, span: &Span, group: &mut Group) {
250        self.visit_node(&mut *group.inner)
251    }
252
253    fn visit_quantifier(
254        &mut self,
255        span: &Span,
256        node: &mut Node,
257        kind: &mut QuantifierKind,
258        lazy: &mut bool,
259    ) {
260        self.visit_node(node);
261    }
262
263    fn visit_named_backreference(&mut self, span: &Span, backreference: &mut str) {}
264}
265
266/// A visitor trait for [`Regex`] which visits all nodes regardless of function overrides.
267#[allow(unused_variables)]
268pub trait VisitAll {
269    #[doc(hidden)]
270    fn _visit_node(&mut self, node: &Node) {
271        match node {
272            Node::Empty => self.visit_empty_node(),
273            Node::Disjunction(span, nodes) => {
274                self._visit_disjunction(span, nodes);
275                self._visit_disjunction(span, nodes)
276            }
277            Node::Assertion(span, kind) => {
278                self.visit_assertion(span, kind);
279                self._visit_assertion(span, kind)
280            }
281            Node::Alternative(span, nodes) => {
282                self.visit_alternative(span, nodes);
283                self._visit_alternative(span, nodes)
284            }
285            Node::Literal(span, literal, _) => self.visit_literal(span, *literal),
286            Node::PerlClass(span, kind, negated) => {
287                self.visit_perl_class(span, kind.to_owned(), *negated)
288            }
289            Node::BackReference(span, reference) => self.visit_backreference(span, *reference),
290            Node::Dot(span) => self.visit_dot(span),
291            Node::CharacterClass(span, class) => {
292                self.visit_character_class(span, class);
293                self._visit_character_class(span, class);
294            }
295            Node::Group(span, group) => {
296                self.visit_group(span, group);
297                self._visit_group(span, group)
298            }
299
300            Node::Quantifier(span, node, kind, lazy) => {
301                self.visit_quantifier(span, node, kind.to_owned(), *lazy);
302                self._visit_quantifier(span, node, kind.to_owned(), *lazy);
303            }
304            Node::NamedBackReference(span, backreference) => {
305                self.visit_named_backreference(span, backreference)
306            }
307        }
308    }
309
310    #[doc(hidden)]
311    fn _visit_disjunction(&mut self, span: &Span, nodes: &[Node]) {
312        for node in nodes {
313            self._visit_node(node);
314        }
315    }
316
317    #[doc(hidden)]
318    fn _visit_assertion(&mut self, span: &Span, kind: &AssertionKind) {
319        match kind {
320            AssertionKind::Lookahead(node)
321            | AssertionKind::Lookbehind(node)
322            | AssertionKind::NegativeLookahead(node)
323            | AssertionKind::NegativeLookbehind(node) => self._visit_node(node),
324            _ => {}
325        }
326    }
327
328    #[doc(hidden)]
329    fn _visit_alternative(&mut self, span: &Span, nodes: &[Node]) {
330        for node in nodes {
331            self.visit_node(node);
332            self._visit_node(node);
333        }
334    }
335
336    #[doc(hidden)]
337    fn _visit_character_class(&mut self, span: &Span, class: &CharacterClass) {
338        for member in &class.members {
339            match member {
340                CharacterClassMember::Range(l, r) => {
341                    self.visit_node(l);
342                    self._visit_node(l);
343                    self.visit_node(r);
344                    self._visit_node(r);
345                }
346                CharacterClassMember::Single(n) => {
347                    self.visit_node(n);
348                    self._visit_node(n)
349                }
350            }
351        }
352    }
353
354    #[doc(hidden)]
355    fn _visit_group(&mut self, span: &Span, group: &Group) {
356        self.visit_node(&group.inner);
357        self._visit_node(&group.inner)
358    }
359
360    #[doc(hidden)]
361    fn _visit_quantifier(&mut self, span: &Span, node: &Node, kind: QuantifierKind, lazy: bool) {
362        self.visit_node(node);
363        self._visit_node(node);
364    }
365
366    fn visit_regex(&mut self, regex: &Regex) {
367        self.visit_node(&regex.node);
368        self._visit_node(&regex.node);
369    }
370    fn visit_named_backreference(&mut self, span: &Span, backreference: &str) {}
371    fn visit_alternative(&mut self, span: &Span, nodes: &[Node]) {}
372    fn visit_literal(&mut self, span: &Span, literal: char) {}
373    fn visit_perl_class(&mut self, span: &Span, kind: ClassPerlKind, negated: bool) {}
374    fn visit_backreference(&mut self, span: &Span, reference: u32) {}
375    fn visit_dot(&mut self, span: &Span) {}
376    fn visit_node(&mut self, node: &Node) {}
377    fn visit_empty_node(&mut self) {}
378    fn visit_group(&mut self, span: &Span, group: &Group) {}
379    fn visit_quantifier(&mut self, span: &Span, node: &Node, kind: QuantifierKind, lazy: bool) {}
380    fn visit_character_class(&mut self, span: &Span, class: &CharacterClass) {}
381    fn visit_assertion(&mut self, span: &Span, kind: &AssertionKind) {}
382}
383
384#[allow(unused_variables)]
385pub trait VisitAllMut {
386    #[doc(hidden)]
387    fn _visit_node(&mut self, node: &mut Node) {
388        match node {
389            Node::Empty => self.visit_empty_node(),
390            Node::Disjunction(span, nodes) => {
391                self._visit_disjunction(span, nodes);
392                self._visit_disjunction(span, nodes)
393            }
394            Node::Assertion(span, kind) => {
395                self.visit_assertion(span, kind);
396                self._visit_assertion(span, kind)
397            }
398            Node::Alternative(span, nodes) => {
399                self.visit_alternative(span, nodes);
400                self._visit_alternative(span, nodes)
401            }
402            Node::Literal(span, literal, _) => self.visit_literal(span, literal),
403            Node::PerlClass(span, kind, negated) => self.visit_perl_class(span, kind, negated),
404            Node::BackReference(span, reference) => self.visit_backreference(span, reference),
405            Node::Dot(span) => self.visit_dot(span),
406            Node::CharacterClass(span, class) => {
407                self.visit_character_class(span, class);
408                self._visit_character_class(span, class);
409            }
410            Node::Group(span, group) => {
411                self.visit_group(span, group);
412                self._visit_group(span, group)
413            }
414
415            Node::Quantifier(span, node, kind, lazy) => {
416                self.visit_quantifier(span, node, kind, lazy);
417                self._visit_quantifier(span, node, kind, lazy);
418            }
419            Node::NamedBackReference(span, backreference) => {
420                self.visit_named_backreference(span, backreference)
421            }
422        }
423    }
424
425    #[doc(hidden)]
426    fn _visit_disjunction(&mut self, span: &Span, nodes: &mut [Node]) {
427        for node in nodes {
428            self._visit_node(node);
429        }
430    }
431
432    #[doc(hidden)]
433    fn _visit_assertion(&mut self, span: &Span, kind: &mut AssertionKind) {
434        match kind {
435            AssertionKind::Lookahead(node)
436            | AssertionKind::Lookbehind(node)
437            | AssertionKind::NegativeLookahead(node)
438            | AssertionKind::NegativeLookbehind(node) => self._visit_node(node),
439            _ => {}
440        }
441    }
442
443    #[doc(hidden)]
444    fn _visit_alternative(&mut self, span: &Span, nodes: &mut [Node]) {
445        for node in nodes {
446            self.visit_node(node);
447            self._visit_node(node);
448        }
449    }
450
451    #[doc(hidden)]
452    fn _visit_character_class(&mut self, span: &Span, class: &mut CharacterClass) {
453        for member in &mut class.members {
454            match member {
455                CharacterClassMember::Range(l, r) => {
456                    self.visit_node(l);
457                    self._visit_node(l);
458                    self.visit_node(r);
459                    self._visit_node(r);
460                }
461                CharacterClassMember::Single(n) => {
462                    self.visit_node(n);
463                    self._visit_node(n)
464                }
465            }
466        }
467    }
468
469    #[doc(hidden)]
470    fn _visit_group(&mut self, span: &Span, group: &mut Group) {
471        self.visit_node(&mut group.inner);
472        self._visit_node(&mut group.inner)
473    }
474
475    #[doc(hidden)]
476    fn _visit_quantifier(
477        &mut self,
478        span: &Span,
479        node: &mut Node,
480        kind: &mut QuantifierKind,
481        lazy: &mut bool,
482    ) {
483        self.visit_node(node);
484        self._visit_node(node);
485    }
486
487    fn visit_regex(&mut self, regex: &mut Regex) {
488        self.visit_node(&mut regex.node);
489        self._visit_node(&mut regex.node);
490    }
491    fn visit_named_backreference(&mut self, span: &Span, backreference: &mut str) {}
492    fn visit_alternative(&mut self, span: &Span, nodes: &mut [Node]) {}
493    fn visit_literal(&mut self, span: &Span, literal: &mut char) {}
494    fn visit_perl_class(&mut self, span: &Span, kind: &mut ClassPerlKind, negated: &mut bool) {}
495    fn visit_backreference(&mut self, span: &Span, reference: &mut u32) {}
496    fn visit_dot(&mut self, span: &Span) {}
497    fn visit_node(&mut self, node: &mut Node) {}
498    fn visit_empty_node(&mut self) {}
499    fn visit_group(&mut self, span: &Span, group: &mut Group) {}
500    fn visit_quantifier(
501        &mut self,
502        span: &Span,
503        node: &mut Node,
504        kind: &mut QuantifierKind,
505        lazy: &mut bool,
506    ) {
507    }
508    fn visit_character_class(&mut self, span: &Span, class: &mut CharacterClass) {}
509    fn visit_assertion(&mut self, span: &Span, kind: &mut AssertionKind) {}
510}