summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2020-04-17 14:25:21 -0300
committerSoniEx2 <endermoneymod@gmail.com>2020-04-17 14:26:01 -0300
commitb259b179f5ceba60a1d04fef07559c0b01720c31 (patch)
treea0315c67fddc7919e6d6892e8d1eb15f328057a6
parent3f9f66712aaa071bd3bb32c46e1e4dc1fed13378 (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__.py2
-rw-r--r--abdl/_vm.py16
-rw-r--r--setup.py2
-rw-r--r--testing/test_abdl.py2
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