1use std::borrow::Cow;
25
26const MAX_GROUPS: usize = 16;
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31enum Op {
32 Char(u8),
33 Any,
34 Class,
35 Match,
36 Jmp(i32),
37 Split(i32, i32),
38 Save(usize),
39 Bol,
40 Eol,
41 Wb,
42 Nwb,
43}
44
45#[derive(Clone, Copy)]
46struct CharClass {
47 bits: [u8; 32],
48}
49
50impl CharClass {
51 fn zero() -> Self {
52 Self { bits: [0; 32] }
53 }
54 fn set(&mut self, c: u8) {
55 self.bits[(c >> 3) as usize] |= 1u8 << (c & 7);
56 }
57 fn set_range(&mut self, lo: u8, hi: u8) {
58 let (lo, hi) = if lo > hi { (hi, lo) } else { (lo, hi) };
59 for c in lo..=hi {
60 self.set(c);
61 }
62 }
63 fn has(&self, c: u8) -> bool {
64 (self.bits[(c >> 3) as usize] >> (c & 7)) & 1 == 1
65 }
66 fn negate(&mut self) {
67 for b in self.bits.iter_mut() {
68 *b = !*b;
69 }
70 }
71 fn predef(&mut self, c: u8) {
72 match c {
73 b'd' => self.set_range(b'0', b'9'),
74 b'D' => {
75 self.set_range(0, 255);
76 for x in b'0'..=b'9' {
77 self.bits[(x >> 3) as usize] &= !(1u8 << (x & 7));
78 }
79 }
80 b's' => {
81 for c in [b' ', b'\t', b'\n', b'\r', 0x0C, 0x0B].iter() {
82 self.set(*c);
83 }
84 }
85 b'S' => {
86 self.set_range(0, 255);
87 for c in [b' ', b'\t', b'\n', b'\r', 0x0C, 0x0B].iter() {
88 self.bits[(*c >> 3) as usize] &= !(1u8 << (c & 7));
89 }
90 }
91 b'w' => {
92 self.set_range(b'0', b'9');
93 self.set_range(b'A', b'Z');
94 self.set_range(b'a', b'z');
95 self.set(b'_');
96 }
97 b'W' => {
98 self.set_range(0, 255);
99 for x in b'0'..=b'9' {
100 self.bits[(x >> 3) as usize] &= !(1u8 << (x & 7));
101 }
102 for x in b'A'..=b'Z' {
103 self.bits[(x >> 3) as usize] &= !(1u8 << (x & 7));
104 }
105 for x in b'a'..=b'z' {
106 self.bits[(x >> 3) as usize] &= !(1u8 << (x & 7));
107 }
108 self.bits[(b'_' >> 3) as usize] &= !(1u8 << (b'_' & 7));
109 }
110 _ => {}
111 }
112 }
113}
114
115#[derive(Clone, Copy)]
116struct Insn {
117 op: Op,
118 cc: CharClass, }
120
121impl Insn {
122 fn new(op: Op) -> Self {
123 Self {
124 op,
125 cc: CharClass::zero(),
126 }
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct RegexError(pub String);
134
135impl std::fmt::Display for RegexError {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 write!(f, "regex: {}", self.0)
138 }
139}
140
141impl std::error::Error for RegexError {}
142
143pub struct Regex {
144 code: Vec<Insn>,
145 ngroups: usize, anchored_start: bool,
147}
148
149struct Parser<'a> {
152 src: &'a [u8],
153 pos: usize,
154 next_group: usize,
155 err: Option<String>,
156 code: Vec<Insn>,
157}
158
159fn hexval(c: u8) -> i32 {
160 match c {
161 b'0'..=b'9' => (c - b'0') as i32,
162 b'a'..=b'f' => (c - b'a') as i32 + 10,
163 b'A'..=b'F' => (c - b'A') as i32 + 10,
164 _ => -1,
165 }
166}
167
168impl<'a> Parser<'a> {
169 fn perr(&mut self, msg: &str) {
170 if self.err.is_none() {
171 self.err = Some(format!("regex parse error at {}: {}", self.pos, msg));
172 }
173 }
174
175 fn parse_escape(&mut self) -> (u8, u8, u8) {
178 if self.pos >= self.src.len() {
179 self.perr("trailing backslash");
180 return (0, 0, 0);
181 }
182 let c = self.src[self.pos];
183 self.pos += 1;
184 match c {
185 b'n' => (b'\n', 0, 0),
186 b't' => (b'\t', 0, 0),
187 b'r' => (b'\r', 0, 0),
188 b'f' => (0x0C, 0, 0),
189 b'v' => (0x0B, 0, 0),
190 b'0' => (0, 0, 0),
191 b'a' => (0x07, 0, 0),
192 b'e' => (27, 0, 0),
193 b'x' => {
194 if self.pos + 1 >= self.src.len() {
195 self.perr("bad \\xNN");
196 return (0, 0, 0);
197 }
198 let h1 = hexval(self.src[self.pos]);
199 let h2 = hexval(self.src[self.pos + 1]);
200 if h1 < 0 || h2 < 0 {
201 self.perr("bad \\xNN");
202 return (0, 0, 0);
203 }
204 self.pos += 2;
205 (((h1 << 4) | h2) as u8, 0, 0)
206 }
207 b'd' | b'D' | b's' | b'S' | b'w' | b'W' => (0, c, 1),
208 b'b' | b'B' => (0, c, 2),
209 _ => (c, 0, 0),
210 }
211 }
212
213 fn parse_class(&mut self) -> CharClass {
214 let mut out = CharClass::zero();
215 let mut neg = false;
216 if self.pos < self.src.len() && self.src[self.pos] == b'^' {
217 neg = true;
218 self.pos += 1;
219 }
220 let mut first = true;
221 while self.pos < self.src.len() && (first || self.src[self.pos] != b']') {
222 first = false;
223 let c;
224 if self.src[self.pos] == b'\\' {
225 self.pos += 1;
226 let (b, p, k) = self.parse_escape();
227 if k == 1 {
228 let mut sub = CharClass::zero();
229 sub.predef(p);
230 for i in 0..32 {
231 out.bits[i] |= sub.bits[i];
232 }
233 continue;
234 }
235 c = if k == 2 { 8 } else { b };
236 } else {
237 c = self.src[self.pos];
238 self.pos += 1;
239 }
240 if self.pos + 1 < self.src.len()
241 && self.src[self.pos] == b'-'
242 && self.src[self.pos + 1] != b']'
243 {
244 self.pos += 1;
245 let hi;
246 if self.src[self.pos] == b'\\' {
247 self.pos += 1;
248 let (b, _p, k) = self.parse_escape();
249 hi = if k == 0 { b } else { b'-' };
250 } else {
251 hi = self.src[self.pos];
252 self.pos += 1;
253 }
254 out.set_range(c, hi);
255 } else {
256 out.set(c);
257 }
258 }
259 if self.pos >= self.src.len() || self.src[self.pos] != b']' {
260 self.perr("unclosed [");
261 return out;
262 }
263 self.pos += 1;
264 if neg {
265 out.negate();
266 }
267 out
268 }
269
270 fn emit(&mut self, op: Op) -> usize {
271 let ix = self.code.len();
272 self.code.push(Insn::new(op));
273 ix
274 }
275
276 fn parse_atom(&mut self) -> usize {
278 if self.pos >= self.src.len() {
279 return self.code.len();
280 }
281 let start = self.code.len();
282 let c = self.src[self.pos];
283 if c == b'(' {
284 self.pos += 1;
285 let mut capture = true;
286 if self.pos + 1 < self.src.len()
287 && self.src[self.pos] == b'?'
288 && self.src[self.pos + 1] == b':'
289 {
290 capture = false;
291 self.pos += 2;
292 } else if self.pos + 2 < self.src.len()
293 && self.src[self.pos] == b'?'
294 && self.src[self.pos + 1] == b'P'
295 && self.src[self.pos + 2] == b'<'
296 {
297 self.pos += 3;
299 while self.pos < self.src.len() && self.src[self.pos] != b'>' {
300 self.pos += 1;
301 }
302 if self.pos < self.src.len() {
303 self.pos += 1;
304 }
305 }
306 let group = if capture {
307 let g = self.next_group;
308 self.next_group += 1;
309 self.emit(Op::Save(g * 2));
310 g
311 } else {
312 0
313 };
314 self.parse_alt();
315 if self.pos >= self.src.len() || self.src[self.pos] != b')' {
316 self.perr("unclosed (");
317 return start;
318 }
319 self.pos += 1;
320 if capture {
321 self.emit(Op::Save(group * 2 + 1));
322 }
323 } else if c == b'[' {
324 self.pos += 1;
325 let cc = self.parse_class();
326 let ix = self.emit(Op::Class);
327 self.code[ix].cc = cc;
328 } else if c == b'.' {
329 self.pos += 1;
330 self.emit(Op::Any);
331 } else if c == b'^' {
332 self.pos += 1;
333 self.emit(Op::Bol);
334 } else if c == b'$' {
335 self.pos += 1;
336 self.emit(Op::Eol);
337 } else if c == b'\\' {
338 self.pos += 1;
339 let (b, p, k) = self.parse_escape();
340 match k {
341 1 => {
342 let mut cc = CharClass::zero();
343 cc.predef(p);
344 let ix = self.emit(Op::Class);
345 self.code[ix].cc = cc;
346 }
347 2 => {
348 self.emit(if p == b'b' { Op::Wb } else { Op::Nwb });
349 }
350 _ => {
351 self.emit(Op::Char(b));
352 }
353 }
354 } else if c == b')' || c == b'|' {
355 return start;
356 } else {
357 self.pos += 1;
358 self.emit(Op::Char(c));
359 }
360 start
361 }
362
363 fn code_clone(&mut self, from: usize, to: usize) -> usize {
364 let delta = self.code.len() as i32 - from as i32;
365 let start = self.code.len();
366 for i in from..to {
367 let mut insn = self.code[i];
368 match insn.op {
369 Op::Jmp(t) if (t as usize) >= from && (t as usize) < to => {
370 insn.op = Op::Jmp(t + delta);
371 }
372 Op::Split(x, y) => {
373 let nx = if (x as usize) >= from && (x as usize) < to {
374 x + delta
375 } else {
376 x
377 };
378 let ny = if (y as usize) >= from && (y as usize) < to {
379 y + delta
380 } else {
381 y
382 };
383 insn.op = Op::Split(nx, ny);
384 }
385 _ => {}
386 }
387 self.code.push(insn);
388 }
389 start
390 }
391
392 fn shift_targets_after(&mut self, from: usize, by: i32) {
393 for i in (from + 1)..self.code.len() {
394 match &mut self.code[i].op {
395 Op::Jmp(t) if *t as usize >= from => {
396 *t += by;
397 }
398 Op::Split(x, y) => {
399 if *x as usize >= from {
400 *x += by;
401 }
402 if *y as usize >= from {
403 *y += by;
404 }
405 }
406 _ => {}
407 }
408 }
409 }
410
411 fn apply_quant(&mut self, start: usize, q: u8, n_lo: i32, n_hi: i32, lazy: bool) {
412 let end = self.code.len();
413 let alen = end - start;
414 if alen == 0 {
415 return;
416 }
417
418 match q {
419 b'?' => {
420 self.code.insert(start, Insn::new(Op::Split(0, 0)));
422 let after = self.code.len() as i32;
423 let to_atom = (start + 1) as i32;
424 self.code[start].op = Op::Split(
425 if lazy { after } else { to_atom },
426 if lazy { to_atom } else { after },
427 );
428 self.shift_targets_after(start, 1);
429 }
430 b'*' => {
431 self.code.insert(start, Insn::new(Op::Split(0, 0)));
433 self.shift_targets_after(start, 1);
434 let after_atom = self.code.len(); self.emit(Op::Jmp(start as i32));
437 let exit = self.code.len();
438 let to_atom = (start + 1) as i32;
439 self.code[start].op = Op::Split(
440 if lazy { exit as i32 } else { to_atom },
441 if lazy { to_atom } else { exit as i32 },
442 );
443 let _ = after_atom;
444 }
445 b'+' => {
446 let _ix = self.emit(Op::Split(0, 0));
447 let after = self.code.len() as i32;
448 let s_ix = self.code.len() - 1;
449 self.code[s_ix].op = Op::Split(
450 if lazy { after } else { start as i32 },
451 if lazy { start as i32 } else { after },
452 );
453 }
454 b'{' => {
455 for _ in 1..n_lo {
457 self.code_clone(start, end);
458 }
459 if n_hi == -1 {
460 let split_ix = self.emit(Op::Split(0, 0));
462 let atom_start = self.code.len();
463 self.code_clone(start, end);
464 let jmp_ix = self.emit(Op::Jmp(split_ix as i32));
465 let exit = self.code.len() as i32;
466 self.code[split_ix].op = Op::Split(
467 if lazy { exit } else { atom_start as i32 },
468 if lazy { atom_start as i32 } else { exit },
469 );
470 let _ = jmp_ix;
471 } else if n_hi > n_lo {
472 for _ in 0..(n_hi - n_lo) {
473 let sp = self.emit(Op::Split(0, 0));
474 let clone_start = self.code.len();
475 self.code_clone(start, end);
476 let after = self.code.len() as i32;
477 self.code[sp].op = Op::Split(
478 if lazy { after } else { clone_start as i32 },
479 if lazy { clone_start as i32 } else { after },
480 );
481 }
482 }
483 }
484 _ => {}
485 }
486 }
487
488 fn parse_concat(&mut self) -> usize {
489 let start = self.code.len();
490 while self.pos < self.src.len() && self.src[self.pos] != b')' && self.src[self.pos] != b'|'
491 {
492 let atom_start = self.parse_atom();
493 if self.err.is_some() {
494 return start;
495 }
496 if self.pos < self.src.len() {
497 let q = self.src[self.pos];
498 if q == b'*' || q == b'+' || q == b'?' {
499 self.pos += 1;
500 let mut lazy = false;
501 if self.pos < self.src.len() && self.src[self.pos] == b'?' {
502 lazy = true;
503 self.pos += 1;
504 }
505 self.apply_quant(atom_start, q, 0, 0, lazy);
506 } else if q == b'{' {
507 let save = self.pos;
508 self.pos += 1;
509 let mut n_lo: i32 = 0;
510 let mut got_lo = false;
511 while self.pos < self.src.len() && self.src[self.pos].is_ascii_digit() {
512 n_lo = n_lo * 10 + (self.src[self.pos] - b'0') as i32;
513 got_lo = true;
514 self.pos += 1;
515 }
516 let mut n_hi: i32 = n_lo;
517 let mut open = false;
518 if !got_lo {
519 self.pos = save;
520 } else {
521 if self.pos < self.src.len() && self.src[self.pos] == b',' {
522 self.pos += 1;
523 n_hi = -1;
524 let mut hi: i32 = 0;
525 let mut got_hi = false;
526 while self.pos < self.src.len() && self.src[self.pos].is_ascii_digit() {
527 hi = hi * 10 + (self.src[self.pos] - b'0') as i32;
528 got_hi = true;
529 self.pos += 1;
530 }
531 if got_hi {
532 n_hi = hi;
533 } else {
534 open = true;
535 }
536 }
537 if self.pos < self.src.len() && self.src[self.pos] == b'}' {
538 self.pos += 1;
539 let mut lazy = false;
540 if self.pos < self.src.len() && self.src[self.pos] == b'?' {
541 lazy = true;
542 self.pos += 1;
543 }
544 self.apply_quant(
545 atom_start,
546 b'{',
547 n_lo,
548 if open { -1 } else { n_hi },
549 lazy,
550 );
551 } else {
552 self.perr("bad {n,m}");
553 }
554 }
555 }
556 }
557 }
558 start
559 }
560
561 fn parse_alt(&mut self) -> usize {
562 let start = self.parse_concat();
563 if self.err.is_some() {
564 return start;
565 }
566 while self.pos < self.src.len() && self.src[self.pos] == b'|' {
567 let branch1_end = self.code.len();
568 let jmp_ix = self.emit(Op::Jmp(0)); let branch2_start = self.code.len();
570 self.code.insert(start, Insn::new(Op::Split(0, 0)));
572 for i in (start + 1)..self.code.len() {
574 match &mut self.code[i].op {
575 Op::Jmp(t) if *t as usize >= start => {
576 *t += 1;
577 }
578 Op::Split(x, y) => {
579 if *x as usize >= start {
580 *x += 1;
581 }
582 if *y as usize >= start {
583 *y += 1;
584 }
585 }
586 _ => {}
587 }
588 }
589 self.code[start].op = Op::Split((start + 1) as i32, (branch2_start + 1) as i32);
590 let _ = branch1_end;
591 let jmp_ix = jmp_ix + 1;
592 self.pos += 1;
593 self.parse_concat();
594 self.code[jmp_ix].op = Op::Jmp(self.code.len() as i32);
595 }
596 start
597 }
598}
599
600impl Regex {
603 pub fn new(pattern: &str) -> Result<Self, RegexError> {
604 let bytes = pattern.as_bytes();
605 let mut p = Parser {
606 src: bytes,
607 pos: 0,
608 next_group: 1, err: None,
610 code: Vec::new(),
611 };
612 p.code.push(Insn::new(Op::Save(0)));
614 let anchored_start = !bytes.is_empty() && bytes[0] == b'^';
615 p.parse_alt();
616 if let Some(e) = p.err {
617 return Err(RegexError(e));
618 }
619 if p.pos < bytes.len() {
620 return Err(RegexError(format!(
621 "regex parse error at {}: unexpected )",
622 p.pos
623 )));
624 }
625 p.code.push(Insn::new(Op::Save(1)));
626 p.code.push(Insn::new(Op::Match));
627 Ok(Self {
628 code: p.code,
629 ngroups: p.next_group,
630 anchored_start,
631 })
632 }
633
634 pub fn is_match(&self, input: &str) -> bool {
635 self.find_first(input.as_bytes()).is_some()
636 }
637
638 pub fn captures<'h>(&self, input: &'h str) -> Option<Captures<'h>> {
639 let slots = self.find_first(input.as_bytes())?;
640 Some(Captures {
641 input,
642 slots,
643 ngroups: self.ngroups,
644 })
645 }
646
647 pub fn captures_iter<'r, 'h>(&'r self, input: &'h str) -> CapturesIter<'r, 'h> {
648 CapturesIter {
649 re: self,
650 input,
651 pos: 0,
652 done: false,
653 }
654 }
655
656 pub fn replace<'h, R: Replacer>(&self, input: &'h str, mut rep: R) -> Cow<'h, str> {
658 let bytes = input.as_bytes();
659 let mut start = 0usize;
660 let slots = loop {
661 if start > bytes.len() {
662 return Cow::Borrowed(input);
663 }
664 if let Some(s) = self.match_at(bytes, start) {
665 break s;
666 }
667 if self.anchored_start {
668 return Cow::Borrowed(input);
669 }
670 start += 1;
671 };
672 let mut out = String::with_capacity(input.len());
673 out.push_str(&input[..start]);
674 let caps = Captures {
675 input,
676 slots: slots.clone(),
677 ngroups: self.ngroups,
678 };
679 rep.replace_into(&caps, &mut out);
680 let mend = slots[1] as usize;
681 out.push_str(&input[mend..]);
682 Cow::Owned(out)
683 }
684
685 pub fn replace_all<'h, R: Replacer>(&self, input: &'h str, mut rep: R) -> Cow<'h, str> {
686 let bytes = input.as_bytes();
687 let mut out = String::new();
688 let mut pos: usize = 0;
689 let mut any = false;
690 while pos <= bytes.len() {
691 let mut start = pos;
692 let mut found_slots: Option<Vec<i32>> = None;
693 while start <= bytes.len() {
694 if let Some(s) = self.match_at(bytes, start) {
695 found_slots = Some(s);
696 break;
697 }
698 if self.anchored_start && start > pos {
699 break;
700 }
701 start += 1;
702 if start > bytes.len() {
703 break;
704 }
705 }
706 let slots = match found_slots {
707 Some(s) => s,
708 None => {
709 out.push_str(&input[pos..]);
710 break;
711 }
712 };
713 any = true;
714 out.push_str(&input[pos..start]);
716 let caps = Captures {
717 input,
718 slots: slots.clone(),
719 ngroups: self.ngroups,
720 };
721 rep.replace_into(&caps, &mut out);
722 let mend = slots[1] as usize;
723 if slots[1] == slots[0] {
724 if mend < bytes.len() {
726 let ch = input[mend..].chars().next().unwrap();
727 out.push(ch);
728 pos = mend + ch.len_utf8();
729 } else {
730 pos = mend + 1;
731 }
732 } else {
733 pos = mend;
734 }
735 }
736 if any {
737 Cow::Owned(out)
738 } else {
739 Cow::Borrowed(input)
740 }
741 }
742
743 fn find_first(&self, bytes: &[u8]) -> Option<Vec<i32>> {
746 let mut start: usize = 0;
747 loop {
748 if let Some(s) = self.match_at(bytes, start) {
749 return Some(s);
750 }
751 if self.anchored_start {
752 return None;
753 }
754 if start > bytes.len() {
755 return None;
756 }
757 start += 1;
758 }
759 }
760
761 fn match_at(&self, input: &[u8], start: usize) -> Option<Vec<i32>> {
762 let nslots = self.ngroups * 2;
763 let mut cur = ThreadList::new(self.code.len());
764 let mut nxt = ThreadList::new(self.code.len());
765 let init: Vec<i32> = vec![-1; nslots];
766 cur.gen = 1; cur.add(self, input, 0, &init, start);
768 let mut best: Option<Vec<i32>> = None;
769 let mut sp = start;
770 loop {
771 if cur.threads.is_empty() {
772 break;
773 }
774 nxt.reset();
775 let c: i32 = if sp < input.len() {
776 input[sp] as i32
777 } else {
778 -1
779 };
780 for th in &cur.threads {
781 let insn = &self.code[th.pc];
782 match insn.op {
783 Op::Char(b) if c == b as i32 => {
784 nxt.add(self, input, th.pc + 1, &th.slots, sp + 1);
785 }
786 Op::Any if c >= 0 && c != b'\n' as i32 => {
787 nxt.add(self, input, th.pc + 1, &th.slots, sp + 1);
788 }
789 Op::Class if c >= 0 && insn.cc.has(c as u8) => {
790 nxt.add(self, input, th.pc + 1, &th.slots, sp + 1);
791 }
792 Op::Match => {
793 best = Some(th.slots.clone());
798 break;
799 }
800 _ => {}
801 }
802 }
803 std::mem::swap(&mut cur, &mut nxt);
804 sp += 1;
805 if cur.threads.is_empty() {
806 break;
807 }
808 }
809 for th in &cur.threads {
813 if matches!(self.code[th.pc].op, Op::Match) {
814 best = Some(th.slots.clone());
815 break;
816 }
817 }
818 best
819 }
820}
821
822#[derive(Clone)]
825struct Thread {
826 pc: usize,
827 slots: Vec<i32>,
828}
829
830struct ThreadList {
831 threads: Vec<Thread>,
832 visited: Vec<u32>,
833 gen: u32,
834}
835
836impl ThreadList {
837 fn new(code_len: usize) -> Self {
838 Self {
839 threads: Vec::new(),
840 visited: vec![0; code_len],
841 gen: 0,
842 }
843 }
844
845 fn reset(&mut self) {
846 self.threads.clear();
847 self.gen = self.gen.wrapping_add(1);
848 if self.gen == 0 {
849 self.visited.iter_mut().for_each(|v| *v = 0);
850 self.gen = 1;
851 }
852 }
853
854 fn add(&mut self, re: &Regex, input: &[u8], pc: usize, slots: &[i32], sp: usize) {
855 let mut stack: Vec<(usize, Vec<i32>)> = vec![(pc, slots.to_vec())];
864 while let Some((cur_pc, cur_slots)) = stack.pop() {
865 if cur_pc >= re.code.len() {
866 continue;
867 }
868 if self.visited[cur_pc] == self.gen {
869 continue;
870 }
871 self.visited[cur_pc] = self.gen;
872 match re.code[cur_pc].op {
873 Op::Jmp(t) => {
874 stack.push((t as usize, cur_slots));
875 }
876 Op::Split(x, y) => {
877 stack.push((y as usize, cur_slots.clone()));
879 stack.push((x as usize, cur_slots));
880 }
881 Op::Save(slot) => {
882 let mut ns = cur_slots;
883 ns[slot] = sp as i32;
884 stack.push((cur_pc + 1, ns));
885 }
886 Op::Bol => {
887 if sp == 0 || (sp - 1 < input.len() && input[sp - 1] == b'\n') {
888 stack.push((cur_pc + 1, cur_slots));
889 }
890 }
891 Op::Eol => {
892 if sp >= input.len() || input[sp] == b'\n' {
893 stack.push((cur_pc + 1, cur_slots));
894 }
895 }
896 Op::Wb | Op::Nwb => {
897 let left = sp > 0
898 && sp - 1 < input.len()
899 && (input[sp - 1].is_ascii_alphanumeric() || input[sp - 1] == b'_');
900 let right = sp < input.len()
901 && (input[sp].is_ascii_alphanumeric() || input[sp] == b'_');
902 let at_boundary = left != right;
903 let want = matches!(re.code[cur_pc].op, Op::Wb);
904 if at_boundary == want {
905 stack.push((cur_pc + 1, cur_slots));
906 }
907 }
908 _ => {
909 self.threads.push(Thread {
911 pc: cur_pc,
912 slots: cur_slots,
913 });
914 }
915 }
916 }
917 }
918}
919
920impl ThreadList {
921 }
923
924impl ThreadList {
926 #[allow(dead_code)] #[inline]
928 fn ensure_first_gen(&mut self) {
929 if self.gen == 0 {
930 self.gen = 1;
931 }
932 }
933}
934
935#[derive(Debug, Clone, Copy)]
938pub struct Match<'h> {
939 text: &'h str,
940 start: usize,
941 end: usize,
942}
943
944impl<'h> Match<'h> {
945 pub fn as_str(&self) -> &'h str {
946 &self.text[self.start..self.end]
947 }
948 pub fn start(&self) -> usize {
949 self.start
950 }
951 pub fn end(&self) -> usize {
952 self.end
953 }
954}
955
956pub struct Captures<'h> {
957 input: &'h str,
958 slots: Vec<i32>,
959 ngroups: usize,
960}
961
962impl<'h> Captures<'h> {
963 pub fn get(&self, i: usize) -> Option<Match<'h>> {
964 if i >= self.ngroups {
965 return None;
966 }
967 let s = self.slots[2 * i];
968 let e = self.slots[2 * i + 1];
969 if s < 0 || e < 0 || e < s {
970 return None;
971 }
972 Some(Match {
973 text: self.input,
974 start: s as usize,
975 end: e as usize,
976 })
977 }
978 pub fn len(&self) -> usize {
979 self.ngroups
980 }
981 pub fn is_empty(&self) -> bool {
982 self.ngroups == 0
983 }
984 pub fn iter(&self) -> impl Iterator<Item = Option<Match<'h>>> + '_ {
985 (0..self.ngroups).map(move |i| self.get(i))
986 }
987}
988
989impl<'h> std::ops::Index<usize> for Captures<'h> {
990 type Output = str;
991 fn index(&self, i: usize) -> &str {
992 self.get(i).map(|m| m.as_str()).unwrap_or("")
993 }
994}
995
996pub struct CapturesIter<'r, 'h> {
999 re: &'r Regex,
1000 input: &'h str,
1001 pos: usize,
1002 done: bool,
1003}
1004
1005impl<'r, 'h> Iterator for CapturesIter<'r, 'h> {
1006 type Item = Captures<'h>;
1007 fn next(&mut self) -> Option<Self::Item> {
1008 if self.done {
1009 return None;
1010 }
1011 let bytes = self.input.as_bytes();
1012 let mut start = self.pos;
1013 let mut found: Option<Vec<i32>> = None;
1014 loop {
1015 if start > bytes.len() {
1016 break;
1017 }
1018 if let Some(s) = self.re.match_at(bytes, start) {
1019 found = Some(s);
1020 break;
1021 }
1022 if self.re.anchored_start {
1023 break;
1024 }
1025 start += 1;
1026 }
1027 let slots = found?;
1028 let mend = slots[1] as usize;
1030 self.pos = if slots[1] == slots[0] { mend + 1 } else { mend };
1031 if mend > bytes.len() {
1032 self.done = true;
1033 }
1034 Some(Captures {
1035 input: self.input,
1036 slots,
1037 ngroups: self.re.ngroups,
1038 })
1039 }
1040}
1041
1042pub trait Replacer {
1045 fn replace_into(&mut self, caps: &Captures<'_>, dst: &mut String);
1046}
1047
1048impl Replacer for &str {
1049 fn replace_into(&mut self, caps: &Captures<'_>, dst: &mut String) {
1050 let bytes = self.as_bytes();
1051 let mut i = 0;
1052 while i < bytes.len() {
1053 if bytes[i] == b'$' && i + 1 < bytes.len() {
1054 let nc = bytes[i + 1];
1055 if nc == b'&' {
1056 if let Some(m) = caps.get(0) {
1057 dst.push_str(m.as_str());
1058 }
1059 i += 2;
1060 continue;
1061 }
1062 if nc.is_ascii_digit() {
1063 let g = (nc - b'0') as usize;
1064 if let Some(m) = caps.get(g) {
1065 dst.push_str(m.as_str());
1066 }
1067 i += 2;
1068 continue;
1069 }
1070 if nc == b'{' {
1071 let mut j = i + 2;
1073 let mut g: usize = 0;
1074 let mut any = false;
1075 while j < bytes.len() && bytes[j].is_ascii_digit() {
1076 g = g * 10 + (bytes[j] - b'0') as usize;
1077 any = true;
1078 j += 1;
1079 }
1080 if any && j < bytes.len() && bytes[j] == b'}' {
1081 if let Some(m) = caps.get(g) {
1082 dst.push_str(m.as_str());
1083 }
1084 i = j + 1;
1085 continue;
1086 }
1087 }
1088 if nc == b'$' {
1089 dst.push('$');
1090 i += 2;
1091 continue;
1092 }
1093 }
1094 dst.push(bytes[i] as char);
1095 i += 1;
1096 }
1097 }
1098}
1099
1100impl Replacer for String {
1101 fn replace_into(&mut self, caps: &Captures<'_>, dst: &mut String) {
1102 let mut s = self.as_str();
1103 s.replace_into(caps, dst);
1104 }
1105}
1106
1107impl<F: FnMut(&Captures<'_>) -> String> Replacer for F {
1108 fn replace_into(&mut self, caps: &Captures<'_>, dst: &mut String) {
1109 dst.push_str(&self(caps));
1110 }
1111}
1112
1113#[allow(dead_code)]
1115const _: () = {
1116 let _ = MAX_GROUPS;
1117};
1118
1119#[cfg(test)]
1124mod tests {
1125 use super::*;
1126
1127 #[test]
1128 fn test_simple_match() {
1129 let r = Regex::new(r"^hello$").unwrap();
1130 assert!(r.is_match("hello"));
1131 assert!(!r.is_match("hello world"));
1132 }
1133
1134 #[test]
1135 fn test_captures() {
1136 let r = Regex::new(r"(\w+)\s(\w+)").unwrap();
1137 let c = r.captures("foo bar").unwrap();
1138 assert_eq!(&c[0], "foo bar");
1139 assert_eq!(&c[1], "foo");
1140 assert_eq!(&c[2], "bar");
1141 }
1142
1143 #[test]
1144 fn test_alternation() {
1145 let r = Regex::new(r"cat|dog").unwrap();
1146 assert!(r.is_match("cat"));
1147 assert!(r.is_match("dog"));
1148 assert!(!r.is_match("fish"));
1149 }
1150
1151 #[test]
1152 fn test_quant() {
1153 let r = Regex::new(r"a{2,4}").unwrap();
1154 assert!(r.is_match("aa"));
1155 assert!(r.is_match("aaaa"));
1156 assert!(!r.is_match("a"));
1157 }
1158
1159 #[test]
1160 fn test_replace_simple() {
1161 let r = Regex::new(r"\d+").unwrap();
1162 let out = r.replace_all("a1b22c333", "X");
1163 assert_eq!(out, "aXbXcX");
1164 }
1165
1166 #[test]
1167 fn test_replace_backref() {
1168 let r = Regex::new(r"(\w+)").unwrap();
1169 let out = r.replace_all("hi", "<$1>");
1170 assert_eq!(out, "<hi>");
1171 }
1172
1173 #[test]
1174 fn test_classes() {
1175 let r = Regex::new(r"^[a-z]+$").unwrap();
1176 assert!(r.is_match("hello"));
1177 assert!(!r.is_match("Hello"));
1178 }
1179
1180 #[test]
1181 fn test_predef_class() {
1182 let r = Regex::new(r"^\d+$").unwrap();
1183 assert!(r.is_match("123"));
1184 assert!(!r.is_match("12a"));
1185 }
1186
1187 #[test]
1188 fn test_word_boundary() {
1189 let r = Regex::new(r"\bcat\b").unwrap();
1190 assert!(r.is_match("the cat sat"));
1191 assert!(!r.is_match("category"));
1192 }
1193
1194 #[test]
1195 fn test_meta_path_pattern() {
1196 let r = Regex::new(r"^([^$]+)\$([=~])(.+)$").unwrap();
1198 let c = r.captures("name$=value").unwrap();
1199 assert_eq!(&c[1], "name");
1200 assert_eq!(&c[2], "=");
1201 assert_eq!(&c[3], "value");
1202 }
1203
1204 #[test]
1205 fn test_injection_full_pattern() {
1206 let r = Regex::new(r"^`(\$[A-Z]+|[^`]*)[0-9]*`$").unwrap();
1207 let c = r.captures("`$NAME`").unwrap();
1208 assert_eq!(&c[1], "$NAME");
1209 let c = r.captures("`foo.bar`").unwrap();
1210 assert_eq!(&c[1], "foo.bar");
1211 }
1212
1213 #[test]
1214 fn test_captures_iter() {
1215 let r = Regex::new(r"\d+").unwrap();
1216 let nums: Vec<String> = r
1217 .captures_iter("a1 b22 c333")
1218 .map(|c| c[0].to_string())
1219 .collect();
1220 assert_eq!(nums, vec!["1", "22", "333"]);
1221 }
1222
1223 #[test]
1224 fn test_replace_callback() {
1225 let r = Regex::new(r"(\w+)").unwrap();
1226 let out = r.replace_all("foo bar", |c: &Captures<'_>| c[1].to_uppercase());
1227 assert_eq!(out, "FOO BAR");
1228 }
1229}