diff options
author | Michael Milton <ttmigueltt@gmail.com> | 2020-06-09 15:09:21 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-09 00:09:21 -0500 |
commit | 17aaf616a752990327ec51f41d2ac4c4a0871215 (patch) | |
tree | 8a96779b8b238c8eb1c80783a1cd4574c1e14a06 | |
parent | 1181390886f39240a98977348bd5b9030986f98d (diff) | |
download | pyparsing-git-17aaf616a752990327ec51f41d2ac4c4a0871215.tar.gz |
Additional configuration for `skipWhitespace` and `leaveWhitespace` (#219)
* Add .ignoreWhitespace() method
* Add recursive arg to leave- and ignoreWhitespace(), with tests
* Add tests and implementation of the recursive flag
-rw-r--r-- | pyparsing/core.py | 71 | ||||
-rw-r--r-- | tests/test_simple_unit.py | 91 |
2 files changed, 148 insertions, 14 deletions
diff --git a/pyparsing/core.py b/pyparsing/core.py index 431e656..8f2979e 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -1343,11 +1343,23 @@ class ParserElement: """ return Suppress(self) - def leaveWhitespace(self): + def ignoreWhitespace(self, recursive=True): + """ + Enables the skipping of whitespace before matching the characters in the + :class:`ParserElement`'s defined pattern. + + :param recursive: If true (the default), also enable whitespace skipping in child elements (if any) + """ + self.skipWhitespace = True + return self + + def leaveWhitespace(self, recursive=True): """ Disables the skipping of whitespace before matching the characters in the :class:`ParserElement`'s defined pattern. This is normally only used internally by the pyparsing module, but may be needed in some whitespace-sensitive grammars. + + :param recursive: If true (the default), also disable whitespace skipping in child elements (if any) """ self.skipWhitespace = False return self @@ -2995,13 +3007,29 @@ class ParseExpression(ParserElement): self.strRepr = None return self - def leaveWhitespace(self): - """Extends ``leaveWhitespace`` defined in base class, and also invokes ``leaveWhitespace`` on - all contained expressions.""" - self.skipWhitespace = False - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.leaveWhitespace() + def leaveWhitespace(self, recursive=True): + """ + Extends ``leaveWhitespace`` defined in base class, and also invokes ``leaveWhitespace`` on + all contained expressions. + """ + super().leaveWhitespace(recursive) + + if recursive: + self.exprs = [e.copy() for e in self.exprs] + for e in self.exprs: + e.leaveWhitespace(recursive) + return self + + def ignoreWhitespace(self, recursive=True): + """ + Extends ``ignoreWhitespace`` defined in base class, and also invokes ``leaveWhitespace`` on + all contained expressions. + """ + super().ignoreWhitespace(recursive) + if recursive: + self.exprs = [e.copy() for e in self.exprs] + for e in self.exprs: + e.ignoreWhitespace(recursive) return self def ignore(self, other): @@ -3672,11 +3700,22 @@ class ParseElementEnhance(ParserElement): else: raise ParseException("", loc, self.errmsg, self) - def leaveWhitespace(self): - self.skipWhitespace = False - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.leaveWhitespace() + def leaveWhitespace(self, recursive=True): + super().leaveWhitespace(recursive) + + if recursive: + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace(recursive) + return self + + def ignoreWhitespace(self, recursive=True): + super().ignoreWhitespace(recursive) + + if recursive: + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.ignoreWhitespace(recursive) return self def ignore(self, other): @@ -4283,10 +4322,14 @@ class Forward(ParseElementEnhance): ret = super().__or__(other) return ret - def leaveWhitespace(self): + def leaveWhitespace(self, recursive=True): self.skipWhitespace = False return self + def ignoreWhitespace(self, recursive=True): + self.skipWhitespace = True + return self + def streamline(self): if not self.streamlined: self.streamlined = True diff --git a/tests/test_simple_unit.py b/tests/test_simple_unit.py index 15edc33..8682f0f 100644 --- a/tests/test_simple_unit.py +++ b/tests/test_simple_unit.py @@ -568,6 +568,97 @@ class TestCommonHelperExpressions(PyparsingExpressionTestCase): ] +class TestWhitespaceMethods(PyparsingExpressionTestCase): + tests = [ + # These test the single-element versions + PpTestSpec( + desc="The word foo", + expr=pp.Literal("foo").ignoreWhitespace(), + text=" foo ", + expected_list=["foo"], + ), + PpTestSpec( + desc="The word foo", + expr=pp.Literal("foo").leaveWhitespace(), + text=" foo ", + expected_fail_locn=0, + ), + PpTestSpec( + desc="The word foo", + expr=pp.Literal("foo").ignoreWhitespace(), + text="foo", + expected_list=["foo"], + ), + PpTestSpec( + desc="The word foo", + expr=pp.Literal("foo").leaveWhitespace(), + text="foo", + expected_list=["foo"], + ), + # These test the composite elements + PpTestSpec( + desc="If we recursively leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + expr=pp.And( + [ + pp.Literal(" foo").ignoreWhitespace(), + pp.Literal(" bar").ignoreWhitespace(), + ] + ).leaveWhitespace(recursive=True), + text=" foo bar", + expected_list=[" foo", " bar"], + ), + # + PpTestSpec( + desc="If we recursively ignore whitespace in our parsing, this whitespace-dependent grammar will fail, even if the children themselves keep whitespace", + expr=pp.And( + [ + pp.Literal(" foo").leaveWhitespace(), + pp.Literal(" bar").leaveWhitespace(), + ] + ).ignoreWhitespace(recursive=True), + text=" foo bar", + expected_fail_locn=1, + ), + PpTestSpec( + desc="If we leave whitespace on the parent, but it isn't recursive, this whitespace-dependent grammar will fail", + expr=pp.And( + [ + pp.Literal(" foo").ignoreWhitespace(), + pp.Literal(" bar").ignoreWhitespace(), + ] + ).leaveWhitespace(recursive=False), + text=" foo bar", + expected_fail_locn=5, + ), + # These test the Enhance classes + PpTestSpec( + desc="If we recursively leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + expr=pp.Optional(pp.Literal(" foo").ignoreWhitespace()).leaveWhitespace( + recursive=True + ), + text=" foo", + expected_list=[" foo"], + ), + # + PpTestSpec( + desc="If we ignore whitespace on the parent, but it isn't recursive, parsing will fail because we skip to the first character 'f' before the internal expr can see it", + expr=pp.Optional(pp.Literal(" foo").leaveWhitespace()).ignoreWhitespace( + recursive=True + ), + text=" foo", + expected_list=[], + ), + # PpTestSpec( + # desc="If we leave whitespace on the parent, this whitespace-dependent grammar will succeed, even if the children themselves skip whitespace", + # expr=pp.Optional(pp.Literal(" foo").ignoreWhitespace()).leaveWhitespace( + # recursive=False + # ), + # text=" foo", + # expected_list=[] + # ), + ] + + def _get_decl_line_no(cls): import inspect |