1mod backtrack;
96mod look_behind;
97mod translate;
98
99use {
100 self::{
101 backtrack::Program,
102 look_behind::{LookBehinds, Refusal, check_look_behinds},
103 translate::{JavaTranslation, translate_java},
104 },
105 fancy_regex::{CompileError, Error as FancyError, Expr, Regex},
106 std::{collections::BTreeMap, fmt::Display, ops::Range, sync::Arc},
107};
108
109#[derive(Debug, Clone)]
112pub struct JavaPattern(Arc<Compiled>);
113
114#[derive(Debug)]
117struct Compiled {
118 engine: Engine,
120 groups: usize,
122 names: BTreeMap<String, usize>,
124 input_limit: Option<usize>,
127 wide_input: WideInput,
130}
131
132#[derive(Debug)]
137enum WideInput {
138 Engine,
141 Program(Program),
143 Refused,
145}
146
147#[derive(Debug)]
149enum Engine {
150 Fancy {
152 search: Regex,
154 whole: Regex,
156 },
157 Backtrack(Program),
160}
161
162#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
165#[error("{0}")]
166pub struct PatternError(String);
167
168#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
173#[error("{0}")]
174pub struct MatchError(String);
175
176impl MatchError {
177 fn search(error: impl Display) -> Self { Self(error.to_string()) }
180}
181
182impl JavaPattern {
183 pub fn compile(pattern: &str) -> Result<Self, PatternError> {
191 let JavaTranslation {
192 pattern: translated,
193 backref_folds,
194 linebreak_atoms,
195 group_repeats,
196 look_behind_code_points,
197 } = translate_java(pattern)
198 .ok_or_else(|| PatternError(format!("{pattern:?} is not a java.util.regex pattern")))?;
199 let tree =
200 Expr::parse_tree(&translated).map_err(|error| PatternError(error.to_string()))?;
201 let groups = 1 + count_groups(&tree.expr);
202 if let Some(group) = backref_past(&tree.expr, groups) {
206 let error = FancyError::CompileError(Box::new(CompileError::InvalidBackref(group)));
207 return Err(PatternError(error.to_string()));
208 }
209 let refuse = |refusal: Refusal| PatternError(format!("{pattern:?} {}", refusal.reason()));
210 let LookBehinds {
213 input_limit,
214 too_large,
215 windows,
216 } = check_look_behinds(&tree.expr, &look_behind_code_points).map_err(refuse)?;
217 let fancy = if too_large {
218 Err(FancyFailure::Unbuilt(refuse(Refusal::TooLarge)))
219 } else {
220 compile_fancy(&translated)
221 };
222 let names = tree.named_groups.into_iter().collect();
223 let program = Program::new(
224 &translated,
225 groups,
226 &backref_folds,
227 &linebreak_atoms,
228 &group_repeats,
229 &windows,
230 );
231 let counts_units = windows.iter().any(|window| !window.code_points);
235 let (engine, wide_input) = match (fancy, program) {
236 (Ok(_), Some(program)) if program.differs_from_fancy() => {
237 (Engine::Backtrack(program), WideInput::Engine)
238 }
239 (Ok((search, whole)), program) => (Engine::Fancy { search, whole }, match program {
240 _ if !counts_units => WideInput::Engine,
241 Some(program) => WideInput::Program(program),
242 None => WideInput::Refused,
243 }),
244 (Err(FancyFailure::Unbuilt(_)), Some(program)) => {
245 (Engine::Backtrack(program), WideInput::Engine)
246 }
247 (Err(FancyFailure::Unbuilt(error) | FancyFailure::Refused(error)), _) => {
248 return Err(error);
249 }
250 };
251 Ok(Self(Arc::new(Compiled {
252 engine,
253 groups,
254 names,
255 input_limit,
256 wide_input,
257 })))
258 }
259
260 #[must_use]
262 pub fn group_count(&self) -> usize { self.0.groups - 1 }
263
264 pub fn matches(&self, input: &str) -> Result<bool, MatchError> {
278 check_input_length(self.0.input_limit, input)?;
279 if let Some(program) = self.wide_program(input)? {
280 return program.matches(input).map_err(MatchError::search);
281 }
282 match &self.0.engine {
283 Engine::Backtrack(program) => program.matches(input).map_err(MatchError::search),
284 Engine::Fancy { whole, .. } => whole.is_match(input).map_err(MatchError::search),
285 }
286 }
287
288 fn wide_program(&self, input: &str) -> Result<Option<&Program>, MatchError> {
292 if !input.bytes().any(|byte| byte >= 0xF0) {
294 return Ok(None);
295 }
296 match &self.0.wide_input {
297 WideInput::Engine => Ok(None),
298 WideInput::Program(program) => Ok(Some(program)),
299 WideInput::Refused => Err(MatchError(
300 "the input has a char past U+FFFF, which a look-behind of the pattern reads as \
301 two UTF-16 units, and the backtracking matcher cannot run the pattern"
302 .to_owned(),
303 )),
304 }
305 }
306
307 fn find(
313 &self,
314 input: &str,
315 from: usize,
316 last: usize,
317 ) -> Result<Option<Vec<Option<Range<usize>>>>, MatchError> {
318 if let Some(program) = self.wide_program(input)? {
319 return program.find(input, from, last).map_err(MatchError::search);
320 }
321 let search = match &self.0.engine {
322 Engine::Backtrack(program) => {
323 return program.find(input, from, last).map_err(MatchError::search);
324 }
325 Engine::Fancy { search, .. } => search,
326 };
327 let captures = search
328 .captures_from_pos(input, from)
329 .map_err(MatchError::search)?;
330 Ok(captures.map(|captures| {
331 captures
332 .iter()
333 .map(|group| group.map(|group| group.range()))
334 .collect()
335 }))
336 }
337
338 pub fn replace(&self, input: &str, replacement: &str, all: bool) -> Result<String, MatchError> {
354 check_input_length(self.0.input_limit, input)?;
355 let mut out = String::with_capacity(input.len());
356 let mut copied = 0;
357 let mut search_from = 0;
358 while search_from <= input.len() {
359 let Some(groups) = self.find(input, search_from, copied)? else {
360 break;
361 };
362 let Some(whole) = groups.first().cloned().flatten() else {
363 break;
364 };
365 out.push_str(&input[copied..whole.start]);
366 self.append_replacement(&mut out, replacement, input, &groups)?;
367 copied = whole.end;
368 if !all {
369 break;
370 }
371 search_from = next_search_start(input, &whole);
372 }
373 out.push_str(&input[copied..]);
374 Ok(out)
375 }
376
377 fn append_replacement(
381 &self,
382 out: &mut String,
383 replacement: &str,
384 input: &str,
385 groups: &[Option<Range<usize>>],
386 ) -> Result<(), MatchError> {
387 let refuse = |message: &str| MatchError(message.to_owned());
388 let group_count = self.group_count();
389 let mut chars = replacement.chars().peekable();
390 while let Some(character) = chars.next() {
391 match character {
392 '\\' => out.push(
393 chars
394 .next()
395 .ok_or_else(|| refuse("character to be escaped is missing"))?,
396 ),
397 '$' => {
398 let first = chars
399 .next()
400 .ok_or_else(|| refuse("illegal group reference: group index is missing"))?;
401 let group = if first == '{' {
402 let mut name = String::new();
403 while let Some(next) = chars.next_if(char::is_ascii_alphanumeric) {
404 name.push(next);
405 }
406 if name.is_empty() {
407 return Err(refuse("named capturing group has 0 length name"));
408 }
409 if chars.next() != Some('}') {
410 return Err(refuse("named capturing group is missing trailing '}'"));
411 }
412 *self
413 .0
414 .names
415 .get(&name)
416 .ok_or_else(|| MatchError(format!("no group with name {name}")))?
417 } else {
418 let mut group = first
419 .to_digit(10)
420 .ok_or_else(|| refuse("illegal group reference"))?
421 as usize;
422 if group > group_count {
423 return Err(MatchError(format!("no group {group}")));
424 }
425 while let Some(digit) = chars.peek().and_then(|next| next.to_digit(10)) {
426 let longer = group * 10 + digit as usize;
427 if longer > group_count {
428 break;
429 }
430 group = longer;
431 chars.next();
432 }
433 group
434 };
435 if let Some(Some(text)) = groups.get(group) {
436 out.push_str(input.get(text.clone()).ok_or_else(|| {
439 MatchError(format!(
440 "group {group} holds half of a char past U+FFFF, which a Rust \
441 string cannot hold"
442 ))
443 })?);
444 }
445 }
446 other => out.push(other),
447 }
448 }
449 Ok(())
450 }
451}
452
453#[derive(Debug)]
456enum FancyFailure {
457 Unbuilt(PatternError),
462 Refused(PatternError),
465}
466
467fn compile_fancy(translated: &str) -> Result<(Regex, Regex), FancyFailure> {
470 let compile = |source: &str| {
471 Regex::new(source).map_err(|error| {
472 let unbuilt = matches!(
473 &error,
474 FancyError::CompileError(compile_error) if matches!(
475 **compile_error,
476 CompileError::FeatureNotYetSupported(_)
477 | CompileError::LookBehindNotConst
478 | CompileError::InnerError(_)
479 | CompileError::DfaBuildError(..)
480 )
481 );
482 let error = PatternError(error.to_string());
483 if unbuilt {
484 FancyFailure::Unbuilt(error)
485 } else {
486 FancyFailure::Refused(error)
487 }
488 })
489 };
490 Ok((
491 compile(translated)?,
492 compile(&format!("\\A(?:{translated})\\z"))?,
493 ))
494}
495
496fn backref_past(expr: &Expr, groups: usize) -> Option<usize> {
499 match expr {
500 Expr::Backref { group, .. } if *group >= groups => Some(*group),
501 _ => expr
502 .children_iter()
503 .find_map(|child| backref_past(child, groups)),
504 }
505}
506
507fn count_groups(expr: &Expr) -> usize {
509 usize::from(matches!(expr, Expr::Group(_)))
510 + expr.children_iter().map(count_groups).sum::<usize>()
511}
512
513fn check_input_length(limit: Option<usize>, input: &str) -> Result<(), MatchError> {
517 match limit {
518 Some(limit) if input.len() > limit && input.encode_utf16().count() > limit => {
519 Err(MatchError(format!(
520 "the input is longer than the {limit} UTF-16 units that krabka reads a \
521 look-behind of this pattern for as Java does"
522 )))
523 }
524 _ => Ok(()),
525 }
526}
527
528fn next_search_start(input: &str, whole: &Range<usize>) -> usize {
532 if whole.is_empty() {
533 input[whole.end..]
534 .chars()
535 .next()
536 .map_or(input.len() + 1, |next| whole.end + next.len_utf8())
537 } else {
538 whole.end
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use {
545 super::*,
546 assert2::{assert, check},
547 };
548
549 fn pattern(source: &str) -> JavaPattern {
550 JavaPattern::compile(source).unwrap_or_else(|error| panic!("{source}: {error}"))
551 }
552
553 fn assert_pattern_matches(source: &str, input: &str, expected: bool) {
554 check!(
555 pattern(source).matches(input).ok() == Some(expected),
556 "{source:?} against {input:?}"
557 );
558 }
559
560 struct MatchCase<'a> {
562 source: &'a str,
563 input: &'a str,
564 expected: bool,
565 }
566
567 const fn row<'a>(source: &'a str, input: &'a str, expected: bool) -> MatchCase<'a> {
569 MatchCase {
570 source,
571 input,
572 expected,
573 }
574 }
575
576 struct ReplaceCase<'a> {
578 source: &'a str,
579 input: &'a str,
580 replacement: &'a str,
581 expected: &'a str,
582 }
583
584 const fn replace_row<'a>(
586 source: &'a str,
587 input: &'a str,
588 replacement: &'a str,
589 expected: &'a str,
590 ) -> ReplaceCase<'a> {
591 ReplaceCase {
592 source,
593 input,
594 replacement,
595 expected,
596 }
597 }
598
599 fn check_matches<'a>(cases: impl IntoIterator<Item = MatchCase<'a>>, debug_names: bool) {
600 for MatchCase {
601 source,
602 input,
603 expected,
604 } in cases
605 {
606 let context = if debug_names {
607 format!("{source:?} against {input:?}")
608 } else {
609 format!("{source} against {input}")
610 };
611 check!(
612 pattern(source).matches(input).ok() == Some(expected),
613 "{context}"
614 );
615 }
616 }
617
618 fn check_replacements<'a>(
619 cases: impl IntoIterator<Item = ReplaceCase<'a>>,
620 all: bool,
621 include_replacement: bool,
622 ) {
623 for ReplaceCase {
624 source,
625 input,
626 replacement,
627 expected,
628 } in cases
629 {
630 let context = if include_replacement {
631 format!("{source} on {input} with {replacement}")
632 } else {
633 format!("{source} on {input}")
634 };
635 check!(
636 pattern(source)
637 .replace(input, replacement, all)
638 .ok()
639 .as_deref()
640 == Some(expected),
641 "{context}"
642 );
643 }
644 }
645
646 #[test]
649 fn matches_backtracks_to_span_the_whole_input() {
650 let cases = [
652 row("a|ab", "ab", true),
653 row("a|ab", "abc", false),
654 row("CN=(.*?)", "CN=abc", true),
655 row("(a+)+b", "aaab", true),
656 row("ali", "alice", false),
657 row("ali.*", "alice", true),
658 ];
659 check_matches(cases, false);
660 }
661
662 #[test]
666 fn matches_reports_a_search_the_engine_gave_up_on() {
667 let hostile = format!("{}b", "a".repeat(40));
668
669 let result = pattern("^(a|aa)+\\1$").matches(&hostile);
670
671 assert!(result.is_err());
672 }
673
674 #[test]
679 fn replace_all_finds_an_empty_match_after_a_non_empty_one() {
680 let cases = [
682 replace_row("(.*)", "alice", "$1x", "alicexx"),
683 replace_row("a*", "baaac", "-", "-b--c-"),
684 replace_row("x*", "abc", "-", "-a-b-c-"),
685 replace_row("\\w*", "ab cd", "[$0]", "[ab][] [cd][]"),
686 replace_row("(?:)", "Γ©!", "|", "|Γ©|!|"),
687 replace_row("", "", "|", "|"),
688 replace_row("a", "", "|", ""),
689 replace_row("b*", "aΓ©", "-", "-a-Γ©-"),
690 ];
691 check_replacements(cases, true, false);
692 }
693
694 #[test]
696 fn replace_first_stops_after_the_first_match_even_when_it_is_empty() {
697 check_replacements(
699 [
700 replace_row("(.*)", "alice", "$1x", "alicex"),
701 replace_row("b*", "abc", "-", "-abc"),
702 ],
703 false,
704 false,
705 );
706 }
707
708 #[test]
713 fn matches_reads_classes_as_ascii_and_dot_as_java_does() {
714 let cases = [
716 row("\\w+", "abc_09", true),
717 row("\\w+", "Γ©", false),
718 row("\\W", "Γ©", true),
719 row("\\d", "Ω£", false),
720 row("\\D", "Ω£", true),
721 row("\\s", " \t\u{0B}", false),
722 row("\\s+", " \t\u{0B}", true),
723 row("\\s", "\u{a0}", false),
724 row("\\s", "\u{2003}", false),
725 row("\\S", "\u{a0}", true),
726 row("[\\w.]+", "a.b", true),
727 row("[\\w.]+", "Γ©", false),
728 row("[^\\w]", "Γ©", true),
729 row("[\\W]+", "Γ©!", true),
730 row("[\\d-]+", "1-2", true),
731 row("[\\s]", "\u{a0}", false),
732 row("(?U)\\w+", "Γ©", true),
733 row("(?U)\\d", "Ω£", true),
734 row("(?U)\\s", "\u{a0}", true),
735 row("(?U:\\w)\\w", "Γ©a", true),
736 row("(?U:\\w)\\w", "éé", false),
737 row("(?:(?U)\\w)\\w", "éé", false),
738 row("(?U)(?-U)\\w", "Γ©", false),
739 row("\\bfoo", "foo", true),
740 row("a\\b", "a", true),
741 row("\\Bfoo", "foo", false),
742 row("a\\bΓ©", "aΓ©", true),
743 row("\\bΓ©", "Γ©", false),
744 row("a\\BΓ©", "aΓ©", false),
745 row("a.b", "axb", true),
746 row("a.b", "a\nb", false),
747 row("a.b", "a\rb", false),
748 row("a.b", "a\u{85}b", false),
749 row("a.b", "a\u{2028}b", false),
750 row("a.b", "a\u{2029}b", false),
751 row(".*", "abc\rdef", false),
752 row("(?s)a.b", "a\rb", true),
753 row("(?s)a.b", "a\nb", true),
754 row("(?d)a.b", "a\rb", true),
755 row("(?d)a.b", "a\u{85}b", true),
756 row("(?d)a.b", "a\nb", false),
757 row("(?s:a.)b.", "a\nbx", true),
758 row("(?s:a.)b.", "a\nb\r", false),
759 row("(?s)(?-s).", "\n", false),
760 row("a[.]b", "a.b", true),
761 row("a\\.b", "axb", false),
762 ];
763 check_matches(cases, true);
764 }
765
766 #[test]
773 fn matches_folds_case_as_java_does() {
774 let cases = [
776 row("(?i)service-", "SERVICE-", true),
777 row("(?i)service-", "service-", true),
778 row("(?i)service-", "\u{17f}ervice-", false),
779 row("(?iu)service-", "\u{17f}ervice-", true),
780 row("(?iu)service-", "SERVICE-", true),
781 row("(?i)k", "\u{212a}", false),
782 row("(?iu)k", "\u{212a}", true),
783 row("(?ui)k", "\u{212a}", true),
784 row("(?u)k", "K", false),
785 row("(?i)Γ©", "Γ", false),
786 row("(?i)Γ©", "Γ©", true),
787 row("(?iu)Γ©", "Γ", true),
788 row("(?U)(?i)k", "\u{212a}", true),
789 row("(?iU)k", "\u{212a}", true),
790 row("(?iU)(?-U)k", "\u{212a}", false),
791 row("(?iU)(?-U)k", "K", true),
792 row("(?iU)(?-u)k", "\u{212a}", false),
793 row("(?iU)(?-u)\\w", "\u{e9}", true),
794 row("(?i)a(?u)k", "A\u{212a}", true),
795 row("(?i)a(?u)k", "AK", true),
796 row("(?iu)a(?-u)k", "A\u{212a}", false),
797 row("(?iu)a(?-u)k", "AK", true),
798 row("(?i)ss", "\u{df}", false),
799 row("(?iu)ss", "\u{df}", false),
800 row("(?i)i", "\u{130}", false),
801 row("(?i)i", "\u{131}", false),
802 row("(?i)\\u00E5", "\u{212b}", false),
803 row("(?iu)\\u00E5", "\u{212b}", true),
804 row("(?i)[a-f]+", "AbCdEf", true),
805 row("(?i)[a-f]", "\u{212a}", false),
806 row("(?i)[k]", "\u{212a}", false),
807 row("(?iu)[k]", "\u{212a}", true),
808 row("(?i)[^a]", "A", false),
809 row("(?i)[^a]", "b", true),
810 row("(?i)[^a]", "\u{e9}", true),
811 row("(?i)[^k]", "\u{212a}", true),
812 row("(?iu)[^k]", "\u{212a}", false),
813 row("(?i)[X-c]", "x", true),
814 row("(?i)[X-c]", "A", true),
815 row("(?i)[X-c]", "[", true),
816 row("(?i)[X-c]", "d", false),
817 row("(?i)[+-z]", "Q", true),
818 row("(?i)[a-c&&[^b]]", "B", false),
819 row("(?i)[a-c&&[^b]]", "C", true),
820 row("(?i)[a-c&&[^b]]", "A", true),
821 row("(?i)[a-c[^x-z]]", "Y", false),
822 row("(?i)[a-c[^x-z]]", "B", true),
823 row("(?i)[\\x41-\\x43]", "b", true),
824 row("(?i)[\\u0041-\\u0043]", "b", true),
825 row("(?i)[\\Qab\\E]", "B", true),
826 row("(?i)[a-]", "A", true),
827 row("(?i)[-a]", "A", true),
828 row("(?i)[]a]", "]", true),
829 row("(?i)[]a]", "A", true),
830 row("(?i)[a-c-e]", "E", true),
831 row("(?i)[a-c-e]", "-", true),
832 row("(?i)[\\w&&[^a]]", "A", false),
833 row("(?i)[\\w&&[^a]]", "B", true),
834 row("(?i)[a\\-c]", "C", true),
835 row("(?i)[a\\-c]", "-", true),
836 row("(?i)[\\0101-\\0103]", "b", true),
837 row("(?i)[\\0101-\\0103]", "d", false),
838 row("(?i)[\\x41-c]", "b", true),
839 row("(?i)[a-c[d-f]]", "E", true),
840 row("(?i)[\u{e9}-\u{eb}]", "\u{ca}", false),
841 row("(?ix) [ a - c ]", "B", true),
842 row("(?ix) [ a - c ] # x", "d", false),
843 row("(?i)a\\.b", "A.B", true),
844 row("(?i)\\[a\\]", "[A]", true),
845 row("(?i)a{1,2}b", "aAB", true),
846 row("(?i)\u{e9}+", "\u{e9}\u{e9}", true),
847 row("(?i)\\0101\\0102", "aB", true),
848 row("(?i)\\x41", "a", true),
849 row("(?i)\\u0041", "a", true),
850 row("(?i)\\x{41}", "a", true),
851 row("(?i)\\0101", "a", true),
852 row("(?i)\\c!", "A", true),
853 row("(?i)\\Qab\\E", "AB", true),
854 row("(?i)\\Q.b\\E", ".B", true),
855 row("(?i)\\Q.b\\E", "xB", false),
856 row("(?i)\\Qk\\E", "\u{212a}", false),
857 row("(?i)\\p{Lower}", "A", true),
858 row("(?i)\\p{Lower}", "\u{e9}", false),
859 row("(?i)\\p{Upper}", "a", true),
860 row("(?i)\\p{Upper}", "\u{c9}", false),
861 row("\\p{Lower}", "a", true),
862 row("\\p{Lower}", "A", false),
863 row("\\p{Lower}", "\u{e9}", false),
864 row("\\p{Upper}", "A", true),
865 row("\\p{Upper}", "\u{c9}", false),
866 row("(?i)\\p{Lu}", "a", true),
867 row("(?i)\\p{Lu}", "\u{e9}", true),
868 row("(?i)\\p{Lu}", "1", false),
869 row("(?i)\\P{Lu}", "a", false),
870 row("(?i)\\P{Lu}", "1", true),
871 row("\\p{Lu}", "a", false),
872 row("\\p{Lu}", "A", true),
873 row("(?i)[\\p{Lower}]", "A", true),
874 row("(?i)[\\P{Lower}]", "A", false),
875 row("(?i)[\\P{Lower}]", "1", true),
876 row("(?i)\\p{IsLowercase}", "A", true),
877 row("(?i)\\p{gc=Lu}", "a", true),
878 row("(?i)\\p{IsLu}", "a", true),
879 row("(?i:a)b", "Ab", true),
880 row("(?i:a)b", "AB", false),
881 row("a(?i)b", "aB", true),
882 row("a(?i)b", "Ab", false),
883 row("a(?i:b)c", "aBc", true),
884 row("a(?i:b)c", "aBC", false),
885 row("(?i)a(?-i)b", "Ab", true),
886 row("(?i)a(?-i)b", "AB", false),
887 row("((?i)a)b", "Ab", true),
888 row("((?i)a)b", "AB", false),
889 row("(?i)a|b", "B", true),
890 row("(?i)(?i)(?-i)a", "A", false),
891 row("(?i)(?i)(?-i)a", "a", true),
892 row("(?i)a{2}", "aA", true),
893 row("(?i)(?:a|b)+", "AbBa", true),
894 row("(?i)^ab$", "AB", true),
895 row("(?i)\\bab", "AB", true),
896 row("(?i)\\w", "A", true),
897 row("(?i)a.c", "AxC", true),
898 row("(?i)(?<Name>a)", "A", true),
899 row("(?i)(?=a)A", "A", true),
900 row("(?i)(?!a)b", "B", true),
901 row("(?ix) a b # comment", "AB", true),
902 row("(?x)a b # Comment here", "ab", true),
903 row("(?x)a b # Comment here", "AB", false),
904 row("(?iu)(a)\\1", "aA", true),
905 row("(?iu)(a)\\1", "ab", false),
906 row("(?i)(a)(?-i)\\1", "aA", false),
907 row("(?i)(a)(?-i)\\1", "aa", true),
908 row("(?i)(a)(?-i:\\1)", "aA", false),
909 row("(a)\\1", "aa", true),
910 row("(a)\\1", "aA", false),
911 ];
912 check_matches(cases, true);
913 }
914
915 #[test]
922 fn matches_reads_quotes_classes_and_references_as_java_does() {
923 let cases = [
925 row("(?i)[\\Qa\\E-c]", "B", true),
927 row("(?i)[\\Qa\\E-c]", "b", true),
928 row("(?i)[\\Qa\\E-c]", "A", true),
929 row("(?i)[\\Qa\\E-c]", "X", false),
930 row("(?i)[a-\\Qc\\E]", "B", true),
931 row("(?i)[a-\\Qc\\E]", "X", false),
932 row("(?i)[\\Qa\\E-\\Qc\\E]", "B", true),
933 row("(?i)[\\Qa\\E-\\Qc\\E]", "X", false),
934 row("(?i)[\\Qa\\Eb-d]", "C", true),
935 row("(?i)[\\Qa\\Eb-d]", "A", true),
936 row("(?i)[\\Qa\\Eb-d]", "X", false),
937 row("(?i)[\\Q\\Ea-c]", "B", true),
938 row("(?i)[a-\\Q\\Ec]", "B", true),
939 row("(?i)[a-\\Q\\Ec]", "X", false),
940 row("[\\Qa\\E-c]", "b", true),
941 row("[\\Qa\\E-c]", "B", false),
942 row("[\\Qa\\E-c]", "X", false),
943 row("(?i)[\\Q]\\E-c]", "A", true),
944 row("(?i)[\\Q]\\E-c]", "X", false),
945 row("(?i)[\\Q1\\E-3]", "2", true),
946 row("(?i)[\\Qa-c\\E]", "B", false),
948 row("(?i)[\\Qa-c\\E]", "-", true),
949 row("(?i)[\\Qa-c\\E]", "C", true),
950 row("(?i)[a\\Q-\\Ec]", "B", false),
951 row("(?i)[a\\Q-\\Ec]", "-", true),
952 row("(?i)[a\\Q-\\Ec]", "C", true),
953 row("(?i)x\\Qa", "XA", true),
955 row("(?i)x\\Qa\\E", "XA", true),
956 row("(?i)a\\Q\\E+", "AA", true),
957 row("\\Q1\\E2", "12", true),
958 row("\\Qa\\\\E", "a\\", true),
959 row("\\Q\\\\E", "\\", true),
960 row("\\Qa.b\\E", "a.b", true),
961 row("\\Qa.b\\E", "axb", false),
962 row("(?i)\\Qk\\E", "\u{212a}", false),
963 row("(?iu)\\Qk\\E", "\u{212a}", true),
964 row("\\<", "<", true),
966 row("\\>", ">", true),
967 row("[\\<]", "<", true),
968 row("[\\>]", ">", true),
969 row("\\Q<\\E", "<", true),
970 row("[\\Q<\\E]", "<", true),
971 row("\\Q>\\E", ">", true),
972 row("(?i)[]-c]", "A", true),
974 row("(?i)[]-c]", "B", true),
975 row("(?i)[]-c]", "_", true),
976 row("(?i)[]-c]", "]", true),
977 row("(?i)[]-c]", "X", false),
978 row("(?i)[]-c]", "d", false),
979 row("(?i)[]-c]", "D", false),
980 row("(?i)[^]-c]", "A", false),
981 row("(?i)[^]-c]", "X", true),
982 row("(?i)[^]-c]", "d", true),
983 row("(?i)[]-cx]", "X", true),
984 row("(?i)[]-]", "-", true),
985 row("(?i)[]-]", "]", true),
986 row("(?iu)[]-c]", "A", true),
987 row("(?ix)[ ]-c ]", "A", true),
988 row("[]-c]", "a", true),
989 row("[]-c]", "A", false),
990 row("[]-c]", "]", true),
991 row("(?iu)\\p{Lower}", "\u{17f}", false),
993 row("(?iu)\\p{Lower}", "\u{212a}", false),
994 row("(?iu)\\p{Lower}", "A", true),
995 row("(?iu)\\p{Lower}", "a", true),
996 row("(?iu)\\p{Upper}", "\u{17f}", false),
997 row("(?iu)\\p{Upper}", "\u{212a}", false),
998 row("(?iu)\\p{Upper}", "a", true),
999 row("(?iu)\\p{Upper}", "\u{e9}", false),
1000 row("(?iu)\\P{Lower}", "\u{17f}", true),
1001 row("(?iu)\\P{Lower}", "A", false),
1002 row("(?iu)\\P{Lower}", "1", true),
1003 row("(?iu)\\P{Upper}", "\u{212a}", true),
1004 row("(?u)\\p{Lower}", "\u{17f}", false),
1005 row("(?u)\\p{Lower}", "A", false),
1006 row("(?iu)\\w", "\u{17f}", false),
1008 row("(?iu)\\w", "\u{212a}", false),
1009 row("(?iu)\\w", "a", true),
1010 row("(?iu)\\w", "_", true),
1011 row("(?iu)\\W", "\u{17f}", true),
1012 row("(?iu)\\W", "\u{212a}", true),
1013 row("(?iu)\\W", "a", false),
1014 row("(?iu)\\W", "-", true),
1015 row("(?iu)\\w+", "k\u{212a}", false),
1016 row("(?iu)k\\w", "k\u{212a}", false),
1017 row("(?iu)\\w\\p{Lower}", "k\u{212a}", false),
1018 row("(?i)\\w", "\u{17f}", false),
1019 row("(?u)\\w", "\u{17f}", false),
1020 row("(?iU)\\w", "\u{17f}", true),
1021 row("(?i)(a)\\1", "aA", true),
1023 row("(?i)(a)\\1", "aa", true),
1024 row("(?i)(a)\\1", "ab", false),
1025 row("(?i)(\\d+)-\\1", "12-12", true),
1026 row("(?i)(\\d+)-\\1", "12-13", false),
1027 row("(?i)(?<x>a)\\k<x>", "aA", true),
1028 row("(?i)(?<x>a)\\k<x>", "ab", false),
1029 row("(?i)(a+)\\1", "aaAA", true),
1030 row("(?i)(a)\\1+", "aAAa", true),
1031 row("(?i)(a)(\\1)", "aA", true),
1032 row("(?i)(k)\\1", "kK", true),
1033 row("(?i)(k)\\1", "k\u{212a}", false),
1034 row("(?i:(a)\\1)", "aA", true),
1035 row("a(?i:(b)\\1)", "aBb", true),
1036 row("a(?i:(b)\\1)", "aBB", true),
1037 row("a(?i:(b)\\1)", "AbB", false),
1038 row("(?i)(a)((b))\\3\\2\\1", "aBBBA", true),
1039 row("(?i)(a)((b))\\3\\2\\1", "aBbBa", true),
1040 ];
1041 check_matches(cases, true);
1042 }
1043
1044 #[test]
1049 fn matches_folds_unicode_case_in_a_class_as_java_does() {
1050 let cases = [
1052 row("(?iu)[\\w]", "\u{17f}", false),
1054 row("(?iu)[\\w]", "\u{212a}", false),
1055 row("(?iu)[\\w]", "a", true),
1056 row("(?iu)[\\w]", "K", true),
1057 row("(?iu)[\\w]", "_", true),
1058 row("(?iu)[\\w]", "\u{e9}", false),
1059 row("(?iu)[\\w-]", "\u{17f}", false),
1060 row("(?iu)[\\w-]", "\u{212a}", false),
1061 row("(?iu)[\\w-]", "-", true),
1062 row("(?iu)[\\w-]", "z", true),
1063 row("(?iu)[\\W]", "\u{17f}", true),
1064 row("(?iu)[\\W]", "\u{212a}", true),
1065 row("(?iu)[\\W]", "a", false),
1066 row("(?iu)[\\W]", "-", true),
1067 row("(?iu)[^\\w]", "\u{17f}", true),
1068 row("(?iu)[^\\w]", "k", false),
1069 row("(?iu)[\\p{Lower}]", "\u{17f}", false),
1070 row("(?iu)[\\p{Lower}]", "\u{212a}", false),
1071 row("(?iu)[\\p{Lower}]", "A", true),
1072 row("(?iu)[\\p{Lower}]", "a", true),
1073 row("(?iu)[\\p{Lower}]", "\u{e9}", false),
1074 row("(?iu)[\\p{Upper}]", "\u{17f}", false),
1075 row("(?iu)[\\p{Upper}]", "\u{212a}", false),
1076 row("(?iu)[\\p{Upper}]", "a", true),
1077 row("(?iu)[\\p{Upper}]", "\u{c9}", false),
1078 row("(?iu)[\\P{Lower}]", "\u{17f}", true),
1079 row("(?iu)[\\P{Lower}]", "A", false),
1080 row("(?iu)[\\P{Lower}]", "1", true),
1081 row("(?iu)[\\P{Upper}]", "\u{212a}", true),
1082 row("(?iu)[\\p{Lower}k]", "\u{17f}", false),
1083 row("(?iu)[\\p{Lower}k]", "\u{212a}", true),
1084 row("(?iu)[\\p{Lower}s]", "\u{17f}", true),
1085 row("(?iu)[\\w&&[^a]]", "A", false),
1086 row("(?iu)[\\w&&[^a]]", "b", true),
1087 row("(?iu)[\\w&&[^k]]", "\u{212a}", false),
1088 row("(?iu)[\\w&&[^k]]", "K", false),
1089 row("(?iu)[^\\p{Lower}]", "\u{17f}", true),
1090 row("(?iu)[^\\p{Lower}]", "a", false),
1091 row("(?iu)[^\\p{Lower}]", "1", true),
1092 row("(?iu)[a-z]", "\u{212a}", true),
1094 row("(?iu)[a-z]", "\u{17f}", true),
1095 row("(?iu)[a-z]", "\u{130}", true),
1096 row("(?iu)[a-z]", "\u{131}", true),
1097 row("(?iu)[a-z]", "K", true),
1098 row("(?iu)[a-z]", "\u{df}", false),
1099 row("(?iu)[A-Z]", "\u{212a}", false),
1100 row("(?iu)[A-Z]", "\u{17f}", true),
1101 row("(?iu)[A-Z]", "\u{130}", false),
1102 row("(?iu)[A-Z]", "\u{131}", true),
1103 row("(?iu)[A-Z]", "k", true),
1104 row("(?iu)[A-K]", "\u{212a}", false),
1105 row("(?iu)[A-K]", "k", true),
1106 row("(?iu)[A-K]", "\u{17f}", false),
1107 row("(?iu)[A-c]", "\u{212a}", false),
1108 row("(?iu)[A-c]", "k", true),
1109 row("(?iu)[A-c]", "C", true),
1110 row("(?iu)[A-c]", "d", true),
1111 row("(?iu)[A-k]", "\u{212a}", true),
1112 row("(?iu)[k-k]", "\u{212a}", true),
1113 row("(?iu)[K-K]", "\u{212a}", false),
1114 row("(?iu)[K-K]", "k", true),
1115 row("(?iu)[s-s]", "\u{17f}", true),
1116 row("(?iu)[S-S]", "\u{17f}", true),
1117 row("(?iu)[i-i]", "\u{130}", true),
1118 row("(?iu)[I-I]", "\u{131}", true),
1119 row("(?iu)[^A-Z]", "\u{212a}", true),
1120 row("(?iu)[^A-Z]", "\u{17f}", false),
1121 row("(?iu)[^a-z]", "\u{212a}", false),
1122 row("(?iu)[^A-K]", "\u{212a}", true),
1123 row("(?iu)[^A-K]", "k", false),
1124 row("(?iu)[\\x{E0}-\\x{FE}]", "\u{c9}", true),
1125 row("(?iu)[\\x{E0}-\\x{FE}]", "\u{178}", false),
1126 row("(?iu)[\\x{C0}-\\x{DE}]", "\u{ff}", false),
1127 row("(?iu)[\\x{C0}-\\x{DE}]", "\u{178}", false),
1128 row("(?iu)[\\x{391}-\\x{3A9}]", "\u{3c3}", true),
1129 row("(?iu)[\\x{391}-\\x{3A9}]", "\u{3c2}", true),
1130 row("(?iu)[\\x{3B1}-\\x{3C9}]", "\u{3a3}", true),
1131 row("(?iu)[\\x{410}-\\x{42F}]", "\u{44f}", true),
1132 row("(?iu)[\\x{410}-\\x{42F}]", "\u{436}", true),
1133 row("(?iu)[\\x{DF}-\\x{DF}]", "\u{1e9e}", true),
1134 row("(?iu)[\\x{1E9E}-\\x{1E9E}]", "\u{df}", false),
1135 row("(?iu)[k]", "\u{212a}", true),
1137 row("(?iu)[K]", "\u{212a}", true),
1138 row("(?iu)[s]", "\u{17f}", true),
1139 row("(?iu)[S]", "\u{17f}", true),
1140 row("(?iu)[i]", "\u{130}", true),
1141 row("(?iu)[i]", "\u{131}", true),
1142 row("(?iu)[I]", "\u{130}", true),
1143 row("(?iu)[I]", "\u{131}", true),
1144 row("(?iu)[\\x{E5}]", "\u{212b}", true),
1145 row("(?iu)[\\x{C5}]", "\u{212b}", true),
1146 row("(?iu)[\\x{FF}]", "\u{178}", true),
1147 row("(?iu)[\\x{B5}]", "\u{3bc}", true),
1148 row("(?iu)[\\x{B5}]", "\u{39c}", true),
1149 row("(?iu)[\\x{3BC}]", "\u{b5}", true),
1150 row("(?iu)[\\x{E9}]", "\u{c9}", true),
1151 row("(?iu)[\\x{C9}]", "\u{e9}", true),
1152 row("(?iu)[\\x{DF}]", "\u{1e9e}", false),
1153 row("(?iu)[\\x{1E9E}]", "\u{df}", true),
1154 row("(?iu)[\\x{3C3}]", "\u{3c2}", true),
1155 row("(?iu)[\\x{3C2}]", "\u{3a3}", true),
1156 row("(?iu)[\\x{3A3}]", "\u{3c2}", true),
1157 row("(?iu)[\\x{1C6}]", "\u{1c5}", true),
1158 row("(?iu)[\\x{1C5}]", "\u{1c4}", true),
1159 row("(?iu)[\\x{1F88}]", "\u{1f80}", true),
1160 row("(?iu)[\\x{1F80}]", "\u{1f88}", true),
1161 row("(?iu)[\\x{10428}]", "\u{10400}", true),
1162 row("(?iu)[\\x{10400}-\\x{1044F}]", "\u{10428}", true),
1163 row("(?iu)[z]", "Z", true),
1164 row("(?iu)[1]", "1", true),
1165 row("(?iu)[a-z&&[^k]]", "\u{212a}", false),
1167 row("(?iu)[a-z&&[^k]]", "K", false),
1168 row("(?iu)[a-z&&[^k]]", "j", true),
1169 row("(?iu)[a-z&&k]", "\u{212a}", true),
1170 row("(?iu)[a-z&&k]", "K", true),
1171 row("(?iu)[a-c[x-z]]", "X", true),
1172 row("(?iu)[a-c[^x-z]]", "Y", false),
1173 row("(?iu)[a-c[^x-z]]", "B", true),
1174 row("(?iu)[^a-z&&[^k]]", "k", true),
1175 row("(?iu)[^a-z&&[^k]]", "\u{212a}", true),
1176 row("(?iu)[a&&-1]", "-", false),
1177 row("(?iu)[a&&-1]", "a", false),
1178 row("(?iu)[a-c&&b-d]", "B", true),
1179 row("(?iu)[a-c&&b-d]", "A", false),
1180 row("(?iU)[\\w]", "\u{17f}", true),
1182 row("(?iU)[\\w]", "\u{e9}", true),
1183 row("(?iU)[a-z]", "\u{212a}", true),
1184 row("(?iU)[A-Z]", "\u{212a}", false),
1185 row("(?iU)[\\p{Lower}]", "A", true),
1186 row("(?iU)[\\p{Upper}]", "a", true),
1187 row("(?iU)[\\p{Lower}]", "\u{1c5}", true),
1188 row("(?iU)[\\p{Lu}]", "a", true),
1189 row("(?iU)[\\p{Lu}]", "1", false),
1190 row("(?i)[a-z]", "\u{212a}", false),
1192 row("(?i)[a-z]", "\u{17f}", false),
1193 row("(?i)[A-K]", "k", true),
1194 row("(?i)[k]", "\u{212a}", false),
1195 row("(?i)[\\w]", "\u{17f}", false),
1196 row("(?u)[a-z]", "A", false),
1197 row("(?u)[k]", "\u{212a}", false),
1198 row("[a-z]", "K", false),
1199 row("(?iu)(?-i)[a-z]", "A", false),
1201 row("(?iu)(?-i)[a-z]", "\u{212a}", false),
1202 row("(?iu)(?-u)[a-z]", "A", true),
1203 row("(?iu)(?-u)[a-z]", "\u{212a}", false),
1204 row("(?iu)(?-u)[k]", "\u{212a}", false),
1205 row("(?iu:[a-z])A", "AA", true),
1206 row("(?iu:[a-z])A", "Aa", false),
1207 row("(?iu:[a-z])[A-Z]", "aa", false),
1208 row("(?iu:[a-z])[A-Z]", "\u{212a}a", false),
1209 ];
1210 check_matches(cases, true);
1211 }
1212
1213 #[test]
1217 fn matches_reads_a_class_across_the_surrogates_as_java_does() {
1218 let cases = [
1220 row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{e000}", false),
1221 row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{d7ff}", false),
1222 row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{e001}", false),
1223 row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{10976}", true),
1224 row("(?iu)[^\\x{3c2}-\\x{10975}]", "a", true),
1225 row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{d7ff}", true),
1226 row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{e000}", true),
1227 row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{e001}", false),
1228 row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{d7ff}", false),
1229 row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{e000}", false),
1230 row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{d7fe}", true),
1231 row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{e001}", true),
1232 row("(?iu)[^\\x{D800}-\\x{E001}]", "\u{e000}", false),
1233 row("(?iu)[^\\x{D800}-\\x{E001}]", "\u{e002}", true),
1234 row("(?iu)[^\\x{41}-\\x{DBFF}]", "\u{d7ff}", false),
1235 row("(?iu)[^\\x{41}-\\x{DBFF}]", "\u{e000}", true),
1236 row("(?iu)[\\x{D800}-\\x{DFFF}]", "a", false),
1237 row("(?iu)[^\\x{D800}-\\x{DFFF}]", "a", true),
1238 row("(?iu)[^\\x{D800}-\\x{DFFF}]", "\u{e000}", true),
1239 row("(?iu)[a\\x{D800}-\\x{DFFF}]", "a", true),
1240 row("(?iu)[a\\x{D800}-\\x{DFFF}]", "\u{d7ff}", false),
1241 row("(?iu)[^a\\x{D800}-\\x{DFFF}]", "a", false),
1242 row("(?iu)[^a\\x{D800}-\\x{DFFF}]", "b", true),
1243 ];
1244 check_matches(cases, true);
1245 }
1246
1247 #[test]
1252 fn matches_reads_the_posix_classes_and_properties_as_java_does() {
1253 let cases = [
1255 row("\\p{Alpha}", "a", true),
1257 row("\\p{Alpha}", "Z", true),
1258 row("\\p{Alpha}", "\u{e9}", false),
1259 row("\\p{Alpha}", "\u{3b1}", false),
1260 row("\\p{Alpha}", "1", false),
1261 row("\\p{Alpha}", "_", false),
1262 row("\\P{Alpha}", "\u{e9}", true),
1263 row("\\P{Alpha}", "a", false),
1264 row("\\p{Alnum}", "5", true),
1265 row("\\p{Alnum}", "q", true),
1266 row("\\p{Alnum}", "_", false),
1267 row("\\p{Alnum}", "\u{e9}", false),
1268 row("\\p{Alnum}", "\u{663}", false),
1269 row("\\p{Digit}", "7", true),
1270 row("\\p{Digit}", "\u{663}", false),
1271 row("\\p{Digit}", "\u{ff17}", false),
1272 row("\\p{Punct}", "!", true),
1273 row("\\p{Punct}", "$", true),
1274 row("\\p{Punct}", "_", true),
1275 row("\\p{Punct}", "~", true),
1276 row("\\p{Punct}", "`", true),
1277 row("\\p{Punct}", "@", true),
1278 row("\\p{Punct}", "a", false),
1279 row("\\p{Punct}", "\u{a1}", false),
1280 row("\\p{Punct}", "\u{2014}", false),
1281 row("\\P{Punct}", "\u{a1}", true),
1282 row("\\P{Punct}", "!", false),
1283 row("\\p{ASCII}", "a", true),
1284 row("\\p{ASCII}", "\u{7f}", true),
1285 row("\\p{ASCII}", "\u{0}", true),
1286 row("\\p{ASCII}", "\u{80}", false),
1287 row("\\p{ASCII}", "\u{e9}", false),
1288 row("\\P{ASCII}", "\u{e9}", true),
1289 row("\\p{XDigit}", "f", true),
1290 row("\\p{XDigit}", "F", true),
1291 row("\\p{XDigit}", "9", true),
1292 row("\\p{XDigit}", "g", false),
1293 row("\\p{XDigit}", "\u{ff11}", false),
1294 row("\\p{Blank}", " ", true),
1295 row("\\p{Blank}", "\t", true),
1296 row("\\p{Blank}", "\n", false),
1297 row("\\p{Blank}", "\u{a0}", false),
1298 row("\\p{Cntrl}", "\u{1f}", true),
1299 row("\\p{Cntrl}", "\u{7f}", true),
1300 row("\\p{Cntrl}", "\u{0}", true),
1301 row("\\p{Cntrl}", " ", false),
1302 row("\\p{Cntrl}", "\u{80}", false),
1303 row("\\p{Graph}", "!", true),
1304 row("\\p{Graph}", "~", true),
1305 row("\\p{Graph}", " ", false),
1306 row("\\p{Graph}", "\u{e9}", false),
1307 row("\\p{Print}", " ", true),
1308 row("\\p{Print}", "~", true),
1309 row("\\p{Print}", "\u{7f}", false),
1310 row("\\p{Print}", "\u{e9}", false),
1311 row("\\p{Space}", " ", true),
1312 row("\\p{Space}", "\u{b}", true),
1313 row("\\p{Space}", "\u{a0}", false),
1314 row("\\p{Space}", "\u{2003}", false),
1315 row("[\\p{Alpha}]", "\u{e9}", false),
1316 row("[\\p{Alpha}]", "b", true),
1317 row("[\\p{Alpha}\\d]", "7", true),
1318 row("[\\p{Alnum}_]", "_", true),
1319 row("[\\p{Alnum}_]", "\u{e9}", false),
1320 row("[\\p{Punct}]", "\u{a1}", false),
1321 row("[\\p{Punct}]", ";", true),
1322 row("[^\\p{Punct}]", ";", false),
1323 row("[^\\p{Punct}]", "\u{a1}", true),
1324 row("[\\P{Punct}]", ";", false),
1325 row("[\\p{ASCII}]", "\u{e9}", false),
1326 row("[\\p{ASCII}-]", "-", true),
1327 row("[\\p{XDigit}]", "\u{ff11}", false),
1328 row("[\\p{Blank}]", "\u{a0}", false),
1329 row("[\\p{Space}x]", "\u{a0}", false),
1330 row("[\\p{Cntrl}]", "\u{80}", false),
1331 row("[\\p{Graph}]", "\u{e9}", false),
1332 row("[\\p{Print}]", "\u{e9}", false),
1333 row("[\\p{Alpha}&&[^a]]", "a", false),
1334 row("[\\p{Alpha}&&[^a]]", "b", true),
1335 row("(?i)\\p{Alpha}", "\u{17f}", false),
1337 row("(?i)\\p{Alpha}", "\u{212a}", false),
1338 row("(?iu)\\p{Alpha}", "\u{17f}", false),
1339 row("(?iu)\\p{Alpha}", "\u{212a}", false),
1340 row("(?iu)\\p{ASCII}", "\u{17f}", false),
1341 row("(?iu)\\p{ASCII}", "\u{212a}", false),
1342 row("(?iu)\\p{ASCII}", "K", true),
1343 row("(?iu)\\p{Punct}", "\u{212a}", false),
1344 row("(?iu)\\p{Digit}", "1", true),
1345 row("(?iu)[\\p{Alnum}]", "\u{17f}", false),
1346 row("(?iu)[\\p{Alnum}]", "\u{212a}", false),
1347 row("(?iu)[\\p{ASCII}]", "\u{212a}", false),
1348 row("(?iu)[\\p{XDigit}]", "\u{212a}", false),
1349 row("(?iu)[\\p{Punct}k]", "\u{212a}", true),
1350 row("(?i)\\p{Lower}", "A", true),
1351 row("(?i)\\p{Upper}", "a", true),
1352 row("(?i)\\p{Lower}", "\u{e9}", false),
1353 row("(?iu)\\p{Lower}", "\u{17f}", false),
1354 row("(?iu)\\p{Upper}", "\u{212a}", false),
1355 row("(?iu)\\p{IsGreek}", "\u{345}", false),
1357 row("(?iu)\\p{IsLatin}", "\u{212a}", true),
1358 row("(?iu)\\p{IsLatin}", "\u{17f}", true),
1359 row("(?iu)\\p{L}", "\u{17f}", true),
1360 row("(?iu)\\p{Nd}", "\u{663}", true),
1361 row("(?iu)[\\p{IsGreek}]", "\u{345}", false),
1362 row("(?iu)\\p{Lu}", "a", true),
1363 row("(?iu)\\p{Lu}", "\u{df}", true),
1364 row("(?iu)\\p{Lu}", "\u{138}", true),
1365 row("(?iu)\\p{Lu}", "1", false),
1366 row("(?iu)\\p{Ll}", "A", true),
1367 row("(?iu)\\p{Lt}", "a", true),
1368 row("(?iu)\\P{Lu}", "a", false),
1369 row("(?iu)\\P{Lu}", "1", true),
1370 row("(?iu)[\\p{Lu}]", "\u{df}", true),
1371 row("(?iu)[\\p{Lu}]", "\u{138}", true),
1372 row("(?iu)[^\\p{Lu}]", "\u{df}", false),
1373 row("(?iu)[^\\p{Lu}]", "1", true),
1374 row("(?iu)\\p{IsLowercase}", "A", true),
1375 row("(?iu)\\p{IsUppercase}", "a", true),
1376 row("(?iu)\\p{IsLowercase}", "\u{2102}", true),
1377 row("(?iu)\\p{gc=Lu}", "\u{138}", true),
1378 row("(?iu)\\p{IsLu}", "\u{138}", true),
1379 row("(?iu)\\p{Lower}", "A", true),
1380 row("(?U)\\p{Alpha}", "\u{e9}", true),
1382 row("(?U)\\p{Alpha}", "1", false),
1383 row("(?U)\\p{Alnum}", "\u{e9}", true),
1384 row("(?U)\\p{Alnum}", "\u{663}", true),
1385 row("(?U)\\p{Alnum}", "_", false),
1386 row("(?U)\\p{Digit}", "\u{663}", true),
1387 row("(?U)\\p{Digit}", "a", false),
1388 row("(?U)\\p{Punct}", "\u{a1}", true),
1389 row("(?U)\\p{Punct}", "$", false),
1390 row("(?U)\\p{Punct}", "!", true),
1391 row("(?U)\\p{Space}", "\u{a0}", true),
1392 row("(?U)\\p{Space}", "\u{85}", true),
1393 row("(?U)\\p{Space}", "\u{2028}", true),
1394 row("(?U)\\p{Space}", "a", false),
1395 row("(?U)\\p{XDigit}", "\u{ff11}", true),
1396 row("(?U)\\p{XDigit}", "\u{663}", true),
1397 row("(?U)\\p{XDigit}", "g", false),
1398 row("(?U)\\p{Blank}", "\u{a0}", true),
1399 row("(?U)\\p{Blank}", "\t", true),
1400 row("(?U)\\p{Blank}", "\n", false),
1401 row("(?U)\\p{Cntrl}", "\u{80}", true),
1402 row("(?U)\\p{Cntrl}", " ", false),
1403 row("(?U)\\p{Graph}", "\u{e9}", true),
1404 row("(?U)\\p{Graph}", " ", false),
1405 row("(?U)\\p{Graph}", "\u{a0}", false),
1406 row("(?U)\\p{Graph}", "\u{2028}", false),
1407 row("(?U)\\p{Print}", "\u{e9}", true),
1408 row("(?U)\\p{Print}", " ", true),
1409 row("(?U)\\p{Print}", "\u{a0}", true),
1410 row("(?U)\\p{Print}", "\n", false),
1411 row("(?U)\\p{Print}", "\u{2028}", false),
1412 row("(?U)\\p{Lower}", "\u{e9}", true),
1413 row("(?U)\\p{Lower}", "A", false),
1414 row("(?U)\\p{Upper}", "\u{c9}", true),
1415 row("(?U)\\p{alpha}", "\u{e9}", true),
1416 row("(?U)\\P{Graph}", " ", true),
1417 row("(?U)\\P{Graph}", "a", false),
1418 row("(?U)[\\p{Alpha}]", "\u{e9}", true),
1419 row("(?U)[\\p{Graph}]", "\u{e9}", true),
1420 row("(?U)[\\P{Graph}x]", " ", true),
1421 row("(?U)[\\P{Graph}x]", "x", true),
1422 row("(?U)[\\P{Graph}x]", "a", false),
1423 row("(?iU)\\p{Lower}", "A", true),
1424 row("(?iU)\\p{Lower}", "\u{1c5}", true),
1425 row("(?iU)\\p{Upper}", "a", true),
1426 row("(?iU)\\p{Lower}", "\u{2102}", true),
1427 row("(?iU)\\p{Alpha}", "\u{212a}", true),
1428 row("(?iU)\\p{Punct}", "\u{a1}", true),
1429 row("(?iU)\\p{Graph}", "\u{212a}", true),
1430 ];
1431 check_matches(cases, true);
1432 }
1433
1434 #[test]
1438 fn matches_folds_unicode_case_of_a_literal_as_java_does() {
1439 let cases = [
1441 row("(?iu)i", "\u{130}", true),
1443 row("(?iu)i", "\u{131}", true),
1444 row("(?iu)I", "\u{130}", true),
1445 row("(?iu)I", "\u{131}", true),
1446 row("(?iu)\u{130}", "i", true),
1447 row("(?iu)\u{130}", "\u{131}", true),
1448 row("(?iu)\u{131}", "I", true),
1449 row("(?iu)\u{131}", "\u{130}", true),
1450 row("(?iu)i", "I", true),
1451 row("(?iu)id", "\u{130}D", true),
1452 row("(?iu)k", "\u{212a}", true),
1453 row("(?iu)K", "\u{212a}", true),
1454 row("(?iu)\u{212a}", "k", true),
1455 row("(?iu)s", "\u{17f}", true),
1456 row("(?iu)\u{17f}", "S", true),
1457 row("(?iu)\u{e5}", "\u{212b}", true),
1458 row("(?iu)\u{212b}", "\u{c5}", true),
1459 row("(?iu)\u{3c3}", "\u{3c2}", true),
1460 row("(?iu)\u{3c2}", "\u{3a3}", true),
1461 row("(?iu)\u{3a3}", "\u{3c3}", true),
1462 row("(?iu)\u{b5}", "\u{39c}", true),
1463 row("(?iu)\u{b5}", "\u{3bc}", true),
1464 row("(?iu)\u{1c5}", "\u{1c6}", true),
1465 row("(?iu)\u{1f80}", "\u{1f88}", true),
1466 row("(?iu)\u{1f88}", "\u{1f80}", true),
1467 row("(?iu)\u{1f80}", "\u{1f80}", true),
1468 row("(?iu)\u{ff}", "\u{178}", true),
1469 row("(?iu)\u{10428}", "\u{10400}", true),
1470 row("(?iu)\u{e9}", "\u{c9}", true),
1471 row("(?iu)\u{436}", "\u{416}", true),
1472 row("(?iu)1", "1", true),
1473 row("(?iu)ss", "\u{df}", false),
1474 row("(?iu)SS", "\u{1e9e}", false),
1475 row("(?iu)\u{df}", "\u{df}", true),
1476 row("(?i)i", "\u{130}", false),
1478 row("(?i)i", "\u{131}", false),
1479 row("(?i)k", "\u{212a}", false),
1480 row("(?u)i", "I", false),
1481 row("(?u)i", "\u{130}", false),
1482 row("(?iu)(?-i)i", "I", false),
1483 row("(?iu)(?-i)i", "\u{130}", false),
1484 row("(?iu)(?-u)i", "\u{130}", false),
1485 row("(?iu)(?-u)i", "I", true),
1486 row("(?iu:i)", "\u{130}", true),
1487 row("(?iu:i)i", "\u{130}i", true),
1488 row("(?iu:i)i", "\u{130}I", false),
1489 ];
1490 check_matches(cases, true);
1491 }
1492
1493 #[test]
1497 fn matches_reads_a_run_of_literals_as_java_does() {
1498 let cases = [
1500 row("(?iu)\u{df}", "\u{1e9e}", false),
1502 row("(?iu)\u{df}", "\u{df}", true),
1503 row("(?iu)\u{df}\u{df}", "\u{1e9e}\u{1e9e}", true),
1504 row("(?iu)\u{df}a", "\u{1e9e}a", true),
1505 row("(?iu)a\u{df}", "a\u{1e9e}", true),
1506 row("(?iu)\u{df}*", "\u{1e9e}", false),
1507 row("(?iu)\u{df}a*", "\u{1e9e}", false),
1508 row("(?iu)\u{df}a*", "\u{1e9e}a", false),
1509 row("(?iu)\u{df}ab*", "\u{1e9e}ab", true),
1510 row("(?iu)\u{df}ab*", "\u{1e9e}a", true),
1511 row("(?iu)a\u{df}*", "a\u{1e9e}", false),
1512 row("(?iu)a\u{df}*", "a", true),
1513 row("(?iu)a{2}\u{df}", "aa\u{1e9e}", false),
1514 row("(?iu)a{2}\u{df}\u{df}", "aa\u{1e9e}\u{1e9e}", true),
1515 row("(?iu)(\u{df})", "\u{1e9e}", false),
1516 row("(?iu)(\u{df}\u{df})", "\u{1e9e}\u{1e9e}", true),
1517 row("(?iu)\u{df}|\u{df}", "\u{1e9e}", false),
1518 row("(?iu)\u{df}.", "\u{1e9e}x", false),
1519 row("(?iu)\u{df}\\.", "\u{1e9e}.", true),
1520 row("(?iu)\u{df}\\w", "\u{1e9e}x", false),
1521 row("(?iu)\u{df}{1,2}", "\u{1e9e}", false),
1522 row("(?iu)\u{df}\u{df}?", "\u{1e9e}", false),
1523 row("(?iu)\u{df}\u{df}?", "\u{1e9e}\u{1e9e}", false),
1524 row("(?iu)x\u{df}", "x\u{1e9e}", true),
1525 row("(?iu)\u{df}+x", "\u{1e9e}x", false),
1526 row("(?iu)\u{df}x", "\u{1e9e}x", true),
1527 row("(?iu)(?:\u{df})\u{df}", "\u{1e9e}\u{1e9e}", false),
1528 row("(?iu)(?:\u{df})", "\u{1e9e}", false),
1529 row("(?iu)\u{df}$", "\u{1e9e}", false),
1530 row("(?iu)^\u{df}", "\u{1e9e}", false),
1531 row("(?iu)\u{df}[a]", "\u{1e9e}a", false),
1532 row("(?iu)\u{df}\\x{DF}", "\u{1e9e}\u{1e9e}", true),
1533 row("(?iu)\\x{DF}", "\u{1e9e}", false),
1534 row("(?iu)\\Q\u{df}\\E", "\u{1e9e}", false),
1535 row("(?iu)\\Q\u{df}\u{df}\\E", "\u{1e9e}\u{1e9e}", true),
1536 row("(?iux)\u{df} \u{df}", "\u{1e9e}\u{1e9e}", true),
1537 row("(?iux)\u{df} #c\n\u{df}", "\u{1e9e}\u{1e9e}", true),
1538 row("(?iux)\u{df} #c\n", "\u{1e9e}", false),
1539 row("(?iu)\u{1e9e}", "\u{df}", true),
1540 row("(?iu)\u{1e9e}\u{1e9e}", "\u{df}\u{df}", true),
1541 ];
1542 check_matches(cases, true);
1543 }
1544
1545 #[test]
1548 fn matches_ends_flag_x_with_its_group_as_java_does() {
1549 let cases = [
1551 row("(?u)(?<n>.(?x))A(?i)I ", "&A\u{131} ", true),
1553 row("(?u)(?<n>.(?x))A(?i)I ", "&A\u{131}", false),
1554 row("(?u)(?<n>.(?x))A(?i)I", "&A\u{131}", true),
1555 row("(?u)(.(?x))A#", "xA#", true),
1556 row("(?u)(.(?x))A#", "xA", false),
1557 row("(?x)(.(?x))A#c\nB", "xAB", true),
1558 row("(?x:a )b", "ab", true),
1559 row("(?x:a )b", "a b", false),
1560 row("(?x:a)#b", "a#b", true),
1561 row("(?x:a)#b", "a", false),
1562 ];
1563 check_matches(cases, true);
1564 }
1565
1566 #[test]
1572 fn matches_reads_a_literal_caret_and_dash_in_a_class_as_java_does() {
1573 let cases = [
1575 row("(?x)[ ^a]", "^", true),
1577 row("(?x)[ ^a]", "a", true),
1578 row("(?x)[ ^a]", "b", false),
1579 row("(?ix)[ ^a]", "^", true),
1580 row("(?ix)[ ^a]", "A", true),
1581 row("(?ix)[ ^a]", "b", false),
1582 row("(?ix)[#c\n^a]", "^", true),
1583 row("(?ix)[#c\n^a]", "A", true),
1584 row("(?ix)[#c\n^a]", "b", false),
1585 row("(?x)[ ^-b]", "^", true),
1586 row("(?x)[ ^-b]", "a", true),
1587 row("(?x)[ ^-b]", "c", false),
1588 row("(?x)[a[ ^b]]", "^", true),
1589 row("(?x)[a[ ^b]]", "b", true),
1590 row("(?x)[a[ ^b]]", "c", false),
1591 row("(?x)[^ a]", "a", false),
1592 row("(?x)[^ a]", "b", true),
1593 row("(?x)[^ ^a]", "^", false),
1594 row("(?x)[^ ^a]", "b", true),
1595 row("[\\[^a]", "^", true),
1596 row("[\\[^a]", "b", false),
1597 row("\\[^a", "[a", false),
1599 row("\\[^a", "[^a", false),
1600 row("(?x)\\[ ^a", "[^a", false),
1601 row("[\\w-.]", "-", true),
1604 row("[\\w-.]", ".", true),
1605 row("[\\w-.]", "a", true),
1606 row("[\\w-.]", ",", false),
1607 row("[\\w-a]", "-", true),
1608 row("[\\w-a]", "a", true),
1609 row("[\\w-a]", "`", false),
1610 row("[\\s-.]", "-", true),
1611 row("[\\s-.]", ".", true),
1612 row("[\\s-.]", " ", true),
1613 row("[\\s-.]", ",", false),
1614 row("(?i)[a-[b]]", "-", true),
1615 row("(?i)[a-[b]]", "A", true),
1616 row("(?i)[a-[b]]", "B", true),
1617 row("(?i)[a-[b]]", "X", false),
1618 row("(?i)[a-[b]]", "[", false),
1619 row("[\\p{Lower}-z]", "-", true),
1620 row("[\\p{Lower}-z]", ".", false),
1621 row("[\\p{L}-z]", "-", true),
1622 row("[\\p{L}-z]", "\u{e9}", true),
1623 row("[\\p{L}-z]", ".", false),
1624 row("[\\w-\\d]", "-", true),
1625 row("[\\d-a]", "-", true),
1626 row("[\\d-a]", "a", true),
1627 row("[\\d-a]", "b", false),
1628 row("[a-b-c]", "-", true),
1629 row("[a-b-c]", "c", true),
1630 row("[a-b-c]", "d", false),
1631 row("(?i)[a-b-c]", "-", true),
1632 row("(?i)[a-b-c]", "C", true),
1633 row("(?i)[a-b-c]", "D", false),
1634 row("[a-c-e]", "-", true),
1635 row("[a-c-e]", "b", true),
1636 row("[a-c-e]", "d", false),
1637 row("[a-c-e]", "e", true),
1638 row("[-a]", "-", true),
1640 row("[^-a]", "-", false),
1641 row("[^-a]", "b", true),
1642 row("[a-]", "-", true),
1643 row("[+--]", ",", true),
1645 row("[+--]", "-", true),
1646 row("[+--]", ".", false),
1647 row("(?i)[+--]", "-", true),
1648 row("[--/]", ".", true),
1649 row("[--/]", "0", false),
1650 row("[!--]", ",", true),
1651 row("[!--]", ".", false),
1652 row("a-b", "a-b", true),
1654 row("(?i)a-b", "A-B", true),
1655 ];
1656 check_matches(cases, true);
1657 }
1658
1659 #[test]
1664 fn matches_reads_a_range_that_ends_past_x_flag_white_space_as_java_does() {
1665 let cases = [
1667 row("(?x)[+- ]]", ",", true),
1668 row("(?x)[+- ]]", "a", false),
1669 row("(?x)[+- ]]", "]", true),
1670 row("(?x)[+- ]]", "-", true),
1671 row("(?x)[+- [b]]", "X]", true),
1672 row("(?x)[+- [b]]", "b]", true),
1673 row("(?x)[+- [b]]", "c]", false),
1674 row("(?x)[+- [b]]", "-]", true),
1675 row("(?x)[+-#c\n]]", "a", false),
1676 row("(?x)[+-#c\n]]", ",", true),
1677 row("(?x)[+-#c\n]]", "]", true),
1678 row("(?x)[a-b- ]", "-", true),
1679 row("(?x)[a-b- ]", "b", true),
1680 row("(?x)[a-b- ]", "c", false),
1681 row("(?x)[a - c]", "b", true),
1682 row("(?x)[a - c]", "-", false),
1683 row("(?x)[a - c]", " ", false),
1684 row("(?x)[a-\n b]", "a", true),
1685 row("(?x)[a-\n b]", "b", true),
1686 row("(?x)[a-\n b]", "-", false),
1687 row("(?x)[a-#c\nb]", "b", true),
1688 row("(?x)[a-#c\nb]", "-", false),
1689 row("(?x)[a-]", "-", true),
1690 row("(?x)[a-[b]]", "-", true),
1691 row("(?x)[a-[b]]", "b", true),
1692 row("(?x)[a-[b]]", "c", false),
1693 ];
1694 check_matches(cases, true);
1695 }
1696
1697 #[yare::parameterized(
1700 range_ends_at_space = { "(?x)[.- ]" },
1701 range_ends_at_space_after_letter = { "(?x)[a- ]" },
1702 range_ends_at_newline = { "(?x)[a-\n]" },
1703 range_ends_at_newline_after_dot = { "(?x)[.-\n]" },
1704 nested_class_range_ends_at_space = { "(?x)[_- [b]]" },
1705 comment_then_range_ends_at_close = { "(?x)[a-#c\n]]" },
1706 )]
1707 fn extended_class_ranges_ending_after_whitespace_are_refused(source: &str) {
1708 check!(JavaPattern::compile(source).is_err(), "{source:?}");
1709 }
1710
1711 #[yare::parameterized(
1716 alternation_with_stray_close = { "a)|(b" },
1717 simple_stray_close = { "a)" },
1718 double_stray_close = { "(a))" },
1719 flag_group_then_stray_close = { "a(?i))" },
1720 lone_close = { ")" },
1721 extended_mode_stray_close = { "(?x)a )" },
1722 noncapturing_group_then_stray_close = { "(?:a))" },
1723 lookahead_then_stray_close = { "(?=a))" },
1724 scoped_flag_group_then_stray_close = { "(?i:a))" },
1725 reversed_character_range = { "[a--b]" },
1726 range_to_word = { "[a-\\w]" },
1727 range_to_digit = { "[a-\\d]" },
1728 range_to_digit_from_bang = { "[!-\\d]" },
1729 range_to_word_from_plus = { "[+-\\w]" },
1730 range_to_unicode_property = { "[a-\\p{L}]" },
1731 range_to_space = { "[a-\\s]" },
1732 range_to_nonword = { "[!-\\W]" },
1733 range_from_hyphen_to_word = { "[--\\w]" },
1734 case_insensitive_range_to_word = { "(?i)[a-\\w]" },
1735 extended_mode_range_to_word = { "(?x)[a- \\w]" },
1736 )]
1737 fn a_stray_closing_paren_or_a_range_to_a_shorthand_does_not_compile(source: &str) {
1738 check!(JavaPattern::compile(source).is_err(), "{source:?}");
1739 }
1740
1741 #[yare::parameterized(
1744 close_inside_class = { "[)]", ")", true },
1745 escaped_close = { "\\)", ")", true },
1746 quoted_close = { "\\Q)\\E", ")", true },
1747 close_inside_comment = { "(?x)a #c)\n", "a", true },
1748 close_in_alternative = { "(a)|(b)", "b", true },
1749 close_in_case_insensitive_alternative = { "(?i)(a)|b", "B", true },
1750 )]
1751 fn a_closing_paren_that_closes_no_group_is_still_a_character_where_java_reads_one(
1752 source: &str,
1753 input: &str,
1754 expected: bool,
1755 ) {
1756 assert_pattern_matches(source, input, expected);
1757 }
1758
1759 #[test]
1766 fn a_backreference_under_a_case_flag_compares_as_java_does() {
1767 let cases = [
1769 row("(?i)(\u{e9})\\1", "\u{e9}\u{c9}", false),
1771 row("(?i)(\u{e9})\\1", "\u{e9}\u{e9}", true),
1772 row("(?i)(\u{3c3})\\1", "\u{3c3}\u{3c2}", false),
1773 row("(?i)(a\u{e9})\\1", "a\u{e9}A\u{e9}", true),
1774 row("(?i)(a\u{e9})\\1", "A\u{e9}a\u{c9}", false),
1775 row("(?i)(k)\\1", "k\u{212a}", false),
1776 row("(?i)(s)\\1", "s\u{17f}", false),
1777 row("(?i)(i)\\1", "i\u{130}", false),
1778 row("(?i)(ab)\\1", "abAB", true),
1779 row("(?i)(\\w+)-\\1", "Ab-aB", true),
1780 row("(?i)(?<n>\u{e9})\\k<n>", "\u{e9}\u{c9}", false),
1781 row("(?iu)(i)\\1", "i\u{130}", true),
1783 row("(?iu)(i)\\1", "i\u{131}", true),
1784 row("(?iu)(\u{130})\\1", "\u{130}i", true),
1785 row("(?iu)(\u{130})\\1", "\u{130}I", true),
1786 row("(?iu)(\u{131})\\1", "\u{131}I", true),
1787 row("(?iu)(\u{130})\\1", "\u{130}\u{131}", true),
1788 row("(?iu)(s)\\1", "s\u{17f}", true),
1789 row("(?iu)(S)\\1", "S\u{17f}", true),
1790 row("(?iu)(k)\\1", "k\u{212a}", true),
1791 row("(?iu)(\u{df})\\1", "\u{df}\u{1e9e}", true),
1792 row("(?iu)(\u{e5})\\1", "\u{e5}\u{212b}", true),
1793 row("(?iu)(a)\\1", "aA", true),
1794 row("(?iu)(a)\\1", "ab", false),
1795 row("(?iu)(\u{e9})\\1", "\u{e9}\u{c9}", true),
1796 row("(?iu)(\u{3c3})\\1", "\u{3c3}\u{3c2}", true),
1797 row("(?iu)(\u{3c3})\\1", "\u{3c3}\u{3a3}", true),
1798 row("(?iu)(\u{1c6})\\1", "\u{1c6}\u{1c5}", true),
1799 row("(?iu)(\u{3b8})\\1", "\u{3b8}\u{3d1}", true),
1800 row("(?iu)(\u{3c9})\\1", "\u{3c9}\u{2126}", true),
1801 row("(?iu)(\u{b5})\\1", "\u{b5}\u{3bc}", true),
1802 row("(?iu)(\\p{L}+)\\1", "\u{e9}\u{c9}", true),
1803 row("(?iu)(?<n>i)\\k<n>", "i\u{130}", true),
1804 row("(?iU)(s)\\1", "s\u{17f}", true),
1805 row("(?iu)(ss)\\1", "ss\u{df}", false),
1807 row("(?iu)(\u{df})\\1", "\u{df}ss", false),
1808 row("(?iu)(\\w+)\\1", "sS", true),
1809 row("(?iu)(.+)\\1", "stra\u{df}eSTRA\u{1e9e}E", true),
1810 row("(?u)(?i:(s))\\1", "s\u{17f}", false),
1812 row("(?i)(?u:(s)\\1)", "s\u{17f}", true),
1813 row("(?iu)(s)(?-u)\\1", "s\u{17f}", false),
1814 row("(?iu)(s)(?-i)\\1", "s\u{17f}", false),
1815 row("(?i)(.)\\1", "\u{10400}\u{10428}", false),
1818 row("(?iu)(.)\\1", "\u{10400}\u{10428}", true),
1819 row("(?iu)(.)\\1", "\u{10400}\u{10400}", true),
1820 row("(?iu)(.)\\1x", "\u{10400}\u{10428}x", true),
1821 row("(?iu)(.)\\1.", "\u{10400}\u{10400}\u{10400}", true),
1822 row("(?iu)(.)\\1.", "\u{10400}\u{10400}b", true),
1823 row("(?iu)(.)(.)\\1", "\u{10400}\u{10400}\u{10400}", true),
1824 row("(?iu)(.)\\1\\1", "\u{10400}\u{10400}\u{10400}", true),
1825 row("(?iu)(.)\\1", "\u{10400}a", false),
1826 row("(?iu)(a)\\1", "a\u{10400}", false),
1827 row("(?iu)(.\u{10428})\\1", "a\u{10400}A\u{10400}", true),
1828 ];
1829 check_matches(cases, true);
1830 }
1831
1832 #[test]
1835 fn replace_compares_a_backreference_under_a_case_flag_as_java_does() {
1836 let cases = [
1838 replace_row(
1839 "(?i)(\\w)\\1",
1840 "aAbB\u{e9}\u{c9}",
1841 "<$1>",
1842 "<a><b>\u{e9}\u{c9}",
1843 ),
1844 replace_row("(?iu)(\\w)\\1", "sS", "<$1>", "<s>"),
1845 replace_row("(?iu)(.)\\1", "i\u{130}s\u{17f}", "<$1>", "<i><s>"),
1846 replace_row(
1847 "(?iu)CN=(\\w+),OU=\\1",
1848 "CN=kim,OU=KIM",
1849 "$0",
1850 "CN=kim,OU=KIM",
1851 ),
1852 replace_row("(?iu)CN=(.+),OU=\\1", "CN=\u{131},OU=I", "$1", "\u{131}"),
1853 replace_row(
1854 "(?i)CN=(.+),OU=\\1",
1855 "CN=\u{131},OU=I",
1856 "$1",
1857 "CN=\u{131},OU=I",
1858 ),
1859 replace_row("(?i)\\G(a)\\1", "aAaAbaA", "x", "xxbaA"),
1860 ];
1861 check_replacements(cases, true, true);
1862 }
1863
1864 #[test]
1869 fn a_pattern_with_a_folded_backreference_reads_the_rest_as_java_does() {
1870 let cases = [
1872 row("(?i)(a|ab)(c|bcd)\\1(d*)", "abcdAd", true),
1873 row("(?i)(a|ab)(c|bcd)\\1(d*)", "abcdAbd", false),
1874 row("(?i)(a+?)\\1", "aaAA", true),
1875 row("(?i)(a+)a\\1", "aaA", true),
1876 row("(?i)(a++)a\\1", "aaA", false),
1877 row("(?i)(?>(a+))a\\1", "aaA", false),
1878 row("(?i)(a)(?=b)\\w\\1", "abA", true),
1879 row("(?i)(a)(?!b)\\w\\1", "abA", false),
1880 row("(?i)(a)\\w(?<=b)\\1", "abA", true),
1881 row("(?i)(a)\\w(?<!b)\\1", "abA", false),
1882 row("(?i)(a){2,3}\\1", "aaA", true),
1883 row("(?i)(a){2,3}\\1", "aA", false),
1884 row("(?i)(a){2,3}\\1", "aaaaA", false),
1885 row("(?i)(a|)*\\1", "aa", true),
1886 row("(?i)((?=())|(?=())){2}\\2\\3", "", true),
1888 row("(?i)((?=())|(?=())){2}\\2\\3", "x", false),
1889 row("(?i)(a?){3}\\1", "aA", true),
1890 row("(?i)(a|()){2}\\2b", "ab", true),
1891 row("(?i)(?:(a)|b?){2}\\1", "bA", false),
1892 row("(?i)(x)?\\1", "", false),
1893 row("(?i)^(a)\\1$", "aA", true),
1894 row("(?i)(a)\\1\\z", "aA", true),
1895 row("(?i)(?m)(a)$\\n^\\1", "a\nA", true),
1896 row("(?i)(?s)(.)\\1", "\n\n", true),
1897 row("(?i)(.)\\1", "\n\n", false),
1898 row("(?iU)(\\w+)\\b\\1", "ab", false),
1899 row("(?iU)(\\w)\\B\\1", "aA", true),
1900 row("(?i)\\G(a)\\1", "aA", true),
1901 row("(?i)(a)\\R\\1", "a\r\nA", true),
1902 row("(?i)(a)\\R\\1", "a\rA", true),
1903 row("(?i)(a)\\R\\1", "a\n\nA", false),
1904 row("(?i)(a)\\R\\1", "a\u{2028}A", true),
1905 ];
1906 check_matches(cases, true);
1907 }
1908
1909 #[test]
1913 fn a_pattern_with_a_folded_backreference_takes_a_long_input_and_gives_up_at_the_limit() {
1914 let long = format!("{}A", "a".repeat(200_000));
1915 check!(pattern("(?i)(a)(a*)\\1").matches(&long).ok() == Some(true));
1916 check!(pattern("(?i)(a*)*b\\1").matches(&"a".repeat(64)).is_err());
1917 }
1918
1919 #[test]
1926 fn a_group_keeps_its_text_where_java_keeps_it() {
1927 let cases = [
1929 row("(?:(?=(a))ax|a)\\1", "aa", true),
1930 row("(?:(?!(a))|a)\\1", "aa", true),
1931 row("a(?:(?<=(a))x|)\\1", "aa", true),
1932 row("a(?:(?<!(a))|)\\1", "aa", true),
1933 row("(?:(?>(a))x|a)\\1", "aa", true),
1934 row("(?:(a)++x|a)\\1", "aa", true),
1935 row("(?:(a)?+x|a)\\1", "aa", true),
1936 row("(?:(?:(a))*+x|a)\\1", "aa", true),
1937 row("(?:(a){1,2}+x|a)\\1", "aa", true),
1938 row("(?:(a)x|a)\\1", "aa", false),
1939 row("(?:(?=(a))ax|a)\\1", "a", false),
1940 row("((?=())|(?=()))*\\2\\3", "", true),
1941 row("((?=())|(?=())){1,2}\\2\\3", "", true),
1942 row("(a|b\\1)+", "aba", true),
1943 row("(a|b\\1)+", "abb", false),
1944 row("(?i)(a|b\\1)+", "abA", true),
1945 row("(a\\1?)+", "aaa", true),
1946 row("(a\\1?){2}", "aaa", true),
1947 ];
1948 check_matches(cases, true);
1949 }
1950
1951 #[test]
1956 fn replace_sees_a_group_that_kept_its_text_as_java_does() {
1957 let cases = [
1959 replace_row("(?:(?=(b))bx|b)", "b", "[$1]", "[b]"),
1960 replace_row("(?:(?=(b))bx|b)", "ab", "[$1]", "a[b]"),
1961 replace_row("(?=(a)c)|b", "ab", "[$1]", "a[]"),
1962 replace_row("(?:(?=(a))ax|b)", "ab", "[$1]", "a[a]"),
1963 replace_row("(?:(?!(a))|b)", "ab", "[$1]", "a[a]b[]"),
1964 replace_row("\\R", "a\r\nb", "|", "a|b"),
1965 replace_row("\\R\\n", "a\r\nb", "|", "a|b"),
1966 ];
1967 check_replacements(cases, true, true);
1968 }
1969
1970 #[test]
1977 fn a_linebreak_gives_back_its_newline_where_java_does() {
1978 let cases = [
1980 row("(a)\\R\\n\\1", "a\r\na", true),
1981 row("\\R", "\r", true),
1982 row("\\R\\R", "\r\n", true),
1983 row("\\R", "\r\n", true),
1984 row("\\R", "\n\r", false),
1985 row("\\R", "\u{2028}", true),
1986 row("\\R", "\u{85}", true),
1987 row("(?-u)\\R", "\u{2028}", true),
1988 row("\\R\\n", "\r\n", true),
1989 row("(?>\\R)\\n", "\r\n", false),
1990 row("\\R{2}", "\r\n", false),
1991 row("\\R?\\n", "\r\n", false),
1992 row("x\\R+\\n", "x\r\n\r\n", false),
1993 row("(\\R)?\\n", "\r\n", true),
1994 row("(?:a\\R){2}\\n", "a\r\na\r\n", false),
1995 row("(?:a\\R|b){2}\\n", "a\r\na\r\n", true),
1996 row("(?=\\R\\n)\\R", "\r\n", true),
1997 row("(?:\\R)?\\n", "\r\n", true),
1998 row("(?:\\R)??\\n", "\r\n", true),
1999 row("(?:\\R){0,1}\\n", "\r\n", true),
2000 row("(?:(?:\\R))?\\n", "\r\n", true),
2001 row("(?x)(?:\\R) ?\\n", "\r\n", true),
2002 row("(?x)\\R ?\\n", "\r\n", false),
2003 row("(?x)\\R #c\n?\\n", "\r\n", false),
2004 row("(?:\\R)?+\\n", "\r\n", false),
2005 row("(?:\\R)*\\n", "\r\n", false),
2006 row("(?:\\R)+\\n", "\r\n", false),
2007 row("(?:\\R?)?\\n", "\r\n", false),
2008 row("a(?:\\R)?\\n\\R?", "a\r\n\r\n", true),
2009 ];
2010 check_matches(cases, true);
2011 }
2012
2013 #[test]
2021 fn anchors_match_at_line_terminators_as_java_does() {
2022 let cases = [
2024 row("a$\\n", "a\n", true),
2025 row("a$\\r\\n", "a\r\n", true),
2026 row("a\\r$\\n", "a\r\n", false),
2027 row("a$\u{2028}", "a\u{2028}", true),
2028 row("a$\\u0085", "a\u{85}", true),
2029 row("a$\\r", "a\r", true),
2030 row("a$\\nb", "a\nb", false),
2031 row("a\\Z\\n", "a\n", true),
2032 row("a\\Z\\n\\n", "a\n\n", false),
2033 row("a$\\n\\n", "a\n\n", false),
2034 row("(?d)a$\\r", "a\r", false),
2035 row("(?d)a$\\n", "a\n", true),
2036 row("(?d)a\\Z\\n", "a\n", true),
2037 row("(?m)a$\\r\\nb", "a\r\nb", true),
2038 row("(?m)a\\r$\\nb", "a\r\nb", false),
2039 row("(?m)a$\u{2028}b", "a\u{2028}b", true),
2040 row("(?m)a$\\rb", "a\rb", true),
2041 row("(?md)a$\\rb", "a\rb", false),
2042 row("(?md)a$\\nb", "a\nb", true),
2043 row("(?m)a\\n^b", "a\nb", true),
2044 row("(?m)a\\r^b", "a\rb", true),
2045 row("(?m)a\\r^\\nb", "a\r\nb", false),
2046 row("(?m)a\\n^", "a\n", false),
2047 row("(?m)a\\u0085^b", "a\u{85}b", true),
2048 row("(?md)a\\r^b", "a\rb", false),
2049 row("(?md)a\\n^b", "a\nb", true),
2050 row("(?m)^", "", false),
2051 row("^", "", true),
2052 row("$", "", true),
2053 row("(?m)$", "", true),
2054 row("a$?b", "ab", true),
2055 row("(?m:a$)\\n^b", "a\nb", false),
2056 row("(?m:x)a$\\nb", "a\nb", false),
2057 row("a$*b", "ab", true),
2058 row("a$+b", "ab", false),
2059 row("a$+", "a", true),
2060 row("a\\Z?b", "ab", true),
2061 row("(?m)a\\n^?b", "a\nb", true),
2062 row("(?m)a^+b", "ab", false),
2063 row("(?m)a\\n^+b", "a\nb", true),
2064 row("(?x)a$ ?b", "ab", true),
2065 ];
2066 check_matches(cases, true);
2067 }
2068
2069 #[test]
2072 fn replace_finds_anchors_at_line_terminators_as_java_does() {
2073 let cases = [
2075 replace_row("a$", "a\n", "X", "X\n"),
2076 replace_row("$", "a\n", "|", "a|\n|"),
2077 replace_row("(?m)$", "a\r\nb", "|", "a|\r\nb|"),
2078 replace_row("(?m)^", "a\r\nb", "|", "|a\r\n|b"),
2079 replace_row("(?m)^", "a\n", "|", "|a\n"),
2080 replace_row("\\Z", "a\r\n", "|", "a|\r\n|"),
2081 replace_row("\\h+", "a\u{a0}\u{3000}b", "_", "a_b"),
2082 ];
2083 check_replacements(cases, true, true);
2084 }
2085
2086 #[test]
2091 fn horizontal_and_vertical_whitespace_are_javas() {
2092 let cases = [
2094 row("\\h", "\u{a0}", true),
2095 row("\\h", "\u{3000}", true),
2096 row("\\h", "\u{2001}", true),
2097 row("\\h", "\n", false),
2098 row("\\H", "a", true),
2099 row("\\H", "\t", false),
2100 row("[\\h]", "\u{2000}", true),
2101 row("[\\H]", "\u{2000}", false),
2102 row("[\\h-a]", "-", true),
2103 row("(?U)\\h", "\u{a0}", true),
2104 row("\\v", "\n", true),
2105 row("\\v", "\u{2028}", true),
2106 row("\\v", "\u{c}", true),
2107 row("\\V", "\n", false),
2108 row("\\V", "a", true),
2109 row("[\\v]", "\r", true),
2110 row("[\\v-\\x0d]", "\u{c}", true),
2111 row("[\\v-]", "-", true),
2112 row("[\\v-]", "\n", false),
2113 row("[^\\h]", "a", true),
2114 ];
2115 check_matches(cases, true);
2116 }
2117
2118 #[yare::parameterized(
2121 star_after_unicode_flags = { "(?iu)*" },
2122 plus_after_case_flag = { "(?i)+a" },
2123 optional_after_dotall_flag = { "(?d)?" },
2124 star_after_extended_flags = { "(?ix) *" },
2125 )]
2126 fn a_star_plus_or_question_mark_after_inline_flags_is_refused(source: &str) {
2127 check!(JavaPattern::compile(source).is_err(), "{source:?}");
2128 }
2129
2130 #[yare::parameterized(
2133 named_character = { "\\N{LATIN SMALL LETTER A}" },
2134 case_insensitive_named_character = { "(?i)\\N{LATIN SMALL LETTER A}" },
2135 )]
2136 fn a_named_character_is_refused(source: &str) {
2137 check!(JavaPattern::compile(source).is_err(), "{source:?}");
2138 }
2139
2140 #[test]
2142 fn replace_all_expands_a_replacement_as_java_does() {
2143 let cases = [
2145 replace_row("(a)-(b)", "a-b", "$1_$2", "a_b"),
2147 replace_row("(a)", "a", "$1suffix", "asuffix"),
2148 replace_row("(a)", "a", "$10", "a0"),
2150 replace_row("(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)", "abcdefghij", "$10", "j"),
2151 replace_row("(a)", "a", "$0!", "a!"),
2152 replace_row("(?<who>a)", "a", "${who}-$1", "a-a"),
2153 replace_row("(a)", "a", "\\$1", "$1"),
2155 replace_row("a", "aa", "b", "bb"),
2156 replace_row("(a)|(b)", "b", "[$1]", "[]"),
2158 replace_row("x", "abc", "$9", "abc"),
2159 ];
2160 check_replacements(cases, true, true);
2161 }
2162
2163 #[yare::parameterized(
2165 group_number_out_of_range = { "(a)", "a", "$2" },
2166 group_number_without_groups = { "a", "a", "$1" },
2167 trailing_dollar = { "(a)", "a", "$" },
2168 nonnumeric_group_reference = { "(a)", "a", "$x" },
2169 trailing_backslash = { "(a)", "a", "\\" },
2170 unknown_named_group = { "(a)", "a", "${who}" },
2171 empty_named_group = { "(a)", "a", "${}" },
2172 unclosed_named_group = { "(a)", "a", "${who" },
2173 group_number_on_the_matcher_alone = { "(?<=\\R?x)y", "xy", "$1" },
2174 group_number_past_a_large_repeat = { "(?<n>x){1,1000000000}", "x", "$2" },
2175 unknown_named_group_past_a_large_repeat = { "(?<n>x){1,1000000000}", "x", "${m}" },
2176 )]
2177 fn replace_all_refuses_what_java_throws_on(source: &str, input: &str, replacement: &str) {
2178 check!(
2179 pattern(source).replace(input, replacement, true).is_err(),
2180 "{source} on {input} with {replacement}"
2181 );
2182 }
2183
2184 #[test]
2185 fn replace_first_stops_after_one_match() {
2186 let result = pattern("a").replace("aaa", "b", false);
2187 assert!(result.ok().as_deref() == Some("baa"));
2188 }
2189
2190 #[yare::parameterized(
2191 python_named_group = { "(?P<x>a)" },
2192 unclosed_character_class = { "[a-" },
2193 unclosed_group = { "(a" },
2194 )]
2195 fn a_pattern_java_refuses_does_not_compile(source: &str) {
2196 check!(JavaPattern::compile(source).is_err(), "{source}");
2197 }
2198
2199 #[test]
2207 fn a_pattern_java_refuses_where_fancy_regex_takes_it_does_not_compile() {
2208 let sources = [
2209 "(?<a>x)(?<a>y)",
2210 "(?<a>x)|(?<a>y)",
2211 "\\k<1>",
2212 "(x)\\k<1>",
2213 "\\k<a>(?<a>x)",
2214 "(?<a>x)\\k<b>",
2215 "(?<a>x)\\k<a",
2216 "(?<a>x)\\k<>",
2217 "(?<a>x)\\k",
2218 "(?<_a>x)",
2219 "(?<a_b>x)",
2220 "a*?+",
2221 "a**",
2222 "a*+*",
2223 "a*??",
2224 "a+?+",
2225 "a?*",
2226 "a??+",
2227 "a{2}++",
2228 "a{2}*",
2229 "a{2}?+",
2230 "(?x)a*? +",
2231 "(?x)a* * ",
2232 "a*(?i)+",
2233 "*a",
2234 "(*a)",
2235 "a|*",
2236 "x{2,1}",
2237 "x{3,2}",
2238 "x{,2}",
2239 "x{2147483648}",
2240 "x{1,2147483648}",
2241 "[\\R]",
2242 "[\\X]",
2243 "[\\b]",
2244 "[\\B]",
2245 "[\\A]",
2246 "[\\G]",
2247 "[\\z]",
2248 "[\\Z]",
2249 "[\\k<a>]",
2250 "[\\1]",
2251 "(a)(?<=\\1)",
2252 "(?<=(a)\\1)b",
2253 "(?<=(?:ab)*)c",
2254 "(?<=(ab)*)c",
2255 "(?<!(a)\\1)b",
2256 "\\p{IsHyphen}",
2257 "\\p{Greek}",
2258 "\\p{lu}",
2259 "\\p{LU}",
2260 "\\p{isLu}",
2261 "\\p{Islu}",
2262 "\\p{IsOldItalic}",
2263 "\\p{IsOld Italic}",
2264 "\\p{sc=OldItalic}",
2265 "\\p{gc=lu}",
2266 "\\p{gc=Uppercase_Letter}",
2267 "\\p{Uppercase_Letter}",
2268 "\\p{Letter}",
2269 "\\p{Alphabetic}",
2270 "\\p{IsWhite Space}",
2271 "\\p{White_Space}",
2272 "\\p{Emoji}",
2273 "\\p{Any}",
2274 "\\p{IsAny}",
2275 "\\p{Ascii}",
2276 "\\p{alpha}",
2277 "\\p{Hex_Digit}",
2278 "\\p{Ideographic}",
2279 "\\p{Han}",
2280 "\\p{Common}",
2281 "\\p{Decimal_Number}",
2282 "\\p{Math}",
2283 "\\p{IsMath}",
2284 "\\p{Dash}",
2285 "\\p{IsDash}",
2286 "\\p{Cased}",
2287 "\\p{IsCased}",
2288 "\\pl",
2289 "\\pX",
2290 "\\p{L&}",
2291 "\\p{IsL&}",
2292 "(?i)\\p{lu}",
2293 "(?<=a{0,1073741824}b{0,1073741824})c",
2294 "(?<=(?:ab){0,1073741824})c",
2295 "(?<=(?:a|b){1,2})c",
2296 "(?<=(a|b)*)c",
2297 "(?<=\\R*)c",
2298 "(?<=\\R{0,1073741824})c",
2299 "(?<=(?>ab)*)c",
2300 ];
2301 for source in sources {
2302 check!(JavaPattern::compile(source).is_err(), "{source:?}");
2303 }
2304 }
2305
2306 #[test]
2314 fn a_pattern_next_to_one_java_refuses_compiles_and_matches_as_java_does() {
2315 let cases = [
2317 row("(?<a>x)(?<b>y)", "xy", true),
2318 row("(?<ab>x)(?<a>y)", "xy", true),
2319 row("(?<a1>x)\\k<a1>", "xx", true),
2320 row("(?<a>\\k<a>x)", "x", false),
2321 row("(?<a>x)\\k<a>?", "x", true),
2322 row("a{2}+", "aa", true),
2323 row("a{2}{3}", "aaaaaa", false),
2324 row("(?x)a* +", "aa", true),
2325 row("^*", "", true),
2326 row("$*", "", true),
2327 row("\\b*", "", true),
2328 row("x{1,1}", "x", true),
2329 row("x{0,0}", "", true),
2330 row("[\\h]", " ", true),
2331 row("[\\v]", "\u{b}", true),
2332 row("[\\p{L}]", "a", true),
2333 row("(?<=a*)b", "b", true),
2334 row("(?<=a+)b", "ab", false),
2335 row("(?<=a{2,})b", "aab", false),
2336 row("(?<=a{0,1000})b", "b", true),
2337 row("(?<=.*)b", "b", true),
2338 row("(?<=a|bc)d", "bcd", false),
2339 row("(?<=(?:ab){1,2})c", "abc", false),
2340 row("(?<=\\b)a", "a", true),
2341 row("(?<=(?=a*)a)b", "ab", false),
2342 row("(?<=(?!a*)x)b", "xb", false),
2343 row("(?<!a*)b", "b", false),
2344 row("(?<=a*?)b", "b", true),
2345 row("(?<=a?)b", "b", true),
2346 row("(?<=\\w+)b", "ab", false),
2347 row("(?<=\\Ga*)b", "b", true),
2348 row("\\p{Lu}", "A", true),
2349 row("\\p{IsLu}", "A", true),
2350 row("\\p{IsLatin}", "a", true),
2351 row("\\p{Islatin}", "a", true),
2352 row("\\p{IsLATIN}", "a", true),
2353 row("\\p{IsLatn}", "a", true),
2354 row("\\p{Isold_italic}", "a", false),
2355 row("\\p{IsOld_Italic}", "\u{10300}", true),
2356 row("\\p{sc=Latin}", "a", true),
2357 row("\\p{SC=Latin}", "a", true),
2358 row("\\p{sc=latin}", "a", true),
2359 row("\\p{sc=Latn}", "a", true),
2360 row("\\p{Script=Latin}", "a", true),
2361 row("\\p{gc=Lu}", "A", true),
2362 row("\\p{general_category=Lu}", "A", true),
2363 row("\\p{gc=L}", "A", true),
2364 row("\\p{gc=LC}", "A", true),
2365 row("\\p{L}", "A", true),
2366 row("\\p{IsL}", "A", true),
2367 row("\\p{IsLetter}", "A", true),
2368 row("\\p{Isletter}", "A", true),
2369 row("\\p{IsAlphabetic}", "A", true),
2370 row("\\p{IsWhite_Space}", " ", true),
2371 row("\\p{IsWhiteSpace}", " ", true),
2372 row("\\p{IsEmoji}", "\u{1f600}", true),
2373 row("\\p{IsAssigned}", "a", true),
2374 row("\\p{ASCII}", "a", true),
2375 row("\\p{IsASCII}", "a", true),
2376 row("\\p{Alpha}", "a", true),
2377 row("(?U)\\p{alpha}", "a", true),
2378 row("(?U)\\p{ALPHA}", "a", true),
2379 row("\\p{IsAlpha}", "a", true),
2380 row("\\p{Isalpha}", "a", true),
2381 row("\\p{IsDigit}", "1", true),
2382 row("\\p{IsPunct}", "!", true),
2383 row("\\p{IsSpace}", " ", true),
2384 row("\\p{IsCntrl}", "\u{1}", true),
2385 row("\\p{IsLower}", "a", true),
2386 row("\\p{IsUpper}", "A", true),
2387 row("\\p{XDigit}", "F", true),
2388 row("\\p{IsHex_Digit}", "F", true),
2389 row("\\p{IsHexDigit}", "F", true),
2390 row("\\p{IsIdeographic}", "\u{4e00}", true),
2391 row("\\p{IsHan}", "\u{4e00}", true),
2392 row("\\p{IsHani}", "\u{4e00}", true),
2393 row("\\p{IsCommon}", "!", true),
2394 row("\\p{IsZyyy}", "!", true),
2395 row("\\p{IsInherited}", "\u{301}", true),
2396 row("\\p{Nd}", "1", true),
2397 row("\\p{IsNd}", "1", true),
2398 row("\\p{N}", "1", true),
2399 row("\\pL", "a", true),
2400 row("\\pN", "1", true),
2401 row("\\pZ", " ", true),
2402 row("\\pS", "+", true),
2403 row("\\pC", "\u{1}", true),
2404 row("\\pM", "\u{301}", true),
2405 row("\\pP", "!", true),
2406 row("\\p{Ll}", "a", true),
2407 row("\\p{Lo}", "\u{4e00}", true),
2408 row("\\p{Cn}", "\u{378}", true),
2409 row("\\p{Co}", "\u{e000}", true),
2410 row("\\p{Cs}", "a", false),
2411 row("\\p{Lm}", "\u{2b0}", true),
2412 row("(?<=ab*)c", "ac", false),
2413 row("(?<=a*b)c", "bc", false),
2414 row("(?<=(?:a){1,2})c", "ac", false),
2415 row("(?<=(?:a)*)c", "c", true),
2416 row("(?<=(a)*)c", "c", true),
2417 row("(?<=a|b*)c", "c", true),
2418 row("(?<=(?=b*)a*)c", "c", true),
2419 row("(?<=(?<=a*)b*)c", "c", true),
2420 row("(?<=[ab]*)c", "c", true),
2421 row("(?<=\\x{1F600}*)c", "c", true),
2422 row("(?<=(?i)a*)c", "c", true),
2423 row("(?<=(?iu)\\u00df*)c", "c", true),
2424 row("(?<=a*?b)c", "bc", false),
2425 row("(?<=a??b*)c", "c", true),
2426 row("(?<=a?b?)c", "c", true),
2427 row("(?<=(ab)?)c", "c", true),
2428 ];
2429 check_matches(cases, true);
2430 }
2431
2432 #[test]
2439 fn a_class_intersection_with_an_empty_side_is_the_set_java_builds() {
2440 let cases = [
2442 row("[a-z&&]", "a", true),
2443 row("[a-z&&]", "A", false),
2444 row("[&&a]", "a", true),
2445 row("[&&a]", "b", false),
2446 row("[a&&&b]", "a", true),
2447 row("[a&&&b]", "b", true),
2448 row("[a&&&b]", "&", true),
2449 row("[a&&]", "a", true),
2450 row("[a&&]", "b", false),
2451 row("[^a&&]", "a", false),
2452 row("[^a&&]", "b", true),
2453 row("[^&&a]", "b", true),
2454 row("[a&&&&b]", "a", false),
2455 row("[a&&[b]&&]", "b", false),
2456 row("[a&&[b]&&]", "a", false),
2457 row("[[a]&&]", "a", true),
2458 row("[a&& ]", "a", false),
2459 row("[a&& ]", " ", false),
2460 row("(?x)[a&& ]", "a", true),
2461 row("(?x)[a&& b]", "a", false),
2462 row("[a-c&&&&b]", "b", true),
2463 row("[a-c&&&&b]", "a", false),
2464 row("[a&&\\w]", "a", true),
2465 row("[\\w&&]", "a", true),
2466 row("[&&[a]]", "a", true),
2467 row("[b&&[a]]", "a", false),
2468 row("[a[b]&&]", "b", true),
2469 row("[a[b]&&c]", "b", false),
2470 row("[a&&b[c]]", "c", false),
2471 row("[ab-c&&]", "a", false),
2472 row("[ab-c&&]", "b", true),
2473 row("[a&&[b]&c]", "a", true),
2474 row("[a&&[b]&c]", "b", false),
2475 row("[a&&[b]&c]", "c", true),
2476 row("[a&&[b]&c]", "&", true),
2477 row("[a&&b&&c]", "a", false),
2478 row("[a-c&&b-d&&c]", "c", true),
2479 row("[a-c&&b-d&&c]", "b", false),
2480 row("[^a-c&&b]", "b", false),
2481 row("[^a-c&&b]", "a", true),
2482 row("[^a-c&&]", "d", true),
2483 row("[^a-c&&]", "a", false),
2484 row("(?i)[a-c&&]", "B", true),
2485 row("(?i)[a&&]", "A", true),
2486 row("(?i)[A&&&b]", "B", true),
2487 row("(?iu)[k&&]", "\u{212a}", true),
2488 row("(?iu)[k&&&a]", "a", true),
2489 row("[\\w&&\\d]", "5", true),
2490 row("[\\w&&\\d]", "a", false),
2491 row("[\\d&&]", "5", true),
2492 row("[a\\d&&]", "a", false),
2493 row("[a\\d&&]", "5", true),
2494 row("[[a-c]&&]", "b", true),
2495 row("[x[a-c]&&]", "x", false),
2496 row("[x[a-c]&&]", "b", true),
2497 row("[a-z&&[^aeiou]]", "b", true),
2498 row("[a-z&&[^aeiou]]", "a", false),
2499 row("[a&&[b]c]", "c", false),
2500 row("[ab&&b]", "b", true),
2501 row("[ab&&b]", "a", false),
2502 row("[\u{100}&&]", "\u{100}", true),
2503 row("[a\u{100}&&]", "a", false),
2504 row("[a&&&&b]", "b", false),
2505 row("[a-c&&&&b]", "c", false),
2506 row("[&&a-c]", "b", true),
2507 row("[&&[a]&&]", "a", true),
2508 row("[a&&]]", "]", false),
2509 row("[]&&]]", "]", false),
2510 row("[]a&&]", "a", true),
2511 ];
2512 check_matches(cases, true);
2513 }
2514
2515 #[yare::parameterized(
2519 both_sides_empty = { "[&&]" },
2520 both_sides_empty_then_ampersand = { "[&&&a]" },
2521 empty_side_after_a_single_char = { "[a-cd&&]" },
2522 empty_side_after_a_single_char_after_a_class = { "[[a-c]x&&]" },
2523 empty_nested_class = { "[a&&[]]" },
2524 )]
2525 fn a_class_intersection_java_refuses_does_not_compile(source: &str) {
2526 check!(JavaPattern::compile(source).is_err(), "{source:?}");
2527 }
2528
2529 #[test]
2534 fn replace_sees_the_groups_of_an_empty_last_iteration_as_java_does() {
2535 let cases = [
2537 replace_row("(a*)*", "aa", "[$1]", "[][]"),
2538 replace_row("(a|)*", "aa", "[$1]", "[][]"),
2539 replace_row("(a*)+", "aa", "[$1]", "[][]"),
2540 replace_row("(a*){2,}", "aa", "[$1]", "[][]"),
2541 replace_row("(a*){1,3}", "aa", "[$1]", "[][]"),
2542 replace_row("(a*){0,5}", "aaa", "[$1]", "[][]"),
2543 replace_row("(a*)*?", "aa", "[$1]", "[]a[]a[]"),
2544 replace_row("(a*)*+", "aa", "[$1]", "[][]"),
2545 replace_row("(a*)+?b", "aab", "[$1]", "[aa]"),
2546 replace_row("(?:(a)|b|)*", "ab", "[$1]", "[a][]"),
2547 replace_row("(?:(a)|)*", "aa", "[$1]", "[a][]"),
2548 replace_row("(?:(a)?)*", "aa", "[$1]", "[a][]"),
2549 replace_row("(?:b|(a?))*", "ab", "[$1]", "[][]"),
2550 replace_row("((a)|)*", "aa", "[$1|$2]", "[|a][|]"),
2551 replace_row("(a?)*", "aa", "[$1]", "[][]"),
2552 replace_row("(a?)+", "a", "[$1]", "[][]"),
2553 replace_row("(a??)*", "aa", "[$1]", "[]a[]a[]"),
2554 replace_row("()*", "a", "[$1]", "[]a[]"),
2555 replace_row("(\\b)*", "a", "[$1]", "[]a[]"),
2556 replace_row("(a{0})*", "a", "[$1]", "[]a[]"),
2557 replace_row("(?:a(b*))*", "abab", "[$1]", "[b][]"),
2558 replace_row("(?:a(b*))*", "aba", "[$1]", "[][]"),
2559 replace_row("(?:(a*)b)*", "abb", "[$1]", "[][]"),
2560 replace_row("((a*)*)*", "aa", "[$1|$2]", "[|][|]"),
2561 replace_row("(a*)*x", "aax", "[$1]", "[]"),
2562 replace_row("(a|b*)*c", "abc", "[$1]", "[]"),
2563 ];
2564 check_replacements(cases, true, true);
2565 }
2566
2567 #[test]
2570 fn a_reference_sees_the_group_of_an_empty_last_iteration_as_java_does() {
2571 let cases = [
2573 row("(a*)*\\1", "aa", true),
2574 row("(a*)+\\1", "aa", true),
2575 row("(a|)*\\1", "aa", true),
2576 row("(?:(a)|)*\\1", "aa", true),
2577 row("(a*)*b\\1", "aab", true),
2578 row("(a*)*b\\1", "aabaa", true),
2579 row("(a|)+b\\1", "ab", true),
2580 row("(a|)+b\\1", "aba", true),
2581 row("()*+\\1", "", true),
2582 row("(){1,}\\1", "", true),
2583 row("()+\\1", "", true),
2584 row("(()|a)*\\2", "aa", true),
2585 row("(?:())*\\1", "", true),
2586 row("(())*\\2", "", true),
2587 ];
2588 check_matches(cases, true);
2589 }
2590
2591 #[test]
2596 fn last_match_anchor_stays_where_the_last_match_ended() {
2597 let cases = [
2599 replace_row("\\G", "aa", "-", "-aa"),
2600 replace_row("\\Ga", "aaba", "-", "--ba"),
2601 replace_row("\\G|a", "aa", "-", "-a--"),
2602 replace_row("\\G|b", "ab", "-", "-a--"),
2603 replace_row("a|\\G", "baa", "-", "-b---"),
2604 replace_row("(?<=\\G.)", "abc", "-", "a-b-c-"),
2605 replace_row("(?=\\G)", "aa", "-", "-aa"),
2606 replace_row("x*\\G", "aa", "-", "-aa"),
2607 replace_row("\\G(a|)", "aab", "[$1]", "[a][a][]b"),
2608 replace_row("a*?\\G", "aa", "-", "-aa"),
2609 ];
2610 check_replacements(cases, true, true);
2611 }
2612
2613 #[yare::parameterized(
2618 two_greedy_repeats = { "(?<=a*b*)c" },
2620 two_greedy_repeats_negated = { "(?<!a*b*)c" },
2622 two_greedy_repeats_alone = { "(?<=a*b*)" },
2624 wrapped_below_the_minimum = { "(?<=a*b*cde)f" },
2626 wrapped_to_zero = { "(?<=a*b*cd)f" },
2628 wrapped_to_minus_one = { "(?<=a*b*c)f" },
2630 four_greedy_repeats = { "(?<=a*b*c*d*)e" },
2632 two_greedy_classes = { "(?<=\\w*\\d*)x" },
2634 four_greedy_repeats_with_a_class = { "(?<=a*b*c*\\w*)d" },
2636 wrapped_to_zero_below_the_minimum = { "(?<=a*\\d*bc)d" },
2638 wrapped_branch = { "(?<=xy|a*b*cde)f" },
2640 supplementary_literal = { "(?<=a*bcπ)d" },
2642 supplementary_literal_wrapped_to_a_large_maximum = { "(?<=a*b*c*π)d" },
2644 )]
2645 fn a_look_behind_whose_maximum_wraps_is_refused_where_java_reads_it_otherwise(source: &str) {
2646 check!(
2647 JavaPattern::compile(source).err()
2648 == Some(PatternError(format!(
2649 "{source:?} {}",
2650 Refusal::MaximumOverflows.reason()
2651 )))
2652 );
2653 }
2654
2655 #[test]
2661 fn a_look_behind_whose_maximum_wraps_matches_as_java_does() {
2662 let cases = [
2664 replace_row("(?<=a*b*c*)d", "abcd", "<$0>", "abc<d>"),
2666 replace_row("(?<=a*b*c*)d", "xd", "<$0>", "x<d>"),
2667 replace_row("(?<!a*b*c*)d", "abcd", "<$0>", "abcd"),
2668 replace_row("(?<!a*b*c*)d", "d", "<$0>", "d"),
2669 replace_row("(?<=b|a*c*)d", "bd", "<$0>", "b<d>"),
2670 replace_row("(?<=x|a*b*)c", "c", "<$0>", "<c>"),
2671 replace_row("(?<=x|a*b*)c", "abc", "<$0>", "ab<c>"),
2672 replace_row("(?<=a*b*c*x)y", "abcxy", "<$0>", "abcx<y>"),
2674 replace_row("(?<=a*b*c*x)y", "xy", "<$0>", "x<y>"),
2675 replace_row("(?<=a*b*c*x)y", "y", "<$0>", "y"),
2676 replace_row("(?<=(?:a*b*c*)?x)y", "xy", "<$0>", "x<y>"),
2677 replace_row("(?<=a*b*c*\\x{1F600})d", "abcπd", "<$0>", "abcπ<d>"),
2680 replace_row("(?<=a*bc)d", "bcd", "<$0>", "bc<d>"),
2683 replace_row("(?<=a*bc)d", "d", "<$0>", "d"),
2684 replace_row("(?<!a*bc)d", "d", "<$0>", "<d>"),
2685 replace_row("(?<!a*bc)d", "abcd", "<$0>", "abcd"),
2686 replace_row("(?<!a*bc)d", "bdd", "<$0>", "b<d><d>"),
2687 replace_row("(?<=a*bc\\x{1F600})d", "bcπd", "<$0>", "bcπ<d>"),
2688 replace_row("(?<=a??b*)c", "c", "<$0>", "<c>"),
2691 replace_row("(?<=a*)c", "aac", "<$0>", "aa<c>"),
2693 ];
2694 check_replacements(cases, true, false);
2695 }
2696
2697 #[test]
2705 fn a_look_behind_over_a_char_past_u_ffff_matches_as_java_does() {
2706 let cases = [
2707 replace_row(".(?<=.)x", "πx", "<$0>", "<πx>"),
2708 replace_row(".(?<=.)x", "aπx", "<$0>", "a<πx>"),
2709 replace_row("a.(?<=a.)x", "aπx", "<$0>", "aπx"),
2710 replace_row("(?<=..)x", "aπx", "<$0>", "aπx"),
2711 replace_row("(?<=.{1,2})x", "aπx", "<$0>", "aπ<x>"),
2712 replace_row(".(?<=[^a])x", "aπx", "<$0>", "a<πx>"),
2713 replace_row(".(?<=\\p{So})x", "aπx", "<$0>", "aπx"),
2714 replace_row("(?<=\\x{1F600})x", "πx", "<$0>", "πx"),
2715 replace_row("(?<!.)x", "πx", "<$0>", "πx"),
2716 replace_row("(?<!\\p{So})x", "πx", "<$0>", "π<x>"),
2717 replace_row("(?<=^.)x", "πx", "<$0>", "πx"),
2718 replace_row("(?<=\\b.)x", "aπx", "<$0>", "aπx"),
2719 replace_row("(?<=π)x", "aπx", "<$0>", "aπ<x>"),
2721 replace_row("(?<=\\p{So})xπ?", "πx", "<$0>", "π<x>"),
2722 replace_row("(?<=..)x", "abx", "<$0>", "ab<x>"),
2724 ];
2725 check_replacements(cases, true, false);
2726 }
2727
2728 #[test]
2732 fn a_group_holding_half_a_char_is_refused_in_a_replacement() {
2733 let source = ".(?<=(.))x";
2734 check!(pattern(source).replace("πx", "<$0>", true).ok().as_deref() == Some("<πx>"));
2735 check!(pattern(source).replace("πx", "<$1>", true).is_err());
2736 }
2737
2738 #[test]
2742 fn a_wrapped_look_behind_maximum_limits_the_input() {
2743 let cases = [
2745 ("(?<=a*b*c*x)y", Some(2_147_483_646)),
2746 ("(?<=(?:a*b*c*)?x)y", Some(2_147_483_646)),
2747 ("(?<=a*b*c*xy)z(?<=a*b*c*x)", Some(2_147_483_646)),
2748 ("(?<=a*b*c*d*x{1,1100000000})y", Some(1_099_999_996)),
2752 ("(?<=a*b*c*)d", None),
2753 ("(?<=a*bc)d", None),
2754 ("(?<=a*)c", None),
2755 ];
2756 for (source, expected) in cases {
2757 check!(pattern(source).0.input_limit == expected, "{source}");
2758 }
2759 }
2760
2761 #[test]
2764 fn an_input_past_the_limit_is_refused() {
2765 let cases = [
2767 (None, "abc", false),
2768 (Some(3), "abc", false),
2769 (Some(2), "abc", true),
2770 (Some(0), "", false),
2771 (Some(1), "Γ©", false),
2772 (Some(2), "π", false),
2773 (Some(1), "π", true),
2774 (Some(3), "aπ", false),
2775 (Some(2), "aπ", true),
2776 ];
2777 for (limit, input, refused) in cases {
2778 check!(
2779 check_input_length(limit, input).is_err() == refused,
2780 "{limit:?} {input}"
2781 );
2782 }
2783 }
2784
2785 #[test]
2791 fn a_look_behind_fancy_regex_cannot_build_matches_as_java_does() {
2792 let cases = [
2794 replace_row("(?<=\\R?x)y", "xy", "<$0>", "x<y>"),
2795 replace_row("(?<=\\R?x)y", "\nxy", "<$0>", "\nx<y>"),
2796 replace_row("(?<=\\R?x)y", "\r\nxy", "<$0>", "\r\nx<y>"),
2797 replace_row("(?<=\\R?x)y", "y", "<$0>", "y"),
2798 replace_row("(?<=\\R?x)y", "zy", "<$0>", "zy"),
2799 replace_row("(?<!\\R?x)y", "xy", "<$0>", "xy"),
2800 replace_row("(?<!\\R?x)y", "\nxy", "<$0>", "\nxy"),
2801 replace_row("(?<!\\R?x)y", "zy", "<$0>", "z<y>"),
2802 replace_row("(?<!\\R?x)y", "y", "<$0>", "<y>"),
2803 replace_row("(?<=\\Rx|y)z", "\nxz", "<$0>", "\nx<z>"),
2804 replace_row("(?<=\\Rx|y)z", "\r\nxz", "<$0>", "\r\nx<z>"),
2805 replace_row("(?<=\\Rx|y)z", "yz", "<$0>", "y<z>"),
2806 replace_row("(?<=\\Rx|y)z", "xz", "<$0>", "xz"),
2807 replace_row("(?<=\\R{1,2})x", "\r\nx", "<$0>", "\r\n<x>"),
2808 replace_row("(?<=\\R{1,2})x", "\n\nx", "<$0>", "\n\n<x>"),
2809 replace_row("(?<=\\R{1,2})x", "x", "<$0>", "x"),
2810 replace_row("(?<=a\\R?)b", "a\r\nb", "<$0>", "a\r\n<b>"),
2811 replace_row("(?<=a\\R?)b", "b", "<$0>", "b"),
2812 replace_row("(?<=(?=a)ab?)c", "ac", "<$0>", "a<c>"),
2813 replace_row("(?<=(?=a)ab?)c", "abc", "<$0>", "ab<c>"),
2814 replace_row("(?<=(?=a)ab?)c", "bc", "<$0>", "bc"),
2815 replace_row("(?<!(?=a)ab?)c", "abc", "<$0>", "abc"),
2816 replace_row("(?<!(?=a)ab?)c", "bc", "<$0>", "b<c>"),
2817 replace_row("(?<=a(?!b)c?)d", "acd", "<$0>", "ac<d>"),
2818 replace_row("(?<=a(?!b)c?)d", "abd", "<$0>", "abd"),
2819 replace_row("(?<=(?<=a)b?)c", "abc", "<$0>", "ab<c>"),
2820 replace_row("(?<=(?<=a)b?)c", "bc", "<$0>", "bc"),
2821 replace_row("(?<=(?>a|ab)b?)c", "ac", "<$0>", "a<c>"),
2823 replace_row("(?<=(?>a|ab)b?)c", "abc", "<$0>", "ab<c>"),
2824 replace_row("(?<=(?>a|ab)b?)c", "abbc", "<$0>", "abbc"),
2825 replace_row("(?<=(?>ab|a)b?)c", "abbc", "<$0>", "abb<c>"),
2826 replace_row("(?<=a{1,3}+b?)c", "aabc", "<$0>", "aab<c>"),
2827 replace_row("(?<=a{1,3}+b?)c", "bc", "<$0>", "bc"),
2828 replace_row("(?<=\\Gab?)c", "acac", "<$0>", "a<c>a<c>"),
2829 replace_row("(?<=(a)|bc)d", "ad", "[$1$0]", "a[ad]"),
2831 replace_row("(?<=(a)|bc)d", "bcd", "[$1$0]", "bc[d]"),
2832 replace_row("(?<=\\R?(x))(y)", "xy", "[$2$1]", "x[yx]"),
2833 replace_row("(?<=\\R?(?<n>x))y", "\nxy", "[${n}$0]", "\nx[xy]"),
2834 ];
2835 check_replacements(cases, true, true);
2836 }
2837
2838 #[test]
2844 fn a_counted_repeat_too_large_to_build_matches_as_java_does() {
2845 let cases = [
2847 row("x{1,1000000000}", "xxx", true),
2848 row("x{1,1000000000}", "x", true),
2849 row("x{1,1000000000}", "", false),
2850 row("x{1,1000000000}", "y", false),
2851 row("x{1000000000}", "xxx", false),
2852 row("x{2,1000000000}y", "xxy", true),
2853 row("x{2,1000000000}y", "xy", false),
2854 row("(?:ab){1,600000000}", "abab", true),
2855 row("(?:ab){1,600000000}", "aba", false),
2856 row("(x){1,1000000000}", "xxx", true),
2857 row("(?i)x{1,1000000000}", "XXX", true),
2858 ];
2859 check_matches(cases, false);
2860 }
2861
2862 #[test]
2866 fn a_look_behind_too_large_to_build_matches_as_java_does() {
2867 let cases = [
2869 replace_row("(?<=x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2870 replace_row("(?<=x{1,1000000000})y", "xxy", "<$0>", "xx<y>"),
2871 replace_row("(?<=x{1,1000000000})y", "y", "<$0>", "y"),
2872 replace_row("(?<!x{1,1000000000})y", "xy", "<$0>", "xy"),
2873 replace_row("(?<!x{1,1000000000})y", "y", "<$0>", "<y>"),
2874 replace_row("(?<=(?:ab){1,600000000})y", "aby", "<$0>", "ab<y>"),
2875 replace_row("(?<=(?:ab){1,600000000})y", "by", "<$0>", "by"),
2876 replace_row("(?<=a{0,2147483647}b)c", "abc", "<$0>", "ab<c>"),
2877 replace_row("(?<=a{0,2147483647}b)c", "bc", "<$0>", "b<c>"),
2878 replace_row("(?<=a{0,2147483647}b)c", "c", "<$0>", "c"),
2879 replace_row("(?<=a{1,2147483647})b", "ab", "<$0>", "a<b>"),
2880 replace_row("(?<=a{1,2147483647})b", "b", "<$0>", "b"),
2881 replace_row("(?<=[ab]{1,1500000000})c", "abc", "<$0>", "ab<c>"),
2882 replace_row("(?<=[ab]{1,1500000000})c", "c", "<$0>", "c"),
2883 replace_row("(?<=z|x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2884 replace_row("(?<=z|x{1,1000000000})y", "zy", "<$0>", "z<y>"),
2885 replace_row("(?<=z|x{1,1000000000})y", "y", "<$0>", "y"),
2886 replace_row("(?<=(?=x)x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2887 replace_row("(?<=(?=x)x{1,1000000000})y", "y", "<$0>", "y"),
2888 replace_row("(?<=(?i)x{1,1000000000})y", "Xy", "<$0>", "X<y>"),
2889 replace_row("(?<=x{1,1000000000})(y)", "xy", "[$1]", "x[y]"),
2890 replace_row("(?:ab){1,600000000}", "aba", "<$0>", "<ab>a"),
2891 replace_row("(x){1,1000000000}", "xxx", "[$1]", "[x]"),
2892 replace_row("(?<n>x){1,1000000000}", "xxx", "[${n}]", "[x]"),
2893 ];
2894 check_replacements(cases, true, true);
2895 }
2896
2897 #[yare::parameterized(
2902 empty_group = { "(){1000000000}" },
2903 in_an_alternation = { "(?:x{1,1000000000}|(){1000000000})" },
2904 empty_group_alone = { "(?:(){1000000000})+" },
2905 )]
2906 fn a_large_repeat_of_nothing_stops_at_the_step_limit(source: &str) {
2907 check!(pattern(source).matches("").is_err(), "{source:?}");
2908 }
2909
2910 #[test]
2915 fn a_long_input_against_a_large_repeat_gives_java_s_answer_or_an_error() {
2916 let xs = "x".repeat(3000);
2917 let xs_y = format!("{xs}y");
2918 let cases = [
2920 ("x{1,1000000000}", xs.clone(), true, format!("<{xs}>")),
2921 ("x{1,1000000000}y", xs.clone(), false, xs.clone()),
2922 (
2923 "(?<=x{1,1000000000})y",
2924 xs_y.clone(),
2925 false,
2926 format!("{xs}<y>"),
2927 ),
2928 ("(?<!x{1,1000000000})y", xs_y.clone(), false, xs_y.clone()),
2929 ];
2930 for (source, input, matches, replaced) in cases {
2931 let compiled = pattern(source);
2932 check!(
2933 compiled.matches(&input).ok() != Some(!matches),
2934 "{source} matches"
2935 );
2936 check!(
2937 compiled
2938 .replace(&input, "<$0>", true)
2939 .map_or(true, |out| out == replaced),
2940 "{source} replaceAll"
2941 );
2942 }
2943 }
2944
2945 #[yare::parameterized(
2950 alone = { "(a)\\2" },
2951 beside_a_look_behind_too_large_to_build = { "(?<=x{1,1000000000})y(a)\\2" },
2952 beside_a_look_behind_fancy_regex_cannot_build = { "(?<=\\R?x)y(a)\\2" },
2953 after_a_large_repeat = { "x{1,1000000000}(a)\\2" },
2954 )]
2955 fn a_backreference_to_a_missing_group_is_refused(source: &str) {
2956 check!(
2957 JavaPattern::compile(source).err()
2958 == Some(PatternError(
2959 "Error compiling regex: Invalid back reference to group 2".to_owned()
2960 ))
2961 );
2962 }
2963
2964 #[test]
2967 fn a_large_look_behind_under_the_size_limit_matches_as_java_does() {
2968 let cases = [
2970 replace_row("(?<=x{2,20000})y", "xxy", "<$0>", "xx<y>"),
2971 replace_row("(?<=x{2,20000})y", "xy", "<$0>", "xy"),
2972 ];
2973 check_replacements(cases, true, false);
2974 }
2975}