diff --git a/CHANGES b/CHANGES index 0151f93b..ae3684f7 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,8 @@ Change Log Version 3.0.4 - --------------- +- Fixed bug in which Dict classes did not correctly return tokens as nested + ParseResults, reported by and fix identified by Bu Sun Kim, many thanks!!! Version 3.0.3 - diff --git a/pyparsing/core.py b/pyparsing/core.py index c8154686..3c27457e 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -5414,7 +5414,7 @@ def postParse(self, instring, loc, tokenlist): tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0], i) if self._asPythonDict: - return tokenlist.as_dict() + return [tokenlist.as_dict()] if self.resultsName else tokenlist.as_dict() else: return [tokenlist] if self.resultsName else tokenlist diff --git a/tests/test_unit.py b/tests/test_unit.py index f90e42ac..f7d6b171 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -2914,6 +2914,101 @@ def testParseResultsExtendWithParseResults(self): result1, expected, msg="issue with ParseResults.extend(ParseResults)" ) + def testParseResultsWithNestedNames(self): + from pyparsing import ( + Dict, + Literal, + Group, + Optional, + Regex, + QuotedString, + oneOf, + Or, + CaselessKeyword, + ZeroOrMore, + ) + + RELATION_SYMBOLS = "= > < >= <= <> ==" + + def _set_info(string, location, tokens): + for t in tokens: + try: + t["_info_"] = (string, location) + except TypeError: + pass + tokens["_info_"] = (string, location) + + def keywords(name): + words = "any all within encloses adj".split() + return Or(map(CaselessKeyword, words)) + + charString1 = Group(Regex(r'[^()=<>"/\s]+'))("identifier") + charString1.addParseAction(_set_info) + charString2 = Group(QuotedString('"', "\\"))("quoted") + charString2.addParseAction(_set_info) + + term = Group(charString1 | charString2) + modifier_key = charString1 + + # relations + comparitor_symbol = oneOf(RELATION_SYMBOLS) + named_comparitors = keywords("comparitors") + comparitor = Group(comparitor_symbol | named_comparitors)("comparitor") + comparitor.addParseAction(_set_info) + + def modifier_list1(key): + modifier = Dict( + Literal("/") + + Group(modifier_key(key))("name") + + Optional(comparitor_symbol("symbol") + term("value")) + )("modifier") + modifier.addParseAction(_set_info) + return ZeroOrMore(modifier)("modifier_list") + + def modifier_list2(key): + modifier = Dict( + Literal("/") + + Group(modifier_key(key))("name") + + Optional(comparitor_symbol("symbol") + term("value")), + asdict=True, + )("modifier") + modifier.addParseAction(_set_info) + return ZeroOrMore(modifier)("modifier_list") + + def modifier_list3(key): + modifier = Group( # this line is different from the others, must group to get results names + Dict( + Literal("/") + + Group(modifier_key(key))("name") + + Optional(comparitor_symbol("symbol") + term("value")) + ) + ) + modifier.addParseAction(_set_info) + return ZeroOrMore(modifier)("modifier_list") + + def modifier_list4(key): + modifier = Dict( + Literal("/") + + Group(modifier_key(key))("name") + + Optional(comparitor_symbol("symbol") + term("value")), + asdict=True, + ) + modifier.addParseAction(_set_info) + return ZeroOrMore(modifier)("modifier_list") + + for modifier_list_fn in ( + modifier_list1, + modifier_list2, + modifier_list3, + modifier_list4, + ): + modifier_parser = modifier_list_fn("default") + + result = modifier_parser.parseString("/respectaccents/ignoreaccents") + for r in result: + print(r) + print(r.get("_info_")) + def testParseResultsFromDict(self): """test helper classmethod ParseResults.from_dict()"""