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