@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.
Files changed (43) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +3 -0
  3. package/lib/python3.14/site-packages/pyparsing/__init__.py +413 -0
  4. package/lib/python3.14/site-packages/pyparsing/__pycache__/__init__.cpython-314.pyc +0 -0
  5. package/lib/python3.14/site-packages/pyparsing/__pycache__/actions.cpython-314.pyc +0 -0
  6. package/lib/python3.14/site-packages/pyparsing/__pycache__/common.cpython-314.pyc +0 -0
  7. package/lib/python3.14/site-packages/pyparsing/__pycache__/core.cpython-314.pyc +0 -0
  8. package/lib/python3.14/site-packages/pyparsing/__pycache__/exceptions.cpython-314.pyc +0 -0
  9. package/lib/python3.14/site-packages/pyparsing/__pycache__/helpers.cpython-314.pyc +0 -0
  10. package/lib/python3.14/site-packages/pyparsing/__pycache__/results.cpython-314.pyc +0 -0
  11. package/lib/python3.14/site-packages/pyparsing/__pycache__/testing.cpython-314.pyc +0 -0
  12. package/lib/python3.14/site-packages/pyparsing/__pycache__/unicode.cpython-314.pyc +0 -0
  13. package/lib/python3.14/site-packages/pyparsing/__pycache__/util.cpython-314.pyc +0 -0
  14. package/lib/python3.14/site-packages/pyparsing/__pycache__/warnings.cpython-314.pyc +0 -0
  15. package/lib/python3.14/site-packages/pyparsing/actions.py +264 -0
  16. package/lib/python3.14/site-packages/pyparsing/ai/__init__.py +0 -0
  17. package/lib/python3.14/site-packages/pyparsing/ai/__pycache__/__init__.cpython-314.pyc +0 -0
  18. package/lib/python3.14/site-packages/pyparsing/ai/best_practices.md +75 -0
  19. package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__init__.py +0 -0
  20. package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__main__.py +2 -0
  21. package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__pycache__/__init__.cpython-314.pyc +0 -0
  22. package/lib/python3.14/site-packages/pyparsing/ai/show_best_practices/__pycache__/__main__.cpython-314.pyc +0 -0
  23. package/lib/python3.14/site-packages/pyparsing/common.py +570 -0
  24. package/lib/python3.14/site-packages/pyparsing/core.py +6972 -0
  25. package/lib/python3.14/site-packages/pyparsing/diagram/__init__.py +761 -0
  26. package/lib/python3.14/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-314.pyc +0 -0
  27. package/lib/python3.14/site-packages/pyparsing/exceptions.py +353 -0
  28. package/lib/python3.14/site-packages/pyparsing/helpers.py +1769 -0
  29. package/lib/python3.14/site-packages/pyparsing/py.typed +0 -0
  30. package/lib/python3.14/site-packages/pyparsing/results.py +928 -0
  31. package/lib/python3.14/site-packages/pyparsing/testing.py +398 -0
  32. package/lib/python3.14/site-packages/pyparsing/tools/__init__.py +0 -0
  33. package/lib/python3.14/site-packages/pyparsing/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  34. package/lib/python3.14/site-packages/pyparsing/tools/__pycache__/cvt_pyparsing_pep8_names.cpython-314.pyc +0 -0
  35. package/lib/python3.14/site-packages/pyparsing/tools/cvt_pyparsing_pep8_names.py +142 -0
  36. package/lib/python3.14/site-packages/pyparsing/unicode.py +356 -0
  37. package/lib/python3.14/site-packages/pyparsing/util.py +514 -0
  38. package/lib/python3.14/site-packages/pyparsing/warnings.py +10 -0
  39. package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/METADATA +147 -0
  40. package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/RECORD +23 -0
  41. package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/WHEEL +4 -0
  42. package/lib/python3.14/site-packages/pyparsing-3.3.3.dist-info/licenses/LICENSE +20 -0
  43. package/package.json +27 -0
