@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,928 @@
|
|
|
1
|
+
# results.py
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import collections
|
|
6
|
+
from collections.abc import (
|
|
7
|
+
MutableMapping,
|
|
8
|
+
Mapping,
|
|
9
|
+
MutableSequence,
|
|
10
|
+
Iterator,
|
|
11
|
+
Iterable,
|
|
12
|
+
)
|
|
13
|
+
import pprint
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from .util import deprecate_argument, _is_iterable, _flatten
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
str_type: tuple[type, ...] = (str, bytes)
|
|
20
|
+
_generator_type = type((_ for _ in ()))
|
|
21
|
+
NULL_SLICE: slice = slice(None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _ParseResultsWithOffset:
|
|
25
|
+
tup: tuple[ParseResults, int]
|
|
26
|
+
__slots__ = ["tup"]
|
|
27
|
+
|
|
28
|
+
def __init__(self, p1: ParseResults, p2: int) -> None:
|
|
29
|
+
self.tup: tuple[ParseResults, int] = (p1, p2)
|
|
30
|
+
|
|
31
|
+
def __getitem__(self, i):
|
|
32
|
+
return self.tup[i]
|
|
33
|
+
|
|
34
|
+
def __getstate__(self):
|
|
35
|
+
return self.tup
|
|
36
|
+
|
|
37
|
+
def __setstate__(self, *args):
|
|
38
|
+
self.tup = args[0]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ParseResults:
|
|
42
|
+
"""Structured parse results, to provide multiple means of access to
|
|
43
|
+
the parsed data:
|
|
44
|
+
|
|
45
|
+
- as a list (``len(results)``)
|
|
46
|
+
- by list index (``results[0], results[1]``, etc.)
|
|
47
|
+
- by attribute (``results.<results_name>`` - see :class:`ParserElement.set_results_name`)
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
.. testcode::
|
|
52
|
+
|
|
53
|
+
integer = Word(nums)
|
|
54
|
+
date_str = (integer.set_results_name("year") + '/'
|
|
55
|
+
+ integer.set_results_name("month") + '/'
|
|
56
|
+
+ integer.set_results_name("day"))
|
|
57
|
+
# equivalent form:
|
|
58
|
+
# date_str = (integer("year") + '/'
|
|
59
|
+
# + integer("month") + '/'
|
|
60
|
+
# + integer("day"))
|
|
61
|
+
|
|
62
|
+
# parse_string returns a ParseResults object
|
|
63
|
+
result = date_str.parse_string("1999/12/31")
|
|
64
|
+
|
|
65
|
+
def test(s, fn=repr):
|
|
66
|
+
print(f"{s} -> {fn(eval(s))}")
|
|
67
|
+
|
|
68
|
+
test("list(result)")
|
|
69
|
+
test("result[0]")
|
|
70
|
+
test("result['month']")
|
|
71
|
+
test("result.day")
|
|
72
|
+
test("'month' in result")
|
|
73
|
+
test("'minutes' in result")
|
|
74
|
+
test("result.dump()", str)
|
|
75
|
+
|
|
76
|
+
prints:
|
|
77
|
+
|
|
78
|
+
.. testoutput::
|
|
79
|
+
|
|
80
|
+
list(result) -> ['1999', '/', '12', '/', '31']
|
|
81
|
+
result[0] -> '1999'
|
|
82
|
+
result['month'] -> '12'
|
|
83
|
+
result.day -> '31'
|
|
84
|
+
'month' in result -> True
|
|
85
|
+
'minutes' in result -> False
|
|
86
|
+
result.dump() -> ['1999', '/', '12', '/', '31']
|
|
87
|
+
- day: '31'
|
|
88
|
+
- month: '12'
|
|
89
|
+
- year: '1999'
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
_null_values: tuple[Any, ...] = (None, [], ())
|
|
94
|
+
|
|
95
|
+
_name: str
|
|
96
|
+
_parent: ParseResults
|
|
97
|
+
_all_names: set[str]
|
|
98
|
+
_modal: bool
|
|
99
|
+
_toklist: list[Any]
|
|
100
|
+
_tokdict: dict[str, Any]
|
|
101
|
+
|
|
102
|
+
__slots__ = (
|
|
103
|
+
"_name",
|
|
104
|
+
"_parent",
|
|
105
|
+
"_all_names",
|
|
106
|
+
"_modal",
|
|
107
|
+
"_toklist",
|
|
108
|
+
"_tokdict",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
class List(list):
|
|
112
|
+
"""
|
|
113
|
+
Simple wrapper class to distinguish parsed list results that should be preserved
|
|
114
|
+
as actual Python lists, instead of being converted to :class:`ParseResults`:
|
|
115
|
+
|
|
116
|
+
.. testcode::
|
|
117
|
+
|
|
118
|
+
import pyparsing as pp
|
|
119
|
+
ppc = pp.common
|
|
120
|
+
|
|
121
|
+
LBRACK, RBRACK, LPAR, RPAR = pp.Suppress.using_each("[]()")
|
|
122
|
+
element = pp.Forward()
|
|
123
|
+
item = ppc.integer
|
|
124
|
+
item_list = pp.DelimitedList(element)
|
|
125
|
+
element_list = LBRACK + item_list + RBRACK | LPAR + item_list + RPAR
|
|
126
|
+
element <<= item | element_list
|
|
127
|
+
|
|
128
|
+
# add parse action to convert from ParseResults
|
|
129
|
+
# to actual Python collection types
|
|
130
|
+
@element_list.add_parse_action
|
|
131
|
+
def as_python_list(t):
|
|
132
|
+
return pp.ParseResults.List(t.as_list())
|
|
133
|
+
|
|
134
|
+
element.run_tests('''
|
|
135
|
+
100
|
|
136
|
+
[2,3,4]
|
|
137
|
+
[[2, 1],3,4]
|
|
138
|
+
[(2, 1),3,4]
|
|
139
|
+
(2,3,4)
|
|
140
|
+
([2, 3], 4)
|
|
141
|
+
''', post_parse=lambda s, r: (r[0], type(r[0]))
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
prints:
|
|
145
|
+
|
|
146
|
+
.. testoutput::
|
|
147
|
+
:options: +NORMALIZE_WHITESPACE
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
100
|
|
151
|
+
(100, <class 'int'>)
|
|
152
|
+
|
|
153
|
+
[2,3,4]
|
|
154
|
+
([2, 3, 4], <class 'list'>)
|
|
155
|
+
|
|
156
|
+
[[2, 1],3,4]
|
|
157
|
+
([[2, 1], 3, 4], <class 'list'>)
|
|
158
|
+
|
|
159
|
+
[(2, 1),3,4]
|
|
160
|
+
([[2, 1], 3, 4], <class 'list'>)
|
|
161
|
+
|
|
162
|
+
(2,3,4)
|
|
163
|
+
([2, 3, 4], <class 'list'>)
|
|
164
|
+
|
|
165
|
+
([2, 3], 4)
|
|
166
|
+
([[2, 3], 4], <class 'list'>)
|
|
167
|
+
|
|
168
|
+
(Used internally by :class:`Group` when `aslist=True`.)
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __new__(cls, contained=None):
|
|
172
|
+
if contained is None:
|
|
173
|
+
contained = []
|
|
174
|
+
|
|
175
|
+
if not isinstance(contained, list):
|
|
176
|
+
raise TypeError(
|
|
177
|
+
f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return list.__new__(cls)
|
|
181
|
+
|
|
182
|
+
def __new__(cls, toklist=None, name=None, **kwargs):
|
|
183
|
+
if isinstance(toklist, ParseResults):
|
|
184
|
+
return toklist
|
|
185
|
+
self = object.__new__(cls)
|
|
186
|
+
self._name = None
|
|
187
|
+
self._parent = None
|
|
188
|
+
self._all_names = set()
|
|
189
|
+
|
|
190
|
+
if toklist is None:
|
|
191
|
+
self._toklist = []
|
|
192
|
+
elif isinstance(toklist, (list, _generator_type)):
|
|
193
|
+
self._toklist = (
|
|
194
|
+
[toklist[:]]
|
|
195
|
+
if isinstance(toklist, ParseResults.List)
|
|
196
|
+
else list(toklist)
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
self._toklist = [toklist]
|
|
200
|
+
self._tokdict = dict()
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
# Performance tuning: we construct a *lot* of these, so keep this
|
|
204
|
+
# constructor as small and fast as possible
|
|
205
|
+
def __init__(
|
|
206
|
+
self,
|
|
207
|
+
toklist=None,
|
|
208
|
+
name=None,
|
|
209
|
+
aslist=True,
|
|
210
|
+
modal=True,
|
|
211
|
+
isinstance=isinstance,
|
|
212
|
+
**kwargs,
|
|
213
|
+
) -> None:
|
|
214
|
+
asList = deprecate_argument(kwargs, "asList", True, new_name="aslist")
|
|
215
|
+
|
|
216
|
+
asList = asList and aslist
|
|
217
|
+
self._tokdict: dict[str, _ParseResultsWithOffset]
|
|
218
|
+
self._modal = modal
|
|
219
|
+
|
|
220
|
+
if name is None or name == "":
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
if isinstance(name, int):
|
|
224
|
+
name = str(name)
|
|
225
|
+
|
|
226
|
+
if not modal:
|
|
227
|
+
self._all_names = {name}
|
|
228
|
+
|
|
229
|
+
self._name = name
|
|
230
|
+
|
|
231
|
+
if toklist in self._null_values:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
if isinstance(toklist, (str_type, type)):
|
|
235
|
+
toklist = [toklist]
|
|
236
|
+
|
|
237
|
+
if asList:
|
|
238
|
+
if isinstance(toklist, ParseResults):
|
|
239
|
+
self[name] = _ParseResultsWithOffset(ParseResults(toklist._toklist), 0)
|
|
240
|
+
else:
|
|
241
|
+
self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]), 0)
|
|
242
|
+
self[name]._name = name
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
self[name] = toklist[0]
|
|
247
|
+
except (KeyError, TypeError, IndexError):
|
|
248
|
+
if toklist is not self:
|
|
249
|
+
self[name] = toklist
|
|
250
|
+
else:
|
|
251
|
+
self._name = name
|
|
252
|
+
|
|
253
|
+
def __getitem__(self, i):
|
|
254
|
+
if isinstance(i, (int, slice)):
|
|
255
|
+
return self._toklist[i]
|
|
256
|
+
|
|
257
|
+
if i not in self._all_names:
|
|
258
|
+
return self._tokdict[i][-1][0]
|
|
259
|
+
|
|
260
|
+
return ParseResults([v[0] for v in self._tokdict[i]])
|
|
261
|
+
|
|
262
|
+
def __setitem__(self, k, v, isinstance=isinstance):
|
|
263
|
+
if isinstance(v, _ParseResultsWithOffset):
|
|
264
|
+
self._tokdict[k] = self._tokdict.get(k, list()) + [v]
|
|
265
|
+
sub = v[0]
|
|
266
|
+
elif isinstance(k, (int, slice)):
|
|
267
|
+
self._toklist[k] = v
|
|
268
|
+
sub = v
|
|
269
|
+
else:
|
|
270
|
+
self._tokdict[k] = self._tokdict.get(k, []) + [
|
|
271
|
+
_ParseResultsWithOffset(v, 0)
|
|
272
|
+
]
|
|
273
|
+
sub = v
|
|
274
|
+
if isinstance(sub, ParseResults):
|
|
275
|
+
sub._parent = self
|
|
276
|
+
|
|
277
|
+
def __delitem__(self, i):
|
|
278
|
+
if not isinstance(i, (int, slice)):
|
|
279
|
+
del self._tokdict[i]
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
# slight optimization if del results[:]
|
|
283
|
+
if i == NULL_SLICE:
|
|
284
|
+
self._toklist.clear()
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
mylen = len(self._toklist)
|
|
288
|
+
del self._toklist[i]
|
|
289
|
+
|
|
290
|
+
# convert int to slice
|
|
291
|
+
if isinstance(i, int):
|
|
292
|
+
if i < 0:
|
|
293
|
+
i += mylen
|
|
294
|
+
i = slice(i, i + 1)
|
|
295
|
+
# get removed indices
|
|
296
|
+
removed = list(range(*i.indices(mylen)))
|
|
297
|
+
removed.reverse()
|
|
298
|
+
# fixup indices in token dictionary
|
|
299
|
+
for occurrences in self._tokdict.values():
|
|
300
|
+
for j in removed:
|
|
301
|
+
for k, (value, position) in enumerate(occurrences):
|
|
302
|
+
occurrences[k] = _ParseResultsWithOffset(
|
|
303
|
+
value, position - (position > j)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def __contains__(self, k) -> bool:
|
|
307
|
+
return k in self._tokdict
|
|
308
|
+
|
|
309
|
+
def __len__(self) -> int:
|
|
310
|
+
return len(self._toklist)
|
|
311
|
+
|
|
312
|
+
def __bool__(self) -> bool:
|
|
313
|
+
return not not (self._toklist or self._tokdict)
|
|
314
|
+
|
|
315
|
+
def __iter__(self) -> Iterator:
|
|
316
|
+
return iter(self._toklist)
|
|
317
|
+
|
|
318
|
+
def __reversed__(self) -> Iterator:
|
|
319
|
+
return iter(self._toklist[::-1])
|
|
320
|
+
|
|
321
|
+
def keys(self):
|
|
322
|
+
return iter(self._tokdict)
|
|
323
|
+
|
|
324
|
+
def values(self):
|
|
325
|
+
return (self[k] for k in self.keys())
|
|
326
|
+
|
|
327
|
+
def items(self):
|
|
328
|
+
return ((k, self[k]) for k in self.keys())
|
|
329
|
+
|
|
330
|
+
def haskeys(self) -> bool:
|
|
331
|
+
"""
|
|
332
|
+
Since ``keys()`` returns an iterator, this method is helpful in bypassing
|
|
333
|
+
code that looks for the existence of any defined results names."""
|
|
334
|
+
return not not self._tokdict
|
|
335
|
+
|
|
336
|
+
def pop(self, *args, **kwargs):
|
|
337
|
+
"""
|
|
338
|
+
Removes and returns item at specified index (default= ``last``).
|
|
339
|
+
Supports both ``list`` and ``dict`` semantics for ``pop()``. If
|
|
340
|
+
passed no argument or an integer argument, it will use ``list``
|
|
341
|
+
semantics and pop tokens from the list of parsed tokens. If passed
|
|
342
|
+
a non-integer argument (most likely a string), it will use ``dict``
|
|
343
|
+
semantics and pop the corresponding value from any defined results
|
|
344
|
+
names. A second default return value argument is supported, just as in
|
|
345
|
+
``dict.pop()``.
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
|
|
349
|
+
.. doctest::
|
|
350
|
+
|
|
351
|
+
>>> numlist = Word(nums)[...]
|
|
352
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
353
|
+
['0', '123', '321']
|
|
354
|
+
|
|
355
|
+
>>> def remove_first(tokens):
|
|
356
|
+
... tokens.pop(0)
|
|
357
|
+
...
|
|
358
|
+
>>> numlist.add_parse_action(remove_first)
|
|
359
|
+
[W:(0-9)]...
|
|
360
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
361
|
+
['123', '321']
|
|
362
|
+
|
|
363
|
+
>>> label = Word(alphas)
|
|
364
|
+
>>> patt = label("LABEL") + Word(nums)[1, ...]
|
|
365
|
+
>>> print(patt.parse_string("AAB 123 321").dump())
|
|
366
|
+
['AAB', '123', '321']
|
|
367
|
+
- LABEL: 'AAB'
|
|
368
|
+
|
|
369
|
+
>>> # Use pop() in a parse action to remove named result
|
|
370
|
+
>>> # (note that corresponding value is not
|
|
371
|
+
>>> # removed from list form of results)
|
|
372
|
+
>>> def remove_LABEL(tokens):
|
|
373
|
+
... tokens.pop("LABEL")
|
|
374
|
+
... return tokens
|
|
375
|
+
...
|
|
376
|
+
>>> patt.add_parse_action(remove_LABEL)
|
|
377
|
+
{W:(A-Za-z) {W:(0-9)}...}
|
|
378
|
+
>>> print(patt.parse_string("AAB 123 321").dump())
|
|
379
|
+
['AAB', '123', '321']
|
|
380
|
+
|
|
381
|
+
"""
|
|
382
|
+
if not args:
|
|
383
|
+
args = [-1]
|
|
384
|
+
for k, v in kwargs.items():
|
|
385
|
+
if k == "default":
|
|
386
|
+
args = (args[0], v)
|
|
387
|
+
else:
|
|
388
|
+
raise TypeError(f"pop() got an unexpected keyword argument {k!r}")
|
|
389
|
+
if isinstance(args[0], int) or len(args) == 1 or args[0] in self:
|
|
390
|
+
index = args[0]
|
|
391
|
+
ret = self[index]
|
|
392
|
+
del self[index]
|
|
393
|
+
return ret
|
|
394
|
+
else:
|
|
395
|
+
defaultvalue = args[1]
|
|
396
|
+
return defaultvalue
|
|
397
|
+
|
|
398
|
+
def get(self, key, default_value=None):
|
|
399
|
+
"""
|
|
400
|
+
Returns named result matching the given key, or if there is no
|
|
401
|
+
such name, then returns the given ``default_value`` or ``None`` if no
|
|
402
|
+
``default_value`` is specified.
|
|
403
|
+
|
|
404
|
+
Similar to ``dict.get()``.
|
|
405
|
+
|
|
406
|
+
Example:
|
|
407
|
+
|
|
408
|
+
.. doctest::
|
|
409
|
+
|
|
410
|
+
>>> integer = Word(nums)
|
|
411
|
+
>>> date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
|
|
412
|
+
|
|
413
|
+
>>> result = date_str.parse_string("1999/12/31")
|
|
414
|
+
>>> result.get("year")
|
|
415
|
+
'1999'
|
|
416
|
+
>>> result.get("hour", "not specified")
|
|
417
|
+
'not specified'
|
|
418
|
+
>>> result.get("hour")
|
|
419
|
+
|
|
420
|
+
"""
|
|
421
|
+
if key in self:
|
|
422
|
+
return self[key]
|
|
423
|
+
else:
|
|
424
|
+
return default_value
|
|
425
|
+
|
|
426
|
+
def insert(self, index, ins_string):
|
|
427
|
+
"""
|
|
428
|
+
Inserts new element at location index in the list of parsed tokens.
|
|
429
|
+
|
|
430
|
+
Similar to ``list.insert()``.
|
|
431
|
+
|
|
432
|
+
Example:
|
|
433
|
+
|
|
434
|
+
.. doctest::
|
|
435
|
+
|
|
436
|
+
>>> numlist = Word(nums)[...]
|
|
437
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
438
|
+
['0', '123', '321']
|
|
439
|
+
|
|
440
|
+
>>> # use a parse action to insert the parse location
|
|
441
|
+
>>> # in the front of the parsed results
|
|
442
|
+
>>> def insert_locn(locn, tokens):
|
|
443
|
+
... tokens.insert(0, locn)
|
|
444
|
+
...
|
|
445
|
+
>>> numlist.add_parse_action(insert_locn)
|
|
446
|
+
[W:(0-9)]...
|
|
447
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
448
|
+
[0, '0', '123', '321']
|
|
449
|
+
|
|
450
|
+
"""
|
|
451
|
+
self._toklist.insert(index, ins_string)
|
|
452
|
+
# fixup indices in token dictionary
|
|
453
|
+
for occurrences in self._tokdict.values():
|
|
454
|
+
for k, (value, position) in enumerate(occurrences):
|
|
455
|
+
occurrences[k] = _ParseResultsWithOffset(
|
|
456
|
+
value, position + (position > index)
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
def append(self, item):
|
|
460
|
+
"""
|
|
461
|
+
Add single element to end of ``ParseResults`` list of elements.
|
|
462
|
+
|
|
463
|
+
Example:
|
|
464
|
+
|
|
465
|
+
.. doctest::
|
|
466
|
+
|
|
467
|
+
>>> numlist = Word(nums)[...]
|
|
468
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
469
|
+
['0', '123', '321']
|
|
470
|
+
|
|
471
|
+
>>> # use a parse action to compute the sum of the parsed integers,
|
|
472
|
+
>>> # and add it to the end
|
|
473
|
+
>>> def append_sum(tokens):
|
|
474
|
+
... tokens.append(sum(map(int, tokens)))
|
|
475
|
+
...
|
|
476
|
+
>>> numlist.add_parse_action(append_sum)
|
|
477
|
+
[W:(0-9)]...
|
|
478
|
+
>>> print(numlist.parse_string("0 123 321"))
|
|
479
|
+
['0', '123', '321', 444]
|
|
480
|
+
"""
|
|
481
|
+
self._toklist.append(item)
|
|
482
|
+
|
|
483
|
+
def extend(self, itemseq):
|
|
484
|
+
"""
|
|
485
|
+
Add sequence of elements to end of :class:`ParseResults` list of elements.
|
|
486
|
+
|
|
487
|
+
Example:
|
|
488
|
+
|
|
489
|
+
.. testcode::
|
|
490
|
+
|
|
491
|
+
patt = Word(alphas)[1, ...]
|
|
492
|
+
|
|
493
|
+
# use a parse action to append the reverse of the matched strings,
|
|
494
|
+
# to make a palindrome
|
|
495
|
+
def make_palindrome(tokens):
|
|
496
|
+
tokens.extend(reversed([t[::-1] for t in tokens]))
|
|
497
|
+
return ''.join(tokens)
|
|
498
|
+
|
|
499
|
+
patt.add_parse_action(make_palindrome)
|
|
500
|
+
print(patt.parse_string("lskdj sdlkjf lksd"))
|
|
501
|
+
|
|
502
|
+
prints:
|
|
503
|
+
|
|
504
|
+
.. testoutput::
|
|
505
|
+
|
|
506
|
+
['lskdjsdlkjflksddsklfjkldsjdksl']
|
|
507
|
+
"""
|
|
508
|
+
if isinstance(itemseq, ParseResults):
|
|
509
|
+
self.__iadd__(itemseq)
|
|
510
|
+
else:
|
|
511
|
+
self._toklist.extend(itemseq)
|
|
512
|
+
|
|
513
|
+
def clear(self):
|
|
514
|
+
"""
|
|
515
|
+
Clear all elements and results names.
|
|
516
|
+
"""
|
|
517
|
+
del self._toklist[:]
|
|
518
|
+
self._tokdict.clear()
|
|
519
|
+
|
|
520
|
+
def __getattr__(self, name):
|
|
521
|
+
try:
|
|
522
|
+
return self[name]
|
|
523
|
+
except KeyError:
|
|
524
|
+
if name.startswith("__"):
|
|
525
|
+
raise AttributeError(name)
|
|
526
|
+
return ""
|
|
527
|
+
|
|
528
|
+
def __add__(self, other: ParseResults) -> ParseResults:
|
|
529
|
+
ret = self.copy()
|
|
530
|
+
ret += other
|
|
531
|
+
return ret
|
|
532
|
+
|
|
533
|
+
def __iadd__(self, other: ParseResults) -> ParseResults:
|
|
534
|
+
if not other:
|
|
535
|
+
return self
|
|
536
|
+
|
|
537
|
+
if other._tokdict:
|
|
538
|
+
offset = len(self._toklist)
|
|
539
|
+
addoffset = lambda a: offset if a < 0 else a + offset
|
|
540
|
+
otheritems = other._tokdict.items()
|
|
541
|
+
otherdictitems = [
|
|
542
|
+
(k, _ParseResultsWithOffset(v[0], addoffset(v[1])))
|
|
543
|
+
for k, vlist in otheritems
|
|
544
|
+
for v in vlist
|
|
545
|
+
]
|
|
546
|
+
for k, v in otherdictitems:
|
|
547
|
+
self[k] = v
|
|
548
|
+
if isinstance(v[0], ParseResults):
|
|
549
|
+
v[0]._parent = self
|
|
550
|
+
|
|
551
|
+
self._toklist += other._toklist
|
|
552
|
+
self._all_names |= other._all_names
|
|
553
|
+
return self
|
|
554
|
+
|
|
555
|
+
def __radd__(self, other) -> ParseResults:
|
|
556
|
+
if isinstance(other, int) and other == 0:
|
|
557
|
+
# useful for merging many ParseResults using sum() builtin
|
|
558
|
+
return self.copy()
|
|
559
|
+
else:
|
|
560
|
+
# this may raise a TypeError - so be it
|
|
561
|
+
return other + self
|
|
562
|
+
|
|
563
|
+
def __repr__(self) -> str:
|
|
564
|
+
return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})"
|
|
565
|
+
|
|
566
|
+
def __str__(self) -> str:
|
|
567
|
+
return (
|
|
568
|
+
"["
|
|
569
|
+
+ ", ".join(
|
|
570
|
+
[
|
|
571
|
+
str(i) if isinstance(i, ParseResults) else repr(i)
|
|
572
|
+
for i in self._toklist
|
|
573
|
+
]
|
|
574
|
+
)
|
|
575
|
+
+ "]"
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
def _asStringList(self, sep=""):
|
|
579
|
+
out = []
|
|
580
|
+
for item in self._toklist:
|
|
581
|
+
if out and sep:
|
|
582
|
+
out.append(sep)
|
|
583
|
+
if isinstance(item, ParseResults):
|
|
584
|
+
out += item._asStringList()
|
|
585
|
+
else:
|
|
586
|
+
out.append(str(item))
|
|
587
|
+
return out
|
|
588
|
+
|
|
589
|
+
def as_list(self, *, flatten: bool = False) -> list:
|
|
590
|
+
"""
|
|
591
|
+
Returns the parse results as a nested list of matching tokens, all converted to strings.
|
|
592
|
+
If ``flatten`` is True, all the nesting levels in the returned list are collapsed.
|
|
593
|
+
|
|
594
|
+
Example:
|
|
595
|
+
|
|
596
|
+
.. doctest::
|
|
597
|
+
|
|
598
|
+
>>> patt = Word(alphas)[1, ...]
|
|
599
|
+
>>> result = patt.parse_string("sldkj lsdkj sldkj")
|
|
600
|
+
>>> # even though the result prints in string-like form,
|
|
601
|
+
>>> # it is actually a pyparsing ParseResults
|
|
602
|
+
>>> type(result)
|
|
603
|
+
<class 'pyparsing.results.ParseResults'>
|
|
604
|
+
>>> print(result)
|
|
605
|
+
['sldkj', 'lsdkj', 'sldkj']
|
|
606
|
+
|
|
607
|
+
.. doctest::
|
|
608
|
+
|
|
609
|
+
>>> # Use as_list() to create an actual list
|
|
610
|
+
>>> result_list = result.as_list()
|
|
611
|
+
>>> type(result_list)
|
|
612
|
+
<class 'list'>
|
|
613
|
+
>>> print(result_list)
|
|
614
|
+
['sldkj', 'lsdkj', 'sldkj']
|
|
615
|
+
|
|
616
|
+
.. versionchanged:: 3.2.0
|
|
617
|
+
New ``flatten`` argument.
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
if flatten:
|
|
621
|
+
return [*_flatten(self)]
|
|
622
|
+
else:
|
|
623
|
+
return [
|
|
624
|
+
res.as_list() if isinstance(res, ParseResults) else res
|
|
625
|
+
for res in self._toklist
|
|
626
|
+
]
|
|
627
|
+
|
|
628
|
+
def as_dict(self) -> dict:
|
|
629
|
+
"""
|
|
630
|
+
Returns the named parse results as a nested dictionary.
|
|
631
|
+
|
|
632
|
+
Example:
|
|
633
|
+
|
|
634
|
+
.. doctest::
|
|
635
|
+
|
|
636
|
+
>>> integer = pp.Word(pp.nums)
|
|
637
|
+
>>> date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
|
|
638
|
+
|
|
639
|
+
>>> result = date_str.parse_string('1999/12/31')
|
|
640
|
+
>>> type(result)
|
|
641
|
+
<class 'pyparsing.results.ParseResults'>
|
|
642
|
+
>>> result
|
|
643
|
+
ParseResults(['1999', '/', '12', '/', '31'], {'year': '1999', 'month': '12', 'day': '31'})
|
|
644
|
+
|
|
645
|
+
>>> result_dict = result.as_dict()
|
|
646
|
+
>>> type(result_dict)
|
|
647
|
+
<class 'dict'>
|
|
648
|
+
>>> result_dict
|
|
649
|
+
{'year': '1999', 'month': '12', 'day': '31'}
|
|
650
|
+
|
|
651
|
+
>>> # even though a ParseResults supports dict-like access,
|
|
652
|
+
>>> # sometime you just need to have a dict
|
|
653
|
+
>>> import json
|
|
654
|
+
>>> print(json.dumps(result))
|
|
655
|
+
Traceback (most recent call last):
|
|
656
|
+
TypeError: Object of type ParseResults is not JSON serializable
|
|
657
|
+
>>> print(json.dumps(result.as_dict()))
|
|
658
|
+
{"year": "1999", "month": "12", "day": "31"}
|
|
659
|
+
"""
|
|
660
|
+
|
|
661
|
+
def to_item(obj):
|
|
662
|
+
if isinstance(obj, ParseResults):
|
|
663
|
+
return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj]
|
|
664
|
+
else:
|
|
665
|
+
return obj
|
|
666
|
+
|
|
667
|
+
return dict((k, to_item(v)) for k, v in self.items())
|
|
668
|
+
|
|
669
|
+
def copy(self) -> ParseResults:
|
|
670
|
+
"""
|
|
671
|
+
Returns a new shallow copy of a :class:`ParseResults` object.
|
|
672
|
+
:class:`ParseResults` items contained within the source are
|
|
673
|
+
shared with the copy. Use :meth:`ParseResults.deepcopy` to
|
|
674
|
+
create a copy with its own separate content values.
|
|
675
|
+
"""
|
|
676
|
+
ret = ParseResults(self._toklist)
|
|
677
|
+
ret._tokdict = self._tokdict.copy()
|
|
678
|
+
ret._parent = self._parent
|
|
679
|
+
ret._all_names |= self._all_names
|
|
680
|
+
ret._name = self._name
|
|
681
|
+
return ret
|
|
682
|
+
|
|
683
|
+
def deepcopy(self) -> ParseResults:
|
|
684
|
+
"""
|
|
685
|
+
Returns a new deep copy of a :class:`ParseResults` object.
|
|
686
|
+
|
|
687
|
+
.. versionadded:: 3.1.0
|
|
688
|
+
"""
|
|
689
|
+
ret = self.copy()
|
|
690
|
+
# replace values with copies if they are of known mutable types
|
|
691
|
+
for i, obj in enumerate(self._toklist):
|
|
692
|
+
if isinstance(obj, ParseResults):
|
|
693
|
+
ret._toklist[i] = obj.deepcopy()
|
|
694
|
+
elif isinstance(obj, (str, bytes)):
|
|
695
|
+
pass
|
|
696
|
+
elif isinstance(obj, MutableMapping):
|
|
697
|
+
ret._toklist[i] = dest = type(obj)()
|
|
698
|
+
for k, v in obj.items():
|
|
699
|
+
dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v
|
|
700
|
+
elif isinstance(obj, Iterable):
|
|
701
|
+
ret._toklist[i] = type(obj)(
|
|
702
|
+
v.deepcopy() if isinstance(v, ParseResults) else v for v in obj # type: ignore[call-arg]
|
|
703
|
+
)
|
|
704
|
+
return ret
|
|
705
|
+
|
|
706
|
+
def get_name(self) -> str | None:
|
|
707
|
+
r"""
|
|
708
|
+
Returns the results name for this token expression.
|
|
709
|
+
|
|
710
|
+
Useful when several different expressions might match
|
|
711
|
+
at a particular location.
|
|
712
|
+
|
|
713
|
+
Example:
|
|
714
|
+
|
|
715
|
+
.. testcode::
|
|
716
|
+
|
|
717
|
+
integer = Word(nums)
|
|
718
|
+
ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
|
|
719
|
+
house_number_expr = Suppress('#') + Word(nums, alphanums)
|
|
720
|
+
user_data = (Group(house_number_expr)("house_number")
|
|
721
|
+
| Group(ssn_expr)("ssn")
|
|
722
|
+
| Group(integer)("age"))
|
|
723
|
+
user_info = user_data[1, ...]
|
|
724
|
+
|
|
725
|
+
result = user_info.parse_string("22 111-22-3333 #221B")
|
|
726
|
+
for item in result:
|
|
727
|
+
print(item.get_name(), ':', item[0])
|
|
728
|
+
|
|
729
|
+
prints:
|
|
730
|
+
|
|
731
|
+
.. testoutput::
|
|
732
|
+
|
|
733
|
+
age : 22
|
|
734
|
+
ssn : 111-22-3333
|
|
735
|
+
house_number : 221B
|
|
736
|
+
|
|
737
|
+
"""
|
|
738
|
+
if self._name:
|
|
739
|
+
return self._name
|
|
740
|
+
elif self._parent:
|
|
741
|
+
par: ParseResults = self._parent
|
|
742
|
+
parent_tokdict_items = par._tokdict.items()
|
|
743
|
+
return next(
|
|
744
|
+
(
|
|
745
|
+
k
|
|
746
|
+
for k, vlist in parent_tokdict_items
|
|
747
|
+
for v, loc in vlist
|
|
748
|
+
if v is self
|
|
749
|
+
),
|
|
750
|
+
None,
|
|
751
|
+
)
|
|
752
|
+
elif (
|
|
753
|
+
len(self) == 1
|
|
754
|
+
and len(self._tokdict) == 1
|
|
755
|
+
and next(iter(self._tokdict.values()))[0][1] in (0, -1)
|
|
756
|
+
):
|
|
757
|
+
return next(iter(self._tokdict.keys()))
|
|
758
|
+
else:
|
|
759
|
+
return None
|
|
760
|
+
|
|
761
|
+
def dump(self, indent="", full=True, include_list=True, _depth=0) -> str:
|
|
762
|
+
"""
|
|
763
|
+
Diagnostic method for listing out the contents of
|
|
764
|
+
a :class:`ParseResults`. Accepts an optional ``indent`` argument so
|
|
765
|
+
that this string can be embedded in a nested display of other data.
|
|
766
|
+
|
|
767
|
+
Example:
|
|
768
|
+
|
|
769
|
+
.. testcode::
|
|
770
|
+
|
|
771
|
+
integer = Word(nums)
|
|
772
|
+
date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
|
|
773
|
+
|
|
774
|
+
result = date_str.parse_string('1999/12/31')
|
|
775
|
+
print(result.dump())
|
|
776
|
+
|
|
777
|
+
prints:
|
|
778
|
+
|
|
779
|
+
.. testoutput::
|
|
780
|
+
|
|
781
|
+
['1999', '/', '12', '/', '31']
|
|
782
|
+
- day: '31'
|
|
783
|
+
- month: '12'
|
|
784
|
+
- year: '1999'
|
|
785
|
+
"""
|
|
786
|
+
out = []
|
|
787
|
+
NL = "\n"
|
|
788
|
+
out.append(indent + str(self.as_list()) if include_list else "")
|
|
789
|
+
|
|
790
|
+
if not full:
|
|
791
|
+
return "".join(out)
|
|
792
|
+
|
|
793
|
+
if self.haskeys():
|
|
794
|
+
items = sorted((str(k), v) for k, v in self.items())
|
|
795
|
+
for k, v in items:
|
|
796
|
+
if out:
|
|
797
|
+
out.append(NL)
|
|
798
|
+
out.append(f"{indent}{(' ' * _depth)}- {k}: ")
|
|
799
|
+
if not isinstance(v, ParseResults):
|
|
800
|
+
out.append(repr(v))
|
|
801
|
+
continue
|
|
802
|
+
|
|
803
|
+
if not v:
|
|
804
|
+
out.append(str(v))
|
|
805
|
+
continue
|
|
806
|
+
|
|
807
|
+
out.append(
|
|
808
|
+
v.dump(
|
|
809
|
+
indent=indent,
|
|
810
|
+
full=full,
|
|
811
|
+
include_list=include_list,
|
|
812
|
+
_depth=_depth + 1,
|
|
813
|
+
)
|
|
814
|
+
)
|
|
815
|
+
if not any(isinstance(vv, ParseResults) for vv in self):
|
|
816
|
+
return "".join(out)
|
|
817
|
+
|
|
818
|
+
v = self
|
|
819
|
+
incr = " "
|
|
820
|
+
nl = "\n"
|
|
821
|
+
for i, vv in enumerate(v):
|
|
822
|
+
if isinstance(vv, ParseResults):
|
|
823
|
+
vv_dump = vv.dump(
|
|
824
|
+
indent=indent,
|
|
825
|
+
full=full,
|
|
826
|
+
include_list=include_list,
|
|
827
|
+
_depth=_depth + 1,
|
|
828
|
+
)
|
|
829
|
+
out.append(
|
|
830
|
+
f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv_dump}"
|
|
831
|
+
)
|
|
832
|
+
else:
|
|
833
|
+
out.append(
|
|
834
|
+
f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv}"
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
return "".join(out)
|
|
838
|
+
|
|
839
|
+
def pprint(self, *args, **kwargs):
|
|
840
|
+
"""
|
|
841
|
+
Pretty-printer for parsed results as a list, using the
|
|
842
|
+
`pprint <https://docs.python.org/3/library/pprint.html>`_ module.
|
|
843
|
+
Accepts additional positional or keyword args as defined for
|
|
844
|
+
`pprint.pprint <https://docs.python.org/3/library/pprint.html#pprint.pprint>`_ .
|
|
845
|
+
|
|
846
|
+
Example:
|
|
847
|
+
|
|
848
|
+
.. testcode::
|
|
849
|
+
|
|
850
|
+
ident = Word(alphas, alphanums)
|
|
851
|
+
num = Word(nums)
|
|
852
|
+
func = Forward()
|
|
853
|
+
term = ident | num | Group('(' + func + ')')
|
|
854
|
+
func <<= ident + Group(Optional(DelimitedList(term)))
|
|
855
|
+
result = func.parse_string("fna a,b,(fnb c,d,200),100")
|
|
856
|
+
result.pprint(width=40)
|
|
857
|
+
|
|
858
|
+
prints:
|
|
859
|
+
|
|
860
|
+
.. testoutput::
|
|
861
|
+
|
|
862
|
+
['fna',
|
|
863
|
+
['a',
|
|
864
|
+
'b',
|
|
865
|
+
['(', 'fnb', ['c', 'd', '200'], ')'],
|
|
866
|
+
'100']]
|
|
867
|
+
"""
|
|
868
|
+
pprint.pprint(self.as_list(), *args, **kwargs)
|
|
869
|
+
|
|
870
|
+
# add support for pickle protocol
|
|
871
|
+
def __getstate__(self):
|
|
872
|
+
return (
|
|
873
|
+
self._toklist,
|
|
874
|
+
(
|
|
875
|
+
self._tokdict.copy(),
|
|
876
|
+
None,
|
|
877
|
+
self._all_names,
|
|
878
|
+
self._name,
|
|
879
|
+
),
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
def __setstate__(self, state):
|
|
883
|
+
self._toklist, (self._tokdict, par, inAccumNames, self._name) = state
|
|
884
|
+
self._all_names = set(inAccumNames)
|
|
885
|
+
self._parent = None
|
|
886
|
+
|
|
887
|
+
def __getnewargs__(self):
|
|
888
|
+
return self._toklist, self._name
|
|
889
|
+
|
|
890
|
+
def __dir__(self):
|
|
891
|
+
return dir(type(self)) + list(self.keys())
|
|
892
|
+
|
|
893
|
+
@classmethod
|
|
894
|
+
def from_dict(cls, other, name=None) -> ParseResults:
|
|
895
|
+
"""
|
|
896
|
+
Helper classmethod to construct a :class:`ParseResults` from a ``dict``, preserving the
|
|
897
|
+
name-value relations as results names. If an optional ``name`` argument is
|
|
898
|
+
given, a nested :class:`ParseResults` will be returned.
|
|
899
|
+
"""
|
|
900
|
+
ret = cls([])
|
|
901
|
+
for k, v in other.items():
|
|
902
|
+
if isinstance(v, Mapping):
|
|
903
|
+
ret += cls.from_dict(v, name=k)
|
|
904
|
+
else:
|
|
905
|
+
ret += cls([v], name=k, aslist=_is_iterable(v))
|
|
906
|
+
if name is not None:
|
|
907
|
+
ret = cls([ret], name=name)
|
|
908
|
+
return ret
|
|
909
|
+
|
|
910
|
+
asList = as_list
|
|
911
|
+
"""
|
|
912
|
+
.. deprecated:: 3.0.0
|
|
913
|
+
use :meth:`as_list`
|
|
914
|
+
"""
|
|
915
|
+
asDict = as_dict
|
|
916
|
+
"""
|
|
917
|
+
.. deprecated:: 3.0.0
|
|
918
|
+
use :meth:`as_dict`
|
|
919
|
+
"""
|
|
920
|
+
getName = get_name
|
|
921
|
+
"""
|
|
922
|
+
.. deprecated:: 3.0.0
|
|
923
|
+
use :meth:`get_name`
|
|
924
|
+
"""
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
MutableMapping.register(ParseResults)
|
|
928
|
+
MutableSequence.register(ParseResults)
|