@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,1769 @@
1
+ # helpers.py
2
+ from enum import Enum, auto
3
+ import html.entities
4
+ import operator
5
+ import re
6
+ import sys
7
+ import typing
8
+
9
+ from . import __diag__
10
+ from .core import *
11
+ from .util import (
12
+ _bslash,
13
+ _flatten,
14
+ _escape_regex_range_chars,
15
+ make_compressed_re,
16
+ replaced_by_pep8,
17
+ )
18
+
19
+
20
+ def _suppression(expr: Union[ParserElement, str]) -> ParserElement:
21
+ # internal helper to avoid wrapping Suppress inside another Suppress
22
+ if isinstance(expr, Suppress):
23
+ return expr
24
+ return Suppress(expr)
25
+
26
+
27
+ #
28
+ # global helpers
29
+ #
30
+ def counted_array(
31
+ expr: ParserElement, int_expr: typing.Optional[ParserElement] = None, **kwargs
32
+ ) -> ParserElement:
33
+ """Helper to define a counted list of expressions.
34
+
35
+ This helper defines a pattern of the form::
36
+
37
+ integer expr expr expr...
38
+
39
+ where the leading integer tells how many expr expressions follow.
40
+ The matched tokens returns the array of expr tokens as a list - the
41
+ leading count token is suppressed.
42
+
43
+ If ``int_expr`` is specified, it should be a pyparsing expression
44
+ that produces an integer value.
45
+
46
+ Examples:
47
+
48
+ .. doctest::
49
+
50
+ >>> counted_array(Word(alphas)).parse_string('2 ab cd ef')
51
+ ParseResults(['ab', 'cd'], {})
52
+
53
+ - In this parser, the leading integer value is given in binary,
54
+ '10' indicating that 2 values are in the array:
55
+
56
+ .. doctest::
57
+
58
+ >>> binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2))
59
+ >>> counted_array(Word(alphas), int_expr=binary_constant
60
+ ... ).parse_string('10 ab cd ef')
61
+ ParseResults(['ab', 'cd'], {})
62
+
63
+ - If other fields must be parsed after the count but before the
64
+ list items, give the fields results names and they will
65
+ be preserved in the returned ParseResults:
66
+
67
+ .. doctest::
68
+
69
+ >>> ppc = pyparsing.common
70
+ >>> count_with_metadata = ppc.integer + Word(alphas)("type")
71
+ >>> typed_array = counted_array(Word(alphanums),
72
+ ... int_expr=count_with_metadata)("items")
73
+ >>> result = typed_array.parse_string("3 bool True True False")
74
+ >>> print(result.dump())
75
+ ['True', 'True', 'False']
76
+ - items: ['True', 'True', 'False']
77
+ - type: 'bool'
78
+ """
79
+ intExpr: typing.Optional[ParserElement] = deprecate_argument(
80
+ kwargs, "intExpr", None
81
+ )
82
+
83
+ intExpr = intExpr or int_expr
84
+ array_expr = Forward()
85
+
86
+ def count_field_parse_action(s, l, t):
87
+ nonlocal array_expr
88
+ n = t[0]
89
+ array_expr <<= (expr * n) if n else Empty()
90
+ # clear list contents, but keep any named results
91
+ del t[:]
92
+
93
+ if intExpr is None:
94
+ intExpr = Word(nums).set_parse_action(lambda t: int(t[0]))
95
+ else:
96
+ intExpr = intExpr.copy()
97
+ intExpr.set_name("arrayLen")
98
+ intExpr.add_parse_action(count_field_parse_action, call_during_try=True)
99
+ return (intExpr + array_expr).set_name(f"(len) {expr}...")
100
+
101
+
102
+ def match_previous_literal(expr: ParserElement) -> ParserElement:
103
+ """Helper to define an expression that is indirectly defined from
104
+ the tokens matched in a previous expression, that is, it looks for
105
+ a 'repeat' of a previous expression. For example::
106
+
107
+ .. testcode::
108
+
109
+ first = Word(nums)
110
+ second = match_previous_literal(first)
111
+ match_expr = first + ":" + second
112
+
113
+ will match ``"1:1"``, but not ``"1:2"``. Because this
114
+ matches a previous literal, will also match the leading
115
+ ``"1:1"`` in ``"1:10"``. If this is not desired, use
116
+ :class:`match_previous_expr`. Do *not* use with packrat parsing
117
+ enabled.
118
+ """
119
+ rep = Forward()
120
+
121
+ def copy_token_to_repeater(s, l, t):
122
+ if not t:
123
+ rep << Empty()
124
+ return
125
+
126
+ if len(t) == 1:
127
+ rep << t[0]
128
+ return
129
+
130
+ # flatten t tokens
131
+ tflat = _flatten(t.as_list())
132
+ rep << And(Literal(tt) for tt in tflat)
133
+
134
+ expr.add_parse_action(copy_token_to_repeater, call_during_try=True)
135
+ rep.set_name(f"(prev) {expr}")
136
+ return rep
137
+
138
+
139
+ def match_previous_expr(expr: ParserElement) -> ParserElement:
140
+ """Helper to define an expression that is indirectly defined from
141
+ the tokens matched in a previous expression, that is, it looks for
142
+ a 'repeat' of a previous expression. For example:
143
+
144
+ .. testcode::
145
+
146
+ first = Word(nums)
147
+ second = match_previous_expr(first)
148
+ match_expr = first + ":" + second
149
+
150
+ will match ``"1:1"``, but not ``"1:2"``. Because this
151
+ matches by expressions, will *not* match the leading ``"1:1"``
152
+ in ``"1:10"``; the expressions are evaluated first, and then
153
+ compared, so ``"1"`` is compared with ``"10"``. Do *not* use
154
+ with packrat parsing enabled.
155
+ """
156
+ rep = Forward()
157
+ e2 = expr.copy()
158
+ rep <<= e2
159
+
160
+ def copy_token_to_repeater(s, l, t):
161
+ matchTokens = _flatten(t.as_list())
162
+
163
+ def must_match_these_tokens(s, l, t):
164
+ theseTokens = _flatten(t.as_list())
165
+ if theseTokens != matchTokens:
166
+ raise ParseException(
167
+ s, l, f"Expected {matchTokens}, found{theseTokens}"
168
+ )
169
+
170
+ rep.set_parse_action(must_match_these_tokens, call_during_try=True)
171
+
172
+ expr.add_parse_action(copy_token_to_repeater, call_during_try=True)
173
+ rep.set_name(f"(prev) {expr}")
174
+ return rep
175
+
176
+
177
+ def one_of(
178
+ strs: Union[typing.Iterable[str], str],
179
+ caseless: bool = False,
180
+ use_regex: bool = True,
181
+ as_keyword: bool = False,
182
+ **kwargs,
183
+ ) -> ParserElement:
184
+ """Helper to quickly define a set of alternative :class:`Literal` s,
185
+ and makes sure to do longest-first testing when there is a conflict,
186
+ regardless of the input order, but returns
187
+ a :class:`MatchFirst` for best performance.
188
+
189
+ :param strs: a string of space-delimited literals, or a collection of
190
+ string literals
191
+ :param caseless: treat all literals as caseless
192
+ :param use_regex: bool - as an optimization, will
193
+ generate a :class:`Regex` object; otherwise, will generate
194
+ a :class:`MatchFirst` object (if ``caseless=True`` or
195
+ ``as_keyword=True``, or if creating a :class:`Regex` raises an exception)
196
+ :param as_keyword: bool - enforce :class:`Keyword`-style matching on the
197
+ generated expressions
198
+
199
+ Parameters ``asKeyword`` and ``useRegex`` are retained for pre-PEP8
200
+ compatibility, but will be removed in a future release.
201
+
202
+ Example:
203
+
204
+ .. testcode::
205
+
206
+ comp_oper = one_of("< = > <= >= !=")
207
+ var = Word(alphas)
208
+ number = Word(nums)
209
+ term = var | number
210
+ comparison_expr = term + comp_oper + term
211
+ print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12"))
212
+
213
+ prints:
214
+
215
+ .. testoutput::
216
+
217
+ [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
218
+ """
219
+ useRegex: bool = deprecate_argument(kwargs, "useRegex", True)
220
+ asKeyword: bool = deprecate_argument(kwargs, "asKeyword", False)
221
+
222
+ asKeyword = asKeyword or as_keyword
223
+ useRegex = useRegex and use_regex
224
+
225
+ if (
226
+ isinstance(caseless, str_type)
227
+ and __diag__.warn_on_multiple_string_args_to_oneof
228
+ ):
229
+ warnings.warn(
230
+ "warn_on_multiple_string_args_to_oneof:"
231
+ " More than one string argument passed to one_of, pass"
232
+ " choices as a list or space-delimited string",
233
+ PyparsingDiagnosticWarning,
234
+ stacklevel=2,
235
+ )
236
+
237
+ if caseless:
238
+ is_equal = lambda a, b: a.upper() == b.upper()
239
+ masks = lambda a, b: b.upper().startswith(a.upper())
240
+ else:
241
+ is_equal = operator.eq
242
+ masks = lambda a, b: b.startswith(a)
243
+
244
+ symbols: list[str]
245
+ if isinstance(strs, str_type):
246
+ strs = typing.cast(str, strs)
247
+ symbols = strs.split()
248
+ elif isinstance(strs, Iterable):
249
+ symbols = list(strs)
250
+ else:
251
+ raise TypeError("Invalid argument to one_of, expected string or iterable")
252
+ if not symbols:
253
+ return NoMatch()
254
+
255
+ # reorder given symbols to take care to avoid masking longer choices with shorter ones
256
+ # (but only if the given symbols are not just single characters)
257
+ i = 0
258
+ while i < len(symbols) - 1:
259
+ cur = symbols[i]
260
+ for j, other in enumerate(symbols[i + 1 :]):
261
+ if is_equal(other, cur):
262
+ del symbols[i + j + 1]
263
+ break
264
+ if len(other) > len(cur) and masks(cur, other):
265
+ del symbols[i + j + 1]
266
+ symbols.insert(i, other)
267
+ break
268
+ else:
269
+ i += 1
270
+
271
+ if useRegex:
272
+ re_flags: int = re.IGNORECASE if caseless else 0
273
+
274
+ try:
275
+ if all(len(sym) == 1 for sym in symbols):
276
+ # symbols are just single characters, create range regex pattern
277
+ patt = f"[{''.join(_escape_regex_range_chars(sym) for sym in symbols)}]"
278
+ else:
279
+ patt = "|".join(re.escape(sym) for sym in symbols)
280
+
281
+ # wrap with \b word break markers if defining as keywords
282
+ if asKeyword:
283
+ patt = rf"\b(?:{patt})\b"
284
+
285
+ ret = Regex(patt, flags=re_flags)
286
+ ret.set_name(" | ".join(repr(s) for s in symbols))
287
+
288
+ if caseless:
289
+ # add parse action to return symbols as specified, not in random
290
+ # casing as found in input string
291
+ symbol_map = {sym.lower(): sym for sym in symbols}
292
+ ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()])
293
+
294
+ return ret
295
+
296
+ except re.error:
297
+ warnings.warn(
298
+ "Exception creating Regex for one_of, building MatchFirst",
299
+ PyparsingDiagnosticWarning,
300
+ stacklevel=2,
301
+ )
302
+
303
+ # last resort, just use MatchFirst of Token class corresponding to caseless
304
+ # and asKeyword settings
305
+ CASELESS = KEYWORD = True
306
+ parse_element_class = {
307
+ (CASELESS, KEYWORD): CaselessKeyword,
308
+ (CASELESS, not KEYWORD): CaselessLiteral,
309
+ (not CASELESS, KEYWORD): Keyword,
310
+ (not CASELESS, not KEYWORD): Literal,
311
+ }[(caseless, asKeyword)]
312
+ return MatchFirst(parse_element_class(sym) for sym in symbols).set_name(
313
+ " | ".join(symbols)
314
+ )
315
+
316
+
317
+ def dict_of(key: ParserElement, value: ParserElement) -> Dict:
318
+ """Helper to easily and clearly define a dictionary by specifying
319
+ the respective patterns for the key and value. Takes care of
320
+ defining the :class:`Dict`, :class:`ZeroOrMore`, and
321
+ :class:`Group` tokens in the proper order. The key pattern
322
+ can include delimiting markers or punctuation, as long as they are
323
+ suppressed, thereby leaving the significant key text. The value
324
+ pattern can include named results, so that the :class:`Dict` results
325
+ can include named token fields.
326
+
327
+ Example:
328
+
329
+ .. doctest::
330
+
331
+ >>> text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
332
+
333
+ >>> data_word = Word(alphas)
334
+ >>> label = data_word + FollowedBy(':')
335
+ >>> attr_expr = (
336
+ ... label
337
+ ... + Suppress(':')
338
+ ... + OneOrMore(data_word, stop_on=label)
339
+ ... .set_parse_action(' '.join))
340
+ >>> print(attr_expr[1, ...].parse_string(text).dump())
341
+ ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
342
+
343
+ >>> attr_label = label
344
+ >>> attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label
345
+ ... ).set_parse_action(' '.join)
346
+
347
+ # similar to Dict, but simpler call format
348
+ >>> result = dict_of(attr_label, attr_value).parse_string(text)
349
+ >>> print(result.dump())
350
+ [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
351
+ - color: 'light blue'
352
+ - posn: 'upper left'
353
+ - shape: 'SQUARE'
354
+ - texture: 'burlap'
355
+ [0]:
356
+ ['shape', 'SQUARE']
357
+ [1]:
358
+ ['posn', 'upper left']
359
+ [2]:
360
+ ['color', 'light blue']
361
+ [3]:
362
+ ['texture', 'burlap']
363
+
364
+ >>> print(result['shape'])
365
+ SQUARE
366
+ >>> print(result.shape) # object attribute access works too
367
+ SQUARE
368
+ >>> print(result.as_dict())
369
+ {'shape': 'SQUARE', 'posn': 'upper left', 'color': 'light blue', 'texture': 'burlap'}
370
+ """
371
+ return Dict(OneOrMore(Group(key + value)))
372
+
373
+
374
+ def original_text_for(
375
+ expr: ParserElement, as_string: bool = True, **kwargs
376
+ ) -> ParserElement:
377
+ """Helper to return the original, untokenized text for a given
378
+ expression. Useful to restore the parsed fields of an HTML start
379
+ tag into the raw tag text itself, or to revert separate tokens with
380
+ intervening whitespace back to the original matching input text. By
381
+ default, returns a string containing the original parsed text.
382
+
383
+ If the optional ``as_string`` argument is passed as
384
+ ``False``, then the return value is
385
+ a :class:`ParseResults` containing any results names that
386
+ were originally matched, and a single token containing the original
387
+ matched text from the input string. So if the expression passed to
388
+ :class:`original_text_for` contains expressions with defined
389
+ results names, you must set ``as_string`` to ``False`` if you
390
+ want to preserve those results name values.
391
+
392
+ The ``asString`` pre-PEP8 argument is retained for compatibility,
393
+ but will be removed in a future release.
394
+
395
+ Example:
396
+
397
+ .. testcode::
398
+
399
+ src = "this is test <b> bold <i>text</i> </b> normal text "
400
+ for tag in ("b", "i"):
401
+ opener, closer = make_html_tags(tag)
402
+ patt = original_text_for(opener + ... + closer)
403
+ print(patt.search_string(src)[0])
404
+
405
+ prints:
406
+
407
+ .. testoutput::
408
+
409
+ ['<b> bold <i>text</i> </b>']
410
+ ['<i>text</i>']
411
+ """
412
+ asString: bool = deprecate_argument(kwargs, "asString", True)
413
+
414
+ asString = asString and as_string
415
+
416
+ locMarker = Empty().set_parse_action(lambda s, loc, t: loc)
417
+ endlocMarker = locMarker.copy()
418
+ endlocMarker.callPreparse = False
419
+ matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
420
+ if asString:
421
+ extractText = lambda s, l, t: s[t._original_start : t._original_end]
422
+ else:
423
+
424
+ def extractText(s, l, t):
425
+ t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]]
426
+
427
+ matchExpr.set_parse_action(extractText)
428
+ matchExpr.ignoreExprs = expr.ignoreExprs
429
+ matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection)
430
+ return matchExpr
431
+
432
+
433
+ def ungroup(expr: ParserElement) -> ParserElement:
434
+ """Helper to undo pyparsing's default grouping of And expressions,
435
+ even if all but one are non-empty.
436
+ """
437
+ return TokenConverter(expr).add_parse_action(lambda t: t[0])
438
+
439
+
440
+ def locatedExpr(expr: ParserElement) -> ParserElement:
441
+ """
442
+ .. deprecated:: 3.0.0
443
+ Use the :class:`Located` class instead. Note that `Located`
444
+ returns results with one less grouping level.
445
+
446
+ Helper to decorate a returned token with its starting and ending
447
+ locations in the input string.
448
+
449
+ This helper adds the following results names:
450
+
451
+ - ``locn_start`` - location where matched expression begins
452
+ - ``locn_end`` - location where matched expression ends
453
+ - ``value`` - the actual parsed results
454
+
455
+ Be careful if the input text contains ``<TAB>`` characters, you
456
+ may want to call :meth:`ParserElement.parse_with_tabs`
457
+ """
458
+ warnings.warn(
459
+ f"{'locatedExpr'!r} deprecated - use {'Located'!r}",
460
+ PyparsingDeprecationWarning,
461
+ stacklevel=2,
462
+ )
463
+
464
+ locator = Empty().set_parse_action(lambda ss, ll, tt: ll)
465
+ return Group(
466
+ locator("locn_start")
467
+ + expr("value")
468
+ + locator.copy().leave_whitespace()("locn_end")
469
+ )
470
+
471
+
472
+ # define special default value to permit None as a significant value for
473
+ # ignore_expr
474
+ _NO_IGNORE_EXPR_GIVEN = NoMatch()
475
+
476
+
477
+ class _NestedExpr(ParseElementEnhance):
478
+ """Helper method for defining nested lists enclosed in opening and
479
+ closing delimiters (``"("`` and ``")"`` are the default).
480
+
481
+ :param opener: str - opening character for a nested list
482
+ (default= ``"("``); can also be a pyparsing expression
483
+
484
+ :param closer: str - closing character for a nested list
485
+ (default= ``")"``); can also be a pyparsing expression
486
+
487
+ :param content: expression for items within the nested lists
488
+
489
+ :param ignore_expr: expression for ignoring opening and closing delimiters
490
+ (default = :class:`quoted_string`)
491
+
492
+ Parameter ``ignoreExpr`` is retained for compatibility
493
+ but will be removed in a future release.
494
+
495
+ If an expression is not provided for the content argument, the
496
+ nested expression will capture all whitespace-delimited content
497
+ between delimiters as a list of separate values.
498
+
499
+ Use the ``ignore_expr`` argument to define expressions that may
500
+ contain opening or closing characters that should not be treated as
501
+ opening or closing characters for nesting, such as quoted_string or
502
+ a comment expression. Specify multiple expressions using an
503
+ :class:`Or` or :class:`MatchFirst`. The default is
504
+ :class:`quoted_string`, but if no expressions are to be ignored, then
505
+ pass ``None`` for this argument.
506
+
507
+ Example:
508
+
509
+ .. testcode::
510
+
511
+ data_type = one_of("void int short long char float double")
512
+ decl_data_type = Combine(data_type + Opt(Word('*')))
513
+ ident = Word(alphas+'_', alphanums+'_')
514
+ number = pyparsing_common.number
515
+ arg = Group(decl_data_type + ident)
516
+ LPAR, RPAR = map(Suppress, "()")
517
+
518
+ code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment))
519
+
520
+ c_function = (decl_data_type("type")
521
+ + ident("name")
522
+ + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
523
+ + code_body("body"))
524
+ c_function.ignore(c_style_comment)
525
+
526
+ source_code = '''
527
+ int is_odd(int x) {
528
+ return (x%2);
529
+ }
530
+
531
+ int dec_to_hex(char hchar) {
532
+ if (hchar >= '0' && hchar <= '9') {
533
+ return (ord(hchar)-ord('0'));
534
+ } else {
535
+ return (10+ord(hchar)-ord('A'));
536
+ }
537
+ }
538
+ '''
539
+ for func in c_function.search_string(source_code):
540
+ print(f"{func.name} ({func.type}) args: {func.args}")
541
+
542
+
543
+ prints:
544
+
545
+ .. testoutput::
546
+
547
+ is_odd (int) args: [['int', 'x']]
548
+ dec_to_hex (int) args: [['char', 'hchar']]
549
+ """
550
+
551
+ def __init__(
552
+ self,
553
+ opener: Union[str, ParserElement] = "(",
554
+ closer: Union[str, ParserElement] = ")",
555
+ content: typing.Optional[ParserElement] = None,
556
+ ignore_expr: typing.Optional[ParserElement] = _NO_IGNORE_EXPR_GIVEN,
557
+ **kwargs,
558
+ ):
559
+ ignoreExpr = deprecate_argument(kwargs, "ignoreExpr", _NO_IGNORE_EXPR_GIVEN)
560
+ if ignoreExpr != ignore_expr:
561
+ ignoreExpr = (
562
+ ignore_expr if ignoreExpr is _NO_IGNORE_EXPR_GIVEN else ignoreExpr
563
+ )
564
+ if ignoreExpr is _NO_IGNORE_EXPR_GIVEN:
565
+ ignoreExpr = quoted_string()
566
+
567
+ if opener == closer:
568
+ raise ValueError("opening and closing strings cannot be the same")
569
+
570
+ original_content = content
571
+ if content is None:
572
+ if isinstance(opener, str_type) and isinstance(closer, str_type):
573
+ opener_str = str(opener)
574
+ closer_str = str(closer)
575
+ if len(opener_str) == 1 and len(closer_str) == 1:
576
+ if ignoreExpr is not None:
577
+ content = Combine(
578
+ OneOrMore(
579
+ ~ignoreExpr
580
+ + CharsNotIn(
581
+ opener_str
582
+ + closer_str
583
+ + ParserElement.DEFAULT_WHITE_CHARS,
584
+ exact=1,
585
+ )
586
+ )
587
+ )
588
+ else:
589
+ content = Combine(
590
+ Empty()
591
+ + CharsNotIn(
592
+ opener_str
593
+ + closer_str
594
+ + ParserElement.DEFAULT_WHITE_CHARS
595
+ )
596
+ )
597
+ else:
598
+ if ignoreExpr is not None:
599
+ content = Combine(
600
+ OneOrMore(
601
+ ~ignoreExpr
602
+ + ~Literal(opener_str)
603
+ + ~Literal(closer_str)
604
+ + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1)
605
+ )
606
+ )
607
+ else:
608
+ content = Combine(
609
+ OneOrMore(
610
+ ~Literal(opener_str)
611
+ + ~Literal(closer_str)
612
+ + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1)
613
+ )
614
+ )
615
+ else:
616
+ raise ValueError(
617
+ "opening and closing arguments must be strings if no content expression is given"
618
+ )
619
+
620
+ if ParserElement.DEFAULT_WHITE_CHARS:
621
+ content.set_parse_action(
622
+ lambda t: t[0].strip(ParserElement.DEFAULT_WHITE_CHARS)
623
+ )
624
+
625
+ super().__init__(content, savelist=True)
626
+ self.opener = _suppression(opener)
627
+ self.closer = _suppression(closer)
628
+ self.opener_raw = opener
629
+ self.closer_raw = closer
630
+ self.content = content
631
+ self.ignore_expr = ignoreExpr
632
+ self.saveAsList = True
633
+ self.errmsg = None
634
+ self.original_content = original_content
635
+
636
+ def _generateDefaultName(self) -> str:
637
+ if self.original_content is None:
638
+ return f"nested {self.opener_raw}{self.closer_raw} expression"
639
+ else:
640
+ return f"nested {self.opener_raw}{self.original_content}{self.closer_raw} expression"
641
+
642
+ def parseImpl(self, instring, loc, do_actions=True):
643
+ loc, _ = self.opener._parse(instring, loc, do_actions=do_actions)
644
+ stack = [[]]
645
+ while stack:
646
+ # 1. try ignore_expr
647
+ if self.ignore_expr is not None:
648
+ try:
649
+ loc, toks = self.ignore_expr._parse(
650
+ instring, loc, do_actions=do_actions
651
+ )
652
+ stack[-1].extend(toks)
653
+ continue
654
+ except ParseException:
655
+ pass
656
+ # 2. try opener
657
+ try:
658
+ loc, _ = self.opener._parse(instring, loc, do_actions=do_actions)
659
+ stack.append([])
660
+ continue
661
+ except ParseException:
662
+ pass
663
+ # 3. try content
664
+ try:
665
+ next_loc, toks = self.content._parse(
666
+ instring, loc, do_actions=do_actions
667
+ )
668
+ if next_loc > loc:
669
+ loc = next_loc
670
+ stack[-1].extend(toks)
671
+ continue
672
+ except ParseException:
673
+ pass
674
+ # 4. try closer
675
+ try:
676
+ loc, _ = self.closer._parse(instring, loc, do_actions=do_actions)
677
+ top = ParseResults(stack.pop())
678
+ if stack:
679
+ stack[-1].append(top)
680
+ else:
681
+ return loc, ParseResults([top])
682
+ continue
683
+ except ParseException:
684
+ pass
685
+
686
+ raise ParseException(instring, loc, f"Expected {self.closer_raw!r}")
687
+
688
+
689
+ def nested_expr(
690
+ opener: Union[str, ParserElement] = "(",
691
+ closer: Union[str, ParserElement] = ")",
692
+ content: typing.Optional[ParserElement] = None,
693
+ ignore_expr: typing.Optional[ParserElement] = _NO_IGNORE_EXPR_GIVEN,
694
+ **kwargs,
695
+ ) -> ParserElement:
696
+ """Helper method for defining nested lists enclosed in opening and
697
+ closing delimiters (``"("`` and ``")"`` are the default).
698
+
699
+ :param opener: str - opening character for a nested list
700
+ (default= ``"("``); can also be a pyparsing expression
701
+
702
+ :param closer: str - closing character for a nested list
703
+ (default= ``")"``); can also be a pyparsing expression
704
+
705
+ :param content: expression for items within the nested lists
706
+
707
+ :param ignore_expr: expression for ignoring opening and closing delimiters
708
+ (default = :class:`quoted_string`)
709
+
710
+ Parameter ``ignoreExpr`` is retained for compatibility
711
+ but will be removed in a future release.
712
+
713
+ If an expression is not provided for the content argument, the
714
+ nested expression will capture all whitespace-delimited content
715
+ between delimiters as a list of separate values.
716
+
717
+ Use the ``ignore_expr`` argument to define expressions that may
718
+ contain opening or closing characters that should not be treated as
719
+ opening or closing characters for nesting, such as quoted_string or
720
+ a comment expression. Specify multiple expressions using an
721
+ :class:`Or` or :class:`MatchFirst`. The default is
722
+ :class:`quoted_string`, but if no expressions are to be ignored, then
723
+ pass ``None`` for this argument.
724
+
725
+ Example:
726
+
727
+ .. testcode::
728
+
729
+ data_type = one_of("void int short long char float double")
730
+ decl_data_type = Combine(data_type + Opt(Word('*')))
731
+ ident = Word(alphas+'_', alphanums+'_')
732
+ number = pyparsing_common.number
733
+ arg = Group(decl_data_type + ident)
734
+ LPAR, RPAR = map(Suppress, "()")
735
+
736
+ code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment))
737
+
738
+ c_function = (decl_data_type("type")
739
+ + ident("name")
740
+ + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
741
+ + code_body("body"))
742
+ c_function.ignore(c_style_comment)
743
+
744
+ source_code = '''
745
+ int is_odd(int x) {
746
+ return (x%2);
747
+ }
748
+
749
+ int dec_to_hex(char hchar) {
750
+ if (hchar >= '0' && hchar <= '9') {
751
+ return (ord(hchar)-ord('0'));
752
+ } else {
753
+ return (10+ord(hchar)-ord('A'));
754
+ }
755
+ }
756
+ '''
757
+ for func in c_function.search_string(source_code):
758
+ print(f"{func.name} ({func.type}) args: {func.args}")
759
+
760
+
761
+ prints:
762
+
763
+ .. testoutput::
764
+
765
+ is_odd (int) args: [['int', 'x']]
766
+ dec_to_hex (int) args: [['char', 'hchar']]
767
+ """
768
+ return _NestedExpr(
769
+ opener, closer, content=content, ignore_expr=ignore_expr, **kwargs
770
+ )
771
+
772
+
773
+ def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")):
774
+ """Internal helper to construct opening and closing tag expressions,
775
+ given a tag name"""
776
+ if isinstance(tagStr, str_type):
777
+ resname = tagStr
778
+ tagStr = Keyword(tagStr, caseless=not xml)
779
+ else:
780
+ resname = tagStr.name
781
+
782
+ tagAttrName = Word(alphas, alphanums + "_-:")
783
+ if xml:
784
+ tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes)
785
+ openTag = (
786
+ suppress_LT
787
+ + tagStr("tag")
788
+ + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue)))
789
+ + Opt("/", default=[False])("empty").set_parse_action(
790
+ lambda s, l, t: t[0] == "/"
791
+ )
792
+ + suppress_GT
793
+ )
794
+ else:
795
+ tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word(
796
+ printables, exclude_chars=">"
797
+ )
798
+ openTag = (
799
+ suppress_LT
800
+ + tagStr("tag")
801
+ + Dict(
802
+ ZeroOrMore(
803
+ Group(
804
+ tagAttrName.set_parse_action(lambda t: t[0].lower())
805
+ + Opt(Suppress("=") + tagAttrValue)
806
+ )
807
+ )
808
+ )
809
+ + Opt("/", default=[False])("empty").set_parse_action(
810
+ lambda s, l, t: t[0] == "/"
811
+ )
812
+ + suppress_GT
813
+ )
814
+ closeTag = Combine(Literal("</") + tagStr + ">", adjacent=False)
815
+
816
+ openTag.set_name(f"<{resname}>")
817
+ # add start<tagname> results name in parse action now that ungrouped names are not reported at two levels
818
+ openTag.add_parse_action(
819
+ lambda t: t.__setitem__(
820
+ "start" + "".join(resname.replace(":", " ").title().split()), t.copy()
821
+ )
822
+ )
823
+ closeTag = closeTag(
824
+ "end" + "".join(resname.replace(":", " ").title().split())
825
+ ).set_name(f"</{resname}>")
826
+ openTag.tag = resname
827
+ closeTag.tag = resname
828
+ openTag.tag_body = SkipTo(closeTag())
829
+ return openTag, closeTag
830
+
831
+
832
+ def make_html_tags(
833
+ tag_str: Union[str, ParserElement],
834
+ ) -> tuple[ParserElement, ParserElement]:
835
+ """Helper to construct opening and closing tag expressions for HTML,
836
+ given a tag name. Matches tags in either upper or lower case,
837
+ attributes with namespaces and with quoted or unquoted values.
838
+
839
+ Example:
840
+
841
+ .. testcode::
842
+
843
+ text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
844
+ # make_html_tags returns pyparsing expressions for the opening and
845
+ # closing tags as a 2-tuple
846
+ a, a_end = make_html_tags("A")
847
+ link_expr = a + SkipTo(a_end)("link_text") + a_end
848
+
849
+ for link in link_expr.search_string(text):
850
+ # attributes in the <A> tag (like "href" shown here) are
851
+ # also accessible as named results
852
+ print(link.link_text, '->', link.href)
853
+
854
+ prints:
855
+
856
+ .. testoutput::
857
+
858
+ pyparsing -> https://github.com/pyparsing/pyparsing/wiki
859
+ """
860
+ return _makeTags(tag_str, False)
861
+
862
+
863
+ def make_xml_tags(
864
+ tag_str: Union[str, ParserElement],
865
+ ) -> tuple[ParserElement, ParserElement]:
866
+ """Helper to construct opening and closing tag expressions for XML,
867
+ given a tag name. Matches tags only in the given upper/lower case.
868
+
869
+ Example: similar to :class:`make_html_tags`
870
+ """
871
+ return _makeTags(tag_str, True)
872
+
873
+
874
+ any_open_tag: ParserElement
875
+ any_close_tag: ParserElement
876
+ any_open_tag, any_close_tag = make_html_tags(
877
+ Word(alphas, alphanums + "_:").set_name("any tag")
878
+ )
879
+
880
+ _htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()}
881
+ _most_common_entities = "nbsp lt gt amp quot apos cent pound euro copy".replace(
882
+ " ", "|"
883
+ )
884
+ common_html_entity = Regex(
885
+ lambda: f"&(?P<entity>{_most_common_entities}|{make_compressed_re(_htmlEntityMap)});"
886
+ ).set_name("common HTML entity")
887
+
888
+
889
+ def replace_html_entity(s, l, t):
890
+ """Helper parser action to replace common HTML entities with their special characters"""
891
+ return _htmlEntityMap.get(t.entity)
892
+
893
+
894
+ class OpAssoc(Enum):
895
+ """Enumeration of operator associativity
896
+ - used in constructing InfixNotationOperatorSpec for :class:`infix_notation`"""
897
+
898
+ LEFT = 1
899
+ RIGHT = 2
900
+
901
+
902
+ InfixNotationOperatorArgType = Union[
903
+ ParserElement, str, tuple[Union[ParserElement, str], Union[ParserElement, str]]
904
+ ]
905
+ InfixNotationOperatorSpec = Union[
906
+ tuple[
907
+ InfixNotationOperatorArgType,
908
+ int,
909
+ OpAssoc,
910
+ typing.Optional[ParseAction],
911
+ ],
912
+ tuple[
913
+ InfixNotationOperatorArgType,
914
+ int,
915
+ OpAssoc,
916
+ ],
917
+ ]
918
+
919
+
920
+ class _ParserState(Enum):
921
+ EXPECT_OPERAND = auto()
922
+ EXPECT_OPERATOR = auto()
923
+
924
+
925
+ class _OpType(Enum):
926
+ PREFIX = auto()
927
+ POSTFIX = auto()
928
+ INFIX2 = auto()
929
+ INFIX2_RIGHT = auto()
930
+ TERNARY_LEFT_STAGE1 = auto()
931
+ TERNARY_LEFT_STAGE2 = auto()
932
+ TERNARY_RIGHT_STAGE1 = auto()
933
+ TERNARY_RIGHT_STAGE2 = auto()
934
+ LPAR = auto()
935
+
936
+
937
+ class _InfixNotationOperatorSpec(NamedTuple):
938
+ op: InfixNotationOperatorArgType
939
+ arity: int
940
+ op_type: _OpType
941
+ parse_action: typing.Optional[list[Callable]]
942
+
943
+
944
+ class _InfixNotation(ParseElementEnhance):
945
+ """
946
+ An iterative / stack-based implementation of infix_notation (using operator-precedence /
947
+ shunting-yard style evaluation with an explicit stack).
948
+ Prevents Python recursion limits from being exceeded on deeply nested expressions or long chains.
949
+ """
950
+ def __init__(
951
+ self,
952
+ base_expr: ParserElement,
953
+ op_list: list[_InfixNotationOperatorSpec],
954
+ lpar: Union[str, ParserElement] = Suppress("("),
955
+ rpar: Union[str, ParserElement] = Suppress(")"),
956
+ ):
957
+ from .core import _trim_arity
958
+
959
+ while isinstance(base_expr, _InfixNotation):
960
+ op_list[:0] = base_expr.op_list[:] # type: ignore[has-type]
961
+ base_expr = base_expr.base_expr # type: ignore[has-type]
962
+
963
+ super().__init__(base_expr, savelist=True)
964
+ self.base_expr = base_expr
965
+ self.op_list = op_list
966
+ self.lpar = Suppress(lpar) if isinstance(lpar, str) else lpar
967
+ self.rpar = Suppress(rpar) if isinstance(rpar, str) else rpar
968
+ self.keep_parens = not (isinstance(self.lpar, Suppress) and isinstance(self.rpar, Suppress))
969
+ self.errmsg = None
970
+
971
+ # Classify operators by arity and associativity
972
+ # Precedence is defined by order in op_list (higher index in op_list = lower precedence)
973
+ # We assign higher numeric precedence to earlier entries in op_list
974
+ self.prefix_ops: list[_InfixNotationOperatorSpec] = [] # (expr, prec, assoc, pa_list)
975
+ self.postfix_ops: list[_InfixNotationOperatorSpec] = [] # (expr, prec, assoc, pa_list)
976
+ self.infix_ops = [] # type: ignore[var-annotated]
977
+
978
+ total_ops = len(op_list)
979
+ for idx, oper_def in enumerate(op_list):
980
+ op_expr, arity, assoc, *pa_opt = (oper_def + (None,))[:4]
981
+ pa = pa_opt[0] if pa_opt else None
982
+ pa_list = (
983
+ [_trim_arity(f) for f in pa]
984
+ if isinstance(pa, (tuple, list))
985
+ else ([_trim_arity(pa)] if pa is not None else [])
986
+ )
987
+
988
+ if not 1 <= arity <= 3:
989
+ raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
990
+
991
+ if assoc not in (OpAssoc.LEFT, OpAssoc.RIGHT):
992
+ raise ValueError("operator must indicate right or left associativity")
993
+
994
+ # Precedence: top of list has highest precedence
995
+ prec = (total_ops - idx) * 10
996
+
997
+ if arity == 1:
998
+ if isinstance(op_expr, str_type):
999
+ op_expr = Literal(op_expr)
1000
+ if assoc is OpAssoc.RIGHT:
1001
+ self.prefix_ops.append((op_expr, prec, assoc, pa_list))
1002
+ else:
1003
+ self.postfix_ops.append((op_expr, prec, assoc, pa_list))
1004
+ elif arity == 2:
1005
+ if op_expr is not None and isinstance(op_expr, str_type):
1006
+ op_expr = Literal(op_expr)
1007
+ self.infix_ops.append((op_expr, prec, assoc, pa_list, 2, None))
1008
+ elif arity == 3:
1009
+ if not isinstance(op_expr, (tuple, list)) or len(op_expr) != 2:
1010
+ raise ValueError(
1011
+ "if numterms=3, opExpr must be a tuple or list of two expressions"
1012
+ )
1013
+ op1, op2 = op_expr
1014
+ if isinstance(op1, str_type):
1015
+ op1 = Literal(op1)
1016
+ if isinstance(op2, str_type):
1017
+ op2 = Literal(op2)
1018
+ self.infix_ops.append((op1, prec, assoc, pa_list, 3, op2))
1019
+
1020
+ def _generateDefaultName(self) -> str:
1021
+ return f"{self.base_expr} infix expression"
1022
+
1023
+ def _run_parse_actions(self, pa_list, instring, loc, tokens):
1024
+ ret_tokens = tokens
1025
+ for fn in pa_list:
1026
+ res = fn(instring, loc, ret_tokens)
1027
+ if res is not None and res is not ret_tokens:
1028
+ if isinstance(res, (ParseResults, list, tuple)):
1029
+ ret_tokens = ParseResults(res, aslist=True)
1030
+ else:
1031
+ return res
1032
+ return ret_tokens
1033
+
1034
+ def _apply_operator(self, op_info, operand_stack, instring):
1035
+ op_type = op_info["type"]
1036
+ pa_list = op_info.get("pa", [])
1037
+ start_loc = op_info.get("loc", 0)
1038
+
1039
+ if op_type is _OpType.PREFIX:
1040
+ op_tok = op_info["op"]
1041
+ arg = operand_stack.pop()
1042
+ tokens = []
1043
+ if isinstance(op_tok, (list, ParseResults)):
1044
+ tokens.extend(op_tok)
1045
+ elif op_tok is not None:
1046
+ tokens.append(op_tok)
1047
+ tokens.append(arg)
1048
+ tokens_pr = ParseResults(tokens)
1049
+ if pa_list:
1050
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([tokens_pr]))
1051
+ else:
1052
+ res = tokens_pr
1053
+ operand_stack.append(res)
1054
+
1055
+ elif op_type is _OpType.POSTFIX:
1056
+ arg = operand_stack.pop()
1057
+ if isinstance(arg, ParseResults):
1058
+ res_pr = arg.copy()
1059
+ elif isinstance(arg, list):
1060
+ res_pr = ParseResults(arg)
1061
+ else:
1062
+ res_pr = ParseResults([arg])
1063
+ for op_tok in op_info["ops"]:
1064
+ if isinstance(op_tok, ParseResults):
1065
+ res_pr += op_tok
1066
+ elif isinstance(op_tok, list):
1067
+ res_pr.extend(op_tok)
1068
+ elif op_tok is not None:
1069
+ res_pr.append(op_tok)
1070
+ if pa_list:
1071
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([res_pr]))
1072
+ else:
1073
+ res = res_pr
1074
+ operand_stack.append(res)
1075
+
1076
+ elif op_type is _OpType.INFIX2:
1077
+ ops = op_info["ops"]
1078
+ num_ops = len(ops)
1079
+ args = [operand_stack.pop() for _ in range(num_ops + 1)]
1080
+ args.reverse()
1081
+ tokens = [args[0]]
1082
+ for i in range(num_ops):
1083
+ op_tok = ops[i]
1084
+ if isinstance(op_tok, (list, ParseResults)):
1085
+ tokens.extend(op_tok)
1086
+ elif op_tok is not None:
1087
+ tokens.append(op_tok)
1088
+ tokens.append(args[i + 1])
1089
+ tokens_pr = ParseResults(tokens)
1090
+ if pa_list:
1091
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([tokens_pr]))
1092
+ else:
1093
+ res = tokens_pr
1094
+ operand_stack.append(res)
1095
+
1096
+ elif op_type is _OpType.INFIX2_RIGHT:
1097
+ op_tok = op_info["op"]
1098
+ right = operand_stack.pop()
1099
+ left = operand_stack.pop()
1100
+ tokens = [left]
1101
+ if isinstance(op_tok, (list, ParseResults)):
1102
+ tokens.extend(op_tok)
1103
+ elif op_tok is not None:
1104
+ tokens.append(op_tok)
1105
+ tokens.append(right)
1106
+ tokens_pr = ParseResults(tokens)
1107
+ if pa_list:
1108
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([tokens_pr]))
1109
+ else:
1110
+ res = tokens_pr
1111
+ operand_stack.append(res)
1112
+
1113
+ elif op_type is _OpType.TERNARY_LEFT_STAGE2:
1114
+ ops_list = op_info["ops"]
1115
+ num_ops = len(ops_list)
1116
+ num_operands = 2 * num_ops + 1
1117
+ args = [operand_stack.pop() for _ in range(num_operands)]
1118
+ args.reverse()
1119
+ tokens = [args[0]]
1120
+ for i in range(num_ops):
1121
+ op1_tok, op2_tok = ops_list[i]
1122
+ if isinstance(op1_tok, (list, ParseResults)):
1123
+ tokens.extend(op1_tok)
1124
+ elif op1_tok is not None:
1125
+ tokens.append(op1_tok)
1126
+ tokens.append(args[2 * i + 1])
1127
+ if isinstance(op2_tok, (list, ParseResults)):
1128
+ tokens.extend(op2_tok)
1129
+ elif op2_tok is not None:
1130
+ tokens.append(op2_tok)
1131
+ tokens.append(args[2 * i + 2])
1132
+ tokens_pr = ParseResults(tokens)
1133
+ if pa_list:
1134
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([tokens_pr]))
1135
+ else:
1136
+ res = tokens_pr
1137
+ operand_stack.append(res)
1138
+
1139
+ elif op_type is _OpType.TERNARY_RIGHT_STAGE2:
1140
+ op1_tok = op_info["op1"]
1141
+ op2_tok = op_info["op2"]
1142
+ arg3 = operand_stack.pop()
1143
+ arg2 = operand_stack.pop()
1144
+ arg1 = operand_stack.pop()
1145
+ tokens = [arg1]
1146
+ if isinstance(op1_tok, (list, ParseResults)):
1147
+ tokens.extend(op1_tok)
1148
+ elif op1_tok is not None:
1149
+ tokens.append(op1_tok)
1150
+ tokens.append(arg2)
1151
+ if isinstance(op2_tok, (list, ParseResults)):
1152
+ tokens.extend(op2_tok)
1153
+ elif op2_tok is not None:
1154
+ tokens.append(op2_tok)
1155
+ tokens.append(arg3)
1156
+ tokens_pr = ParseResults(tokens)
1157
+ if pa_list:
1158
+ res = self._run_parse_actions(pa_list, instring, start_loc, ParseResults([tokens_pr]))
1159
+ else:
1160
+ res = tokens_pr
1161
+ operand_stack.append(res)
1162
+
1163
+ return bool(pa_list)
1164
+
1165
+ def parseImpl(self, instring, loc, do_actions=True):
1166
+ operand_stack = []
1167
+ operator_stack = []
1168
+ last_pa_applied = [False]
1169
+
1170
+ def reduce_operators(min_prec):
1171
+ while operator_stack:
1172
+ top = operator_stack[-1]
1173
+ if top["type"] in (_OpType.LPAR, _OpType.TERNARY_LEFT_STAGE1, _OpType.TERNARY_RIGHT_STAGE1):
1174
+ break
1175
+ if top["prec"] > min_prec:
1176
+ op_info = operator_stack.pop()
1177
+ last_pa_applied[0] = self._apply_operator(op_info, operand_stack, instring)
1178
+ else:
1179
+ break
1180
+
1181
+ state = _ParserState.EXPECT_OPERAND
1182
+ paren_depth = 0
1183
+
1184
+ while True:
1185
+ try:
1186
+ loc = self.preParse(instring, loc)
1187
+ except ParseException:
1188
+ pass
1189
+
1190
+ if state is _ParserState.EXPECT_OPERAND:
1191
+ # 1. Try prefix operators
1192
+ matched_prefix = False
1193
+ for op_expr, prec, assoc, pa_list in self.prefix_ops:
1194
+ try:
1195
+ next_loc, toks = op_expr._parse(instring, loc, do_actions=do_actions)
1196
+ op_tok = toks.as_list()[0] if len(toks) == 1 else toks.as_list()
1197
+ operator_stack.append({
1198
+ "type": _OpType.PREFIX,
1199
+ "op": op_tok,
1200
+ "prec": prec,
1201
+ "assoc": assoc,
1202
+ "pa": pa_list if do_actions else [],
1203
+ "loc": loc,
1204
+ })
1205
+ loc = next_loc
1206
+ matched_prefix = True
1207
+ break
1208
+ except ParseException:
1209
+ pass
1210
+ if matched_prefix:
1211
+ continue
1212
+
1213
+ # 2. Try base_expr
1214
+ try:
1215
+ next_loc, base_toks = self.base_expr._parse(instring, loc, do_actions=do_actions)
1216
+ if not base_toks and not base_toks._tokdict:
1217
+ loc = next_loc
1218
+ continue
1219
+ operand = base_toks[0] if len(base_toks) == 1 else base_toks
1220
+ operand_stack.append(operand)
1221
+ loc = next_loc
1222
+ state = _ParserState.EXPECT_OPERATOR
1223
+ continue
1224
+ except ParseException:
1225
+ pass
1226
+
1227
+ # 3. Try lpar
1228
+ try:
1229
+ next_loc, lpar_toks = self.lpar._parse(instring, loc, do_actions=do_actions)
1230
+ operator_stack.append({
1231
+ "type": _OpType.LPAR,
1232
+ "paren_toks": lpar_toks.as_list(),
1233
+ "loc": loc,
1234
+ })
1235
+ paren_depth += 1
1236
+ loc = next_loc
1237
+ continue
1238
+ except ParseException:
1239
+ raise ParseException(instring, loc, f"Expected {self.base_expr}")
1240
+
1241
+ elif state is _ParserState.EXPECT_OPERATOR:
1242
+ # 1. Try postfix operators
1243
+ matched_postfix = False
1244
+ for op_expr, prec, assoc, pa_list in self.postfix_ops:
1245
+ try:
1246
+ next_loc, toks = op_expr._parse(instring, loc, do_actions=do_actions)
1247
+ op_tok = toks
1248
+ if (
1249
+ operator_stack
1250
+ and operator_stack[-1]["type"] is _OpType.POSTFIX
1251
+ and operator_stack[-1]["prec"] == prec
1252
+ and operator_stack[-1]["assoc"] is OpAssoc.LEFT
1253
+ ):
1254
+ operator_stack[-1]["ops"].append(op_tok)
1255
+ else:
1256
+ reduce_operators(prec)
1257
+ operator_stack.append({
1258
+ "type": _OpType.POSTFIX,
1259
+ "ops": [op_tok],
1260
+ "prec": prec,
1261
+ "assoc": assoc,
1262
+ "pa": pa_list if do_actions else [],
1263
+ "loc": loc,
1264
+ })
1265
+ loc = next_loc
1266
+ matched_postfix = True
1267
+ break
1268
+ except ParseException:
1269
+ pass
1270
+ if matched_postfix:
1271
+ continue
1272
+
1273
+ # 2. Try rpar if inside parens
1274
+ if paren_depth > 0:
1275
+ try:
1276
+ next_loc, rpar_toks = self.rpar._parse(instring, loc, do_actions=do_actions)
1277
+ reduce_operators(-1) # reduce all operators inside this paren
1278
+ if operator_stack and operator_stack[-1]["type"] is _OpType.LPAR:
1279
+ lpar_info = operator_stack.pop()
1280
+ if self.keep_parens:
1281
+ top_val = operand_stack.pop()
1282
+ operand_stack.append(ParseResults([*lpar_info["paren_toks"], top_val, *rpar_toks.as_list()]))
1283
+ paren_depth -= 1
1284
+ loc = next_loc
1285
+ continue
1286
+ except ParseException:
1287
+ pass
1288
+
1289
+ # 3. Try matching op2 for an open ternary operator (STAGE1)
1290
+ matched_op2 = False
1291
+ for i in range(len(operator_stack) - 1, -1, -1):
1292
+ item = operator_stack[i]
1293
+ if item["type"] is _OpType.LPAR:
1294
+ break
1295
+ if item["type"] in (_OpType.TERNARY_LEFT_STAGE1, _OpType.TERNARY_RIGHT_STAGE1):
1296
+ try:
1297
+ next_loc, toks = item["op2_expr"]._parse(instring, loc, do_actions=do_actions)
1298
+ op2_tok = toks.as_list()[0] if len(toks) == 1 else toks.as_list()
1299
+ while operator_stack and operator_stack[-1] is not item:
1300
+ op_info = operator_stack.pop()
1301
+ self._apply_operator(op_info, operand_stack, instring)
1302
+ if item["type"] is _OpType.TERNARY_LEFT_STAGE1:
1303
+ item["type"] = _OpType.TERNARY_LEFT_STAGE2
1304
+ item["ops"][-1].append(op2_tok)
1305
+ else:
1306
+ item["type"] = _OpType.TERNARY_RIGHT_STAGE2
1307
+ item["op2"] = op2_tok
1308
+ loc = next_loc
1309
+ matched_op2 = True
1310
+ state = _ParserState.EXPECT_OPERAND
1311
+ break
1312
+ except ParseException:
1313
+ pass
1314
+ if matched_op2:
1315
+ continue
1316
+
1317
+ def _check_operand_follows(check_loc):
1318
+ for p_op, _, _, _ in self.prefix_ops:
1319
+ try:
1320
+ p_op._parse(instring, check_loc, do_actions=False)
1321
+ return True
1322
+ except ParseException:
1323
+ pass
1324
+ try:
1325
+ self.base_expr._parse(instring, check_loc, do_actions=False)
1326
+ return True
1327
+ except ParseException:
1328
+ pass
1329
+ try:
1330
+ self.lpar._parse(instring, check_loc, do_actions=False)
1331
+ return True
1332
+ except ParseException:
1333
+ pass
1334
+ return False
1335
+
1336
+ # 4. Try infix binary and ternary operators
1337
+ matched_infix = False
1338
+ for op_expr, prec, assoc, pa_list, arity, op2_expr in self.infix_ops:
1339
+ if arity == 2:
1340
+ if op_expr is None:
1341
+ if not _check_operand_follows(loc):
1342
+ continue
1343
+ next_loc = loc
1344
+ op_tok = None
1345
+ else:
1346
+ try:
1347
+ next_loc, toks = op_expr._parse(instring, loc, do_actions=do_actions)
1348
+ except ParseException:
1349
+ continue
1350
+ if next_loc == loc:
1351
+ if not _check_operand_follows(loc):
1352
+ continue
1353
+ op_tok = (toks.as_list()[0] if len(toks) == 1 else toks.as_list()) if toks else None
1354
+ else:
1355
+ op_tok = toks.as_list()[0] if len(toks) == 1 else toks.as_list()
1356
+
1357
+ if assoc is OpAssoc.LEFT:
1358
+ reduce_operators(prec)
1359
+ if (
1360
+ operator_stack
1361
+ and operator_stack[-1]["type"] is _OpType.INFIX2
1362
+ and operator_stack[-1]["prec"] == prec
1363
+ ):
1364
+ operator_stack[-1]["ops"].append(op_tok)
1365
+ else:
1366
+ operator_stack.append({
1367
+ "type": _OpType.INFIX2,
1368
+ "ops": [op_tok],
1369
+ "prec": prec,
1370
+ "assoc": assoc,
1371
+ "pa": pa_list if do_actions else [],
1372
+ "loc": loc,
1373
+ })
1374
+ else:
1375
+ reduce_operators(prec)
1376
+ operator_stack.append({
1377
+ "type": _OpType.INFIX2_RIGHT,
1378
+ "op": op_tok,
1379
+ "prec": prec,
1380
+ "assoc": assoc,
1381
+ "pa": pa_list if do_actions else [],
1382
+ "loc": loc,
1383
+ })
1384
+ loc = next_loc
1385
+ matched_infix = True
1386
+ state = _ParserState.EXPECT_OPERAND
1387
+ break
1388
+ elif arity == 3:
1389
+ try:
1390
+ next_loc, toks = op_expr._parse(instring, loc, do_actions=do_actions)
1391
+ if next_loc == loc and not _check_operand_follows(loc):
1392
+ continue
1393
+ op_tok = toks.as_list()[0] if len(toks) == 1 else toks.as_list()
1394
+ if assoc is OpAssoc.LEFT:
1395
+ if (
1396
+ operator_stack
1397
+ and operator_stack[-1]["type"] is _OpType.TERNARY_LEFT_STAGE2
1398
+ and operator_stack[-1]["prec"] == prec
1399
+ ):
1400
+ operator_stack[-1]["type"] = _OpType.TERNARY_LEFT_STAGE1
1401
+ operator_stack[-1]["ops"].append([op_tok])
1402
+ else:
1403
+ reduce_operators(prec)
1404
+ operator_stack.append({
1405
+ "type": _OpType.TERNARY_LEFT_STAGE1,
1406
+ "ops": [[op_tok]],
1407
+ "op2_expr": op2_expr,
1408
+ "prec": prec,
1409
+ "assoc": assoc,
1410
+ "pa": pa_list if do_actions else [],
1411
+ "loc": loc,
1412
+ })
1413
+ else:
1414
+ reduce_operators(prec)
1415
+ operator_stack.append({
1416
+ "type": _OpType.TERNARY_RIGHT_STAGE1,
1417
+ "op1": op_tok,
1418
+ "op2_expr": op2_expr,
1419
+ "prec": prec,
1420
+ "assoc": assoc,
1421
+ "pa": pa_list if do_actions else [],
1422
+ "loc": loc,
1423
+ })
1424
+ loc = next_loc
1425
+ matched_infix = True
1426
+ state = _ParserState.EXPECT_OPERAND
1427
+ break
1428
+ except ParseException:
1429
+ pass
1430
+ if matched_infix:
1431
+ continue
1432
+
1433
+ # No more operators can be consumed at this level
1434
+ break
1435
+
1436
+ # Reduce remaining operators
1437
+ reduce_operators(-1)
1438
+
1439
+ if paren_depth != 0 or len(operand_stack) != 1 or operator_stack:
1440
+ raise ParseException(instring, loc, "Unbalanced parentheses or expression syntax error")
1441
+
1442
+ final_result = operand_stack.pop()
1443
+ if last_pa_applied[0] and isinstance(final_result, ParseResults):
1444
+ return loc, final_result
1445
+ else:
1446
+ return loc, ParseResults([final_result])
1447
+
1448
+
1449
+ def infix_notation(base_expr, op_list, lpar="(", rpar=")"):
1450
+ """Helper method for constructing grammars of expressions made up of
1451
+ operators working in a precedence hierarchy. Operators may be unary
1452
+ or binary, left- or right-associative. Parse actions can also be
1453
+ attached to operator expressions. The generated parser will also
1454
+ recognize the use of parentheses to override operator precedences
1455
+ (see example below).
1456
+
1457
+ Note: if you define a deep operator list, you may see performance
1458
+ issues when using infix_notation. See
1459
+ :class:`ParserElement.enable_packrat` for a mechanism to potentially
1460
+ improve your parser performance.
1461
+
1462
+ Parameters:
1463
+
1464
+ :param base_expr: expression representing the most basic operand to
1465
+ be used in the expression
1466
+ :param op_list: list of tuples, one for each operator precedence level
1467
+ in the expression grammar; each tuple is of the form ``(op_expr,
1468
+ num_operands, right_left_assoc, (optional)parse_action)``, where:
1469
+
1470
+ - ``op_expr`` is the pyparsing expression for the operator; may also
1471
+ be a string, which will be converted to a Literal; if ``num_operands``
1472
+ is 3, ``op_expr`` is a tuple of two expressions, for the two
1473
+ operators separating the 3 terms
1474
+ - ``num_operands`` is the number of terms for this operator (must be 1,
1475
+ 2, or 3)
1476
+ - ``right_left_assoc`` is the indicator whether the operator is right
1477
+ or left associative, using the pyparsing-defined constants
1478
+ ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``.
1479
+ - ``parse_action`` is the parse action to be associated with
1480
+ expressions matching this operator expression (the parse action
1481
+ tuple member may be omitted); if the parse action is passed
1482
+ a tuple or list of functions, this is equivalent to calling
1483
+ ``set_parse_action(*fn)``
1484
+ (:class:`ParserElement.set_parse_action`)
1485
+
1486
+ :param lpar: expression for matching left-parentheses; if passed as a
1487
+ str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as
1488
+ an expression (such as ``Literal('(')``), then it will be kept in
1489
+ the parsed results, and grouped with them. (default= ``Suppress('(')``)
1490
+ :param rpar: expression for matching right-parentheses; if passed as a
1491
+ str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as
1492
+ an expression (such as ``Literal(')')``), then it will be kept in
1493
+ the parsed results, and grouped with them. (default= ``Suppress(')')``)
1494
+
1495
+ Example:
1496
+
1497
+ .. testcode::
1498
+
1499
+ # simple example of four-function arithmetic with ints and
1500
+ # variable names
1501
+ integer = pyparsing_common.signed_integer
1502
+ varname = pyparsing_common.identifier
1503
+
1504
+ arith_expr = infix_notation(integer | varname,
1505
+ [
1506
+ ('-', 1, OpAssoc.RIGHT),
1507
+ (one_of('* /'), 2, OpAssoc.LEFT),
1508
+ (one_of('+ -'), 2, OpAssoc.LEFT),
1509
+ ])
1510
+
1511
+ arith_expr.run_tests('''
1512
+ 5+3*6
1513
+ (5+3)*6
1514
+ (5+x)*y
1515
+ -2--11
1516
+ ''', full_dump=False)
1517
+
1518
+ prints:
1519
+
1520
+ .. testoutput::
1521
+ :options: +NORMALIZE_WHITESPACE
1522
+
1523
+
1524
+ 5+3*6
1525
+ [[5, '+', [3, '*', 6]]]
1526
+
1527
+ (5+3)*6
1528
+ [[[5, '+', 3], '*', 6]]
1529
+
1530
+ (5+x)*y
1531
+ [[[5, '+', 'x'], '*', 'y']]
1532
+
1533
+ -2--11
1534
+ [[['-', 2], '-', ['-', 11]]]
1535
+ """
1536
+ return _InfixNotation(base_expr, op_list, lpar=lpar, rpar=rpar)
1537
+
1538
+
1539
+ def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]):
1540
+ """
1541
+ .. deprecated:: 3.0.0
1542
+ Use the :class:`IndentedBlock` class instead. Note that `IndentedBlock`
1543
+ has a difference method signature.
1544
+
1545
+ Helper method for defining space-delimited indentation blocks,
1546
+ such as those used to define block statements in Python source code.
1547
+
1548
+ :param blockStatementExpr: expression defining syntax of statement that
1549
+ is repeated within the indented block
1550
+
1551
+ :param indentStack: list created by caller to manage indentation stack
1552
+ (multiple ``statementWithIndentedBlock`` expressions within a single
1553
+ grammar should share a common ``indentStack``)
1554
+
1555
+ :param indent: boolean indicating whether block must be indented beyond
1556
+ the current level; set to ``False`` for block of left-most statements
1557
+
1558
+ A valid block must contain at least one ``blockStatement``.
1559
+
1560
+ (Note that indentedBlock uses internal parse actions which make it
1561
+ incompatible with packrat parsing.)
1562
+
1563
+ Example:
1564
+
1565
+ .. testcode::
1566
+
1567
+ data = '''
1568
+ def A(z):
1569
+ A1
1570
+ B = 100
1571
+ G = A2
1572
+ A2
1573
+ A3
1574
+ B
1575
+ def BB(a,b,c):
1576
+ BB1
1577
+ def BBA():
1578
+ bba1
1579
+ bba2
1580
+ bba3
1581
+ C
1582
+ D
1583
+ def spam(x,y):
1584
+ def eggs(z):
1585
+ pass
1586
+ '''
1587
+
1588
+ indentStack = [1]
1589
+ stmt = Forward()
1590
+
1591
+ identifier = Word(alphas, alphanums)
1592
+ funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":")
1593
+ func_body = indentedBlock(stmt, indentStack)
1594
+ funcDef = Group(funcDecl + func_body)
1595
+
1596
+ rvalue = Forward()
1597
+ funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")")
1598
+ rvalue << (funcCall | identifier | Word(nums))
1599
+ assignment = Group(identifier + "=" + rvalue)
1600
+ stmt << (funcDef | assignment | identifier)
1601
+
1602
+ module_body = stmt[1, ...]
1603
+
1604
+ parseTree = module_body.parseString(data)
1605
+ parseTree.pprint()
1606
+
1607
+ prints:
1608
+
1609
+ .. testoutput::
1610
+
1611
+ [['def',
1612
+ 'A',
1613
+ ['(', 'z', ')'],
1614
+ ':',
1615
+ [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]],
1616
+ 'B',
1617
+ ['def',
1618
+ 'BB',
1619
+ ['(', 'a', 'b', 'c', ')'],
1620
+ ':',
1621
+ [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]],
1622
+ 'C',
1623
+ 'D',
1624
+ ['def',
1625
+ 'spam',
1626
+ ['(', 'x', 'y', ')'],
1627
+ ':',
1628
+ [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]]
1629
+ """
1630
+ warnings.warn(
1631
+ f"{'indentedBlock'!r} deprecated - use {'IndentedBlock'!r}",
1632
+ PyparsingDeprecationWarning,
1633
+ stacklevel=2,
1634
+ )
1635
+
1636
+ backup_stacks.append(indentStack[:])
1637
+
1638
+ def reset_stack():
1639
+ indentStack[:] = backup_stacks[-1]
1640
+
1641
+ def checkPeerIndent(s, l, t):
1642
+ if l >= len(s):
1643
+ return
1644
+ curCol = col(l, s)
1645
+ if curCol != indentStack[-1]:
1646
+ if curCol > indentStack[-1]:
1647
+ raise ParseException(s, l, "illegal nesting")
1648
+ raise ParseException(s, l, "not a peer entry")
1649
+
1650
+ def checkSubIndent(s, l, t):
1651
+ curCol = col(l, s)
1652
+ if curCol > indentStack[-1]:
1653
+ indentStack.append(curCol)
1654
+ else:
1655
+ raise ParseException(s, l, "not a subentry")
1656
+
1657
+ def checkUnindent(s, l, t):
1658
+ if l >= len(s):
1659
+ return
1660
+ curCol = col(l, s)
1661
+ if not (indentStack and curCol in indentStack):
1662
+ raise ParseException(s, l, "not an unindent")
1663
+ if curCol < indentStack[-1]:
1664
+ indentStack.pop()
1665
+
1666
+ NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress())
1667
+ INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT")
1668
+ PEER = Empty().set_parse_action(checkPeerIndent).set_name("")
1669
+ UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT")
1670
+ if indent:
1671
+ smExpr = Group(
1672
+ Opt(NL)
1673
+ + INDENT
1674
+ + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL))
1675
+ + UNDENT
1676
+ )
1677
+ else:
1678
+ smExpr = Group(
1679
+ Opt(NL)
1680
+ + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL))
1681
+ + Opt(UNDENT)
1682
+ )
1683
+
1684
+ # add a parse action to remove backup_stack from list of backups
1685
+ smExpr.add_parse_action(
1686
+ lambda: backup_stacks.pop(-1) and None if backup_stacks else None
1687
+ )
1688
+ smExpr.set_fail_action(lambda a, b, c, d: reset_stack())
1689
+ blockStatementExpr.ignore(_bslash + LineEnd())
1690
+ return smExpr.set_name("indented block")
1691
+
1692
+
1693
+ # it's easy to get these comment structures wrong - they're very common,
1694
+ # so may as well make them available
1695
+ c_style_comment = Regex(r"/\*(?:[^*]|\*(?!/))*\*\/").set_name("C style comment")
1696
+ "Comment of the form ``/* ... */``"
1697
+
1698
+ html_comment = Regex(r"<!--[\s\S]*?-->").set_name("HTML comment")
1699
+ "Comment of the form ``<!-- ... -->``"
1700
+
1701
+ rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line")
1702
+ dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment")
1703
+ "Comment of the form ``// ... (to end of line)``"
1704
+
1705
+ cpp_style_comment = Regex(
1706
+ r"(?:/\*(?:[^*]|\*(?!/))*\*\/)|(?://(?:\\\n|[^\n])*)"
1707
+ ).set_name("C++ style comment")
1708
+ "Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`"
1709
+
1710
+ java_style_comment = cpp_style_comment
1711
+ "Same as :class:`cpp_style_comment`"
1712
+
1713
+ python_style_comment = Regex(r"#.*").set_name("Python style comment")
1714
+ "Comment of the form ``# ... (to end of line)``"
1715
+
1716
+
1717
+ # build list of built-in expressions, for future reference if a global default value
1718
+ # gets updated
1719
+ _builtin_exprs: list[ParserElement] = [
1720
+ v for v in vars().values() if isinstance(v, ParserElement)
1721
+ ]
1722
+
1723
+
1724
+ # compatibility function, superseded by DelimitedList class
1725
+ def delimited_list(
1726
+ expr: Union[str, ParserElement],
1727
+ delim: Union[str, ParserElement] = ",",
1728
+ combine: bool = False,
1729
+ min: typing.Optional[int] = None,
1730
+ max: typing.Optional[int] = None,
1731
+ *,
1732
+ allow_trailing_delim: bool = False,
1733
+ ) -> ParserElement:
1734
+ """
1735
+ .. deprecated:: 3.1.0
1736
+ Use the :class:`DelimitedList` class instead.
1737
+ """
1738
+ return DelimitedList(
1739
+ expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim
1740
+ )
1741
+
1742
+
1743
+ # Compatibility synonyms
1744
+ # fmt: off
1745
+ opAssoc = OpAssoc
1746
+ anyOpenTag = any_open_tag
1747
+ anyCloseTag = any_close_tag
1748
+ commonHTMLEntity = common_html_entity
1749
+ cStyleComment = c_style_comment
1750
+ htmlComment = html_comment
1751
+ restOfLine = rest_of_line
1752
+ dblSlashComment = dbl_slash_comment
1753
+ cppStyleComment = cpp_style_comment
1754
+ javaStyleComment = java_style_comment
1755
+ pythonStyleComment = python_style_comment
1756
+ delimitedList = replaced_by_pep8("delimitedList", DelimitedList)
1757
+ delimited_list = replaced_by_pep8("delimited_list", DelimitedList)
1758
+ countedArray = replaced_by_pep8("countedArray", counted_array)
1759
+ matchPreviousLiteral = replaced_by_pep8("matchPreviousLiteral", match_previous_literal)
1760
+ matchPreviousExpr = replaced_by_pep8("matchPreviousExpr", match_previous_expr)
1761
+ oneOf = replaced_by_pep8("oneOf", one_of)
1762
+ dictOf = replaced_by_pep8("dictOf", dict_of)
1763
+ originalTextFor = replaced_by_pep8("originalTextFor", original_text_for)
1764
+ nestedExpr = replaced_by_pep8("nestedExpr", nested_expr)
1765
+ makeHTMLTags = replaced_by_pep8("makeHTMLTags", make_html_tags)
1766
+ makeXMLTags = replaced_by_pep8("makeXMLTags", make_xml_tags)
1767
+ replaceHTMLEntity = replaced_by_pep8("replaceHTMLEntity", replace_html_entity)
1768
+ infixNotation = replaced_by_pep8("infixNotation", infix_notation)
1769
+ # fmt: on