1use alloc::boxed::Box;
30use alloc::vec;
31use alloc::vec::Vec;
32
33fn class_holds(name: &str, c: char) -> Option<bool> {
37 Some(match name {
38 "alpha" => c.is_alphabetic(),
39 "digit" => c.is_ascii_digit(),
40 "alnum" => c.is_alphanumeric(),
41 "upper" => c.is_uppercase(),
42 "lower" => c.is_lowercase(),
43 "space" => c.is_whitespace(),
44 "blank" => c == ' ' || c == '\t',
45 "punct" => c.is_ascii_punctuation(),
46 "xdigit" => c.is_ascii_hexdigit(),
47 "print" => !c.is_control(),
48 "graph" => !c.is_control() && !c.is_whitespace(),
49 "cntrl" => c.is_control(),
50 _ => return None,
51 })
52}
53
54fn bracket(p: &[char], mut i: usize, glob: bool) -> Option<(Set, usize)> {
57 let mut set = Set { neg: false, items: Vec::new() };
58 if i < p.len() && (p[i] == '^' || (glob && p[i] == '!')) {
59 set.neg = true;
60 i += 1;
61 }
62 let start = i;
63 loop {
64 let c = *p.get(i)?;
65 if c == ']' && i > start {
66 return Some((set, i + 1));
67 }
68 if c == '[' && p.get(i + 1) == Some(&':') {
69 let rest = &p[i + 2..];
70 let end = rest.windows(2).position(|w| w == [':', ']'])?;
71 let name: alloc::string::String = rest[..end].iter().collect();
72 class_holds(&name, 'a')?;
73 set.items.push(Item::Class(name));
74 i += 2 + end + 2;
75 continue;
76 }
77 if c == '[' && matches!(p.get(i + 1), Some('.') | Some('=')) {
78 return None;
79 }
80 let mut lo = c;
81 i += 1;
82 if glob && c == '\\' && i < p.len() {
83 lo = p[i];
84 i += 1;
85 }
86 if p.get(i) == Some(&'-') && p.get(i + 1).is_some_and(|&x| x != ']') {
87 let mut hi = p[i + 1];
88 i += 2;
89 if glob && hi == '\\' && i < p.len() {
90 hi = p[i];
91 i += 1;
92 }
93 if hi < lo {
94 return None;
95 }
96 set.items.push(Item::Range(lo, hi));
97 } else {
98 set.items.push(Item::Range(lo, lo));
99 }
100 }
101}
102
103pub fn glob(pat: &str, s: &str) -> bool {
105 let p: Vec<char> = pat.chars().collect();
106 let t: Vec<char> = s.chars().collect();
107 let (mut pi, mut ti) = (0usize, 0usize);
108 let mut star: Option<(usize, usize)> = None;
110 loop {
111 if pi < p.len() {
112 match p[pi] {
113 '*' => {
114 star = Some((pi, ti));
115 pi += 1;
116 continue;
117 }
118 '?' if ti < t.len() => {
119 pi += 1;
120 ti += 1;
121 continue;
122 }
123 '[' => {
124 if let Some((set, next)) = bracket(&p, pi + 1, true) {
125 if ti < t.len() && set.holds(t[ti], false) {
126 pi = next;
127 ti += 1;
128 continue;
129 }
130 } else if ti < t.len() && t[ti] == '[' {
131 pi += 1;
133 ti += 1;
134 continue;
135 }
136 }
137 '\\' if pi + 1 < p.len() => {
138 if ti < t.len() && t[ti] == p[pi + 1] {
139 pi += 2;
140 ti += 1;
141 continue;
142 }
143 }
144 '?' => {}
145 c => {
146 if ti < t.len() && t[ti] == c {
147 pi += 1;
148 ti += 1;
149 continue;
150 }
151 }
152 }
153 } else if ti == t.len() {
154 return true;
155 }
156 match star {
157 Some((sp, st)) if st < t.len() => {
158 star = Some((sp, st + 1));
159 pi = sp + 1;
160 ti = st + 1;
161 }
162 _ => return false,
163 }
164 }
165}
166
167pub fn has_glob(s: &str) -> bool {
169 let mut esc = false;
170 for c in s.chars() {
171 if esc {
172 esc = false;
173 continue;
174 }
175 match c {
176 '\\' => esc = true,
177 '*' | '?' | '[' => return true,
178 _ => {}
179 }
180 }
181 false
182}
183
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
188#[non_exhaustive]
189pub enum Syntax {
190 Basic,
192 Extended,
194}
195
196#[derive(Clone, Copy, Debug, PartialEq, Eq)]
198#[non_exhaustive]
199pub enum ReErr {
200 Paren,
202 CloseParen,
204 Bracket,
206 Range,
208 Class,
210 Backslash,
212 Brace,
214 BraceContent,
216 Repeat,
218 TooBig,
220}
221
222impl ReErr {
223 pub fn text(self) -> &'static str {
225 match self {
226 ReErr::Paren => "Unmatched ( or \\(",
227 ReErr::CloseParen => "Unmatched ) or \\)",
228 ReErr::Bracket => "Unmatched [, [^, [:, [., or [=",
229 ReErr::Range => "Invalid range end",
230 ReErr::Class => "Invalid character class name",
231 ReErr::Backslash => "Trailing backslash",
232 ReErr::Brace => "Unmatched \\{",
233 ReErr::BraceContent => "Invalid content of \\{\\}",
234 ReErr::Repeat => "Invalid preceding regular expression",
235 ReErr::TooBig => "Regular expression too big",
236 }
237 }
238}
239
240#[derive(Clone, Debug, PartialEq, Eq)]
241enum Item {
242 Range(char, char),
243 Class(alloc::string::String),
244}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
248struct Set {
249 neg: bool,
250 items: Vec<Item>,
251}
252
253fn lower(c: char) -> char {
254 c.to_lowercase().next().unwrap_or(c)
255}
256
257fn upper(c: char) -> char {
258 c.to_uppercase().next().unwrap_or(c)
259}
260
261impl Set {
262 fn holds(&self, c: char, icase: bool) -> bool {
263 let one = |c: char| {
264 self.items.iter().any(|it| match it {
265 Item::Range(a, b) => *a <= c && c <= *b,
266 Item::Class(n) => class_holds(n, c).unwrap_or(false),
267 })
268 };
269 let hit = one(c) || (icase && (one(lower(c)) || one(upper(c))));
270 hit != self.neg
271 }
272 fn word(neg: bool) -> Set {
273 Set { neg, items: vec![Item::Class(alloc::string::String::from("alnum")), Item::Range('_', '_')] }
274 }
275 fn space(neg: bool) -> Set {
276 Set { neg, items: vec![Item::Class(alloc::string::String::from("space"))] }
277 }
278}
279
280#[derive(Clone, Copy, Debug, PartialEq, Eq)]
282enum Edge {
283 Bol,
284 Eol,
285 WordStart,
287 WordEnd,
288 Word,
290 NotWord,
291}
292
293#[derive(Clone, Debug, PartialEq, Eq)]
294enum Node {
295 Empty,
296 Char(char),
297 Any,
298 Set(Set),
299 Edge(Edge),
300 Cat(Vec<Node>),
301 Alt(Vec<Node>),
302 Rep(Box<Node>, u32, Option<u32>),
303}
304
305const REP_MAX: u32 = 255;
306const PROG_MAX: usize = 20_000;
307
308struct Parser<'a> {
309 p: &'a [char],
310 i: usize,
311 ere: bool,
312 depth: u32,
313}
314
315impl Parser<'_> {
316 fn peek(&self) -> Option<char> {
317 self.p.get(self.i).copied()
318 }
319 fn peek2(&self) -> Option<char> {
320 self.p.get(self.i + 1).copied()
321 }
322 fn at_alt(&self) -> bool {
324 if self.ere { self.peek() == Some('|') } else { self.peek() == Some('\\') && self.peek2() == Some('|') }
325 }
326 fn at_close(&self) -> bool {
328 if self.ere { self.peek() == Some(')') } else { self.peek() == Some('\\') && self.peek2() == Some(')') }
329 }
330 fn alt(&mut self) -> Result<Node, ReErr> {
331 let mut alts = vec![self.cat()?];
332 while self.at_alt() {
333 self.i += if self.ere { 1 } else { 2 };
334 alts.push(self.cat()?);
335 }
336 Ok(if alts.len() == 1 { alts.pop().unwrap_or(Node::Empty) } else { Node::Alt(alts) })
337 }
338 fn cat(&mut self) -> Result<Node, ReErr> {
339 let mut v: Vec<Node> = Vec::new();
340 let begin = self.i;
341 while self.i < self.p.len() && !self.at_alt() && !(self.depth > 0 && self.at_close()) {
342 if self.at_close() {
343 return Err(ReErr::CloseParen);
345 }
346 let first = self.i == begin;
347 let atom = self.atom(first)?;
348 let atom = self.reps(atom)?;
349 v.push(atom);
350 }
351 Ok(match v.len() {
352 0 => Node::Empty,
353 1 => v.pop().unwrap_or(Node::Empty),
354 _ => Node::Cat(v),
355 })
356 }
357 fn interval(&mut self) -> Result<Option<(u32, Option<u32>)>, ReErr> {
360 let save = self.i;
361 let num = |s: &mut Parser| -> Option<u32> {
362 let st = s.i;
363 while s.peek().is_some_and(|c| c.is_ascii_digit()) {
364 s.i += 1;
365 }
366 if s.i == st {
367 return None;
368 }
369 let t: alloc::string::String = s.p[st..s.i].iter().collect();
370 Some(t.parse::<u32>().unwrap_or(u32::MAX))
371 };
372 let lo = num(self);
373 let comma = self.peek() == Some(',');
374 if comma {
375 self.i += 1;
376 }
377 let hi = if comma { num(self) } else { lo };
378 let closed = if self.ere {
379 if self.peek() == Some('}') {
380 self.i += 1;
381 true
382 } else {
383 false
384 }
385 } else if self.peek() == Some('\\') && self.peek2() == Some('}') {
386 self.i += 2;
387 true
388 } else {
389 false
390 };
391 if !closed || (lo.is_none() && !comma) {
392 if self.ere {
393 self.i = save;
394 return Ok(None);
395 }
396 return Err(if closed { ReErr::BraceContent } else { ReErr::Brace });
397 }
398 let lo = lo.unwrap_or(0);
400 if lo > REP_MAX || hi.is_some_and(|h| h > REP_MAX || h < lo) {
401 return Err(if hi.is_some_and(|h| h < lo) { ReErr::BraceContent } else { ReErr::TooBig });
402 }
403 Ok(Some((lo, hi)))
404 }
405 fn reps(&mut self, mut atom: Node) -> Result<Node, ReErr> {
407 loop {
408 if matches!(atom, Node::Edge(_)) {
410 return Ok(atom);
411 }
412 let (lo, hi) = match (self.peek(), self.peek2()) {
413 (Some('*'), _) => {
414 self.i += 1;
415 (0, None)
416 }
417 (Some('+'), _) if self.ere => {
418 self.i += 1;
419 (1, None)
420 }
421 (Some('?'), _) if self.ere => {
422 self.i += 1;
423 (0, Some(1))
424 }
425 (Some('\\'), Some('+')) if !self.ere => {
426 self.i += 2;
427 (1, None)
428 }
429 (Some('\\'), Some('?')) if !self.ere => {
430 self.i += 2;
431 (0, Some(1))
432 }
433 (Some('{'), _) if self.ere => {
434 self.i += 1;
435 match self.interval()? {
436 Some(x) => x,
437 None => {
438 self.i -= 1;
440 return Ok(atom);
441 }
442 }
443 }
444 (Some('\\'), Some('{')) if !self.ere => {
445 self.i += 2;
446 match self.interval()? {
447 Some(x) => x,
448 None => return Err(ReErr::Brace),
449 }
450 }
451 _ => return Ok(atom),
452 };
453 if let Node::Rep(_, 0, None) = atom
455 && lo <= 1
456 {
457 continue;
458 }
459 atom = Node::Rep(Box::new(atom), lo, hi);
460 }
461 }
462 fn atom(&mut self, first: bool) -> Result<Node, ReErr> {
463 let Some(c) = self.peek() else { return Ok(Node::Empty) };
464 self.i += 1;
465 match c {
466 '.' => Ok(Node::Any),
467 '[' => {
468 let (set, next) = match bracket(self.p, self.i, false) {
469 Some(x) => x,
470 None => return Err(self.bracket_err()),
471 };
472 self.i = next;
473 Ok(Node::Set(set))
474 }
475 '^' if self.ere || first => Ok(Node::Edge(Edge::Bol)),
476 '$' if self.ere || self.i == self.p.len() || self.at_close() || self.at_alt() => Ok(Node::Edge(Edge::Eol)),
477 '*' => Ok(Node::Char('*')),
479 '+' | '?' if self.ere && first => Ok(Node::Char(c)),
480 '{' if self.ere && first => Ok(Node::Char('{')),
481 '(' if self.ere => self.group(),
482 '\\' => {
483 let Some(e) = self.peek() else { return Err(ReErr::Backslash) };
484 self.i += 1;
485 match e {
486 '(' if !self.ere => self.group(),
487 '{' if !self.ere && first => Ok(Node::Char('{')),
488 'w' => Ok(Node::Set(Set::word(false))),
489 'W' => Ok(Node::Set(Set::word(true))),
490 's' => Ok(Node::Set(Set::space(false))),
491 'S' => Ok(Node::Set(Set::space(true))),
492 '<' => Ok(Node::Edge(Edge::WordStart)),
493 '>' => Ok(Node::Edge(Edge::WordEnd)),
494 'b' => Ok(Node::Edge(Edge::Word)),
495 'B' => Ok(Node::Edge(Edge::NotWord)),
496 'n' => Ok(Node::Char('\n')),
497 't' => Ok(Node::Char('\t')),
498 x => Ok(Node::Char(x)),
499 }
500 }
501 x => Ok(Node::Char(x)),
502 }
503 }
504 fn bracket_err(&self) -> ReErr {
505 let rest = &self.p[self.i..];
507 let end = rest.iter().skip(1).position(|&c| c == ']').map(|x| x + 1);
508 match end {
509 Some(e) => {
510 let body = &rest[..e];
511 if body.windows(2).any(|w| w == ['[', ':']) {
512 ReErr::Class
513 } else if body.windows(3).any(|w| w[1] == '-' && w[2] < w[0]) {
514 ReErr::Range
515 } else {
516 ReErr::Bracket
517 }
518 }
519 None => ReErr::Bracket,
520 }
521 }
522 fn group(&mut self) -> Result<Node, ReErr> {
523 self.depth += 1;
524 let inner = self.alt()?;
525 self.depth -= 1;
526 if !self.at_close() {
527 return Err(ReErr::Paren);
528 }
529 self.i += if self.ere { 1 } else { 2 };
530 Ok(inner)
531 }
532}
533
534#[derive(Clone, Debug, PartialEq, Eq)]
535enum Inst {
536 Char(char),
537 Any,
538 Set(usize),
539 Edge(Edge),
540 Split(usize, usize),
541 Jmp(usize),
542 Match,
543}
544
545#[derive(Clone, Debug, PartialEq, Eq)]
547pub struct Regex {
548 prog: Vec<Inst>,
549 sets: Vec<Set>,
550 icase: bool,
551}
552
553struct Comp {
554 prog: Vec<Inst>,
555 sets: Vec<Set>,
556}
557
558impl Comp {
559 fn emit(&mut self, i: Inst) -> Result<usize, ReErr> {
560 if self.prog.len() >= PROG_MAX {
561 return Err(ReErr::TooBig);
562 }
563 self.prog.push(i);
564 Ok(self.prog.len() - 1)
565 }
566 fn node(&mut self, n: &Node) -> Result<(), ReErr> {
567 match n {
568 Node::Empty => {}
569 Node::Char(c) => {
570 self.emit(Inst::Char(*c))?;
571 }
572 Node::Any => {
573 self.emit(Inst::Any)?;
574 }
575 Node::Set(s) => {
576 self.sets.push(s.clone());
577 let k = self.sets.len() - 1;
578 self.emit(Inst::Set(k))?;
579 }
580 Node::Edge(e) => {
581 self.emit(Inst::Edge(*e))?;
582 }
583 Node::Cat(v) => {
584 for x in v {
585 self.node(x)?;
586 }
587 }
588 Node::Alt(v) => {
589 let mut jumps = Vec::new();
590 for (k, x) in v.iter().enumerate() {
591 if k + 1 < v.len() {
592 let split = self.emit(Inst::Split(0, 0))?;
593 self.node(x)?;
594 jumps.push(self.emit(Inst::Jmp(0))?);
595 let next = self.prog.len();
596 self.prog[split] = Inst::Split(split + 1, next);
597 } else {
598 self.node(x)?;
599 }
600 }
601 let end = self.prog.len();
602 for j in jumps {
603 self.prog[j] = Inst::Jmp(end);
604 }
605 }
606 Node::Rep(x, lo, hi) => {
607 for _ in 0..*lo {
608 self.node(x)?;
609 }
610 match hi {
611 None => {
612 let split = self.emit(Inst::Split(0, 0))?;
614 self.node(x)?;
615 self.emit(Inst::Jmp(split))?;
616 let end = self.prog.len();
617 self.prog[split] = Inst::Split(split + 1, end);
618 }
619 Some(h) => {
620 let mut splits = Vec::new();
622 for _ in *lo..*h {
623 splits.push(self.emit(Inst::Split(0, 0))?);
624 self.node(x)?;
625 }
626 let end = self.prog.len();
627 for s in splits {
628 self.prog[s] = Inst::Split(s + 1, end);
629 }
630 }
631 }
632 }
633 }
634 Ok(())
635 }
636}
637
638fn is_word(c: Option<char>) -> bool {
639 c.is_some_and(|c| c.is_alphanumeric() || c == '_')
640}
641
642impl Regex {
643 pub fn new(pat: &str, syntax: Syntax, icase: bool) -> Result<Regex, ReErr> {
645 let p: Vec<char> = pat.chars().collect();
646 let mut ps = Parser { p: &p, i: 0, ere: syntax == Syntax::Extended, depth: 0 };
647 let node = ps.alt()?;
648 if ps.i < p.len() {
649 return Err(if ps.at_close() { ReErr::CloseParen } else { ReErr::Paren });
650 }
651 let mut c = Comp { prog: Vec::new(), sets: Vec::new() };
652 c.node(&node)?;
653 c.emit(Inst::Match)?;
654 Ok(Regex { prog: c.prog, sets: c.sets, icase })
655 }
656
657 pub fn is_match(&self, s: &str) -> bool {
659 self.find(s).is_some()
660 }
661
662 pub fn find(&self, s: &str) -> Option<(usize, usize)> {
664 self.find_at(s, 0)
665 }
666
667 pub fn find_at(&self, s: &str, from: usize) -> Option<(usize, usize)> {
669 let chars: Vec<(usize, char)> = s.char_indices().collect();
670 let start_k = chars.iter().position(|&(b, _)| b >= from).unwrap_or(chars.len());
671 let mut cur: Vec<(usize, usize)> = Vec::new();
674 let mut next: Vec<(usize, usize)> = Vec::new();
675 let mut seen = vec![usize::MAX; self.prog.len()];
676 let mut best: Option<(usize, usize)> = None;
677 let byte_at = |k: usize| chars.get(k).map_or(s.len(), |x| x.0);
678 let ch = |k: usize| chars.get(k).map(|x| x.1);
679 let mut k = start_k;
680 loop {
681 let prev = if k == 0 { None } else { ch(k - 1) };
682 let here = ch(k);
683 if best.is_none() {
685 self.add(&mut cur, &mut seen, k, 0, byte_at(k), prev, here);
686 }
687 next.clear();
688 for &(pc, st) in cur.iter() {
689 let step = match &self.prog[pc] {
690 Inst::Match => {
691 let end = byte_at(k);
692 if best.is_none_or(|(bs, be)| st < bs || (st == bs && end > be)) {
693 best = Some((st, end));
694 }
695 false
696 }
697 Inst::Char(c) => here.is_some_and(|h| h == *c || (self.icase && lower(h) == lower(*c))),
698 Inst::Any => here.is_some_and(|h| h != '\n'),
699 Inst::Set(i) => here.is_some_and(|h| self.sets[*i].holds(h, self.icase)),
700 _ => false,
701 };
702 if step {
703 self.add(&mut next, &mut seen, k + 1, pc + 1, st, here, ch(k + 1));
704 }
705 }
706 if let Some((bs, _)) = best {
708 next.retain(|t| t.1 <= bs);
709 }
710 if k >= chars.len() || (next.is_empty() && best.is_some()) {
711 break;
712 }
713 core::mem::swap(&mut cur, &mut next);
714 k += 1;
715 }
716 best
717 }
718
719 #[allow(clippy::too_many_arguments)]
722 fn add(&self, list: &mut Vec<(usize, usize)>, seen: &mut [usize], k: usize, pc: usize, st: usize, prev: Option<char>, here: Option<char>) {
723 if seen[pc] == k {
724 return;
725 }
726 seen[pc] = k;
727 match &self.prog[pc] {
728 Inst::Jmp(t) => self.add(list, seen, k, *t, st, prev, here),
729 Inst::Split(a, b) => {
730 self.add(list, seen, k, *a, st, prev, here);
731 self.add(list, seen, k, *b, st, prev, here);
732 }
733 Inst::Edge(e) => {
734 let ok = match e {
735 Edge::Bol => prev.is_none() || prev == Some('\n'),
736 Edge::Eol => here.is_none() || here == Some('\n'),
737 Edge::WordStart => !is_word(prev) && is_word(here),
738 Edge::WordEnd => is_word(prev) && !is_word(here),
739 Edge::Word => is_word(prev) != is_word(here),
740 Edge::NotWord => is_word(prev) == is_word(here),
741 };
742 if ok {
743 self.add(list, seen, k, pc + 1, st, prev, here);
744 }
745 }
746 _ => list.push((pc, st)),
747 }
748 }
749}