annotate CodonSwitchTool/sre_yield.py @ 2:aad5e435e4dc draft default tip

Uploaded
author gianmarco_piccinno
date Tue, 21 May 2019 05:24:56 -0400
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
1 #!/usr/bin/env python2
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
2 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
3 # Copyright 2011-2016 Google Inc.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
4 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
5 # Licensed under the Apache License, Version 2.0 (the "License");
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
6 # you may not use this file except in compliance with the License.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
7 # You may obtain a copy of the License at
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
8 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
9 # http://www.apache.org/licenses/LICENSE-2.0
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
10 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
11 # Unless required by applicable law or agreed to in writing, software
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
12 # distributed under the License is distributed on an "AS IS" BASIS,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
14 # See the License for the specific language governing permissions and
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
15 # limitations under the License.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
16 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
17 # vim: sw=2 sts=2 et
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
18
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
19 """This module can generate all strings that match a regular expression.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
20
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
21 The regex is parsed using the SRE module that is standard in python,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
22 then the data structure is executed to form a bunch of iterators.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
23 """
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
24
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
25 __author__ = 'alexperry@google.com (Alex Perry)'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
26 __all__ = ['Values', 'AllStrings', 'AllMatches', 'ParseError']
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
27
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
28
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
29 import bisect
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
30 import math
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
31 import re
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
32 import sre_constants
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
33 import sre_parse
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
34 import string
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
35 import sys
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
36 import types
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
37
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
38 import cachingseq
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
39 import fastdivmod
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
40
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
41 try:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
42 xrange = xrange
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
43 except NameError:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
44 xrange = range
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
45
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
46 _RE_METACHARS = r'$^{}*+\\'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
47 _ESCAPED_METACHAR = r'\\[' + _RE_METACHARS + r']'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
48 ESCAPED_METACHAR_RE = re.compile(_ESCAPED_METACHAR)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
49 # ASCII by default, see https://github.com/google/sre_yield/issues/3
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
50 CHARSET = [chr(c) for c in range(256)]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
51
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
52 WORD = string.ascii_letters + string.digits + '_'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
53
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
54 try:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
55 DEFAULT_RE_FLAGS = re.ASCII
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
56 except AttributeError:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
57 DEFAULT_RE_FLAGS = 0
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
58
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
59 STATE_START, STATE_MIDDLE, STATE_END = list(range(3))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
60
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
61 def Not(chars):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
62 return ''.join(sorted(set(CHARSET) - set(chars)))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
63
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
64
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
65 CATEGORIES = {
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
66 sre_constants.CATEGORY_WORD: WORD,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
67 sre_constants.CATEGORY_NOT_WORD: Not(WORD),
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
68 sre_constants.CATEGORY_DIGIT: string.digits,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
69 sre_constants.CATEGORY_NOT_DIGIT: Not(string.digits),
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
70 sre_constants.CATEGORY_SPACE: string.whitespace,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
71 sre_constants.CATEGORY_NOT_SPACE: Not(string.whitespace),
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
72 }
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
73
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
74 # This constant varies between builds of Python; this is the lower value.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
75 MAX_REPEAT_COUNT = 65535
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
76
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
77
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
78 class ParseError(Exception):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
79 pass
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
80
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
81
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
82 def slice_indices(slice_obj, size):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
83 """slice_obj.indices() except this one supports longs."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
84 # start stop step
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
85 start = slice_obj.start
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
86 stop = slice_obj.stop
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
87 step = slice_obj.step
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
88
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
89 # We don't always update a value for negative indices (if we wrote it here
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
90 # due to None).
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
91 if step is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
92 step = 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
93 if start is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
94 if step > 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
95 start = 0
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
96 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
97 start = size - 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
98 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
99 start = _adjust_index(start, size)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
100
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
101 if stop is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
102 if step > 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
103 stop = size
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
104 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
105 stop = -1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
106 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
107 stop = _adjust_index(stop, size)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
108
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
109 return (start, stop, step)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
110
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
111
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
112 def _adjust_index(n, size):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
113 if n < 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
114 n += size
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
115
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
116 if n < 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
117 raise IndexError("Out of range")
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
118 if n > size:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
119 n = size
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
120 return n
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
121
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
122
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
123 def _xrange(*args):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
124 """Because xrange doesn't support longs :("""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
125 # prefer real xrange if it works
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
126 try:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
127 return xrange(*args)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
128 except OverflowError:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
129 return _bigrange(*args)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
130
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
131
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
132 def _bigrange(*args):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
133 if len(args) == 1:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
134 start = 0; stop = args[0]; step = 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
135 elif len(args) == 2:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
136 start, stop = args
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
137 step = 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
138 elif len(args) == 3:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
139 start, stop, step = args
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
140 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
141 raise ValueError("Too many args for _bigrange")
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
142
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
143 i = start
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
144 while True:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
145 yield i
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
146 i += step
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
147 if step < 0 and i <= stop:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
148 break
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
149 if step > 0 and i >= stop:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
150 break
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
151
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
152
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
153 class WrappedSequence(object):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
154 """This wraps a sequence, purely as a base clase for the other uses."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
155
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
156 def __init__(self, raw):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
157 # Derived classes will likely override this constructor
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
158 self.raw = raw
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
159 # Note that we can't use the function len() because it insists on trying
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
160 # to convert the returned number from a long-int to an ordinary int.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
161 self.length = raw.__len__()
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
162
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
163 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
164 i = _adjust_index(i, self.length)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
165 if hasattr(self.raw, 'get_item'):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
166 return self.raw.get_item(i, d)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
167 return self.raw[i]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
168
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
169 def __len__(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
170 return self.length
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
171
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
172 def __getitem__(self, i):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
173 # If the user wanted a slice, we provide a wrapper
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
174 if isinstance(i, slice):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
175 result = SlicedSequence(self, slicer=i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
176 if result.__len__() < 16:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
177 # Short lists are unpacked
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
178 result = [item for item in result]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
179 return result
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
180 i = _adjust_index(i, self.length)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
181 # Usually we just call the user-provided function
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
182 return self.get_item(i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
183
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
184 def __iter__(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
185 for i in _xrange(int(self.length)):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
186 yield self.get_item(i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
187
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
188
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
189 def _sign(x):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
190 if x > 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
191 return 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
192 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
193 return -1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
194
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
195
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
196 class SlicedSequence(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
197 """This is part of an immutable and potentially arbitrarily long list."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
198
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
199 def __init__(self, raw, slicer=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
200 # Derived classes will likely override this constructor
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
201 self.raw = raw
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
202 if slicer is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
203 self.start, self.stop, self.step = 0, raw.__len__(), 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
204 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
205 self.start, self.stop, self.step = slice_indices(slicer, raw.__len__())
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
206
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
207 # Integer round up, depending on step direction
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
208 self.length = ((self.stop - self.start + self.step - _sign(self.step)) /
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
209 self.step)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
210
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
211 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
212 j = i * self.step + self.start
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
213 return self.raw[j]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
214
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
215
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
216 class ConcatenatedSequence(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
217 """This is equivalent to using extend() but without unpacking the lists."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
218
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
219 def __init__(self, *alternatives):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
220 self.list_lengths = [(a, a.__len__()) for a in alternatives]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
221 self.length = sum(a_len for _, a_len in self.list_lengths)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
222
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
223 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
224 for a, a_len in self.list_lengths:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
225 if i < a_len:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
226 return a[i]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
227 i -= a_len
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
228 raise IndexError('Too Big')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
229
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
230 def __contains__(self, item):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
231 for a, _ in self.list_lengths:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
232 if item in a:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
233 return True
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
234 return False
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
235
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
236 def __repr__(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
237 return '{concat ' + repr(self.list_lengths) + '}'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
238
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
239
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
240 class CombinatoricsSequence(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
241 """This uses all combinations of one item from each passed list."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
242
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
243 def __init__(self, *components):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
244 self.list_lengths = [(a, a.__len__()) for a in components]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
245 self.length = 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
246 for _, c_len in self.list_lengths:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
247 self.length *= c_len
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
248
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
249 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
250 result = []
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
251 if i < 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
252 i += self.length
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
253 if i < 0 or i >= self.length:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
254 raise IndexError("Index %d out of bounds" % (i,))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
255
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
256 if len(self.list_lengths) == 1:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
257 # skip unnecessary ''.join -- big speedup
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
258 return self.list_lengths[0][0][i]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
259
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
260 for c, c_len in self.list_lengths:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
261 i, mod = divmod(i, c_len)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
262 if hasattr(c, 'get_item'):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
263 result.append(c.get_item(mod, d))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
264 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
265 result.append(c[mod])
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
266 return ''.join(result)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
267
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
268 def __repr__(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
269 return '{combin ' + repr(self.list_lengths) + '}'
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
270
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
271
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
272 class RepetitiveSequence(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
273 """This chooses an entry from a list, many times, and concatenates."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
274
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
275 def __init__(self, content, lowest=1, highest=1):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
276 self.content = content
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
277 self.content_length = content.__len__()
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
278 self.length = fastdivmod.powersum(self.content_length, lowest, highest)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
279 self.lowest = lowest
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
280 self.highest = highest
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
281
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
282 def arbitrary_entry(i):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
283 return (fastdivmod.powersum(self.content_length, lowest, i+lowest-1), i+lowest)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
284
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
285 def entry_from_prev(i, prev):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
286 return (prev[0] + (self.content_length ** prev[1]), prev[1] + 1)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
287
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
288 self.offsets = cachingseq.CachingFuncSequence(
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
289 arbitrary_entry, highest - lowest+1, entry_from_prev)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
290 # This needs to be a constant in order to reuse caclulations in future
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
291 # calls to bisect (a moving target will produce more misses).
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
292 if self.offsets[-1][0] > sys.maxsize:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
293 i = 0
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
294 while i + 2 < len(self.offsets):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
295 if self.offsets[i+1][0] > sys.maxsize:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
296 self.index_of_offset = i
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
297 self.offset_break = self.offsets[i][0]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
298 break
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
299 i += 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
300 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
301 self.index_of_offset = len(self.offsets)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
302 self.offset_break = sys.maxsize
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
303
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
304 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
305 """Finds out how many repeats this index implies, then picks strings."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
306 if i < self.offset_break:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
307 by_bisect = bisect.bisect_left(self.offsets, (i, -1), hi=self.index_of_offset)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
308 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
309 by_bisect = bisect.bisect_left(self.offsets, (i, -1), lo=self.index_of_offset)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
310
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
311 if by_bisect == len(self.offsets) or self.offsets[by_bisect][0] > i:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
312 by_bisect -= 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
313
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
314 num = i - self.offsets[by_bisect][0]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
315 count = self.offsets[by_bisect][1]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
316
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
317 if count > 100 and self.content_length < 1000:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
318 content = list(self.content)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
319 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
320 content = self.content
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
321
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
322 result = []
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
323
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
324 if count == 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
325 return ''
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
326
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
327 for modulus in fastdivmod.divmod_iter(num, self.content_length):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
328 result.append(content[modulus])
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
329
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
330 leftover = count - len(result)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
331 if leftover:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
332 assert leftover > 0
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
333 result.extend([content[0]] * leftover)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
334
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
335 # smallest place value ends up on the right
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
336 return ''.join(result[::-1])
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
337
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
338 def __repr__(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
339 return '{repeat base=%d low=%d high=%d}' % (self.content_length, self.lowest, self.highest)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
340
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
341
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
342 class SaveCaptureGroup(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
343 def __init__(self, parsed, key):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
344 self.key = key
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
345 super(SaveCaptureGroup, self).__init__(parsed)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
346
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
347 def get_item(self, n, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
348 rv = super(SaveCaptureGroup, self).get_item(n, d)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
349 if d is not None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
350 d[self.key] = rv
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
351 return rv
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
352
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
353
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
354 class ReadCaptureGroup(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
355 def __init__(self, n):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
356 self.num = n
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
357 self.length = 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
358
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
359 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
360 if i != 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
361 raise IndexError(i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
362 if d is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
363 raise ValueError('ReadCaptureGroup with no dict')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
364 return d.get(self.num, "fail")
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
365
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
366
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
367 class RegexMembershipSequence(WrappedSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
368 """Creates a sequence from the regex, knows how to test membership."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
369
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
370 def empty_list(self, *_):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
371 return []
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
372
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
373 def nothing_added(self, *_):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
374 return ['']
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
375
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
376 def branch_values(self, _, items):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
377 """Converts SRE parser data into literals and merges those lists."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
378 return ConcatenatedSequence(
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
379 *[self.sub_values(parsed) for parsed in items])
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
380
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
381 def max_repeat_values(self, min_count, max_count, items):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
382 """Sequential expansion of the count to be combinatorics."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
383 max_count = min(max_count, self.max_count)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
384 return RepetitiveSequence(
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
385 self.sub_values(items), min_count, max_count)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
386
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
387 def in_values(self, items):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
388 # Special case which distinguishes branch from charset operator
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
389 if items and items[0][0] == sre_constants.NEGATE:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
390 items = self.branch_values(None, items[1:])
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
391 return [item for item in self.charset if item not in items]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
392 return self.branch_values(None, items)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
393
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
394 def not_literal(self, y):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
395 return self.in_values(((sre_constants.NEGATE,),
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
396 (sre_constants.LITERAL, y),))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
397
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
398 def category(self, y):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
399 return CATEGORIES[y]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
400
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
401 def groupref(self, n):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
402 self.has_groupref = True
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
403 return ReadCaptureGroup(n)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
404
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
405 def get_item(self, i, d=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
406 """Typically only pass i. d is an internal detail, for consistency with other classes.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
407
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
408 If you care about the capture groups, you should use
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
409 RegexMembershipSequenceMatches instead, which returns a Match object
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
410 instead of a string."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
411 if self.has_groupref or d is not None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
412 if d is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
413 d = {}
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
414 return super(RegexMembershipSequence, self).get_item(i, d)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
415 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
416 return super(RegexMembershipSequence, self).get_item(i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
417
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
418 def sub_values(self, parsed):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
419 """This knows how to convert one piece of parsed pattern."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
420 # If this is a subpattern object, we just want its data
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
421 if isinstance(parsed, sre_parse.SubPattern):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
422 parsed = parsed.data
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
423 # A list indicates sequential elements of a string
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
424 if isinstance(parsed, list):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
425 elements = [self.sub_values(p) for p in parsed]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
426 return CombinatoricsSequence(*elements)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
427 # If not a list, a tuple represents a specific match type
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
428 if isinstance(parsed, tuple) and parsed:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
429 matcher, arguments = parsed
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
430 if not isinstance(arguments, tuple):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
431 arguments = (arguments,)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
432 if matcher in self.backends:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
433 self.check_anchor_state(matcher, arguments)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
434 return self.backends[matcher](*arguments)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
435 # No idea what to do here
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
436 raise ParseError(repr(parsed))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
437
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
438 def maybe_save(self, *args):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
439 # Python 3.6 has group, add_flags, del_flags, parsed
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
440 # while earlier versions just have group, parsed
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
441 group = args[0]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
442 parsed = args[-1]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
443 rv = self.sub_values(parsed)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
444 if group is not None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
445 rv = SaveCaptureGroup(rv, group)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
446 return rv
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
447
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
448 def check_anchor_state(self, matcher, arguments):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
449 # A bit of a hack to support zero-width leading anchors. The goal is
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
450 # that /^(a|b)$/ will match properly, and that /a^b/ or /a\bb/ throws
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
451 # an error. (It's unfortunate that I couldn't easily handle /$^/ which
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
452 # matches the empty string; I went for the common case.)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
453 #
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
454 # There are three states, for example:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
455 # / STATE_START
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
456 # | / STATE_START (^ causes no transition here, but is illegal at STATE_MIDDLE or STATE_END)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
457 # | | / STATE_START (\b causes no transition here, but advances MIDDLE to END)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
458 # | | | / (same as above for ^)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
459 # | | | | / STATE_MIDDLE (anything besides ^ and \b advances START to MIDDLE)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
460 # | | | | | / still STATE_MIDDLE
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
461 # . . . . . . / advances MIDDLE to END
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
462 # ^ \b ^ X Y \b $
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
463 old_state = self.state
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
464 if self.state == STATE_START:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
465 if matcher == sre_constants.AT:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
466 if arguments[0] in (sre_constants.AT_END, sre_constants.AT_END_STRING):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
467 self.state = STATE_END
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
468 elif arguments[0] == sre_constants.AT_NON_BOUNDARY:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
469 # This is nonsensical at beginning of string
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
470 raise ParseError('Anchor %r found at START state' % (arguments[0],))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
471 # All others (AT_BEGINNING, AT_BEGINNING_STRING, and AT_BOUNDARY) remain in START.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
472 elif matcher != sre_constants.SUBPATTERN:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
473 self.state = STATE_MIDDLE
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
474 # subpattern remains in START
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
475 elif self.state == STATE_END:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
476 if matcher == sre_constants.AT:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
477 if arguments[0] not in (
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
478 sre_constants.AT_END, sre_constants.AT_END_STRING,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
479 sre_constants.AT_BOUNDARY):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
480 raise ParseError('Anchor %r found at END state' % (arguments[0],))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
481 # those three remain in END
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
482 elif matcher != sre_constants.SUBPATTERN:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
483 raise ParseError('Non-end-anchor %r found at END state' % (arguments[0],))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
484 # subpattern remains in END
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
485 else: # self.state == STATE_MIDDLE
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
486 if matcher == sre_constants.AT:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
487 if arguments[0] not in (
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
488 sre_constants.AT_END, sre_constants.AT_END_STRING,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
489 sre_constants.AT_BOUNDARY):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
490 raise ParseError('Anchor %r found at MIDDLE state' % (arguments[0],))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
491 # All others (AT_END, AT_END_STRING, AT_BOUNDARY) advance to END.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
492 self.state = STATE_END
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
493
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
494 def __init__(self, pattern, flags=0, charset=CHARSET, max_count=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
495 # If the RE module cannot compile it, we give up quickly
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
496 self.matcher = re.compile(r'(?:%s)\Z' % pattern, flags)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
497 if not flags & re.DOTALL:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
498 charset = ''.join(c for c in charset if c != '\n')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
499 self.charset = charset
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
500
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
501 self.named_group_lookup = self.matcher.groupindex
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
502
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
503 flags |= DEFAULT_RE_FLAGS # https://github.com/google/sre_yield/issues/3
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
504 if flags & re.IGNORECASE:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
505 raise ParseError('Flag "i" not supported. https://github.com/google/sre_yield/issues/4')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
506 elif flags & re.UNICODE:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
507 raise ParseError('Flag "u" not supported. https://github.com/google/sre_yield/issues/3')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
508 elif flags & re.LOCALE:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
509 raise ParseError('Flag "l" not supported. https://github.com/google/sre_yield/issues/5')
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
510
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
511 if max_count is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
512 self.max_count = MAX_REPEAT_COUNT
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
513 else:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
514 self.max_count = max_count
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
515
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
516 self.has_groupref = False
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
517
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
518 # Configure the parser backends
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
519 self.backends = {
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
520 sre_constants.LITERAL: lambda y: [chr(y)],
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
521 sre_constants.RANGE: lambda l, h: [chr(c) for c in range(l, h+1)],
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
522 sre_constants.SUBPATTERN: self.maybe_save,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
523 sre_constants.BRANCH: self.branch_values,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
524 sre_constants.MIN_REPEAT: self.max_repeat_values,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
525 sre_constants.MAX_REPEAT: self.max_repeat_values,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
526 sre_constants.AT: self.nothing_added,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
527 sre_constants.ASSERT: self.empty_list,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
528 sre_constants.ASSERT_NOT: self.empty_list,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
529 sre_constants.ANY:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
530 lambda _: self.in_values(((sre_constants.NEGATE,),)),
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
531 sre_constants.IN: self.in_values,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
532 sre_constants.NOT_LITERAL: self.not_literal,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
533 sre_constants.CATEGORY: self.category,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
534 sre_constants.GROUPREF: self.groupref,
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
535 }
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
536 self.state = STATE_START
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
537 # Now build a generator that knows all possible patterns
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
538 self.raw = self.sub_values(sre_parse.parse(pattern, flags))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
539 # Configure this class instance to know about that result
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
540 self.length = self.raw.__len__()
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
541
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
542 def __contains__(self, item):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
543 # Since we have a regex, we can search the list really cheaply
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
544 return self.matcher.match(item) is not None
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
545
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
546
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
547 class RegexMembershipSequenceMatches(RegexMembershipSequence):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
548 def __getitem__(self, i):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
549 if isinstance(i, slice):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
550 result = SlicedSequence(self, slicer=i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
551 if result.__len__() < 16:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
552 # Short lists are unpacked
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
553 result = [item for item in result]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
554 return result
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
555
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
556 d = {}
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
557 s = super(RegexMembershipSequenceMatches, self).get_item(i, d)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
558 return Match(s, d, self.named_group_lookup)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
559
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
560
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
561 def AllStrings(regex, flags=0, charset=CHARSET, max_count=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
562 """Constructs an object that will generate all matching strings."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
563 return RegexMembershipSequence(regex, flags, charset, max_count=max_count)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
564
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
565 Values = AllStrings
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
566
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
567
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
568 class Match(object):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
569 def __init__(self, string, groups, named_groups):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
570 # TODO keep group(0) only, and spans for the rest.
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
571 self._string = string
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
572 self._groups = groups
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
573 self._named_groups = named_groups
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
574 self.lastindex = len(groups) + 1
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
575
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
576 def group(self, n=0):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
577 if n == 0:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
578 return self._string
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
579 if not isinstance(n, int):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
580 n = self._named_groups[n]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
581 return self._groups[n]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
582
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
583 def groups(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
584 return tuple(self._groups[i] for i in range(1, self.lastindex))
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
585
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
586 def groupdict(self):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
587 d = {}
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
588 for k, v in self._named_groups.items():
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
589 d[k] = self._groups[v]
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
590 return d
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
591
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
592 def span(self, n=0):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
593 raise NotImplementedError()
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
594
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
595
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
596 def AllMatches(regex, flags=0, charset=CHARSET, max_count=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
597 """Constructs an object that will generate all matching strings."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
598 return RegexMembershipSequenceMatches(regex, flags, charset, max_count=max_count)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
599
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
600
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
601 def main(argv=None):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
602 """This module can be executed on the command line for testing."""
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
603 if argv is None:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
604 argv = sys.argv
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
605 for arg in argv[1:]:
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
606 for i in AllStrings(arg):
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
607 print(i)
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
608
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
609
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
610 if __name__ == '__main__':
aad5e435e4dc Uploaded
gianmarco_piccinno
parents:
diff changeset
611 main()