1use alloc::boxed::Box;
11use alloc::string::String;
12use alloc::vec;
13use alloc::vec::Vec;
14
15#[derive(Clone)]
17enum ClassItem {
18 Ch(char),
19 Range(char, char),
20 Digit,
21 NotDigit,
22 Word,
23 NotWord,
24 Space,
25 NotSpace,
26 UnicodeProp(String, bool),
27}
28
29#[derive(Clone)]
30struct ClassData {
31 negate: bool,
32 items: Vec<ClassItem>,
33}
34
35impl ClassData {
36 fn matches(&self, c: char, ignorecase: bool) -> bool {
37 let hit = self.items.iter().any(|it| item_match(it, c, ignorecase));
38 hit ^ self.negate
39 }
40}
41
42fn item_match(it: &ClassItem, c: char, ignorecase: bool) -> bool {
43 match it {
44 ClassItem::Ch(x) => eqc(*x, c, ignorecase),
45 ClassItem::Range(a, b) => {
46 if *a <= c && c <= *b {
47 return true;
48 }
49 if ignorecase {
50 let lc = c.to_ascii_lowercase();
51 let uc = c.to_ascii_uppercase();
52 (*a <= lc && lc <= *b) || (*a <= uc && uc <= *b)
53 } else {
54 false
55 }
56 }
57 ClassItem::Digit => c.is_ascii_digit(),
58 ClassItem::NotDigit => !c.is_ascii_digit(),
59 ClassItem::Word => c == '_' || c.is_ascii_alphanumeric(),
60 ClassItem::NotWord => !(c == '_' || c.is_ascii_alphanumeric()),
61 ClassItem::Space => c.is_whitespace(),
62 ClassItem::NotSpace => !c.is_whitespace(),
63 ClassItem::UnicodeProp(prop, negate) => {
64 let hit = match prop.as_str() {
65 "ASCII" => c.is_ascii(),
66 "Letter" | "L" => c.is_alphabetic(),
67 "Number" | "N" => c.is_numeric(),
68 "Punctuation" | "P" => c.is_ascii_punctuation(),
69 "Symbol" | "S" => c.is_ascii_punctuation() || (!c.is_alphanumeric() && !c.is_whitespace()),
70 "Separator" | "Z" | "Space" => c.is_whitespace(),
71 _ => c.is_alphanumeric(),
72 };
73 hit ^ negate
74 }
75 }
76}
77
78fn eqc(a: char, b: char, ignorecase: bool) -> bool {
79 if a == b {
80 return true;
81 }
82 ignorecase && a.eq_ignore_ascii_case(&b)
83}
84
85fn is_word(c: char) -> bool {
86 c == '_' || c.is_ascii_alphanumeric()
87}
88
89enum Node {
92 Empty,
93 Char(char),
94 Any,
95 Class(ClassData),
96 Start,
97 End,
98 WordBoundary(bool), Group {
100 idx: Option<usize>,
101 node: Box<Node>,
102 },
103 Concat(Vec<Node>),
104 Alt(Vec<Node>),
105 Repeat {
106 node: Box<Node>,
107 min: usize,
108 max: Option<usize>,
109 greedy: bool,
110 },
111 Backref(usize),
112}
113
114enum Inst {
117 Char(char),
118 Any,
119 Class(ClassData),
120 Match,
121 Jmp(usize),
122 Split(usize, usize),
123 Save(usize),
124 AssertStart,
125 AssertEnd,
126 WordBoundary(bool),
127 Backref(usize),
128}
129
130pub struct Regex {
131 prog: Vec<Inst>,
132 pub ngroups: usize,
133 pub ignorecase: bool,
134 pub multiline: bool,
135 pub dotall: bool,
136 pub global: bool,
137 pub sticky: bool,
138 pub has_indices: bool,
141 pub unicode: bool,
142 pub unicode_sets: bool,
143 pub source: String,
144 pub flags: String,
145 pub group_names: Vec<(String, usize)>,
148}
149
150pub struct Match {
152 pub start: usize,
153 pub end: usize,
154 pub captures: Vec<Option<(usize, usize)>>,
155}
156
157pub struct RegExpData {
159 pub re: Regex,
160 pub last_index: usize,
161}
162
163struct Parser {
164 chars: Vec<char>,
165 pos: usize,
166 ngroups: usize,
167 group_names: Vec<(String, usize)>,
169}
170
171impl Regex {
172 pub fn new(pattern: &str, flags: &str) -> Regex {
174 let mut p = Parser {
175 chars: pattern.chars().collect(),
176 pos: 0,
177 ngroups: 0,
178 group_names: Vec::new(),
179 };
180 let node = p.parse_alt();
181 let ngroups = p.ngroups;
182 let group_names = p.group_names;
183 let mut prog = Vec::new();
185 prog.push(Inst::Save(0));
186 compile(&node, &mut prog);
187 prog.push(Inst::Save(1));
188 prog.push(Inst::Match);
189 Regex {
190 prog,
191 ngroups,
192 ignorecase: flags.contains('i'),
193 multiline: flags.contains('m'),
194 dotall: flags.contains('s'),
195 global: flags.contains('g'),
196 sticky: flags.contains('y'),
197 has_indices: flags.contains('d'),
198 unicode: flags.contains('u'),
199 unicode_sets: flags.contains('v'),
200 source: String::from(pattern),
201 flags: String::from(flags),
202 group_names,
203 }
204 }
205
206 pub fn find_at(&self, subject: &[char], start: usize) -> Option<Match> {
208 for sp in start..=subject.len() {
209 if let Some(m) = self.try_match_at(subject, sp) {
210 return Some(m);
211 }
212 }
213 None
214 }
215
216 pub fn find_exact_at(&self, subject: &[char], start: usize) -> Option<Match> {
219 if start > subject.len() {
220 return None;
221 }
222 self.try_match_at(subject, start)
223 }
224
225 fn try_match_at(&self, subject: &[char], sp: usize) -> Option<Match> {
226 let nslots = 2 * (self.ngroups + 1);
227 let mut caps: Vec<isize> = vec![-1; nslots];
228 let mut budget: u32 = 600_000;
229 if self.run(0, subject, sp, &mut caps, &mut budget) {
230 let s = caps[0].max(0) as usize;
231 let e = caps[1].max(0) as usize;
232 let mut captures = Vec::with_capacity(self.ngroups + 1);
233 for g in 0..=self.ngroups {
234 let a = caps[2 * g];
235 let b = caps[2 * g + 1];
236 if a >= 0 && b >= 0 {
237 captures.push(Some((a as usize, b as usize)));
238 } else {
239 captures.push(None);
240 }
241 }
242 Some(Match {
243 start: s,
244 end: e,
245 captures,
246 })
247 } else {
248 None
249 }
250 }
251
252 fn run(
253 &self,
254 mut pc: usize,
255 s: &[char],
256 mut sp: usize,
257 caps: &mut Vec<isize>,
258 budget: &mut u32,
259 ) -> bool {
260 loop {
261 if *budget == 0 {
262 return false;
263 }
264 *budget -= 1;
265 match &self.prog[pc] {
266 Inst::Match => return true,
267 Inst::Char(c) => {
268 if sp < s.len() && eqc(*c, s[sp], self.ignorecase) {
269 pc += 1;
270 sp += 1;
271 } else {
272 return false;
273 }
274 }
275 Inst::Any => {
276 if sp < s.len() && (self.dotall || s[sp] != '\n') {
277 pc += 1;
278 sp += 1;
279 } else {
280 return false;
281 }
282 }
283 Inst::Class(cd) => {
284 if sp < s.len() && cd.matches(s[sp], self.ignorecase) {
285 pc += 1;
286 sp += 1;
287 } else {
288 return false;
289 }
290 }
291 Inst::Jmp(x) => pc = *x,
292 Inst::Split(a, b) => {
293 let (a, b) = (*a, *b);
294 let saved = caps.clone();
295 if self.run(a, s, sp, caps, budget) {
296 return true;
297 }
298 *caps = saved;
299 pc = b;
300 }
301 Inst::Save(n) => {
302 let n = *n;
303 let old = caps[n];
304 caps[n] = sp as isize;
305 if self.run(pc + 1, s, sp, caps, budget) {
306 return true;
307 }
308 caps[n] = old;
309 return false;
310 }
311 Inst::AssertStart => {
312 let ok = sp == 0 || (self.multiline && sp > 0 && s[sp - 1] == '\n');
313 if ok {
314 pc += 1;
315 } else {
316 return false;
317 }
318 }
319 Inst::AssertEnd => {
320 let ok = sp == s.len() || (self.multiline && s[sp] == '\n');
321 if ok {
322 pc += 1;
323 } else {
324 return false;
325 }
326 }
327 Inst::WordBoundary(want) => {
328 let before = sp > 0 && is_word(s[sp - 1]);
329 let after = sp < s.len() && is_word(s[sp]);
330 let boundary = before != after;
331 if boundary == *want {
332 pc += 1;
333 } else {
334 return false;
335 }
336 }
337 Inst::Backref(idx) => {
338 let (a, b) = (caps[2 * idx], caps[2 * idx + 1]);
339 if a < 0 || b < 0 {
340 pc += 1;
341 continue;
342 } let (a, b) = (a as usize, b as usize);
344 let len = b - a;
345 if sp + len <= s.len()
346 && (0..len).all(|k| eqc(s[a + k], s[sp + k], self.ignorecase))
347 {
348 sp += len;
349 pc += 1;
350 } else {
351 return false;
352 }
353 }
354 }
355 }
356 }
357}
358
359fn compile(node: &Node, prog: &mut Vec<Inst>) {
360 match node {
361 Node::Empty => {}
362 Node::Char(c) => prog.push(Inst::Char(*c)),
363 Node::Any => prog.push(Inst::Any),
364 Node::Class(cd) => prog.push(Inst::Class(cd.clone())),
365 Node::Start => prog.push(Inst::AssertStart),
366 Node::End => prog.push(Inst::AssertEnd),
367 Node::WordBoundary(b) => prog.push(Inst::WordBoundary(*b)),
368 Node::Backref(i) => prog.push(Inst::Backref(*i)),
369 Node::Concat(items) => {
370 for it in items {
371 compile(it, prog);
372 }
373 }
374 Node::Group { idx, node } => {
375 if let Some(i) = idx {
376 prog.push(Inst::Save(2 * i));
377 }
378 compile(node, prog);
379 if let Some(i) = idx {
380 prog.push(Inst::Save(2 * i + 1));
381 }
382 }
383 Node::Alt(branches) => {
384 let mut jmp_fixups = Vec::new();
386 for (k, br) in branches.iter().enumerate() {
387 if k + 1 < branches.len() {
388 let split_pc = prog.len();
389 prog.push(Inst::Split(0, 0)); let l1 = prog.len();
391 compile(br, prog);
392 let jmp_pc = prog.len();
393 prog.push(Inst::Jmp(0));
394 jmp_fixups.push(jmp_pc);
395 let l2 = prog.len();
396 if let Inst::Split(a, b) = &mut prog[split_pc] {
397 *a = l1;
398 *b = l2;
399 }
400 } else {
401 compile(br, prog);
402 }
403 }
404 let end = prog.len();
405 for j in jmp_fixups {
406 if let Inst::Jmp(x) = &mut prog[j] {
407 *x = end;
408 }
409 }
410 }
411 Node::Repeat {
412 node,
413 min,
414 max,
415 greedy,
416 } => {
417 for _ in 0..*min {
419 compile(node, prog);
420 }
421 match max {
422 None => {
423 let l1 = prog.len();
425 let split_pc = prog.len();
426 prog.push(Inst::Split(0, 0));
427 let body = prog.len();
428 compile(node, prog);
429 prog.push(Inst::Jmp(l1));
430 let after = prog.len();
431 if let Inst::Split(a, b) = &mut prog[split_pc] {
432 if *greedy {
433 *a = body;
434 *b = after;
435 } else {
436 *a = after;
437 *b = body;
438 }
439 }
440 }
441 Some(mx) => {
442 let opt = mx.saturating_sub(*min);
444 let mut split_fixups = Vec::new();
445 for _ in 0..opt {
446 let split_pc = prog.len();
447 prog.push(Inst::Split(0, 0));
448 let body = prog.len();
449 compile(node, prog);
450 split_fixups.push((split_pc, body));
451 }
452 let after = prog.len();
453 for (sp_pc, body) in split_fixups {
454 if let Inst::Split(a, b) = &mut prog[sp_pc] {
455 if *greedy {
456 *a = body;
457 *b = after;
458 } else {
459 *a = after;
460 *b = body;
461 }
462 }
463 }
464 }
465 }
466 }
467 }
468}
469
470impl Parser {
471 fn peek(&self) -> Option<char> {
472 self.chars.get(self.pos).copied()
473 }
474 fn peek_at(&self, offset: usize) -> Option<char> {
475 self.chars.get(self.pos + offset).copied()
476 }
477 fn bump(&mut self) -> Option<char> {
478 let c = self.peek();
479 if c.is_some() {
480 self.pos += 1;
481 }
482 c
483 }
484 fn eat(&mut self, c: char) -> bool {
485 if self.peek() == Some(c) {
486 self.pos += 1;
487 true
488 } else {
489 false
490 }
491 }
492
493 fn parse_alt(&mut self) -> Node {
494 let mut branches = vec![self.parse_concat()];
495 while self.eat('|') {
496 branches.push(self.parse_concat());
497 }
498 if branches.len() == 1 {
499 branches.pop().unwrap_or(Node::Empty)
500 } else {
501 Node::Alt(branches)
502 }
503 }
504
505 fn parse_concat(&mut self) -> Node {
506 let mut items = Vec::new();
507 while let Some(c) = self.peek() {
508 if c == '|' || c == ')' {
509 break;
510 }
511 let atom = self.parse_atom();
512 let quantified = self.parse_quantifier(atom);
513 items.push(quantified);
514 }
515 if items.is_empty() {
516 Node::Empty
517 } else if items.len() == 1 {
518 items.pop().unwrap_or(Node::Empty)
519 } else {
520 Node::Concat(items)
521 }
522 }
523
524 fn parse_quantifier(&mut self, atom: Node) -> Node {
525 let (min, max) = match self.peek() {
526 Some('*') => {
527 self.bump();
528 (0, None)
529 }
530 Some('+') => {
531 self.bump();
532 (1, None)
533 }
534 Some('?') => {
535 self.bump();
536 (0, Some(1))
537 }
538 Some('{') => {
539 if let Some((mn, mx)) = self.try_parse_brace() {
540 (mn, mx)
541 } else {
542 return atom;
543 }
544 }
545 _ => return atom,
546 };
547 let greedy = !self.eat('?'); Node::Repeat {
549 node: Box::new(atom),
550 min,
551 max,
552 greedy,
553 }
554 }
555
556 fn try_parse_brace(&mut self) -> Option<(usize, Option<usize>)> {
557 let save = self.pos;
558 self.bump(); let mut min_s = String::new();
560 while let Some(c) = self.peek() {
561 if c.is_ascii_digit() {
562 min_s.push(c);
563 self.bump();
564 } else {
565 break;
566 }
567 }
568 if min_s.is_empty() {
569 self.pos = save;
570 return None;
571 }
572 let min: usize = min_s.parse().unwrap_or(0);
573 let max = if self.eat(',') {
574 let mut max_s = String::new();
575 while let Some(c) = self.peek() {
576 if c.is_ascii_digit() {
577 max_s.push(c);
578 self.bump();
579 } else {
580 break;
581 }
582 }
583 if max_s.is_empty() {
584 None
585 } else {
586 Some(max_s.parse().unwrap_or(min))
587 }
588 } else {
589 Some(min)
590 };
591 if !self.eat('}') {
592 self.pos = save;
593 return None;
594 }
595 Some((min, max))
596 }
597
598 fn parse_atom(&mut self) -> Node {
599 match self.peek() {
600 Some('(') => {
601 self.bump();
602 let mut group_name: Option<String> = None;
603 let idx = if self.peek() == Some('?') {
604 self.bump(); if self.peek() == Some('<') && !matches!(self.peek_at(1), Some('=') | Some('!'))
610 {
611 self.bump(); let mut name = String::new();
613 while let Some(c) = self.peek() {
614 if c == '>' {
615 break;
616 }
617 name.push(c);
618 self.bump();
619 }
620 self.bump(); self.ngroups += 1;
622 group_name = Some(name);
623 Some(self.ngroups)
624 } else {
625 self.bump();
628 None
629 }
630 } else {
631 self.ngroups += 1;
632 Some(self.ngroups)
633 };
634 let inner = self.parse_alt();
635 self.eat(')');
636 if let (Some(idx), Some(name)) = (idx, group_name) {
637 self.group_names.push((name, idx));
638 }
639 Node::Group {
640 idx,
641 node: Box::new(inner),
642 }
643 }
644 Some('[') => self.parse_class(),
645 Some('.') => {
646 self.bump();
647 Node::Any
648 }
649 Some('^') => {
650 self.bump();
651 Node::Start
652 }
653 Some('$') => {
654 self.bump();
655 Node::End
656 }
657 Some('\\') => {
658 self.bump();
659 self.parse_escape()
660 }
661 Some(c) => {
662 self.bump();
663 Node::Char(c)
664 }
665 None => Node::Empty,
666 }
667 }
668
669 fn parse_escape(&mut self) -> Node {
670 match self.bump() {
671 Some('d') => Node::Class(ClassData {
672 negate: false,
673 items: vec![ClassItem::Digit],
674 }),
675 Some('D') => Node::Class(ClassData {
676 negate: false,
677 items: vec![ClassItem::NotDigit],
678 }),
679 Some('w') => Node::Class(ClassData {
680 negate: false,
681 items: vec![ClassItem::Word],
682 }),
683 Some('W') => Node::Class(ClassData {
684 negate: false,
685 items: vec![ClassItem::NotWord],
686 }),
687 Some('s') => Node::Class(ClassData {
688 negate: false,
689 items: vec![ClassItem::Space],
690 }),
691 Some('S') => Node::Class(ClassData {
692 negate: false,
693 items: vec![ClassItem::NotSpace],
694 }),
695 Some('b') => Node::WordBoundary(true),
696 Some('B') => Node::WordBoundary(false),
697 Some('n') => Node::Char('\n'),
698 Some('t') => Node::Char('\t'),
699 Some('r') => Node::Char('\r'),
700 Some('f') => Node::Char('\u{0C}'),
701 Some('v') => Node::Char('\u{0B}'),
702 Some('0') => Node::Char('\0'),
703 Some('u') => Node::Char(self.parse_u_escape()),
707 Some('x') => Node::Char(self.parse_x_escape()),
708 Some('p') => self.parse_p_escape(false),
709 Some('P') => self.parse_p_escape(true),
710 Some(c) if c.is_ascii_digit() => {
711 let mut num = String::new();
712 num.push(c);
713 while let Some(d) = self.peek() {
714 if d.is_ascii_digit() {
715 num.push(d);
716 self.bump();
717 } else {
718 break;
719 }
720 }
721 Node::Backref(num.parse().unwrap_or(0))
722 }
723 Some(c) => Node::Char(c), None => Node::Empty,
725 }
726 }
727
728 fn parse_p_escape(&mut self, negate: bool) -> Node {
729 if self.peek() == Some('{') {
730 self.bump();
731 let mut prop_name = String::new();
732 while let Some(c) = self.bump() {
733 if c == '}' {
734 break;
735 }
736 prop_name.push(c);
737 }
738 Node::Class(ClassData {
739 negate: false,
740 items: vec![ClassItem::UnicodeProp(prop_name, negate)],
741 })
742 } else {
743 Node::Char(if negate { 'P' } else { 'p' })
744 }
745 }
746
747 fn parse_u_escape(&mut self) -> char {
750 if self.peek() == Some('{') {
751 self.bump(); let mut hex = String::new();
753 while let Some(c) = self.peek() {
754 if c == '}' {
755 break;
756 }
757 hex.push(c);
758 self.bump();
759 }
760 self.bump(); u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32).unwrap_or('u')
762 } else {
763 let save = self.pos;
764 let mut hex = String::new();
765 for _ in 0..4 {
766 match self.peek() {
767 Some(c) if c.is_ascii_hexdigit() => {
768 hex.push(c);
769 self.bump();
770 }
771 _ => break,
772 }
773 }
774 match u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32) {
775 Some(ch) if hex.len() == 4 => ch,
776 _ => {
777 self.pos = save;
780 'u'
781 }
782 }
783 }
784 }
785 fn parse_x_escape(&mut self) -> char {
787 let save = self.pos;
788 let mut hex = String::new();
789 for _ in 0..2 {
790 match self.peek() {
791 Some(c) if c.is_ascii_hexdigit() => {
792 hex.push(c);
793 self.bump();
794 }
795 _ => break,
796 }
797 }
798 match u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32) {
799 Some(ch) if hex.len() == 2 => ch,
800 _ => {
801 self.pos = save;
802 'x'
803 }
804 }
805 }
806
807 fn parse_class(&mut self) -> Node {
808 self.bump(); let negate = self.eat('^');
810 let mut items = Vec::new();
811 while let Some(c) = self.peek() {
812 if c == ']' {
813 break;
814 }
815 let lo = if c == '\\' {
816 self.bump();
817 match self.bump() {
818 Some('d') => {
819 items.push(ClassItem::Digit);
820 continue;
821 }
822 Some('D') => {
823 items.push(ClassItem::NotDigit);
824 continue;
825 }
826 Some('w') => {
827 items.push(ClassItem::Word);
828 continue;
829 }
830 Some('W') => {
831 items.push(ClassItem::NotWord);
832 continue;
833 }
834 Some('s') => {
835 items.push(ClassItem::Space);
836 continue;
837 }
838 Some('S') => {
839 items.push(ClassItem::NotSpace);
840 continue;
841 }
842 Some('n') => '\n',
843 Some('t') => '\t',
844 Some('r') => '\r',
845 Some('u') => self.parse_u_escape(),
849 Some('x') => self.parse_x_escape(),
850 Some(e) => e,
851 None => break,
852 }
853 } else {
854 self.bump();
855 c
856 };
857 if self.peek() == Some('-') && self.chars.get(self.pos + 1).is_some_and(|&x| x != ']') {
859 self.bump(); let hi = if self.peek() == Some('\\') {
861 self.bump();
862 match self.bump() {
863 Some('u') => self.parse_u_escape(),
864 Some('x') => self.parse_x_escape(),
865 Some(e) => e,
866 None => lo,
867 }
868 } else {
869 self.bump().unwrap_or(lo)
870 };
871 items.push(ClassItem::Range(lo, hi));
872 } else {
873 items.push(ClassItem::Ch(lo));
874 }
875 }
876 self.eat(']');
877 Node::Class(ClassData { negate, items })
878 }
879}