summary refs log tree commit diff stats
path: root/abdl/_vm.py
diff options
context:
space:
mode:
Diffstat (limited to 'abdl/_vm.py')
-rw-r--r--abdl/_vm.py40
1 files changed, 29 insertions, 11 deletions
diff --git a/abdl/_vm.py b/abdl/_vm.py
index 0ec1018..5ab7efb 100644
--- a/abdl/_vm.py
+++ b/abdl/_vm.py
@@ -41,6 +41,14 @@ class PatternElement:
         """
         raise RuntimeError(self)
 
+    def on_end(self, frame, path, defs, in_key):
+        """Called when the pattern has reached the end.
+
+        Returns the new value of in_key and a dict to be yielded, or
+        None and a dict to be yielded.
+        """
+        raise RuntimeError(self)
+
     def collect_params(self, res: list):
         """Appends parameter names used in this pattern to ``res``.
         """
@@ -245,6 +253,10 @@ class ApplyPredicate(PatternElement):
     def collect_params(self, res: list):
         res.append(self.key)
 
+    def on_end(self, frame, path, defs, in_key):
+        assert not in_key
+        raise NotImplementedError
+
 class End(PatternElement):
     """Pseudo-token, used to advance iteration."""
 
@@ -260,6 +272,19 @@ class End(PatternElement):
                 path.clear()
         return True
 
+    def on_end(self, frame, path, defs, in_key):
+        assert not path[-1].empty
+        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)
+        if not frame.prev():
+            return (None, res)
+        return (True, res)
+
     @classmethod
     def action(cls, toks):
         return [cls()]
@@ -356,24 +381,17 @@ def match_helper(ops, defs, tree):
     """
 
     frame = _Frame(ops)
+    if not len(frame.ops): # no ops?
+        return # do nothing
 
     path = [Holder(value=tree, parent=None, iterator=iter(()))]
     in_key = False
     while path:
         if not frame.next():
-            assert not path[-1].empty
-            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)
+            in_key, res = frame.current_op.on_end(frame, path, defs, in_key)
             yield res
-            assert len(path) == 1 or isinstance(frame.current_op, End)
-            if not frame.prev():
+            if in_key is None:
                 return
-            in_key = True
         else:
             if in_key:
                 in_key = frame.current_op.on_in_key(frame, path, defs)