1use std::collections::HashMap;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18pub struct Flags {
19 pub ignorecase: bool,
20 pub multiline: bool,
21 pub dotall: bool,
22 pub ascii: bool,
23}
24
25impl Flags {
26 #[must_use]
28 pub fn ignorecase_multiline() -> Self {
29 Self {
30 ignorecase: true,
31 multiline: true,
32 dotall: false,
33 ascii: false,
34 }
35 }
36
37 pub(crate) fn apply_delta(&mut self, add: &Flags, del: &Flags) {
38 self.ignorecase = if del.ignorecase {
39 false
40 } else {
41 self.ignorecase || add.ignorecase
42 };
43 self.multiline = if del.multiline {
44 false
45 } else {
46 self.multiline || add.multiline
47 };
48 self.dotall = if del.dotall {
49 false
50 } else {
51 self.dotall || add.dotall
52 };
53 self.ascii = if del.ascii {
54 false
55 } else {
56 self.ascii || add.ascii
57 };
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum ClassItem {
64 Literal(u32),
65 Range(u32, u32),
66 Category(Category),
67 Negate,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub enum Category {
73 Digit,
74 NotDigit,
75 Space,
76 NotSpace,
77 Word,
78 NotWord,
79}
80
81impl Category {
82 fn from_escape(escape: char) -> Option<Self> {
83 Some(match escape {
84 'd' => Self::Digit,
85 'D' => Self::NotDigit,
86 's' => Self::Space,
87 'S' => Self::NotSpace,
88 'w' => Self::Word,
89 'W' => Self::NotWord,
90 _ => return None,
91 })
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum At {
98 Beginning,
99 BeginningLine,
100 End,
101 EndLine,
102 BeginningString,
103 EndString,
104 Boundary,
105 NonBoundary,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum RepeatKind {
111 Greedy,
112 Lazy,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum Op {
118 Literal(u32),
119 NotLiteral(u32),
120 Any,
121 In(Vec<ClassItem>),
122 Category(Category),
123 Repeat {
125 kind: RepeatKind,
126 low: u32,
127 high: Option<u32>,
128 body: Vec<Op>,
129 },
130 SubPattern {
131 group: Option<u32>,
132 add: Flags,
133 del: Flags,
134 body: Vec<Op>,
135 },
136 Branch(Vec<Vec<Op>>),
137 Assert {
138 behind: bool,
139 negated: bool,
140 body: Vec<Op>,
141 },
142 Failure,
144 At(At),
145 GroupRef(u32),
146 GroupRefExists {
147 group: u32,
148 yes: Vec<Op>,
149 no: Option<Vec<Op>>,
150 },
151}
152
153impl Op {
154 #[must_use]
157 pub fn is_pairing(&self) -> bool {
158 matches!(
159 self,
160 Self::Literal(_) | Self::NotLiteral(_) | Self::In(_) | Self::Any | Self::Category(_)
161 )
162 }
163}
164
165const MAXREPEAT: u64 = 4_294_967_287;
166
167#[derive(Debug, Clone, PartialEq, Eq)]
169pub struct ParseError(pub String);
170
171impl std::fmt::Display for ParseError {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 write!(f, "{}", self.0)
174 }
175}
176
177impl std::error::Error for ParseError {}
178
179struct Parser {
180 chars: Vec<char>,
181 pos: usize,
182 next_group: u32,
183 names: HashMap<String, u32>,
184 open_groups: Vec<u32>,
185 flags: Flags,
187}
188
189fn is_digit(c: char) -> bool {
190 c.is_ascii_digit()
191}
192
193fn is_oct_digit(c: char) -> bool {
194 matches!(c, '0'..='7')
195}
196
197fn is_hex_digit(c: char) -> bool {
198 c.is_ascii_hexdigit()
199}
200
201impl Parser {
202 fn new(pattern: &str, flags: Flags) -> Self {
203 Self {
204 chars: pattern.chars().collect(),
205 pos: 0,
206 next_group: 1,
207 names: HashMap::new(),
208 open_groups: Vec::new(),
209 flags,
210 }
211 }
212
213 fn peek(&self) -> Option<char> {
214 self.chars.get(self.pos).copied()
215 }
216
217 fn get(&mut self) -> Option<char> {
218 let c = self.peek();
219 if c.is_some() {
220 self.pos += 1;
221 }
222 c
223 }
224
225 fn match_char(&mut self, c: char) -> bool {
226 if self.peek() == Some(c) {
227 self.pos += 1;
228 true
229 } else {
230 false
231 }
232 }
233
234 fn get_while<F: Fn(char) -> bool>(&mut self, pred: F) -> String {
235 let mut out = String::new();
236 while let Some(c) = self.peek() {
237 if !pred(c) {
238 break;
239 }
240 out.push(c);
241 self.pos += 1;
242 }
243 out
244 }
245
246 fn get_until(&mut self, end: char, what: &str) -> Result<String, ParseError> {
247 let mut out = String::new();
248 while let Some(c) = self.get() {
249 if c == end {
250 return Ok(out);
251 }
252 out.push(c);
253 }
254 Err(ParseError(format!("missing {end}, unterminated {what}")))
255 }
256
257 fn check_group_name(&self, name: &str) -> Result<(), ParseError> {
258 let invalid = name.is_empty()
261 || name
262 .chars()
263 .any(|c| !(c.is_ascii_alphanumeric() || c == '_'))
264 || name.chars().next().is_some_and(|c| c.is_ascii_digit());
265 if invalid {
266 return Err(ParseError(format!("bad character in group name {name:?}")));
267 }
268 Ok(())
269 }
270
271 fn opengroup(&mut self, name: Option<String>) -> Result<u32, ParseError> {
272 let gid = self.next_group;
273 self.next_group += 1;
274 if let Some(name) = name {
275 self.names.insert(name, gid);
276 }
277 self.open_groups.push(gid);
278 Ok(gid)
279 }
280
281 fn closegroup(&mut self, gid: u32) {
282 self.open_groups.retain(|g| *g != gid);
283 }
284
285 fn checkgroup(&self, gid: u32) -> bool {
286 !self.open_groups.contains(&gid)
287 }
288
289 fn parse_flags(&mut self) -> Result<Option<(Flags, Flags)>, ParseError> {
291 let mut add = Flags::default();
292 let mut del = Flags::default();
293 let mut char_opt = self.get();
294 let mut negating = false;
295 loop {
296 let Some(c) = char_opt else {
297 return Err(ParseError("missing -, : or )".into()));
298 };
299 match c {
300 ')' => {
301 if negating {
302 return Err(ParseError("missing :".into()));
305 }
306 self.flags.apply_delta(&add, &del);
308 return Ok(None);
309 }
310 ':' => break,
311 '-' => negating = true,
312 'i' | 'm' | 's' | 'a' => {
313 let flag = Flags {
314 ignorecase: c == 'i',
315 multiline: c == 'm',
316 dotall: c == 's',
317 ascii: c == 'a',
318 };
319 if negating {
320 del.apply_delta(&flag, &Flags::default());
321 } else {
322 add.apply_delta(&flag, &Flags::default());
323 }
324 }
325 'x' | 'u' => {}
328 'L' => {
329 return Err(ParseError(
330 "bad inline flags: cannot use 'L' flag with a str pattern".into(),
331 ));
332 }
333 _ => {
334 if c.is_ascii_alphabetic() {
335 return Err(ParseError("unknown flag".into()));
336 }
337 return Err(ParseError("missing -, : or )".into()));
338 }
339 }
340 char_opt = self.get();
341 }
342 if add.ignorecase && del.ignorecase
343 || add.multiline && del.multiline
344 || add.dotall && del.dotall
345 || add.ascii && del.ascii
346 {
347 return Err(ParseError(
348 "bad inline flags: flag turned on and off".into(),
349 ));
350 }
351 Ok(Some((add, del)))
352 }
353
354 fn class_escape(&mut self) -> Result<ClassItem, ParseError> {
356 let escape = self.get().ok_or_else(|| ParseError("bad escape".into()))?;
357 match escape {
358 'a' => Ok(ClassItem::Literal(0x07)),
359 'b' => Ok(ClassItem::Literal(0x08)),
360 'f' => Ok(ClassItem::Literal(0x0C)),
361 'n' => Ok(ClassItem::Literal(0x0A)),
362 'r' => Ok(ClassItem::Literal(0x0D)),
363 't' => Ok(ClassItem::Literal(0x09)),
364 'v' => Ok(ClassItem::Literal(0x0B)),
365 '\\' => Ok(ClassItem::Literal(0x5C)),
366 'd' | 'D' | 's' | 'S' | 'w' | 'W' => Ok(ClassItem::Category(
367 Category::from_escape(escape).expect("category letters guarded by the match arm"),
368 )),
369 'x' => {
370 let digits = self.get_while(is_hex_digit);
371 if digits.len() != 2 {
372 return Err(ParseError(format!("incomplete escape \\x{digits}")));
373 }
374 let value = u32::from_str_radix(&digits, 16).unwrap_or(0);
375 Ok(ClassItem::Literal(value))
376 }
377 'u' => {
378 let digits = self.get_while(is_hex_digit);
379 if digits.len() != 4 {
380 return Err(ParseError(format!("incomplete escape \\u{digits}")));
381 }
382 let value = u32::from_str_radix(&digits, 16).unwrap_or(0);
383 char::from_u32(value)
384 .map(|_| ClassItem::Literal(value))
385 .ok_or_else(|| ParseError(format!("illegal Unicode character in \\u{digits}")))
386 }
387 'U' => {
388 let digits = self.get_while(is_hex_digit);
389 if digits.len() != 8 {
390 return Err(ParseError(format!("incomplete escape \\U{digits}")));
391 }
392 let value = u32::from_str_radix(&digits, 16).unwrap_or(0);
393 char::from_u32(value)
394 .map(|_| ClassItem::Literal(value))
395 .ok_or_else(|| ParseError(format!("illegal Unicode character in \\U{digits}")))
396 }
397 'N' => Err(ParseError(
398 "bad escape \\N: named escapes are not supported by the safety chain".into(),
399 )),
400 c if is_oct_digit(c) => {
401 let mut digits = String::new();
402 digits.push(c);
403 digits.push_str(&self.get_while(is_oct_digit));
404 let value = u32::from_str_radix(&digits, 8).unwrap_or(0x11000);
406 if value > 0o377 {
407 return Err(ParseError(format!(
408 "octal escape value \\{digits} outside of range 0-0o377"
409 )));
410 }
411 Ok(ClassItem::Literal(value))
412 }
413 c if is_digit(c) => Err(ParseError(format!("bad escape \\{c}"))),
414 c if c.is_ascii_alphabetic() => Err(ParseError(format!("bad escape \\{c}"))),
415 c => Ok(ClassItem::Literal(u32::from(c))),
416 }
417 }
418
419 fn escape(&mut self) -> Result<Op, ParseError> {
421 let escape = self.get().ok_or_else(|| ParseError("bad escape".into()))?;
422 if let Some(category) = Category::from_escape(escape) {
423 return Ok(Op::In(vec![ClassItem::Category(category)]));
424 }
425 match escape {
426 'A' => Ok(Op::At(At::BeginningString)),
427 'z' | 'Z' => Ok(Op::At(At::EndString)),
428 'b' => Ok(Op::At(At::Boundary)),
429 'B' => Ok(Op::At(At::NonBoundary)),
430 'a' => Ok(Op::Literal(0x07)),
431 'f' => Ok(Op::Literal(0x0C)),
432 'n' => Ok(Op::Literal(0x0A)),
433 'r' => Ok(Op::Literal(0x0D)),
434 't' => Ok(Op::Literal(0x09)),
435 'v' => Ok(Op::Literal(0x0B)),
436 '\\' => Ok(Op::Literal(0x5C)),
437 'x' => {
438 let digits = self.get_while(is_hex_digit);
439 if digits.len() != 2 {
440 return Err(ParseError(format!("incomplete escape \\x{digits}")));
441 }
442 Ok(Op::Literal(u32::from_str_radix(&digits, 16).unwrap_or(0)))
443 }
444 'u' => {
445 let digits = self.get_while(is_hex_digit);
446 if digits.len() != 4 {
447 return Err(ParseError(format!("incomplete escape \\u{digits}")));
448 }
449 let value = u32::from_str_radix(&digits, 16).unwrap_or(0);
450 char::from_u32(value)
451 .map(|_| Op::Literal(value))
452 .ok_or_else(|| ParseError(format!("illegal Unicode character in \\u{digits}")))
453 }
454 'U' => {
455 let digits = self.get_while(is_hex_digit);
456 if digits.len() != 8 {
457 return Err(ParseError(format!("incomplete escape \\U{digits}")));
458 }
459 let value = u32::from_str_radix(&digits, 16).unwrap_or(0);
460 char::from_u32(value)
461 .map(|_| Op::Literal(value))
462 .ok_or_else(|| ParseError(format!("illegal Unicode character in \\U{digits}")))
463 }
464 'N' => Err(ParseError(
465 "bad escape \\N: named escapes are not supported by the safety chain".into(),
466 )),
467 '0' => {
468 let digits = self.get_while(is_oct_digit);
469 Ok(Op::Literal(u32::from_str_radix(&digits, 8).unwrap_or(0)))
470 }
471 c if is_digit(c) => {
472 let mut digits = String::new();
473 digits.push(c);
474 if self.peek().is_some_and(is_digit) {
475 digits.push(self.get().expect("peeked digit"));
476 let all_octal =
477 digits.chars().all(is_oct_digit) && self.peek().is_some_and(is_oct_digit);
478 if all_octal {
479 digits.push(self.get().expect("peeked octal digit"));
480 let value = u32::from_str_radix(&digits, 8).expect("octal digits");
481 if value > 0o377 {
482 return Err(ParseError(format!(
483 "octal escape value \\{digits} outside of range 0-0o377"
484 )));
485 }
486 return Ok(Op::Literal(value));
487 }
488 }
489 let group: u32 = digits.parse().unwrap_or(u32::MAX);
490 if (group as u64) < u64::from(self.next_group) {
491 if !self.checkgroup(group) {
492 return Err(ParseError("cannot refer to an open group".into()));
493 }
494 return Ok(Op::GroupRef(group));
495 }
496 Err(ParseError(format!("invalid group reference {group}")))
497 }
498 c if c.is_ascii_alphabetic() => Err(ParseError(format!("bad escape \\{c}"))),
499 c => Ok(Op::Literal(u32::from(c))),
500 }
501 }
502
503 fn parse_class(&mut self) -> Result<Op, ParseError> {
504 let negate = self.match_char('^');
505 let mut set: Vec<ClassItem> = Vec::new();
506 loop {
507 let this = self
508 .get()
509 .ok_or_else(|| ParseError("unterminated character set".into()))?;
510 if this == ']' && !set.is_empty() {
511 break;
512 }
513 let this_text = if this == '\\' {
514 format!("\\{}", self.peek().map(String::from).unwrap_or_default())
515 } else {
516 this.to_string()
517 };
518 let code1 = if this == '\\' {
519 self.class_escape()?
520 } else {
521 ClassItem::Literal(u32::from(this))
522 };
523 if self.match_char('-') {
524 let that = self
525 .get()
526 .ok_or_else(|| ParseError("unterminated character set".into()))?;
527 if that == ']' {
528 set.push(code1);
529 set.push(ClassItem::Literal(u32::from('-')));
530 break;
531 }
532 let that_text = if that == '\\' {
533 format!("\\{}", self.peek().map(String::from).unwrap_or_default())
534 } else {
535 that.to_string()
536 };
537 let code2 = if that == '\\' {
538 self.class_escape()?
539 } else {
540 ClassItem::Literal(u32::from(that))
541 };
542 let (ClassItem::Literal(lo), ClassItem::Literal(hi)) = (&code1, &code2) else {
543 return Err(ParseError(format!(
544 "bad character range {this_text}-{that_text}"
545 )));
546 };
547 if hi < lo {
548 return Err(ParseError(format!(
549 "bad character range {this_text}-{that_text}"
550 )));
551 }
552 set.push(ClassItem::Range(*lo, *hi));
553 } else {
554 set.push(code1);
555 }
556 }
557 let mut seen: Vec<ClassItem> = Vec::new();
559 for item in set {
560 if !seen.contains(&item) {
561 seen.push(item);
562 }
563 }
564 if seen.len() == 1
565 && let ClassItem::Literal(cp) = seen[0]
566 {
567 if negate {
568 return Ok(Op::NotLiteral(cp));
569 }
570 return Ok(Op::Literal(cp));
571 }
572 let mut items = Vec::with_capacity(seen.len() + 1);
573 if negate {
574 items.push(ClassItem::Negate);
575 }
576 items.extend(seen);
577 Ok(Op::In(items))
578 }
579
580 fn parse_repeat(&mut self, ops: &mut Vec<Op>, marker: char) -> Result<(), ParseError> {
581 let (low, high) = if marker == '?' {
583 (0u32, Some(1u32))
584 } else if marker == '*' || marker == '+' {
585 (u32::from(marker == '+'), None)
586 } else {
587 let brace_pos = self.pos - 1;
589 if self.peek() == Some('}') {
590 ops.push(Op::Literal(u32::from('{')));
591 return Ok(());
592 }
593 let lo = self.get_while(is_digit);
594 let hi = if self.match_char(',') {
595 Some(self.get_while(is_digit))
596 } else {
597 None };
599 if !self.match_char('}') {
600 ops.push(Op::Literal(u32::from('{')));
603 self.pos = brace_pos + 1;
604 return Ok(());
605 }
606 let mut min = 0u32;
607 let mut max = high_from(lo.as_str(), hi.as_deref())?;
608 if !lo.is_empty() {
609 min = parse_repeat_count(lo.as_str())?;
610 }
611 if let Some(mx) = max {
612 if mx < min {
613 return Err(ParseError("min repeat greater than max repeat".into()));
614 }
615 max = Some(mx);
616 }
617 (min, max)
618 };
619 let Some(last) = ops.last() else {
620 return Err(ParseError("nothing to repeat".into()));
621 };
622 if matches!(last, Op::At(_)) {
623 return Err(ParseError("nothing to repeat".into()));
624 }
625 if matches!(last, Op::Repeat { .. }) {
626 return Err(ParseError("multiple repeat".into()));
627 }
628 let last = ops.pop().expect("checked non-empty");
629 let body = match last {
630 Op::SubPattern {
631 group: None,
632 add,
633 del,
634 body,
635 } if add == Flags::default() && del == Flags::default() => body,
636 other => vec![other],
637 };
638 let mut kind = RepeatKind::Greedy;
641 if self.match_char('?') {
642 kind = RepeatKind::Lazy;
643 } else if self.peek() == Some('+') {
644 return Err(ParseError("multiple repeat".into()));
645 }
646 ops.push(Op::Repeat {
647 kind,
648 low,
649 high,
650 body,
651 });
652 Ok(())
653 }
654
655 fn parse_sequence(&mut self, depth: usize) -> Result<Vec<Op>, ParseError> {
656 if depth > 200 {
657 return Err(ParseError("pattern nesting too deep".into()));
658 }
659 let mut branches: Vec<Vec<Op>> = Vec::new();
661 loop {
662 let branch = self.parse_branch(depth)?;
663 branches.push(branch);
664 if self.peek() == Some('|') {
665 self.pos += 1;
666 continue;
667 }
668 break;
669 }
670 let branches: Vec<Vec<Op>> = branches
671 .into_iter()
672 .map(unpack_transparent_groups)
673 .collect();
674 if branches.len() == 1 {
675 return Ok(branches.into_iter().next().expect("one branch"));
676 }
677 Ok(vec![Op::Branch(branches)])
678 }
679
680 fn parse_branch(&mut self, depth: usize) -> Result<Vec<Op>, ParseError> {
681 let mut ops: Vec<Op> = Vec::new();
682 while let Some(this) = self.peek() {
683 if this == '|' || this == ')' {
684 break;
685 }
686 self.pos += 1;
687 match this {
691 '\\' => {
692 ops.push(self.escape()?);
693 }
694 '[' => {
695 ops.push(self.parse_class()?);
696 }
697 '*' | '+' | '?' | '{' => {
698 self.parse_repeat(&mut ops, this)?;
699 }
700 '.' => ops.push(Op::Any),
701 '^' => ops.push(Op::At(At::Beginning)),
702 '$' => ops.push(Op::At(At::End)),
703 '(' => {
704 if self.peek() == Some('?') {
705 self.pos += 1;
706 let Some(char) = self.get() else {
707 return Err(ParseError("unexpected end of pattern".into()));
708 };
709 match char {
710 'P' => {
711 if self.match_char('<') {
712 let name = self.get_until('>', "group name")?;
713 self.check_group_name(&name)?;
714 #[cfg(not(coverage))] let gid = self.opengroup(Some(name))?;
718 #[cfg(coverage)]
719 let gid =
720 self.opengroup(Some(name)).expect("opengroup cannot fail");
721 let body = self.parse_group_body(depth)?;
722 self.closegroup(gid);
723 ops.push(Op::SubPattern {
724 group: Some(gid),
725 add: Flags::default(),
726 del: Flags::default(),
727 body,
728 });
729 } else if self.match_char('=') {
730 let name = self.get_until(')', "group name")?;
731 self.check_group_name(&name)?;
732 let Some(gid) = self.names.get(&name).copied() else {
733 return Err(ParseError(format!(
734 "unknown group name {name:?}"
735 )));
736 };
737 if !self.checkgroup(gid) {
738 return Err(ParseError(
739 "cannot refer to an open group".into(),
740 ));
741 }
742 ops.push(Op::GroupRef(gid));
743 } else {
744 let char = self.get().ok_or_else(|| {
745 ParseError("unexpected end of pattern".into())
746 })?;
747 return Err(ParseError(format!("unknown extension ?P{char}")));
748 }
749 }
750 ':' => {
751 let body = self.parse_group_body(depth)?;
752 ops.push(Op::SubPattern {
753 group: None,
754 add: Flags::default(),
755 del: Flags::default(),
756 body,
757 });
758 }
759 '#' => loop {
760 match self.get() {
761 None => {
762 return Err(ParseError(
763 "missing ), unterminated comment".into(),
764 ));
765 }
766 Some(')') => break,
767 Some(_) => {}
768 }
769 },
770 '=' => {
771 let body = self.parse_group_body(depth)?;
772 ops.push(Op::Assert {
773 behind: false,
774 negated: false,
775 body,
776 });
777 }
778 '!' => {
779 let body = self.parse_group_body(depth)?;
780 if body.is_empty() {
781 ops.push(Op::Failure);
782 } else {
783 ops.push(Op::Assert {
784 behind: false,
785 negated: true,
786 body,
787 });
788 }
789 }
790 '<' => {
791 let Some(marker) = self.get() else {
792 return Err(ParseError("unexpected end of pattern".into()));
793 };
794 if marker != '=' && marker != '!' {
795 return Err(ParseError(format!(
796 "unknown extension ?<{marker}"
797 )));
798 }
799 let body = self.parse_group_body(depth)?;
800 if marker == '=' {
801 ops.push(Op::Assert {
802 behind: true,
803 negated: false,
804 body,
805 });
806 } else if body.is_empty() {
807 ops.push(Op::Failure);
808 } else {
809 ops.push(Op::Assert {
810 behind: true,
811 negated: true,
812 body,
813 });
814 }
815 }
816 '(' => {
817 let condname = self.get_until(')', "group name")?;
818 let group = if !condname.is_empty()
819 && condname.chars().all(|c| c.is_ascii_digit())
820 {
821 let value: u32 = condname.parse().map_err(|_| {
822 ParseError(format!("invalid group reference {condname}"))
823 })?;
824 if value == 0 {
825 return Err(ParseError("bad group number".into()));
826 }
827 value
828 } else {
829 self.check_group_name(&condname)?;
830 let Some(gid) = self.names.get(&condname).copied() else {
831 return Err(ParseError(format!(
832 "unknown group name {condname:?}"
833 )));
834 };
835 gid
836 };
837 let yes = self.parse_branch(depth + 1)?;
841 let no = if self.match_char('|') {
842 let no = self.parse_branch(depth + 1)?;
843 if self.peek() == Some('|') {
844 return Err(ParseError(
845 "conditional backref with more than two branches"
846 .into(),
847 ));
848 }
849 Some(no)
850 } else {
851 None
852 };
853 if !self.match_char(')') {
854 return Err(ParseError(
855 "missing ), unterminated subpattern".into(),
856 ));
857 }
858 ops.push(Op::GroupRefExists { group, yes, no });
859 }
860 '>' => {
864 return Err(ParseError("unknown extension ?>".into()));
865 }
866 'i' | 'm' | 's' | 'a' | 'x' | 'u' | 'L' | '-' => {
867 self.pos -= 1;
870 let Some((add, del)) = self.parse_flags()? else {
871 if depth != 0 || !ops.is_empty() {
874 return Err(ParseError(
875 "global flags not at the start of the expression"
876 .into(),
877 ));
878 }
879 continue;
880 };
881 let body = self.parse_group_body(depth)?;
882 ops.push(Op::SubPattern {
883 group: None,
884 add,
885 del,
886 body,
887 });
888 }
889 other => {
890 return Err(ParseError(format!("unknown extension ?{other}")));
891 }
892 }
893 } else {
894 #[cfg(not(coverage))] let gid = self.opengroup(None)?;
897 #[cfg(coverage)]
898 let gid = self.opengroup(None).expect("opengroup cannot fail");
899 let body = self.parse_group_body(depth)?;
900 self.closegroup(gid);
901 ops.push(Op::SubPattern {
902 group: Some(gid),
903 add: Flags::default(),
904 del: Flags::default(),
905 body,
906 });
907 }
908 }
909 other => ops.push(Op::Literal(u32::from(other))),
910 }
911 }
912 Ok(ops)
913 }
914
915 fn parse_group_body(&mut self, depth: usize) -> Result<Vec<Op>, ParseError> {
916 let body = self.parse_sequence(depth + 1)?;
917 if !self.match_char(')') {
918 return Err(ParseError("missing ), unterminated subpattern".into()));
919 }
920 Ok(body)
921 }
922}
923
924fn parse_repeat_count(digits: &str) -> Result<u32, ParseError> {
925 let value: u64 = digits
926 .parse()
927 .map_err(|_| ParseError("the repetition number is too large".into()))?;
928 if value >= MAXREPEAT {
929 return Err(ParseError("the repetition number is too large".into()));
930 }
931 #[cfg(not(coverage))] let count = u32::try_from(value)
934 .map_err(|_| ParseError("the repetition number is too large".into()))?;
935 #[cfg(coverage)]
936 let count = u32::try_from(value).expect("the guard above bounds value to u32");
937 Ok(count)
938}
939
940fn high_from(lo: &str, hi: Option<&str>) -> Result<Option<u32>, ParseError> {
943 match hi {
944 None if lo.is_empty() => Ok(None),
945 None => parse_repeat_count(lo).map(Some),
946 Some("") => Ok(None),
947 Some(digits) => parse_repeat_count(digits).map(Some),
948 }
949}
950
951fn unpack_transparent_groups(ops: Vec<Op>) -> Vec<Op> {
954 let mut unpacked = Vec::with_capacity(ops.len());
955 for op in ops {
956 match op {
957 Op::SubPattern {
958 group: None,
959 add,
960 del,
961 body,
962 } if add == Flags::default() && del == Flags::default() => {
963 unpacked.extend(body);
964 }
965 other => unpacked.push(other),
966 }
967 }
968 unpacked
969}
970
971pub fn parse(pattern: &str, flags: Flags) -> Result<(Vec<Op>, Flags), ParseError> {
979 let mut parser = Parser::new(pattern, flags);
980 let ops = parser.parse_sequence(0)?;
981 if parser.peek() == Some(')') {
982 return Err(ParseError("unbalanced parenthesis".into()));
983 }
984 Ok((ops, parser.flags))
985}
986
987#[cfg(test)]
988mod tests {
989 use super::*;
990
991 fn parse_ok(pattern: &str, flags: Flags) -> (Vec<Op>, Flags) {
992 parse(pattern, flags).expect("pattern must parse")
993 }
994
995 fn subpattern_parts(op: Op) -> (Option<u32>, Flags, Flags, Vec<Op>) {
997 match op {
998 Op::SubPattern {
999 group,
1000 add,
1001 del,
1002 body,
1003 } => (group, add, del, body),
1004 other => panic!("expected a subpattern, got {other:?}"),
1005 }
1006 }
1007
1008 #[test]
1009 fn literals_and_anchors() {
1010 let (ops, _) = parse_ok(r"a^$", Flags::default());
1011 assert_eq!(
1012 ops,
1013 vec![
1014 Op::Literal(u32::from('a')),
1015 Op::At(At::Beginning),
1016 Op::At(At::End),
1017 ]
1018 );
1019 }
1020
1021 #[test]
1022 fn any_node_and_dotall_inline_flag() {
1023 let (plain, _) = parse_ok(".", Flags::default());
1024 assert_eq!(plain, vec![Op::Any]);
1025 let (dotted, flags) = parse_ok("(?s).", Flags::default());
1026 assert_eq!(dotted, vec![Op::Any]);
1027 assert!(flags.dotall);
1028 }
1029
1030 #[test]
1031 fn char_class_with_range_negation_and_escapes() {
1032 let (ops, _) = parse_ok(r"[a-c\d]", Flags::default());
1033 assert_eq!(
1034 ops,
1035 vec![Op::In(vec![
1036 ClassItem::Range(u32::from('a'), u32::from('c')),
1037 ClassItem::Category(Category::Digit),
1038 ])]
1039 );
1040 }
1041
1042 #[test]
1043 fn negated_single_literal_becomes_not_literal() {
1044 let (ops, _) = parse_ok(r"[^a]", Flags::default());
1045 assert_eq!(ops, vec![Op::NotLiteral(u32::from('a'))]);
1046 }
1047
1048 #[test]
1049 fn negated_multi_item_class_inserts_negate() {
1050 let (ops, _) = parse_ok(r"[^ab]", Flags::default());
1051 assert_eq!(
1052 ops,
1053 vec![Op::In(vec![
1054 ClassItem::Negate,
1055 ClassItem::Literal(u32::from('a')),
1056 ClassItem::Literal(u32::from('b')),
1057 ])]
1058 );
1059 }
1060
1061 #[test]
1062 fn leading_bracket_and_dash_are_literal_inside_a_class() {
1063 let (ops, _) = parse_ok(r"[]-]", Flags::default());
1064 assert_eq!(
1065 ops,
1066 vec![Op::In(vec![
1067 ClassItem::Literal(u32::from(']')),
1068 ClassItem::Literal(u32::from('-')),
1069 ])]
1070 );
1071 }
1072
1073 #[test]
1074 fn backslash_b_inside_a_class_is_backspace() {
1075 let (ops, _) = parse_ok(r"[\b]", Flags::default());
1077 assert_eq!(ops, vec![Op::Literal(0x08)]);
1078 }
1079
1080 #[test]
1081 fn escaped_backslash_inside_a_class_is_a_literal_backslash() {
1082 let (ops, _) = parse_ok(r"[\\]", Flags::default());
1084 assert_eq!(ops, vec![Op::Literal(0x5C)]);
1085 }
1086
1087 #[test]
1088 fn class_dedupes_preserving_order() {
1089 let (ops, _) = parse_ok(r"[aab]", Flags::default());
1090 assert_eq!(
1091 ops,
1092 vec![Op::In(vec![
1093 ClassItem::Literal(u32::from('a')),
1094 ClassItem::Literal(u32::from('b')),
1095 ])]
1096 );
1097 }
1098
1099 #[test]
1100 fn alternation_builds_a_branch_node() {
1101 let (ops, _) = parse_ok("a|bc", Flags::default());
1102 assert_eq!(
1103 ops,
1104 vec![Op::Branch(vec![
1105 vec![Op::Literal(u32::from('a'))],
1106 vec![Op::Literal(u32::from('b')), Op::Literal(u32::from('c')),],
1107 ])]
1108 );
1109 }
1110
1111 #[test]
1112 fn empty_alternation_branch_is_allowed() {
1113 let (ops, _) = parse_ok("(a|)", Flags::default());
1114 let (_, _, _, body) = subpattern_parts(ops.into_iter().next().expect("an op"));
1115 assert_eq!(
1116 body,
1117 vec![Op::Branch(vec![vec![Op::Literal(u32::from('a'))], vec![],])]
1118 );
1119 }
1120
1121 #[test]
1122 fn non_capturing_groups_are_unpacked() {
1123 let (ops, _) = parse_ok("(?:a)(?:b)", Flags::default());
1124 assert_eq!(
1125 ops,
1126 vec![Op::Literal(u32::from('a')), Op::Literal(u32::from('b')),]
1127 );
1128 }
1129
1130 #[test]
1131 fn nested_non_capturing_wrappers_unpack_completely() {
1132 let (ops, _) = parse_ok("(?:(?:ab))", Flags::default());
1133 assert_eq!(
1134 ops,
1135 vec![Op::Literal(u32::from('a')), Op::Literal(u32::from('b')),]
1136 );
1137 }
1138
1139 #[test]
1140 fn capturing_groups_are_numbered_in_order() {
1141 let (ops, _) = parse_ok("(?:a)(b)(c)", Flags::default());
1143 let groups: Vec<Option<u32>> = ops
1144 .iter()
1145 .filter_map(|op| match op {
1146 Op::SubPattern { group, .. } => Some(*group),
1147 _ => None,
1148 })
1149 .collect();
1150 assert_eq!(groups, vec![Some(1), Some(2)]);
1151 }
1152
1153 #[test]
1154 fn named_groups_register_a_backreference_target() {
1155 let (ops, _) = parse_ok(r"(?P<word>x)(?P=word)", Flags::default());
1156 assert_eq!(ops[1], Op::GroupRef(1));
1157 }
1158
1159 #[test]
1160 fn comment_groups_disappear() {
1161 let (ops, _) = parse_ok("(?#note)a", Flags::default());
1162 assert_eq!(ops, vec![Op::Literal(u32::from('a'))]);
1163 }
1164
1165 #[test]
1166 fn lookahead_and_lookbehind_become_asserts() {
1167 let (ops, _) = parse_ok(r"(?=a)(?!b)(?<=c)(?<!d)", Flags::default());
1168 let expected = [
1169 (false, false, 'a'),
1170 (false, true, 'b'),
1171 (true, false, 'c'),
1172 (true, true, 'd'),
1173 ];
1174 for (index, (behind, negated, literal)) in expected.into_iter().enumerate() {
1175 assert_eq!(
1176 ops[index],
1177 Op::Assert {
1178 behind,
1179 negated,
1180 body: vec![Op::Literal(u32::from(literal))],
1181 }
1182 );
1183 }
1184 }
1185
1186 #[test]
1187 fn empty_negative_assertions_become_failure() {
1188 let (ops, _) = parse_ok("(?!)", Flags::default());
1189 assert_eq!(ops, vec![Op::Failure]);
1190 let (ops, _) = parse_ok("(?<!)", Flags::default());
1191 assert_eq!(ops, vec![Op::Failure]);
1192 }
1193
1194 #[test]
1195 fn conditional_groups_parse_both_branches() {
1196 let (ops, _) = parse_ok("(a)(?(1)b|c)", Flags::default());
1197 assert_eq!(
1198 ops[1],
1199 Op::GroupRefExists {
1200 group: 1,
1201 yes: vec![Op::Literal(u32::from('b'))],
1202 no: Some(vec![Op::Literal(u32::from('c'))]),
1203 }
1204 );
1205 }
1206
1207 #[test]
1208 fn conditional_group_by_name_resolves_the_number() {
1209 let (ops, _) = parse_ok("(?P<x>a)(?(x)b)", Flags::default());
1210 assert_eq!(
1211 ops[1],
1212 Op::GroupRefExists {
1213 group: 1,
1214 yes: vec![Op::Literal(u32::from('b'))],
1215 no: None,
1216 }
1217 );
1218 }
1219
1220 #[test]
1221 fn repeats_apply_to_the_previous_item() {
1222 let (ops, _) = parse_ok("a*b+c?d{2}e{3,}f{1,4}g", Flags::default());
1224 let kinds: Vec<(u32, Option<u32>)> = ops
1225 .iter()
1226 .filter_map(|op| match op {
1227 Op::Repeat { low, high, .. } => Some((*low, *high)),
1228 _ => None,
1229 })
1230 .collect();
1231 assert_eq!(
1232 kinds,
1233 vec![
1234 (0, None),
1235 (1, None),
1236 (0, Some(1)),
1237 (2, Some(2)),
1238 (3, None),
1239 (1, Some(4))
1240 ]
1241 );
1242 }
1243
1244 #[test]
1245 fn open_brace_counts_are_unbounded_and_lazy_marker_is_consumed() {
1246 let (ops, _) = parse_ok("a{2,}?", Flags::default());
1247 assert_eq!(
1248 ops,
1249 vec![Op::Repeat {
1250 kind: RepeatKind::Lazy,
1251 low: 2,
1252 high: None,
1253 body: vec![Op::Literal(u32::from('a'))],
1254 }]
1255 );
1256 }
1257
1258 #[test]
1259 fn empty_brace_bounds_mean_zero_to_unbounded() {
1260 let (ops, _) = parse_ok("a{,}", Flags::default());
1261 assert_eq!(
1262 ops[0],
1263 Op::Repeat {
1264 kind: RepeatKind::Greedy,
1265 low: 0,
1266 high: None,
1267 body: vec![Op::Literal(u32::from('a'))],
1268 }
1269 );
1270 }
1271
1272 #[test]
1273 fn invalid_brace_becomes_a_literal_and_reparse_follows() {
1274 let (ops, _) = parse_ok("a{x}", Flags::default());
1275 assert_eq!(
1276 ops,
1277 vec![
1278 Op::Literal(u32::from('a')),
1279 Op::Literal(u32::from('{')),
1280 Op::Literal(u32::from('x')),
1281 Op::Literal(u32::from('}')),
1282 ]
1283 );
1284 }
1285
1286 #[test]
1287 fn repeats_unwrap_a_transparent_group_body() {
1288 let (ops, _) = parse_ok("(?:ab)+", Flags::default());
1289 assert_eq!(
1290 ops,
1291 vec![Op::Repeat {
1292 kind: RepeatKind::Greedy,
1293 low: 1,
1294 high: None,
1295 body: vec![Op::Literal(u32::from('a')), Op::Literal(u32::from('b')),],
1296 }]
1297 );
1298 }
1299
1300 #[test]
1301 fn flagged_groups_keep_their_flag_deltas() {
1302 let (ops, _) = parse_ok("(?i:x)", Flags::default());
1303 let (_, add, del, _) = subpattern_parts(ops.into_iter().next().expect("an op"));
1304 assert!(add.ignorecase);
1305 assert_eq!(del, Flags::default());
1306 }
1307
1308 #[test]
1309 fn negated_scoped_flags_are_recorded() {
1310 let (ops, _) = parse_ok("(?-i:x)", Flags::ignorecase_multiline());
1311 let (_, _, del, _) = subpattern_parts(ops.into_iter().next().expect("an op"));
1312 assert!(del.ignorecase);
1313 }
1314
1315 #[test]
1316 fn global_inline_flags_update_the_final_flags() {
1317 let (_, flags) = parse_ok("(?i)a", Flags::default());
1318 assert!(flags.ignorecase);
1319 let (_, flags) = parse_ok("(?is)a", Flags::default());
1320 assert!(flags.ignorecase && flags.dotall);
1321 }
1322
1323 #[test]
1324 fn global_flags_must_precede_any_pattern_item() {
1325 assert_eq!(
1326 parse("a(?i)", Flags::default()).err(),
1327 Some(ParseError(
1328 "global flags not at the start of the expression".into()
1329 ))
1330 );
1331 assert_eq!(
1332 parse("(a)(?i)", Flags::default()).err(),
1333 Some(ParseError(
1334 "global flags not at the start of the expression".into()
1335 ))
1336 );
1337 }
1338
1339 #[test]
1340 fn consecutive_global_flag_groups_are_allowed_at_the_start() {
1341 let (_, flags) = parse_ok("(?i)(?m)a", Flags::default());
1342 assert!(flags.ignorecase && flags.multiline);
1343 }
1344
1345 #[test]
1346 fn negated_global_flag_group_is_an_error() {
1347 assert_eq!(
1348 parse("(?-i)", Flags::default()).err(),
1349 Some(ParseError("missing :".into()))
1350 );
1351 }
1352
1353 #[test]
1354 fn turned_on_and_off_flag_is_an_error() {
1355 assert_eq!(
1356 parse("(?i-i:x)", Flags::default()).err(),
1357 Some(ParseError(
1358 "bad inline flags: flag turned on and off".into()
1359 ))
1360 );
1361 }
1362
1363 #[test]
1364 fn locale_flag_is_rejected_for_str_patterns() {
1365 assert_eq!(
1366 parse("(?L:x)", Flags::default()).err(),
1367 Some(ParseError(
1368 "bad inline flags: cannot use 'L' flag with a str pattern".into()
1369 ))
1370 );
1371 }
1372
1373 #[test]
1374 fn unknown_flag_is_an_error() {
1375 assert_eq!(
1377 parse("(?q:x)", Flags::default()).err(),
1378 Some(ParseError("unknown extension ?q".into()))
1379 );
1380 }
1381
1382 #[test]
1383 fn escapes_and_octal_forms() {
1384 let (ops, _) = parse_ok(r"\a\f\n\r\t\v\\\x41\101\0\A", Flags::default());
1386 let literals: Vec<u32> = ops
1387 .iter()
1388 .filter_map(|op| match op {
1389 Op::Literal(cp) => Some(*cp),
1390 _ => None,
1391 })
1392 .collect();
1393 assert_eq!(
1394 literals,
1395 vec![0x07, 0x0C, 0x0A, 0x0D, 0x09, 0x0B, 0x5C, 0x41, 0o101, 0x0]
1396 );
1397 }
1398
1399 #[test]
1400 fn unicode_escapes() {
1401 let (ops, _) = parse_ok(r"\u00e9\U0001F600", Flags::default());
1402 assert_eq!(ops, vec![Op::Literal(0xE9), Op::Literal(0x1F600)]);
1403 }
1404
1405 #[test]
1406 fn zero_width_escapes() {
1407 let (ops, _) = parse_ok(r"\A\Z\b\B", Flags::default());
1408 assert_eq!(
1409 ops,
1410 vec![
1411 Op::At(At::BeginningString),
1412 Op::At(At::EndString),
1413 Op::At(At::Boundary),
1414 Op::At(At::NonBoundary),
1415 ]
1416 );
1417 }
1418
1419 #[test]
1420 fn escaped_punctuation_is_literal() {
1421 let (ops, _) = parse_ok(r"\.\*\+\?", Flags::default());
1422 assert_eq!(
1423 ops,
1424 vec![
1425 Op::Literal(u32::from('.')),
1426 Op::Literal(u32::from('*')),
1427 Op::Literal(u32::from('+')),
1428 Op::Literal(u32::from('?')),
1429 ]
1430 );
1431 }
1432
1433 #[test]
1434 fn backreference_to_a_closed_group_parses() {
1435 let (ops, _) = parse_ok(r"(a)\1", Flags::default());
1436 assert_eq!(ops[1], Op::GroupRef(1));
1437 let (ops, _) = parse_ok(r"(a)\1\1", Flags::default());
1438 assert_eq!(ops[1], Op::GroupRef(1));
1439 assert_eq!(ops[2], Op::GroupRef(1));
1440 }
1441
1442 #[test]
1443 fn errors_mirror_the_reference_messages() {
1444 let cases = [
1445 (
1446 "(unclosed",
1447 ParseError("missing ), unterminated subpattern".into()),
1448 ),
1449 (")", ParseError("unbalanced parenthesis".into())),
1450 ("[invalid", ParseError("unterminated character set".into())),
1451 ("*leading", ParseError("nothing to repeat".into())),
1452 ("^*", ParseError("nothing to repeat".into())),
1453 ("a**", ParseError("multiple repeat".into())),
1454 (
1455 "a{3,2}",
1456 ParseError("min repeat greater than max repeat".into()),
1457 ),
1458 (
1459 "a{99999999999999999999}",
1460 ParseError("the repetition number is too large".into()),
1461 ),
1462 (
1463 "a{4294967287}",
1464 ParseError("the repetition number is too large".into()),
1465 ),
1466 (r"[z-a]", ParseError("bad character range z-a".into())),
1467 (r"[\d-a]", ParseError("bad character range \\d-a".into())),
1468 (r"\q", ParseError("bad escape \\q".into())),
1469 (r"\x2", ParseError("incomplete escape \\x2".into())),
1470 (r"\u12", ParseError("incomplete escape \\u12".into())),
1471 (
1472 r"\N{DASH}",
1473 ParseError(
1474 "bad escape \\N: named escapes are not supported by the safety chain".into(),
1475 ),
1476 ),
1477 (r"\1", ParseError("invalid group reference 1".into())),
1478 (r"(a)\2", ParseError("invalid group reference 2".into())),
1479 (r"(a\1)", ParseError("cannot refer to an open group".into())),
1480 (
1481 "(?P<1bad>x)",
1482 ParseError("bad character in group name \"1bad\"".into()),
1483 ),
1484 ("(?P=n)", ParseError("unknown group name \"n\"".into())),
1485 ("(?(0)a)", ParseError("bad group number".into())),
1486 ("(?(n)a)", ParseError("unknown group name \"n\"".into())),
1487 (
1488 "(?P<x",
1489 ParseError("missing >, unterminated group name".into()),
1490 ),
1491 ("(?P", ParseError("unexpected end of pattern".into())),
1492 ("(?P!x)", ParseError("unknown extension ?P!".into())),
1493 ("(?<x>a)", ParseError("unknown extension ?<x".into())),
1494 ("(?", ParseError("unexpected end of pattern".into())),
1495 (
1496 "(?#open",
1497 ParseError("missing ), unterminated comment".into()),
1498 ),
1499 (
1500 "(?(1)a|b|c)",
1501 ParseError("conditional backref with more than two branches".into()),
1502 ),
1503 (
1504 "(?P<>x)",
1505 ParseError("bad character in group name \"\"".into()),
1506 ),
1507 (
1508 "(?P<n>(?P=n)",
1509 ParseError("cannot refer to an open group".into()),
1510 ),
1511 ("(?<", ParseError("unexpected end of pattern".into())),
1512 (
1513 "(?(99999999999)a)",
1514 ParseError("invalid group reference 99999999999".into()),
1515 ),
1516 (
1517 "(?(1)a",
1518 ParseError("missing ), unterminated subpattern".into()),
1519 ),
1520 ];
1521 for (pattern, expected) in cases {
1522 assert_eq!(
1523 parse(pattern, Flags::default()).err(),
1524 Some(expected),
1525 "pattern {pattern:?}"
1526 );
1527 }
1528 }
1529
1530 #[test]
1531 fn group_name_validation_rejects_embedded_and_leading_bad_characters() {
1532 assert_eq!(
1534 parse(r"(?P<a-b>x)", Flags::default()).err(),
1535 Some(ParseError("bad character in group name \"a-b\"".into()))
1536 );
1537 assert_eq!(
1539 parse(r"(a)(?P=a-b)", Flags::default()).err(),
1540 Some(ParseError("bad character in group name \"a-b\"".into()))
1541 );
1542 assert_eq!(
1544 parse(r"(a)(?(a-b)x)", Flags::default()).err(),
1545 Some(ParseError("bad character in group name \"a-b\"".into()))
1546 );
1547 }
1548
1549 #[test]
1550 fn every_flag_turned_on_and_off_is_an_error() {
1551 for flags in ["i", "m", "s", "a"] {
1552 let pattern = format!("(?{flags}-{flags}:x)");
1553 assert_eq!(
1554 parse(&pattern, Flags::default()).err(),
1555 Some(ParseError(
1556 "bad inline flags: flag turned on and off".into()
1557 )),
1558 "{pattern}"
1559 );
1560 }
1561 }
1562
1563 #[test]
1564 fn class_escape_error_arms_match_the_reference() {
1565 assert_eq!(
1567 parse(r"[a\", Flags::default()).err(),
1568 Some(ParseError("bad escape".into()))
1569 );
1570 assert_eq!(
1572 parse(r"[\U00110000]", Flags::default()).err(),
1573 Some(ParseError(
1574 "illegal Unicode character in \\U00110000".into()
1575 ))
1576 );
1577 assert_eq!(
1579 parse(r"[\8]", Flags::default()).err(),
1580 Some(ParseError("bad escape \\8".into()))
1581 );
1582 assert_eq!(
1583 parse(r"[\q]", Flags::default()).err(),
1584 Some(ParseError("bad escape \\q".into()))
1585 );
1586 }
1587
1588 #[test]
1589 fn outside_class_escape_error_arms_match_the_reference() {
1590 assert_eq!(
1592 parse(r"\", Flags::default()).err(),
1593 Some(ParseError("bad escape".into()))
1594 );
1595 assert_eq!(
1598 parse(r"\ud800", Flags::default()).err(),
1599 Some(ParseError("illegal Unicode character in \\ud800".into()))
1600 );
1601 assert_eq!(
1602 parse(r"\U00110000", Flags::default()).err(),
1603 Some(ParseError(
1604 "illegal Unicode character in \\U00110000".into()
1605 ))
1606 );
1607 }
1608
1609 #[test]
1610 fn class_range_error_arms_match_the_reference() {
1611 assert_eq!(
1613 parse(r"[a-", Flags::default()).err(),
1614 Some(ParseError("unterminated character set".into()))
1615 );
1616 let (ops, _) = parse_ok(r"[a-\x7f]", Flags::default());
1618 assert_eq!(ops[0], Op::In(vec![ClassItem::Range(0x61, 0x7f)]));
1619 assert_eq!(
1621 parse(r"[a-\q]", Flags::default()).err(),
1622 Some(ParseError("bad escape \\q".into()))
1623 );
1624 }
1625
1626 #[test]
1627 fn repeat_bound_errors_match_the_reference() {
1628 assert_eq!(
1630 parse("a{4294967287}", Flags::default()).err(),
1631 Some(ParseError("the repetition number is too large".into()))
1632 );
1633 assert_eq!(
1635 parse("a{99999999999999999999}", Flags::default()).err(),
1636 Some(ParseError("the repetition number is too large".into()))
1637 );
1638 assert_eq!(
1641 parse("a{4294967287,}", Flags::default()).err(),
1642 Some(ParseError("the repetition number is too large".into()))
1643 );
1644 }
1645
1646 #[test]
1647 fn high_from_maps_every_bound_shape() {
1648 assert_eq!(high_from("", None), Ok(None), "no comma and no low");
1649 assert_eq!(high_from("3", None), Ok(Some(3)), "no comma reuses the low");
1650 assert_eq!(
1651 high_from("", Some("")),
1652 Ok(None),
1653 "an open comma is unbounded"
1654 );
1655 assert_eq!(high_from("2", Some("5")), Ok(Some(5)));
1656 assert_eq!(
1657 high_from("9", Some("99999999999999999999")).err(),
1658 Some(ParseError("the repetition number is too large".into()))
1659 );
1660 }
1661
1662 #[test]
1663 fn named_reference_and_conditional_error_arms_match_the_reference() {
1664 assert_eq!(
1666 parse(r"(a)(?P=x", Flags::default()).err(),
1667 Some(ParseError("missing ), unterminated group name".into()))
1668 );
1669 assert_eq!(
1671 parse(r"(?=[", Flags::default()).err(),
1672 Some(ParseError("unterminated character set".into()))
1673 );
1674 assert_eq!(
1676 parse(r"(?!(", Flags::default()).err(),
1677 Some(ParseError("missing ), unterminated subpattern".into()))
1678 );
1679 assert_eq!(
1681 parse(r"(?<=(", Flags::default()).err(),
1682 Some(ParseError("missing ), unterminated subpattern".into()))
1683 );
1684 }
1685
1686 #[test]
1687 fn conditional_error_arms_match_the_reference() {
1688 assert_eq!(
1690 parse(r"(a)(?(x", Flags::default()).err(),
1691 Some(ParseError("missing ), unterminated group name".into()))
1692 );
1693 assert_eq!(
1695 parse(r"(a)(?(1)[", Flags::default()).err(),
1696 Some(ParseError("unterminated character set".into()))
1697 );
1698 assert_eq!(
1700 parse(r"(a)(?(1)b|(", Flags::default()).err(),
1701 Some(ParseError("missing ), unterminated subpattern".into()))
1702 );
1703 }
1704
1705 #[test]
1706 fn a_plain_group_body_that_cannot_parse_is_an_error() {
1707 assert_eq!(
1708 parse("([", Flags::default()).err(),
1709 Some(ParseError("unterminated character set".into()))
1710 );
1711 assert_eq!(
1713 parse("(?i:[", Flags::default()).err(),
1714 Some(ParseError("unterminated character set".into()))
1715 );
1716 }
1717
1718 #[test]
1719 fn possessive_quantifiers_are_rejected_at_the_reference_floor() {
1720 assert_eq!(
1721 parse("a*+", Flags::default()).err(),
1722 Some(ParseError("multiple repeat".into()))
1723 );
1724 }
1725
1726 #[test]
1727 fn atomic_groups_are_rejected_at_the_reference_floor() {
1728 assert_eq!(
1729 parse("(?>a)+", Flags::default()).err(),
1730 Some(ParseError("unknown extension ?>".into()))
1731 );
1732 }
1733
1734 #[test]
1735 fn an_open_group_reference_is_an_error() {
1736 assert_eq!(
1737 parse(r"(a\1)", Flags::default()).err(),
1738 Some(ParseError("cannot refer to an open group".into()))
1739 );
1740 }
1741
1742 #[test]
1743 fn two_digit_group_references_prefer_octal_when_it_fits() {
1744 let (ops, _) = parse_ok(r"\101", Flags::default());
1746 assert_eq!(ops, vec![Op::Literal(0o101)]);
1747 }
1748
1749 #[test]
1750 fn two_digit_reference_to_a_defined_group_parses() {
1751 assert_eq!(
1754 parse(r"()()\12", Flags::default()).err(),
1755 Some(ParseError("invalid group reference 12".into()))
1756 );
1757 let groups = "()".repeat(12);
1758 let (ops, _) = parse_ok(&format!("{groups}\\12"), Flags::default());
1759 assert_eq!(ops[12], Op::GroupRef(12));
1760 }
1761
1762 #[test]
1763 fn deep_nesting_is_an_error() {
1764 let pattern = format!("{}a{}", "(".repeat(300), ")".repeat(300));
1765 assert!(parse(&pattern, Flags::default()).is_err());
1766 }
1767
1768 #[test]
1769 fn scoped_flag_group_body_is_parsed() {
1770 let (ops, _) = parse_ok("(?i:a|b)", Flags::default());
1771 let (_, _, _, body) = subpattern_parts(ops.into_iter().next().expect("an op"));
1772 assert_eq!(body.len(), 1);
1773 }
1774
1775 #[test]
1776 fn conditional_without_else_uses_none() {
1777 let (ops, _) = parse_ok("(a)(?(1)b)", Flags::default());
1778 assert_eq!(
1779 ops[1],
1780 Op::GroupRefExists {
1781 group: 1,
1782 yes: vec![Op::Literal(u32::from('b'))],
1783 no: None,
1784 }
1785 );
1786 }
1787
1788 #[test]
1789 fn is_pairing_covers_the_reference_pairing_ops() {
1790 assert!(Op::Literal(97).is_pairing());
1791 assert!(Op::NotLiteral(97).is_pairing());
1792 assert!(Op::Any.is_pairing());
1793 assert!(Op::Category(Category::Digit).is_pairing());
1794 assert!(Op::In(vec![]).is_pairing());
1795 assert!(!Op::At(At::Boundary).is_pairing());
1796 assert!(!Op::Failure.is_pairing());
1797 assert!(!Op::GroupRef(1).is_pairing());
1798 }
1799
1800 #[test]
1801 fn apply_delta_delete_arms_clear_flags() {
1802 let mut flags = Flags {
1806 ignorecase: true,
1807 multiline: true,
1808 dotall: true,
1809 ascii: true,
1810 };
1811 let del = Flags {
1812 ignorecase: true,
1813 multiline: true,
1814 dotall: true,
1815 ascii: true,
1816 };
1817 flags.apply_delta(&Flags::default(), &del);
1818 assert_eq!(flags, Flags::default());
1819 }
1820
1821 #[test]
1822 fn negated_category_escapes_inside_a_class() {
1823 let (ops, _) = parse_ok(r"[\D\S\W]", Flags::default());
1824 assert_eq!(
1825 ops,
1826 vec![Op::In(vec![
1827 ClassItem::Category(Category::NotDigit),
1828 ClassItem::Category(Category::NotSpace),
1829 ClassItem::Category(Category::NotWord),
1830 ])]
1831 );
1832 }
1833
1834 #[test]
1835 fn parse_error_display_shows_the_message() {
1836 let err = parse("(", Flags::default()).expect_err("pattern must fail");
1837 assert_eq!(err.to_string(), "missing ), unterminated subpattern");
1838 }
1839
1840 #[test]
1841 fn verbose_and_unicode_flags_parse_as_noops() {
1842 let (ops, flags) = parse_ok("(?ux)a", Flags::default());
1845 assert_eq!(ops, vec![Op::Literal(u32::from('a'))]);
1846 assert_eq!(flags, Flags::default());
1847 }
1848
1849 #[test]
1850 fn flag_parser_rejects_junk_after_valid_flags() {
1851 assert_eq!(
1852 parse("(?i", Flags::default()).err(),
1853 Some(ParseError("missing -, : or )".into()))
1854 );
1855 assert_eq!(
1856 parse("(?iz", Flags::default()).err(),
1857 Some(ParseError("unknown flag".into()))
1858 );
1859 assert_eq!(
1860 parse("(?i1", Flags::default()).err(),
1861 Some(ParseError("missing -, : or )".into()))
1862 );
1863 }
1864
1865 #[test]
1866 fn class_control_escapes_are_literals() {
1867 let (ops, _) = parse_ok(r"[\a\f\n\r\t\v]", Flags::default());
1868 assert_eq!(
1869 ops,
1870 vec![Op::In(vec![
1871 ClassItem::Literal(0x07),
1872 ClassItem::Literal(0x0C),
1873 ClassItem::Literal(0x0A),
1874 ClassItem::Literal(0x0D),
1875 ClassItem::Literal(0x09),
1876 ClassItem::Literal(0x0B),
1877 ])]
1878 );
1879 }
1880
1881 #[test]
1882 fn class_unicode_escape_edges() {
1883 let (ops, _) = parse_ok(r"[\u00e9\U0001F600]", Flags::default());
1884 assert_eq!(
1885 ops,
1886 vec![Op::In(vec![
1887 ClassItem::Literal(0xE9),
1888 ClassItem::Literal(0x1F600),
1889 ])]
1890 );
1891 assert_eq!(
1892 parse(r"[\x1]", Flags::default()).err(),
1893 Some(ParseError("incomplete escape \\x1".into()))
1894 );
1895 assert_eq!(
1896 parse(r"[\u12]", Flags::default()).err(),
1897 Some(ParseError("incomplete escape \\u12".into()))
1898 );
1899 assert_eq!(
1900 parse(r"[\U0001]", Flags::default()).err(),
1901 Some(ParseError("incomplete escape \\U0001".into()))
1902 );
1903 assert_eq!(
1904 parse(r"[\ud800]", Flags::default()).err(),
1905 Some(ParseError("illegal Unicode character in \\ud800".into()))
1906 );
1907 assert_eq!(
1908 parse(r"[\N]", Flags::default()).err(),
1909 Some(ParseError(
1910 "bad escape \\N: named escapes are not supported by the safety chain".into()
1911 ))
1912 );
1913 }
1914
1915 #[test]
1916 fn class_octal_escape_edges() {
1917 let (ops, _) = parse_ok(r"[\101\0]", Flags::default());
1918 assert_eq!(
1919 ops,
1920 vec![Op::In(vec![
1921 ClassItem::Literal(0o101),
1922 ClassItem::Literal(0),
1923 ])]
1924 );
1925 assert_eq!(
1926 parse(r"[\777]", Flags::default()).err(),
1927 Some(ParseError(
1928 "octal escape value \\777 outside of range 0-0o377".into()
1929 ))
1930 );
1931 }
1932
1933 #[test]
1934 fn outside_class_escape_edges() {
1935 assert_eq!(
1936 parse(r"\U0001", Flags::default()).err(),
1937 Some(ParseError("incomplete escape \\U0001".into()))
1938 );
1939 assert_eq!(
1940 parse(r"\777", Flags::default()).err(),
1941 Some(ParseError(
1942 "octal escape value \\777 outside of range 0-0o377".into()
1943 ))
1944 );
1945 }
1946
1947 #[test]
1948 fn empty_brace_pair_is_a_literal_brace() {
1949 let (ops, _) = parse_ok("a{}", Flags::default());
1950 assert_eq!(
1951 ops,
1952 vec![
1953 Op::Literal(u32::from('a')),
1954 Op::Literal(u32::from('{')),
1955 Op::Literal(u32::from('}')),
1956 ]
1957 );
1958 }
1959
1960 #[test]
1961 #[should_panic(expected = "expected a subpattern")]
1962 fn subpattern_parts_rejects_other_variants() {
1963 let _ = subpattern_parts(Op::Any);
1964 }
1965}