mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Add re.py, sre_{compile,constants,parse}.py from CPython 3.0
This commit is contained in:
1 parent
015cbc2bb1
commit
0540fc19e4
4 files changed
+1939
No files matched your search
@@ -0,0 +1,352 @@
|
||||
#
|
||||
# Secret Labs' Regular Expression Engine
|
||||
#
|
||||
# re-compatible interface for the sre matching engine
|
||||
#
|
||||
# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved.
|
||||
#
|
||||
# This version of the SRE library can be redistributed under CNRI's
|
||||
# Python 1.6 license. For any other use, please contact Secret Labs
|
||||
# AB (info@pythonware.com).
|
||||
#
|
||||
# Portions of this engine have been developed in cooperation with
|
||||
# CNRI. Hewlett-Packard provided funding for 1.6 integration and
|
||||
# other compatibility work.
|
||||
#
|
||||
|
||||
r"""Support for regular expressions (RE).
|
||||
|
||||
This module provides regular expression matching operations similar to
|
||||
those found in Perl. It supports both 8-bit and Unicode strings; both
|
||||
the pattern and the strings being processed can contain null bytes and
|
||||
characters outside the US ASCII range.
|
||||
|
||||
Regular expressions can contain both special and ordinary characters.
|
||||
Most ordinary characters, like "A", "a", or "0", are the simplest
|
||||
regular expressions; they simply match themselves. You can
|
||||
concatenate ordinary characters, so last matches the string 'last'.
|
||||
|
||||
The special characters are:
|
||||
"." Matches any character except a newline.
|
||||
"^" Matches the start of the string.
|
||||
"$" Matches the end of the string or just before the newline at
|
||||
the end of the string.
|
||||
"*" Matches 0 or more (greedy) repetitions of the preceding RE.
|
||||
Greedy means that it will match as many repetitions as possible.
|
||||
"+" Matches 1 or more (greedy) repetitions of the preceding RE.
|
||||
"?" Matches 0 or 1 (greedy) of the preceding RE.
|
||||
*?,+?,?? Non-greedy versions of the previous three special characters.
|
||||
{m,n} Matches from m to n repetitions of the preceding RE.
|
||||
{m,n}? Non-greedy version of the above.
|
||||
"\\" Either escapes special characters or signals a special sequence.
|
||||
[] Indicates a set of characters.
|
||||
A "^" as the first character indicates a complementing set.
|
||||
"|" A|B, creates an RE that will match either A or B.
|
||||
(...) Matches the RE inside the parentheses.
|
||||
The contents can be retrieved or matched later in the string.
|
||||
(?aiLmsux) Set the A, I, L, M, S, U, or X flag for the RE (see below).
|
||||
(?:...) Non-grouping version of regular parentheses.
|
||||
(?P<name>...) The substring matched by the group is accessible by name.
|
||||
(?P=name) Matches the text matched earlier by the group named name.
|
||||
(?#...) A comment; ignored.
|
||||
(?=...) Matches if ... matches next, but doesn't consume the string.
|
||||
(?!...) Matches if ... doesn't match next.
|
||||
(?<=...) Matches if preceded by ... (must be fixed length).
|
||||
(?<!...) Matches if not preceded by ... (must be fixed length).
|
||||
(?(id/name)yes|no) Matches yes pattern if the group with id/name matched,
|
||||
the (optional) no pattern otherwise.
|
||||
|
||||
The special sequences consist of "\\" and a character from the list
|
||||
below. If the ordinary character is not on the list, then the
|
||||
resulting RE will match the second character.
|
||||
\number Matches the contents of the group of the same number.
|
||||
\A Matches only at the start of the string.
|
||||
\Z Matches only at the end of the string.
|
||||
\b Matches the empty string, but only at the start or end of a word.
|
||||
\B Matches the empty string, but not at the start or end of a word.
|
||||
\d Matches any decimal digit; equivalent to the set [0-9] 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 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].
|
||||
\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
|
||||
range of Unicode alphanumeric characters (letters plus digits
|
||||
plus underscore).
|
||||
With LOCALE, it will match the set [0-9_] plus characters defined
|
||||
as letters for the current locale.
|
||||
\W Matches the complement of \w.
|
||||
\\ 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.
|
||||
|
||||
Some of the functions in this module takes flags as optional parameters:
|
||||
A ASCII For string patterns, make \w, \W, \b, \B, \d, \D
|
||||
match the corresponding ASCII character categories
|
||||
(rather than the whole Unicode categories, which is the
|
||||
default).
|
||||
For bytes patterns, this flag is the only available
|
||||
behaviour and needn't be specified.
|
||||
I IGNORECASE Perform case-insensitive matching.
|
||||
L LOCALE Make \w, \W, \b, \B, dependent on the current locale.
|
||||
M MULTILINE "^" matches the beginning of lines (after a newline)
|
||||
as well as the string.
|
||||
"$" matches the end of lines (before a newline) as well
|
||||
as the end of the string.
|
||||
S DOTALL "." matches any character at all, including the newline.
|
||||
X VERBOSE Ignore whitespace and comments for nicer looking RE's.
|
||||
U UNICODE For compatibility only. Ignored for string patterns (it
|
||||
is the default), and forbidden for bytes patterns.
|
||||
|
||||
This module also defines an exception 'error'.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import sre_compile
|
||||
import sre_parse
|
||||
|
||||
# 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" ]
|
||||
|
||||
__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
|
||||
|
||||
# sre exception
|
||||
error = sre_compile.error
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# public interface
|
||||
|
||||
def match(pattern, string, flags=0):
|
||||
"""Try to apply the pattern at the start of the string, returning
|
||||
a match object, or None if no match was found."""
|
||||
return _compile(pattern, flags).match(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):
|
||||
"""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)
|
||||
|
||||
def subn(pattern, repl, string, count=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
|
||||
string by the replacement repl. number is the number of
|
||||
substitutions that were made. 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).subn(repl, string, count)
|
||||
|
||||
def split(pattern, string, maxsplit=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)
|
||||
|
||||
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
|
||||
has more than one group.
|
||||
|
||||
Empty matches are included in the result."""
|
||||
return _compile(pattern, flags).findall(string)
|
||||
|
||||
if 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.
|
||||
|
||||
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"
|
||||
_cache.clear()
|
||||
_cache_repl.clear()
|
||||
|
||||
def template(pattern, flags=0):
|
||||
"Compile a template pattern, returning a pattern object"
|
||||
return _compile(pattern, flags|T)
|
||||
|
||||
_alphanum_str = frozenset(
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
_alphanum_bytes = frozenset(
|
||||
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
|
||||
|
||||
def escape(pattern):
|
||||
"Escape all non-alphanumeric characters in pattern."
|
||||
if isinstance(pattern, str):
|
||||
alphanum = _alphanum_str
|
||||
s = list(pattern)
|
||||
for i in range(len(pattern)):
|
||||
c = pattern[i]
|
||||
if c not in alphanum:
|
||||
if c == "\000":
|
||||
s[i] = "\\000"
|
||||
else:
|
||||
s[i] = "\\" + c
|
||||
return "".join(s)
|
||||
else:
|
||||
alphanum = _alphanum_bytes
|
||||
s = []
|
||||
esc = ord(b"\\")
|
||||
for c in pattern:
|
||||
if c in alphanum:
|
||||
s.append(c)
|
||||
else:
|
||||
if c == 0:
|
||||
s.extend(b"\\000")
|
||||
else:
|
||||
s.append(esc)
|
||||
s.append(c)
|
||||
return bytes(s)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# internals
|
||||
|
||||
_cache = {}
|
||||
_cache_repl = {}
|
||||
|
||||
_pattern_type = type(sre_compile.compile("", 0))
|
||||
|
||||
_MAXCACHE = 100
|
||||
|
||||
def _compile(*key):
|
||||
# internal: compile pattern
|
||||
cachekey = (type(key[0]),) + key
|
||||
p = _cache.get(cachekey)
|
||||
if p is not None:
|
||||
return p
|
||||
pattern, flags = key
|
||||
if isinstance(pattern, _pattern_type):
|
||||
if flags:
|
||||
raise ValueError(
|
||||
"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
|
||||
return p
|
||||
|
||||
def _compile_repl(*key):
|
||||
# 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
|
||||
|
||||
def _expand(pattern, match, template):
|
||||
# internal: match.expand implementation hook
|
||||
template = sre_parse.parse_template(template, pattern)
|
||||
return sre_parse.expand_template(template, match)
|
||||
|
||||
def _subx(pattern, template):
|
||||
# internal: pattern.sub/subn implementation helper
|
||||
template = _compile_repl(template, pattern)
|
||||
if not template[0] and len(template[1]) == 1:
|
||||
# literal replacement
|
||||
return template[1][0]
|
||||
def filter(match, template=template):
|
||||
return sre_parse.expand_template(template, match)
|
||||
return filter
|
||||
|
||||
# register myself for pickling
|
||||
|
||||
import copyreg
|
||||
|
||||
def _pickle(p):
|
||||
return _compile, (p.pattern, p.flags)
|
||||
|
||||
copyreg.pickle(_pattern_type, _pickle, _compile)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# experimental stuff (see python-dev discussions for details)
|
||||
|
||||
class Scanner:
|
||||
def __init__(self, lexicon, flags=0):
|
||||
from sre_constants import BRANCH, SUBPATTERN
|
||||
self.lexicon = lexicon
|
||||
# combine phrases into a compound pattern
|
||||
p = []
|
||||
s = sre_parse.Pattern()
|
||||
s.flags = flags
|
||||
for phrase, action in lexicon:
|
||||
p.append(sre_parse.SubPattern(s, [
|
||||
(SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))),
|
||||
]))
|
||||
s.groups = len(p)+1
|
||||
p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
|
||||
self.scanner = sre_compile.compile(p)
|
||||
def scan(self, string):
|
||||
result = []
|
||||
append = result.append
|
||||
match = self.scanner.scanner(string).match
|
||||
i = 0
|
||||
while 1:
|
||||
m = match()
|
||||
if not m:
|
||||
break
|
||||
j = m.end()
|
||||
if i == j:
|
||||
break
|
||||
action = self.lexicon[m.lastindex-1][1]
|
||||
if hasattr(action, "__call__"):
|
||||
self.match = m
|
||||
action = action(self, m.group())
|
||||
if action is not None:
|
||||
append(action)
|
||||
i = j
|
||||
return result, string[i:]
|
||||
Vendored
+515
@@ -0,0 +1,515 @@
|
||||
#
|
||||
# Secret Labs' Regular Expression Engine
|
||||
#
|
||||
# convert template to internal format
|
||||
#
|
||||
# Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved.
|
||||
#
|
||||
# See the sre.py file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""Internal support module for sre"""
|
||||
|
||||
import _sre, sys
|
||||
import sre_parse
|
||||
from sre_constants import *
|
||||
|
||||
assert _sre.MAGIC == MAGIC, "SRE module mismatch"
|
||||
|
||||
if _sre.CODESIZE == 2:
|
||||
MAXCODE = 65535
|
||||
else:
|
||||
MAXCODE = 0xFFFFFFFF
|
||||
|
||||
def _identityfunction(x):
|
||||
return x
|
||||
|
||||
_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])
|
||||
|
||||
def _compile(code, pattern, flags):
|
||||
# internal: compile a (sub)pattern
|
||||
emit = code.append
|
||||
_len = len
|
||||
LITERAL_CODES = _LITERAL_CODES
|
||||
REPEATING_CODES = _REPEATING_CODES
|
||||
SUCCESS_CODES = _SUCCESS_CODES
|
||||
ASSERT_CODES = _ASSERT_CODES
|
||||
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))
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
emit(av)
|
||||
elif op is IN:
|
||||
if flags & SRE_FLAG_IGNORECASE:
|
||||
emit(OPCODES[OP_IGNORE[op]])
|
||||
def fixup(literal, flags=flags):
|
||||
return _sre.getlower(literal, flags)
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
fixup = _identityfunction
|
||||
skip = _len(code); emit(0)
|
||||
_compile_charset(av, flags, code, fixup)
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is ANY:
|
||||
if flags & SRE_FLAG_DOTALL:
|
||||
emit(OPCODES[ANY_ALL])
|
||||
else:
|
||||
emit(OPCODES[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
|
||||
elif _simple(av) and op is not REPEAT:
|
||||
if op is MAX_REPEAT:
|
||||
emit(OPCODES[REPEAT_ONE])
|
||||
else:
|
||||
emit(OPCODES[MIN_REPEAT_ONE])
|
||||
skip = _len(code); emit(0)
|
||||
emit(av[0])
|
||||
emit(av[1])
|
||||
_compile(code, av[2], flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
code[skip] = _len(code) - skip
|
||||
else:
|
||||
emit(OPCODES[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])
|
||||
else:
|
||||
emit(OPCODES[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)
|
||||
elif op in SUCCESS_CODES:
|
||||
emit(OPCODES[op])
|
||||
elif op in ASSERT_CODES:
|
||||
emit(OPCODES[op])
|
||||
skip = _len(code); emit(0)
|
||||
if av[0] >= 0:
|
||||
emit(0) # look ahead
|
||||
else:
|
||||
lo, hi = av[1].getwidth()
|
||||
if lo != hi:
|
||||
raise error("look-behind requires fixed-width pattern")
|
||||
emit(lo) # look behind
|
||||
_compile(code, av[1], flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is CALL:
|
||||
emit(OPCODES[op])
|
||||
skip = _len(code); emit(0)
|
||||
_compile(code, av, flags)
|
||||
emit(OPCODES[SUCCESS])
|
||||
code[skip] = _len(code) - skip
|
||||
elif op is AT:
|
||||
emit(OPCODES[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:
|
||||
av = AT_UNICODE.get(av, av)
|
||||
emit(ATCODES[av])
|
||||
elif op is BRANCH:
|
||||
emit(OPCODES[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])
|
||||
tailappend(_len(code)); emit(0)
|
||||
code[skip] = _len(code) - skip
|
||||
emit(0) # end of branch
|
||||
for tail in tail:
|
||||
code[tail] = _len(code) - tail
|
||||
elif op is CATEGORY:
|
||||
emit(OPCODES[op])
|
||||
if flags & SRE_FLAG_LOCALE:
|
||||
av = CH_LOCALE[av]
|
||||
elif flags & SRE_FLAG_UNICODE:
|
||||
av = CH_UNICODE[av]
|
||||
emit(CHCODES[av])
|
||||
elif op is GROUPREF:
|
||||
if flags & SRE_FLAG_IGNORECASE:
|
||||
emit(OPCODES[OP_IGNORE[op]])
|
||||
else:
|
||||
emit(OPCODES[op])
|
||||
emit(av-1)
|
||||
elif op is GROUPREF_EXISTS:
|
||||
emit(OPCODES[op])
|
||||
emit(av[0]-1)
|
||||
skipyes = _len(code); emit(0)
|
||||
_compile(code, av[1], flags)
|
||||
if av[2]:
|
||||
emit(OPCODES[JUMP])
|
||||
skipno = _len(code); emit(0)
|
||||
code[skipyes] = _len(code) - skipyes + 1
|
||||
_compile(code, av[2], flags)
|
||||
code[skipno] = _len(code) - skipno
|
||||
else:
|
||||
code[skipyes] = _len(code) - skipyes + 1
|
||||
else:
|
||||
raise ValueError("unsupported operand type", op)
|
||||
|
||||
def _compile_charset(charset, flags, code, fixup=None):
|
||||
# compile charset subprogram
|
||||
emit = code.append
|
||||
if fixup is None:
|
||||
fixup = _identityfunction
|
||||
for op, av in _optimize_charset(charset, fixup):
|
||||
emit(OPCODES[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]))
|
||||
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]])
|
||||
else:
|
||||
emit(CHCODES[av])
|
||||
else:
|
||||
raise error("internal: unsupported set operator")
|
||||
emit(OPCODES[FAILURE])
|
||||
|
||||
def _optimize_charset(charset, fixup):
|
||||
# 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)
|
||||
# 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:
|
||||
# use literal/range
|
||||
for p, n in runs:
|
||||
if n == 1:
|
||||
outappend((LITERAL, p))
|
||||
else:
|
||||
outappend((RANGE, (p, p+n-1)))
|
||||
if len(out) < len(charset):
|
||||
return out
|
||||
else:
|
||||
# use bitmap
|
||||
data = _mk_bitmap(charmap)
|
||||
outappend((CHARSET, data))
|
||||
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 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.
|
||||
|
||||
# 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]
|
||||
comps = {}
|
||||
mapping = [0]*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)]
|
||||
|
||||
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
|
||||
prefix = []
|
||||
prefixappend = prefix.append
|
||||
prefix_skip = 0
|
||||
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 op is LITERAL:
|
||||
charsetappend((op, av))
|
||||
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
|
||||
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
|
||||
else:
|
||||
charset = c
|
||||
elif op is IN:
|
||||
charset = av
|
||||
## if prefix:
|
||||
## print "*** PREFIX", prefix, prefix_skip
|
||||
## if charset:
|
||||
## print "*** CHARSET", charset
|
||||
# add an info block
|
||||
emit = code.append
|
||||
emit(OPCODES[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
|
||||
elif charset:
|
||||
mask = mask + SRE_INFO_CHARSET
|
||||
emit(mask)
|
||||
# pattern length
|
||||
if lo < MAXCODE:
|
||||
emit(lo)
|
||||
else:
|
||||
emit(MAXCODE)
|
||||
prefix = prefix[:MAXCODE]
|
||||
if hi < MAXCODE:
|
||||
emit(hi)
|
||||
else:
|
||||
emit(0)
|
||||
# add literal prefix
|
||||
if prefix:
|
||||
emit(len(prefix)) # length
|
||||
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
|
||||
elif charset:
|
||||
_compile_charset(charset, flags, code)
|
||||
code[skip] = len(code) - skip
|
||||
|
||||
def isstring(obj):
|
||||
return isinstance(obj, (str, bytes))
|
||||
|
||||
def _code(p, flags):
|
||||
|
||||
flags = p.pattern.flags | flags
|
||||
code = []
|
||||
|
||||
# compile info block
|
||||
_compile_info(code, p, flags)
|
||||
|
||||
# compile the pattern
|
||||
_compile(code, p.data, flags)
|
||||
|
||||
code.append(OPCODES[SUCCESS])
|
||||
|
||||
return code
|
||||
|
||||
def compile(p, flags=0):
|
||||
# internal: convert pattern list to internal format
|
||||
|
||||
if isstring(p):
|
||||
pattern = p
|
||||
p = sre_parse.parse(p, flags)
|
||||
else:
|
||||
pattern = None
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
# map in either direction
|
||||
groupindex = p.pattern.groupdict
|
||||
indexgroup = [None] * p.pattern.groups
|
||||
for k, i in groupindex.items():
|
||||
indexgroup[i] = k
|
||||
|
||||
return _sre.compile(
|
||||
pattern, flags | p.pattern.flags, code,
|
||||
p.pattern.groups-1,
|
||||
groupindex, indexgroup
|
||||
)
|
||||
Vendored
+262
@@ -0,0 +1,262 @@
|
||||
#
|
||||
# Secret Labs' Regular Expression Engine
|
||||
#
|
||||
# various symbols used by the regular expression engine.
|
||||
# run this script to update the _sre include files!
|
||||
#
|
||||
# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved.
|
||||
#
|
||||
# See the sre.py file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""Internal support module for sre"""
|
||||
|
||||
# update when constants are added or removed
|
||||
|
||||
MAGIC = 20031017
|
||||
|
||||
# max code word in this release
|
||||
|
||||
MAXREPEAT = 65535
|
||||
|
||||
# SRE standard exception (access as sre.error)
|
||||
# should this really be here?
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
# operators
|
||||
|
||||
FAILURE = "failure"
|
||||
SUCCESS = "success"
|
||||
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
AT_MULTILINE = {
|
||||
AT_BEGINNING: AT_BEGINNING_LINE,
|
||||
AT_END: AT_END_LINE
|
||||
}
|
||||
|
||||
AT_LOCALE = {
|
||||
AT_BOUNDARY: AT_LOC_BOUNDARY,
|
||||
AT_NON_BOUNDARY: AT_LOC_NON_BOUNDARY
|
||||
}
|
||||
|
||||
AT_UNICODE = {
|
||||
AT_BOUNDARY: AT_UNI_BOUNDARY,
|
||||
AT_NON_BOUNDARY: AT_UNI_NON_BOUNDARY
|
||||
}
|
||||
|
||||
CH_LOCALE = {
|
||||
CATEGORY_DIGIT: CATEGORY_DIGIT,
|
||||
CATEGORY_NOT_DIGIT: CATEGORY_NOT_DIGIT,
|
||||
CATEGORY_SPACE: CATEGORY_SPACE,
|
||||
CATEGORY_NOT_SPACE: CATEGORY_NOT_SPACE,
|
||||
CATEGORY_WORD: CATEGORY_LOC_WORD,
|
||||
CATEGORY_NOT_WORD: CATEGORY_LOC_NOT_WORD,
|
||||
CATEGORY_LINEBREAK: CATEGORY_LINEBREAK,
|
||||
CATEGORY_NOT_LINEBREAK: CATEGORY_NOT_LINEBREAK
|
||||
}
|
||||
|
||||
CH_UNICODE = {
|
||||
CATEGORY_DIGIT: CATEGORY_UNI_DIGIT,
|
||||
CATEGORY_NOT_DIGIT: CATEGORY_UNI_NOT_DIGIT,
|
||||
CATEGORY_SPACE: CATEGORY_UNI_SPACE,
|
||||
CATEGORY_NOT_SPACE: CATEGORY_UNI_NOT_SPACE,
|
||||
CATEGORY_WORD: CATEGORY_UNI_WORD,
|
||||
CATEGORY_NOT_WORD: CATEGORY_UNI_NOT_WORD,
|
||||
CATEGORY_LINEBREAK: CATEGORY_UNI_LINEBREAK,
|
||||
CATEGORY_NOT_LINEBREAK: CATEGORY_UNI_NOT_LINEBREAK
|
||||
}
|
||||
|
||||
# flags
|
||||
SRE_FLAG_TEMPLATE = 1 # template mode (disable backtracking)
|
||||
SRE_FLAG_IGNORECASE = 2 # case insensitive
|
||||
SRE_FLAG_LOCALE = 4 # honour system locale
|
||||
SRE_FLAG_MULTILINE = 8 # treat target as multiline string
|
||||
SRE_FLAG_DOTALL = 16 # treat target as a single string
|
||||
SRE_FLAG_UNICODE = 32 # use unicode "locale"
|
||||
SRE_FLAG_VERBOSE = 64 # ignore whitespace and comments
|
||||
SRE_FLAG_DEBUG = 128 # debugging
|
||||
SRE_FLAG_ASCII = 256 # use ascii "locale"
|
||||
|
||||
# flags for INFO primitive
|
||||
SRE_INFO_PREFIX = 1 # has prefix
|
||||
SRE_INFO_LITERAL = 2 # entire pattern is literal (given by prefix)
|
||||
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("""\
|
||||
/*
|
||||
* Secret Labs' Regular Expression Engine
|
||||
*
|
||||
* regular expression matching engine
|
||||
*
|
||||
* NOTE: This file is generated by sre_constants.py. If you need
|
||||
* to change anything in here, edit sre_constants.py and run it.
|
||||
*
|
||||
* Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved.
|
||||
*
|
||||
* See the _sre.c file for information on usage and redistribution.
|
||||
*/
|
||||
|
||||
""")
|
||||
|
||||
f.write("#define SRE_MAGIC %d\n" % MAGIC)
|
||||
|
||||
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_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
+810
@@ -0,0 +1,810 @@
|
||||
#
|
||||
# Secret Labs' Regular Expression Engine
|
||||
#
|
||||
# convert re-style regular expression to sre pattern
|
||||
#
|
||||
# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved.
|
||||
#
|
||||
# See the sre.py file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""Internal support module for sre"""
|
||||
|
||||
# XXX: show string offset and offending character for all errors
|
||||
|
||||
import sys
|
||||
|
||||
from sre_constants import *
|
||||
|
||||
SPECIAL_CHARS = ".\\[{()*+?^$|"
|
||||
REPEAT_CHARS = "*+?{"
|
||||
|
||||
DIGITS = set("0123456789")
|
||||
|
||||
OCTDIGITS = set("01234567")
|
||||
HEXDIGITS = set("0123456789abcdefABCDEF")
|
||||
|
||||
WHITESPACE = set(" \t\n\r\v\f")
|
||||
|
||||
ESCAPES = {
|
||||
r"\a": (LITERAL, ord("\a")),
|
||||
r"\b": (LITERAL, ord("\b")),
|
||||
r"\f": (LITERAL, ord("\f")),
|
||||
r"\n": (LITERAL, ord("\n")),
|
||||
r"\r": (LITERAL, ord("\r")),
|
||||
r"\t": (LITERAL, ord("\t")),
|
||||
r"\v": (LITERAL, ord("\v")),
|
||||
r"\\": (LITERAL, ord("\\"))
|
||||
}
|
||||
|
||||
CATEGORIES = {
|
||||
r"\A": (AT, AT_BEGINNING_STRING), # start of string
|
||||
r"\b": (AT, AT_BOUNDARY),
|
||||
r"\B": (AT, AT_NON_BOUNDARY),
|
||||
r"\d": (IN, [(CATEGORY, CATEGORY_DIGIT)]),
|
||||
r"\D": (IN, [(CATEGORY, CATEGORY_NOT_DIGIT)]),
|
||||
r"\s": (IN, [(CATEGORY, CATEGORY_SPACE)]),
|
||||
r"\S": (IN, [(CATEGORY, CATEGORY_NOT_SPACE)]),
|
||||
r"\w": (IN, [(CATEGORY, CATEGORY_WORD)]),
|
||||
r"\W": (IN, [(CATEGORY, CATEGORY_NOT_WORD)]),
|
||||
r"\Z": (AT, AT_END_STRING), # end of string
|
||||
}
|
||||
|
||||
FLAGS = {
|
||||
# standard flags
|
||||
"i": SRE_FLAG_IGNORECASE,
|
||||
"L": SRE_FLAG_LOCALE,
|
||||
"m": SRE_FLAG_MULTILINE,
|
||||
"s": SRE_FLAG_DOTALL,
|
||||
"x": SRE_FLAG_VERBOSE,
|
||||
# extensions
|
||||
"a": SRE_FLAG_ASCII,
|
||||
"t": SRE_FLAG_TEMPLATE,
|
||||
"u": SRE_FLAG_UNICODE,
|
||||
}
|
||||
|
||||
class Pattern:
|
||||
# master pattern object. keeps track of global attributes
|
||||
def __init__(self):
|
||||
self.flags = 0
|
||||
self.open = []
|
||||
self.groups = 1
|
||||
self.groupdict = {}
|
||||
def opengroup(self, name=None):
|
||||
gid = self.groups
|
||||
self.groups = gid + 1
|
||||
if name is not None:
|
||||
ogid = self.groupdict.get(name, None)
|
||||
if ogid is not None:
|
||||
raise error("redefinition of group name %s as group %d; "
|
||||
"was group %d" % (repr(name), gid, ogid))
|
||||
self.groupdict[name] = gid
|
||||
self.open.append(gid)
|
||||
return gid
|
||||
def closegroup(self, gid):
|
||||
self.open.remove(gid)
|
||||
def checkgroup(self, gid):
|
||||
return gid < self.groups and gid not in self.open
|
||||
|
||||
class SubPattern:
|
||||
# a subpattern, in intermediate form
|
||||
def __init__(self, pattern, data=None):
|
||||
self.pattern = pattern
|
||||
if data is None:
|
||||
data = []
|
||||
self.data = data
|
||||
self.width = None
|
||||
def dump(self, level=0):
|
||||
nl = 1
|
||||
seqtypes = (tuple, list)
|
||||
for op, av in self.data:
|
||||
print(level*" " + op, end=' '); nl = 0
|
||||
if op == "in":
|
||||
# member sublanguage
|
||||
print(); nl = 1
|
||||
for op, a in av:
|
||||
print((level+1)*" " + op, a)
|
||||
elif op == "branch":
|
||||
print(); nl = 1
|
||||
i = 0
|
||||
for a in av[1]:
|
||||
if i > 0:
|
||||
print(level*" " + "or")
|
||||
a.dump(level+1); nl = 1
|
||||
i = i + 1
|
||||
elif isinstance(av, seqtypes):
|
||||
for a in av:
|
||||
if isinstance(a, SubPattern):
|
||||
if not nl: print()
|
||||
a.dump(level+1); nl = 1
|
||||
else:
|
||||
print(a, end=' ') ; nl = 0
|
||||
else:
|
||||
print(av, end=' ') ; nl = 0
|
||||
if not nl: print()
|
||||
def __repr__(self):
|
||||
return repr(self.data)
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
def __delitem__(self, index):
|
||||
del self.data[index]
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, slice):
|
||||
return SubPattern(self.pattern, self.data[index])
|
||||
return self.data[index]
|
||||
def __setitem__(self, index, code):
|
||||
self.data[index] = code
|
||||
def insert(self, index, code):
|
||||
self.data.insert(index, code)
|
||||
def append(self, code):
|
||||
self.data.append(code)
|
||||
def getwidth(self):
|
||||
# determine the width (min, max) for this subpattern
|
||||
if self.width:
|
||||
return self.width
|
||||
lo = hi = 0
|
||||
UNITCODES = (ANY, RANGE, IN, LITERAL, NOT_LITERAL, CATEGORY)
|
||||
REPEATCODES = (MIN_REPEAT, MAX_REPEAT)
|
||||
for op, av in self.data:
|
||||
if op is BRANCH:
|
||||
i = sys.maxsize
|
||||
j = 0
|
||||
for av in av[1]:
|
||||
l, h = av.getwidth()
|
||||
i = min(i, l)
|
||||
j = max(j, h)
|
||||
lo = lo + i
|
||||
hi = hi + j
|
||||
elif op is CALL:
|
||||
i, j = av.getwidth()
|
||||
lo = lo + i
|
||||
hi = hi + j
|
||||
elif op is SUBPATTERN:
|
||||
i, j = av[1].getwidth()
|
||||
lo = lo + i
|
||||
hi = hi + j
|
||||
elif op in REPEATCODES:
|
||||
i, j = av[2].getwidth()
|
||||
lo = lo + int(i) * av[0]
|
||||
hi = hi + int(j) * av[1]
|
||||
elif op in UNITCODES:
|
||||
lo = lo + 1
|
||||
hi = hi + 1
|
||||
elif op == SUCCESS:
|
||||
break
|
||||
self.width = int(min(lo, sys.maxsize)), int(min(hi, sys.maxsize))
|
||||
return self.width
|
||||
|
||||
class Tokenizer:
|
||||
def __init__(self, string):
|
||||
self.string = string
|
||||
self.index = 0
|
||||
self.__next()
|
||||
def __next(self):
|
||||
if self.index >= len(self.string):
|
||||
self.next = None
|
||||
return
|
||||
char = self.string[self.index:self.index+1]
|
||||
# Special case for the str8, since indexing returns a integer
|
||||
# XXX This is only needed for test_bug_926075 in test_re.py
|
||||
if char and isinstance(char, bytes):
|
||||
char = chr(char[0])
|
||||
if char == "\\":
|
||||
try:
|
||||
c = self.string[self.index + 1]
|
||||
except IndexError:
|
||||
raise error("bogus escape (end of line)")
|
||||
if isinstance(self.string, bytes):
|
||||
c = chr(c)
|
||||
char = char + c
|
||||
self.index = self.index + len(char)
|
||||
self.next = char
|
||||
def match(self, char, skip=1):
|
||||
if char == self.next:
|
||||
if skip:
|
||||
self.__next()
|
||||
return 1
|
||||
return 0
|
||||
def get(self):
|
||||
this = self.next
|
||||
self.__next()
|
||||
return this
|
||||
def tell(self):
|
||||
return self.index, self.next
|
||||
def seek(self, index):
|
||||
self.index, self.next = index
|
||||
|
||||
def isident(char):
|
||||
return "a" <= char <= "z" or "A" <= char <= "Z" or char == "_"
|
||||
|
||||
def isdigit(char):
|
||||
return "0" <= char <= "9"
|
||||
|
||||
def isname(name):
|
||||
# check that group name is a valid string
|
||||
if not isident(name[0]):
|
||||
return False
|
||||
for char in name[1:]:
|
||||
if not isident(char) and not isdigit(char):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _class_escape(source, escape):
|
||||
# handle escape code inside character class
|
||||
code = ESCAPES.get(escape)
|
||||
if code:
|
||||
return code
|
||||
code = CATEGORIES.get(escape)
|
||||
if code:
|
||||
return code
|
||||
try:
|
||||
c = escape[1:2]
|
||||
if c == "x":
|
||||
# hexadecimal escape (exactly two digits)
|
||||
while source.next in HEXDIGITS and len(escape) < 4:
|
||||
escape = escape + source.get()
|
||||
escape = escape[2:]
|
||||
if len(escape) != 2:
|
||||
raise error("bogus escape: %s" % repr("\\" + escape))
|
||||
return LITERAL, int(escape, 16) & 0xff
|
||||
elif c in OCTDIGITS:
|
||||
# octal escape (up to three digits)
|
||||
while source.next in OCTDIGITS and len(escape) < 4:
|
||||
escape = escape + source.get()
|
||||
escape = escape[1:]
|
||||
return LITERAL, int(escape, 8) & 0xff
|
||||
elif c in DIGITS:
|
||||
raise error("bogus escape: %s" % repr(escape))
|
||||
if len(escape) == 2:
|
||||
return LITERAL, ord(escape[1])
|
||||
except ValueError:
|
||||
pass
|
||||
raise error("bogus escape: %s" % repr(escape))
|
||||
|
||||
def _escape(source, escape, state):
|
||||
# handle escape code in expression
|
||||
code = CATEGORIES.get(escape)
|
||||
if code:
|
||||
return code
|
||||
code = ESCAPES.get(escape)
|
||||
if code:
|
||||
return code
|
||||
try:
|
||||
c = escape[1:2]
|
||||
if c == "x":
|
||||
# hexadecimal escape
|
||||
while source.next in HEXDIGITS and len(escape) < 4:
|
||||
escape = escape + source.get()
|
||||
if len(escape) != 4:
|
||||
raise ValueError
|
||||
return LITERAL, int(escape[2:], 16) & 0xff
|
||||
elif c == "0":
|
||||
# octal escape
|
||||
while source.next in OCTDIGITS and len(escape) < 4:
|
||||
escape = escape + source.get()
|
||||
return LITERAL, int(escape[1:], 8) & 0xff
|
||||
elif c in DIGITS:
|
||||
# octal escape *or* decimal group reference (sigh)
|
||||
if source.next in DIGITS:
|
||||
escape = escape + source.get()
|
||||
if (escape[1] in OCTDIGITS and escape[2] in OCTDIGITS and
|
||||
source.next in OCTDIGITS):
|
||||
# got three octal digits; this is an octal escape
|
||||
escape = escape + source.get()
|
||||
return LITERAL, int(escape[1:], 8) & 0xff
|
||||
# not an octal escape, so this is a group reference
|
||||
group = int(escape[1:])
|
||||
if group < state.groups:
|
||||
if not state.checkgroup(group):
|
||||
raise error("cannot refer to open group")
|
||||
return GROUPREF, group
|
||||
raise ValueError
|
||||
if len(escape) == 2:
|
||||
return LITERAL, ord(escape[1])
|
||||
except ValueError:
|
||||
pass
|
||||
raise error("bogus escape: %s" % repr(escape))
|
||||
|
||||
def _parse_sub(source, state, nested=1):
|
||||
# parse an alternation: a|b|c
|
||||
|
||||
items = []
|
||||
itemsappend = items.append
|
||||
sourcematch = source.match
|
||||
while 1:
|
||||
itemsappend(_parse(source, state))
|
||||
if sourcematch("|"):
|
||||
continue
|
||||
if not nested:
|
||||
break
|
||||
if not source.next or sourcematch(")", 0):
|
||||
break
|
||||
else:
|
||||
raise error("pattern not properly closed")
|
||||
|
||||
if len(items) == 1:
|
||||
return items[0]
|
||||
|
||||
subpattern = SubPattern(state)
|
||||
subpatternappend = subpattern.append
|
||||
|
||||
# check if all items share a common prefix
|
||||
while 1:
|
||||
prefix = None
|
||||
for item in items:
|
||||
if not item:
|
||||
break
|
||||
if prefix is None:
|
||||
prefix = item[0]
|
||||
elif item[0] != prefix:
|
||||
break
|
||||
else:
|
||||
# all subitems start with a common "prefix".
|
||||
# move it out of the branch
|
||||
for item in items:
|
||||
del item[0]
|
||||
subpatternappend(prefix)
|
||||
continue # check next one
|
||||
break
|
||||
|
||||
# check if the branch can be replaced by a character set
|
||||
for item in items:
|
||||
if len(item) != 1 or item[0][0] != LITERAL:
|
||||
break
|
||||
else:
|
||||
# we can store this as a character set instead of a
|
||||
# branch (the compiler may optimize this even more)
|
||||
set = []
|
||||
setappend = set.append
|
||||
for item in items:
|
||||
setappend(item[0])
|
||||
subpatternappend((IN, set))
|
||||
return subpattern
|
||||
|
||||
subpattern.append((BRANCH, (None, items)))
|
||||
return subpattern
|
||||
|
||||
def _parse_sub_cond(source, state, condgroup):
|
||||
item_yes = _parse(source, state)
|
||||
if source.match("|"):
|
||||
item_no = _parse(source, state)
|
||||
if source.match("|"):
|
||||
raise error("conditional backref with more than two branches")
|
||||
else:
|
||||
item_no = None
|
||||
if source.next and not source.match(")", 0):
|
||||
raise error("pattern not properly closed")
|
||||
subpattern = SubPattern(state)
|
||||
subpattern.append((GROUPREF_EXISTS, (condgroup, item_yes, item_no)))
|
||||
return subpattern
|
||||
|
||||
_PATTERNENDERS = set("|)")
|
||||
_ASSERTCHARS = set("=!<")
|
||||
_LOOKBEHINDASSERTCHARS = set("=!")
|
||||
_REPEATCODES = set([MIN_REPEAT, MAX_REPEAT])
|
||||
|
||||
def _parse(source, state):
|
||||
# parse a simple pattern
|
||||
subpattern = SubPattern(state)
|
||||
|
||||
# precompute constants into local variables
|
||||
subpatternappend = subpattern.append
|
||||
sourceget = source.get
|
||||
sourcematch = source.match
|
||||
_len = len
|
||||
PATTERNENDERS = _PATTERNENDERS
|
||||
ASSERTCHARS = _ASSERTCHARS
|
||||
LOOKBEHINDASSERTCHARS = _LOOKBEHINDASSERTCHARS
|
||||
REPEATCODES = _REPEATCODES
|
||||
|
||||
while 1:
|
||||
|
||||
if source.next in PATTERNENDERS:
|
||||
break # end of subpattern
|
||||
this = sourceget()
|
||||
if this is None:
|
||||
break # end of pattern
|
||||
|
||||
if state.flags & SRE_FLAG_VERBOSE:
|
||||
# skip whitespace and comments
|
||||
if this in WHITESPACE:
|
||||
continue
|
||||
if this == "#":
|
||||
while 1:
|
||||
this = sourceget()
|
||||
if this in (None, "\n"):
|
||||
break
|
||||
continue
|
||||
|
||||
if this and this[0] not in SPECIAL_CHARS:
|
||||
subpatternappend((LITERAL, ord(this)))
|
||||
|
||||
elif this == "[":
|
||||
# character set
|
||||
set = []
|
||||
setappend = set.append
|
||||
## if sourcematch(":"):
|
||||
## pass # handle character classes
|
||||
if sourcematch("^"):
|
||||
setappend((NEGATE, None))
|
||||
# check remaining characters
|
||||
start = set[:]
|
||||
while 1:
|
||||
this = sourceget()
|
||||
if this == "]" and set != start:
|
||||
break
|
||||
elif this and this[0] == "\\":
|
||||
code1 = _class_escape(source, this)
|
||||
elif this:
|
||||
code1 = LITERAL, ord(this)
|
||||
else:
|
||||
raise error("unexpected end of regular expression")
|
||||
if sourcematch("-"):
|
||||
# potential range
|
||||
this = sourceget()
|
||||
if this == "]":
|
||||
if code1[0] is IN:
|
||||
code1 = code1[1][0]
|
||||
setappend(code1)
|
||||
setappend((LITERAL, ord("-")))
|
||||
break
|
||||
elif this:
|
||||
if this[0] == "\\":
|
||||
code2 = _class_escape(source, this)
|
||||
else:
|
||||
code2 = LITERAL, ord(this)
|
||||
if code1[0] != LITERAL or code2[0] != LITERAL:
|
||||
raise error("bad character range")
|
||||
lo = code1[1]
|
||||
hi = code2[1]
|
||||
if hi < lo:
|
||||
raise error("bad character range")
|
||||
setappend((RANGE, (lo, hi)))
|
||||
else:
|
||||
raise error("unexpected end of regular expression")
|
||||
else:
|
||||
if code1[0] is IN:
|
||||
code1 = code1[1][0]
|
||||
setappend(code1)
|
||||
|
||||
# XXX: <fl> should move set optimization to compiler!
|
||||
if _len(set)==1 and set[0][0] is LITERAL:
|
||||
subpatternappend(set[0]) # optimization
|
||||
elif _len(set)==2 and set[0][0] is NEGATE and set[1][0] is LITERAL:
|
||||
subpatternappend((NOT_LITERAL, set[1][1])) # optimization
|
||||
else:
|
||||
# XXX: <fl> should add charmap optimization here
|
||||
subpatternappend((IN, set))
|
||||
|
||||
elif this and this[0] in REPEAT_CHARS:
|
||||
# repeat previous item
|
||||
if this == "?":
|
||||
min, max = 0, 1
|
||||
elif this == "*":
|
||||
min, max = 0, MAXREPEAT
|
||||
|
||||
elif this == "+":
|
||||
min, max = 1, MAXREPEAT
|
||||
elif this == "{":
|
||||
if source.next == "}":
|
||||
subpatternappend((LITERAL, ord(this)))
|
||||
continue
|
||||
here = source.tell()
|
||||
min, max = 0, MAXREPEAT
|
||||
lo = hi = ""
|
||||
while source.next in DIGITS:
|
||||
lo = lo + source.get()
|
||||
if sourcematch(","):
|
||||
while source.next in DIGITS:
|
||||
hi = hi + sourceget()
|
||||
else:
|
||||
hi = lo
|
||||
if not sourcematch("}"):
|
||||
subpatternappend((LITERAL, ord(this)))
|
||||
source.seek(here)
|
||||
continue
|
||||
if lo:
|
||||
min = int(lo)
|
||||
if hi:
|
||||
max = int(hi)
|
||||
if max < min:
|
||||
raise error("bad repeat interval")
|
||||
else:
|
||||
raise error("not supported")
|
||||
# figure out which item to repeat
|
||||
if subpattern:
|
||||
item = subpattern[-1:]
|
||||
else:
|
||||
item = None
|
||||
if not item or (_len(item) == 1 and item[0][0] == AT):
|
||||
raise error("nothing to repeat")
|
||||
if item[0][0] in REPEATCODES:
|
||||
raise error("multiple repeat")
|
||||
if sourcematch("?"):
|
||||
subpattern[-1] = (MIN_REPEAT, (min, max, item))
|
||||
else:
|
||||
subpattern[-1] = (MAX_REPEAT, (min, max, item))
|
||||
|
||||
elif this == ".":
|
||||
subpatternappend((ANY, None))
|
||||
|
||||
elif this == "(":
|
||||
group = 1
|
||||
name = None
|
||||
condgroup = None
|
||||
if sourcematch("?"):
|
||||
group = 0
|
||||
# options
|
||||
if sourcematch("P"):
|
||||
# python extensions
|
||||
if sourcematch("<"):
|
||||
# named group: skip forward to end of name
|
||||
name = ""
|
||||
while 1:
|
||||
char = sourceget()
|
||||
if char is None:
|
||||
raise error("unterminated name")
|
||||
if char == ">":
|
||||
break
|
||||
name = name + char
|
||||
group = 1
|
||||
if not isname(name):
|
||||
raise error("bad character in group name")
|
||||
elif sourcematch("="):
|
||||
# named backreference
|
||||
name = ""
|
||||
while 1:
|
||||
char = sourceget()
|
||||
if char is None:
|
||||
raise error("unterminated name")
|
||||
if char == ")":
|
||||
break
|
||||
name = name + char
|
||||
if not isname(name):
|
||||
raise error("bad character in group name")
|
||||
gid = state.groupdict.get(name)
|
||||
if gid is None:
|
||||
raise error("unknown group name")
|
||||
subpatternappend((GROUPREF, gid))
|
||||
continue
|
||||
else:
|
||||
char = sourceget()
|
||||
if char is None:
|
||||
raise error("unexpected end of pattern")
|
||||
raise error("unknown specifier: ?P%s" % char)
|
||||
elif sourcematch(":"):
|
||||
# non-capturing group
|
||||
group = 2
|
||||
elif sourcematch("#"):
|
||||
# comment
|
||||
while 1:
|
||||
if source.next is None or source.next == ")":
|
||||
break
|
||||
sourceget()
|
||||
if not sourcematch(")"):
|
||||
raise error("unbalanced parenthesis")
|
||||
continue
|
||||
elif source.next in ASSERTCHARS:
|
||||
# lookahead assertions
|
||||
char = sourceget()
|
||||
dir = 1
|
||||
if char == "<":
|
||||
if source.next not in LOOKBEHINDASSERTCHARS:
|
||||
raise error("syntax error")
|
||||
dir = -1 # lookbehind
|
||||
char = sourceget()
|
||||
p = _parse_sub(source, state)
|
||||
if not sourcematch(")"):
|
||||
raise error("unbalanced parenthesis")
|
||||
if char == "=":
|
||||
subpatternappend((ASSERT, (dir, p)))
|
||||
else:
|
||||
subpatternappend((ASSERT_NOT, (dir, p)))
|
||||
continue
|
||||
elif sourcematch("("):
|
||||
# conditional backreference group
|
||||
condname = ""
|
||||
while 1:
|
||||
char = sourceget()
|
||||
if char is None:
|
||||
raise error("unterminated name")
|
||||
if char == ")":
|
||||
break
|
||||
condname = condname + char
|
||||
group = 2
|
||||
if isname(condname):
|
||||
condgroup = state.groupdict.get(condname)
|
||||
if condgroup is None:
|
||||
raise error("unknown group name")
|
||||
else:
|
||||
try:
|
||||
condgroup = int(condname)
|
||||
except ValueError:
|
||||
raise error("bad character in group name")
|
||||
else:
|
||||
# flags
|
||||
if not source.next in FLAGS:
|
||||
raise error("unexpected end of pattern")
|
||||
while source.next in FLAGS:
|
||||
state.flags = state.flags | FLAGS[sourceget()]
|
||||
if group:
|
||||
# parse group contents
|
||||
if group == 2:
|
||||
# anonymous group
|
||||
group = None
|
||||
else:
|
||||
group = state.opengroup(name)
|
||||
if condgroup:
|
||||
p = _parse_sub_cond(source, state, condgroup)
|
||||
else:
|
||||
p = _parse_sub(source, state)
|
||||
if not sourcematch(")"):
|
||||
raise error("unbalanced parenthesis")
|
||||
if group is not None:
|
||||
state.closegroup(group)
|
||||
subpatternappend((SUBPATTERN, (group, p)))
|
||||
else:
|
||||
while 1:
|
||||
char = sourceget()
|
||||
if char is None:
|
||||
raise error("unexpected end of pattern")
|
||||
if char == ")":
|
||||
break
|
||||
raise error("unknown extension")
|
||||
|
||||
elif this == "^":
|
||||
subpatternappend((AT, AT_BEGINNING))
|
||||
|
||||
elif this == "$":
|
||||
subpattern.append((AT, AT_END))
|
||||
|
||||
elif this and this[0] == "\\":
|
||||
code = _escape(source, this, state)
|
||||
subpatternappend(code)
|
||||
|
||||
else:
|
||||
raise error("parser error")
|
||||
|
||||
return subpattern
|
||||
|
||||
def fix_flags(src, flags):
|
||||
# Check and fix flags according to the type of pattern (str or bytes)
|
||||
if isinstance(src, str):
|
||||
if not flags & SRE_FLAG_ASCII:
|
||||
flags |= SRE_FLAG_UNICODE
|
||||
elif flags & SRE_FLAG_UNICODE:
|
||||
raise ValueError("ASCII and UNICODE flags are incompatible")
|
||||
else:
|
||||
if flags & SRE_FLAG_UNICODE:
|
||||
raise ValueError("can't use UNICODE flag with a bytes pattern")
|
||||
return flags
|
||||
|
||||
def parse(str, flags=0, pattern=None):
|
||||
# parse 're' pattern into list of (opcode, argument) tuples
|
||||
|
||||
source = Tokenizer(str)
|
||||
|
||||
if pattern is None:
|
||||
pattern = Pattern()
|
||||
pattern.flags = flags
|
||||
pattern.str = str
|
||||
|
||||
p = _parse_sub(source, pattern, 0)
|
||||
p.pattern.flags = fix_flags(str, p.pattern.flags)
|
||||
|
||||
tail = source.get()
|
||||
if tail == ")":
|
||||
raise error("unbalanced parenthesis")
|
||||
elif tail:
|
||||
raise error("bogus characters at end of regular expression")
|
||||
|
||||
if flags & SRE_FLAG_DEBUG:
|
||||
p.dump()
|
||||
|
||||
if not (flags & SRE_FLAG_VERBOSE) and p.pattern.flags & SRE_FLAG_VERBOSE:
|
||||
# the VERBOSE flag was switched on inside the pattern. to be
|
||||
# on the safe side, we'll parse the whole thing again...
|
||||
return parse(str, p.pattern.flags)
|
||||
|
||||
return p
|
||||
|
||||
def parse_template(source, pattern):
|
||||
# parse 're' replacement string into list of literals and
|
||||
# group references
|
||||
s = Tokenizer(source)
|
||||
sget = s.get
|
||||
p = []
|
||||
a = p.append
|
||||
def literal(literal, p=p, pappend=a):
|
||||
if p and p[-1][0] is LITERAL:
|
||||
p[-1] = LITERAL, p[-1][1] + literal
|
||||
else:
|
||||
pappend((LITERAL, literal))
|
||||
sep = source[:0]
|
||||
if isinstance(sep, str):
|
||||
makechar = chr
|
||||
else:
|
||||
makechar = chr
|
||||
while 1:
|
||||
this = sget()
|
||||
if this is None:
|
||||
break # end of replacement string
|
||||
if this and this[0] == "\\":
|
||||
# group
|
||||
c = this[1:2]
|
||||
if c == "g":
|
||||
name = ""
|
||||
if s.match("<"):
|
||||
while 1:
|
||||
char = sget()
|
||||
if char is None:
|
||||
raise error("unterminated group name")
|
||||
if char == ">":
|
||||
break
|
||||
name = name + char
|
||||
if not name:
|
||||
raise error("bad group name")
|
||||
try:
|
||||
index = int(name)
|
||||
if index < 0:
|
||||
raise error("negative group number")
|
||||
except ValueError:
|
||||
if not isname(name):
|
||||
raise error("bad character in group name")
|
||||
try:
|
||||
index = pattern.groupindex[name]
|
||||
except KeyError:
|
||||
raise IndexError("unknown group name")
|
||||
a((MARK, index))
|
||||
elif c == "0":
|
||||
if s.next in OCTDIGITS:
|
||||
this = this + sget()
|
||||
if s.next in OCTDIGITS:
|
||||
this = this + sget()
|
||||
literal(makechar(int(this[1:], 8) & 0xff))
|
||||
elif c in DIGITS:
|
||||
isoctal = False
|
||||
if s.next in DIGITS:
|
||||
this = this + sget()
|
||||
if (c in OCTDIGITS and this[2] in OCTDIGITS and
|
||||
s.next in OCTDIGITS):
|
||||
this = this + sget()
|
||||
isoctal = True
|
||||
literal(makechar(int(this[1:], 8) & 0xff))
|
||||
if not isoctal:
|
||||
a((MARK, int(this[1:])))
|
||||
else:
|
||||
try:
|
||||
this = makechar(ESCAPES[this][1])
|
||||
except KeyError:
|
||||
pass
|
||||
literal(this)
|
||||
else:
|
||||
literal(this)
|
||||
# convert template to groups and literals lists
|
||||
i = 0
|
||||
groups = []
|
||||
groupsappend = groups.append
|
||||
literals = [None] * len(p)
|
||||
for c, s in p:
|
||||
if c is MARK:
|
||||
groupsappend((i, s))
|
||||
# literal[i] is already None
|
||||
else:
|
||||
literals[i] = s
|
||||
i = i + 1
|
||||
return groups, literals
|
||||
|
||||
def expand_template(template, match):
|
||||
g = match.group
|
||||
sep = match.string[:0]
|
||||
groups, literals = template
|
||||
literals = literals[:]
|
||||
try:
|
||||
for index, group in groups:
|
||||
literals[index] = s = g(group)
|
||||
if s is None:
|
||||
raise error("unmatched group")
|
||||
except IndexError:
|
||||
raise error("invalid group reference")
|
||||
return sep.join(literals)
|
||||
Reference in new issue
Block a user