summary refs log tree commit diff stats
path: root/src/vm/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/mod.rs')
-rw-r--r--src/vm/mod.rs66
1 files changed, 50 insertions, 16 deletions
diff --git a/src/vm/mod.rs b/src/vm/mod.rs
index 8122778..dca15a9 100644
--- a/src/vm/mod.rs
+++ b/src/vm/mod.rs
@@ -95,6 +95,8 @@ pub(crate) enum PatternElement {
         /// expected value of this entry.
         name_and_value: These<usize, Value>,
     },
+    /// Marks the end of pattern iteration and the start of subtrees (if any).
+    SubtreeMarker,
     /// A value subtree is a subtree for values.
     ///
     /// It is applied *after* tags, and thus any value subtrees come last in
@@ -102,10 +104,10 @@ pub(crate) enum PatternElement {
     ValueSubtree {
         /// The proto index of the subtree.
         index: usize,
-        /// Whether to allow this value subtree to produce no results.
+        /// Whether to allow this value subtree to match nothing.
         ///
         /// By default, a datafu pattern only matches a tree if every branch of
-        /// the tree produces results. This enables opting out of that.
+        /// the tree matches something. This enables opting out of that.
         optional: bool,
     },
 }
@@ -292,15 +294,16 @@ pub(crate) enum SerdeObject<'de> {
 }
 
 impl<'de> SerdeObject<'de> {
-    fn check<E: serde::de::Error>(self, ty: Option<Type>) -> Result<Self, E> {
+    /// Checks the type of this object.
+    fn check<E: serde::de::Error>(&self, ty: Option<Type>) -> Result<(), E> {
         let ty = match ty {
-            None => return Ok(self),
+            None => return Ok(()),
             Some(ty) => ty,
         };
         match (ty, self) {
             | (Type::Any, v)
             | (Type::IgnoredAny, v)
-            => Ok(v),
+            => Ok(()),
             | (Type::Bool, v @ SerdeObject::Bool(_))
             | (Type::I8, v @ SerdeObject::I8(_))
             | (Type::I16, v @ SerdeObject::I16(_))
@@ -324,7 +327,7 @@ impl<'de> SerdeObject<'de> {
             | (Type::Unit, v @ SerdeObject::Unit)
             | (Type::Seq, v @ SerdeObject::Seq(_))
             | (Type::Map, v @ SerdeObject::Map(_))
-            => Ok(v),
+            => Ok(()),
             _ => todo!(),
         }
     }
@@ -358,8 +361,6 @@ pub(crate) struct Interpreter<'pat, 'state, O: Serialize> {
     error: &'state mut Option<crate::errors::MatchError>,
     /// The current interpreter frames.
     frames: &'state mut Vec<Frame<'pat>>,
-    ///// The final output.
-    //output: &'state Cell<Pack<'pat, 'de>>,
 }
 
 pub(crate) struct Frame<'pat> {
@@ -367,10 +368,12 @@ pub(crate) struct Frame<'pat> {
     ops: &'pat [PatternElement],
     /// The instruction index being processed.
     iar: Option<usize>,
-    /// How many steps this frame has failed to match.
-    overstep: Option<usize>,
-    ///// Elements collected while processing this frame?
-    //path: Pack<'pat, 'de>,
+    /// How many steps this frame has not actually advanced for.
+    ///
+    /// This is used at end of frame and on match failure.
+    overstep: usize,
+    /// Whether this frame matches the data so far.
+    matches: bool,
 }
 
 impl<'pat, 'state, O: Serialize> Interpreter<'pat, 'state, O> {
@@ -384,7 +387,8 @@ impl<'pat, 'state, O: Serialize> Interpreter<'pat, 'state, O> {
         frames.push(Frame {
             ops: &pat.protos[0],
             iar: None,
-            overstep: None,
+            overstep: 0,
+            matches: true,
             //path: Default::default(),
         });
         Self {
@@ -461,12 +465,42 @@ impl<'pat> Frame<'pat> {
     ///
     /// Panics if called on a non-matching frame or if iteration hasn't begun.
     fn op(&self) -> PatternElement {
-        assert!(self.matches(), "op() called on non-matching frame");
+        assert!(self.active(), "op() called on inactive frame");
+        self.ops[self.iar.expect("ops[iar]")]
+    }
+
+    /// Counts the number of *active* subtrees, if any.
+    ///
+    /// # Panics
+    ///
+    /// Panics if iteration hasn't begun.
+    fn num_subtrees(&self) -> Option<usize> {
+        let iar = self.iar.expect("iar");
+        // check if there are any subtrees
+        matches!(
+            self.ops[iar],
+            | PatternElement::ValueSubtree { .. }
+            | PatternElement::SubtreeMarker
+        ).then(|| {
+            // count the number of subtrees
+            self.ops[0..=iar].iter().rev().take_while(|x| {
+                matches!(x, PatternElement::ValueSubtree { .. })
+            }).count()
+        })
+    }
+
+    /// Returns the raw instruction.
+    ///
+    /// # Panics
+    ///
+    /// Panics if iteration hasn't begun.
+    fn raw_op(&self) -> PatternElement {
         self.ops[self.iar.expect("ops[iar]")]
     }
 
-    fn matches(&self) -> bool {
-        self.overstep.is_none()
+    /// Returns whether this frame is active (not overstepped).
+    fn active(&self) -> bool {
+        self.overstep == 0
     }
 
     /// Rewinds the instruction address register.