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}