summary refs log tree commit diff stats
path: root/abdl
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 /abdl
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.
Diffstat (limited to 'abdl')
-rw-r--r--abdl/__init__.py2
-rw-r--r--abdl/_vm.py16
2 files changed, 14 insertions, 4 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)