From ec420e9735ecc2a2870f8d86e3e9657245e6bfeb Mon Sep 17 00:00:00 2001 From: ptmcg Date: Sat, 13 Aug 2016 02:27:58 +0000 Subject: Simplified merging ParseResults from expressions in an Each; also fixed class hierarchy for OneOrMore vs. ZeroOrMore, which was causing ZeroOrMore expressions to be treated as required expressions in an Each git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@416 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b --- src/CHANGES | 5 ++++ src/pyparsing.py | 71 +++++++++++++++++++++++++------------------------------- src/unitTests.py | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/src/CHANGES b/src/CHANGES index b73b7bb..0fca15f 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -12,6 +12,11 @@ Version 2.1.8 - - Fixed bug in ParserElement.inlineLiteralsUsing, causing infinite loop with Suppress. +- Fixed bug in Each, when merging named results from multiple + expressions in a ZeroOrMore or OneOrMore. Also fixed bug when + ZeroOrMore expressions were erroneously treated as required + expressions in an Each expression. + - Added a few more inline doc examples. diff --git a/src/pyparsing.py b/src/pyparsing.py index 9191426..a99140c 100644 --- a/src/pyparsing.py +++ b/src/pyparsing.py @@ -58,7 +58,7 @@ The pyparsing module handles some of the problems that are typically vexing when """ __version__ = "2.1.8" -__versionTime__ = "12 Aug 2016 14:46 UTC" +__versionTime__ = "13 Aug 2016 02:22 UTC" __author__ = "Paul McGuire " import string @@ -3554,17 +3554,7 @@ class Each(ParseExpression): loc,results = e._parse(instring,loc,doActions) resultlist.append(results) - finalResults = ParseResults() - for r in resultlist: - dups = {} - for k in r.keys(): - if k in finalResults: - tmp = ParseResults(finalResults[k]) - tmp += ParseResults(r[k]) - dups[k] = tmp - finalResults += ParseResults(r) - for k,v in dups.items(): - finalResults[k] = v + finalResults = sum(resultlist, ParseResults([])) return loc, finalResults def __str__( self ): @@ -3717,34 +3707,9 @@ class NotAny(ParseElementEnhance): return self.strRepr - -class OneOrMore(ParseElementEnhance): - """ - Repetition of one or more of the given expression. - - Parameters: - - expr - expression that must match one or more times - - stopOn - (default=C{None}) - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - - Example:: - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) - - text = "shape: SQUARE posn: upper left color: BLACK" - OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] - - # use stopOn attribute for OneOrMore to avoid reading label string as part of the data - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) - OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] - - # could also be written as - (attr_expr * (1,)).parseString(text).pprint() - """ +class _MultipleMatch(ParseElementEnhance): def __init__( self, expr, stopOn=None): - super(OneOrMore, self).__init__(expr) + super(_MultipleMatch, self).__init__(expr) ender = stopOn if isinstance(ender, basestring): ender = ParserElement._literalStringClass(ender) @@ -3778,6 +3743,32 @@ class OneOrMore(ParseElementEnhance): pass return loc, tokens + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ def __str__( self ): if hasattr(self,"name"): @@ -3793,7 +3784,7 @@ class OneOrMore(ParseElementEnhance): ret.saveAsList = True return ret -class ZeroOrMore(OneOrMore): +class ZeroOrMore(_MultipleMatch): """ Optional repetition of zero or more of the given expression. diff --git a/src/unitTests.py b/src/unitTests.py index 1f7964e..b80a9f4 100644 --- a/src/unitTests.py +++ b/src/unitTests.py @@ -2275,9 +2275,53 @@ class OptionalEachTest(ParseTestCase): assert modifiers == "with foo=bar bing=baz using id-deadbeef" assert not modifiers == "with foo=bar bing=baz using id-deadbeef using id-feedfeed" + def runTest3(self): + from pyparsing import Literal,Suppress,ZeroOrMore,OneOrMore + + foo = Literal('foo') + bar = Literal('bar') + + openBrace = Suppress(Literal("{")) + closeBrace = Suppress(Literal("}")) + + exp = openBrace + (OneOrMore(foo)("foo") & ZeroOrMore(bar)("bar")) + closeBrace + + tests = """\ + {foo} + {bar foo bar foo bar foo} + """.splitlines() + for test in tests: + test = test.strip() + if not test: + continue + result = exp.parseString(test) + print(test, '->', result.asList()) + assert result.asList() == test.strip("{}").split(), "failed to parse Each expression %r" % test + print(result.dump()) + + try: + result = exp.parseString("{bar}") + assert False, "failed to raise exception when required element is missing" + except ParseException as pe: + pass + + def runTest4(self): + from pyparsing import pyparsing_common, ZeroOrMore, Group + + expr = ((~pyparsing_common.iso8601_date + pyparsing_common.integer("id")) + & ZeroOrMore(Group(pyparsing_common.iso8601_date)("date*"))) + + expr.runTests(""" + 1999-12-31 100 2001-01-01 + 42 + """) + + def runTest(self): self.runTest1() self.runTest2() + self.runTest3() + self.runTest4() class SumParseResultsTest(ParseTestCase): def runTest(self): -- cgit v1.2.1