Skip to content

Commit 8bb77f8

Browse files
authored
Fix re.fullmatch POSSESSIVE_REPEAT (RustPython#7187)
1 parent 068e0a0 commit 8bb77f8

4 files changed

Lines changed: 26 additions & 6 deletions

File tree

Lib/test/test_re.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2537,7 +2537,6 @@ def test_possessive_quantifiers(self):
25372537
self.assertIsNone(re.match("^x{}+$", "xxx"))
25382538
self.assertTrue(re.match("^x{}+$", "x{}"))
25392539

2540-
@unittest.expectedFailure # TODO: RUSTPYTHON
25412540
def test_fullmatch_possessive_quantifiers(self):
25422541
self.assertTrue(re.fullmatch(r'a++', 'a'))
25432542
self.assertTrue(re.fullmatch(r'a*+', 'a'))
@@ -2590,7 +2589,6 @@ def test_atomic_grouping(self):
25902589
self.assertIsNone(re.match(r'(?>x)++x', 'xxx'))
25912590
self.assertIsNone(re.match(r'(?>x++)x', 'xxx'))
25922591

2593-
@unittest.expectedFailure # TODO: RUSTPYTHON
25942592
def test_fullmatch_atomic_grouping(self):
25952593
self.assertTrue(re.fullmatch(r'(?>a+)', 'a'))
25962594
self.assertTrue(re.fullmatch(r'(?>a*)', 'a'))
@@ -2629,7 +2627,6 @@ def test_findall_atomic_grouping(self):
26292627
self.assertEqual(re.findall(r'(?>(?:ab)?)', 'ababc'), ['ab', 'ab', '', ''])
26302628
self.assertEqual(re.findall(r'(?>(?:ab){1,3})', 'ababc'), ['abab'])
26312629

2632-
@unittest.expectedFailure # TODO: RUSTPYTHON
26332630
def test_bug_gh91616(self):
26342631
self.assertTrue(re.fullmatch(r'(?s:(?>.*?\.).*)\z', "a.txt")) # reproducer
26352632
self.assertTrue(re.fullmatch(r'(?s:(?=(?P<g0>.*?\.))(?P=g0).*)\z', "a.txt"))

crates/sre_engine/src/engine.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,10 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
470470
Jump::PossessiveRepeat1 => {
471471
let min_count = ctx.peek_code(req, 2) as isize;
472472
if ctx.count < min_count {
473-
break 'context ctx.next_offset(4, Jump::PossessiveRepeat2);
473+
// modified next.toplevel from inherited to false
474+
let mut next = ctx.next_offset(4, Jump::PossessiveRepeat2);
475+
next.toplevel = false;
476+
break 'context next;
474477
}
475478
// zero match protection
476479
ctx.cursor.position = usize::MAX;
@@ -494,7 +497,9 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
494497
{
495498
state.marks.push();
496499
ctx.cursor = state.cursor;
497-
break 'context ctx.next_offset(4, Jump::PossessiveRepeat4);
500+
let mut next = ctx.next_offset(4, Jump::PossessiveRepeat4);
501+
next.toplevel = false; // modified next.toplevel from inherited to false
502+
break 'context next;
498503
}
499504
ctx.cursor = state.cursor;
500505
ctx.skip_code_from(req, 1);
@@ -832,7 +837,9 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
832837
/* <ATOMIC_GROUP> <skip> pattern <SUCCESS> tail */
833838
SreOpcode::ATOMIC_GROUP => {
834839
state.cursor = ctx.cursor;
835-
break 'context ctx.next_offset(2, Jump::AtomicGroup1);
840+
let mut next_ctx = ctx.next_offset(2, Jump::AtomicGroup1);
841+
next_ctx.toplevel = false; // modified next.toplevel from inherited to false
842+
break 'context next_ctx;
836843
}
837844
/* <POSSESSIVE_REPEAT> <skip> <1=min> <2=max> pattern
838845
<SUCCESS> tail */

crates/sre_engine/tests/tests.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ fn test_possessive_quantifier() {
145145
assert!(state.py_match(&req));
146146
}
147147

148+
#[test]
149+
fn test_possessive_repeat_fullmatch() {
150+
// pattern p = re.compile("([0-9]++(?:\.[0-9]+)*+)", re.I )
151+
// [INFO, 4, 0, 1, 4294967295, MARK, 0, POSSESSIVE_REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, POSSESSIVE_REPEAT, 16, 0, MAXREPEAT, LITERAL, 46, REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, SUCCESS, MARK, 1, SUCCESS]
152+
// START GENERATED by generate_tests.py
153+
#[rustfmt::skip] let p = Pattern { pattern: "([0-9]++(?:\\.[0-9]+)*+)", code: &[14, 4, 0, 1, 4294967295, 17, 0, 29, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 28, 16, 0, 4294967295, 16, 46, 24, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 1, 17, 1, 1] };
154+
// END GENERATED
155+
let (mut req, mut state) = p.state("1.25.38");
156+
req.match_all = true;
157+
assert!(state.py_match(&req), "should match");
158+
}
159+
148160
#[test]
149161
fn test_possessive_atomic_group() {
150162
// pattern p = re.compile('(?>x)++x')

extra_tests/snippets/stdlib_re.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,7 @@
7575

7676
assert re.compile("(?:\w+(?:\s|/(?!>))*)*").match("a /bb />ccc").group() == "a /bb "
7777
assert re.compile("(?:(1)?)*").match("111").group() == "111"
78+
79+
# Test of fix re.fullmatch POSSESSIVE_REPEAT, issue #7183
80+
assert re.fullmatch(r"([0-9]++(?:\.[0-9]+)*+)", "1.25.38")
81+
assert re.fullmatch(r"([0-9]++(?:\.[0-9]+)*+)", "1.25.38").group(0) == "1.25.38"

0 commit comments

Comments
 (0)