1use std::fmt;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Width {
22 Narrow,
24 Wide,
26}
27
28#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct RegexError {
31 pub pattern: String,
32 pub at: usize,
33 pub why: &'static str,
34}
35
36impl fmt::Display for RegexError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "the pre-tokenizer pattern {:?} is refused at unit {}: {}", self.pattern, self.at, self.why)
39 }
40}
41
42impl std::error::Error for RegexError {}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45enum Named {
46 Space,
47 Digit,
48 Word,
49}
50
51impl Named {
52 fn contains(self, u: u32) -> bool {
53 match self {
54 Named::Space => matches!(u, 0x09..=0x0D | 0x20),
55 Named::Digit => (0x30..=0x39).contains(&u),
56 Named::Word => {
57 (0x30..=0x39).contains(&u) || (0x41..=0x5A).contains(&u) || (0x61..=0x7A).contains(&u) || u == 0x5F
58 }
59 }
60 }
61}
62
63fn key(width: Width, u: u32) -> i64 {
65 match width {
66 Width::Narrow => u as u8 as i8 as i64,
67 Width::Wide => u as i64,
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72enum ClassItem {
73 Unit(u32),
74 Range(i64, i64),
75 Named { class: Named, negate: bool },
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
79struct Class {
80 negate: bool,
81 items: Vec<ClassItem>,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85enum Look {
86 LineStart,
87 LineEnd,
88 WordBoundary,
89 NotWordBoundary,
90}
91
92#[derive(Clone, Debug, PartialEq, Eq)]
93enum Node {
94 Unit(u32),
95 Class(Class),
96 Any,
97 Concat(Vec<Node>),
98 Alt(Vec<Node>),
99 Repeat { node: Box<Node>, min: u32, max: Option<u32>, greedy: bool },
100 Look(Look),
101 Ahead { node: Box<Node>, negate: bool },
102}
103
104impl Node {
105 fn nullable(&self) -> bool {
107 match self {
108 Node::Unit(_) | Node::Class(_) | Node::Any => false,
109 Node::Concat(v) => v.iter().all(Node::nullable),
110 Node::Alt(v) => v.iter().any(Node::nullable),
111 Node::Repeat { node, min, .. } => *min == 0 || node.nullable(),
112 Node::Look(_) | Node::Ahead { .. } => true,
113 }
114 }
115}
116
117struct Parser<'a> {
118 p: &'a [u32],
119 i: usize,
120 pattern: &'a str,
121 width: Width,
122}
123
124impl<'a> Parser<'a> {
125 fn err(&self, why: &'static str) -> RegexError {
126 RegexError { pattern: self.pattern.to_string(), at: self.i, why }
127 }
128
129 fn peek(&self) -> Option<u32> {
130 self.p.get(self.i).copied()
131 }
132
133 fn peek_at(&self, k: usize) -> Option<u32> {
134 self.p.get(self.i + k).copied()
135 }
136
137 fn eat(&mut self, c: char) -> bool {
138 if self.peek() == Some(c as u32) {
139 self.i += 1;
140 true
141 } else {
142 false
143 }
144 }
145
146 fn disjunction(&mut self) -> Result<Node, RegexError> {
147 let mut alts = vec![self.alternative()?];
148 while self.eat('|') {
149 alts.push(self.alternative()?);
150 }
151 Ok(if alts.len() == 1 { alts.pop().expect("one alternative") } else { Node::Alt(alts) })
152 }
153
154 fn alternative(&mut self) -> Result<Node, RegexError> {
155 let mut terms = Vec::new();
156 while let Some(c) = self.peek() {
157 if c == '|' as u32 || c == ')' as u32 {
158 break;
159 }
160 terms.push(self.term()?);
161 }
162 Ok(if terms.len() == 1 { terms.pop().expect("one term") } else { Node::Concat(terms) })
163 }
164
165 fn term(&mut self) -> Result<Node, RegexError> {
166 let c = self.peek().expect("a term starts at a unit");
167 if c == '^' as u32 {
168 self.i += 1;
169 return Ok(Node::Look(Look::LineStart));
170 }
171 if c == '$' as u32 {
172 self.i += 1;
173 return Ok(Node::Look(Look::LineEnd));
174 }
175 if c == '\\' as u32 {
176 match self.peek_at(1) {
177 Some(b) if b == 'b' as u32 => {
178 self.i += 2;
179 return Ok(Node::Look(Look::WordBoundary));
180 }
181 Some(b) if b == 'B' as u32 => {
182 self.i += 2;
183 return Ok(Node::Look(Look::NotWordBoundary));
184 }
185 _ => {}
186 }
187 }
188 if c == '(' as u32 && self.peek_at(1) == Some('?' as u32) {
189 match self.peek_at(2) {
190 Some(k) if k == '=' as u32 || k == '!' as u32 => {
191 self.i += 3;
192 let node = self.disjunction()?;
193 if !self.eat(')') {
194 return Err(self.err("a lookahead is not closed"));
195 }
196 return Ok(Node::Ahead { node: Box::new(node), negate: k == '!' as u32 });
197 }
198 _ => {}
199 }
200 }
201 let atom = self.atom()?;
202 self.quantified(atom)
203 }
204
205 fn quantified(&mut self, atom: Node) -> Result<Node, RegexError> {
206 let (min, max) = match self.peek() {
207 Some(c) if c == '*' as u32 => {
208 self.i += 1;
209 (0, None)
210 }
211 Some(c) if c == '+' as u32 => {
212 self.i += 1;
213 (1, None)
214 }
215 Some(c) if c == '?' as u32 => {
216 self.i += 1;
217 (0, Some(1))
218 }
219 Some(c) if c == '{' as u32 => {
220 self.i += 1;
221 let min = self.decimal().ok_or_else(|| self.err("an interval has no lower bound"))?;
222 let max = if self.eat(',') {
223 if self.peek() == Some('}' as u32) {
224 None
225 } else {
226 Some(self.decimal().ok_or_else(|| self.err("an interval's upper bound is not a number"))?)
227 }
228 } else {
229 Some(min)
230 };
231 if !self.eat('}') {
232 return Err(self.err("an interval is not closed"));
233 }
234 if max.is_some_and(|m| m < min) {
235 return Err(self.err("an interval's upper bound is below its lower bound"));
236 }
237 (min, max)
238 }
239 _ => return Ok(atom),
240 };
241 let greedy = !self.eat('?');
242 if atom.nullable() && max != Some(1) {
243 return Err(self.err("a quantified group that can match the empty string"));
244 }
245 Ok(Node::Repeat { node: Box::new(atom), min, max, greedy })
246 }
247
248 fn decimal(&mut self) -> Option<u32> {
249 let start = self.i;
250 let mut v: u32 = 0;
251 while let Some(c) = self.peek() {
252 if !(0x30..=0x39).contains(&c) {
253 break;
254 }
255 v = v.checked_mul(10)?.checked_add(c - 0x30)?;
256 self.i += 1;
257 }
258 (self.i > start).then_some(v)
259 }
260
261 fn atom(&mut self) -> Result<Node, RegexError> {
262 let c = self.peek().expect("an atom starts at a unit");
263 self.i += 1;
264 match char::from_u32(c) {
265 Some('.') => Ok(Node::Any),
266 Some('(') => {
267 if self.peek() == Some('?' as u32) {
268 if self.peek_at(1) == Some(':' as u32) {
269 self.i += 2;
270 } else {
271 return Err(self.err("a group modifier other than (?: (?= (?!"));
272 }
273 }
274 let node = self.disjunction()?;
275 if !self.eat(')') {
276 return Err(self.err("a group is not closed"));
277 }
278 Ok(node)
279 }
280 Some(')') => Err(self.err("an unmatched )")),
281 Some('[') => self.class(),
282 Some('*') | Some('+') | Some('?') | Some('{') => Err(self.err("a quantifier with nothing to repeat")),
283 Some('\\') => self.atom_escape(),
284 _ => Ok(Node::Unit(c)),
285 }
286 }
287
288 fn hex(&mut self, n: usize) -> Result<u32, RegexError> {
289 let mut v = 0u32;
290 for _ in 0..n {
291 let d = self
292 .peek()
293 .and_then(char::from_u32)
294 .and_then(|c| c.to_digit(16))
295 .ok_or_else(|| self.err("a hex escape is short"))?;
296 v = v * 16 + d;
297 self.i += 1;
298 }
299 Ok(v)
300 }
301
302 fn escape(&mut self, in_class: bool) -> Result<Result<u32, (Named, bool)>, RegexError> {
304 let c = self.peek().ok_or_else(|| self.err("a pattern ends in a backslash"))?;
305 self.i += 1;
306 Ok(match char::from_u32(c) {
307 Some('d') => Err((Named::Digit, false)),
308 Some('D') => Err((Named::Digit, true)),
309 Some('s') => Err((Named::Space, false)),
310 Some('S') => Err((Named::Space, true)),
311 Some('w') => Err((Named::Word, false)),
312 Some('W') => Err((Named::Word, true)),
313 Some('f') => Ok(0x0C),
314 Some('n') => Ok(0x0A),
315 Some('r') => Ok(0x0D),
316 Some('t') => Ok(0x09),
317 Some('v') => Ok(0x0B),
318 Some('b') if in_class => Ok(0x08),
319 Some('0') => Ok(0),
320 Some('x') => Ok(self.hex(2)?),
321 Some('u') => Ok(self.hex(4)?),
322 Some('c') => {
323 let l = self
324 .peek()
325 .filter(|&l| (0x41..=0x5A).contains(&l) || (0x61..=0x7A).contains(&l))
326 .ok_or_else(|| self.err("a control escape is not followed by a letter"))?;
327 self.i += 1;
328 Ok(l % 32)
329 }
330 Some(d) if d.is_ascii_digit() => return Err(self.err("a back-reference")),
331 _ => Ok(c),
332 })
333 }
334
335 fn atom_escape(&mut self) -> Result<Node, RegexError> {
336 Ok(match self.escape(false)? {
337 Ok(u) => Node::Unit(u),
338 Err((class, negate)) => {
339 Node::Class(Class { negate: false, items: vec![ClassItem::Named { class, negate }] })
340 }
341 })
342 }
343
344 fn class(&mut self) -> Result<Node, RegexError> {
345 let negate = self.eat('^');
346 let mut items = Vec::new();
347 if self.peek() == Some(']' as u32) {
348 return Err(self.err("an empty bracket expression"));
349 }
350 loop {
351 let c = self.peek().ok_or_else(|| self.err("a bracket expression is not closed"))?;
352 if c == ']' as u32 {
353 self.i += 1;
354 break;
355 }
356 let lo = self.class_atom()?;
357 let dash = self.peek() == Some('-' as u32) && self.peek_at(1).is_some_and(|n| n != ']' as u32);
358 match lo {
359 Ok(lo) if dash => {
360 self.i += 1;
361 let hi = match self.class_atom()? {
362 Ok(hi) => hi,
363 Err(_) => return Err(self.err("a range ends in a class escape")),
364 };
365 let (lo, hi) = (key(self.width, lo), key(self.width, hi));
366 if lo > hi {
367 return Err(self.err("a range whose start is above its end"));
368 }
369 items.push(ClassItem::Range(lo, hi));
370 }
371 Ok(u) => items.push(ClassItem::Unit(u)),
372 Err((class, n)) => items.push(ClassItem::Named { class, negate: n }),
373 }
374 }
375 Ok(Node::Class(Class { negate, items }))
376 }
377
378 fn class_atom(&mut self) -> Result<Result<u32, (Named, bool)>, RegexError> {
379 let c = self.peek().expect("a class atom starts at a unit");
380 self.i += 1;
381 if c == '\\' as u32 {
382 self.escape(true)
383 } else {
384 Ok(Ok(c))
385 }
386 }
387}
388
389#[derive(Clone, Debug)]
390enum Inst {
391 Unit(u32),
392 Class(usize),
393 Any,
394 Split(usize, usize),
395 Jmp(usize),
396 Look(Look),
397 Ahead { body: usize, negate: bool },
398 Match,
399}
400
401#[derive(Clone, Debug)]
403pub struct Regex {
404 insts: Vec<Inst>,
405 classes: Vec<Class>,
406 width: Width,
407}
408
409struct Compiler {
410 insts: Vec<Inst>,
411 classes: Vec<Class>,
412 pending: Vec<(usize, Node)>,
413}
414
415impl Compiler {
416 fn emit(&mut self, i: Inst) -> usize {
417 self.insts.push(i);
418 self.insts.len() - 1
419 }
420
421 fn node(&mut self, n: &Node) {
422 match n {
423 Node::Unit(u) => {
424 self.emit(Inst::Unit(*u));
425 }
426 Node::Class(c) => {
427 self.classes.push(c.clone());
428 let id = self.classes.len() - 1;
429 self.emit(Inst::Class(id));
430 }
431 Node::Any => {
432 self.emit(Inst::Any);
433 }
434 Node::Concat(v) => v.iter().for_each(|n| self.node(n)),
435 Node::Alt(v) => {
436 let mut exits = Vec::new();
437 for (k, alt) in v.iter().enumerate() {
438 if k + 1 < v.len() {
439 let split = self.emit(Inst::Split(0, 0));
440 self.node(alt);
441 exits.push(self.emit(Inst::Jmp(0)));
442 let next = self.insts.len();
443 self.insts[split] = Inst::Split(split + 1, next);
444 } else {
445 self.node(alt);
446 }
447 }
448 let end = self.insts.len();
449 for e in exits {
450 self.insts[e] = Inst::Jmp(end);
451 }
452 }
453 Node::Repeat { node, min, max, greedy } => {
454 for _ in 0..*min {
455 self.node(node);
456 }
457 match max {
458 None => {
459 let split = self.emit(Inst::Split(0, 0));
460 self.node(node);
461 self.emit(Inst::Jmp(split));
462 let out = self.insts.len();
463 self.insts[split] = if *greedy { Inst::Split(split + 1, out) } else { Inst::Split(out, split + 1) };
464 }
465 Some(max) => {
466 let mut splits = Vec::new();
467 for _ in *min..*max {
468 splits.push(self.emit(Inst::Split(0, 0)));
469 self.node(node);
470 }
471 let out = self.insts.len();
472 for s in splits {
473 self.insts[s] = if *greedy { Inst::Split(s + 1, out) } else { Inst::Split(out, s + 1) };
474 }
475 }
476 }
477 }
478 Node::Look(l) => {
479 self.emit(Inst::Look(*l));
480 }
481 Node::Ahead { node, negate } => {
482 let at = self.emit(Inst::Ahead { body: 0, negate: *negate });
483 self.pending.push((at, (**node).clone()));
484 }
485 }
486 }
487}
488
489#[derive(Clone, Copy)]
491struct Ctx {
492 begin: usize,
493 prev_avail: bool,
494 not_null: bool,
495}
496
497impl Regex {
498 pub fn new(pattern: &str, width: Width) -> Result<Regex, RegexError> {
500 let units: Vec<u32> = pattern.chars().map(|c| c as u32).collect();
501 Regex::from_units(&units, pattern, width)
502 }
503
504 pub fn from_units(units: &[u32], pattern: &str, width: Width) -> Result<Regex, RegexError> {
506 let mut p = Parser { p: units, i: 0, pattern, width };
507 let node = p.disjunction()?;
508 if p.i != units.len() {
509 return Err(p.err("an unmatched )"));
510 }
511 let mut c = Compiler { insts: Vec::new(), classes: Vec::new(), pending: Vec::new() };
512 c.node(&node);
513 c.emit(Inst::Match);
514 while let Some((at, body)) = c.pending.pop() {
515 let start = c.insts.len();
516 c.node(&body);
517 c.emit(Inst::Match);
518 if let Inst::Ahead { negate, .. } = c.insts[at] {
519 c.insts[at] = Inst::Ahead { body: start, negate };
520 }
521 }
522 Ok(Regex { insts: c.insts, classes: c.classes, width })
523 }
524
525 fn class_has(&self, id: usize, u: u32) -> bool {
526 let c = &self.classes[id];
527 let k = key(self.width, u);
528 let hit = c.items.iter().any(|item| match *item {
529 ClassItem::Unit(v) => v == u,
530 ClassItem::Range(lo, hi) => lo <= k && k <= hi,
531 ClassItem::Named { class, negate } => class.contains(u) != negate,
532 });
533 hit != c.negate
534 }
535
536 fn any(&self, u: u32) -> bool {
537 match self.width {
538 Width::Narrow => u != 0x0A && u != 0x0D,
539 Width::Wide => !matches!(u, 0x0A | 0x0D | 0x2028 | 0x2029),
540 }
541 }
542
543 fn look(&self, l: Look, text: &[u32], pos: usize, ctx: Ctx) -> bool {
544 let word = |u: u32| Named::Word.contains(u);
545 match l {
546 Look::LineStart => pos == ctx.begin && !ctx.prev_avail,
547 Look::LineEnd => pos == text.len(),
548 Look::WordBoundary | Look::NotWordBoundary => {
549 let left = (pos != ctx.begin || ctx.prev_avail) && pos > 0 && word(text[pos - 1]);
550 let right = pos < text.len() && word(text[pos]);
551 (left != right) == (l == Look::WordBoundary)
552 }
553 }
554 }
555
556 fn run(&self, pc: usize, pos: usize, text: &[u32], ctx: Ctx) -> Option<usize> {
558 let mut stack = vec![(pc, pos)];
559 while let Some((mut pc, mut pos)) = stack.pop() {
560 loop {
561 match self.insts[pc] {
562 Inst::Unit(u) => {
563 if pos < text.len() && text[pos] == u {
564 pc += 1;
565 pos += 1;
566 } else {
567 break;
568 }
569 }
570 Inst::Class(id) => {
571 if pos < text.len() && self.class_has(id, text[pos]) {
572 pc += 1;
573 pos += 1;
574 } else {
575 break;
576 }
577 }
578 Inst::Any => {
579 if pos < text.len() && self.any(text[pos]) {
580 pc += 1;
581 pos += 1;
582 } else {
583 break;
584 }
585 }
586 Inst::Split(a, b) => {
587 stack.push((b, pos));
588 pc = a;
589 }
590 Inst::Jmp(a) => pc = a,
591 Inst::Look(l) => {
592 if self.look(l, text, pos, ctx) {
593 pc += 1;
594 } else {
595 break;
596 }
597 }
598 Inst::Ahead { body, negate } => {
599 let sub = Ctx { begin: pos, ..ctx };
600 if self.run(body, pos, text, sub).is_some() != negate {
601 pc += 1;
602 } else {
603 break;
604 }
605 }
606 Inst::Match => {
607 if ctx.not_null && pos == ctx.begin {
608 break;
609 }
610 return Some(pos);
611 }
612 }
613 }
614 }
615 None
616 }
617
618 fn search(&self, text: &[u32], start: usize, prev_avail: bool, continuous: bool, not_null: bool) -> Option<(usize, usize)> {
620 let mut s = start;
621 loop {
622 let ctx = Ctx { begin: s, prev_avail: prev_avail || s > start, not_null };
623 if let Some(e) = self.run(0, s, text, ctx) {
624 return Some((s, e));
625 }
626 if continuous || s == text.len() {
627 return None;
628 }
629 s += 1;
630 }
631 }
632
633 pub fn matches(&self, text: &[u32]) -> Vec<(usize, usize)> {
635 let mut out = Vec::new();
636 let mut m = self.search(text, 0, false, false, false);
637 while let Some((ms, me)) = m {
638 out.push((ms, me - ms));
639 let mut start = me;
640 if ms == me {
641 if me == text.len() {
642 break;
643 }
644 if let Some(next) = self.search(text, me, true, true, true) {
645 m = Some(next);
646 continue;
647 }
648 start += 1;
649 }
650 m = self.search(text, start, true, false, false);
651 }
652 out
653 }
654}