Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Polishing.
  • Loading branch information
serhiy-storchaka committed Mar 19, 2022
commit 5066bd1221a83e549a97f2ffc6dcd56600bff0ce
7 changes: 6 additions & 1 deletion Doc/library/re.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ The special characters are:
only ``'<a>'``.

.. index::
single: {} (curly brackets); in regular expressions
single: *+; in regular expressions
single: ++; in regular expressions
single: ?+; in regular expressions

Comment thread
serhiy-storchaka marked this conversation as resolved.
``*+``, ``++``, ``?+``
Like the ``'*'``, ``'+'``, and ``'?'`` qualifiers, those where ``'+'`` is
Expand All @@ -172,6 +174,9 @@ The special characters are:

.. versionadded:: 3.11

.. index::
single: {} (curly brackets); in regular expressions

``{m}``
Specifies that exactly *m* copies of the previous RE should be matched; fewer
matches cause the entire RE not to match. For example, ``a{6}`` will match
Expand Down
23 changes: 9 additions & 14 deletions Lib/sre_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def _compile(code, pattern, flags):
if op is MAX_REPEAT:
emit(REPEAT_ONE)
elif op is POSSESSIVE_REPEAT:
emit(POSSESSIVE_ONE)
emit(POSSESSIVE_REPEAT_ONE)
else:
emit(MIN_REPEAT_ONE)
skip = _len(code); emit(0)
Expand All @@ -150,24 +150,19 @@ def _compile(code, pattern, flags):
_compile(code, av[2], flags)
emit(SUCCESS)
code[skip] = _len(code) - skip
elif op is POSSESSIVE_REPEAT:
emit(POSSESSIVE_REPEAT)
skip = _len(code); emit(0)
emit(av[0])
emit(av[1])
_compile(code, av[2], flags)
code[skip] = _len(code) - skip
emit(SUCCESS)
else:
emit(REPEAT)
if op is POSSESSIVE_REPEAT:
emit(POSSESSIVE_REPEAT)
else:
emit(REPEAT)
skip = _len(code); emit(0)
emit(av[0])
emit(av[1])
_compile(code, av[2], flags)
code[skip] = _len(code) - skip
# TODO: What if op is REPEAT, not MIN_REPEAT;
# Default of MIN_UNTIL may be wrong
if op is MAX_REPEAT:
if op is POSSESSIVE_REPEAT:
emit(SUCCESS)
elif op is MAX_REPEAT:
emit(MAX_UNTIL)
else:
emit(MIN_UNTIL)
Expand Down Expand Up @@ -733,7 +728,7 @@ def print_2(*args):
print_(FAILURE)
i += 1
elif op in (REPEAT, REPEAT_ONE, MIN_REPEAT_ONE,
POSSESSIVE_REPEAT, POSSESSIVE_ONE):
POSSESSIVE_REPEAT, POSSESSIVE_REPEAT_ONE):
skip, min, max = code[i: i+3]
if max == MAXREPEAT:
max = 'MAXREPEAT'
Expand Down
2 changes: 1 addition & 1 deletion Lib/sre_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def _makecodes(names):
MIN_REPEAT_ONE
ATOMIC_GROUP
POSSESSIVE_REPEAT
POSSESSIVE_ONE
POSSESSIVE_REPEAT_ONE

GROUPREF_IGNORE
IN_IGNORE
Expand Down
3 changes: 1 addition & 2 deletions Lib/sre_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,8 +854,7 @@ def _parse(source, state, verbose, nested, first=False):
if group is not None:
state.closegroup(group, p)
if atomic:
# TODO: Assert that group is always None in this
# case
assert group is None
subpatternappend((ATOMIC_GROUP, p))
else:
subpatternappend((SUBPATTERN, (group, add_flags, del_flags, p)))
Expand Down
10 changes: 3 additions & 7 deletions Lib/test/test_re.py
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,7 @@ def test_inline_flags(self):


