// spell-checker:disable #[cfg(test)] mod tests { use rustpython_sre_engine::{Request, State, StrDrive}; use rustpython_wtf8::Wtf8Buf; struct Pattern { #[expect(unused, reason = "Needed for automated script")] pattern: &'static str, code: &'static [u32], } impl Pattern { fn state<'a, S: StrDrive>(&self, string: S) -> (Request<'a, S>, State) { let req = Request::new(string, 0, usize::MAX, self.code, false); let state = State::default(); (req, state) } } #[test] fn gh_2427() { // pattern lookbehind = re.compile(r'(?x)++x') // START GENERATED by generate_tests.py #[rustfmt::skip] let p = Pattern { pattern: "(?>x)++x", code: &[14, 4, 0, 2, 4294967295, 28, 8, 1, 4294967295, 27, 4, 16, 120, 1, 1, 16, 120, 1] }; // END GENERATED let (req, mut state) = p.state("xxx"); assert!(!state.py_match(&req)); } #[test] fn possessive_repeat_keeps_last_capture() { use optional::Optioned; let single_code = &[17, 0, 24, 6, 0, 1, 16, 101, 1, 17, 1, 1]; let req = Request::new("eeea", 3, usize::MAX, single_code, false); let mut single_state = State::default(); assert!(single_state.py_match(&req)); assert_eq!( single_state.marks.get(0), (Optioned::some(3), Optioned::some(3)) ); // (e?){2,4}+a: the fourth successful iteration is empty, so group 1 // must retain its final empty span rather than the previous "e". #[rustfmt::skip] let optional = Pattern { pattern: "(e?){2,4}+a", code: &[14, 4, 0, 1, 5, 28, 14, 2, 4, 17, 0, 24, 6, 0, 1, 16, 101, 1, 17, 1, 1, 16, 97, 1], }; let (req, mut state) = optional.state("eeea"); assert!(state.py_match(&req)); assert_eq!(state.marks.get(0), (Optioned::some(3), Optioned::some(3))); // ((x)|y|z){3}+: group 1 is the final "z"; group 2 retains "x". #[rustfmt::skip] let alternation = Pattern { pattern: "((x)|y|z){3}+", code: &[14, 4, 0, 3, 3, 28, 28, 3, 3, 17, 0, 7, 9, 17, 2, 16, 120, 17, 3, 15, 12, 5, 16, 121, 15, 7, 5, 16, 122, 15, 2, 0, 17, 1, 1, 1], }; let (req, mut state) = alternation.state("xyz"); assert!(state.py_match(&req)); assert_eq!(state.marks.get(0), (Optioned::some(2), Optioned::some(3))); assert_eq!(state.marks.get(1), (Optioned::some(0), Optioned::some(1))); } #[test] fn bug_20998() { // pattern p = re.compile('[a-c]+', re.I) // START GENERATED by generate_tests.py #[rustfmt::skip] let p = Pattern { pattern: "[a-c]+", code: &[14, 4, 0, 1, 4294967295, 24, 10, 1, 4294967295, 39, 5, 22, 97, 99, 0, 1, 1] }; // END GENERATED let (mut req, mut state) = p.state("ABC"); req.match_all = true; assert!(state.py_match(&req)); assert_eq!(state.cursor.position, 3); } #[test] fn ascii_ignore_keeps_nonascii_range_literal() { // pattern p = re.compile(r'[\u0430-\u045f]', re.I | re.A) // // ASCII-only case folding must not discard an exact non-ASCII range: // U+0450 lies in the compiled U+0430..U+045F interval. #[rustfmt::skip] let p = Pattern { pattern: "[\\u0430-\\u045f]", code: &[14, 8, 4, 1, 1, 22, 1072, 1119, 0, 13, 5, 22, 1072, 1119, 0, 1], }; let (req, mut state) = p.state("\u{0450}"); assert!(state.py_match(&req)); let subject = Wtf8Buf::from("\u{0450}"); let (req, mut state) = p.state(subject.as_ref()); assert!(state.py_match(&req)); } #[test] fn bigcharset() { // pattern p = re.compile('[a-z]*', re.I) // START GENERATED by generate_tests.py #[rustfmt::skip] let p = Pattern { pattern: "[a-z]*", code: &[14, 4, 0, 0, 4294967295, 24, 97, 0, 4294967295, 39, 92, 10, 3, 33685760, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 33686018, 0, 0, 0, 134217726, 0, 0, 0, 0, 0, 131072, 0, 2147483648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1] }; // END GENERATED let (req, mut state) = p.state("x "); assert!(state.py_match(&req)); assert_eq!(state.cursor.position, 1); } #[test] fn search_nonascii() { #[allow(unused)] // pattern p = re.compile('\xe0+') // START GENERATED by generate_tests.py #[rustfmt::skip] let p = Pattern { pattern: "\u{e0}+", code: &[14, 4, 0, 1, 4294967295, 24, 6, 1, 4294967295, 16, 224, 1, 1] }; // END GENERATED } #[test] fn search_literal_prefix_longer_than_skip() { // The INFO block carries prefix_len=4 and prefix_skip=2, so the tail // match has to resume at the skip boundary rather than past the whole // prefix. // pattern p = re.compile('ab(cd)') // START GENERATED by generate_tests.py #[rustfmt::skip] let p = Pattern { pattern: "ab(cd)", code: &[14, 14, 1, 4, 4, 4, 2, 97, 98, 99, 100, 0, 0, 0, 0, 16, 97, 16, 98, 17, 0, 16, 99, 16, 100, 17, 1, 1] }; // END GENERATED let (req, mut state) = p.state("xabcdcd"); assert!(state.search(req)); assert_eq!(state.start, 1); assert_eq!(state.cursor.position, 5); } }