mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Update Python sre modules to their 3.6 versions
This commit is contained in:
4 files changed
+1064
-846
No files matched your search
@@ -69,8 +69,11 @@ resulting RE will match the second character.
|
||||
In string patterns without the ASCII flag, it will match the whole
|
||||
range of Unicode digits.
|
||||
\D Matches any non-digit character; equivalent to [^\d].
|
||||
\s Matches any whitespace character; equivalent to [ \t\n\r\f\v].
|
||||
\S Matches any non-whitespace character; equiv. to [^ \t\n\r\f\v].
|
||||
\s Matches any whitespace character; equivalent to [ \t\n\r\f\v] in
|
||||
bytes patterns or string patterns with the ASCII flag.
|
||||
In string patterns without the ASCII flag, it will match the whole
|
||||
range of Unicode whitespace characters.
|
||||
\S Matches any non-whitespace character; equivalent to [^\s].
|
||||
\w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]
|
||||
in bytes patterns or string patterns with the ASCII flag.
|
||||
In string patterns without the ASCII flag, it will match the
|
||||
@@ -82,16 +85,17 @@ resulting RE will match the second character.
|
||||
\\ Matches a literal backslash.
|
||||
|
||||
This module exports the following functions:
|
||||
match Match a regular expression pattern to the beginning of a string.
|
||||
search Search a string for the presence of a pattern.
|
||||
sub Substitute occurrences of a pattern found in a string.
|
||||
subn Same as sub, but also return the number of substitutions made.
|
||||
split Split a string by the occurrences of a pattern.
|
||||
findall Find all occurrences of a pattern in a string.
|
||||
finditer Return an iterator yielding a match object for each match.
|
||||
compile Compile a pattern into a RegexObject.
|
||||
purge Clear the regular expression cache.
|
||||
escape Backslash all non-alphanumerics in a string.
|
||||
match Match a regular expression pattern to the beginning of a string.
|
||||
fullmatch Match a regular expression pattern to all of a string.
|
||||
search Search a string for the presence of a pattern.
|
||||
sub Substitute occurrences of a pattern found in a string.
|
||||
subn Same as sub, but also return the number of substitutions made.
|
||||
split Split a string by the occurrences of a pattern.
|
||||
findall Find all occurrences of a pattern in a string.
|
||||
finditer Return an iterator yielding a match object for each match.
|
||||
compile Compile a pattern into a RegexObject.
|
||||
purge Clear the regular expression cache.
|
||||
escape Backslash all non-alphanumerics in a string.
|
||||
|
||||
Some of the functions in this module takes flags as optional parameters:
|
||||
A ASCII For string patterns, make \w, \W, \b, \B, \d, \D
|
||||
@@ -115,30 +119,46 @@ This module also defines an exception 'error'.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import enum
|
||||
import sre_compile
|
||||
import sre_parse
|
||||
import functools
|
||||
try:
|
||||
import _locale
|
||||
except ImportError:
|
||||
_locale = None
|
||||
|
||||
# public symbols
|
||||
__all__ = [ "match", "search", "sub", "subn", "split", "findall",
|
||||
"compile", "purge", "template", "escape", "A", "I", "L", "M", "S", "X",
|
||||
"U", "ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
|
||||
"UNICODE", "error" ]
|
||||
__all__ = [
|
||||
"match", "fullmatch", "search", "sub", "subn", "split",
|
||||
"findall", "finditer", "compile", "purge", "template", "escape",
|
||||
"error", "A", "I", "L", "M", "S", "X", "U",
|
||||
"ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
|
||||
"UNICODE",
|
||||
]
|
||||
|
||||
__version__ = "2.2.1"
|
||||
|
||||
# flags
|
||||
A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"
|
||||
I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case
|
||||
L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale
|
||||
U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale"
|
||||
M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline
|
||||
S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline
|
||||
X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments
|
||||
|
||||
# sre extensions (experimental, don't rely on these)
|
||||
T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
|
||||
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
|
||||
class RegexFlag(enum.IntFlag):
|
||||
ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"
|
||||
IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case
|
||||
LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale
|
||||
UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale"
|
||||
MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline
|
||||
DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline
|
||||
VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments
|
||||
A = ASCII
|
||||
I = IGNORECASE
|
||||
L = LOCALE
|
||||
U = UNICODE
|
||||
M = MULTILINE
|
||||
S = DOTALL
|
||||
X = VERBOSE
|
||||
# sre extensions (experimental, don't rely on these)
|
||||
TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
|
||||
T = TEMPLATE
|
||||
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
|
||||
globals().update(RegexFlag.__members__)
|
||||
|
||||
# sre exception
|
||||
error = sre_compile.error
|
||||
@@ -151,21 +171,26 @@ def match(pattern, string, flags=0):
|
||||
a match object, or None if no match was found."""
|
||||
return _compile(pattern, flags).match(string)
|
||||
|
||||
def fullmatch(pattern, string, flags=0):
|
||||
"""Try to apply the pattern to all of the string, returning
|
||||
a match object, or None if no match was found."""
|
||||
return _compile(pattern, flags).fullmatch(string)
|
||||
|
||||
def search(pattern, string, flags=0):
|
||||
"""Scan through string looking for a match to the pattern, returning
|
||||
a match object, or None if no match was found."""
|
||||
return _compile(pattern, flags).search(string)
|
||||
|
||||
def sub(pattern, repl, string, count=0):
|
||||
def sub(pattern, repl, string, count=0, flags=0):
|
||||
"""Return the string obtained by replacing the leftmost
|
||||
non-overlapping occurrences of the pattern in string by the
|
||||
replacement repl. repl can be either a string or a callable;
|
||||
if a string, backslash escapes in it are processed. If it is
|
||||
a callable, it's passed the match object and must return
|
||||
a replacement string to be used."""
|
||||
return _compile(pattern, 0).sub(repl, string, count)
|
||||
return _compile(pattern, flags).sub(repl, string, count)
|
||||
|
||||
def subn(pattern, repl, string, count=0):
|
||||
def subn(pattern, repl, string, count=0, flags=0):
|
||||
"""Return a 2-tuple containing (new_string, number).
|
||||
new_string is the string obtained by replacing the leftmost
|
||||
non-overlapping occurrences of the pattern in the source
|
||||
@@ -174,57 +199,61 @@ def subn(pattern, repl, string, count=0):
|
||||
callable; if a string, backslash escapes in it are processed.
|
||||
If it is a callable, it's passed the match object and must
|
||||
return a replacement string to be used."""
|
||||
return _compile(pattern, 0).subn(repl, string, count)
|
||||
return _compile(pattern, flags).subn(repl, string, count)
|
||||
|
||||
def split(pattern, string, maxsplit=0):
|
||||
def split(pattern, string, maxsplit=0, flags=0):
|
||||
"""Split the source string by the occurrences of the pattern,
|
||||
returning a list containing the resulting substrings."""
|
||||
return _compile(pattern, 0).split(string, maxsplit)
|
||||
returning a list containing the resulting substrings. If
|
||||
capturing parentheses are used in pattern, then the text of all
|
||||
groups in the pattern are also returned as part of the resulting
|
||||
list. If maxsplit is nonzero, at most maxsplit splits occur,
|
||||
and the remainder of the string is returned as the final element
|
||||
of the list."""
|
||||
return _compile(pattern, flags).split(string, maxsplit)
|
||||
|
||||
def findall(pattern, string, flags=0):
|
||||
"""Return a list of all non-overlapping matches in the string.
|
||||
|
||||
If one or more groups are present in the pattern, return a
|
||||
list of groups; this will be a list of tuples if the pattern
|
||||
If one or more capturing groups are present in the pattern, return
|
||||
a list of groups; this will be a list of tuples if the pattern
|
||||
has more than one group.
|
||||
|
||||
Empty matches are included in the result."""
|
||||
return _compile(pattern, flags).findall(string)
|
||||
|
||||
if True: # XXX RustPython: when sys.hexversion is available, change to (sys.hexversion >= 0x02020000)
|
||||
__all__.append("finditer")
|
||||
def finditer(pattern, string, flags=0):
|
||||
"""Return an iterator over all non-overlapping matches in the
|
||||
string. For each match, the iterator returns a match object.
|
||||
def finditer(pattern, string, flags=0):
|
||||
"""Return an iterator over all non-overlapping matches in the
|
||||
string. For each match, the iterator returns a match object.
|
||||
|
||||
Empty matches are included in the result."""
|
||||
return _compile(pattern, flags).finditer(string)
|
||||
Empty matches are included in the result."""
|
||||
return _compile(pattern, flags).finditer(string)
|
||||
|
||||
def compile(pattern, flags=0):
|
||||
"Compile a regular expression pattern, returning a pattern object."
|
||||
return _compile(pattern, flags)
|
||||
|
||||
def purge():
|
||||
"Clear the regular expression cache"
|
||||
"Clear the regular expression caches"
|
||||
_cache.clear()
|
||||
_cache_repl.clear()
|
||||
_compile_repl.cache_clear()
|
||||
|
||||
def template(pattern, flags=0):
|
||||
"Compile a template pattern, returning a pattern object"
|
||||
return _compile(pattern, flags|T)
|
||||
|
||||
_alphanum_str = frozenset(
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
_alphanum_bytes = frozenset(
|
||||
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
b"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
|
||||
def escape(pattern):
|
||||
"Escape all non-alphanumeric characters in pattern."
|
||||
"""
|
||||
Escape all the characters in pattern except ASCII letters, numbers and '_'.
|
||||
"""
|
||||
if isinstance(pattern, str):
|
||||
alphanum = _alphanum_str
|
||||
s = list(pattern)
|
||||
for i in range(len(pattern)):
|
||||
c = pattern[i]
|
||||
for i, c in enumerate(pattern):
|
||||
if c not in alphanum:
|
||||
if c == "\000":
|
||||
s[i] = "\\000"
|
||||
@@ -250,43 +279,42 @@ def escape(pattern):
|
||||
# internals
|
||||
|
||||
_cache = {}
|
||||
_cache_repl = {}
|
||||
|
||||
_pattern_type = type(sre_compile.compile("", 0))
|
||||
|
||||
_MAXCACHE = 100
|
||||
|
||||
def _compile(*key):
|
||||
_MAXCACHE = 512
|
||||
def _compile(pattern, flags):
|
||||
# internal: compile pattern
|
||||
cachekey = (type(key[0]),) + key
|
||||
p = _cache.get(cachekey)
|
||||
if p is not None:
|
||||
return p
|
||||
pattern, flags = key
|
||||
try:
|
||||
p, loc = _cache[type(pattern), pattern, flags]
|
||||
if loc is None or loc == _locale.setlocale(_locale.LC_CTYPE):
|
||||
return p
|
||||
except KeyError:
|
||||
pass
|
||||
if isinstance(pattern, _pattern_type):
|
||||
if flags:
|
||||
raise ValueError(
|
||||
"Cannot process flags argument with a compiled pattern")
|
||||
"cannot process flags argument with a compiled pattern")
|
||||
return pattern
|
||||
if not sre_compile.isstring(pattern):
|
||||
raise TypeError("first argument must be string or compiled pattern")
|
||||
p = sre_compile.compile(pattern, flags)
|
||||
if len(_cache) >= _MAXCACHE:
|
||||
_cache.clear()
|
||||
_cache[cachekey] = p
|
||||
if not (flags & DEBUG):
|
||||
if len(_cache) >= _MAXCACHE:
|
||||
_cache.clear()
|
||||
if p.flags & LOCALE:
|
||||
if not _locale:
|
||||
return p
|
||||
loc = _locale.setlocale(_locale.LC_CTYPE)
|
||||
else:
|
||||
loc = None
|
||||
_cache[type(pattern), pattern, flags] = p, loc
|
||||
return p
|
||||
|
||||
def _compile_repl(*key):
|
||||
@functools.lru_cache(_MAXCACHE)
|
||||
def _compile_repl(repl, pattern):
|
||||
# internal: compile replacement pattern
|
||||
p = _cache_repl.get(key)
|
||||
if p is not None:
|
||||
return p
|
||||
repl, pattern = key
|
||||
p = sre_parse.parse_template(repl, pattern)
|
||||
if len(_cache_repl) >= _MAXCACHE:
|
||||
_cache_repl.clear()
|
||||
_cache_repl[key] = p
|
||||
return p
|
||||
return sre_parse.parse_template(repl, pattern)
|
||||
|
||||
def _expand(pattern, match, template):
|
||||
# internal: match.expand implementation hook
|
||||
@@ -324,10 +352,11 @@ class Scanner:
|
||||
s = sre_parse.Pattern()
|
||||
s.flags = flags
|
||||
for phrase, action in lexicon:
|
||||
gid = s.opengroup()
|
||||
p.append(sre_parse.SubPattern(s, [
|
||||
(SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))),
|
||||
(SUBPATTERN, (gid, 0, 0, sre_parse.parse(phrase, flags))),
|
||||
]))
|
||||
s.groups = len(p)+1
|
||||
s.closegroup(gid, p[-1])
|
||||
p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
|
||||
self.scanner = sre_compile.compile(p)
|
||||
def scan(self, string):
|
||||
@@ -335,7 +364,7 @@ class Scanner:
|
||||
append = result.append
|
||||
match = self.scanner.scanner(string).match
|
||||
i = 0
|
||||
while 1:
|
||||
while True:
|
||||
m = match()
|
||||
if not m:
|
||||
break
|
||||
@@ -343,7 +372,7 @@ class Scanner:
|
||||
if i == j:
|
||||
break
|
||||
action = self.lexicon[m.lastindex-1][1]
|
||||
if hasattr(action, "__call__"):
|
||||
if callable(action):
|
||||
self.match = m
|
||||
action = action(self, m.group())
|
||||
if action is not None:
|
||||
|
||||
Vendored
+342
-277
@@ -10,24 +10,56 @@
|
||||
|
||||
"""Internal support module for sre"""
|
||||
|
||||
import _sre, sys
|
||||
import _sre
|
||||
import sre_parse
|
||||
from sre_constants import *
|
||||
|
||||
assert _sre.MAGIC == MAGIC, "SRE module mismatch"
|
||||
|
||||
if _sre.CODESIZE == 2:
|
||||
MAXCODE = 65535
|
||||
else:
|
||||
MAXCODE = 0xFFFFFFFF
|
||||
_LITERAL_CODES = {LITERAL, NOT_LITERAL}
|
||||
_REPEATING_CODES = {REPEAT, MIN_REPEAT, MAX_REPEAT}
|
||||
_SUCCESS_CODES = {SUCCESS, FAILURE}
|
||||
_ASSERT_CODES = {ASSERT, ASSERT_NOT}
|
||||
|
||||
def _identityfunction(x):
|
||||
return x
|
||||
# Sets of lowercase characters which have the same uppercase.
|
||||
_equivalences = (
|
||||
# LATIN SMALL LETTER I, LATIN SMALL LETTER DOTLESS I
|
||||
(0x69, 0x131), # iı
|
||||
# LATIN SMALL LETTER S, LATIN SMALL LETTER LONG S
|
||||
(0x73, 0x17f), # sſ
|
||||
# MICRO SIGN, GREEK SMALL LETTER MU
|
||||
(0xb5, 0x3bc), # µμ
|
||||
# COMBINING GREEK YPOGEGRAMMENI, GREEK SMALL LETTER IOTA, GREEK PROSGEGRAMMENI
|
||||
(0x345, 0x3b9, 0x1fbe), # \u0345ιι
|
||||
# GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS, GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
|
||||
(0x390, 0x1fd3), # ΐΐ
|
||||
# GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS, GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
|
||||
(0x3b0, 0x1fe3), # ΰΰ
|
||||
# GREEK SMALL LETTER BETA, GREEK BETA SYMBOL
|
||||
(0x3b2, 0x3d0), # βϐ
|
||||
# GREEK SMALL LETTER EPSILON, GREEK LUNATE EPSILON SYMBOL
|
||||
(0x3b5, 0x3f5), # εϵ
|
||||
# GREEK SMALL LETTER THETA, GREEK THETA SYMBOL
|
||||
(0x3b8, 0x3d1), # θϑ
|
||||
# GREEK SMALL LETTER KAPPA, GREEK KAPPA SYMBOL
|
||||
(0x3ba, 0x3f0), # κϰ
|
||||
# GREEK SMALL LETTER PI, GREEK PI SYMBOL
|
||||
(0x3c0, 0x3d6), # πϖ
|
||||
# GREEK SMALL LETTER RHO, GREEK RHO SYMBOL
|
||||
(0x3c1, 0x3f1), # ρϱ
|
||||
# GREEK SMALL LETTER FINAL SIGMA, GREEK SMALL LETTER SIGMA
|
||||
(0x3c2, 0x3c3), # ςσ
|
||||
# GREEK SMALL LETTER PHI, GREEK PHI SYMBOL
|
||||
(0x3c6, 0x3d5), # φϕ
|
||||
# LATIN SMALL LETTER S WITH DOT ABOVE, LATIN SMALL LETTER LONG S WITH DOT ABOVE
|
||||
(0x1e61, 0x1e9b), # ṡẛ
|
||||
# LATIN SMALL LIGATURE LONG S T, LATIN SMALL LIGATURE ST
|
||||
(0xfb05, 0xfb06), # ſtst
|
||||
)
|
||||
|
||||
_LITERAL_CODES = set([LITERAL, NOT_LITERAL])
|
||||
_REPEATING_CODES = set([REPEAT, MIN_REPEAT, MAX_REPEAT])
|
||||
_SUCCESS_CODES = set([SUCCESS, FAILURE])
|
||||
_ASSERT_CODES = set([ASSERT, ASSERT_NOT])
|
||||
# Maps the lowercase code to lowercase codes which have the same uppercase.
|
||||
_ignorecase_fixes = {i: tuple(j for j in t if i != j)
|
||||
for t in _equivalences for i in t}
|
||||
|
||||
def _compile(code, pattern, flags):
|
||||
# internal: compile a (sub)pattern
|
||||
@@ -37,75 +69,88 @@ def _compile(code, pattern, flags):
|
||||
REPEATING_CODES = _REPEATING_CODES
|
||||
SUCCESS_CODES = _SUCCESS_CODES
|
||||
ASSERT_CODES = _ASSERT_CODES
|
||||
if (flags & SRE_FLAG_IGNORECASE and
|
||||
not (flags & SRE_FLAG_LOCALE) and
|
||||
flags & SRE_FLAG_UNICODE and
|
||||
not (flags & SRE_FLAG_ASCII)):
|
||||
fixes = _ignorecase_fixes
|
||||
else:
|
||||
fixes = None
|
||||
for op, av in pattern:
|
||||
if op in LITERAL_CODES:
|
||||
if flags & SRE_FLAG_IGNORECASE:
|
||||
emit(OPCODES[OP_IGNORE[op]])
|
||||
emit(_sre.getlower(av, flags))
|
||||
lo = _sre.getlower(av, flags)
|
||||
if fixes and lo in fixes:
|
||||
emit(IN_IGNORE)
|
||||
skip = _len(code); emit(0)
|
||||
if op is NOT_LITERAL:
|
||||
emit(NEGATE)
|
||||
for k in (lo,) + fixes[lo]:
|
||||
emit(LITERAL)
|
||||
emit(k)
|
||||
emit(FAILURE)
|
||||
code[skip] = _len(code) - skip
|
||||
else:
|
||||
emit(OP_IGNORE[op])
|
||||
emit(lo)
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
emit(av)
|
||||
elif op is IN:
|
||||
if flags & SRE_FLAG_IGNORECASE:
|
||||
emit(OPCODES[OP_IGNORE[op]])
|
||||
emit(OP_IGNORE[op])
|
||||
def fixup(literal, flags=flags):
|
||||
return _sre.getlower(literal, flags)
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
fixup = _identityfunction
|
||||
emit(op)
|
||||
fixup = None
|
||||
skip = _len(code); emit(0)
|
||||
_compile_charset(av, flags, code, fixup)
|
||||
_compile_charset(av, flags, code, fixup, fixes)
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is ANY:
|
||||
if flags & SRE_FLAG_DOTALL:
|
||||
emit(OPCODES[ANY_ALL])
|
||||
emit(ANY_ALL)
|
||||
else:
|
||||
emit(OPCODES[ANY])
|
||||
emit(ANY)
|
||||
elif op in REPEATING_CODES:
|
||||
if flags & SRE_FLAG_TEMPLATE:
|
||||
raise error("internal: unsupported template operator")
|
||||
emit(OPCODES[REPEAT])
|
||||
skip = _len(code); emit(0)
|
||||
emit(av[0])
|
||||
emit(av[1])
|
||||
_compile(code, av[2], flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
code[skip] = _len(code) - skip
|
||||
raise error("internal: unsupported template operator %r" % (op,))
|
||||
elif _simple(av) and op is not REPEAT:
|
||||
if op is MAX_REPEAT:
|
||||
emit(OPCODES[REPEAT_ONE])
|
||||
emit(REPEAT_ONE)
|
||||
else:
|
||||
emit(OPCODES[MIN_REPEAT_ONE])
|
||||
emit(MIN_REPEAT_ONE)
|
||||
skip = _len(code); emit(0)
|
||||
emit(av[0])
|
||||
emit(av[1])
|
||||
_compile(code, av[2], flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
emit(SUCCESS)
|
||||
code[skip] = _len(code) - skip
|
||||
else:
|
||||
emit(OPCODES[REPEAT])
|
||||
emit(REPEAT)
|
||||
skip = _len(code); emit(0)
|
||||
emit(av[0])
|
||||
emit(av[1])
|
||||
_compile(code, av[2], flags)
|
||||
code[skip] = _len(code) - skip
|
||||
if op is MAX_REPEAT:
|
||||
emit(OPCODES[MAX_UNTIL])
|
||||
emit(MAX_UNTIL)
|
||||
else:
|
||||
emit(OPCODES[MIN_UNTIL])
|
||||
emit(MIN_UNTIL)
|
||||
elif op is SUBPATTERN:
|
||||
if av[0]:
|
||||
emit(OPCODES[MARK])
|
||||
emit((av[0]-1)*2)
|
||||
# _compile_info(code, av[1], flags)
|
||||
_compile(code, av[1], flags)
|
||||
if av[0]:
|
||||
emit(OPCODES[MARK])
|
||||
emit((av[0]-1)*2+1)
|
||||
group, add_flags, del_flags, p = av
|
||||
if group:
|
||||
emit(MARK)
|
||||
emit((group-1)*2)
|
||||
# _compile_info(code, p, (flags | add_flags) & ~del_flags)
|
||||
_compile(code, p, (flags | add_flags) & ~del_flags)
|
||||
if group:
|
||||
emit(MARK)
|
||||
emit((group-1)*2+1)
|
||||
elif op in SUCCESS_CODES:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
elif op in ASSERT_CODES:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
skip = _len(code); emit(0)
|
||||
if av[0] >= 0:
|
||||
emit(0) # look ahead
|
||||
@@ -115,57 +160,57 @@ def _compile(code, pattern, flags):
|
||||
raise error("look-behind requires fixed-width pattern")
|
||||
emit(lo) # look behind
|
||||
_compile(code, av[1], flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
emit(SUCCESS)
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is CALL:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
skip = _len(code); emit(0)
|
||||
_compile(code, av, flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
emit(SUCCESS)
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is AT:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
if flags & SRE_FLAG_MULTILINE:
|
||||
av = AT_MULTILINE.get(av, av)
|
||||
if flags & SRE_FLAG_LOCALE:
|
||||
av = AT_LOCALE.get(av, av)
|
||||
elif flags & SRE_FLAG_UNICODE:
|
||||
elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII):
|
||||
av = AT_UNICODE.get(av, av)
|
||||
emit(ATCODES[av])
|
||||
emit(av)
|
||||
elif op is BRANCH:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
tail = []
|
||||
tailappend = tail.append
|
||||
for av in av[1]:
|
||||
skip = _len(code); emit(0)
|
||||
# _compile_info(code, av, flags)
|
||||
_compile(code, av, flags)
|
||||
emit(OPCODES[JUMP])
|
||||
emit(JUMP)
|
||||
tailappend(_len(code)); emit(0)
|
||||
code[skip] = _len(code) - skip
|
||||
emit(0) # end of branch
|
||||
emit(FAILURE) # end of branch
|
||||
for tail in tail:
|
||||
code[tail] = _len(code) - tail
|
||||
elif op is CATEGORY:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
if flags & SRE_FLAG_LOCALE:
|
||||
av = CH_LOCALE[av]
|
||||
elif flags & SRE_FLAG_UNICODE:
|
||||
elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII):
|
||||
av = CH_UNICODE[av]
|
||||
emit(CHCODES[av])
|
||||
emit(av)
|
||||
elif op is GROUPREF:
|
||||
if flags & SRE_FLAG_IGNORECASE:
|
||||
emit(OPCODES[OP_IGNORE[op]])
|
||||
emit(OP_IGNORE[op])
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
emit(av-1)
|
||||
elif op is GROUPREF_EXISTS:
|
||||
emit(OPCODES[op])
|
||||
emit(op)
|
||||
emit(av[0]-1)
|
||||
skipyes = _len(code); emit(0)
|
||||
_compile(code, av[1], flags)
|
||||
if av[2]:
|
||||
emit(OPCODES[JUMP])
|
||||
emit(JUMP)
|
||||
skipno = _len(code); emit(0)
|
||||
code[skipyes] = _len(code) - skipyes + 1
|
||||
_compile(code, av[2], flags)
|
||||
@@ -173,225 +218,235 @@ def _compile(code, pattern, flags):
|
||||
else:
|
||||
code[skipyes] = _len(code) - skipyes + 1
|
||||
else:
|
||||
raise ValueError("unsupported operand type", op)
|
||||
raise error("internal: unsupported operand type %r" % (op,))
|
||||
|
||||
def _compile_charset(charset, flags, code, fixup=None):
|
||||
def _compile_charset(charset, flags, code, fixup=None, fixes=None):
|
||||
# compile charset subprogram
|
||||
emit = code.append
|
||||
if fixup is None:
|
||||
fixup = _identityfunction
|
||||
for op, av in _optimize_charset(charset, fixup):
|
||||
emit(OPCODES[op])
|
||||
for op, av in _optimize_charset(charset, fixup, fixes):
|
||||
emit(op)
|
||||
if op is NEGATE:
|
||||
pass
|
||||
elif op is LITERAL:
|
||||
emit(fixup(av))
|
||||
elif op is RANGE:
|
||||
emit(fixup(av[0]))
|
||||
emit(fixup(av[1]))
|
||||
emit(av)
|
||||
elif op is RANGE or op is RANGE_IGNORE:
|
||||
emit(av[0])
|
||||
emit(av[1])
|
||||
elif op is CHARSET:
|
||||
code.extend(av)
|
||||
elif op is BIGCHARSET:
|
||||
code.extend(av)
|
||||
elif op is CATEGORY:
|
||||
if flags & SRE_FLAG_LOCALE:
|
||||
emit(CHCODES[CH_LOCALE[av]])
|
||||
elif flags & SRE_FLAG_UNICODE:
|
||||
emit(CHCODES[CH_UNICODE[av]])
|
||||
emit(CH_LOCALE[av])
|
||||
elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII):
|
||||
emit(CH_UNICODE[av])
|
||||
else:
|
||||
emit(CHCODES[av])
|
||||
emit(av)
|
||||
else:
|
||||
raise error("internal: unsupported set operator")
|
||||
emit(OPCODES[FAILURE])
|
||||
raise error("internal: unsupported set operator %r" % (op,))
|
||||
emit(FAILURE)
|
||||
|
||||
def _optimize_charset(charset, fixup):
|
||||
def _optimize_charset(charset, fixup, fixes):
|
||||
# internal: optimize character set
|
||||
out = []
|
||||
outappend = out.append
|
||||
charmap = [0]*256
|
||||
try:
|
||||
for op, av in charset:
|
||||
if op is NEGATE:
|
||||
outappend((op, av))
|
||||
elif op is LITERAL:
|
||||
charmap[fixup(av)] = 1
|
||||
elif op is RANGE:
|
||||
for i in range(fixup(av[0]), fixup(av[1])+1):
|
||||
charmap[i] = 1
|
||||
elif op is CATEGORY:
|
||||
# XXX: could append to charmap tail
|
||||
return charset # cannot compress
|
||||
except IndexError:
|
||||
# character set contains unicode characters
|
||||
return _optimize_unicode(charset, fixup)
|
||||
tail = []
|
||||
charmap = bytearray(256)
|
||||
for op, av in charset:
|
||||
while True:
|
||||
try:
|
||||
if op is LITERAL:
|
||||
if fixup:
|
||||
lo = fixup(av)
|
||||
charmap[lo] = 1
|
||||
if fixes and lo in fixes:
|
||||
for k in fixes[lo]:
|
||||
charmap[k] = 1
|
||||
else:
|
||||
charmap[av] = 1
|
||||
elif op is RANGE:
|
||||
r = range(av[0], av[1]+1)
|
||||
if fixup:
|
||||
r = map(fixup, r)
|
||||
if fixup and fixes:
|
||||
for i in r:
|
||||
charmap[i] = 1
|
||||
if i in fixes:
|
||||
for k in fixes[i]:
|
||||
charmap[k] = 1
|
||||
else:
|
||||
for i in r:
|
||||
charmap[i] = 1
|
||||
elif op is NEGATE:
|
||||
out.append((op, av))
|
||||
else:
|
||||
tail.append((op, av))
|
||||
except IndexError:
|
||||
if len(charmap) == 256:
|
||||
# character set contains non-UCS1 character codes
|
||||
charmap += b'\0' * 0xff00
|
||||
continue
|
||||
# Character set contains non-BMP character codes.
|
||||
# There are only two ranges of cased non-BMP characters:
|
||||
# 10400-1044F (Deseret) and 118A0-118DF (Warang Citi),
|
||||
# and for both ranges RANGE_IGNORE works.
|
||||
if fixup and op is RANGE:
|
||||
op = RANGE_IGNORE
|
||||
tail.append((op, av))
|
||||
break
|
||||
|
||||
# compress character map
|
||||
i = p = n = 0
|
||||
runs = []
|
||||
runsappend = runs.append
|
||||
for c in charmap:
|
||||
if c:
|
||||
if n == 0:
|
||||
p = i
|
||||
n = n + 1
|
||||
elif n:
|
||||
runsappend((p, n))
|
||||
n = 0
|
||||
i = i + 1
|
||||
if n:
|
||||
runsappend((p, n))
|
||||
if len(runs) <= 2:
|
||||
q = 0
|
||||
while True:
|
||||
p = charmap.find(1, q)
|
||||
if p < 0:
|
||||
break
|
||||
if len(runs) >= 2:
|
||||
runs = None
|
||||
break
|
||||
q = charmap.find(0, p)
|
||||
if q < 0:
|
||||
runs.append((p, len(charmap)))
|
||||
break
|
||||
runs.append((p, q))
|
||||
if runs is not None:
|
||||
# use literal/range
|
||||
for p, n in runs:
|
||||
if n == 1:
|
||||
outappend((LITERAL, p))
|
||||
for p, q in runs:
|
||||
if q - p == 1:
|
||||
out.append((LITERAL, p))
|
||||
else:
|
||||
outappend((RANGE, (p, p+n-1)))
|
||||
if len(out) < len(charset):
|
||||
out.append((RANGE, (p, q - 1)))
|
||||
out += tail
|
||||
# if the case was changed or new representation is more compact
|
||||
if fixup or len(out) < len(charset):
|
||||
return out
|
||||
else:
|
||||
# use bitmap
|
||||
# else original character set is good enough
|
||||
return charset
|
||||
|
||||
# use bitmap
|
||||
if len(charmap) == 256:
|
||||
data = _mk_bitmap(charmap)
|
||||
outappend((CHARSET, data))
|
||||
out.append((CHARSET, data))
|
||||
out += tail
|
||||
return out
|
||||
return charset
|
||||
|
||||
def _mk_bitmap(bits):
|
||||
data = []
|
||||
dataappend = data.append
|
||||
if _sre.CODESIZE == 2:
|
||||
start = (1, 0)
|
||||
else:
|
||||
start = (1, 0)
|
||||
m, v = start
|
||||
for c in bits:
|
||||
if c:
|
||||
v = v + m
|
||||
m = m + m
|
||||
if m > MAXCODE:
|
||||
dataappend(v)
|
||||
m, v = start
|
||||
return data
|
||||
# To represent a big charset, first a bitmap of all characters in the
|
||||
# set is constructed. Then, this bitmap is sliced into chunks of 256
|
||||
# characters, duplicate chunks are eliminated, and each chunk is
|
||||
# given a number. In the compiled expression, the charset is
|
||||
# represented by a 32-bit word sequence, consisting of one word for
|
||||
# the number of different chunks, a sequence of 256 bytes (64 words)
|
||||
# of chunk numbers indexed by their original chunk position, and a
|
||||
# sequence of 256-bit chunks (8 words each).
|
||||
|
||||
# To represent a big charset, first a bitmap of all characters in the
|
||||
# set is constructed. Then, this bitmap is sliced into chunks of 256
|
||||
# characters, duplicate chunks are eliminated, and each chunk is
|
||||
# given a number. In the compiled expression, the charset is
|
||||
# represented by a 16-bit word sequence, consisting of one word for
|
||||
# the number of different chunks, a sequence of 256 bytes (128 words)
|
||||
# of chunk numbers indexed by their original chunk position, and a
|
||||
# sequence of chunks (16 words each).
|
||||
# Compression is normally good: in a typical charset, large ranges of
|
||||
# Unicode will be either completely excluded (e.g. if only cyrillic
|
||||
# letters are to be matched), or completely included (e.g. if large
|
||||
# subranges of Kanji match). These ranges will be represented by
|
||||
# chunks of all one-bits or all zero-bits.
|
||||
|
||||
# Compression is normally good: in a typical charset, large ranges of
|
||||
# Unicode will be either completely excluded (e.g. if only cyrillic
|
||||
# letters are to be matched), or completely included (e.g. if large
|
||||
# subranges of Kanji match). These ranges will be represented by
|
||||
# chunks of all one-bits or all zero-bits.
|
||||
# Matching can be also done efficiently: the more significant byte of
|
||||
# the Unicode character is an index into the chunk number, and the
|
||||
# less significant byte is a bit index in the chunk (just like the
|
||||
# CHARSET matching).
|
||||
|
||||
# Matching can be also done efficiently: the more significant byte of
|
||||
# the Unicode character is an index into the chunk number, and the
|
||||
# less significant byte is a bit index in the chunk (just like the
|
||||
# CHARSET matching).
|
||||
|
||||
# In UCS-4 mode, the BIGCHARSET opcode still supports only subsets
|
||||
# of the basic multilingual plane; an efficient representation
|
||||
# for all of UTF-16 has not yet been developed. This means,
|
||||
# in particular, that negated charsets cannot be represented as
|
||||
# bigcharsets.
|
||||
|
||||
def _optimize_unicode(charset, fixup):
|
||||
try:
|
||||
import array
|
||||
except ImportError:
|
||||
return charset
|
||||
charmap = [0]*65536
|
||||
negate = 0
|
||||
try:
|
||||
for op, av in charset:
|
||||
if op is NEGATE:
|
||||
negate = 1
|
||||
elif op is LITERAL:
|
||||
charmap[fixup(av)] = 1
|
||||
elif op is RANGE:
|
||||
for i in range(fixup(av[0]), fixup(av[1])+1):
|
||||
charmap[i] = 1
|
||||
elif op is CATEGORY:
|
||||
# XXX: could expand category
|
||||
return charset # cannot compress
|
||||
except IndexError:
|
||||
# non-BMP characters
|
||||
return charset
|
||||
if negate:
|
||||
if sys.maxunicode != 65535:
|
||||
# XXX: negation does not work with big charsets
|
||||
return charset
|
||||
for i in range(65536):
|
||||
charmap[i] = not charmap[i]
|
||||
charmap = bytes(charmap) # should be hashable
|
||||
comps = {}
|
||||
mapping = [0]*256
|
||||
mapping = bytearray(256)
|
||||
block = 0
|
||||
data = []
|
||||
for i in range(256):
|
||||
chunk = tuple(charmap[i*256:(i+1)*256])
|
||||
new = comps.setdefault(chunk, block)
|
||||
mapping[i] = new
|
||||
if new == block:
|
||||
block = block + 1
|
||||
data = data + _mk_bitmap(chunk)
|
||||
header = [block]
|
||||
if _sre.CODESIZE == 2:
|
||||
code = 'H'
|
||||
else:
|
||||
code = 'I'
|
||||
# Convert block indices to byte array of 256 bytes
|
||||
mapping = array.array('b', mapping).tostring()
|
||||
# Convert byte array to word array
|
||||
mapping = array.array(code, mapping)
|
||||
assert mapping.itemsize == _sre.CODESIZE
|
||||
assert len(mapping) * mapping.itemsize == 256
|
||||
header = header + mapping.tolist()
|
||||
data[0:0] = header
|
||||
return [(BIGCHARSET, data)]
|
||||
data = bytearray()
|
||||
for i in range(0, 65536, 256):
|
||||
chunk = charmap[i: i + 256]
|
||||
if chunk in comps:
|
||||
mapping[i // 256] = comps[chunk]
|
||||
else:
|
||||
mapping[i // 256] = comps[chunk] = block
|
||||
block += 1
|
||||
data += chunk
|
||||
data = _mk_bitmap(data)
|
||||
data[0:0] = [block] + _bytes_to_codes(mapping)
|
||||
out.append((BIGCHARSET, data))
|
||||
out += tail
|
||||
return out
|
||||
|
||||
_CODEBITS = _sre.CODESIZE * 8
|
||||
MAXCODE = (1 << _CODEBITS) - 1
|
||||
_BITS_TRANS = b'0' + b'1' * 255
|
||||
def _mk_bitmap(bits, _CODEBITS=_CODEBITS, _int=int):
|
||||
s = bits.translate(_BITS_TRANS)[::-1]
|
||||
return [_int(s[i - _CODEBITS: i], 2)
|
||||
for i in range(len(s), 0, -_CODEBITS)]
|
||||
|
||||
def _bytes_to_codes(b):
|
||||
# Convert block indices to word array
|
||||
a = memoryview(b).cast('I')
|
||||
assert a.itemsize == _sre.CODESIZE
|
||||
assert len(a) * a.itemsize == len(b)
|
||||
return a.tolist()
|
||||
|
||||
def _simple(av):
|
||||
# check if av is a "simple" operator
|
||||
lo, hi = av[2].getwidth()
|
||||
if lo == 0 and hi == MAXREPEAT:
|
||||
raise error("nothing to repeat")
|
||||
return lo == hi == 1 and av[2][0][0] != SUBPATTERN
|
||||
|
||||
def _compile_info(code, pattern, flags):
|
||||
# internal: compile an info block. in the current version,
|
||||
# this contains min/max pattern width, and an optional literal
|
||||
# prefix or a character map
|
||||
lo, hi = pattern.getwidth()
|
||||
if lo == 0:
|
||||
return # not worth it
|
||||
# look for a literal prefix
|
||||
def _generate_overlap_table(prefix):
|
||||
"""
|
||||
Generate an overlap table for the following prefix.
|
||||
An overlap table is a table of the same size as the prefix which
|
||||
informs about the potential self-overlap for each index in the prefix:
|
||||
- if overlap[i] == 0, prefix[i:] can't overlap prefix[0:...]
|
||||
- if overlap[i] == k with 0 < k <= i, prefix[i-k+1:i+1] overlaps with
|
||||
prefix[0:k]
|
||||
"""
|
||||
table = [0] * len(prefix)
|
||||
for i in range(1, len(prefix)):
|
||||
idx = table[i - 1]
|
||||
while prefix[i] != prefix[idx]:
|
||||
if idx == 0:
|
||||
table[i] = 0
|
||||
break
|
||||
idx = table[idx - 1]
|
||||
else:
|
||||
table[i] = idx + 1
|
||||
return table
|
||||
|
||||
def _get_literal_prefix(pattern):
|
||||
# look for literal prefix
|
||||
prefix = []
|
||||
prefixappend = prefix.append
|
||||
prefix_skip = 0
|
||||
prefix_skip = None
|
||||
for op, av in pattern.data:
|
||||
if op is LITERAL:
|
||||
prefixappend(av)
|
||||
elif op is SUBPATTERN:
|
||||
group, add_flags, del_flags, p = av
|
||||
if add_flags & SRE_FLAG_IGNORECASE:
|
||||
break
|
||||
prefix1, prefix_skip1, got_all = _get_literal_prefix(p)
|
||||
if prefix_skip is None:
|
||||
if group is not None:
|
||||
prefix_skip = len(prefix)
|
||||
elif prefix_skip1 is not None:
|
||||
prefix_skip = len(prefix) + prefix_skip1
|
||||
prefix.extend(prefix1)
|
||||
if not got_all:
|
||||
break
|
||||
else:
|
||||
break
|
||||
else:
|
||||
return prefix, prefix_skip, True
|
||||
return prefix, prefix_skip, False
|
||||
|
||||
def _get_charset_prefix(pattern):
|
||||
charset = [] # not used
|
||||
charsetappend = charset.append
|
||||
if not (flags & SRE_FLAG_IGNORECASE):
|
||||
# look for literal prefix
|
||||
for op, av in pattern.data:
|
||||
if op is LITERAL:
|
||||
if len(prefix) == prefix_skip:
|
||||
prefix_skip = prefix_skip + 1
|
||||
prefixappend(av)
|
||||
elif op is SUBPATTERN and len(av[1]) == 1:
|
||||
op, av = av[1][0]
|
||||
if op is LITERAL:
|
||||
prefixappend(av)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
# if no prefix, look for charset prefix
|
||||
if not prefix and pattern.data:
|
||||
op, av = pattern.data[0]
|
||||
if op is SUBPATTERN and av[1]:
|
||||
op, av = av[1][0]
|
||||
if pattern.data:
|
||||
op, av = pattern.data[0]
|
||||
if op is SUBPATTERN:
|
||||
group, add_flags, del_flags, p = av
|
||||
if p and not (add_flags & SRE_FLAG_IGNORECASE):
|
||||
op, av = p[0]
|
||||
if op is LITERAL:
|
||||
charsetappend((op, av))
|
||||
elif op is BRANCH:
|
||||
@@ -407,37 +462,59 @@ def _compile_info(code, pattern, flags):
|
||||
break
|
||||
else:
|
||||
charset = c
|
||||
elif op is BRANCH:
|
||||
c = []
|
||||
cappend = c.append
|
||||
for p in av[1]:
|
||||
if not p:
|
||||
break
|
||||
op, av = p[0]
|
||||
if op is LITERAL:
|
||||
cappend((op, av))
|
||||
else:
|
||||
break
|
||||
elif op is BRANCH:
|
||||
c = []
|
||||
cappend = c.append
|
||||
for p in av[1]:
|
||||
if not p:
|
||||
break
|
||||
op, av = p[0]
|
||||
if op is LITERAL:
|
||||
cappend((op, av))
|
||||
else:
|
||||
charset = c
|
||||
elif op is IN:
|
||||
charset = av
|
||||
break
|
||||
else:
|
||||
charset = c
|
||||
elif op is IN:
|
||||
charset = av
|
||||
return charset
|
||||
|
||||
def _compile_info(code, pattern, flags):
|
||||
# internal: compile an info block. in the current version,
|
||||
# this contains min/max pattern width, and an optional literal
|
||||
# prefix or a character map
|
||||
lo, hi = pattern.getwidth()
|
||||
if hi > MAXCODE:
|
||||
hi = MAXCODE
|
||||
if lo == 0:
|
||||
code.extend([INFO, 4, 0, lo, hi])
|
||||
return
|
||||
# look for a literal prefix
|
||||
prefix = []
|
||||
prefix_skip = 0
|
||||
charset = [] # not used
|
||||
if not (flags & SRE_FLAG_IGNORECASE):
|
||||
# look for literal prefix
|
||||
prefix, prefix_skip, got_all = _get_literal_prefix(pattern)
|
||||
# if no prefix, look for charset prefix
|
||||
if not prefix:
|
||||
charset = _get_charset_prefix(pattern)
|
||||
## if prefix:
|
||||
## print "*** PREFIX", prefix, prefix_skip
|
||||
## print("*** PREFIX", prefix, prefix_skip)
|
||||
## if charset:
|
||||
## print "*** CHARSET", charset
|
||||
## print("*** CHARSET", charset)
|
||||
# add an info block
|
||||
emit = code.append
|
||||
emit(OPCODES[INFO])
|
||||
emit(INFO)
|
||||
skip = len(code); emit(0)
|
||||
# literal flag
|
||||
mask = 0
|
||||
if prefix:
|
||||
mask = SRE_INFO_PREFIX
|
||||
if len(prefix) == prefix_skip == len(pattern.data):
|
||||
mask = mask + SRE_INFO_LITERAL
|
||||
if prefix_skip is None and got_all:
|
||||
mask = mask | SRE_INFO_LITERAL
|
||||
elif charset:
|
||||
mask = mask + SRE_INFO_CHARSET
|
||||
mask = mask | SRE_INFO_CHARSET
|
||||
emit(mask)
|
||||
# pattern length
|
||||
if lo < MAXCODE:
|
||||
@@ -445,22 +522,16 @@ def _compile_info(code, pattern, flags):
|
||||
else:
|
||||
emit(MAXCODE)
|
||||
prefix = prefix[:MAXCODE]
|
||||
if hi < MAXCODE:
|
||||
emit(hi)
|
||||
else:
|
||||
emit(0)
|
||||
emit(min(hi, MAXCODE))
|
||||
# add literal prefix
|
||||
if prefix:
|
||||
emit(len(prefix)) # length
|
||||
if prefix_skip is None:
|
||||
prefix_skip = len(prefix)
|
||||
emit(prefix_skip) # skip
|
||||
code.extend(prefix)
|
||||
# generate overlap table
|
||||
table = [-1] + ([0]*len(prefix))
|
||||
for i in range(len(prefix)):
|
||||
table[i+1] = table[i]+1
|
||||
while table[i+1] > 0 and prefix[i] != prefix[table[i+1]-1]:
|
||||
table[i+1] = table[table[i+1]-1]+1
|
||||
code.extend(table[1:]) # don't store first entry
|
||||
code.extend(_generate_overlap_table(prefix))
|
||||
elif charset:
|
||||
_compile_charset(charset, flags, code)
|
||||
code[skip] = len(code) - skip
|
||||
@@ -479,7 +550,7 @@ def _code(p, flags):
|
||||
# compile the pattern
|
||||
_compile(code, p.data, flags)
|
||||
|
||||
code.append(OPCODES[SUCCESS])
|
||||
code.append(SUCCESS)
|
||||
|
||||
return code
|
||||
|
||||
@@ -494,13 +565,7 @@ def compile(p, flags=0):
|
||||
|
||||
code = _code(p, flags)
|
||||
|
||||
# print code
|
||||
|
||||
# XXX: <fl> get rid of this limitation!
|
||||
if p.pattern.groups > 100:
|
||||
raise AssertionError(
|
||||
"sorry, but this version only supports 100 named groups"
|
||||
)
|
||||
# print(code)
|
||||
|
||||
# map in either direction
|
||||
groupindex = p.pattern.groupdict
|
||||
|
||||
Vendored
+109
-149
@@ -13,155 +13,115 @@
|
||||
|
||||
# update when constants are added or removed
|
||||
|
||||
MAGIC = 20031017
|
||||
MAGIC = 20140917
|
||||
|
||||
# max code word in this release
|
||||
|
||||
MAXREPEAT = 65535
|
||||
from _sre import MAXREPEAT, MAXGROUPS
|
||||
|
||||
# SRE standard exception (access as sre.error)
|
||||
# should this really be here?
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
def __init__(self, msg, pattern=None, pos=None):
|
||||
self.msg = msg
|
||||
self.pattern = pattern
|
||||
self.pos = pos
|
||||
if pattern is not None and pos is not None:
|
||||
msg = '%s at position %d' % (msg, pos)
|
||||
if isinstance(pattern, str):
|
||||
newline = '\n'
|
||||
else:
|
||||
newline = b'\n'
|
||||
self.lineno = pattern.count(newline, 0, pos) + 1
|
||||
self.colno = pos - pattern.rfind(newline, 0, pos)
|
||||
if newline in pattern:
|
||||
msg = '%s (line %d, column %d)' % (msg, self.lineno, self.colno)
|
||||
else:
|
||||
self.lineno = self.colno = None
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class _NamedIntConstant(int):
|
||||
def __new__(cls, value, name):
|
||||
self = super(_NamedIntConstant, cls).__new__(cls, value)
|
||||
self.name = name
|
||||
return self
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
MAXREPEAT = _NamedIntConstant(MAXREPEAT, 'MAXREPEAT')
|
||||
|
||||
def _makecodes(names):
|
||||
names = names.strip().split()
|
||||
items = [_NamedIntConstant(i, name) for i, name in enumerate(names)]
|
||||
globals().update({item.name: item for item in items})
|
||||
return items
|
||||
|
||||
# operators
|
||||
# failure=0 success=1 (just because it looks better that way :-)
|
||||
OPCODES = _makecodes("""
|
||||
FAILURE SUCCESS
|
||||
|
||||
FAILURE = "failure"
|
||||
SUCCESS = "success"
|
||||
ANY ANY_ALL
|
||||
ASSERT ASSERT_NOT
|
||||
AT
|
||||
BRANCH
|
||||
CALL
|
||||
CATEGORY
|
||||
CHARSET BIGCHARSET
|
||||
GROUPREF GROUPREF_EXISTS GROUPREF_IGNORE
|
||||
IN IN_IGNORE
|
||||
INFO
|
||||
JUMP
|
||||
LITERAL LITERAL_IGNORE
|
||||
MARK
|
||||
MAX_UNTIL
|
||||
MIN_UNTIL
|
||||
NOT_LITERAL NOT_LITERAL_IGNORE
|
||||
NEGATE
|
||||
RANGE
|
||||
REPEAT
|
||||
REPEAT_ONE
|
||||
SUBPATTERN
|
||||
MIN_REPEAT_ONE
|
||||
RANGE_IGNORE
|
||||
|
||||
ANY = "any"
|
||||
ANY_ALL = "any_all"
|
||||
ASSERT = "assert"
|
||||
ASSERT_NOT = "assert_not"
|
||||
AT = "at"
|
||||
BIGCHARSET = "bigcharset"
|
||||
BRANCH = "branch"
|
||||
CALL = "call"
|
||||
CATEGORY = "category"
|
||||
CHARSET = "charset"
|
||||
GROUPREF = "groupref"
|
||||
GROUPREF_IGNORE = "groupref_ignore"
|
||||
GROUPREF_EXISTS = "groupref_exists"
|
||||
IN = "in"
|
||||
IN_IGNORE = "in_ignore"
|
||||
INFO = "info"
|
||||
JUMP = "jump"
|
||||
LITERAL = "literal"
|
||||
LITERAL_IGNORE = "literal_ignore"
|
||||
MARK = "mark"
|
||||
MAX_REPEAT = "max_repeat"
|
||||
MAX_UNTIL = "max_until"
|
||||
MIN_REPEAT = "min_repeat"
|
||||
MIN_UNTIL = "min_until"
|
||||
NEGATE = "negate"
|
||||
NOT_LITERAL = "not_literal"
|
||||
NOT_LITERAL_IGNORE = "not_literal_ignore"
|
||||
RANGE = "range"
|
||||
REPEAT = "repeat"
|
||||
REPEAT_ONE = "repeat_one"
|
||||
SUBPATTERN = "subpattern"
|
||||
MIN_REPEAT_ONE = "min_repeat_one"
|
||||
MIN_REPEAT MAX_REPEAT
|
||||
""")
|
||||
del OPCODES[-2:] # remove MIN_REPEAT and MAX_REPEAT
|
||||
|
||||
# positions
|
||||
AT_BEGINNING = "at_beginning"
|
||||
AT_BEGINNING_LINE = "at_beginning_line"
|
||||
AT_BEGINNING_STRING = "at_beginning_string"
|
||||
AT_BOUNDARY = "at_boundary"
|
||||
AT_NON_BOUNDARY = "at_non_boundary"
|
||||
AT_END = "at_end"
|
||||
AT_END_LINE = "at_end_line"
|
||||
AT_END_STRING = "at_end_string"
|
||||
AT_LOC_BOUNDARY = "at_loc_boundary"
|
||||
AT_LOC_NON_BOUNDARY = "at_loc_non_boundary"
|
||||
AT_UNI_BOUNDARY = "at_uni_boundary"
|
||||
AT_UNI_NON_BOUNDARY = "at_uni_non_boundary"
|
||||
ATCODES = _makecodes("""
|
||||
AT_BEGINNING AT_BEGINNING_LINE AT_BEGINNING_STRING
|
||||
AT_BOUNDARY AT_NON_BOUNDARY
|
||||
AT_END AT_END_LINE AT_END_STRING
|
||||
AT_LOC_BOUNDARY AT_LOC_NON_BOUNDARY
|
||||
AT_UNI_BOUNDARY AT_UNI_NON_BOUNDARY
|
||||
""")
|
||||
|
||||
# categories
|
||||
CATEGORY_DIGIT = "category_digit"
|
||||
CATEGORY_NOT_DIGIT = "category_not_digit"
|
||||
CATEGORY_SPACE = "category_space"
|
||||
CATEGORY_NOT_SPACE = "category_not_space"
|
||||
CATEGORY_WORD = "category_word"
|
||||
CATEGORY_NOT_WORD = "category_not_word"
|
||||
CATEGORY_LINEBREAK = "category_linebreak"
|
||||
CATEGORY_NOT_LINEBREAK = "category_not_linebreak"
|
||||
CATEGORY_LOC_WORD = "category_loc_word"
|
||||
CATEGORY_LOC_NOT_WORD = "category_loc_not_word"
|
||||
CATEGORY_UNI_DIGIT = "category_uni_digit"
|
||||
CATEGORY_UNI_NOT_DIGIT = "category_uni_not_digit"
|
||||
CATEGORY_UNI_SPACE = "category_uni_space"
|
||||
CATEGORY_UNI_NOT_SPACE = "category_uni_not_space"
|
||||
CATEGORY_UNI_WORD = "category_uni_word"
|
||||
CATEGORY_UNI_NOT_WORD = "category_uni_not_word"
|
||||
CATEGORY_UNI_LINEBREAK = "category_uni_linebreak"
|
||||
CATEGORY_UNI_NOT_LINEBREAK = "category_uni_not_linebreak"
|
||||
CHCODES = _makecodes("""
|
||||
CATEGORY_DIGIT CATEGORY_NOT_DIGIT
|
||||
CATEGORY_SPACE CATEGORY_NOT_SPACE
|
||||
CATEGORY_WORD CATEGORY_NOT_WORD
|
||||
CATEGORY_LINEBREAK CATEGORY_NOT_LINEBREAK
|
||||
CATEGORY_LOC_WORD CATEGORY_LOC_NOT_WORD
|
||||
CATEGORY_UNI_DIGIT CATEGORY_UNI_NOT_DIGIT
|
||||
CATEGORY_UNI_SPACE CATEGORY_UNI_NOT_SPACE
|
||||
CATEGORY_UNI_WORD CATEGORY_UNI_NOT_WORD
|
||||
CATEGORY_UNI_LINEBREAK CATEGORY_UNI_NOT_LINEBREAK
|
||||
""")
|
||||
|
||||
OPCODES = [
|
||||
|
||||
# failure=0 success=1 (just because it looks better that way :-)
|
||||
FAILURE, SUCCESS,
|
||||
|
||||
ANY, ANY_ALL,
|
||||
ASSERT, ASSERT_NOT,
|
||||
AT,
|
||||
BRANCH,
|
||||
CALL,
|
||||
CATEGORY,
|
||||
CHARSET, BIGCHARSET,
|
||||
GROUPREF, GROUPREF_EXISTS, GROUPREF_IGNORE,
|
||||
IN, IN_IGNORE,
|
||||
INFO,
|
||||
JUMP,
|
||||
LITERAL, LITERAL_IGNORE,
|
||||
MARK,
|
||||
MAX_UNTIL,
|
||||
MIN_UNTIL,
|
||||
NOT_LITERAL, NOT_LITERAL_IGNORE,
|
||||
NEGATE,
|
||||
RANGE,
|
||||
REPEAT,
|
||||
REPEAT_ONE,
|
||||
SUBPATTERN,
|
||||
MIN_REPEAT_ONE
|
||||
|
||||
]
|
||||
|
||||
ATCODES = [
|
||||
AT_BEGINNING, AT_BEGINNING_LINE, AT_BEGINNING_STRING, AT_BOUNDARY,
|
||||
AT_NON_BOUNDARY, AT_END, AT_END_LINE, AT_END_STRING,
|
||||
AT_LOC_BOUNDARY, AT_LOC_NON_BOUNDARY, AT_UNI_BOUNDARY,
|
||||
AT_UNI_NON_BOUNDARY
|
||||
]
|
||||
|
||||
CHCODES = [
|
||||
CATEGORY_DIGIT, CATEGORY_NOT_DIGIT, CATEGORY_SPACE,
|
||||
CATEGORY_NOT_SPACE, CATEGORY_WORD, CATEGORY_NOT_WORD,
|
||||
CATEGORY_LINEBREAK, CATEGORY_NOT_LINEBREAK, CATEGORY_LOC_WORD,
|
||||
CATEGORY_LOC_NOT_WORD, CATEGORY_UNI_DIGIT, CATEGORY_UNI_NOT_DIGIT,
|
||||
CATEGORY_UNI_SPACE, CATEGORY_UNI_NOT_SPACE, CATEGORY_UNI_WORD,
|
||||
CATEGORY_UNI_NOT_WORD, CATEGORY_UNI_LINEBREAK,
|
||||
CATEGORY_UNI_NOT_LINEBREAK
|
||||
]
|
||||
|
||||
def makedict(list):
|
||||
d = {}
|
||||
i = 0
|
||||
for item in list:
|
||||
d[item] = i
|
||||
i = i + 1
|
||||
return d
|
||||
|
||||
OPCODES = makedict(OPCODES)
|
||||
ATCODES = makedict(ATCODES)
|
||||
CHCODES = makedict(CHCODES)
|
||||
|
||||
# replacement operations for "ignore case" mode
|
||||
OP_IGNORE = {
|
||||
GROUPREF: GROUPREF_IGNORE,
|
||||
IN: IN_IGNORE,
|
||||
LITERAL: LITERAL_IGNORE,
|
||||
NOT_LITERAL: NOT_LITERAL_IGNORE
|
||||
NOT_LITERAL: NOT_LITERAL_IGNORE,
|
||||
RANGE: RANGE_IGNORE,
|
||||
}
|
||||
|
||||
AT_MULTILINE = {
|
||||
@@ -219,12 +179,11 @@ SRE_INFO_CHARSET = 4 # pattern starts with character from given set
|
||||
|
||||
if __name__ == "__main__":
|
||||
def dump(f, d, prefix):
|
||||
items = d.items()
|
||||
items.sort(key=lambda a: a[1])
|
||||
for k, v in items:
|
||||
f.write("#define %s_%s %s\n" % (prefix, k.upper(), v))
|
||||
f = open("sre_constants.h", "w")
|
||||
f.write("""\
|
||||
items = sorted(d)
|
||||
for item in items:
|
||||
f.write("#define %s_%s %d\n" % (prefix, item, item))
|
||||
with open("sre_constants.h", "w") as f:
|
||||
f.write("""\
|
||||
/*
|
||||
* Secret Labs' Regular Expression Engine
|
||||
*
|
||||
@@ -240,23 +199,24 @@ if __name__ == "__main__":
|
||||
|
||||
""")
|
||||
|
||||
f.write("#define SRE_MAGIC %d\n" % MAGIC)
|
||||
f.write("#define SRE_MAGIC %d\n" % MAGIC)
|
||||
|
||||
dump(f, OPCODES, "SRE_OP")
|
||||
dump(f, ATCODES, "SRE")
|
||||
dump(f, CHCODES, "SRE")
|
||||
dump(f, OPCODES, "SRE_OP")
|
||||
dump(f, ATCODES, "SRE")
|
||||
dump(f, CHCODES, "SRE")
|
||||
|
||||
f.write("#define SRE_FLAG_TEMPLATE %d\n" % SRE_FLAG_TEMPLATE)
|
||||
f.write("#define SRE_FLAG_IGNORECASE %d\n" % SRE_FLAG_IGNORECASE)
|
||||
f.write("#define SRE_FLAG_LOCALE %d\n" % SRE_FLAG_LOCALE)
|
||||
f.write("#define SRE_FLAG_MULTILINE %d\n" % SRE_FLAG_MULTILINE)
|
||||
f.write("#define SRE_FLAG_DOTALL %d\n" % SRE_FLAG_DOTALL)
|
||||
f.write("#define SRE_FLAG_UNICODE %d\n" % SRE_FLAG_UNICODE)
|
||||
f.write("#define SRE_FLAG_VERBOSE %d\n" % SRE_FLAG_VERBOSE)
|
||||
f.write("#define SRE_FLAG_TEMPLATE %d\n" % SRE_FLAG_TEMPLATE)
|
||||
f.write("#define SRE_FLAG_IGNORECASE %d\n" % SRE_FLAG_IGNORECASE)
|
||||
f.write("#define SRE_FLAG_LOCALE %d\n" % SRE_FLAG_LOCALE)
|
||||
f.write("#define SRE_FLAG_MULTILINE %d\n" % SRE_FLAG_MULTILINE)
|
||||
f.write("#define SRE_FLAG_DOTALL %d\n" % SRE_FLAG_DOTALL)
|
||||
f.write("#define SRE_FLAG_UNICODE %d\n" % SRE_FLAG_UNICODE)
|
||||
f.write("#define SRE_FLAG_VERBOSE %d\n" % SRE_FLAG_VERBOSE)
|
||||
f.write("#define SRE_FLAG_DEBUG %d\n" % SRE_FLAG_DEBUG)
|
||||
f.write("#define SRE_FLAG_ASCII %d\n" % SRE_FLAG_ASCII)
|
||||
|
||||
f.write("#define SRE_INFO_PREFIX %d\n" % SRE_INFO_PREFIX)
|
||||
f.write("#define SRE_INFO_LITERAL %d\n" % SRE_INFO_LITERAL)
|
||||
f.write("#define SRE_INFO_CHARSET %d\n" % SRE_INFO_CHARSET)
|
||||
f.write("#define SRE_INFO_PREFIX %d\n" % SRE_INFO_PREFIX)
|
||||
f.write("#define SRE_INFO_LITERAL %d\n" % SRE_INFO_LITERAL)
|
||||
f.write("#define SRE_INFO_CHARSET %d\n" % SRE_INFO_CHARSET)
|
||||
|
||||
f.close()
|
||||
print("done")
|
||||
Vendored
+505
-341
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user