summaryrefslogtreecommitdiff
path: root/pyparsing.py
diff options
context:
space:
mode:
authorptmcg <ptmcg@austin.rr.com>2019-07-05 18:27:55 -0500
committerptmcg <ptmcg@austin.rr.com>2019-07-05 18:27:55 -0500
commitbafd437305c82f064940314574ef8071dc641429 (patch)
tree3e13d4b23dd6aa50c751d78ff9332057381662aa /pyparsing.py
parentc5c4ca6438ec0114c187beb6b8a2298b41eb9fe2 (diff)
downloadpyparsing-git-bafd437305c82f064940314574ef8071dc641429.tar.gz
Add support for "(expr | ...)" as a form of "on-error, skip" notation
Diffstat (limited to 'pyparsing.py')
-rw-r--r--pyparsing.py139
1 files changed, 85 insertions, 54 deletions
diff --git a/pyparsing.py b/pyparsing.py
index febefc2..0f6d499 100644
--- a/pyparsing.py
+++ b/pyparsing.py
@@ -96,7 +96,7 @@ classes inherit from. Use the docstrings for examples of how to:
"""
__version__ = "2.4.1"
-__versionTime__ = "05 Jul 2019 15:20 UTC"
+__versionTime__ = "05 Jul 2019 23:23 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -1248,6 +1248,7 @@ def _trim_arity(func, maxargs=2):
return wrapper
+
class ParserElement(object):
"""Abstract base level parser element class."""
DEFAULT_WHITE_CHARS = " \n\t\r"
@@ -2048,24 +2049,9 @@ class ParserElement(object):
Note that the skipped text is returned with '_skipped' as a results name.
"""
-
- class _PendingSkip(ParserElement):
- # internal placeholder class to hold a place were '...' is added to a parser element,
- # once another ParserElement is added, this placeholder will be replaced with a
- # SkipTo
- def __init__(self, expr):
- super(_PendingSkip, self).__init__()
- self.name = str(expr + '').replace('""', '...')
- self.expr = expr
-
- def __add__(self, other):
- return self.expr + SkipTo(other)("_skipped") + other
-
- def parseImpl(self, *args):
- raise Exception("use of `...` expression without following SkipTo target expression")
-
if other is Ellipsis:
return _PendingSkip(self)
+
if isinstance( other, basestring ):
other = ParserElement._literalStringClass( other )
if not isinstance( other, ParserElement ):
@@ -2113,43 +2099,6 @@ class ParserElement(object):
return None
return other - self
- def __getitem__(self, key):
- """
- use ``[]`` indexing notation as a short form for expression repetition:
- - ``expr[n]`` is equivalent to ``expr*n``
- - ``expr[m, n]`` is equivalent to ``expr*(m, n)``
- - ``expr[n, ...]`` or ``expr[n,]`` is equivalent
- to ``expr*n + ZeroOrMore(expr)``
- (read as "at least n instances of ``expr``")
- - ``expr[..., n]`` is equivalent to ``expr*(0,n)``
- (read as "0 to n instances of ``expr``")
- - ``expr[0, ...]`` is equivalent to ``ZeroOrMore(expr)``
- - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)``
- - ``expr[...]`` is equivalent to ``OneOrMore(expr)``
- ``None`` may be used in place of ``...``.
-
- Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception
- if more than ``n`` ``expr``s exist in the input stream. If this behavior is
- desired, then write ``expr[..., n] + ~expr``.
- """
-
- # convert single arg keys to tuples
- try:
- if isinstance(key, str):
- key = (key,)
- iter(key)
- except TypeError:
- key = (key,)
-
- if len(key) > 2:
- warnings.warn("only 1 or 2 index arguments supported ({0}{1})".format(key[:5],
- '... [{0}]'.format(len(key))
- if len(key) > 5 else ''))
-
- # clip to 2 elements
- ret = self * tuple(key[:2])
- return ret
-
def __mul__(self,other):
"""
Implementation of * operator, allows use of ``expr * 3`` in place of
@@ -2228,6 +2177,9 @@ class ParserElement(object):
"""
Implementation of | operator - returns :class:`MatchFirst`
"""
+ if other is Ellipsis:
+ return _PendingSkip(self, must_skip=True)
+
if isinstance( other, basestring ):
other = ParserElement._literalStringClass( other )
if not isinstance( other, ParserElement ):
@@ -2302,6 +2254,43 @@ class ParserElement(object):
"""
return NotAny( self )
+ def __getitem__(self, key):
+ """
+ use ``[]`` indexing notation as a short form for expression repetition:
+ - ``expr[n]`` is equivalent to ``expr*n``
+ - ``expr[m, n]`` is equivalent to ``expr*(m, n)``
+ - ``expr[n, ...]`` or ``expr[n,]`` is equivalent
+ to ``expr*n + ZeroOrMore(expr)``
+ (read as "at least n instances of ``expr``")
+ - ``expr[..., n]`` is equivalent to ``expr*(0,n)``
+ (read as "0 to n instances of ``expr``")
+ - ``expr[0, ...]`` is equivalent to ``ZeroOrMore(expr)``
+ - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)``
+ - ``expr[...]`` is equivalent to ``OneOrMore(expr)``
+ ``None`` may be used in place of ``...``.
+
+ Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception
+ if more than ``n`` ``expr``s exist in the input stream. If this behavior is
+ desired, then write ``expr[..., n] + ~expr``.
+ """
+
+ # convert single arg keys to tuples
+ try:
+ if isinstance(key, str):
+ key = (key,)
+ iter(key)
+ except TypeError:
+ key = (key,)
+
+ if len(key) > 2:
+ warnings.warn("only 1 or 2 index arguments supported ({0}{1})".format(key[:5],
+ '... [{0}]'.format(len(key))
+ if len(key) > 5 else ''))
+
+ # clip to 2 elements
+ ret = self * tuple(key[:2])
+ return ret
+
def __call__(self, name=None):
"""
Shortcut for :class:`setResultsName`, with ``listAllMatches=False``.
@@ -2669,6 +2658,38 @@ class ParserElement(object):
return success, allResults
+class _PendingSkip(ParserElement):
+ # internal placeholder class to hold a place were '...' is added to a parser element,
+ # once another ParserElement is added, this placeholder will be replaced with a SkipTo
+ def __init__(self, expr, must_skip=False):
+ super(_PendingSkip, self).__init__()
+ self.strRepr = str(expr + Empty()).replace('Empty', '...')
+ self.name = self.strRepr
+ self.anchor = expr
+ self.must_skip = must_skip
+
+ def __add__(self, other):
+ skipper = SkipTo(other).setName("...")("_skipped")
+ if self.must_skip:
+ def must_skip(t):
+ if not t._skipped:
+ del t[0]
+ t.pop("_skipped", None)
+ def show_skip(t):
+ if not t._skipped:
+ t['_skipped'] = 'missing <' + repr(self.anchor) + '>'
+ return (self.anchor + skipper().addParseAction(must_skip)
+ | skipper().addParseAction(show_skip)) + other
+
+ return self.anchor + skipper + other
+
+ def __repr__(self):
+ return self.strRepr
+
+ def parseImpl(self, *args):
+ raise Exception("use of `...` expression without following SkipTo target expression")
+
+
class Token(ParserElement):
"""Abstract :class:`ParserElement` subclass, for defining atomic
matching patterns.
@@ -3834,6 +3855,16 @@ class And(ParseExpression):
self.callPreparse = True
def streamline(self):
+ # collapse any _PendingSkip's
+ if any(isinstance(e, ParseExpression) and isinstance(e.exprs[-1], _PendingSkip) for e in self.exprs[:-1]):
+ for i, e in enumerate(self.exprs[:-1]):
+ if e is None:
+ continue
+ if (isinstance(e, ParseExpression) and isinstance(e.exprs[-1], _PendingSkip)):
+ e.exprs[-1] = e.exprs[-1] + self.exprs[i+1]
+ self.exprs[i+1] = None
+ self.exprs = [e for e in self.exprs if e is not None]
+
super(And, self).streamline()
self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
return self