diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2020-04-17 14:25:21 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2020-04-17 14:26:01 -0300 |
commit | b259b179f5ceba60a1d04fef07559c0b01720c31 (patch) | |
tree | a0315c67fddc7919e6d6892e8d1eb15f328057a6 | |
parent | 3f9f66712aaa071bd3bb32c46e1e4dc1fed13378 (diff) |
Fix bugs with empty and predicate-only matches
Also clarified the behaviour of empty matches, which match anything, but only once. This behaviour is required for consistency with the rest of the matching rules.
-rw-r--r-- | abdl/__init__.py | 2 | ||||
-rw-r--r-- | abdl/_vm.py | 16 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | testing/test_abdl.py | 2 |
4 files changed, 16 insertions, 6 deletions
diff --git a/abdl/__init__.py b/abdl/__init__.py index 547bb68..efcd3df 100644 --- a/abdl/__init__.py +++ b/abdl/__init__.py @@ -88,6 +88,8 @@ Language Reference: needs to be careful when writing code where such behaviour could result in a security vulnerability. + The empty pattern matches anything, but only does so once. + Syntax of ABDL Expressions: ABDL Expressions follow the given syntax, in (pseudo-)extended BNF:: diff --git a/abdl/_vm.py b/abdl/_vm.py index 5ab7efb..2f76586 100644 --- a/abdl/_vm.py +++ b/abdl/_vm.py @@ -255,7 +255,15 @@ class ApplyPredicate(PatternElement): def on_end(self, frame, path, defs, in_key): assert not in_key - raise NotImplementedError + res = {} + for holder in path: + if holder.subtree: + for name, pair in holder.match.items(): + res[name] = pair + elif holder.name is not None: + res[holder.name] = (holder.match, holder.value) + path.clear() + return (False, res) class End(PatternElement): """Pseudo-token, used to advance iteration.""" @@ -282,7 +290,8 @@ class End(PatternElement): elif holder.name is not None: res[holder.name] = (holder.match, holder.value) if not frame.prev(): - return (None, res) + # this should never happen + assert False return (True, res) @classmethod @@ -382,6 +391,7 @@ def match_helper(ops, defs, tree): frame = _Frame(ops) if not len(frame.ops): # no ops? + yield {} return # do nothing path = [Holder(value=tree, parent=None, iterator=iter(()))] @@ -390,8 +400,6 @@ def match_helper(ops, defs, tree): if not frame.next(): in_key, res = frame.current_op.on_end(frame, path, defs, in_key) yield res - if in_key is None: - return else: if in_key: in_key = frame.current_op.on_in_key(frame, path, defs) diff --git a/setup.py b/setup.py index 350ba95..163b9a3 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,3 @@ import setuptools -setuptools.setup(name="gan0f74bd87a23b515b45da7e6f5d9cc82380443dab", version="2.1.3", packages=["abdl"], install_requires=["pyparsing >= 2.4.2"]) +setuptools.setup(name="gan0f74bd87a23b515b45da7e6f5d9cc82380443dab", version="2.1.4", packages=["abdl"], install_requires=["pyparsing >= 2.4.2"]) diff --git a/testing/test_abdl.py b/testing/test_abdl.py index 65549ed..cbdcd9d 100644 --- a/testing/test_abdl.py +++ b/testing/test_abdl.py @@ -281,7 +281,7 @@ def test_multi_key_predicate(foo, pat): @hypothesis.given(objtree, st.just(abdl.compile(""))) def test_empty(foo, pat): def deep(foo): - yield from () + yield {} assert all(LogAndCompare(pat.match(foo), deep(foo))) # FIXME |