@ai-ecoverse/py-pyparsing 3.3.3-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +3 -0
- package/lib/python3.14/site-packages/pyparsing/__init__.py +413 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/actions.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/common.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/core.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/exceptions.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/helpers.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/results.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/testing.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/unicode.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/util.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/__pycache__/warnings.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/actions.py +264 -0
- package/lib/python3.14/site-packages/pyparsing/ai/__init__.py +0 -0
- package/lib/python3.14/site-packages/pyparsing/ai/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/ai/best_practices.md +75 -0
- package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__init__.py +0 -0
- package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__main__.py +2 -0
- package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__pycache__/__main__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/common.py +570 -0
- package/lib/python3.14/site-packages/pyparsing/core.py +6972 -0
- package/lib/python3.14/site-packages/pyparsing/diagram/__init__.py +761 -0
- package/lib/python3.14/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/exceptions.py +353 -0
- package/lib/python3.14/site-packages/pyparsing/helpers.py +1769 -0
- package/lib/python3.14/site-packages/pyparsing/py.typed +0 -0
- package/lib/python3.14/site-packages/pyparsing/results.py +928 -0
- package/lib/python3.14/site-packages/pyparsing/testing.py +398 -0
- package/lib/python3.14/site-packages/pyparsing/tools/__init__.py +0 -0
- package/lib/python3.14/site-packages/pyparsing/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/tools/__pycache__/cvt_pyparsing_pep8_names.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/pyparsing/tools/cvt_pyparsing_pep8_names.py +142 -0
- package/lib/python3.14/site-packages/pyparsing/unicode.py +356 -0
- package/lib/python3.14/site-packages/pyparsing/util.py +514 -0
- package/lib/python3.14/site-packages/pyparsing/warnings.py +10 -0
- package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/METADATA +147 -0
- package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/RECORD +23 -0
- package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/WHEEL +4 -0
- package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/licenses/LICENSE +20 -0
- package/package.json +27 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# util.py
|
|
2
|
+
import contextlib
|
|
3
|
+
import re
|
|
4
|
+
from functools import lru_cache, wraps
|
|
5
|
+
import inspect
|
|
6
|
+
import itertools
|
|
7
|
+
import types
|
|
8
|
+
from typing import Callable, Union, Iterable, TypeVar, cast, Any
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
from .warnings import PyparsingDeprecationWarning, PyparsingDiagnosticWarning
|
|
12
|
+
|
|
13
|
+
_bslash = chr(92)
|
|
14
|
+
C = TypeVar("C", bound=Callable)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class __config_flags:
|
|
18
|
+
"""Internal class for defining compatibility and debugging flags"""
|
|
19
|
+
|
|
20
|
+
_all_names: list[str] = []
|
|
21
|
+
_fixed_names: list[str] = []
|
|
22
|
+
_type_desc = "configuration"
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def _set(cls, dname, value):
|
|
26
|
+
if dname in cls._fixed_names:
|
|
27
|
+
warnings.warn(
|
|
28
|
+
f"{cls.__name__}.{dname} {cls._type_desc} is {str(getattr(cls, dname)).upper()}"
|
|
29
|
+
f" and cannot be overridden",
|
|
30
|
+
PyparsingDiagnosticWarning,
|
|
31
|
+
stacklevel=3,
|
|
32
|
+
)
|
|
33
|
+
return
|
|
34
|
+
if dname in cls._all_names:
|
|
35
|
+
setattr(cls, dname, value)
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(f"no such {cls._type_desc} {dname!r}")
|
|
38
|
+
|
|
39
|
+
enable = classmethod(lambda cls, name: cls._set(name, True))
|
|
40
|
+
disable = classmethod(lambda cls, name: cls._set(name, False))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@lru_cache(maxsize=128)
|
|
44
|
+
def col(loc: int, strg: str) -> int:
|
|
45
|
+
"""
|
|
46
|
+
Returns current column within a string, counting newlines as line separators.
|
|
47
|
+
The first column is number 1.
|
|
48
|
+
|
|
49
|
+
Note: the default parsing behavior is to expand tabs in the input string
|
|
50
|
+
before starting the parsing process. See
|
|
51
|
+
:meth:`ParserElement.parse_string` for more
|
|
52
|
+
information on parsing strings containing ``<TAB>`` s, and suggested
|
|
53
|
+
methods to maintain a consistent view of the parsed string, the parse
|
|
54
|
+
location, and line and column positions within the parsed string.
|
|
55
|
+
"""
|
|
56
|
+
s = strg
|
|
57
|
+
return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@lru_cache(maxsize=128)
|
|
61
|
+
def lineno(loc: int, strg: str) -> int:
|
|
62
|
+
"""Returns current line number within a string, counting newlines as line separators.
|
|
63
|
+
The first line is number 1.
|
|
64
|
+
|
|
65
|
+
Note - the default parsing behavior is to expand tabs in the input string
|
|
66
|
+
before starting the parsing process. See :meth:`ParserElement.parse_string`
|
|
67
|
+
for more information on parsing strings containing ``<TAB>`` s, and
|
|
68
|
+
suggested methods to maintain a consistent view of the parsed string, the
|
|
69
|
+
parse location, and line and column positions within the parsed string.
|
|
70
|
+
"""
|
|
71
|
+
return strg.count("\n", 0, loc) + 1
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@lru_cache(maxsize=128)
|
|
75
|
+
def line(loc: int, strg: str) -> str:
|
|
76
|
+
"""
|
|
77
|
+
Returns the line of text containing loc within a string, counting newlines as line separators.
|
|
78
|
+
"""
|
|
79
|
+
last_cr = strg.rfind("\n", 0, loc)
|
|
80
|
+
next_cr = strg.find("\n", loc)
|
|
81
|
+
return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class _UnboundedCache:
|
|
85
|
+
def __init__(self):
|
|
86
|
+
cache = {}
|
|
87
|
+
cache_get = cache.get
|
|
88
|
+
self.not_in_cache = not_in_cache = object()
|
|
89
|
+
|
|
90
|
+
def get(_, key):
|
|
91
|
+
return cache_get(key, not_in_cache)
|
|
92
|
+
|
|
93
|
+
def set_(_, key, value):
|
|
94
|
+
cache[key] = value
|
|
95
|
+
|
|
96
|
+
def clear(_):
|
|
97
|
+
cache.clear()
|
|
98
|
+
|
|
99
|
+
self.size = None
|
|
100
|
+
self.get = types.MethodType(get, self)
|
|
101
|
+
self.set = types.MethodType(set_, self)
|
|
102
|
+
self.clear = types.MethodType(clear, self)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class _FifoCache:
|
|
106
|
+
def __init__(self, size):
|
|
107
|
+
cache = {}
|
|
108
|
+
self.size = size
|
|
109
|
+
self.not_in_cache = not_in_cache = object()
|
|
110
|
+
cache_get = cache.get
|
|
111
|
+
cache_pop = cache.pop
|
|
112
|
+
|
|
113
|
+
def get(_, key):
|
|
114
|
+
return cache_get(key, not_in_cache)
|
|
115
|
+
|
|
116
|
+
def set_(_, key, value):
|
|
117
|
+
cache[key] = value
|
|
118
|
+
while len(cache) > size:
|
|
119
|
+
# pop oldest element in cache by getting the first key
|
|
120
|
+
cache_pop(next(iter(cache)))
|
|
121
|
+
|
|
122
|
+
def clear(_):
|
|
123
|
+
cache.clear()
|
|
124
|
+
|
|
125
|
+
self.get = types.MethodType(get, self)
|
|
126
|
+
self.set = types.MethodType(set_, self)
|
|
127
|
+
self.clear = types.MethodType(clear, self)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class LRUMemo:
|
|
131
|
+
"""
|
|
132
|
+
A memoizing mapping that retains `capacity` deleted items
|
|
133
|
+
|
|
134
|
+
The memo tracks retained items by their access order; once `capacity` items
|
|
135
|
+
are retained, the least recently used item is discarded.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, capacity):
|
|
139
|
+
self._capacity = capacity
|
|
140
|
+
self._active = {}
|
|
141
|
+
self._memory = {}
|
|
142
|
+
|
|
143
|
+
def __getitem__(self, key):
|
|
144
|
+
try:
|
|
145
|
+
return self._active[key]
|
|
146
|
+
except KeyError:
|
|
147
|
+
self._memory[key] = self._memory.pop(key)
|
|
148
|
+
return self._memory[key]
|
|
149
|
+
|
|
150
|
+
def __setitem__(self, key, value):
|
|
151
|
+
self._memory.pop(key, None)
|
|
152
|
+
self._active[key] = value
|
|
153
|
+
|
|
154
|
+
def __delitem__(self, key):
|
|
155
|
+
try:
|
|
156
|
+
value = self._active.pop(key)
|
|
157
|
+
except KeyError:
|
|
158
|
+
pass
|
|
159
|
+
else:
|
|
160
|
+
oldest_keys = list(self._memory)[: -(self._capacity + 1)]
|
|
161
|
+
for key_to_delete in oldest_keys:
|
|
162
|
+
self._memory.pop(key_to_delete)
|
|
163
|
+
self._memory[key] = value
|
|
164
|
+
|
|
165
|
+
def clear(self):
|
|
166
|
+
self._active.clear()
|
|
167
|
+
self._memory.clear()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class UnboundedMemo(dict):
|
|
171
|
+
"""
|
|
172
|
+
A memoizing mapping that retains all deleted items
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
def __delitem__(self, key):
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _escape_regex_range_chars(s: str) -> str:
|
|
180
|
+
# escape these chars: ^-[]
|
|
181
|
+
for c in r"\^-[]":
|
|
182
|
+
s = s.replace(c, _bslash + c)
|
|
183
|
+
s = s.replace("\n", r"\n")
|
|
184
|
+
s = s.replace("\t", r"\t")
|
|
185
|
+
return str(s)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class _GroupConsecutive:
|
|
189
|
+
"""
|
|
190
|
+
Used as a callable `key` for itertools.groupby to group
|
|
191
|
+
characters that are consecutive:
|
|
192
|
+
|
|
193
|
+
.. testcode::
|
|
194
|
+
|
|
195
|
+
from itertools import groupby
|
|
196
|
+
from pyparsing.util import _GroupConsecutive
|
|
197
|
+
|
|
198
|
+
grouped = groupby("abcdejkmpqrs", key=_GroupConsecutive())
|
|
199
|
+
for index, group in grouped:
|
|
200
|
+
print(tuple([index, list(group)]))
|
|
201
|
+
|
|
202
|
+
prints:
|
|
203
|
+
|
|
204
|
+
.. testoutput::
|
|
205
|
+
|
|
206
|
+
(0, ['a', 'b', 'c', 'd', 'e'])
|
|
207
|
+
(1, ['j', 'k'])
|
|
208
|
+
(2, ['m'])
|
|
209
|
+
(3, ['p', 'q', 'r', 's'])
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
def __init__(self) -> None:
|
|
213
|
+
self.prev = 0
|
|
214
|
+
self.counter = itertools.count()
|
|
215
|
+
self.value = -1
|
|
216
|
+
|
|
217
|
+
def __call__(self, char: str) -> int:
|
|
218
|
+
c_int = ord(char)
|
|
219
|
+
self.prev, prev = c_int, self.prev
|
|
220
|
+
if c_int - prev > 1:
|
|
221
|
+
self.value = next(self.counter)
|
|
222
|
+
return self.value
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _is_iterable(obj, _str_type=(str, bytes), _iter_exception=Exception):
|
|
226
|
+
# str's are iterable, but in pyparsing, we don't want to iterate over them
|
|
227
|
+
if isinstance(obj, _str_type):
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
iter(obj)
|
|
232
|
+
except _iter_exception: # noqa
|
|
233
|
+
return False
|
|
234
|
+
else:
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _escape_re_range_char(c: str) -> str:
|
|
239
|
+
return fr"\{c}" if c in r"\^-][" else c
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _collapse_string_to_ranges(
|
|
243
|
+
s: Union[str, Iterable[str]], re_escape: bool = True
|
|
244
|
+
) -> str:
|
|
245
|
+
r"""
|
|
246
|
+
Take a string or list of single-character strings, and return
|
|
247
|
+
a string of the consecutive characters in that string collapsed
|
|
248
|
+
into groups, as might be used in a regular expression '[a-z]'
|
|
249
|
+
character set::
|
|
250
|
+
|
|
251
|
+
'a' -> 'a' -> '[a]'
|
|
252
|
+
'bc' -> 'bc' -> '[bc]'
|
|
253
|
+
'defgh' -> 'd-h' -> '[d-h]'
|
|
254
|
+
'fdgeh' -> 'd-h' -> '[d-h]'
|
|
255
|
+
'jklnpqrtu' -> 'j-lnp-rtu' -> '[j-lnp-rtu]'
|
|
256
|
+
|
|
257
|
+
Duplicates get collapsed out::
|
|
258
|
+
|
|
259
|
+
'aaa' -> 'a' -> '[a]'
|
|
260
|
+
'bcbccb' -> 'bc' -> '[bc]'
|
|
261
|
+
'defghhgf' -> 'd-h' -> '[d-h]'
|
|
262
|
+
'jklnpqrjjjtu' -> 'j-lnp-rtu' -> '[j-lnp-rtu]'
|
|
263
|
+
|
|
264
|
+
Spaces are preserved::
|
|
265
|
+
|
|
266
|
+
'ab c' -> ' a-c' -> '[ a-c]'
|
|
267
|
+
|
|
268
|
+
Characters that are significant when defining regex ranges
|
|
269
|
+
get escaped::
|
|
270
|
+
|
|
271
|
+
'acde[]-' -> r'\-\[\]ac-e' -> r'[\-\[\]ac-e]'
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
# Developer notes:
|
|
275
|
+
# - Do not optimize this code assuming that the given input string
|
|
276
|
+
# or internal lists will be short (such as in loading generators into
|
|
277
|
+
# lists to make it easier to find the last element); this method is also
|
|
278
|
+
# used to generate regex ranges for character sets in the pyparsing.unicode
|
|
279
|
+
# classes, and these can be _very_ long lists of strings
|
|
280
|
+
|
|
281
|
+
escape_re_range_char: Callable[[str], str]
|
|
282
|
+
if re_escape:
|
|
283
|
+
escape_re_range_char = _escape_re_range_char
|
|
284
|
+
else:
|
|
285
|
+
escape_re_range_char = lambda ss: ss
|
|
286
|
+
|
|
287
|
+
ret = []
|
|
288
|
+
|
|
289
|
+
# reduce input string to remove duplicates, and put in sorted order
|
|
290
|
+
s_chars: list[str] = sorted(set(s))
|
|
291
|
+
|
|
292
|
+
if len(s_chars) > 2:
|
|
293
|
+
# find groups of characters that are consecutive (can be collapsed
|
|
294
|
+
# down to "<first>-<last>")
|
|
295
|
+
for _, chars in itertools.groupby(s_chars, key=_GroupConsecutive()):
|
|
296
|
+
# _ is unimportant, is just used to identify groups
|
|
297
|
+
# chars is an iterator of one or more consecutive characters
|
|
298
|
+
# that comprise the current group
|
|
299
|
+
first = last = next(chars)
|
|
300
|
+
with contextlib.suppress(ValueError):
|
|
301
|
+
*_, last = chars
|
|
302
|
+
|
|
303
|
+
if first == last:
|
|
304
|
+
# there was only a single char in this group
|
|
305
|
+
ret.append(escape_re_range_char(first))
|
|
306
|
+
|
|
307
|
+
elif last == chr(ord(first) + 1):
|
|
308
|
+
# there were only 2 characters in this group
|
|
309
|
+
# 'a','b' -> 'ab'
|
|
310
|
+
ret.append(f"{escape_re_range_char(first)}{escape_re_range_char(last)}")
|
|
311
|
+
|
|
312
|
+
else:
|
|
313
|
+
# there were > 2 characters in this group, make into a range
|
|
314
|
+
# 'c','d','e' -> 'c-e'
|
|
315
|
+
ret.append(
|
|
316
|
+
f"{escape_re_range_char(first)}-{escape_re_range_char(last)}"
|
|
317
|
+
)
|
|
318
|
+
else:
|
|
319
|
+
# only 1 or 2 chars were given to form into groups
|
|
320
|
+
# 'a' -> ['a']
|
|
321
|
+
# 'bc' -> ['b', 'c']
|
|
322
|
+
# 'dg' -> ['d', 'g']
|
|
323
|
+
# no need to list them with "-", just return as a list
|
|
324
|
+
# (after escaping)
|
|
325
|
+
ret = [escape_re_range_char(c) for c in s_chars]
|
|
326
|
+
|
|
327
|
+
return "".join(ret)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _flatten(ll: Iterable) -> list:
|
|
331
|
+
ret = []
|
|
332
|
+
for i in ll:
|
|
333
|
+
# Developer notes:
|
|
334
|
+
# - do not collapse this section of code, isinstance checks are done
|
|
335
|
+
# in optimal order
|
|
336
|
+
if isinstance(i, str):
|
|
337
|
+
ret.append(i)
|
|
338
|
+
elif isinstance(i, Iterable):
|
|
339
|
+
ret.extend(_flatten(i))
|
|
340
|
+
else:
|
|
341
|
+
ret.append(i)
|
|
342
|
+
return ret
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _convert_escaped_numerics_to_char(s: str) -> str:
|
|
346
|
+
if s == "0":
|
|
347
|
+
return "\0"
|
|
348
|
+
if s.isdigit() and len(s) == 3:
|
|
349
|
+
return chr(int(s, 8))
|
|
350
|
+
elif s.startswith(("u", "x")):
|
|
351
|
+
return chr(int(s[1:], 16))
|
|
352
|
+
return s
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def make_compressed_re(
|
|
356
|
+
word_list: Iterable[str],
|
|
357
|
+
max_level: int = 2,
|
|
358
|
+
*,
|
|
359
|
+
non_capturing_groups: bool = True,
|
|
360
|
+
_level: int = 1,
|
|
361
|
+
) -> str:
|
|
362
|
+
"""
|
|
363
|
+
Create a regular expression string from a list of words, collapsing by common
|
|
364
|
+
prefixes and optional suffixes.
|
|
365
|
+
|
|
366
|
+
Calls itself recursively to build nested sublists for each group of suffixes
|
|
367
|
+
that have a shared prefix.
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
def get_suffixes_from_common_prefixes(namelist: list[str]):
|
|
371
|
+
if len(namelist) > 1:
|
|
372
|
+
for prefix, suffixes in itertools.groupby(namelist, key=lambda s: s[:1]):
|
|
373
|
+
yield prefix, sorted([s[1:] for s in suffixes], key=len, reverse=True)
|
|
374
|
+
else:
|
|
375
|
+
yield namelist[0][0], [namelist[0][1:]]
|
|
376
|
+
|
|
377
|
+
if _level == 1:
|
|
378
|
+
if not word_list:
|
|
379
|
+
raise ValueError("no words given to make_compressed_re()")
|
|
380
|
+
|
|
381
|
+
if "" in word_list:
|
|
382
|
+
raise ValueError("word list cannot contain empty string")
|
|
383
|
+
else:
|
|
384
|
+
# internal recursive call, just return empty string if no words
|
|
385
|
+
if not word_list:
|
|
386
|
+
return ""
|
|
387
|
+
|
|
388
|
+
# dedupe the word list
|
|
389
|
+
word_list = list({}.fromkeys(word_list))
|
|
390
|
+
|
|
391
|
+
if max_level == 0:
|
|
392
|
+
if any(len(wd) > 1 for wd in word_list):
|
|
393
|
+
return "|".join(
|
|
394
|
+
sorted([re.escape(wd) for wd in word_list], key=len, reverse=True)
|
|
395
|
+
)
|
|
396
|
+
else:
|
|
397
|
+
return f"[{''.join(_escape_regex_range_chars(wd) for wd in word_list)}]"
|
|
398
|
+
|
|
399
|
+
ret = []
|
|
400
|
+
sep = ""
|
|
401
|
+
ncgroup = "?:" if non_capturing_groups else ""
|
|
402
|
+
|
|
403
|
+
for initial, suffixes in get_suffixes_from_common_prefixes(sorted(word_list)):
|
|
404
|
+
ret.append(sep)
|
|
405
|
+
sep = "|"
|
|
406
|
+
|
|
407
|
+
initial = re.escape(initial)
|
|
408
|
+
|
|
409
|
+
trailing = ""
|
|
410
|
+
if "" in suffixes:
|
|
411
|
+
trailing = "?"
|
|
412
|
+
suffixes.remove("")
|
|
413
|
+
|
|
414
|
+
if len(suffixes) > 1:
|
|
415
|
+
if all(len(s) == 1 for s in suffixes):
|
|
416
|
+
ret.append(
|
|
417
|
+
f"{initial}[{''.join(_escape_regex_range_chars(s) for s in suffixes)}]{trailing}"
|
|
418
|
+
)
|
|
419
|
+
else:
|
|
420
|
+
if _level < max_level:
|
|
421
|
+
suffix_re = make_compressed_re(
|
|
422
|
+
sorted(suffixes),
|
|
423
|
+
max_level,
|
|
424
|
+
non_capturing_groups=non_capturing_groups,
|
|
425
|
+
_level=_level + 1,
|
|
426
|
+
)
|
|
427
|
+
ret.append(f"{initial}({ncgroup}{suffix_re}){trailing}")
|
|
428
|
+
else:
|
|
429
|
+
if all(len(s) == 1 for s in suffixes):
|
|
430
|
+
ret.append(
|
|
431
|
+
f"{initial}[{''.join(_escape_regex_range_chars(s) for s in suffixes)}]{trailing}"
|
|
432
|
+
)
|
|
433
|
+
else:
|
|
434
|
+
suffixes.sort(key=len, reverse=True)
|
|
435
|
+
ret.append(
|
|
436
|
+
f"{initial}({ncgroup}{'|'.join(re.escape(s) for s in suffixes)}){trailing}"
|
|
437
|
+
)
|
|
438
|
+
else:
|
|
439
|
+
if suffixes:
|
|
440
|
+
suffix = re.escape(suffixes[0])
|
|
441
|
+
if len(suffix) > 1 and trailing:
|
|
442
|
+
ret.append(f"{initial}({ncgroup}{suffix}){trailing}")
|
|
443
|
+
else:
|
|
444
|
+
ret.append(f"{initial}{suffix}{trailing}")
|
|
445
|
+
else:
|
|
446
|
+
ret.append(initial)
|
|
447
|
+
return "".join(ret)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def replaced_by_pep8(compat_name: str, fn: C) -> C:
|
|
451
|
+
|
|
452
|
+
# Unwrap staticmethod/classmethod
|
|
453
|
+
fn = getattr(fn, "__func__", fn)
|
|
454
|
+
|
|
455
|
+
# (Presence of 'self' arg in signature is used by explain_exception() methods, so we take
|
|
456
|
+
# some extra steps to add it if present in decorated function.)
|
|
457
|
+
if ["self"] == list(inspect.signature(fn).parameters)[:1]:
|
|
458
|
+
|
|
459
|
+
@wraps(fn)
|
|
460
|
+
def _inner(self, *args, **kwargs):
|
|
461
|
+
warnings.warn(
|
|
462
|
+
f"{compat_name!r} deprecated - use {fn.__name__!r}",
|
|
463
|
+
PyparsingDeprecationWarning,
|
|
464
|
+
stacklevel=2,
|
|
465
|
+
)
|
|
466
|
+
return fn(self, *args, **kwargs)
|
|
467
|
+
|
|
468
|
+
else:
|
|
469
|
+
|
|
470
|
+
@wraps(fn)
|
|
471
|
+
def _inner(*args, **kwargs):
|
|
472
|
+
warnings.warn(
|
|
473
|
+
f"{compat_name!r} deprecated - use {fn.__name__!r}",
|
|
474
|
+
PyparsingDeprecationWarning,
|
|
475
|
+
stacklevel=2,
|
|
476
|
+
)
|
|
477
|
+
return fn(*args, **kwargs)
|
|
478
|
+
|
|
479
|
+
_inner.__doc__ = f"""
|
|
480
|
+
.. deprecated:: 3.0.0
|
|
481
|
+
Use :class:`{fn.__name__}` instead
|
|
482
|
+
"""
|
|
483
|
+
_inner.__name__ = compat_name
|
|
484
|
+
_inner.__annotations__ = fn.__annotations__
|
|
485
|
+
if isinstance(fn, types.FunctionType):
|
|
486
|
+
_inner.__kwdefaults__ = fn.__kwdefaults__ # type: ignore [attr-defined]
|
|
487
|
+
elif isinstance(fn, type) and hasattr(fn, "__init__"):
|
|
488
|
+
_inner.__kwdefaults__ = fn.__init__.__kwdefaults__ # type: ignore [misc,attr-defined]
|
|
489
|
+
else:
|
|
490
|
+
_inner.__kwdefaults__ = None # type: ignore [attr-defined]
|
|
491
|
+
_inner.__qualname__ = fn.__qualname__
|
|
492
|
+
return cast(C, _inner)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _to_pep8_name(s: str, _re_sub_pattern=re.compile(r"([a-z])([A-Z])")) -> str:
|
|
496
|
+
s = _re_sub_pattern.sub(r"\1_\2", s)
|
|
497
|
+
return s.lower()
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def deprecate_argument(
|
|
501
|
+
kwargs: dict[str, Any], arg_name: str, default_value=None, *, new_name: str = ""
|
|
502
|
+
) -> Any:
|
|
503
|
+
|
|
504
|
+
if arg_name in kwargs:
|
|
505
|
+
new_name = new_name or _to_pep8_name(arg_name)
|
|
506
|
+
warnings.warn(
|
|
507
|
+
f"{arg_name!r} argument is deprecated, use {new_name!r}",
|
|
508
|
+
category=PyparsingDeprecationWarning,
|
|
509
|
+
stacklevel=3,
|
|
510
|
+
)
|
|
511
|
+
else:
|
|
512
|
+
kwargs[arg_name] = default_value
|
|
513
|
+
|
|
514
|
+
return kwargs[arg_name]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class PyparsingWarning(UserWarning):
|
|
2
|
+
"""Base warning class for all pyparsing warnings"""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PyparsingDeprecationWarning(PyparsingWarning, DeprecationWarning):
|
|
6
|
+
"""Base warning class for all pyparsing deprecation warnings"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PyparsingDiagnosticWarning(PyparsingWarning):
|
|
10
|
+
"""Base warning class for all pyparsing diagnostic warnings"""
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyparsing
|
|
3
|
+
Version: 3.3.3
|
|
4
|
+
Summary: pyparsing - Classes and methods to define and execute parsing grammars
|
|
5
|
+
Author-email: Paul McGuire <ptmcg.gm+pyparsing@gmail.com>
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/x-rst
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Information Technology
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
26
|
+
Classifier: Topic :: Text Processing
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: railroad-diagrams ; extra == "diagrams"
|
|
30
|
+
Requires-Dist: jinja2 ; extra == "diagrams"
|
|
31
|
+
Project-URL: Documentation, https://pyparsing-docs.readthedocs.io/en/latest/
|
|
32
|
+
Project-URL: Homepage, https://github.com/pyparsing/pyparsing/
|
|
33
|
+
Project-URL: Source, https://github.com/pyparsing/pyparsing.git
|
|
34
|
+
Provides-Extra: diagrams
|
|
35
|
+
Import-Name: pyparsing
|
|
36
|
+
|
|
37
|
+
PyParsing -- A Python Parsing Module
|
|
38
|
+
====================================
|
|
39
|
+
|
|
40
|
+
|Version| |Build Status| |Coverage| |License| |Python Versions| |Snyk Score|
|
|
41
|
+
|
|
42
|
+
Introduction
|
|
43
|
+
============
|
|
44
|
+
|
|
45
|
+
The pyparsing module is an alternative approach to creating and
|
|
46
|
+
executing simple grammars, vs. the traditional lex/yacc approach, or the
|
|
47
|
+
use of regular expressions. The pyparsing module provides a library of
|
|
48
|
+
classes that client code uses to construct the grammar directly in
|
|
49
|
+
Python code.
|
|
50
|
+
|
|
51
|
+
*[Since first writing this description of pyparsing in late 2003, this
|
|
52
|
+
technique for developing parsers has become more widespread, under the
|
|
53
|
+
name Parsing Expression Grammars - PEGs. See more information on PEGs*
|
|
54
|
+
`here <https://en.wikipedia.org/wiki/Parsing_expression_grammar>`__
|
|
55
|
+
*.]*
|
|
56
|
+
|
|
57
|
+
Here is a program to parse ``"Hello, World!"`` (or any greeting of the form
|
|
58
|
+
``"salutation, addressee!"``):
|
|
59
|
+
|
|
60
|
+
.. code:: python
|
|
61
|
+
|
|
62
|
+
from pyparsing import Word, alphas
|
|
63
|
+
greet = Word(alphas) + "," + Word(alphas) + "!"
|
|
64
|
+
hello = "Hello, World!"
|
|
65
|
+
print(hello, "->", greet.parse_string(hello))
|
|
66
|
+
|
|
67
|
+
The program outputs the following::
|
|
68
|
+
|
|
69
|
+
Hello, World! -> ['Hello', ',', 'World', '!']
|
|
70
|
+
|
|
71
|
+
The Python representation of the grammar is quite readable, owing to the
|
|
72
|
+
self-explanatory class names, and the use of '+', '|' and '^' operator
|
|
73
|
+
definitions.
|
|
74
|
+
|
|
75
|
+
The parsed results returned from ``parse_string()`` is a collection of type
|
|
76
|
+
``ParseResults``, which can be accessed as a
|
|
77
|
+
nested list, a dictionary, or an object with named attributes.
|
|
78
|
+
|
|
79
|
+
The pyparsing module handles some of the problems that are typically
|
|
80
|
+
vexing when writing text parsers:
|
|
81
|
+
|
|
82
|
+
- extra or missing whitespace (the above program will also handle ``"Hello,World!"``, ``"Hello , World !"``, etc.)
|
|
83
|
+
- quoted strings
|
|
84
|
+
- embedded comments
|
|
85
|
+
|
|
86
|
+
The examples directory includes a simple SQL parser, simple CORBA IDL
|
|
87
|
+
parser, a config file parser, a chemical formula parser, and a four-
|
|
88
|
+
function algebraic notation parser, among many others.
|
|
89
|
+
|
|
90
|
+
Documentation
|
|
91
|
+
=============
|
|
92
|
+
|
|
93
|
+
There are many examples in the online docstrings of the classes
|
|
94
|
+
and methods in pyparsing. You can find them compiled into `online docs <https://pyparsing-docs.readthedocs.io/en/latest/>`__. Additional
|
|
95
|
+
documentation resources and project info are listed in the online
|
|
96
|
+
`GitHub wiki <https://github.com/pyparsing/pyparsing/wiki>`__. An
|
|
97
|
+
entire directory of examples can be found `here <https://github.com/pyparsing/pyparsing/tree/master/examples>`__.
|
|
98
|
+
|
|
99
|
+
AI Instructions
|
|
100
|
+
===============
|
|
101
|
+
|
|
102
|
+
There are also instructions for AI agents to use when helping you to create your parser. They can
|
|
103
|
+
be pulled from the GitHub project repository, at pyparsing/ai/best_practices.md. You can also tell
|
|
104
|
+
the AI to access them programmatically after installing pyparsing, either from the CLI with
|
|
105
|
+
``python -m pyparsing.ai.show_best_practices`` or within python with
|
|
106
|
+
``import pyparsing; pyparsing.show_best_practices()``.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
License
|
|
110
|
+
=======
|
|
111
|
+
|
|
112
|
+
MIT License. See header of the `pyparsing __init__.py <https://github.com/pyparsing/pyparsing/blob/master/pyparsing/__init__.py#L1-L23>`__ file.
|
|
113
|
+
|
|
114
|
+
History
|
|
115
|
+
=======
|
|
116
|
+
|
|
117
|
+
See `CHANGES <https://github.com/pyparsing/pyparsing/blob/master/CHANGES>`__ file.
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
Performance benchmarks
|
|
121
|
+
======================
|
|
122
|
+
|
|
123
|
+
For usage instructions and details on the performance benchmark suite, see
|
|
124
|
+
``tests/README.md`` in this repository.
|
|
125
|
+
|
|
126
|
+
.. |Build Status| image:: https://github.com/pyparsing/pyparsing/actions/workflows/ci.yml/badge.svg
|
|
127
|
+
:target: https://github.com/pyparsing/pyparsing/actions/workflows/ci.yml
|
|
128
|
+
|
|
129
|
+
.. |Coverage| image:: https://codecov.io/gh/pyparsing/pyparsing/branch/master/graph/badge.svg
|
|
130
|
+
:target: https://codecov.io/gh/pyparsing/pyparsing
|
|
131
|
+
|
|
132
|
+
.. |Version| image:: https://img.shields.io/pypi/v/pyparsing?style=flat-square
|
|
133
|
+
:target: https://pypi.org/project/pyparsing/
|
|
134
|
+
:alt: Version
|
|
135
|
+
|
|
136
|
+
.. |License| image:: https://img.shields.io/pypi/l/pyparsing.svg?style=flat-square
|
|
137
|
+
:target: https://pypi.org/project/pyparsing/
|
|
138
|
+
:alt: License
|
|
139
|
+
|
|
140
|
+
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pyparsing.svg?style=flat-square
|
|
141
|
+
:target: https://pypi.org/project/python-liquid/
|
|
142
|
+
:alt: Python versions
|
|
143
|
+
|
|
144
|
+
.. |Snyk Score| image:: https://snyk.io//advisor/python/pyparsing/badge.svg
|
|
145
|
+
:target: https://snyk.io//advisor/python/pyparsing
|
|
146
|
+
:alt: pyparsing
|
|
147
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
pyparsing/__init__.py,sha256=EEQyRia3Jb1bCljvlfFHfplyCnZl3KQrqT0ULZvkWPA,13438
|
|
2
|
+
pyparsing/actions.py,sha256=pDW63VkWh5TqR6AKOMyDCobI0JF-QuBc45AI-oIkD8w,8104
|
|
3
|
+
pyparsing/common.py,sha256=lokAcppJg0Fshx9UifH-5B2CSPAJqHd3wCxGsUlXAGw,17029
|
|
4
|
+
pyparsing/core.py,sha256=6EZj5oIH9xD49J2XCTFoWhOsAIaU7Ja5_0MAF_pGmZo,253070
|
|
5
|
+
pyparsing/exceptions.py,sha256=Zt9-vrA4uTV8J1IlJVC0glQgakwRBOYcDsN3TfwqQeo,10981
|
|
6
|
+
pyparsing/helpers.py,sha256=GgjbrW28fr3t4hmAYFzFU_2PokVP1xnJqfXknhrZnbA,65644
|
|
7
|
+
pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
pyparsing/results.py,sha256=lEZVyQPWWarznwY_Jf_2t94HvGI7eVDzZ8gS4pf0eTQ,27603
|
|
9
|
+
pyparsing/testing.py,sha256=kcq6sLPyA23hrwDzB0NF5X61KGz0IRDjnhObC62_mOk,15452
|
|
10
|
+
pyparsing/unicode.py,sha256=jmszpRnfyhCQWl2Rh_94dT_lxQM6d4KiSf9ivY9b_m4,10612
|
|
11
|
+
pyparsing/util.py,sha256=VLIrcqIh5w6n9-t2CzwRAIs_j0dQ7_AWbn2eaIyFHxU,15997
|
|
12
|
+
pyparsing/warnings.py,sha256=wQWNM7Kal10OQBHHcG9SgdwaBeFbUe3fDEF3fcHBHCE,357
|
|
13
|
+
pyparsing/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
pyparsing/ai/best_practices.md,sha256=qlPKTmeTEYaaAFRGkwR3EzukP-TeIFLzoYOJzoVBcIY,8472
|
|
15
|
+
pyparsing/ai/show_best_practices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
pyparsing/ai/show_best_practices/__main__.py,sha256=xGXaCqBTTWZxbdLNz5G9y6cMK1Ppm7bcgVYgD1AcKmE,49
|
|
17
|
+
pyparsing/diagram/__init__.py,sha256=NAtp0ZWok37JJ1TVREvZaD4CqzvIQJRUHOAFHl_auoA,26929
|
|
18
|
+
pyparsing/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
pyparsing/tools/cvt_pyparsing_pep8_names.py,sha256=H8AoHdYs7rU8a0RRF4zjurjG_xZ8pOLVBhAi0-FgVOY,6389
|
|
20
|
+
pyparsing-3.3.3.dist-info/licenses/LICENSE,sha256=pUJfncFKx01MXwtnnpQfJELjLMp0UqRBjVsaSYk-vk4,1062
|
|
21
|
+
pyparsing-3.3.3.dist-info/WHEEL,sha256=lqN8jXt_QloFOGcx9kStvWbryhfEXoI2F2z-YURm350,81
|
|
22
|
+
pyparsing-3.3.3.dist-info/METADATA,sha256=8so0BS1yBiLTAAI0cu-K6U_ltX1prLsoqGZ_bdDhNJc,5857
|
|
23
|
+
pyparsing-3.3.3.dist-info/RECORD,,
|