summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@austin.rr.com>2020-10-11 14:21:09 -0500
committerptmcg <ptmcg@austin.rr.com>2020-10-11 14:21:09 -0500
commit5713fba4fc952cd08a136301ff84275f19e1e930 (patch)
tree267deccbc8711e68bd25e0957021dbc35142c3a4
parent1add43913c92157add7823e58e961a20fcf5c31c (diff)
downloadpyparsing-git-5713fba4fc952cd08a136301ff84275f19e1e930.tar.gz
Fixed bugs in Each with ZeroOrMore and OneOrMore (first matched element enclosed in extra nesting level; results names not maintained; did not handle mix with required expressions)
-rw-r--r--CHANGES6
-rw-r--r--pyparsing/core.py23
-rw-r--r--tests/test_unit.py35
3 files changed, 56 insertions, 8 deletions
diff --git a/CHANGES b/CHANGES
index d55c3d6..45d98f7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,12 @@ Version 3.0.0b1
debug actions, see the following bullet regarding an optional API change
for those methods.
+- Fixed bugs in Each when passed OneOrMore or ZeroOrMore expressions:
+ . first expression match could be enclosed in an extra nesting level
+ . out-of-order expressions now handled correctly if mixed with required
+ expressions
+ . results names are maintained correctly for these expressions
+
- Fixed traceback trimming, and added ParserElement.verbose_traceback
save/restore to reset_pyparsing_context().
diff --git a/pyparsing/core.py b/pyparsing/core.py
index 01cebda..00daf61 100644
--- a/pyparsing/core.py
+++ b/pyparsing/core.py
@@ -414,6 +414,8 @@ class ParserElement(ABC):
return self._setResultsName(name, listAllMatches)
def _setResultsName(self, name, listAllMatches=False):
+ if name is None:
+ return self
newself = self.copy()
if name.endswith("*"):
name = name[:-1]
@@ -3573,14 +3575,18 @@ class Each(ParseExpression):
opt2 = [
e
for e in self.exprs
- if e.mayReturnEmpty and not isinstance(e, (Optional, Regex))
+ if e.mayReturnEmpty and not isinstance(e, (Optional, Regex, ZeroOrMore))
]
self.optionals = opt1 + opt2
self.multioptionals = [
- e.expr for e in self.exprs if isinstance(e, ZeroOrMore)
+ e.expr.setResultsName(e.resultsName, listAllMatches=True)
+ for e in self.exprs
+ if isinstance(e, _MultipleMatch)
]
self.multirequired = [
- e.expr for e in self.exprs if isinstance(e, OneOrMore)
+ e.expr.setResultsName(e.resultsName, listAllMatches=True)
+ for e in self.exprs
+ if isinstance(e, OneOrMore)
]
self.required = [
e
@@ -3589,16 +3595,18 @@ class Each(ParseExpression):
]
self.required += self.multirequired
self.initExprGroups = False
+
tmpLoc = loc
tmpReqd = self.required[:]
tmpOpt = self.optionals[:]
+ multis = self.multioptionals[:]
matchOrder = []
keepMatching = True
failed = []
fatals = []
while keepMatching:
- tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+ tmpExprs = tmpReqd + tmpOpt + multis
failed.clear()
fatals.clear()
for e in tmpExprs:
@@ -3642,13 +3650,12 @@ class Each(ParseExpression):
e for e in self.exprs if isinstance(e, Optional) and e.expr in tmpOpt
]
- resultlist = []
+ total_results = ParseResults([])
for e in matchOrder:
loc, results = e._parse(instring, loc, doActions)
- resultlist.append(results)
+ total_results += results
- finalResults = sum(resultlist, ParseResults([]))
- return loc, finalResults
+ return loc, total_results
def _generateDefaultName(self):
return "{" + " & ".join(str(e) for e in self.exprs) + "}"
diff --git a/tests/test_unit.py b/tests/test_unit.py
index 11b86a6..1d6e28b 100644
--- a/tests/test_unit.py
+++ b/tests/test_unit.py
@@ -4636,6 +4636,41 @@ class Test2_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase):
"incorrect exception raised for test string {!r}".format(test_str),
)
+ def testEachWithMultipleMatch(self):
+ size = "size" + pp.oneOf("S M L XL")
+ color = pp.Group(
+ "color" + pp.oneOf("red orange yellow green blue purple white black brown")
+ )
+ size.setName("size_spec")
+ color.setName("color_spec")
+
+ spec0 = size("size") & color[...]("colors")
+ spec1 = size("size") & color[1, ...]("colors")
+
+ for spec in (spec0, spec1):
+ for test, expected_dict in [
+ (
+ "size M color red color yellow",
+ {
+ "colors": [["color", "red"], ["color", "yellow"]],
+ "size": ["size", "M"],
+ },
+ ),
+ (
+ "color green size M color red color yellow",
+ {
+ "colors": [
+ ["color", "green"],
+ ["color", "red"],
+ ["color", "yellow"],
+ ],
+ "size": ["size", "M"],
+ },
+ ),
+ ]:
+ result = spec.parseString(test, parseAll=True)
+ self.assertParseResultsEquals(result, expected_dict=expected_dict)
+
def testSumParseResults(self):
samplestr1 = "garbage;DOB 10-10-2010;more garbage\nID PARI12345678;more garbage"