diff options
author | ptmcg <ptmcg@austin.rr.com> | 2020-10-11 14:21:09 -0500 |
---|---|---|
committer | ptmcg <ptmcg@austin.rr.com> | 2020-10-11 14:21:09 -0500 |
commit | 5713fba4fc952cd08a136301ff84275f19e1e930 (patch) | |
tree | 267deccbc8711e68bd25e0957021dbc35142c3a4 | |
parent | 1add43913c92157add7823e58e961a20fcf5c31c (diff) | |
download | pyparsing-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-- | CHANGES | 6 | ||||
-rw-r--r-- | pyparsing/core.py | 23 | ||||
-rw-r--r-- | tests/test_unit.py | 35 |
3 files changed, 56 insertions, 8 deletions
@@ -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" |