diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2020-11-06 21:03:04 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2020-11-06 21:05:25 -0300 |
commit | 436939628ff1c5bbc37d5c91c4a7c68d83f12f49 (patch) | |
tree | 6930fb0cb46774c79845956a98aee7760504a208 | |
parent | fa55f672dfe9e20cadfef5b4ac8bb14a691277f8 (diff) |
Add optional subvalues
-rw-r--r-- | abdl/__init__.py | 10 | ||||
-rw-r--r-- | abdl/_vm.py | 7 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | testing/test_abdl.py | 7 |
4 files changed, 21 insertions, 5 deletions
diff --git a/abdl/__init__.py b/abdl/__init__.py index efcd3df..7208e44 100644 --- a/abdl/__init__.py +++ b/abdl/__init__.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -"""A Boneless Datastructure Language, version 2.1. +"""A Boneless Datastructure Language, version 2.2. ABDL expressions are regex-like constructs for matching and validating object structures. They can be used with JSON and similar formats, and even @@ -76,7 +76,11 @@ Language Reference: the enclosed ABDL expression to the value (or index) being processed. A subvalue enables the ability to match multiple values on the same object, and accepts a value if and only the enclosed expression - matches the value. + matches the value. A subvalue can be made optional by the presence of + a ``?`` after the subvalue - in case of no match, it will just omit + the relevant keys in the result. Optional subvalues are unrelated to + non-validating syntax elements (see below), they just use the same + syntax. Some syntax elements can be validating or non-validating. Validating syntax elements will raise a :py:exc:`abdl.exceptions.ValidationError` @@ -100,7 +104,7 @@ Language Reference: arrow ::= '->' keymatch ::= '[' {predicate} abdlexpression ']' - subvalue ::= '(' {predicate} abdlexpression ')' + subvalue ::= '(' {predicate} abdlexpression ')' ['?'] For a description of the terminals "parameter", "literal", "regex" and "predicate", see "Syntax Elements of ABDL Expressions" above. diff --git a/abdl/_vm.py b/abdl/_vm.py index 2f76586..c6cd7be 100644 --- a/abdl/_vm.py +++ b/abdl/_vm.py @@ -162,7 +162,7 @@ class ValueSubtree(PatternElement): def __init__(self, toks): self.key = toks[0] - self.skippable = toks[1] == '?' + self.optional = toks[1] == '?' def on_not_in_key(self, frame, path, defs): assert not path[-1].empty @@ -171,8 +171,13 @@ class ValueSubtree(PatternElement): return True def _filter(self, parent, defs): + has_results = False for pair in match_helper(self.key, defs, parent): + has_results = True yield (pair, parent) + # support for optional subtrees + if self.optional and not has_results: + yield ({}, parent) def collect_params(self, res: list): for sub in self.key: diff --git a/setup.py b/setup.py index 163b9a3..8a4e81a 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,3 @@ import setuptools -setuptools.setup(name="gan0f74bd87a23b515b45da7e6f5d9cc82380443dab", version="2.1.4", packages=["abdl"], install_requires=["pyparsing >= 2.4.2"]) +setuptools.setup(name="gan0f74bd87a23b515b45da7e6f5d9cc82380443dab", version="2.2.0", packages=["abdl"], install_requires=["pyparsing >= 2.4.2"]) diff --git a/testing/test_abdl.py b/testing/test_abdl.py index d28658a..79a0edf 100644 --- a/testing/test_abdl.py +++ b/testing/test_abdl.py @@ -262,6 +262,13 @@ def test_empty(foo, pat): yield {} assert all(LogAndCompare(pat.match(foo), deep(foo))) +def test_optional_value_subtree(): + pat = abdl.compile("(->foo'foo'?)?(->bar'bar')") + matcher = pat.match({'foo': 1, 'bar': 2}) + assert list(matcher) == [{'foo': ('foo', 1), 'bar': ('bar', 2)}] + matcher = pat.match({'bar': 2}) + assert list(matcher) == [{'bar': ('bar', 2)}] + # FIXME #@hypothesis.given(objtree, st.text()) #def test_exhaustive(foo, pat): |