summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2021-02-11 00:52:25 -0300
committerSoniEx2 <endermoneymod@gmail.com>2021-02-11 00:52:25 -0300
commitabaee464936a821568c3fdbd52b9aadd3adb6d0f (patch)
tree8a8b5c271f0b8b67eeb77e94a68ecf73ffc4e1f9 /src
parent69652efe8ad9738a94fef571c8b81e342f96e7b4 (diff)
Partially implement VM
The following are now implemented:

- [x] Arrow
- [x] StringKey
- [x] RegexKey
- [ ] KeySubtree
- [ ] ValueSubtree
- [x] Ident
- [ ] Param
- [x] ApplyPredicate
- [x] End
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs5
-rw-r--r--src/parser.rs9
-rw-r--r--src/vm.rs218
3 files changed, 211 insertions, 21 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 2407115..cbd32b2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -127,6 +127,11 @@ pub trait PatternTypes {
         left: RefOwn<'_, Self::Ref, Self::Own>,
         right: RefOwn<'_, Self::Ref, Self::Own>
     ) -> bool;
+
+    /// Returns the value as an &str.
+    fn as_str<'b>(
+        value: RefOwn<'b, Self::Ref, Self::Own>
+    ) -> Option<&'b str>;
 }
 
 // TODO
diff --git a/src/parser.rs b/src/parser.rs
index 00fbcb1..eb54c86 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -623,6 +623,15 @@ mod tests {
         ) -> bool {
             false
         }
+
+        fn as_str<'b>(
+            item: RefOwn<'b, Self::Ref, Self::Own>
+        ) -> Option<&'b str> {
+            match item {
+                RefOwn::Str(key) => Some(key),
+                _ => None,
+            }
+        }
     }
 
     #[test]
diff --git a/src/vm.rs b/src/vm.rs
index a02010f..62fb074 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -156,6 +156,10 @@ impl<'a, 'b, T: PatternTypes> HolderState<'a, 'b, T> {
         matches!(self, HolderState::EmptyKey | HolderState::EmptySubtree)
     }
 
+    fn has_value(&self) -> bool {
+        !self.is_empty()
+    }
+
     fn is_subtree(&self) -> bool {
         matches!(self, HolderState::EmptySubtree | HolderState::Subtree {..})
     }
@@ -168,34 +172,85 @@ impl<'a, 'b, T: PatternTypes> HolderState<'a, 'b, T> {
             _ => None
         }
     }
+
+    fn key(&self) -> Option<RefOwn<'b, T::Ref, T::Own>> {
+        match *self {
+            HolderState::Key((key, _)) => Some(key),
+            _ => None
+        }
+    }
+
+    fn clear(&mut self) {
+        match *self {
+            HolderState::Key(_) => *self = HolderState::EmptyKey,
+            HolderState::Subtree(_, _) => *self = HolderState::EmptySubtree,
+            HolderState::Value(_) => unreachable!(),
+            _ => {},
+        };
+        assert!(self.is_empty());
+    }
 }
 
 /// Stores a single match and associated metadata.
 ///
 /// A single match is generally a key-value pair, but may be a collection of
