diff --git a/CHANGES b/CHANGES index 4b0ba1e5..5db5bb7d 100644 --- a/CHANGES +++ b/CHANGES @@ -11,8 +11,11 @@ Version 3.0.7 - or WordEnd instead of just taking the default value. Originally posted as a question by Parag on StackOverflow, good catch! +- Fixed bug #350, in which White expressions could fail to match due to + unintended whitespace-skipping. Reported by Fu Hanxi, thank you! + - Fixed bug in ParserElement.run_tests where comments would be displayed - using with_line_numbers, even if with_line_numbers was set to False. + using with_line_numbers. - Added optional "min" and "max" arguments to `delimited_list`. PR submitted by Marius, thanks! diff --git a/pyparsing/__init__.py b/pyparsing/__init__.py index 2d45cfa1..4cc4f959 100644 --- a/pyparsing/__init__.py +++ b/pyparsing/__init__.py @@ -126,7 +126,7 @@ def __repr__(self): __version_info__ = version_info(3, 0, 7, "final", 0) -__version_time__ = "02 Jan 2022 22:04 UTC" +__version_time__ = "02 Jan 2022 22:56 UTC" __version__ = __version_info__.__version__ __versionTime__ = __version_time__ __author__ = "Paul McGuire " diff --git a/pyparsing/core.py b/pyparsing/core.py index 53cca4c5..cb9ac9ac 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -3339,7 +3339,7 @@ def __init__(self, ws: str = " \t\r\n", min: int = 1, max: int = 0, exact: int = super().__init__() self.matchWhite = ws self.set_whitespace_chars( - "".join(c for c in self.whiteChars if c not in self.matchWhite), + "".join(c for c in self.whiteStrs if c not in self.matchWhite), copy_defaults=True, ) # self.leave_whitespace() @@ -3780,11 +3780,14 @@ def __init__(self, exprs_arg: IterableType[ParserElement], savelist: bool = True super().__init__(exprs, savelist) if self.exprs: self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - self.set_whitespace_chars( - self.exprs[0].whiteChars, - copy_defaults=self.exprs[0].copyDefaultWhiteChars, - ) - self.skipWhitespace = self.exprs[0].skipWhitespace + if not isinstance(self.exprs[0], White): + self.set_whitespace_chars( + self.exprs[0].whiteChars, + copy_defaults=self.exprs[0].copyDefaultWhiteChars, + ) + self.skipWhitespace = self.exprs[0].skipWhitespace + else: + self.skipWhitespace = False else: self.mayReturnEmpty = True self.callPreparse = True @@ -3913,7 +3916,9 @@ def streamline(self) -> ParserElement: if self.exprs: self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) self.saveAsList = any(e.saveAsList for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) + self.skipWhitespace = all( + e.skipWhitespace and not isinstance(e, White) for e in self.exprs + ) else: self.saveAsList = False return self @@ -4069,7 +4074,9 @@ def streamline(self) -> ParserElement: if self.exprs: self.saveAsList = any(e.saveAsList for e in self.exprs) self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) + self.skipWhitespace = all( + e.skipWhitespace and not isinstance(e, White) for e in self.exprs + ) else: self.saveAsList = False self.mayReturnEmpty = True diff --git a/tests/test_unit.py b/tests/test_unit.py index 1da1a638..b92cc9cc 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -217,6 +217,20 @@ def testScanStringWithOverlap(self): msg="scanString with overlaps failed", ) + def testCombineWithResultsNames(self): + # test case reproducing Issue #350 + from pyparsing import White, alphas, Word + + parser = White(' \t').set_results_name('indent') + Word(alphas).set_results_name('word') + result = parser.parse_string(' test') + print(result.dump()) + self.assertParseResultsEquals(result, [' ', 'test'], {'indent': ' ', 'word': 'test'}) + + parser = White(' \t') + Word(alphas).set_results_name('word') + result = parser.parse_string(' test') + print(result.dump()) + self.assertParseResultsEquals(result, [' ', 'test'], {'word': 'test'}) + def testTransformString(self): make_int_with_commas = ppc.integer().addParseAction( lambda t: "{:,}".format(t[0])