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