@@ -0,0 +1,264 @@
1
+ # actions.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Union, Callable, Any
5
+
6
+ from .exceptions import ParseException
7
+ from .util import col, replaced_by_pep8
8
+ from .results import ParseResults
9
+
10
+
11
+ ParseAction = Union[
12
+ Callable[[], Any],
13
+ Callable[[ParseResults], Any],
14
+ Callable[[int, ParseResults], Any],
15
+ Callable[[str, int, ParseResults], Any],
16
+ ]
17
+
18
+
19
+ class OnlyOnce:
20
+ """
21
+ Wrapper for parse actions, to ensure they are only called once.
22
+ Note: parse action signature must include all 3 arguments.
23
+ """
24
+
25
+ def __init__(self, method_call: Callable[[str, int, ParseResults], Any]) -> None:
26
+ from .core import _trim_arity
27
+
28
+ self.callable = _trim_arity(method_call)
29
+ self.called = False
30
+
31
+ def __call__(self, s: str, l: int, t: ParseResults) -> ParseResults:
32
+ if not self.called:
33
+ results = self.callable(s, l, t)
34
+ self.called = True
35
+ return results
36
+ raise ParseException(s, l, "OnlyOnce obj called multiple times w/out reset")
37
+
38
+ def reset(self):
39
+ """
40
+ Allow the associated parse action to be called once more.
41
+ """
42
+
43
+ self.called = False
44
+
45
+
46
+ def match_only_at_col(n: int) -> ParseAction:
47
+ """
48
+ Helper method for defining parse actions that require matching at
49
+ a specific column in the input text.
50
+ """
51
+
52
+ def verify_col(strg: str, locn: int, toks: ParseResults) -> None:
53
+ if col(locn, strg) != n:
54
+ raise ParseException(strg, locn, f"matched token not at column {n}")
55
+
56
+ return verify_col
57
+
58
+
59
+ def replace_with(repl_str: Any) -> ParseAction:
60
+ """
61
+ Helper method for common parse actions that simply return
62
+ a literal value. Especially useful when used with
63
+ :meth:`~ParserElement.transform_string`.
64
+
65
+ Example:
66
+
67
+ .. doctest::
68
+
69
+ >>> num = Word(nums).set_parse_action(lambda toks: int(toks[0]))
70
+ >>> na = one_of("N/A NA").set_parse_action(replace_with(math.nan))
71
+ >>> term = na | num
72
+
73
+ >>> term[1, ...].parse_string("324 234 N/A 234")
74
+ ParseResults([324, 234, nan, 234], {})
75
+ """
76
+ return lambda s, l, t: [repl_str]
77
+
78
+
79
+ def remove_quotes(s: str, l: int, t: ParseResults) -> Any:
80
+ r"""
81
+ Helper parse action for removing quotation marks from parsed
82
+ quoted strings, that use a single character for quoting. For parsing
83
+ strings that may have multiple characters, use the :class:`QuotedString`
84
+ class.
85
+
86
+ Example:
87
+
88
+ .. doctest::
89
+
90
+ >>> # by default, quotation marks are included in parsed results
91
+ >>> quoted_string.parse_string("'Now is the Winter of our Discontent'")
92
+ ParseResults(["'Now is the Winter of our Discontent'"], {})
93
+
94
+ >>> # use remove_quotes to strip quotation marks from parsed results
95
+ >>> dequoted = quoted_string().set_parse_action(remove_quotes)
96
+ >>> dequoted.parse_string("'Now is the Winter of our Discontent'")
97
+ ParseResults(['Now is the Winter of our Discontent'], {})
98
+ """
99
+ return t[0][1:-1]
100
+
101
+
102
+ def with_attribute(*args: tuple[str, str], **attr_dict) -> ParseAction:
103
+ """
104
+ Helper to create a validating parse action to be used with start
105
+ tags created with :class:`make_xml_tags` or
106
+ :class:`make_html_tags`. Use ``with_attribute`` to qualify
107
+ a starting tag with a required attribute value, to avoid false
108
+ matches on common tags such as ``<TD>`` or ``<DIV>``.
109
+
110
+ Call ``with_attribute`` with a series of attribute names and
111
+ values. Specify the list of filter attributes names and values as:
112
+
113
+ - keyword arguments, as in ``(align="right")``, or
114
+ - as an explicit dict with ``**`` operator, when an attribute
115
+ name is also a Python reserved word, as in ``**{"class":"Customer", "align":"right"}``
116
+ - a list of name-value tuples, as in ``(("ns1:class", "Customer"), ("ns2:align", "right"))``
117
+
118
+ For attribute names with a namespace prefix, you must use the second
119
+ form. Attribute names are matched insensitive to upper/lower case.
120
+
121
+ If just testing for ``class`` (with or without a namespace), use
122
+ :class:`with_class`.
123
+
124
+ To verify that the attribute exists, but without specifying a value,
125
+ pass ``with_attribute.ANY_VALUE`` as the value.
126
+
127
+ The next two examples use the following input data and tag parsers:
128
+
129
+ .. testcode::
130
+
131
+ html = '''
132
+ <div>
133
+ Some text
134
+ <div type="grid">1 4 0 1 0</div>
135
+ <div type="graph">1,3 2,3 1,1</div>
136
+ <div>this has no type</div>
137
+ </div>
138
+ '''
139
+ div,div_end = make_html_tags("div")
140
+
141
+ Only match div tag having a type attribute with value "grid":
142
+
143
+ .. testcode::
144
+
145
+ div_grid = div().set_parse_action(with_attribute(type="grid"))
146
+ grid_expr = div_grid + SkipTo(div | div_end)("body")
147
+ for grid_header in grid_expr.search_string(html):
148
+ print(grid_header.body)
149
+
150
+ prints:
151
+
152
+ .. testoutput::
153
+
154
+ 1 4 0 1 0
155
+
156
+ Construct a match with any div tag having a type attribute,
157
+ regardless of the value:
158
+
159
+ .. testcode::
160
+
161
+ div_any_type = div().set_parse_action(
162
+ with_attribute(type=with_attribute.ANY_VALUE)
163
+ )
164
+ div_expr = div_any_type + SkipTo(div | div_end)("body")
165
+ for div_header in div_expr.search_string(html):
166
+ print(div_header.body)
167
+
168
+ prints:
169
+
170
+ .. testoutput::
171
+
172
+ 1 4 0 1 0
173
+ 1,3 2,3 1,1
174
+ """
175
+ attrs_list: list[tuple[str, str]] = []
176
+ if args:
177
+ attrs_list.extend(args)
178
+ else:
179
+ attrs_list.extend(attr_dict.items())
180
+
181
+ def pa(s: str, l: int, tokens: ParseResults) -> None:
182
+ for attrName, attrValue in attrs_list:
183
+ if attrName not in tokens:
184
+ raise ParseException(s, l, f"no matching attribute {attrName!r}")
185
+ if attrValue != with_attribute.ANY_VALUE and tokens[attrName] != attrValue: # type: ignore [attr-defined]
186
+ raise ParseException(
187
+ s,
188
+ l,
189
+ f"attribute {attrName!r} has value {tokens[attrName]!r}, must be {attrValue!r}",
190
+ )
191
+
192
+ return pa
193
+
194
+
195
+ with_attribute.ANY_VALUE = object() # type: ignore [attr-defined]
196
+ "Value to use with :class:`with_attribute` parse action, to match any value, as long as the attribute is present"
197
+
198
+
199
+ def with_class(classname: str, namespace: str = "") -> ParseAction:
200
+ """
201
+ Simplified version of :meth:`with_attribute` when
202
+ matching on a div class - made difficult because ``class`` is
203
+ a reserved word in Python.
204
+
205
+ Using similar input data to the :meth:`with_attribute` examples:
206
+
207
+ .. testcode::
208
+
209
+ html = '''
210
+ <div>
211
+ Some text
212
+ <div class="grid">1 4 0 1 0</div>
213
+ <div class="graph">1,3 2,3 1,1</div>
214
+ <div>this &lt;div&gt; has no class</div>
215
+ </div>
216
+ '''
217
+ div,div_end = make_html_tags("div")
218
+
219
+ Only match div tag having the "grid" class:
220
+
221
+ .. testcode::
222
+
223
+ div_grid = div().set_parse_action(with_class("grid"))
224
+ grid_expr = div_grid + SkipTo(div | div_end)("body")
225
+ for grid_header in grid_expr.search_string(html):
226
+ print(grid_header.body)
227
+
228
+ prints:
229
+
230
+ .. testoutput::
231
+
232
+ 1 4 0 1 0
233
+
234
+ Construct a match with any div tag having a class attribute,
235
+ regardless of the value:
236
+
237
+ .. testcode::
238
+
239
+ div_any_type = div().set_parse_action(
240
+ with_class(withAttribute.ANY_VALUE)
241
+ )
242
+ div_expr = div_any_type + SkipTo(div | div_end)("body")
243
+ for div_header in div_expr.search_string(html):
244
+ print(div_header.body)
245
+
246
+ prints:
247
+
248
+ .. testoutput::
249
+
250
+ 1 4 0 1 0
251
+ 1,3 2,3 1,1
252
+ """
253
+ classattr = f"{namespace}:class" if namespace else "class"
254
+ return with_attribute(**{classattr: classname})
255
+
256
+
257
+ # Compatibility synonyms
258
+ # fmt: off
259
+ replaceWith = replaced_by_pep8("replaceWith", replace_with)
260
+ removeQuotes = replaced_by_pep8("removeQuotes", remove_quotes)
261
+ withAttribute = replaced_by_pep8("withAttribute", with_attribute)
262
+ withClass = replaced_by_pep8("withClass", with_class)
263
+ matchOnlyAtCol = replaced_by_pep8("matchOnlyAtCol", match_only_at_col)
264
+ # fmt: on
@@ -0,0 +1,75 @@
1
+ <!--
2
+ This file contains instructions for best practices for developing parsers with pyparsing, and can be used by AI agents
3
+ when generating Python code using pyparsing.
4
+ -->
5
+
6
+ ## Planning
7
+ - If not provided or if target language definition is ambiguous, ask for examples of valid strings to be parsed
8
+ - Before developing the pyparsing expressions, define a Backus-Naur Form definition and save this in docs/grammar.md. Update this document as changes are made in the parser.
9
+
10
+ ## Implementing
11
+ - Import pyparsing using `import pyparsing as pp`, and use that for all pyparsing references.
12
+ - If referencing names from `pyparsing.common`, follow the pyparsing import with "ppc = pp.common" and use `ppc` as the namespace to access `pyparsing.common`.
13
+ - If referencing names from `pyparsing.unicode`, follow the pyparsing import with "ppu = pp.unicode" and use `ppu` as the namespace to access `pyparsing.unicode`.
14
+ - When writing parsers that contain recursive elements (using `Forward()` or `infix_notation()`), immediately enable packrat parsing for performance: `pp.ParserElement.enable_packrat()` (call this right after importing pyparsing). See https://pyparsing-docs.readthedocs.io/en/latest/HowToUsePyparsing.html.
15
+ - For recursive grammars, define placeholders with `pp.Forward()` and assign later using the `<<=` operator; give Forwards meaningful names with `set_name()` to improve errors.
16
+ - Use PEP8 method and argument names in the pyparsing API (`parse_string`, not `parseString`).
17
+ - Do not include expressions for matching whitespace in the grammar. Pyparsing skips whitespace by default.
18
+ - For line-oriented grammars where newlines are significant, set skippable whitespace to just spaces/tabs early: `pp.ParserElement.set_default_whitespace_chars(" \t")`, and define `NL = pp.LineEnd().suppress()` to handle line ends explicitly.
19
+ - Prefer operator forms for readability: use +, |, ^, ~, etc., instead of explicit And/MatchFirst/Or/Not classes (see Usage notes in https://pyparsing-docs.readthedocs.io/en/latest/HowToUsePyparsing.html).
20
+ - Use `set_name()` on all major grammar elements to support railroad diagramming and better error/debug output.
21
+ - The grammar should be independently testable, without pulling in separate modules for data structures, evaluation, or command execution.
22
+ - Use results names for robust access to parsed data fields; results names should be valid Python identifiers to support attribute-style access on returned ParseResults.
23
+ - Results names should take the place of numeric indexing into parsed results in most places.
24
+ - Define results names using call format not `set_results_name()`, example: `full_name = Word(alphas)("first_name") + Word(alphas)("last_name")`
25
+ - If adding results name to an expression that is contains one more sub-expressions with results names, the expression must be inclused in a Group.
26
+ - Prefer `Keyword` over `Literal` for reserved words to avoid partial matches (e.g., `Keyword("for")` will not match the leading "for" in "format").
27
+ - Use `pp.CaselessKeyword`/`pp.CaselessLiteral` when keywords should match regardless of case.
28
+ - When the full input must be consumed, call `parse_string` with `parse_all=True`.
29
+ - If the grammar must handle comments, define an expression for them and use the `ignore()` method to skip them.
30
+ - Prefer built-ins like `pp.cpp_style_comment` and `pp.python_style_comment` for common comment syntaxes.
31
+ - Use pyparsing `Group` to organize sub-expressions. Groups are also important for preserving results names when a sub-expression is used in a `OneOrMore` or `ZeroOrMore` expression.
32
+ - Suppress punctuation tokens to keep results clean; a convenient pattern is `LBRACK, RBRACK, LBRACE, RBRACE, COLON = pp.Suppress.using_each("[]{}:")`.
33
+ - For comma-separated sequences, prefer `pp.DelimitedList(...)`; wrap with `pp.Optional(...)` to allow empty lists or objects where appropriate.
34
+ - For helper sub-expressions used only to build larger expressions, consider `set_name(None)` to keep result dumps uncluttered.
35
+ - Use pyparsing `Each()` to define a list of elements that may occur in any order.
36
+ - The '&' operator is the operator form of Each and is often more readable when combining order-independent parts.
37
+ - Use parse actions to do parse-time conversion of data from strings to useful data types.
38
+ - Use objects defined in pyparsing.common for common types like integer, real — these already have their conversion parse actions defined.
39
+ - For quoted strings, use `pp.dbl_quoted_string().set_parse_action(pp.remove_quotes)` to unquote automatically.
40
+ - Map reserved words to Python constants per this example for parsing "true" to auto-convert to a Python True: `pp.Keyword("true").set_parse_action(pp.replace_with(True))` (and similarly for false/null/etc.).
41
+ - When you want native Python containers from the parse, use `pp.Group(..., aslist=True)` for lists and `pp.Dict(..., asdict=True)` for dict-like data.
42
+ - Use "using_each" with a list of keywords to define keyword constants, instead of separate assignments.
43
+ - Choose the appropriate matching method:
44
+ - `parse_string()` parses from the start
45
+ - `search_string()` searches anywhere in the text
46
+ - `scan_string()` yields all matches with positions
47
+ - `transform_string()` is a convenience wrapper around `scan_string` to apply filters or transforms defined in parse actions, to perform batch transforms or conversions of expressions within a larger body of text
48
+ - For line suffixes or directives, combine lookahead and slicing helpers: `pp.FollowedBy(...)` with `pp.rest_of_line`; when reusing a base expression with a different parse action, call `.copy()` before applying the new action to avoid side effects.
49
+ - When defining a parser to be used in a REPL:
50
+ - add pyparsing `Tag()` elements of the form `Tag("command", <command-name>)` to each command definition to support model construction from parsed commands.
51
+ - define model classes using dataclasses, and use the "command" attribute in the parsed results to identify which model class to create. The model classes can then be used to construct the model from the ParseResults returned by parse_string(). Define the models in a separate parser_models.py file.
52
+ - If defining the grammar as part of a Parser class, only the finished grammar needs to be implemented as an instance variable.
53
+ - `ParseResults` support "in" testing for results names. Use "in" tests for the existence of results names, not `hasattr()`.
54
+ - Avoid left recursion where possible. If you must support left-recursive grammars, enable it with `pp.ParserElement.enable_left_recursion()` and do not enable packrat at the same time (these modes are incompatible).
55
+ - Use `pp.SkipTo` as a skipping expression to skip over arbitrary content.
56
+ - For example, `pp.SkipTo(pp.LineEnd())` will skip over all content until the end of the line; add a stop_on argument to SkipTo to stop skipping when a particular string is matched.
57
+ - Use `...` in place of simple SkipTo(expression)
58
+
59
+ ## Testing
60
+ - Use the pyparsing `ParserElement.run_tests` method to run mini validation tests.
61
+ - Pass a single multiline string to `run_tests` to test the parser on multiple test input strings, each line is a separate test.
62
+ - You can add comments starting with "#" within the string passed to `run_tests` to document the individual test cases.
63
+ - To pass test input strings that span multiple lines, pass the test input strings as a list of strings.
64
+ - Pass `parse_all=True` to `run_tests` to test that the entire input is consumed.
65
+ - When generating unit tests for the parser:
66
+ - generate tests that include presence and absence of optional elements
67
+ - use the methods in the mixin class pyparsing.testing.TestParseResultsAsserts to easily define expression, test input string, and expected results
68
+ - do not generate tests for invalid data
69
+
70
+ ## Debugging
71
+ - If troubleshooting parse actions, use pyparsing's `trace_parse_action` decorator to echo arguments and return value
72
+ - During development, call `pp.autoname_elements()` to auto-assign names to unnamed expressions to improve `dump()` and error messages.
73
+ - Sub-expressions can be tested in isolation using `ParserElement.matches()`
74
+ - When defined out of order, Literals can mistakenly match fragments: `Literal("for")` will match the leading "for" in "format". Can be corrected by using `Keyword` instead of `Literal`.
75
+ - Dump the parsed results using `ParseResults.dump()`, `ParseResults.pprint()`, or `repr(ParseResults)`.
@@ -0,0 +1,2 @@
1
+ import pyparsing
2
+ pyparsing.show_best_practices()