def test_dollar_matches_twice(self):
"""Test that $ does not include \\n
r"""Test that $ does not include \n
$ matches the end of string, and just before the terminating \n"""
pattern = re.compile('$')
self.assertEqual(pattern.sub('#', 'a\nb\n'), 'a\nb#\n#')
Expand Down Expand Up @@ -2096,13 +2096,9 @@ def test_possessive_qualifiers(self):
self.assertEqual(re.match('(e?){2,4}+a', 'eeea').groups(),
('',))
self.assertEqual(re.match('()*+a', 'a').groups(), ('',))
self.assertEqual(re.search('x*+', 'axx').span(0), (0, 0))
self.assertEqual(re.search('x*+', 'axx').span(), (0, 0))
self.assertEqual(re.search('x++', 'axx').span(0), (1, 3))
self.assertEqual(re.search('x++', 'axx').span(), (1, 3))
self.assertEqual(re.match('a*+', 'xxx').span(0), (0, 0))
self.assertEqual(re.match('a*+', 'xxx').span(), (0, 0))
self.assertEqual(re.match('x*+', 'xxxa').span(0), (0, 3))
self.assertEqual(re.match('x*+', 'xxxa').span(), (0, 3))
self.assertIsNone(re.match('a++', 'xxx'))
self.assertIsNone(re.match(r"^(\w){1}+$", "abc"))
Expand All @@ -2124,7 +2120,7 @@ def test_possessive_qualifiers(self):

def test_atomic_grouping(self):
"""Test Atomic Grouping
test non-capturing groups of the form (?>...), which acts does
test non-capturing groups of the form (?>...), which does
not maintain any stack point created within the group once the
group is finished being evaluated."""
pattern1 = re.compile(r'a(?>bc|b)c')
Expand Down Expand Up @@ -2215,7 +2211,7 @@ def test_possesive_repeat_one(self):
LITERAL 97

0. INFO 4 0b0 0 1 (to 5)
5: POSSESSIVE_ONE 6 0 1 (to 12)
5: POSSESSIVE_REPEAT_ONE 6 0 1 (to 12)
9. LITERAL 0x61 ('a')
11. SUCCESS
12: SUCCESS
Expand Down
32 changes: 10 additions & 22 deletions Modules/_sre.c
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,7 @@ _validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups)

case SRE_OP_REPEAT_ONE:
case SRE_OP_MIN_REPEAT_ONE:
case SRE_OP_POSSESSIVE_ONE:
case SRE_OP_POSSESSIVE_REPEAT_ONE:
{
SRE_CODE min, max;
GET_SKIP;
Expand All @@ -1827,27 +1827,9 @@ _validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups)
break;

case SRE_OP_REPEAT:
{
SRE_CODE min, max;
GET_SKIP;
GET_ARG; min = arg;
GET_ARG; max = arg;
if (min > max)
FAIL;
if (max > SRE_MAXREPEAT)
FAIL;
if (!_validate_inner(code, code+skip-3, groups))
FAIL;
code += skip-3;
GET_OP;
if (op != SRE_OP_MAX_UNTIL && op != SRE_OP_MIN_UNTIL)
FAIL;
}
break;

case SRE_OP_POSSESSIVE_REPEAT:
{
SRE_CODE min, max;
SRE_CODE op1 = op, min, max;
GET_SKIP;
GET_ARG; min = arg;
GET_ARG; max = arg;
Expand All @@ -1859,8 +1841,14 @@ _validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups)
FAIL;
code += skip-3;
GET_OP;
if (op != SRE_OP_SUCCESS)
FAIL;
if (op1 == SRE_OP_POSSESSIVE_REPEAT) {
if (op != SRE_OP_SUCCESS)
FAIL;
}
else {
if (op != SRE_OP_MAX_UNTIL && op != SRE_OP_MIN_UNTIL)
FAIL;
}
}
break;

Expand Down
2 changes: 1 addition & 1 deletion Modules/sre_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
#define SRE_OP_MIN_REPEAT_ONE 27
#define SRE_OP_ATOMIC_GROUP 28
#define SRE_OP_POSSESSIVE_REPEAT 29
#define SRE_OP_POSSESSIVE_ONE 30
#define SRE_OP_POSSESSIVE_REPEAT_ONE 30
#define SRE_OP_GROUPREF_IGNORE 31
#define SRE_OP_IN_IGNORE 32
#define SRE_OP_LITERAL_IGNORE 33
Expand Down
6 changes: 3 additions & 3 deletions Modules/sre_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
}
RETURN_FAILURE;

case SRE_OP_POSSESSIVE_ONE:
case SRE_OP_POSSESSIVE_REPEAT_ONE:
/* match repeated sequence (maximizing regexp) without
backtracking */

Expand All @@ -962,10 +962,10 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
collecting backtracking points. for other cases,
use the MAX_REPEAT operator */

/* <POSSESSIVE_ONE> <skip> <1=min> <2=max> item <SUCCESS>
/* <POSSESSIVE_REPEAT_ONE> <skip> <1=min> <2=max> item <SUCCESS>
tail */

TRACE(("|%p|%p|POSSESSIVE_ONE %d %d\n", ctx->pattern,
TRACE(("|%p|%p|POSSESSIVE_REPEAT_ONE %d %d\n", ctx->pattern,
ctx->ptr, ctx->pattern[1], ctx->pattern[2]));

if (ctx->ptr + ctx->pattern[1] > end) {
Expand Down