Skip to main content

fancy_regex/
lib.rs

1// Copyright 2026 The Fancy Regex Authors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21#![doc = include_str!("../docs/main.md")]
22#![doc = include_str!("../docs/features.md")]
23#![doc = include_str!("../docs/syntax.md")]
24#![doc = include_str!("../docs/subroutines/1_intro.md")]
25#![doc = include_str!("../docs/subroutines/2_flags.md")]
26#![doc = include_str!("../docs/subroutines/3_left_recursion.md")]
27#![doc = include_str!("../docs/subroutines/4_recursion.md")]
28#![doc = include_str!("../docs/absent.md")]
29#![deny(missing_docs)]
30#![deny(missing_debug_implementations)]
31#![cfg_attr(not(feature = "std"), no_std)]
32
33extern crate alloc;
34
35use alloc::borrow::Cow;
36use alloc::boxed::Box;
37use alloc::string::{String, ToString};
38use alloc::sync::Arc;
39use alloc::vec;
40use alloc::vec::Vec;
41
42use core::convert::TryFrom;
43use core::fmt;
44use core::fmt::{Debug, Formatter};
45use core::ops::{Index, Range};
46use core::str::FromStr;
47use regex_automata::meta::Regex as RaRegex;
48use regex_automata::util::captures::Captures as RaCaptures;
49use regex_automata::util::syntax::Config as SyntaxConfig;
50use regex_automata::Anchored as RaAnchored;
51use regex_automata::Input as RaInput;
52
53mod analyze;
54mod bytes;
55mod compile;
56mod error;
57mod expand;
58mod input;
59mod optimize;
60mod parse;
61mod parse_flags;
62mod regexset;
63mod replacer;
64mod seek;
65mod to_hir;
66mod vm;
67
68use crate::analyze::can_compile_as_anchored;
69use crate::analyze::{analyze, AnalyzeContext};
70use crate::compile::{compile, CompileOptions};
71use crate::optimize::optimize;
72use crate::parse::{ExprTree, NamedGroups, Parser};
73use crate::parse_flags::*;
74#[cfg(feature = "leftmost_longest")]
75use crate::vm::OPTION_LEFTMOST_LONGEST;
76use crate::vm::{Prog, OPTION_FIND_NOT_EMPTY, OPTION_NOT_CONTINUED_FROM_PREVIOUS_MATCH};
77
78pub use crate::bytes::MatchBytes;
79pub use crate::error::{CompileError, Error, ParseError, Result, RuntimeError};
80pub use crate::expand::Expander;
81pub use crate::input::{Input, RegexInput};
82pub use crate::regexset::{RegexSet, RegexSetMatch, RegexSetOptions};
83pub use crate::replacer::{NoExpand, Replacer, ReplacerRef};
84pub use crate::seek::seek_pattern_is_useful;
85
86/// Controls how the regex engine handles input encoding.
87///
88/// This enum represents the three valid combinations of the `utf8` and `unicode`
89/// flags in the underlying regex engine. Each variant has different trade-offs
90/// between input flexibility and character class semantics.
91///
92/// The default is [`BytesMode::Unicode`].
93///
94/// # Variants
95///
96/// ## `BytesMode::Unicode` (default)
97///
98/// - Input is expected to be valid UTF-8
99/// - `.` matches any Unicode scalar value (except `\n` unless `dot_matches_new_line` is set)
100/// - `\w`, `\d`, `\s` match Unicode characters
101/// - Unicode properties like `\p{Letter}` are available
102/// - Word boundaries (`\b`) are Unicode-aware
103///
104/// ## `BytesMode::Ascii`
105///
106/// - Input can be arbitrary bytes (no UTF-8 requirement)
107/// - `.` matches any single **byte** (except `\n` unless `dot_matches_new_line` is set)
108/// - `\w` matches `[a-zA-Z0-9_]` only (ASCII)
109/// - `\d` matches `[0-9]` only (ASCII)
110/// - `\s` matches ASCII whitespace only
111/// - Unicode properties are **not available**
112/// - Word boundaries (`\b`) use ASCII-only word characters
113///
114/// Use this mode when matching raw binary data or filenames that may contain
115/// non-UTF-8 bytes and you don't need Unicode character classes.
116///
117/// ## `BytesMode::UnicodeBytes`
118///
119/// - Input can be arbitrary bytes (no UTF-8 requirement)
120/// - `.` matches Unicode scalar values (sequences of valid UTF-8 bytes)
121/// - `\w`, `\d`, `\s` match Unicode characters
122/// - Unicode properties like `\p{Letter}` are available
123///
124/// Use this mode when the input may contain non-UTF-8 bytes but you still want
125/// Unicode-aware character classes. Note that `.` will **not** match individual
126/// non-UTF-8 bytes — it only matches valid UTF-8 codepoint sequences.
127#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
128pub enum BytesMode {
129    /// Unicode mode: input must be valid UTF-8, full Unicode support.
130    /// (utf8=true, unicode=true)
131    #[default]
132    Unicode,
133    /// ASCII bytes mode: `.` matches any byte, character classes are ASCII-only.
134    /// (utf8=false, unicode=false)
135    Ascii,
136    /// Unicode-aware bytes mode: input can be non-UTF-8, character classes
137    /// remain Unicode-aware. `.` matches Unicode scalar values only.
138    /// (utf8=false, unicode=true)
139    UnicodeBytes,
140}
141
142const MAX_RECURSION: usize = 64;
143
144// the public API
145
146/// A builder for a `Regex` to allow configuring options.
147#[derive(Debug)]
148pub struct RegexBuilder {
149    pattern: String,
150    options: RegexOptionsBuilder,
151}
152
153/// A builder for a `Regex` to allow configuring options.
154#[derive(Debug)]
155pub struct RegexOptionsBuilder {
156    options: RegexOptions,
157}
158
159/// A compiled regular expression.
160#[derive(Clone)]
161pub struct Regex {
162    inner: RegexImpl,
163    named_groups: Arc<NamedGroups>,
164}
165
166// Separate enum because we don't want to expose any of this
167#[derive(Clone)]
168enum RegexImpl {
169    // Do we want to box this? It's pretty big...
170    Wrap {
171        inner: RaRegex,
172        /// The original pattern which the regex was constructed from
173        pattern: String,
174        /// Some optimizations avoid the VM, but need to use an extra capture group to represent the match boundaries
175        explicit_capture_group_0: bool,
176        /// The actual pattern passed to regex-automata for delegation
177        delegated_pattern: String,
178    },
179    Fancy {
180        prog: Arc<Prog>,
181        n_groups: usize,
182        /// The original pattern which the regex was constructed from
183        pattern: String,
184        options: HardRegexRuntimeOptions,
185    },
186}
187
188/// A single match of a regex or group in an input text
189#[derive(Copy, Clone, Debug, Eq, PartialEq)]
190pub struct Match<'t> {
191    text: &'t str,
192    start: usize,
193    end: usize,
194}
195
196/// An iterator over all non-overlapping matches for a particular input.
197///
198/// The iterator yields a `Result<S::Match>`. The iterator stops when no more
199/// matches can be found.
200///
201/// `'r` is the lifetime of the compiled regular expression and `'t` is the
202/// lifetime of the matched input.
203#[derive(Debug)]
204pub struct Matches<'r, 't, S: input::Input + ?Sized> {
205    re: &'r Regex,
206    input: RegexInput<'t, S>,
207    last_match: Option<usize>,
208    last_skipped_empty: bool,
209}
210
211impl<'r, 't, S: input::Input + ?Sized> Matches<'r, 't, S> {
212    /// Return the underlying regex.
213    pub fn regex(&self) -> &'r Regex {
214        self.re
215    }
216
217    /// Return the text being searched.
218    pub fn text(&self) -> &'t S {
219        self.input.haystack()
220    }
221
222    /// Return the current search input configuration.
223    pub fn input(&self) -> &RegexInput<'t, S> {
224        &self.input
225    }
226
227    /// Adapted from the `regex` crate. Calls `find_from_pos`/`captures_from_pos` repeatedly.
228    /// Ignores empty matches immediately after a match.
229    /// Also passes a flag when skipping an empty match, so that \G wouldn't match at the new start position.
230    fn next_with<F, R>(&mut self, mut search: F) -> Option<Result<R>>
231    where
232        F: FnMut(&Regex, &RegexInput<'t, S>, u32) -> Result<Option<(R, (usize, usize))>>,
233    {
234        if self.input.is_done() {
235            return None;
236        }
237
238        let option_flags = if self.last_skipped_empty {
239            OPTION_NOT_CONTINUED_FROM_PREVIOUS_MATCH
240        } else {
241            0
242        };
243
244        let pos = self.input.effective_start();
245        let (result, (match_start, match_end)) = match search(self.re, &self.input, option_flags) {
246            Err(error) => {
247                // Stop on first error: If an error is encountered, return it, and set the "last match position"
248                // to the string length, so that the next next() call will return None, to prevent an infinite loop.
249                self.input
250                    .set_start(self.input.get_range().end.saturating_add(1));
251                return Some(Err(error));
252            }
253            Ok(None) => return None,
254            Ok(Some(pair)) => pair,
255        };
256
257        if match_start == match_end {
258            // This is an empty match. To ensure we make progress, start
259            // the next search at the smallest possible starting position
260            // of the next match following this one.
261            self.input
262                .set_start(self.input.haystack().advance_position(match_end));
263            // Only set OPTION_NOT_CONTINUED_FROM_PREVIOUS_MATCH on the next call if this was a
264            // truly zero-length match (the VM consumed no bytes from `pos`).
265            // This means that \K won't prevent \G from matching.
266            self.last_skipped_empty = match_end == pos;
267            // Don't accept empty matches immediately following a match.
268            // Just move on to the next match.
269            if Some(match_end) == self.last_match {
270                return self.next_with(search);
271            }
272        } else {
273            self.input.set_start(match_end);
274            self.last_skipped_empty = false;
275        }
276
277        self.last_match = Some(match_end);
278
279        Some(Ok(result))
280    }
281}
282
283impl<'r, 't, S: input::Input + ?Sized> Iterator for Matches<'r, 't, S> {
284    type Item = Result<S::Match<'t>>;
285
286    fn next(&mut self) -> Option<Self::Item> {
287        let text = self.input.haystack();
288        self.next_with(move |re, input, flags| {
289            re.find_input_raw(input, flags)
290                .map(|opt| opt.map(|(s, e)| (text.make_match(s, e), (s, e))))
291        })
292    }
293}
294
295/// An iterator that yields all non-overlapping capture groups matching a
296/// particular regular expression.
297///
298/// The iterator stops when no more matches can be found.
299///
300/// `'r` is the lifetime of the compiled regular expression and `'t` is the
301/// lifetime of the matched string.
302#[derive(Debug)]
303pub struct CaptureMatches<'r, 't, S: input::Input + ?Sized>(Matches<'r, 't, S>);
304
305impl<'r, 't, S: input::Input + ?Sized> CaptureMatches<'r, 't, S> {
306    /// Return the text being searched.
307    pub fn text(&self) -> &'t S {
308        self.0.input.haystack()
309    }
310
311    /// Return the underlying regex.
312    pub fn regex(&self) -> &'r Regex {
313        self.0.re
314    }
315}
316
317impl<'r, 't, S: input::Input + ?Sized> Iterator for CaptureMatches<'r, 't, S> {
318    type Item = Result<Captures<'t, S>>;
319
320    fn next(&mut self) -> Option<Self::Item> {
321        self.0.next_with(move |re, input, flags| {
322            let captures = re.captures_input_with_option_flags(input, flags)?;
323            Ok(captures.map(|c| {
324                let (start, end) = c
325                    .inner
326                    .get_span(0)
327                    .expect("`Captures` is expected to have entire match at 0th position");
328                (c, (start, end))
329            }))
330        })
331    }
332}
333
334/// A set of capture groups found for a regex.
335///
336/// `S` is the input type (`str` or `[u8]`).
337#[derive(Debug)]
338pub struct Captures<'t, S: input::Input + ?Sized> {
339    inner: CapturesImpl,
340    named_groups: Arc<NamedGroups>,
341    input: &'t S,
342}
343
344#[derive(Debug)]
345enum CapturesImpl {
346    Wrap {
347        locations: RaCaptures,
348        /// Some optimizations avoid the VM but need an extra capture group to represent the match boundaries.
349        /// Therefore what is actually capture group 1 should be treated as capture group 0, and all other
350        /// capture groups should have their index reduced by one as well to line up with what the pattern specifies.
351        explicit_capture_group_0: bool,
352    },
353    Fancy {
354        saves: Vec<usize>,
355    },
356}
357
358impl CapturesImpl {
359    fn get_span(&self, i: usize) -> Option<(usize, usize)> {
360        match self {
361            CapturesImpl::Wrap {
362                locations,
363                explicit_capture_group_0,
364            } => locations
365                .get_group(i + if *explicit_capture_group_0 { 1 } else { 0 })
366                .map(|span| (span.start, span.end)),
367            CapturesImpl::Fancy { saves } => {
368                let slot = i * 2;
369                if slot >= saves.len() {
370                    return None;
371                }
372                let lo = saves[slot];
373                if lo == usize::MAX {
374                    return None;
375                }
376                let hi = saves[slot + 1];
377                Some((lo, hi))
378            }
379        }
380    }
381
382    fn len(&self) -> usize {
383        match self {
384            CapturesImpl::Wrap {
385                locations,
386                explicit_capture_group_0,
387            } => locations.group_len() - if *explicit_capture_group_0 { 1 } else { 0 },
388            CapturesImpl::Fancy { saves } => saves.len() / 2,
389        }
390    }
391}
392
393/// Iterator for captured groups in order in which they appear in the regex.
394#[derive(Debug)]
395pub struct SubCaptureMatches<'c, 't, S: input::Input + ?Sized> {
396    data: &'c Captures<'t, S>,
397    i: usize,
398}
399
400/// An iterator over all substrings delimited by a regex.
401///
402/// This iterator yields `Result<&'h str>`, where each item is a substring of the
403/// target string that is delimited by matches of the regular expression. It stops when there
404/// are no more substrings to yield.
405///
406/// `'r` is the lifetime of the compiled regular expression, and `'h` is the
407/// lifetime of the target string being split.
408///
409/// This iterator can be created by the [`Regex::split`] method.
410#[derive(Debug)]
411pub struct Split<'r, 'h> {
412    matches: Matches<'r, 'h, str>,
413    next_start: usize,
414    target: &'h str,
415}
416
417impl<'r, 'h> Iterator for Split<'r, 'h> {
418    type Item = Result<&'h str>;
419
420    /// Returns the next substring that results from splitting the target string by the regex.
421    ///
422    /// If no more matches are found, returns the remaining part of the string,
423    /// or `None` if all substrings have been yielded.
424    fn next(&mut self) -> Option<Result<&'h str>> {
425        match self.matches.next() {
426            None => {
427                let len = self.target.len();
428                if self.next_start > len {
429                    // No more substrings to return
430                    None
431                } else {
432                    // Return the last part of the target string
433                    // Next call will return None
434                    let part = &self.target[self.next_start..len];
435                    self.next_start = len + 1;
436                    Some(Ok(part))
437                }
438            }
439            // Return the next substring
440            Some(Ok(m)) => {
441                let part = &self.target[self.next_start..m.start()];
442                self.next_start = m.end();
443                Some(Ok(part))
444            }
445            Some(Err(e)) => Some(Err(e)),
446        }
447    }
448}
449
450impl<'r, 'h> core::iter::FusedIterator for Split<'r, 'h> {}
451
452/// An iterator over at most `N` substrings delimited by a regex.
453///
454/// This iterator yields `Result<&'h str>`, where each item is a substring of the
455/// target that is delimited by matches of the regular expression. It stops either when
456/// there are no more substrings to yield, or after `N` substrings have been yielded.
457///
458/// The `N`th substring is the remaining part of the target.
459///
460/// `'r` is the lifetime of the compiled regular expression, and `'h` is the
461/// lifetime of the target string being split.
462///
463/// This iterator can be created by the [`Regex::splitn`] method.
464#[derive(Debug)]
465pub struct SplitN<'r, 'h> {
466    splits: Split<'r, 'h>,
467    limit: usize,
468}
469
470impl<'r, 'h> Iterator for SplitN<'r, 'h> {
471    type Item = Result<&'h str>;
472
473    /// Returns the next substring resulting from splitting the target by the regex,
474    /// limited to `N` splits.
475    ///
476    /// Returns `None` if no more matches are found or if the limit is reached after yielding
477    /// the remaining part of the target.
478    fn next(&mut self) -> Option<Result<&'h str>> {
479        if self.limit == 0 {
480            // Limit reached. No more substrings available.
481            return None;
482        }
483
484        // Decrement the limit for each split.
485        self.limit -= 1;
486        if self.limit > 0 {
487            return self.splits.next();
488        }
489
490        // Nth split
491        let len = self.splits.target.len();
492        if self.splits.next_start > len {
493            // No more substrings available.
494            None
495        } else {
496            // Return the remaining part of the target
497            let start = self.splits.next_start;
498            self.splits.next_start = len + 1;
499            Some(Ok(&self.splits.target[start..len]))
500        }
501    }
502
503    fn size_hint(&self) -> (usize, Option<usize>) {
504        (0, Some(self.limit))
505    }
506}
507
508impl<'r, 'h> core::iter::FusedIterator for SplitN<'r, 'h> {}
509
510#[derive(Clone)]
511struct RegexOptions {
512    syntaxc: SyntaxConfig,
513    delegate_size_limit: Option<usize>,
514    delegate_dfa_size_limit: Option<usize>,
515    oniguruma_mode: bool,
516    ignore_numbered_groups_when_named_groups_exist: bool,
517    hard_regex_runtime_options: HardRegexRuntimeOptions,
518    bytes_mode: BytesMode,
519    seek_filter: Option<fn(&str) -> bool>,
520    /// Whether the top-level engine of an easy (`Wrap`) pattern should build a
521    /// prefilter. `RegexSet` turns this off for its internally-built members:
522    /// the set only ever searches them anchored at candidate positions, where
523    /// a prefilter is never consulted, so building one wastes time and memory.
524    delegate_prefilter: bool,
525    #[cfg(feature = "leftmost_longest")]
526    leftmost_longest: bool,
527}
528
529impl fmt::Debug for RegexOptions {
530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531        let seek_filter_desc = match self.seek_filter {
532            None => "None",
533            Some(f_ptr) if (f_ptr as *const ()) == (seek_pattern_is_useful as *const ()) => {
534                "Some(seek_pattern_is_useful)"
535            }
536            Some(_) => "Some(<custom>)",
537        };
538        let mut debug = f.debug_struct("RegexOptions");
539        debug
540            .field("syntaxc", &self.syntaxc)
541            .field("delegate_size_limit", &self.delegate_size_limit)
542            .field("delegate_dfa_size_limit", &self.delegate_dfa_size_limit)
543            .field("oniguruma_mode", &self.oniguruma_mode)
544            .field(
545                "ignore_numbered_groups_when_named_groups_exist",
546                &self.ignore_numbered_groups_when_named_groups_exist,
547            )
548            .field(
549                "hard_regex_runtime_options",
550                &self.hard_regex_runtime_options,
551            )
552            .field("seek_filter", &seek_filter_desc)
553            .field("delegate_prefilter", &self.delegate_prefilter);
554        #[cfg(feature = "leftmost_longest")]
555        debug.field("leftmost_longest", &self.leftmost_longest);
556        debug.finish()
557    }
558}
559
560impl Default for RegexOptions {
561    fn default() -> Self {
562        RegexOptions {
563            syntaxc: SyntaxConfig::new().unicode(true),
564            delegate_size_limit: None,
565            delegate_dfa_size_limit: None,
566            oniguruma_mode: false,
567            ignore_numbered_groups_when_named_groups_exist: false,
568            hard_regex_runtime_options: HardRegexRuntimeOptions::default(),
569            bytes_mode: BytesMode::default(),
570            seek_filter: None, // when we are ready to enable seek by default, use: `Some(seek_pattern_is_useful)`
571            delegate_prefilter: true,
572            #[cfg(feature = "leftmost_longest")]
573            leftmost_longest: false,
574        }
575    }
576}
577
578#[derive(Copy, Clone, Debug)]
579struct HardRegexRuntimeOptions {
580    backtrack_limit: usize,
581    find_not_empty: bool,
582    disallow_empty_match_at_eof_after_newline: bool,
583    allow_input_assertion_overrides: bool,
584    #[cfg(feature = "leftmost_longest")]
585    leftmost_longest: bool,
586}
587
588impl RegexOptions {
589    fn get_flag_value(flag_value: bool, enum_value: u32) -> u32 {
590        if flag_value {
591            enum_value
592        } else {
593            0
594        }
595    }
596
597    fn compute_flags(&self) -> u32 {
598        let insensitive = Self::get_flag_value(self.syntaxc.get_case_insensitive(), FLAG_CASEI);
599        let multiline = Self::get_flag_value(self.syntaxc.get_multi_line(), FLAG_MULTI);
600        let whitespace =
601            Self::get_flag_value(self.syntaxc.get_ignore_whitespace(), FLAG_IGNORE_SPACE);
602        let dotnl = Self::get_flag_value(self.syntaxc.get_dot_matches_new_line(), FLAG_DOTNL);
603        let unicode = Self::get_flag_value(
604            self.syntaxc.get_unicode() && !matches!(self.bytes_mode, BytesMode::Ascii),
605            FLAG_UNICODE,
606        );
607        let oniguruma_mode = Self::get_flag_value(self.oniguruma_mode, FLAG_ONIGURUMA_MODE);
608        let crlf = Self::get_flag_value(self.syntaxc.get_crlf(), FLAG_CRLF);
609        let named_groups_only = Self::get_flag_value(
610            self.ignore_numbered_groups_when_named_groups_exist,
611            FLAG_IGNORE_NUMBERED_GROUPS_WHEN_NAMED_GROUPS_EXIST,
612        );
613
614        insensitive
615            | multiline
616            | whitespace
617            | dotnl
618            | unicode
619            | oniguruma_mode
620            | crlf
621            | named_groups_only
622    }
623}
624
625impl Default for HardRegexRuntimeOptions {
626    fn default() -> Self {
627        HardRegexRuntimeOptions {
628            backtrack_limit: 1_000_000,
629            find_not_empty: false,
630            disallow_empty_match_at_eof_after_newline: false,
631            allow_input_assertion_overrides: false,
632            #[cfg(feature = "leftmost_longest")]
633            leftmost_longest: false,
634        }
635    }
636}
637
638impl Default for RegexOptionsBuilder {
639    fn default() -> Self {
640        Self::new()
641    }
642}
643
644impl RegexOptionsBuilder {
645    /// Create a new regex options builder.
646    pub fn new() -> Self {
647        RegexOptionsBuilder {
648            options: RegexOptions::default(),
649        }
650    }
651
652    /// Build a `Regex` from the given pattern.
653    ///
654    /// Returns an [`Error`](enum.Error.html) if the pattern could not be parsed.
655    pub fn build(&self, pattern: String) -> Result<Regex> {
656        Regex::new_options(pattern, &self.options)
657    }
658
659    fn set_config(&mut self, func: impl Fn(SyntaxConfig) -> SyntaxConfig) -> &mut Self {
660        self.options.syntaxc = func(self.options.syntaxc);
661        self
662    }
663
664    /// Override default case insensitive
665    /// this is to enable/disable casing via builder instead of a flag within
666    /// the raw string pattern which will be parsed
667    ///
668    /// Default is false
669    pub fn case_insensitive(&mut self, yes: bool) -> &mut Self {
670        self.set_config(|x| x.case_insensitive(yes))
671    }
672
673    /// Enable multi-line regex
674    pub fn multi_line(&mut self, yes: bool) -> &mut Self {
675        self.set_config(|x| x.multi_line(yes))
676    }
677
678    /// Allow ignore whitespace
679    pub fn ignore_whitespace(&mut self, yes: bool) -> &mut Self {
680        self.set_config(|x| x.ignore_whitespace(yes))
681    }
682
683    /// Enable or disable the "dot matches any character" flag.
684    /// When this is enabled, `.` will match any character. When it's disabled, then `.` will match any character
685    /// except for a new line character.
686    pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut Self {
687        self.set_config(|x| x.dot_matches_new_line(yes))
688    }
689
690    /// Enable or disable the CRLF mode flag (`R`).
691    ///
692    /// When enabled, `\r\n` is treated as a single line ending for the purposes of
693    /// `^` and `$` in multi-line mode, instead of treating `\r` and `\n` as separate
694    /// line endings.
695    ///
696    /// By default, this is disabled. It may be selectively enabled in the regular
697    /// expression by using the `R` flag, e.g. `(?mR)` or `(?Rm)`.
698    pub fn crlf(&mut self, yes: bool) -> &mut Self {
699        self.set_config(|x| x.crlf(yes))
700    }
701
702    /// Enable verbose mode in the regular expression.
703    ///
704    /// The same as ignore_whitespace
705    ///
706    /// When enabled, verbose mode permits insigificant whitespace in many
707    /// places in the regular expression, as well as comments. Comments are
708    /// started using `#` and continue until the end of the line.
709    ///
710    /// By default, this is disabled. It may be selectively enabled in the
711    /// regular expression by using the `x` flag regardless of this setting.
712    pub fn verbose_mode(&mut self, yes: bool) -> &mut Self {
713        self.set_config(|x| x.ignore_whitespace(yes))
714    }
715
716    /// Enable or disable the Unicode flag (`u`) by default.
717    ///
718    /// By default this is **enabled**. The inline `u` flag inside a pattern
719    /// is only accepted when it **matches** the current builder setting (e.g.
720    /// `(?u)` when unicode is already enabled, or `(?-u)` when it is already
721    /// disabled). Attempts to change the mode inline are rejected with a
722    /// [`ParseError::ChangingUnicodeModeUnsupported`] error. Use this builder
723    /// method to set the desired mode instead.
724    ///
725    /// ## Effect on `str` input (default)
726    ///
727    /// When matching against `&str` (the default), the underlying engine
728    /// requires that all matches respect UTF-8 boundaries. Disabling Unicode
729    /// therefore has the following effects:
730    ///
731    /// - **`\w`, `\d`, `\s`** become ASCII-only (`[a-zA-Z0-9_]`, `[0-9]`,
732    ///   and ASCII whitespace respectively).
733    /// - **`\W`, `\D`, `\S`**, bare **`.`**, and **`\p{...}`** Unicode
734    ///   properties **fail to compile**, because they could match byte
735    ///   sequences that violate UTF-8 boundaries.
736    ///
737    /// If you need those constructs with `unicode_mode(false)`, use the bytes
738    /// API with [`BytesMode::Ascii`] instead.
739    ///
740    /// ## Effect on byte input
741    ///
742    /// When matching against `&[u8]` (via [`BytesMode::Ascii`]), all
743    /// constructs work as expected in ASCII mode (`.` matches any byte,
744    /// `\W`/`\D`/`\S` match non-ASCII byte values, etc.).
745    ///
746    /// **WARNING**: Unicode mode can greatly increase the size of the compiled
747    /// DFA, which can noticeably impact both memory usage and compilation
748    /// time. This is especially noticeable if your regex contains character
749    /// classes like `\w` that are impacted by whether Unicode is enabled or
750    /// not. If Unicode is not necessary, you are encouraged to disable it.
751    pub fn unicode_mode(&mut self, yes: bool) -> &mut Self {
752        self.set_config(|x| x.unicode(yes))
753    }
754
755    /// Limit for how many times backtracking should be attempted for fancy regexes (where
756    /// backtracking is used). If this limit is exceeded, execution returns an error with
757    /// [`Error::BacktrackLimitExceeded`](enum.Error.html#variant.BacktrackLimitExceeded).
758    /// This is for preventing a regex with catastrophic backtracking to run for too long.
759    ///
760    /// Default is `1_000_000` (1 million).
761    pub fn backtrack_limit(&mut self, limit: usize) -> &mut Self {
762        self.options.hard_regex_runtime_options.backtrack_limit = limit;
763        self
764    }
765
766    /// Set the approximate size limit of the compiled regular expression.
767    ///
768    /// This option is forwarded from the wrapped `regex` crate. Note that depending on the used
769    /// regex features there may be multiple delegated sub-regexes fed to the `regex` crate. As
770    /// such the actual limit is closer to `<number of delegated regexes> * delegate_size_limit`.
771    pub fn delegate_size_limit(&mut self, limit: usize) -> &mut Self {
772        self.options.delegate_size_limit = Some(limit);
773        self
774    }
775
776    /// Set the approximate size of the cache used by the DFA.
777    ///
778    /// This option is forwarded from the wrapped `regex` crate. Note that depending on the used
779    /// regex features there may be multiple delegated sub-regexes fed to the `regex` crate. As
780    /// such the actual limit is closer to `<number of delegated regexes> *
781    /// delegate_dfa_size_limit`.
782    pub fn delegate_dfa_size_limit(&mut self, limit: usize) -> &mut Self {
783        self.options.delegate_dfa_size_limit = Some(limit);
784        self
785    }
786
787    /// Require that matches are non-empty (i.e. match at least one character).
788    ///
789    /// When this is enabled, any match attempt that would result in a zero-length match is
790    /// rejected.
791    ///
792    /// Default is `false`.
793    ///
794    /// N.B. When `find_not_empty` is set and analysis determines the pattern will only ever
795    /// produce an empty match, compiling the regex will return
796    /// `CompileError::PatternCanNeverMatch` instead of silently constructing a regex that can never
797    /// return a result. This catches the user error at compile time rather than allowing the
798    /// combination to execute pointlessly at runtime.
799    pub fn find_not_empty(&mut self, yes: bool) -> &mut Self {
800        self.options.hard_regex_runtime_options.find_not_empty = yes;
801        self
802    }
803
804    /// Enable leftmost-longest match semantics.
805    ///
806    /// When enabled, among all matches starting at the leftmost possible position,
807    /// the longest match is returned. This contrasts with the default leftmost-first
808    /// (PCRE-style) semantics.
809    ///
810    /// Default is `false`.
811    ///
812    /// **Note:** This requires the `leftmost_longest` feature to be enabled.
813    #[cfg(feature = "leftmost_longest")]
814    pub fn leftmost_longest(&mut self, yes: bool) -> &mut Self {
815        self.options.leftmost_longest = yes;
816        self.options.hard_regex_runtime_options.leftmost_longest = yes;
817        self
818    }
819
820    /// Treat unnamed capture groups as non-capturing when named groups exist.
821    /// Prevents accessing capture groups by number from within the pattern
822    /// (backrefs, subroutine calls) when named groups are present.
823    pub fn ignore_numbered_groups_when_named_groups_exist(&mut self, yes: bool) -> &mut Self {
824        self.options.ignore_numbered_groups_when_named_groups_exist = yes;
825        self
826    }
827
828    /// ⚠️ Experimental: This API may change, be removed without notice, or cause matches to be
829    /// skipped. This requires more real-world testing to prove correctness and observe in which
830    /// circumstances it brings performance benefits and in which it has the opposite effect.
831    /// Feedback (and benchmarks on real-world patterns/haystacks) would be very welcome!
832    ///
833    /// Enable the Seek pre-filter optimization for hard (backtracking) patterns.
834    ///
835    /// When enabled, the compiler attempts to derive a regular approximation of the pattern
836    /// which is used to skip to the earliest plausible match position in the haystack before
837    /// invoking the backtracking VM. This can dramatically speed up searches in long haystacks
838    /// when the pattern can only match at infrequent positions.
839    ///
840    /// The seek pattern is always a conservative over-approximation — it may report false-positive
841    /// positions but will never skip a true match.
842    ///
843    /// When `yes` is `true`, uses the default [`seek_pattern_is_useful`] filter to decide
844    /// whether the derived pattern is worth using. When `false`, disables seek entirely.
845    ///
846    /// To supply a custom filter, use [`seek_filter`](Self::seek_filter) instead.
847    pub fn seek(&mut self, yes: bool) -> &mut Self {
848        self.options.seek_filter = if yes {
849            Some(seek_pattern_is_useful)
850        } else {
851            None
852        };
853        self
854    }
855
856    /// Set a custom filter function that decides whether the derived seek pattern is useful.
857    ///
858    /// The function receives the seek pattern string and returns `true` if the pattern should
859    /// be used as a pre-filter, or `false` to fall back to the standard unanchored search.
860    ///
861    /// Calling this method implicitly enables seek. Pass [`seek_pattern_is_useful`] to restore
862    /// the default behavior.
863    ///
864    /// # Example
865    ///
866    /// ```rust
867    /// use fancy_regex::{RegexOptionsBuilder, seek_pattern_is_useful};
868    ///
869    /// // Use the default filter (equivalent to seek(true))
870    /// let mut builder = RegexOptionsBuilder::new();
871    /// builder.seek_filter(seek_pattern_is_useful);
872    ///
873    /// // Use a custom filter that additionally requires the pattern to be longer than 3 bytes
874    /// let mut builder = RegexOptionsBuilder::new();
875    /// builder.seek_filter(|pat| pat.len() > 3 && seek_pattern_is_useful(pat));
876    /// ```
877    pub fn seek_filter(&mut self, filter: fn(&str) -> bool) -> &mut Self {
878        self.options.seek_filter = Some(filter);
879        self
880    }
881
882    /// Attempts to better match [Oniguruma](https://github.com/kkos/oniguruma)'s default parsing behavior
883    ///
884    /// Currently this amounts to changing behavior with:
885    ///
886    /// # Left and right word bounds
887    ///
888    /// `fancy-regex` follows the default of other regex engines such as the `regex` crate itself
889    /// where `\<` and `\>` correspond to a _left_ and _right_ word-bound respectively. This
890    /// differs from Oniguruma's defaults which treat them as matching the literals `<` and `>`.
891    /// When this option is set using `\<` and `\>` in the pattern will match the literals
892    /// `<` and `>` instead of word bounds.
893    ///
894    /// # Repetition/Quantifiers on empty groups
895    ///
896    /// `fancy-regex` would normally reject patterns like `(?:)+` because the `+` has nothing
897    /// to target. In Oniguruma mode, the empty repeat is silently dropped at parse time.
898    ///
899    /// # Swapped order quantifiers
900    ///
901    /// `fancy-regex` would normally treat `x{0,3}` as a syntax error because the minimum is
902    /// greater than the maximum. In Oniguruma mode, the limits are swapped (behaving as
903    /// `x{3,0}`) and the resulting repeat is treated as atomic (possessive), matching
904    /// Oniguruma's behavior.
905    ///
906    /// # Adjacent quantifiers
907    ///
908    /// `fancy-regex` would normally reject adjacent quantifiers like `a{3}{2}`, treating the
909    /// second as having nothing to target. In Oniguruma mode, each quantifier wraps the
910    /// previous result (e.g. `a{3}{2}` becomes `(?:a{3}){2}`).
911    ///
912    /// Outside Oniguruma mode, `+` after `{...}` is a possessive modifier (e.g. `x{2}+` is
913    /// possessive). In Oniguruma mode, `+` after a user-specified `{...}` repeat is treated
914    /// as another repeat modifier — `x{2}+` is equivalent to `(?:x{2})+`.
915    ///
916    /// # Start of line (`^`) in multiline mode
917    ///
918    /// In multiline mode (`(?m)`), `^` normally matches at the start of the input and after
919    /// any newline. In Oniguruma mode, it additionally rejects a match at the absolute end of
920    /// the input when it is preceded by a trailing newline.
921    /// Inside lookarounds, `^` behaves as a plain line start without the end-of-input rejection.
922    ///
923    /// ## Example
924    ///
925    /// ```
926    /// use fancy_regex::{Regex, RegexBuilder};
927    ///
928    /// let haystack = "turbo::<Fish>";
929    /// let regex = r"\<\w*\>";
930    ///
931    /// // By default `\<` and `\>` will match the start and end of a word boundary
932    /// let word_bounds_regex = Regex::new(regex).unwrap();
933    /// let word_bounds = word_bounds_regex.find(haystack).unwrap().unwrap();
934    /// assert_eq!(word_bounds.as_str(), "turbo");
935    ///
936    /// // With the option set they instead match the literal `<` and `>` characters
937    /// let literals_regex = RegexBuilder::new(regex).oniguruma_mode(true).build().unwrap();
938    /// let literals = literals_regex.find(haystack).unwrap().unwrap();
939    /// assert_eq!(literals.as_str(), "<Fish>");
940    /// ```
941    pub fn oniguruma_mode(&mut self, yes: bool) -> &mut Self {
942        self.options.oniguruma_mode = yes;
943        self
944    }
945
946    /// Set the input encoding mode for the regex.
947    ///
948    /// Controls how the regex engine handles input encoding. See [`BytesMode`]
949    /// for details on each variant.
950    ///
951    /// Default is [`BytesMode::Unicode`].
952    ///
953    /// # Example
954    ///
955    /// ```rust
956    /// use fancy_regex::{BytesMode, RegexBuilder};
957    ///
958    /// // ASCII bytes mode: . matches any byte including non-UTF-8
959    /// let re = RegexBuilder::new(r".+")
960    ///     .bytes_mode(BytesMode::Ascii)
961    ///     .build()
962    ///     .unwrap();
963    /// assert!(re.is_match(b"\x80\x81\x82").unwrap());
964    ///
965    /// // Default Unicode mode: . only matches valid UTF-8 codepoints
966    /// let re = RegexBuilder::new(r".+")
967    ///     .build()
968    ///     .unwrap();
969    /// assert!(!re.is_match(b"\x80\x81\x82").unwrap());
970    /// ```
971    pub fn bytes_mode(&mut self, mode: BytesMode) -> &mut Self {
972        self.options.bytes_mode = mode;
973        self
974    }
975
976    /// Sometimes you want to pass in a haystack containing a trailing newline,
977    /// and have that last position ignored for the purposes of anchors like ^ and $
978    /// and even for lookarounds, unless \z is specifically used to anchor to the end of the string.
979    /// This is how Oniguruma works for example.
980    pub fn disallow_empty_match_at_eof_after_newline(&mut self, yes: bool) -> &mut Self {
981        self.options
982            .hard_regex_runtime_options
983            .disallow_empty_match_at_eof_after_newline = yes;
984        self
985    }
986
987    /// Allow [`RegexInput`] assertion suppression overrides at runtime.
988    ///
989    /// When enabled, patterns containing `\A` and `\z` are treated as hard and compiled to the VM
990    /// so that [`RegexInput::start_text`] and [`RegexInput::end_text`] can suppress those
991    /// assertions.
992    ///
993    /// When disabled (the default), those runtime overrides are ignored.
994    pub fn allow_input_assertion_overrides(&mut self, yes: bool) -> &mut Self {
995        self.options
996            .hard_regex_runtime_options
997            .allow_input_assertion_overrides = yes;
998        self
999    }
1000}
1001
1002impl RegexBuilder {
1003    /// Create a new regex builder.
1004    pub fn new(pattern: &str) -> Self {
1005        RegexBuilder {
1006            pattern: pattern.to_string(),
1007            options: RegexOptionsBuilder::new(),
1008        }
1009    }
1010
1011    /// Build a `Regex` from the given pattern.
1012    ///
1013    /// Returns an [`Error`](enum.Error.html) if the pattern could not be parsed.
1014    pub fn build(&self) -> Result<Regex> {
1015        self.options.build(self.pattern.clone())
1016    }
1017
1018    /// Change the pattern to build. Useful when building multiple regexes from
1019    /// many patterns.
1020    pub fn pattern(&mut self, pattern: String) -> &mut Self {
1021        self.pattern = pattern;
1022        self
1023    }
1024
1025    /// See [`RegexOptionsBuilder::case_insensitive`]
1026    pub fn case_insensitive(&mut self, yes: bool) -> &mut Self {
1027        self.options.case_insensitive(yes);
1028        self
1029    }
1030
1031    /// See [`RegexOptionsBuilder::multi_line`]
1032    pub fn multi_line(&mut self, yes: bool) -> &mut Self {
1033        self.options.multi_line(yes);
1034        self
1035    }
1036
1037    /// See [`RegexOptionsBuilder::ignore_whitespace`]
1038    pub fn ignore_whitespace(&mut self, yes: bool) -> &mut Self {
1039        self.options.ignore_whitespace(yes);
1040        self
1041    }
1042
1043    /// See [`RegexOptionsBuilder::dot_matches_new_line`]
1044    pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut Self {
1045        self.options.dot_matches_new_line(yes);
1046        self
1047    }
1048
1049    /// See [`RegexOptionsBuilder::verbose_mode`]
1050    pub fn verbose_mode(&mut self, yes: bool) -> &mut Self {
1051        self.options.ignore_whitespace(yes);
1052        self
1053    }
1054
1055    /// See [`RegexOptionsBuilder::unicode_mode`]
1056    pub fn unicode_mode(&mut self, yes: bool) -> &mut Self {
1057        self.options.unicode_mode(yes);
1058        self
1059    }
1060
1061    /// See [`RegexOptionsBuilder::backtrack_limit`]
1062    pub fn backtrack_limit(&mut self, limit: usize) -> &mut Self {
1063        self.options.backtrack_limit(limit);
1064        self
1065    }
1066
1067    /// See [`RegexOptionsBuilder::delegate_size_limit`]
1068    pub fn delegate_size_limit(&mut self, limit: usize) -> &mut Self {
1069        self.options.delegate_size_limit(limit);
1070        self
1071    }
1072
1073    /// See [`RegexOptionsBuilder::delegate_dfa_size_limit`]
1074    pub fn delegate_dfa_size_limit(&mut self, limit: usize) -> &mut Self {
1075        self.options.delegate_dfa_size_limit(limit);
1076        self
1077    }
1078
1079    /// See [`RegexOptionsBuilder::oniguruma_mode`]
1080    pub fn oniguruma_mode(&mut self, yes: bool) -> &mut Self {
1081        self.options.oniguruma_mode(yes);
1082        self
1083    }
1084
1085    /// See [`RegexOptionsBuilder::bytes_mode`]
1086    pub fn bytes_mode(&mut self, mode: BytesMode) -> &mut Self {
1087        self.options.bytes_mode(mode);
1088        self
1089    }
1090
1091    /// See [`RegexOptionsBuilder::crlf`]
1092    pub fn crlf(&mut self, yes: bool) -> &mut Self {
1093        self.options.crlf(yes);
1094        self
1095    }
1096
1097    /// See [`RegexOptionsBuilder::find_not_empty`]
1098    pub fn find_not_empty(&mut self, yes: bool) -> &mut Self {
1099        self.options.find_not_empty(yes);
1100        self
1101    }
1102
1103    /// See [`RegexOptionsBuilder::leftmost_longest`]
1104    #[cfg(feature = "leftmost_longest")]
1105    pub fn leftmost_longest(&mut self, yes: bool) -> &mut Self {
1106        self.options.leftmost_longest(yes);
1107        self
1108    }
1109
1110    /// See [`RegexOptionsBuilder::ignore_numbered_groups_when_named_groups_exist`]
1111    pub fn ignore_numbered_groups_when_named_groups_exist(&mut self, yes: bool) -> &mut Self {
1112        self.options
1113            .ignore_numbered_groups_when_named_groups_exist(yes);
1114        self
1115    }
1116
1117    /// See [`RegexOptionsBuilder::seek`]
1118    pub fn seek(&mut self, yes: bool) -> &mut Self {
1119        self.options.seek(yes);
1120        self
1121    }
1122
1123    /// See [`RegexOptionsBuilder::seek_filter`]
1124    pub fn seek_filter(&mut self, filter: fn(&str) -> bool) -> &mut Self {
1125        self.options.seek_filter(filter);
1126        self
1127    }
1128
1129    /// See [`RegexOptionsBuilder::disallow_empty_match_at_eof_after_newline`]
1130    pub fn disallow_empty_match_at_eof_after_newline(&mut self, yes: bool) -> &mut Self {
1131        self.options.disallow_empty_match_at_eof_after_newline(yes);
1132        self
1133    }
1134
1135    /// See [`RegexOptionsBuilder::allow_input_assertion_overrides`]
1136    pub fn allow_input_assertion_overrides(&mut self, yes: bool) -> &mut Self {
1137        self.options.allow_input_assertion_overrides(yes);
1138        self
1139    }
1140}
1141
1142impl fmt::Debug for Regex {
1143    /// Shows the original regular expression.
1144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1145        write!(f, "{}", self.as_str())
1146    }
1147}
1148
1149impl fmt::Display for Regex {
1150    /// Shows the original regular expression
1151    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1152        write!(f, "{}", self.as_str())
1153    }
1154}
1155
1156impl FromStr for Regex {
1157    type Err = Error;
1158
1159    /// Attempts to parse a string into a regular expression
1160    fn from_str(s: &str) -> Result<Regex> {
1161        Regex::new(s)
1162    }
1163}
1164
1165impl Regex {
1166    /// Parse and compile a regex with default options, see `RegexBuilder`.
1167    ///
1168    /// Returns an [`Error`](enum.Error.html) if the pattern could not be parsed.
1169    pub fn new(re: &str) -> Result<Regex> {
1170        Self::new_options(re.to_string(), &RegexOptions::default())
1171    }
1172
1173    pub(crate) fn new_options(pattern: String, options: &RegexOptions) -> Result<Regex> {
1174        let mut tree = Expr::parse_tree_with_flags(&pattern, options.compute_flags())?;
1175
1176        let find_not_empty = options.hard_regex_runtime_options.find_not_empty;
1177        let disallow_empty_match_at_eof_after_newline = options
1178            .hard_regex_runtime_options
1179            .disallow_empty_match_at_eof_after_newline;
1180        let allow_input_assertion_overrides = options
1181            .hard_regex_runtime_options
1182            .allow_input_assertion_overrides;
1183        #[cfg(feature = "leftmost_longest")]
1184        let leftmost_longest = options.leftmost_longest;
1185
1186        let requires_capture_group_fixup = if find_not_empty {
1187            // if the find_not_empty flag is set, we skip optimizations
1188            // partially because we have to go though the VM anyway
1189            // partially because having the last instruction of the expression not have
1190            // ix be at the end of capture group 0 ruins our empty match checking logic.
1191            false
1192        } else {
1193            // try to optimize the expression tree so that a hard pattern could become easy
1194            // with a fixup of the capture groups
1195            optimize(&mut tree)
1196        };
1197        let info = analyze(
1198            &tree,
1199            AnalyzeContext {
1200                explicit_capture_group_0: requires_capture_group_fixup,
1201                find_not_empty,
1202                disallow_empty_match_at_eof_after_newline,
1203                allow_input_assertion_overrides,
1204                #[cfg(feature = "leftmost_longest")]
1205                leftmost_longest,
1206            },
1207        )?;
1208
1209        if find_not_empty && info.const_size && info.min_size == 0 {
1210            return Err(CompileError::PatternCanNeverMatch.into());
1211        }
1212
1213        if !info.hard {
1214            // easy case, wrap regex
1215
1216            // we do our own to_str because escapes are different
1217            // The cooked form is the pattern plus some flag-group decoration. It is
1218            // kept even though the engine is normally built from an Hir below,
1219            // because it doubles as the seek pattern for RegexSet membership and
1220            // as debug output.
1221            let mut re_cooked = String::with_capacity(pattern.len() + pattern.len() / 2);
1222            tree.expr.to_str(&mut re_cooked, 0);
1223            let compile_options = CompileOptions {
1224                bytes_mode: options.bytes_mode,
1225                unicode: options.syntaxc.get_unicode()
1226                    && !matches!(options.bytes_mode, BytesMode::Ascii),
1227                delegate_size_limit: options.delegate_size_limit,
1228                delegate_dfa_size_limit: options.delegate_dfa_size_limit,
1229                // The remaining fields (anchored, contains_subroutines, seek_filter,
1230                // disallow_empty_match_at_eof_after_newline) are irrelevant for a plain
1231                // delegate compile in the easy path and their defaults are correct.
1232                ..CompileOptions::default()
1233            };
1234            // The whole pattern is delegated and searched unanchored, so it keeps
1235            // its prefilter and all capture groups (the user may request captures).
1236            // RegexSet members are the exception: they are only searched anchored,
1237            // so their prefilter build is skipped (see `delegate_prefilter`).
1238            let usage = if options.delegate_prefilter {
1239                compile::DelegateUsage::unanchored()
1240            } else {
1241                compile::DelegateUsage::unanchored_no_prefilter()
1242            };
1243            // Build the engine from an Hir translated directly from the tree, so
1244            // the engine doesn't re-parse the cooked pattern. Fall back to the
1245            // string path for anything the translator doesn't cover.
1246            let utf8 = matches!(compile_options.bytes_mode, BytesMode::Unicode);
1247            let mut hir_ctx = to_hir::HirCtx::new(compile_options.unicode, utf8);
1248            let inner = match to_hir::expr_to_hir(&tree.expr, &mut hir_ctx) {
1249                Some(hir) => compile::compile_inner_from_hir(&hir, &compile_options, usage)?,
1250                None => compile::compile_inner(&re_cooked, &compile_options, usage)?,
1251            };
1252            return Ok(Regex {
1253                inner: RegexImpl::Wrap {
1254                    inner,
1255                    pattern,
1256                    explicit_capture_group_0: requires_capture_group_fixup,
1257                    delegated_pattern: re_cooked,
1258                },
1259                named_groups: Arc::new(tree.named_groups),
1260            });
1261        }
1262
1263        let prog = compile(
1264            &info,
1265            CompileOptions {
1266                anchored: can_compile_as_anchored(&tree.expr),
1267                contains_subroutines: tree.contains_subroutines,
1268                seek_filter: options.seek_filter,
1269                disallow_empty_match_at_eof_after_newline,
1270                bytes_mode: options.bytes_mode,
1271                unicode: options.syntaxc.get_unicode()
1272                    && !matches!(options.bytes_mode, BytesMode::Ascii),
1273                delegate_size_limit: options.delegate_size_limit,
1274                delegate_dfa_size_limit: options.delegate_dfa_size_limit,
1275            },
1276        )?;
1277        Ok(Regex {
1278            inner: RegexImpl::Fancy {
1279                prog: Arc::new(prog),
1280                n_groups: info.end_group(),
1281                options: options.hard_regex_runtime_options,
1282                pattern,
1283            },
1284            named_groups: Arc::new(tree.named_groups),
1285        })
1286    }
1287
1288    /// Returns the original string of this regex.
1289    pub fn as_str(&self) -> &str {
1290        match &self.inner {
1291            RegexImpl::Wrap { pattern, .. } => pattern,
1292            RegexImpl::Fancy { pattern, .. } => pattern,
1293        }
1294    }
1295
1296    /// Check if the regex matches the input.
1297    ///
1298    /// Accepts any type implementing [`Input`]: `&str`, `&String`, or `&[u8]`.
1299    ///
1300    /// # Example
1301    ///
1302    /// Test if some text contains the same word twice:
1303    ///
1304    /// ```rust
1305    /// # use fancy_regex::Regex;
1306    ///
1307    /// let re = Regex::new(r"(\w+) \1").unwrap();
1308    /// assert!(re.is_match("mirror mirror on the wall").unwrap());
1309    /// ```
1310    ///
1311    /// Match against raw bytes:
1312    ///
1313    /// ```rust
1314    /// # use fancy_regex::{BytesMode, RegexBuilder};
1315    ///
1316    /// let re = RegexBuilder::new(r"\d+")
1317    ///     .bytes_mode(BytesMode::Ascii)
1318    ///     .build()
1319    ///     .unwrap();
1320    /// assert!(re.is_match(b"abc 123").unwrap());
1321    /// ```
1322    pub fn is_match<S: input::Input + ?Sized>(&self, input: &S) -> Result<bool> {
1323        self.is_match_input(RegexInput::new(input))
1324    }
1325
1326    /// Returns true if and only if this regex matches anywhere in the given
1327    /// search input.
1328    pub fn is_match_input<S: input::Input + ?Sized>(
1329        &self,
1330        input: RegexInput<'_, S>,
1331    ) -> Result<bool> {
1332        match &self.inner {
1333            RegexImpl::Wrap { inner, .. } => {
1334                if input.is_done() {
1335                    Ok(false)
1336                } else {
1337                    Ok(inner.is_match(ra_input(&input)))
1338                }
1339            }
1340            RegexImpl::Fancy { .. } => self.find_input_raw(&input, 0).map(|m| m.is_some()),
1341        }
1342    }
1343
1344    /// Returns an iterator for each successive non-overlapping match in `text`.
1345    ///
1346    /// If you have capturing groups in your regex that you want to extract, use the [Regex::captures_iter()]
1347    /// method.
1348    ///
1349    /// # Example
1350    ///
1351    /// Find all words followed by an exclamation point:
1352    ///
1353    /// ```rust
1354    /// # use fancy_regex::Regex;
1355    ///
1356    /// let re = Regex::new(r"\w+(?=!)").unwrap();
1357    /// let mut matches = re.find_iter("so fancy! even with! iterators!");
1358    /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "fancy");
1359    /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "with");
1360    /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "iterators");
1361    /// assert!(matches.next().is_none());
1362    /// ```
1363    pub fn find_iter<'r, 't, S: input::Input + ?Sized>(
1364        &'r self,
1365        text: &'t S,
1366    ) -> Matches<'r, 't, S> {
1367        self.find_iter_input(RegexInput::new(text))
1368    }
1369
1370    /// Returns an iterator for each successive non-overlapping match in the
1371    /// given search input.
1372    pub fn find_iter_input<'r, 't, S: input::Input + ?Sized>(
1373        &'r self,
1374        input: RegexInput<'t, S>,
1375    ) -> Matches<'r, 't, S> {
1376        Matches {
1377            re: self,
1378            input,
1379            last_match: None,
1380            last_skipped_empty: false,
1381        }
1382    }
1383
1384    /// Find the first match in the input.
1385    ///
1386    /// Accepts any type implementing [`Input`]: `&str`, `&String`, or `&[u8]`.
1387    ///
1388    /// # Example
1389    ///
1390    /// Find a word that is followed by an exclamation point:
1391    ///
1392    /// ```rust
1393    /// # use fancy_regex::Regex;
1394    ///
1395    /// let re = Regex::new(r"\w+(?=!)").unwrap();
1396    /// assert_eq!(re.find("so fancy!").unwrap().unwrap().as_str(), "fancy");
1397    /// ```
1398    pub fn find<'t, S: input::Input + ?Sized>(&self, input: &'t S) -> Result<Option<S::Match<'t>>> {
1399        self.find_input(RegexInput::new(input))
1400    }
1401
1402    /// Find the first match in the given search input.
1403    pub fn find_input<'t, S: input::Input + ?Sized>(
1404        &self,
1405        input: RegexInput<'t, S>,
1406    ) -> Result<Option<S::Match<'t>>> {
1407        Ok(self
1408            .find_input_raw(&input, 0)?
1409            .map(|(s, e)| input.haystack().make_match(s, e)))
1410    }
1411
1412    /// Returns the first match in `input`, starting from the specified byte position `pos`.
1413    ///
1414    /// # Examples
1415    ///
1416    /// Finding match starting at a position:
1417    ///
1418    /// ```
1419    /// # use fancy_regex::Regex;
1420    /// let re = Regex::new(r"(?m:^)(\d+)").unwrap();
1421    /// let text = "1 test 123\n2 foo";
1422    /// let mat = re.find_from_pos(text, 7).unwrap().unwrap();
1423    ///
1424    /// assert_eq!(mat.start(), 11);
1425    /// assert_eq!(mat.end(), 12);
1426    /// ```
1427    ///
1428    /// Note that in some cases this is not the same as using the `find`
1429    /// method and passing a slice of the string, see [Regex::captures_from_pos()]
1430    /// for details. To constrain matching to a byte range without slicing, use
1431    /// [Regex::find_input()] with [`RegexInput`].
1432    pub fn find_from_pos<'t, S: input::Input + ?Sized>(
1433        &self,
1434        input: &'t S,
1435        pos: usize,
1436    ) -> Result<Option<S::Match<'t>>> {
1437        self.find_input(RegexInput::new(input).from_pos(pos))
1438    }
1439
1440    pub(crate) fn find_input_raw<S: input::Input + ?Sized>(
1441        &self,
1442        input: &RegexInput<'_, S>,
1443        option_flags: u32,
1444    ) -> Result<Option<(usize, usize)>> {
1445        if input.is_done() {
1446            return Ok(None);
1447        }
1448        match &self.inner {
1449            RegexImpl::Wrap {
1450                inner,
1451                explicit_capture_group_0,
1452                ..
1453            } => {
1454                let mut delegated_input = ra_input(input);
1455                if input.is_anchored() {
1456                    delegated_input = delegated_input.anchored(RaAnchored::Yes);
1457                }
1458                let result = if !*explicit_capture_group_0 {
1459                    inner.search(&delegated_input).map(|m| (m.start(), m.end()))
1460                } else {
1461                    // Only group 1's span is needed (the real match bounds of
1462                    // the rewritten pattern); a fixed 4-slot search avoids
1463                    // allocating full captures on every find.
1464                    let mut slots = [None; 4];
1465                    if inner.search_slots(&delegated_input, &mut slots).is_some() {
1466                        slots[2]
1467                            .zip(slots[3])
1468                            .map(|(start, end)| (start.get(), end.get()))
1469                    } else {
1470                        None
1471                    }
1472                };
1473                Ok(result)
1474            }
1475            RegexImpl::Fancy { prog, options, .. } => {
1476                #[allow(unused_mut)]
1477                let mut option_flags = option_flags
1478                    | if options.find_not_empty {
1479                        OPTION_FIND_NOT_EMPTY
1480                    } else {
1481                        0
1482                    };
1483                #[cfg(feature = "leftmost_longest")]
1484                {
1485                    option_flags |= if options.leftmost_longest {
1486                        OPTION_LEFTMOST_LONGEST
1487                    } else {
1488                        0
1489                    };
1490                }
1491                // Span-only VM entry: nothing is moved out of the pooled
1492                // scratch, so this path is allocation-free per call.
1493                vm::run_spans(prog, input, option_flags, options)
1494            }
1495        }
1496    }
1497
1498    /// Build a `Captures` value containing only group 0 for the given span.
1499    ///
1500    /// This is used by `RegexSet` as a fast path for patterns without capture
1501    /// groups, where we only need to preserve the overall match range.
1502    ///
1503    /// The caller must pass a valid `start..end` match span for `input`.
1504    /// Behavior is otherwise undefined for APIs reading the resulting captures.
1505    pub(crate) fn captures_for_span<'t, S: input::Input + ?Sized>(
1506        &self,
1507        input: &'t S,
1508        start: usize,
1509        end: usize,
1510    ) -> Captures<'t, S> {
1511        Captures {
1512            inner: CapturesImpl::Fancy {
1513                saves: vec![start, end],
1514            },
1515            named_groups: self.named_groups.clone(),
1516            input,
1517        }
1518    }
1519
1520    /// Returns an iterator over all the non-overlapping capture groups matched in `text`.
1521    ///
1522    /// # Examples
1523    ///
1524    /// Finding all matches and capturing parts of each:
1525    ///
1526    /// ```rust
1527    /// # use fancy_regex::Regex;
1528    ///
1529    /// let re = Regex::new(r"(\d{4})-(\d{2})").unwrap();
1530    /// let text = "It was between 2018-04 and 2020-01";
1531    /// let mut all_captures = re.captures_iter(text);
1532    ///
1533    /// let first = all_captures.next().unwrap().unwrap();
1534    /// assert_eq!(first.get(1).unwrap().as_str(), "2018");
1535    /// assert_eq!(first.get(2).unwrap().as_str(), "04");
1536    /// assert_eq!(first.get(0).unwrap().as_str(), "2018-04");
1537    ///
1538    /// let second = all_captures.next().unwrap().unwrap();
1539    /// assert_eq!(second.get(1).unwrap().as_str(), "2020");
1540    /// assert_eq!(second.get(2).unwrap().as_str(), "01");
1541    /// assert_eq!(second.get(0).unwrap().as_str(), "2020-01");
1542    ///
1543    /// assert!(all_captures.next().is_none());
1544    /// ```
1545    pub fn captures_iter<'r, 't, S: input::Input + ?Sized>(
1546        &'r self,
1547        text: &'t S,
1548    ) -> CaptureMatches<'r, 't, S> {
1549        self.captures_iter_input(RegexInput::new(text))
1550    }
1551
1552    /// Returns an iterator over all the non-overlapping capture groups matched
1553    /// in the given search input.
1554    pub fn captures_iter_input<'r, 't, S: input::Input + ?Sized>(
1555        &'r self,
1556        input: RegexInput<'t, S>,
1557    ) -> CaptureMatches<'r, 't, S> {
1558        CaptureMatches(self.find_iter_input(input))
1559    }
1560
1561    /// Returns the capture groups for the first match in `text`.
1562    ///
1563    /// If no match is found, then `Ok(None)` is returned.
1564    ///
1565    /// # Examples
1566    ///
1567    /// Finding matches and capturing parts of the match:
1568    ///
1569    /// ```rust
1570    /// # use fancy_regex::Regex;
1571    ///
1572    /// let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
1573    /// let text = "The date was 2018-04-07";
1574    /// let captures = re.captures(text).unwrap().unwrap();
1575    ///
1576    /// assert_eq!(captures.get(1).unwrap().as_str(), "2018");
1577    /// assert_eq!(captures.get(2).unwrap().as_str(), "04");
1578    /// assert_eq!(captures.get(3).unwrap().as_str(), "07");
1579    /// assert_eq!(captures.get(0).unwrap().as_str(), "2018-04-07");
1580    /// ```
1581    pub fn captures<'t, S: input::Input + ?Sized>(
1582        &self,
1583        text: &'t S,
1584    ) -> Result<Option<Captures<'t, S>>> {
1585        self.captures_input(RegexInput::new(text))
1586    }
1587
1588    /// Returns the capture groups for the first match in the given search
1589    /// input.
1590    pub fn captures_input<'t, S: input::Input + ?Sized>(
1591        &self,
1592        input: RegexInput<'t, S>,
1593    ) -> Result<Option<Captures<'t, S>>> {
1594        self.captures_input_with_option_flags(&input, 0)
1595    }
1596
1597    /// Returns the capture groups for the first match in `text`, starting from
1598    /// the specified byte position `pos`.
1599    ///
1600    /// # Examples
1601    ///
1602    /// Finding captures starting at a position:
1603    ///
1604    /// ```
1605    /// # use fancy_regex::Regex;
1606    /// let re = Regex::new(r"(?m:^)(\d+)").unwrap();
1607    /// let text = "1 test 123\n2 foo";
1608    /// let captures = re.captures_from_pos(text, 7).unwrap().unwrap();
1609    ///
1610    /// let group = captures.get(1).unwrap();
1611    /// assert_eq!(group.as_str(), "2");
1612    /// assert_eq!(group.start(), 11);
1613    /// assert_eq!(group.end(), 12);
1614    /// ```
1615    ///
1616    /// Note that in some cases this is not the same as using the `captures`
1617    /// method and passing a slice of the string, see the capture that we get
1618    /// when we do this:
1619    ///
1620    /// ```
1621    /// # use fancy_regex::Regex;
1622    /// let re = Regex::new(r"(?m:^)(\d+)").unwrap();
1623    /// let text = "1 test 123\n2 foo";
1624    /// let captures = re.captures(&text[7..]).unwrap().unwrap();
1625    /// assert_eq!(captures.get(1).unwrap().as_str(), "123");
1626    /// ```
1627    ///
1628    /// This matched the number "123" because it's at the beginning of the text
1629    /// of the string slice.
1630    ///
1631    /// To constrain matching to a byte range without slicing, use
1632    /// [Regex::captures_input()] with [`RegexInput`].
1633    ///
1634    pub fn captures_from_pos<'t, S: input::Input + ?Sized>(
1635        &self,
1636        text: &'t S,
1637        pos: usize,
1638    ) -> Result<Option<Captures<'t, S>>> {
1639        self.captures_input(RegexInput::new(text).from_pos(pos))
1640    }
1641
1642    pub(crate) fn captures_input_with_option_flags<'t, S: input::Input + ?Sized>(
1643        &self,
1644        input: &RegexInput<'t, S>,
1645        option_flags: u32,
1646    ) -> Result<Option<Captures<'t, S>>> {
1647        if input.is_done() {
1648            return Ok(None);
1649        }
1650        let named_groups = self.named_groups.clone();
1651        let haystack = input.haystack();
1652        match &self.inner {
1653            RegexImpl::Wrap {
1654                inner,
1655                explicit_capture_group_0,
1656                ..
1657            } => {
1658                // find_not_empty patterns are always compiled as Fancy, so find_not_empty is
1659                // always false here.
1660                let explicit = *explicit_capture_group_0;
1661                let mut locations = inner.create_captures();
1662                let mut delegated_input = ra_input(input);
1663                if input.is_anchored() {
1664                    delegated_input = delegated_input.anchored(RaAnchored::Yes);
1665                }
1666                inner.captures(delegated_input, &mut locations);
1667                Ok(locations.is_match().then_some(Captures {
1668                    inner: CapturesImpl::Wrap {
1669                        locations,
1670                        explicit_capture_group_0: explicit,
1671                    },
1672                    named_groups,
1673                    input: haystack,
1674                }))
1675            }
1676            RegexImpl::Fancy {
1677                prog,
1678                n_groups,
1679                options,
1680                ..
1681            } => {
1682                #[allow(unused_mut)]
1683                let mut option_flags = option_flags
1684                    | if options.find_not_empty {
1685                        OPTION_FIND_NOT_EMPTY
1686                    } else {
1687                        0
1688                    };
1689                #[cfg(feature = "leftmost_longest")]
1690                {
1691                    option_flags |= if options.leftmost_longest {
1692                        OPTION_LEFTMOST_LONGEST
1693                    } else {
1694                        0
1695                    };
1696                }
1697                let result = vm::run(prog, input, option_flags, options)?;
1698                Ok(result.map(|mut saves| {
1699                    saves.truncate(n_groups * 2);
1700                    Captures {
1701                        inner: CapturesImpl::Fancy { saves },
1702                        named_groups,
1703                        input: haystack,
1704                    }
1705                }))
1706            }
1707        }
1708    }
1709
1710    pub(crate) fn seek_pattern(&self) -> &str {
1711        match &self.inner {
1712            RegexImpl::Wrap {
1713                delegated_pattern, ..
1714            } => delegated_pattern,
1715            RegexImpl::Fancy { prog, .. } => &prog.seek_pattern,
1716        }
1717    }
1718
1719    /// Returns the number of captures, including the implicit capture of the entire expression.
1720    pub fn captures_len(&self) -> usize {
1721        match &self.inner {
1722            RegexImpl::Wrap {
1723                inner,
1724                explicit_capture_group_0,
1725                ..
1726            } => inner.captures_len() - if *explicit_capture_group_0 { 1 } else { 0 },
1727            RegexImpl::Fancy { n_groups, .. } => *n_groups,
1728        }
1729    }
1730
1731    /// Returns an iterator over the capture names.
1732    pub fn capture_names(&self) -> CaptureNames<'_> {
1733        let mut names = Vec::new();
1734        names.resize(self.captures_len(), None);
1735        for (name, &i) in self.named_groups.iter() {
1736            names[i] = Some(name.as_str());
1737        }
1738        CaptureNames(names.into_iter())
1739    }
1740
1741    // for debugging only
1742    #[doc(hidden)]
1743    pub fn debug_print(&self, writer: &mut Formatter<'_>) -> fmt::Result {
1744        match &self.inner {
1745            RegexImpl::Wrap {
1746                delegated_pattern,
1747                explicit_capture_group_0,
1748                ..
1749            } => {
1750                write!(
1751                    writer,
1752                    "wrapped Regex {:?}, explicit_capture_group_0: {:}",
1753                    delegated_pattern, *explicit_capture_group_0
1754                )
1755            }
1756            RegexImpl::Fancy { prog, .. } => prog.debug_print(writer),
1757        }
1758    }
1759
1760    /// Replaces the leftmost-first match with the replacement provided.
1761    /// The replacement can be a regular string (where `$N` and `$name` are
1762    /// expanded to match capture groups) or a function that takes the matches'
1763    /// `Captures` and returns the replaced string.
1764    ///
1765    /// If no match is found, then a copy of the string is returned unchanged.
1766    ///
1767    /// # Replacement string syntax
1768    ///
1769    /// All instances of `$name` in the replacement text is replaced with the
1770    /// corresponding capture group `name`.
1771    ///
1772    /// `name` may be an integer corresponding to the index of the
1773    /// capture group (counted by order of opening parenthesis where `0` is the
1774    /// entire match) or it can be a name (consisting of letters, digits or
1775    /// underscores) corresponding to a named capture group.
1776    ///
1777    /// If `name` isn't a valid capture group (whether the name doesn't exist
1778    /// or isn't a valid index), then it is replaced with the empty string.
1779    ///
1780    /// The longest possible name is used. e.g., `$1a` looks up the capture
1781    /// group named `1a` and not the capture group at index `1`. To exert more
1782    /// precise control over the name, use braces, e.g., `${1}a`.
1783    ///
1784    /// To write a literal `$` use `$$`.
1785    ///
1786    /// # Examples
1787    ///
1788    /// Note that this function is polymorphic with respect to the replacement.
1789    /// In typical usage, this can just be a normal string:
1790    ///
1791    /// ```rust
1792    /// # use fancy_regex::Regex;
1793    /// let re = Regex::new("[^01]+").unwrap();
1794    /// assert_eq!(re.replace("1078910", ""), "1010");
1795    /// ```
1796    ///
1797    /// But anything satisfying the `Replacer` trait will work. For example,
1798    /// a closure of type `|&Captures| -> String` provides direct access to the
1799    /// captures corresponding to a match. This allows one to access
1800    /// capturing group matches easily:
1801    ///
1802    /// ```rust
1803    /// # use fancy_regex::{Regex, Captures};
1804    /// let re = Regex::new(r"([^,\s]+),\s+(\S+)").unwrap();
1805    /// let result = re.replace("Springsteen, Bruce", |caps: &Captures<'_, str>| {
1806    ///     format!("{} {}", &caps[2], &caps[1])
1807    /// });
1808    /// assert_eq!(result, "Bruce Springsteen");
1809    /// ```
1810    ///
1811    /// But this is a bit cumbersome to use all the time. Instead, a simple
1812    /// syntax is supported that expands `$name` into the corresponding capture
1813    /// group. Here's the last example, but using this expansion technique
1814    /// with named capture groups:
1815    ///
1816    /// ```rust
1817    /// # use fancy_regex::Regex;
1818    /// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(?P<first>\S+)").unwrap();
1819    /// let result = re.replace("Springsteen, Bruce", "$first $last");
1820    /// assert_eq!(result, "Bruce Springsteen");
1821    /// ```
1822    ///
1823    /// Note that using `$2` instead of `$first` or `$1` instead of `$last`
1824    /// would produce the same result. To write a literal `$` use `$$`.
1825    ///
1826    /// Sometimes the replacement string requires use of curly braces to
1827    /// delineate a capture group replacement and surrounding literal text.
1828    /// For example, if we wanted to join two words together with an
1829    /// underscore:
1830    ///
1831    /// ```rust
1832    /// # use fancy_regex::Regex;
1833    /// let re = Regex::new(r"(?P<first>\w+)\s+(?P<second>\w+)").unwrap();
1834    /// let result = re.replace("deep fried", "${first}_$second");
1835    /// assert_eq!(result, "deep_fried");
1836    /// ```
1837    ///
1838    /// Without the curly braces, the capture group name `first_` would be
1839    /// used, and since it doesn't exist, it would be replaced with the empty
1840    /// string.
1841    ///
1842    /// Finally, sometimes you just want to replace a literal string with no
1843    /// regard for capturing group expansion. This can be done by wrapping a
1844    /// byte string with `NoExpand`:
1845    ///
1846    /// ```rust
1847    /// # use fancy_regex::Regex;
1848    /// use fancy_regex::NoExpand;
1849    ///
1850    /// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(\S+)").unwrap();
1851    /// let result = re.replace("Springsteen, Bruce", NoExpand("$2 $last"));
1852    /// assert_eq!(result, "$2 $last");
1853    /// ```
1854    pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1855        self.replacen(text, 1, rep)
1856    }
1857
1858    /// Replaces all non-overlapping matches in `text` with the replacement
1859    /// provided. This is the same as calling `replacen` with `limit` set to
1860    /// `0`.
1861    ///
1862    /// See the documentation for `replace` for details on how to access
1863    /// capturing group matches in the replacement string.
1864    pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1865        self.replacen(text, 0, rep)
1866    }
1867
1868    /// Replaces at most `limit` non-overlapping matches in `text` with the
1869    /// replacement provided. If `limit` is 0, then all non-overlapping matches
1870    /// are replaced.
1871    ///
1872    /// Will panic if any errors are encountered. Use `try_replacen`, which this
1873    /// function unwraps, if you want to handle errors.
1874    ///
1875    /// See the documentation for `replace` for details on how to access
1876    /// capturing group matches in the replacement string.
1877    ///
1878    pub fn replacen<'t, R: Replacer>(&self, text: &'t str, limit: usize, rep: R) -> Cow<'t, str> {
1879        self.try_replacen(text, limit, rep).unwrap()
1880    }
1881
1882    /// Replaces at most `limit` non-overlapping matches in `text` with the
1883    /// replacement provided. If `limit` is 0, then all non-overlapping matches
1884    /// are replaced.
1885    ///
1886    /// Propagates any errors encountered, such as `RuntimeError::BacktrackLimitExceeded`.
1887    ///
1888    /// See the documentation for `replace` for details on how to access
1889    /// capturing group matches in the replacement string.
1890    pub fn try_replacen<'t, R: Replacer>(
1891        &self,
1892        text: &'t str,
1893        limit: usize,
1894        mut rep: R,
1895    ) -> Result<Cow<'t, str>> {
1896        // If we know that the replacement doesn't have any capture expansions,
1897        // then we can fast path. The fast path can make a tremendous
1898        // difference:
1899        //
1900        //   1) We use `find_iter` instead of `captures_iter`. Not asking for
1901        //      captures generally makes the regex engines faster.
1902        //   2) We don't need to look up all of the capture groups and do
1903        //      replacements inside the replacement string. We just push it
1904        //      at each match and be done with it.
1905        if let Some(rep) = rep.no_expansion() {
1906            let mut it = self.find_iter(text).enumerate().peekable();
1907            if it.peek().is_none() {
1908                return Ok(Cow::Borrowed(text));
1909            }
1910            let mut new = String::with_capacity(text.len());
1911            let mut last_match = 0;
1912            for (i, m) in it {
1913                let m = m?;
1914
1915                if limit > 0 && i >= limit {
1916                    break;
1917                }
1918                new.push_str(&text[last_match..m.start()]);
1919                new.push_str(&rep);
1920                last_match = m.end();
1921            }
1922            new.push_str(&text[last_match..]);
1923            return Ok(Cow::Owned(new));
1924        }
1925
1926        // The slower path, which we use if the replacement needs access to
1927        // capture groups.
1928        let mut it = self.captures_iter(text).enumerate().peekable();
1929        if it.peek().is_none() {
1930            return Ok(Cow::Borrowed(text));
1931        }
1932        let mut new = String::with_capacity(text.len());
1933        let mut last_match = 0;
1934        for (i, cap) in it {
1935            let cap = cap?;
1936
1937            if limit > 0 && i >= limit {
1938                break;
1939            }
1940            // unwrap on 0 is OK because captures only reports matches
1941            let m = cap.get(0).unwrap();
1942            new.push_str(&text[last_match..m.start()]);
1943            rep.replace_append(&cap, &mut new);
1944            last_match = m.end();
1945        }
1946        new.push_str(&text[last_match..]);
1947        Ok(Cow::Owned(new))
1948    }
1949
1950    /// Splits the string by matches of the regex.
1951    ///
1952    /// Returns an iterator over the substrings of the target string
1953    ///  that *aren't* matched by the regex.
1954    ///
1955    /// # Example
1956    ///
1957    /// To split a string delimited by arbitrary amounts of spaces or tabs:
1958    ///
1959    /// ```rust
1960    /// # use fancy_regex::Regex;
1961    /// let re = Regex::new(r"[ \t]+").unwrap();
1962    /// let target = "a b \t  c\td    e";
1963    /// let fields: Vec<&str> = re.split(target).map(|x| x.unwrap()).collect();
1964    /// assert_eq!(fields, vec!["a", "b", "c", "d", "e"]);
1965    /// ```
1966    pub fn split<'r, 'h>(&'r self, target: &'h str) -> Split<'r, 'h> {
1967        Split {
1968            matches: self.find_iter(target),
1969            next_start: 0,
1970            target,
1971        }
1972    }
1973
1974    /// Splits the string by matches of the regex at most `limit` times.
1975    ///
1976    /// Returns an iterator over the substrings of the target string
1977    /// that *aren't* matched by the regex.
1978    ///
1979    /// The `N`th substring is the remaining part of the target.
1980    ///
1981    /// # Example
1982    ///
1983    /// To split a string delimited by arbitrary amounts of spaces or tabs
1984    /// 3 times:
1985    ///
1986    /// ```rust
1987    /// # use fancy_regex::Regex;
1988    /// let re = Regex::new(r"[ \t]+").unwrap();
1989    /// let target = "a b \t  c\td    e";
1990    /// let fields: Vec<&str> = re.splitn(target, 3).map(|x| x.unwrap()).collect();
1991    /// assert_eq!(fields, vec!["a", "b", "c\td    e"]);
1992    /// ```
1993    pub fn splitn<'r, 'h>(&'r self, target: &'h str, limit: usize) -> SplitN<'r, 'h> {
1994        SplitN {
1995            splits: self.split(target),
1996            limit,
1997        }
1998    }
1999}
2000
2001/// `Display` adapter that prints a [`Regex`]'s internal representation via
2002/// [`Regex::debug_print`]. Intended for debugging and test output only.
2003#[doc(hidden)]
2004#[derive(Debug)]
2005#[allow(dead_code)]
2006pub struct DebugRegex<'a>(pub &'a Regex);
2007
2008impl fmt::Display for DebugRegex<'_> {
2009    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2010        self.0.debug_print(f)
2011    }
2012}
2013
2014fn ra_input<'a, S: input::Input + ?Sized>(input: &'a RegexInput<'a, S>) -> RaInput<'a> {
2015    let mut ra_input = RaInput::new(input.haystack().as_bytes()).range(input.get_range());
2016    ra_input.set_start(input.effective_start());
2017    ra_input
2018}
2019
2020impl TryFrom<&str> for Regex {
2021    type Error = Error;
2022
2023    /// Attempts to parse a string into a regular expression
2024    fn try_from(s: &str) -> Result<Self> {
2025        Self::new(s)
2026    }
2027}
2028
2029impl TryFrom<String> for Regex {
2030    type Error = Error;
2031
2032    /// Attempts to parse a string into a regular expression
2033    fn try_from(s: String) -> Result<Self> {
2034        Self::new(&s)
2035    }
2036}
2037
2038impl<'t> Match<'t> {
2039    /// Returns the starting byte offset of the match in the text.
2040    #[inline]
2041    pub fn start(&self) -> usize {
2042        self.start
2043    }
2044
2045    /// Returns the ending byte offset of the match in the text.
2046    #[inline]
2047    pub fn end(&self) -> usize {
2048        self.end
2049    }
2050
2051    /// Returns the range over the starting and ending byte offsets of the match in text.
2052    #[inline]
2053    pub fn range(&self) -> Range<usize> {
2054        self.start..self.end
2055    }
2056
2057    /// Returns the matched text.
2058    #[inline]
2059    pub fn as_str(&self) -> &'t str {
2060        &self.text[self.start..self.end]
2061    }
2062
2063    /// Creates a new match from the given text and byte offsets.
2064    pub(crate) fn new(text: &'t str, start: usize, end: usize) -> Match<'t> {
2065        Match { text, start, end }
2066    }
2067}
2068
2069impl<'t> From<Match<'t>> for &'t str {
2070    fn from(m: Match<'t>) -> &'t str {
2071        m.as_str()
2072    }
2073}
2074
2075impl<'t> From<Match<'t>> for Range<usize> {
2076    fn from(m: Match<'t>) -> Range<usize> {
2077        m.range()
2078    }
2079}
2080
2081#[allow(clippy::len_without_is_empty)] // follow regex's API
2082impl<'t, S: input::Input + ?Sized> Captures<'t, S> {
2083    pub(crate) fn get_span(&self, i: usize) -> Option<(usize, usize)> {
2084        self.inner.get_span(i)
2085    }
2086
2087    /// Get the capture group by its index in the regex.
2088    ///
2089    /// If there is no match for that group or the index does not correspond to a group, `None` is
2090    /// returned. The index 0 returns the whole match.
2091    pub fn get(&self, i: usize) -> Option<S::Match<'t>> {
2092        self.inner
2093            .get_span(i)
2094            .map(|(start, end)| self.input.make_match(start, end))
2095    }
2096
2097    /// Returns the match for a named capture group.  Returns `None` the capture
2098    /// group did not match or if there is no group with the given name.
2099    pub fn name(&self, name: &str) -> Option<S::Match<'t>> {
2100        self.named_groups.get(name).and_then(|i| self.get(*i))
2101    }
2102
2103    /// Iterate over the captured groups in order in which they appeared in the regex. The first
2104    /// capture corresponds to the whole match.
2105    pub fn iter<'c>(&'c self) -> SubCaptureMatches<'c, 't, S> {
2106        SubCaptureMatches { data: self, i: 0 }
2107    }
2108
2109    /// How many groups were captured. This is always at least 1 because group 0 returns the whole
2110    /// match.
2111    pub fn len(&self) -> usize {
2112        self.inner.len()
2113    }
2114
2115    /// Returns the byte slice of the entire input that was searched.
2116    pub fn input_as_bytes(&self) -> &'t [u8] {
2117        self.input.as_bytes()
2118    }
2119}
2120
2121// str-only methods
2122impl<'t> Captures<'t, str> {
2123    /// Expands all instances of `$group` in `replacement` to the corresponding
2124    /// capture group `name`, and writes them to the `dst` buffer given.
2125    ///
2126    /// `group` may be an integer corresponding to the index of the
2127    /// capture group (counted by order of opening parenthesis where `\0` is the
2128    /// entire match) or it can be a name (consisting of letters, digits or
2129    /// underscores) corresponding to a named capture group.
2130    ///
2131    /// If `group` isn't a valid capture group (whether the name doesn't exist
2132    /// or isn't a valid index), then it is replaced with the empty string.
2133    ///
2134    /// The longest possible name is used. e.g., `$1a` looks up the capture
2135    /// group named `1a` and not the capture group at index `1`. To exert more
2136    /// precise control over the name, use braces, e.g., `${1}a`.
2137    ///
2138    /// To write a literal `$`, use `$$`.
2139    ///
2140    /// For more control over expansion, see [`Expander`].
2141    ///
2142    /// [`Expander`]: expand/struct.Expander.html
2143    pub fn expand(&self, replacement: &str, dst: &mut String) {
2144        Expander::default().append_expansion(dst, replacement, self);
2145    }
2146}
2147
2148/// Get a group by index.
2149///
2150/// `'t` is the lifetime of the matched text.
2151///
2152/// The text can't outlive the `Captures` object if this method is
2153/// used, because of how `Index` is defined (normally `a[i]` is part
2154/// of `a` and can't outlive it); to do that, use `get()` instead.
2155///
2156/// # Panics
2157///
2158/// If there is no group at the given index.
2159impl<'t> Index<usize> for Captures<'t, str> {
2160    type Output = str;
2161
2162    fn index(&self, i: usize) -> &str {
2163        self.get(i)
2164            .map(|m| m.as_str())
2165            .unwrap_or_else(|| panic!("no group at index '{}'", i))
2166    }
2167}
2168
2169/// Get a group by name.
2170///
2171/// `'t` is the lifetime of the matched text and `'i` is the lifetime
2172/// of the group name (the index).
2173///
2174/// The text can't outlive the `Captures` object if this method is
2175/// used, because of how `Index` is defined (normally `a[i]` is part
2176/// of `a` and can't outlive it); to do that, use `name` instead.
2177///
2178/// # Panics
2179///
2180/// If there is no group named by the given value.
2181impl<'t, 'i> Index<&'i str> for Captures<'t, str> {
2182    type Output = str;
2183
2184    fn index<'a>(&'a self, name: &'i str) -> &'a str {
2185        self.name(name)
2186            .map(|m| m.as_str())
2187            .unwrap_or_else(|| panic!("no group named '{}'", name))
2188    }
2189}
2190
2191impl<'c, 't, S: input::Input + ?Sized> Iterator for SubCaptureMatches<'c, 't, S> {
2192    type Item = Option<S::Match<'t>>;
2193
2194    fn next(&mut self) -> Option<Option<S::Match<'t>>> {
2195        if self.i < self.data.len() {
2196            let result = self.data.get(self.i);
2197            self.i += 1;
2198            Some(result)
2199        } else {
2200            None
2201        }
2202    }
2203}
2204
2205// TODO: might be nice to implement ExactSizeIterator etc for SubCaptures
2206
2207/// Regular expression AST. This is public for now but may change.
2208#[derive(Debug, PartialEq, Eq, Clone)]
2209pub enum Expr {
2210    /// An empty expression, e.g. the last branch in `(a|b|)`
2211    Empty,
2212    /// Any character, regex `.`
2213    Any {
2214        /// Whether it also matches newlines or not
2215        newline: bool,
2216        /// Whether CRLF mode is enabled (`\r` also counts as a newline, so dot
2217        /// excludes both `\r` and `\n`)
2218        crlf: bool,
2219    },
2220    /// An assertion
2221    Assertion(Assertion),
2222    /// General newline sequence, `\R`
2223    /// Matches `\r\n` or any single newline character (\n, \v, \f, \r)
2224    /// In Unicode mode, also matches U+0085, U+2028, U+2029
2225    GeneralNewline {
2226        /// Whether Unicode mode is enabled
2227        unicode: bool,
2228    },
2229    /// The string as a literal, e.g. `a`
2230    Literal {
2231        /// The string to match
2232        val: String,
2233        /// Whether match is case-insensitive or not
2234        casei: bool,
2235    },
2236    /// Concatenation of multiple expressions, must match in order, e.g. `a.` is a concatenation of
2237    /// the literal `a` and `.` for any character
2238    Concat(Vec<Expr>),
2239    /// Alternative of multiple expressions, one of them must match, e.g. `a|b` is an alternative
2240    /// where either the literal `a` or `b` must match
2241    Alt(Vec<Expr>),
2242    /// Capturing group of expression, e.g. `(a.)` matches `a` and any character and "captures"
2243    /// (remembers) the match
2244    Group(Arc<Expr>),
2245    /// Look-around (e.g. positive/negative look-ahead or look-behind) with an expression, e.g.
2246    /// `(?=a)` means the next character must be `a` (but the match is not consumed)
2247    LookAround(Box<Expr>, LookAround),
2248    /// Repeat of an expression, e.g. `a*` or `a+` or `a{1,3}`
2249    Repeat {
2250        /// The expression that is being repeated
2251        child: Box<Expr>,
2252        /// The minimum number of repetitions
2253        lo: usize,
2254        /// The maximum number of repetitions (or `usize::MAX`)
2255        hi: usize,
2256        /// Greedy means as much as possible is matched, e.g. `.*b` would match all of `abab`.
2257        /// Non-greedy means as little as possible, e.g. `.*?b` would match only `ab` in `abab`.
2258        greedy: bool,
2259    },
2260    /// Delegate a regex to the regex crate. This is used as a simplification so that we don't have
2261    /// to represent all the expressions in the AST, e.g. character classes.
2262    ///
2263    /// **Constraint**: All Delegate expressions must match exactly 1 character. This ensures
2264    /// consistent analysis and compilation behavior. For zero-width or multi-character patterns,
2265    /// use the appropriate Expr variants instead (e.g., Assertion, Repeat, Concat).
2266    Delegate {
2267        /// The regex
2268        inner: String,
2269        /// Whether the matching is case-insensitive or not
2270        casei: bool,
2271    },
2272    /// Back reference to a capture group, e.g. `\1` in `(abc|def)\1` references the captured group
2273    /// and the whole regex matches either `abcabc` or `defdef`.
2274    Backref {
2275        /// The capture group number being referenced
2276        group: usize,
2277        /// Whether the matching is case-insensitive or not
2278        casei: bool,
2279    },
2280    /// Back reference to a capture group at the given specified relative recursion level.
2281    BackrefWithRelativeRecursionLevel {
2282        /// The capture group number being referenced
2283        group: usize,
2284        /// Relative recursion level
2285        relative_level: isize,
2286        /// Whether the matching is case-insensitive or not
2287        casei: bool,
2288    },
2289    /// Atomic non-capturing group, e.g. `(?>ab|a)` in text that contains `ab` will match `ab` and
2290    /// never backtrack and try `a`, even if matching fails after the atomic group.
2291    AtomicGroup(Box<Expr>),
2292    /// Keep matched text so far out of overall match
2293    KeepOut,
2294    /// Anchor to match at the position where the previous match ended
2295    ContinueFromPreviousMatchEnd,
2296    /// Conditional expression based on whether the numbered capture group matched or not.
2297    /// The optional `relative_recursion_level` qualifies which recursion level's capture is
2298    /// tested (Oniguruma `(?(name+N)...)` syntax).
2299    BackrefExistsCondition {
2300        /// The resolved capture group number
2301        group: usize,
2302        /// Optional relative recursion level (e.g. `+0`, `-1`)
2303        relative_recursion_level: Option<isize>,
2304    },
2305    /// If/Then/Else Condition. If there is no Then/Else, these will just be empty expressions.
2306    Conditional {
2307        /// The conditional expression to evaluate
2308        condition: Box<Expr>,
2309        /// What to execute if the condition is true
2310        true_branch: Box<Expr>,
2311        /// What to execute if the condition is false
2312        false_branch: Box<Expr>,
2313    },
2314    /// Subroutine call to the specified group number
2315    SubroutineCall(usize),
2316    /// Backtracking control verb
2317    BacktrackingControlVerb(BacktrackingControlVerb),
2318    /// Match while the given expression is absent from the haystack
2319    Absent(Absent),
2320    /// DEFINE group - defines capture groups for subroutines without matching anything
2321    /// The expressions inside are parsed and assigned group numbers, but no VM instructions
2322    /// are generated for the DEFINE block itself.
2323    DefineGroup {
2324        /// The expressions/groups being defined
2325        definitions: Box<Expr>,
2326    },
2327    /// Abstract Syntax Tree node - will be resolved into an Expr before analysis.
2328    /// Contains the position in the pattern where the node was parsed from
2329    AstNode(AstNode, usize),
2330}
2331
2332/// Target of a backreference or subroutine call
2333#[derive(Debug, PartialEq, Eq, Clone)]
2334pub enum CaptureGroupTarget {
2335    /// Direct numbered reference
2336    ByNumber(usize),
2337
2338    /// Named reference
2339    ByName(String),
2340
2341    /// Relative reference (e.g., -1, -2, etc.)
2342    Relative(isize),
2343}
2344
2345/// Abstract Syntax Tree node - will be resolved into an Expr before analysis
2346#[derive(Debug, PartialEq, Eq, Clone)]
2347pub enum AstNode {
2348    /// Group with optional name - name is only present if explicitly specified in pattern
2349    AstGroup {
2350        /// Optional name of the capture group, present only when explicitly named in the pattern
2351        name: Option<String>,
2352        /// The inner expression of the group
2353        inner: Box<Expr>,
2354    },
2355    /// Backreference
2356    Backref {
2357        /// The target capture group being referenced
2358        target: CaptureGroupTarget,
2359        /// Whether the matching is case-insensitive or not
2360        // TODO: move out of Backref and prefer a Flags AstNode. The resolver can then track the flags and set casei on the resolved Expr accordingly
2361        casei: bool,
2362        /// Optional relative recursion level for the backreference
2363        relative_recursion_level: Option<isize>,
2364    },
2365    /// Subroutine Call
2366    SubroutineCall(CaptureGroupTarget),
2367    /// Backreference exists condition `(?(name)...)` or `(?(1)...)` - unresolved target.
2368    /// The optional `relative_recursion_level` corresponds to the Oniguruma `+N`/`-N` suffix
2369    /// (e.g. `(?(name+0)...)`) which qualifies which recursion level's capture is tested.
2370    BackrefExistsCondition {
2371        /// The target capture group being tested for existence
2372        target: CaptureGroupTarget,
2373        /// Optional relative recursion level qualifier (e.g. `+0`, `-1`)
2374        relative_recursion_level: Option<isize>,
2375    },
2376}
2377
2378/// Type of look-around assertion as used for a look-around expression.
2379#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2380pub enum LookAround {
2381    /// Look-ahead assertion, e.g. `(?=a)`
2382    LookAhead,
2383    /// Negative look-ahead assertion, e.g. `(?!a)`
2384    LookAheadNeg,
2385    /// Look-behind assertion, e.g. `(?<=a)`
2386    LookBehind,
2387    /// Negative look-behind assertion, e.g. `(?<!a)`
2388    LookBehindNeg,
2389}
2390
2391/// Type of absent operator as used for Oniguruma's absent functionality.
2392#[derive(Debug, PartialEq, Eq, Clone)]
2393pub enum Absent {
2394    /// Absent repeater `(?~absent)` - works like `\O*` (match any character including newline, repeated)
2395    /// but is limited by the range that does not include the string match with `absent`.
2396    /// This is a written abbreviation of `(?~|absent|\O*)`.
2397    Repeater(Box<Expr>),
2398    /// Absent expression `(?~|absent|exp)` - works like `exp`, but is limited by the range
2399    /// that does not include the string match with `absent`.
2400    Expression {
2401        /// The expression to avoid matching
2402        absent: Box<Expr>,
2403        /// The expression to match
2404        exp: Box<Expr>,
2405    },
2406    /// Absent stopper `(?~|absent)` - after this operator, haystack range is limited
2407    /// up to the point where `absent` matches.
2408    Stopper(Box<Expr>),
2409    /// Range clear `(?~|)` - clears the effects caused by absent stoppers.
2410    Clear,
2411}
2412
2413/// Type of backtracking control verb which affects how backtracking will behave.
2414/// See <https://www.regular-expressions.info/verb.html>
2415#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2416pub enum BacktrackingControlVerb {
2417    /// Fail this branch immediately
2418    Fail,
2419    /// Treat match so far as successful overall match
2420    Accept,
2421    /// Abort the entire match on failure
2422    Commit,
2423    /// Restart the entire match attempt at the current position
2424    Skip,
2425    /// Prune all backtracking states and restart the entire match attempt at the next position
2426    Prune,
2427}
2428
2429/// An iterator over capture names in a [Regex].  The iterator
2430/// returns the name of each group, or [None] if the group has
2431/// no name.  Because capture group 0 cannot have a name, the
2432/// first item returned is always [None].
2433pub struct CaptureNames<'r>(vec::IntoIter<Option<&'r str>>);
2434
2435impl Debug for CaptureNames<'_> {
2436    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2437        f.write_str("<CaptureNames>")
2438    }
2439}
2440
2441impl<'r> Iterator for CaptureNames<'r> {
2442    type Item = Option<&'r str>;
2443
2444    fn next(&mut self) -> Option<Self::Item> {
2445        self.0.next()
2446    }
2447}
2448
2449// silly to write my own, but this is super-fast for the common 1-digit
2450// case.
2451fn push_usize(s: &mut String, x: usize) {
2452    if x >= 10 {
2453        push_usize(s, x / 10);
2454        s.push((b'0' + (x % 10) as u8) as char);
2455    } else {
2456        s.push((b'0' + (x as u8)) as char);
2457    }
2458}
2459
2460/// Emit a repeat quantifier `?`, `*`, `+`, or `{lo,hi}` (optionally non-greedy) into `buf`.
2461///
2462/// This is shared between [`Expr::to_str`] and `build_seek_pattern` in the compiler.
2463pub(crate) fn write_quantifier(buf: &mut String, lo: usize, hi: usize, greedy: bool) {
2464    match (lo, hi) {
2465        (0, 1) => buf.push('?'),
2466        (0, usize::MAX) => buf.push('*'),
2467        (1, usize::MAX) => buf.push('+'),
2468        (lo, hi) => {
2469            buf.push('{');
2470            push_usize(buf, lo);
2471            if lo != hi {
2472                buf.push(',');
2473                if hi != usize::MAX {
2474                    push_usize(buf, hi);
2475                }
2476            }
2477            buf.push('}');
2478        }
2479    }
2480    if !greedy {
2481        buf.push('?');
2482    }
2483}
2484
2485fn is_special(c: char) -> bool {
2486    matches!(
2487        c,
2488        '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#'
2489    )
2490}
2491
2492pub(crate) fn push_quoted(buf: &mut String, s: &str) {
2493    if !s.bytes().any(|b| {
2494        b == b'\\'
2495            || b == b'\n'
2496            || b == b'\t'
2497            || b == b'\r'
2498            || matches!(
2499                b,
2500                b'.' | b'+'
2501                    | b'*'
2502                    | b'?'
2503                    | b'('
2504                    | b')'
2505                    | b'|'
2506                    | b'['
2507                    | b']'
2508                    | b'{'
2509                    | b'}'
2510                    | b'^'
2511                    | b'$'
2512                    | b'#'
2513            )
2514    }) {
2515        buf.push_str(s);
2516        return;
2517    }
2518
2519    for c in s.chars() {
2520        match c {
2521            '\\' => buf.push_str("\\\\"),
2522            '\n' => buf.push_str("\\n"),
2523            '\t' => buf.push_str("\\t"),
2524            '\r' => buf.push_str("\\r"),
2525            _ => {
2526                if is_special(c) {
2527                    buf.push('\\');
2528                }
2529                buf.push(c);
2530            }
2531        }
2532    }
2533}
2534
2535/// Escapes special characters in `text` with '\\'.  Returns a string which, when interpreted
2536/// as a regex, matches exactly `text`.
2537pub fn escape(text: &str) -> Cow<'_, str> {
2538    // Using bytes() is OK because all special characters are single bytes.
2539    match text.bytes().filter(|&b| is_special(b as char)).count() {
2540        0 => Cow::Borrowed(text),
2541        n => {
2542            // The capacity calculation is exact because '\\' is a single byte.
2543            let mut buf = String::with_capacity(text.len() + n);
2544            push_quoted(&mut buf, text);
2545            Cow::Owned(buf)
2546        }
2547    }
2548}
2549
2550/// Type of assertions
2551#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2552pub enum Assertion {
2553    /// Start of input text
2554    StartText,
2555    /// End of input text
2556    EndText,
2557    /// End of input text, or before any trailing newlines at the end (Oniguruma's `\Z`)
2558    EndTextIgnoreTrailingNewlines {
2559        /// Whether CRLF mode is enabled.
2560        /// If `true`, trailing `\r\n` pairs (in addition to bare `\n`) are also ignored.
2561        crlf: bool,
2562    },
2563    /// Start of a line
2564    StartLine {
2565        /// CRLF mode.
2566        /// If true, this assertion matches at the starting position of the input text, or at the position immediately
2567        /// following either a `\r` or `\n` character, but never after a `\r` when a `\n` follows.
2568        crlf: bool,
2569    },
2570    /// Start of a line in Oniguruma mode.
2571    /// Behaves like [`Assertion::StartLine`], but additionally rejects matches at the end of the input
2572    /// when it is preceded by a newline.
2573    StartLineOniguruma {
2574        /// CRLF mode.
2575        /// If true, this assertion matches at the starting position of the input text, or at the position immediately
2576        /// following either a `\r` or `\n` character, but never after a `\r` when a `\n` follows.
2577        crlf: bool,
2578    },
2579    /// End of a line
2580    EndLine {
2581        /// CRLF mode
2582        /// If true, this assertion matches at the ending position of the input text, or at the position immediately
2583        /// preceding either a `\r` or `\n` character, but never after a `\r` when a `\n` follows.
2584        crlf: bool,
2585    },
2586    /// Left word boundary
2587    LeftWordBoundary,
2588    /// Left word half boundary
2589    LeftWordHalfBoundary,
2590    /// Right word boundary
2591    RightWordBoundary,
2592    /// Right word half boundary
2593    RightWordHalfBoundary,
2594    /// Both word boundaries
2595    WordBoundary,
2596    /// Not word boundary
2597    NotWordBoundary,
2598}
2599
2600impl Assertion {
2601    pub(crate) fn is_always_hard(&self) -> bool {
2602        use Assertion::*;
2603        matches!(
2604            self,
2605            // these will make regex-automata use PikeVM and are not compabible with certain regex-automata features we use
2606            LeftWordBoundary
2607                | LeftWordHalfBoundary
2608                | RightWordBoundary
2609                | RightWordHalfBoundary
2610                | WordBoundary
2611                | NotWordBoundary
2612                // `\Z` needs custom trailing-newline handling.
2613                | EndTextIgnoreTrailingNewlines { .. }
2614        )
2615    }
2616}
2617
2618/// An iterator over the immediate children of an [`Expr`].
2619///
2620/// This iterator yields references to child expressions but does not recurse into them.
2621#[derive(Debug)]
2622pub enum ExprChildrenIter<'a> {
2623    /// No children (leaf node)
2624    Empty,
2625    /// A single child (Group, LookAround, AtomicGroup, Repeat)
2626    Single(Option<&'a Expr>),
2627    /// Multiple children in a Vec (Concat, Alt)
2628    Vec(alloc::slice::Iter<'a, Expr>),
2629    /// Three children (Conditional)
2630    Triple {
2631        /// First child
2632        first: Option<&'a Expr>,
2633        /// Second child
2634        second: Option<&'a Expr>,
2635        /// Third child
2636        third: Option<&'a Expr>,
2637    },
2638}
2639
2640/// An iterator over the immediate children of an [`Expr`] for mutable access.
2641///
2642/// This iterator yields mutable references to child expressions but does not recurse into them.
2643#[derive(Debug)]
2644pub enum ExprChildrenIterMut<'a> {
2645    /// No children (leaf node)
2646    Empty,
2647    /// A single child (Group, LookAround, AtomicGroup, Repeat)
2648    Single(Option<&'a mut Expr>),
2649    /// Multiple children in a Vec (Concat, Alt)
2650    Vec(alloc::slice::IterMut<'a, Expr>),
2651    /// Three children (Conditional)
2652    Triple {
2653        /// First child
2654        first: Option<&'a mut Expr>,
2655        /// Second child
2656        second: Option<&'a mut Expr>,
2657        /// Third child
2658        third: Option<&'a mut Expr>,
2659    },
2660}
2661
2662impl<'a> Iterator for ExprChildrenIter<'a> {
2663    type Item = &'a Expr;
2664
2665    fn next(&mut self) -> Option<Self::Item> {
2666        match self {
2667            ExprChildrenIter::Empty => None,
2668            ExprChildrenIter::Single(ref mut child) => child.take(),
2669            ExprChildrenIter::Vec(ref mut iter) => iter.next(),
2670            ExprChildrenIter::Triple {
2671                ref mut first,
2672                ref mut second,
2673                ref mut third,
2674            } => first
2675                .take()
2676                .or_else(|| second.take())
2677                .or_else(|| third.take()),
2678        }
2679    }
2680}
2681
2682impl<'a> Iterator for ExprChildrenIterMut<'a> {
2683    type Item = &'a mut Expr;
2684
2685    fn next(&mut self) -> Option<Self::Item> {
2686        match self {
2687            ExprChildrenIterMut::Empty => None,
2688            ExprChildrenIterMut::Single(ref mut child) => child.take(),
2689            ExprChildrenIterMut::Vec(ref mut iter) => iter.next(),
2690            ExprChildrenIterMut::Triple {
2691                ref mut first,
2692                ref mut second,
2693                ref mut third,
2694            } => first
2695                .take()
2696                .or_else(|| second.take())
2697                .or_else(|| third.take()),
2698        }
2699    }
2700}
2701
2702macro_rules! children_iter_match {
2703    ($self:expr, $iter:ident, $vec_method:ident, $single_method:ident, $group_method:ident) => {
2704        match $self {
2705            Expr::Concat(children) | Expr::Alt(children) => $iter::Vec(children.$vec_method()),
2706            Expr::Group(child) => $iter::Single(Some(Arc::$group_method(child))),
2707            Expr::Absent(Absent::Repeater(child))
2708            | Expr::Absent(Absent::Stopper(child))
2709            | Expr::LookAround(child, _)
2710            | Expr::AtomicGroup(child)
2711            | Expr::Repeat { child, .. } => $iter::Single(Some(child.$single_method())),
2712            Expr::Conditional {
2713                condition,
2714                true_branch,
2715                false_branch,
2716            } => $iter::Triple {
2717                first: Some(condition.$single_method()),
2718                second: Some(true_branch.$single_method()),
2719                third: Some(false_branch.$single_method()),
2720            },
2721            Expr::Absent(Absent::Expression { absent, exp }) => $iter::Triple {
2722                first: Some(absent.$single_method()),
2723                second: Some(exp.$single_method()),
2724                third: None,
2725            },
2726            Expr::DefineGroup { definitions } => $iter::Single(Some(definitions.$single_method())),
2727            _ if $self.is_leaf_node() => $iter::Empty,
2728            _ => unimplemented!(),
2729        }
2730    };
2731}
2732impl Expr {
2733    /// Parse the regex and return an expression (AST) and a bit set with the indexes of groups
2734    /// that are referenced by backrefs.
2735    pub fn parse_tree(re: &str) -> Result<ExprTree> {
2736        Expr::parse_tree_with_flags(re, RegexOptions::default().compute_flags())
2737    }
2738
2739    /// Parse the regex and return an expression (AST)
2740    /// Flags should be bit based based on flags
2741    pub fn parse_tree_with_flags(re: &str, flags: u32) -> Result<ExprTree> {
2742        Parser::parse_with_flags(re, flags)
2743    }
2744
2745    /// Returns `true` if this expression is a leaf node (has no children).
2746    ///
2747    /// Leaf nodes include literals, assertions, backreferences, and other atomic expressions.
2748    /// Non-leaf nodes include groups, concatenations, alternations, and repetitions.
2749    pub fn is_leaf_node(&self) -> bool {
2750        matches!(
2751            self,
2752            Expr::Empty
2753                | Expr::Any { .. }
2754                | Expr::Assertion(_)
2755                | Expr::GeneralNewline { .. }
2756                | Expr::Literal { .. }
2757                | Expr::Delegate { .. }
2758                | Expr::Backref { .. }
2759                | Expr::BackrefWithRelativeRecursionLevel { .. }
2760                | Expr::KeepOut
2761                | Expr::ContinueFromPreviousMatchEnd
2762                | Expr::BackrefExistsCondition { .. }
2763                | Expr::BacktrackingControlVerb(_)
2764                |             Expr::SubroutineCall(_)
2765                | Expr::Absent(Absent::Clear)
2766                // An unresolved AstNode has no separate child Expr to iterate; the resolver
2767                // should have replaced it before analysis, so treat it as a leaf so that
2768                // collection/iteration doesn't panic, and let the analyzer emit the error.
2769                | Expr::AstNode(..),
2770        )
2771    }
2772
2773    /// Returns `true` if any descendant of this expression (not including itself)
2774    /// satisfies the given predicate.
2775    ///
2776    /// This performs an iterative depth-first search using [`children_iter`](Self::children_iter).
2777    pub fn has_descendant(&self, predicate: impl Fn(&Expr) -> bool) -> bool {
2778        let mut stack: Vec<&Expr> = self.children_iter().collect();
2779        while let Some(expr) = stack.pop() {
2780            if predicate(expr) {
2781                return true;
2782            }
2783            stack.extend(expr.children_iter());
2784        }
2785        false
2786    }
2787
2788    /// Returns an iterator over the immediate children of this expression.
2789    ///
2790    /// For leaf nodes, this returns an empty iterator. For non-leaf nodes, it returns
2791    /// references to their immediate children (non-recursive).
2792    pub fn children_iter(&self) -> ExprChildrenIter<'_> {
2793        children_iter_match!(self, ExprChildrenIter, iter, as_ref, as_ref)
2794    }
2795
2796    /// Returns an iterator over the immediate children of this expression for mutable access.
2797    ///
2798    /// For leaf nodes, this returns an empty iterator. For non-leaf nodes, it returns
2799    /// mutable references to their immediate children (non-recursive).
2800    pub fn children_iter_mut(&mut self) -> ExprChildrenIterMut<'_> {
2801        children_iter_match!(self, ExprChildrenIterMut, iter_mut, as_mut, make_mut)
2802    }
2803
2804    /// Convert expression to a regex string in the regex crate's syntax.
2805    ///
2806    /// # Panics
2807    ///
2808    /// Panics for expressions that are hard, i.e. can not be handled by the regex crate.
2809    pub fn to_str(&self, buf: &mut String, precedence: u8) {
2810        match *self {
2811            Expr::Empty => (),
2812            Expr::Any { newline, crlf } => buf.push_str(match (newline, crlf) {
2813                (true, _) => "(?s:.)",
2814                (false, true) => "(?R-s:.)",
2815                (false, false) => ".",
2816            }),
2817            Expr::Literal { ref val, casei } => {
2818                if casei {
2819                    buf.push_str("(?i:");
2820                }
2821                push_quoted(buf, val);
2822                if casei {
2823                    buf.push(')');
2824                }
2825            }
2826            Expr::Assertion(Assertion::StartText) => buf.push('^'),
2827            Expr::Assertion(Assertion::EndText) => buf.push('$'),
2828            Expr::Assertion(
2829                Assertion::StartLine { crlf: false }
2830                | Assertion::StartLineOniguruma { crlf: false },
2831            ) => buf.push_str("(?m:^)"),
2832            Expr::Assertion(Assertion::EndLine { crlf: false }) => buf.push_str("(?m:$)"),
2833            Expr::Assertion(
2834                Assertion::StartLine { crlf: true } | Assertion::StartLineOniguruma { crlf: true },
2835            ) => buf.push_str("(?Rm:^)"),
2836            Expr::Assertion(Assertion::EndLine { crlf: true }) => buf.push_str("(?Rm:$)"),
2837            Expr::Concat(ref children) => {
2838                if precedence > 1 {
2839                    buf.push_str("(?:");
2840                }
2841                for child in children {
2842                    child.to_str(buf, 2);
2843                }
2844                if precedence > 1 {
2845                    buf.push(')')
2846                }
2847            }
2848            Expr::Alt(_) => {
2849                if precedence > 0 {
2850                    buf.push_str("(?:");
2851                }
2852                let mut children = self.children_iter();
2853                if let Some(first) = children.next() {
2854                    first.to_str(buf, 1);
2855                    for child in children {
2856                        buf.push('|');
2857                        child.to_str(buf, 1);
2858                    }
2859                }
2860                if precedence > 0 {
2861                    buf.push(')');
2862                }
2863            }
2864            Expr::Group(ref child) => {
2865                buf.push('(');
2866                child.to_str(buf, 0);
2867                buf.push(')');
2868            }
2869            Expr::Repeat {
2870                ref child,
2871                lo,
2872                hi,
2873                greedy,
2874            } => {
2875                if precedence > 2 {
2876                    buf.push_str("(?:");
2877                }
2878                child.to_str(buf, 3);
2879                write_quantifier(buf, lo, hi, greedy);
2880                if precedence > 2 {
2881                    buf.push(')');
2882                }
2883            }
2884            Expr::Delegate {
2885                ref inner, casei, ..
2886            } => {
2887                // at the moment, delegate nodes are just atoms
2888                if casei {
2889                    buf.push_str("(?i:");
2890                }
2891                buf.push_str(inner);
2892                if casei {
2893                    buf.push(')');
2894                }
2895            }
2896            Expr::DefineGroup { .. } => {
2897                // DEFINE groups match nothing - output empty string for delegation
2898            }
2899            _ => panic!("attempting to format hard expr {:?}", self),
2900        }
2901    }
2902}
2903
2904// precondition: ix > 0
2905fn prev_codepoint_ix(s: &str, mut ix: usize) -> usize {
2906    let bytes = s.as_bytes();
2907    loop {
2908        ix -= 1;
2909        // fancy bit magic for ranges 0..0x80 + 0xc0..
2910        if (bytes[ix] as i8) >= -0x40 {
2911            break;
2912        }
2913    }
2914    ix
2915}
2916
2917fn codepoint_len(b: u8) -> usize {
2918    match b {
2919        b if b < 0x80 => 1,
2920        b if b < 0xe0 => 2,
2921        b if b < 0xf0 => 3,
2922        _ => 4,
2923    }
2924}
2925
2926// If this returns false, then there is no possible backref in the re
2927
2928// Both potential implementations are turned off, because we currently
2929// always need to do a deeper analysis because of 1-character
2930// look-behind. If we could call a find_from_pos method of regex::Regex,
2931// it would make sense to bring this back.
2932/*
2933pub fn detect_possible_backref(re: &str) -> bool {
2934    let mut last = b'\x00';
2935    for b in re.as_bytes() {
2936        if b'0' <= *b && *b <= b'9' && last == b'\\' { return true; }
2937        last = *b;
2938    }
2939    false
2940}
2941
2942pub fn detect_possible_backref(re: &str) -> bool {
2943    let mut bytes = re.as_bytes();
2944    loop {
2945        match memchr::memchr(b'\\', &bytes[..bytes.len() - 1]) {
2946            Some(i) => {
2947                bytes = &bytes[i + 1..];
2948                let c = bytes[0];
2949                if b'0' <= c && c <= b'9' { return true; }
2950            }
2951            None => return false
2952        }
2953    }
2954}
2955*/
2956
2957/// The internal module only exists so that the toy example can access internals for debugging and
2958/// experimenting.
2959#[doc(hidden)]
2960pub mod internal {
2961    pub use crate::analyze::{analyze, can_compile_as_anchored, AnalyzeContext, Info};
2962    pub use crate::compile::{compile, CompileOptions};
2963    pub use crate::optimize::optimize;
2964    pub use crate::parse_flags::{
2965        FLAG_CASEI, FLAG_CRLF, FLAG_DOTNL, FLAG_IGNORE_NUMBERED_GROUPS_WHEN_NAMED_GROUPS_EXIST,
2966        FLAG_IGNORE_SPACE, FLAG_MULTI, FLAG_ONIGURUMA_MODE, FLAG_UNICODE,
2967    };
2968    pub use crate::vm::{run_default, run_trace, Insn, Prog, Seek};
2969}
2970
2971#[cfg(test)]
2972mod tests {
2973    use alloc::borrow::Cow;
2974    use alloc::boxed::Box;
2975    use alloc::string::{String, ToString};
2976    use alloc::sync::Arc;
2977    use alloc::vec::Vec;
2978    use alloc::{format, vec};
2979
2980    use crate::parse::{make_group, make_literal};
2981    use crate::{Absent, Expr, Regex, RegexBuilder, RegexImpl, RegexInput};
2982
2983    //use detect_possible_backref;
2984
2985    // tests for to_str
2986
2987    fn to_str(e: Expr) -> String {
2988        let mut s = String::new();
2989        e.to_str(&mut s, 0);
2990        s
2991    }
2992
2993    #[test]
2994    fn to_str_concat_alt() {
2995        let e = Expr::Concat(vec![
2996            Expr::Alt(vec![make_literal("a"), make_literal("b")]),
2997            make_literal("c"),
2998        ]);
2999        assert_eq!(to_str(e), "(?:a|b)c");
3000    }
3001
3002    #[test]
3003    fn to_str_rep_concat() {
3004        let e = Expr::Repeat {
3005            child: Box::new(Expr::Concat(vec![make_literal("a"), make_literal("b")])),
3006            lo: 2,
3007            hi: 3,
3008            greedy: true,
3009        };
3010        assert_eq!(to_str(e), "(?:ab){2,3}");
3011    }
3012
3013    #[test]
3014    fn to_str_group_alt() {
3015        let e = Expr::Group(Arc::new(Expr::Alt(vec![
3016            make_literal("a"),
3017            make_literal("b"),
3018        ])));
3019        assert_eq!(to_str(e), "(a|b)");
3020    }
3021
3022    #[test]
3023    fn to_str_literal_special_chars() {
3024        assert_eq!(to_str(make_literal("\n")), "\\n");
3025        assert_eq!(to_str(make_literal("\t")), "\\t");
3026        assert_eq!(to_str(make_literal("\r")), "\\r");
3027        assert_eq!(to_str(make_literal("\\")), "\\\\");
3028        assert_eq!(to_str(make_literal(".")), "\\.");
3029    }
3030
3031    #[test]
3032    fn as_str_debug() {
3033        let s = r"(a+)b\1";
3034        let regex = Regex::new(s).unwrap();
3035        assert_eq!(s, regex.as_str());
3036        assert_eq!(s, format!("{:?}", regex));
3037    }
3038
3039    #[test]
3040    fn display() {
3041        let s = r"(a+)b\1";
3042        let regex = Regex::new(s).unwrap();
3043        assert_eq!(s, format!("{}", regex));
3044    }
3045
3046    #[test]
3047    fn from_str() {
3048        let s = r"(a+)b\1";
3049        let regex = s.parse::<Regex>().unwrap();
3050        assert_eq!(regex.as_str(), s);
3051    }
3052
3053    #[test]
3054    fn to_str_repeat() {
3055        fn repeat(lo: usize, hi: usize, greedy: bool) -> Expr {
3056            Expr::Repeat {
3057                child: Box::new(make_literal("a")),
3058                lo,
3059                hi,
3060                greedy,
3061            }
3062        }
3063
3064        assert_eq!(to_str(repeat(2, 2, true)), "a{2}");
3065        assert_eq!(to_str(repeat(2, 2, false)), "a{2}?");
3066        assert_eq!(to_str(repeat(2, 3, true)), "a{2,3}");
3067        assert_eq!(to_str(repeat(2, 3, false)), "a{2,3}?");
3068        assert_eq!(to_str(repeat(2, usize::MAX, true)), "a{2,}");
3069        assert_eq!(to_str(repeat(2, usize::MAX, false)), "a{2,}?");
3070        assert_eq!(to_str(repeat(0, 1, true)), "a?");
3071        assert_eq!(to_str(repeat(0, 1, false)), "a??");
3072        assert_eq!(to_str(repeat(0, usize::MAX, true)), "a*");
3073        assert_eq!(to_str(repeat(0, usize::MAX, false)), "a*?");
3074        assert_eq!(to_str(repeat(1, usize::MAX, true)), "a+");
3075        assert_eq!(to_str(repeat(1, usize::MAX, false)), "a+?");
3076    }
3077
3078    #[test]
3079    fn escape() {
3080        // Check that strings that need no quoting are borrowed, and that non-special punctuation
3081        // is not quoted.
3082        match crate::escape("@foo") {
3083            Cow::Borrowed(s) => assert_eq!(s, "@foo"),
3084            _ => panic!("Value should be borrowed."),
3085        }
3086
3087        // Check typical usage.
3088        assert_eq!(crate::escape("fo*o").into_owned(), "fo\\*o");
3089
3090        // Check that multibyte characters are handled correctly.
3091        assert_eq!(crate::escape("fø*ø").into_owned(), "fø\\*ø");
3092    }
3093
3094    #[test]
3095    fn push_quoted_special_chars() {
3096        fn pq(s: &str) -> String {
3097            let mut buf = String::new();
3098            crate::push_quoted(&mut buf, s);
3099            buf
3100        }
3101        assert_eq!(pq("\n"), "\\n");
3102        assert_eq!(pq("\t"), "\\t");
3103        assert_eq!(pq("\r"), "\\r");
3104        assert_eq!(pq("\\"), "\\\\");
3105        assert_eq!(pq("."), "\\.");
3106        assert_eq!(pq("hello"), "hello");
3107        assert_eq!(pq("a.b"), "a\\.b");
3108        assert_eq!(pq("fø*ø"), "fø\\*ø");
3109    }
3110
3111    #[test]
3112    fn trailing_positive_lookahead_wrap_capture_group_fixup() {
3113        let s = r"a+(?=c)";
3114        let regex = s.parse::<Regex>().unwrap();
3115        assert!(matches!(regex.inner,
3116            RegexImpl::Wrap { explicit_capture_group_0: true, .. }),
3117            "trailing positive lookahead for an otherwise easy pattern should avoid going through the VM");
3118        assert_eq!(s, regex.as_str());
3119        assert_eq!(s, format!("{:?}", regex));
3120    }
3121
3122    #[test]
3123    fn easy_regex() {
3124        let s = r"(a+)b";
3125        let regex = s.parse::<Regex>().unwrap();
3126        assert!(
3127            matches!(regex.inner, RegexImpl::Wrap { explicit_capture_group_0: false, .. }),
3128            "easy pattern should avoid going through the VM, and capture group 0 should be implicit"
3129        );
3130
3131        assert_eq!(s, regex.as_str());
3132        assert_eq!(s, format!("{:?}", regex));
3133    }
3134
3135    #[test]
3136    fn hard_regex() {
3137        let s = r"(a+)(?>c)";
3138        let regex = s.parse::<Regex>().unwrap();
3139        assert!(
3140            matches!(regex.inner, RegexImpl::Fancy { .. }),
3141            "hard regex should be compiled into a VM"
3142        );
3143        assert_eq!(s, regex.as_str());
3144        assert_eq!(s, format!("{:?}", regex));
3145    }
3146
3147    #[cfg(feature = "leftmost_longest")]
3148    #[test]
3149    fn leftmost_longest_alt_compiles_to_fancy() {
3150        let regex = RegexBuilder::new(r"a|ab")
3151            .leftmost_longest(true)
3152            .build()
3153            .unwrap();
3154        assert!(
3155            matches!(regex.inner, RegexImpl::Fancy { .. }),
3156            "alternation with non-const size should compile to VM when leftmost_longest is enabled"
3157        );
3158    }
3159
3160    #[test]
3161    fn start_end_text_assertions_can_stay_wrap_without_override_opt_in() {
3162        let regex = Regex::new(r"\Afoo\z").unwrap();
3163        assert!(
3164            matches!(regex.inner, RegexImpl::Wrap { .. }),
3165            r"\A...\z should stay on the wrap path unless input assertion overrides are enabled"
3166        );
3167    }
3168
3169    #[test]
3170    fn start_end_text_assertions_become_fancy_with_override_opt_in() {
3171        let regex = RegexBuilder::new(r"\Afoo\z")
3172            .allow_input_assertion_overrides(true)
3173            .build()
3174            .unwrap();
3175        assert!(
3176            matches!(regex.inner, RegexImpl::Fancy { .. }),
3177            r"\A...\z should use the VM when input assertion overrides are enabled"
3178        );
3179    }
3180
3181    /*
3182    #[test]
3183    fn detect_backref() {
3184        assert_eq!(detect_possible_backref("a0a1a2"), false);
3185        assert_eq!(detect_possible_backref("a0a1\\a2"), false);
3186        assert_eq!(detect_possible_backref("a0a\\1a2"), true);
3187        assert_eq!(detect_possible_backref("a0a1a2\\"), false);
3188    }
3189    */
3190
3191    #[test]
3192    fn test_is_leaf_node_leaf_nodes() {
3193        // Test all leaf node variants
3194        assert!(Expr::Empty.is_leaf_node());
3195        assert!(Expr::Any {
3196            newline: false,
3197            crlf: false
3198        }
3199        .is_leaf_node());
3200        assert!(Expr::Any {
3201            newline: true,
3202            crlf: false
3203        }
3204        .is_leaf_node());
3205        assert!(Expr::Assertion(crate::Assertion::StartText).is_leaf_node());
3206        assert!(Expr::Literal {
3207            val: "test".to_string(),
3208            casei: false
3209        }
3210        .is_leaf_node());
3211        assert!(Expr::Delegate {
3212            inner: "[0-9]".to_string(),
3213            casei: false,
3214        }
3215        .is_leaf_node());
3216        assert!(Expr::Backref {
3217            group: 1,
3218            casei: false
3219        }
3220        .is_leaf_node());
3221        assert!(Expr::BackrefWithRelativeRecursionLevel {
3222            group: 1,
3223            relative_level: -1,
3224            casei: false
3225        }
3226        .is_leaf_node());
3227        assert!(Expr::KeepOut.is_leaf_node());
3228        assert!(Expr::ContinueFromPreviousMatchEnd.is_leaf_node());
3229        assert!(Expr::BackrefExistsCondition {
3230            group: 1,
3231            relative_recursion_level: None
3232        }
3233        .is_leaf_node());
3234        assert!(Expr::BacktrackingControlVerb(crate::BacktrackingControlVerb::Fail).is_leaf_node());
3235        assert!(Expr::SubroutineCall(1).is_leaf_node());
3236
3237        assert!(Expr::Absent(Absent::Clear).is_leaf_node());
3238    }
3239
3240    #[test]
3241    fn test_is_leaf_node_non_leaf_nodes() {
3242        // Test all non-leaf node variants
3243        assert!(!Expr::Concat(vec![make_literal("a")]).is_leaf_node());
3244        assert!(!Expr::Alt(vec![make_literal("a"), make_literal("b")]).is_leaf_node());
3245        assert!(!make_group(make_literal("a")).is_leaf_node());
3246        assert!(
3247            !Expr::LookAround(Box::new(make_literal("a")), crate::LookAround::LookAhead)
3248                .is_leaf_node()
3249        );
3250        assert!(!Expr::Repeat {
3251            child: Box::new(make_literal("a")),
3252            lo: 0,
3253            hi: 1,
3254            greedy: true
3255        }
3256        .is_leaf_node());
3257        assert!(!Expr::AtomicGroup(Box::new(make_literal("a"))).is_leaf_node());
3258        assert!(!Expr::Conditional {
3259            condition: Box::new(Expr::BackrefExistsCondition {
3260                group: 1,
3261                relative_recursion_level: None
3262            }),
3263            true_branch: Box::new(make_literal("a")),
3264            false_branch: Box::new(Expr::Empty)
3265        }
3266        .is_leaf_node());
3267
3268        assert!(!Expr::Absent(Absent::Repeater(Box::new(make_literal("a")))).is_leaf_node());
3269        assert!(!Expr::Absent(Absent::Expression {
3270            absent: Box::new(make_literal("/*")),
3271            exp: Box::new(Expr::Repeat {
3272                child: Box::new(Expr::Any {
3273                    newline: true,
3274                    crlf: false
3275                }),
3276                lo: 0,
3277                hi: usize::MAX,
3278                greedy: true
3279            })
3280        })
3281        .is_leaf_node());
3282        assert!(!Expr::Absent(Absent::Stopper(Box::new(make_literal("/*")))).is_leaf_node());
3283    }
3284
3285    #[test]
3286    fn test_children_iter_empty() {
3287        // Leaf nodes should return empty iterator
3288        let expr = Expr::Empty;
3289        let mut iter = expr.children_iter();
3290        assert!(iter.next().is_none());
3291
3292        let expr = make_literal("test");
3293        let mut iter = expr.children_iter();
3294        assert!(iter.next().is_none());
3295    }
3296
3297    #[test]
3298    fn test_children_iter_single() {
3299        // Group, LookAround, AtomicGroup, Repeat should return single child
3300        let child = make_literal("a");
3301        let expr = make_group(child.clone());
3302        let children: Vec<_> = expr.children_iter().collect();
3303        assert_eq!(children.len(), 1);
3304
3305        let expr = Expr::Repeat {
3306            child: Box::new(child.clone()),
3307            lo: 0,
3308            hi: 1,
3309            greedy: true,
3310        };
3311        let children: Vec<_> = expr.children_iter().collect();
3312        assert_eq!(children.len(), 1);
3313    }
3314
3315    #[test]
3316    fn test_children_iter_vec() {
3317        // Concat and Alt should return all children
3318        let children_vec = vec![make_literal("a"), make_literal("b"), make_literal("c")];
3319        let expr = Expr::Concat(children_vec.clone());
3320        let children: Vec<_> = expr.children_iter().collect();
3321        assert_eq!(children.len(), 3);
3322
3323        let expr = Expr::Alt(children_vec);
3324        let children: Vec<_> = expr.children_iter().collect();
3325        assert_eq!(children.len(), 3);
3326    }
3327
3328    #[test]
3329    fn test_children_iter_triple() {
3330        // Conditional should return three children
3331        let expr = Expr::Conditional {
3332            condition: Box::new(Expr::BackrefExistsCondition {
3333                group: 1,
3334                relative_recursion_level: None,
3335            }),
3336            true_branch: Box::new(make_literal("a")),
3337            false_branch: Box::new(make_literal("b")),
3338        };
3339        let children: Vec<_> = expr.children_iter().collect();
3340        assert_eq!(children.len(), 3);
3341
3342        // Absent expression should return two children
3343        let expr = Expr::Absent(Absent::Expression {
3344            absent: Box::new(make_literal("/*")),
3345            exp: Box::new(Expr::Repeat {
3346                child: Box::new(Expr::Any {
3347                    newline: true,
3348                    crlf: false,
3349                }),
3350                lo: 0,
3351                hi: usize::MAX,
3352                greedy: true,
3353            }),
3354        });
3355        let children: Vec<_> = expr.children_iter().collect();
3356        assert_eq!(children.len(), 2);
3357    }
3358
3359    #[test]
3360    fn find_input_raw_honors_anchored_flag_for_wrapped_regex() {
3361        let regex = Regex::new("abc").unwrap();
3362
3363        // Unanchored search finds match at position 1
3364        let input = RegexInput::new("zabc");
3365        assert_eq!(
3366            Some((1, 4)),
3367            regex
3368                .find_input(input)
3369                .unwrap()
3370                .map(|m| (m.start(), m.end()))
3371        );
3372
3373        // Anchored at position 0 returns None (abc doesn't match at pos 0)
3374        let anchored_at_start = RegexInput::new("zabc").anchored(true);
3375        assert!(regex.find_input(anchored_at_start).unwrap().is_none());
3376
3377        // Anchored at position 1 finds match
3378        let anchored_at_match = RegexInput::new("zabc").from_pos(1).anchored(true);
3379        assert_eq!(
3380            regex
3381                .find_input(anchored_at_match)
3382                .unwrap()
3383                .map(|m| (m.start(), m.end())),
3384            Some((1, 4))
3385        );
3386    }
3387}