-/// named pairs in the case of subtree matches.
+/// named pairs in the case of subtree matches, or just a value for the initial
+/// holder.
 struct Holder<'a, 'b, T: PatternTypes> {
      name: Option<&'a str>,
      value: HolderState<'a, 'b, T>,
      parent: Option<RefOwn<'b, T::Ref, T::Own>>,
      iterator: Option<Box<dyn Iterator<Item=KVPair<'b, T>> + 'b>>,
-     filters: Vec<Box<dyn for<'c> Fn(&'c mut HolderState<'a, 'b, T>) + 'a>>,
+     filters: Vec<Box<dyn (for<'c> Fn(&'c mut HolderState<'a, 'b, T>) -> Result<(), MatchError>) + 'a>>,
 }
 
 impl<'a, 'b, T: PatternTypes> Holder<'a, 'b, T> {
-    fn next(&mut self) -> Option<Result<(), MatchError>> {
-        // FIXME what even is the point of this?
-        if let Self { value: ref mut v, iterator: Some(ref mut it), .. } = self {
-            let is_subtree = v.is_subtree();
-            *v = match it.next() {
-                Some(pair) => HolderState::Key(pair),
-                None => return None
-            };
-            // just try to make sure the type doesn't change.
-            // (and that if we get to this point the result isn't empty.)
-            assert!(!v.is_empty() && v.is_subtree() == is_subtree);
+    fn next(&mut self) -> Result<bool, MatchError> {
+        self.ensure_iterator()?;
+        match self {
+            Self {
+                value: ref mut v,
+                iterator: Some(ref mut it),
+                ref filters,
+                ..
+            } => {
+                let is_subtree = v.is_subtree();
+                let mut next_v;
+                loop {
+                    next_v = match it.next() {
+                        Some(pair) => HolderState::Key(pair),
+                        None => return Ok(false)
+                    };
+                    for filter in filters {
+                        filter(&mut next_v)?;
+                        if next_v.is_empty() {
+                            break;
+                        }
+                    }
+                    if next_v.has_value() {
+                        break;
+                    }
+                }
+                assert!(next_v.has_value());
+                assert!(next_v.is_subtree() == is_subtree);
+                *v = next_v;
+                Ok(true)
+            },
+            _ => unreachable!()
         }
-        Some(Ok(()))
+    }
+
+    /// Ensure `self.iterator.is_some()`, creating an iterator if necessary.
+    fn ensure_iterator(&mut self) -> Result<(), MatchError> {
+        if self.iterator.is_none() {
+            let iter = T::pairs(self.parent.unwrap());
+            if iter.is_none() {
+                return Err(MatchError::UnsupportedOperation);
+            }
+            self.iterator = iter;
+        }
+        assert!(self.iterator.is_some());
+        Ok(())
     }
 }
 
@@ -216,6 +271,60 @@ pub struct Matcher<'a, 'b, T: PatternTypes> {
     frame: Frame<'a, 'b, T>,
 }
 
+// TODO:
+//
+// [x] Arrow
+// [x] StringKey
+// [x] RegexKey
+// [ ] KeySubtree
+// [ ] ValueSubtree
+// [x] Ident
+// [ ] Param
+// [x] ApplyPredicate
+// [x] End
+
+/// Helper for `PatternElement::StringKey`.
+fn on_string_key<'a, 'b, T: PatternTypes>(
+    matcher: &mut Matcher<'a, 'b, T>,
+    id: usize,
+    skippable: bool,
+) -> Result<bool, MatchError> {
+    let path = matcher.frame.path.last_mut().unwrap();
+    assert!(path.iterator.is_none());
+    let key = &matcher.defs.strings[id];
+    let iter = T::get(path.parent.unwrap(), RefOwn::Str(key));
+    match iter {
+        None => Err(MatchError::UnsupportedOperation),
+        Some(opt) => {
+            path.iterator = Some(Box::new(opt.into_iter()));
+            Ok(true)
+        }
+    }
+}
+
+/// Helper for `PatternElement::RegexKey`.
+fn on_regex_key<'a, 'b, T: PatternTypes>(
+    matcher: &mut Matcher<'a, 'b, T>,
+    id: usize,
+    skippable: bool,
+) -> Result<bool, MatchError> {
+    matcher.frame.path.last_mut().unwrap().ensure_iterator()?;
+    let re = &matcher.defs.regices[id];
+    let path = matcher.frame.path.last_mut().unwrap();
+    path.filters.push(Box::new(move |value| {
+        let s = T::as_str(value.key().unwrap());
+        match (s.map_or(false, |s| re.is_match(s)), skippable) {
+            (true, _) => Ok(()),
+            (false, true) => {
+                value.clear();
+                Ok(())
+            },
+            (false, false) => Err(MatchError::ValidationError),
+        }
+    }));
+    Ok(true)
+}
+
 impl<'a, 'b, T: PatternTypes> Matcher<'a, 'b, T> {
     pub(crate) fn new(obj: RefOwn<'b, T::Ref, T::Own>, defs: &'a PatternConstants<T>, proto: usize, rlimit: usize) -> Result<Self, MatchError> {
         let depth = rlimit.checked_sub(1).ok_or(MatchError::StackOverflow)?;
@@ -233,6 +342,7 @@ impl<'a, 'b, T: PatternTypes> Matcher<'a, 'b, T> {
                 } else {
                     let mut holder = Holder::default();
                     holder.value = HolderState::Value(obj);
+                    holder.iterator = Some(Box::new(std::iter::empty()));
                     vec![holder]
                 },
                 in_key: false,
@@ -243,8 +353,49 @@ impl<'a, 'b, T: PatternTypes> Matcher<'a, 'b, T> {
     fn on_in_key(&mut self) -> Result<bool, MatchError> {
         match self.frame.op() {
             PatternElement::End => {
-                todo!()
-            }
+                let path = self.frame.path.last_mut().unwrap();
+                if path.next()? {
+                    Ok(false)
+                } else {
+                    drop(path);
+                    self.frame.path.pop().unwrap();
+                    // stop at previous End, or start of frame
+                    while self.frame.prev() {
+                        if matches!(self.frame.op(), PatternElement::End) {
+                            break;
+                        }
+                    }
+                    // is start of frame?
+                    if !self.frame.prev() {
+                        self.frame.path.clear();
+                    }
+                    Ok(true)
+                }
+            },
+            PatternElement::ApplyPredicate(id, skippable, _) => {
+                // failing on T::get() is already handled, but we may need a
+                // T::pairs(). construct it here.
+                self.frame.path.last_mut().unwrap().ensure_iterator()?;
+                let pred = &self.defs.predicates[id];
+                let path = self.frame.path.last_mut().unwrap();
+                path.filters.push(Box::new(move |value| {
+                    match (pred(value.value().unwrap()), skippable) {
+                        (true, _) => Ok(()),
+                        (false, true) => {
+                            value.clear();
+                            Ok(())
+                        },
+                        (false, false) => Err(MatchError::ValidationError),
+                    }
+                }));
+                Ok(true)
+            },
+            PatternElement::StringKey(id, skippable) => {
+                on_string_key(self, id, skippable)
+            },
+            PatternElement::RegexKey(id, skippable) => {
+                on_regex_key(self, id, skippable)
+            },
             _ => unreachable!("on_in_key")
         }
     }
@@ -252,17 +403,42 @@ impl<'a, 'b, T: PatternTypes> Matcher<'a, 'b, T> {
     fn on_not_in_key(&mut self) -> Result<bool, MatchError> {
         match self.frame.op() {
             PatternElement::Arrow => {
-                assert!(!self.frame.path.last().expect("path").value.is_empty());
+                // this *should* always pass.
+                assert!(self.frame.path.last().unwrap().iterator.is_some());
                 let mut holder = Holder::default();
-                holder.parent = self.frame.path.last().expect("path").value.value();
+                holder.parent = self.frame.path.last().unwrap().value.value();
+                assert!(holder.parent.is_some());
                 self.frame.path.push(holder);
                 Ok(false)
             },
             PatternElement::Identifier(id) => {
                 let name = self.defs.strings.get(id).map(|s| &**s);
-                self.frame.path.last_mut().expect("path").name = name;
-                todo!()
-                //Ok(true)
+                let path = self.frame.path.last_mut().unwrap();
+                path.name = name;
+                assert!(path.iterator.is_none());
+                // we don't actually create the iterator here,
+                // as we may still wanna use T::get() instead.
+                Ok(true)
+            },
+            PatternElement::ApplyPredicate(id, skippable, _) => {
+                assert!(self.frame.path.len() == 1);
+                let pred = &self.defs.predicates[id];
+                let value = self.frame.path.last().unwrap().value.value();
+                match (pred(value.unwrap()), skippable) {
+                    (true, _) => Ok(false),
+                    (false, true) => {
+                        self.frame.path.clear();
+                        // any Ok(_) will do
+                        Ok(false)
+                    },
+                    (false, false) => Err(MatchError::ValidationError),
+                }
+            },
+            PatternElement::StringKey(id, skippable) => {
+                on_string_key(self, id, skippable)
+            },
+            PatternElement::RegexKey(id, skippable) => {
+                on_regex_key(self, id, skippable)
             },
             _ => unreachable!("on_not_in_key")
         }