summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/errors.rs10
-rw-r--r--src/lib.rs35
-rw-r--r--src/pattern.rs11
-rw-r--r--src/vm.rs18
4 files changed, 60 insertions, 14 deletions
diff --git a/src/errors.rs b/src/errors.rs
index 4fc2144..ddc6a1a 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,8 +1,18 @@
 pub struct PatternError;
 
+/// Error type returned by the matcher.
 #[derive(Clone)]
 pub enum MatchError {
+    /// Returned if the pattern nests too deeply.
     StackOverflow,
+    /// Returned if the pattern rejects the input.
     ValidationError,
+    /// Returned if the pattern attempts an unsupported operation.
+    ///
+    /// In particular, if the PatternTypes doesn't support get or pairs for a
+    /// given value, this error will be returned. It can be treated as a
+    /// ValidationError, or as a bug in the pattern, at the user's discretion.
+    UnsupportedOperation,
+    /// Returned if an unspecified non-critical error occurred.
     Other
 }
diff --git a/src/lib.rs b/src/lib.rs
index 9e84de1..f41b4c6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,12 +5,41 @@ mod vm;
 
 pub use pattern::Pattern;
 
-// TODO
+// TODO investigate if this should be PatternTypes: Default
+/// Defines the types and operations used for matching.
 pub trait PatternTypes {
     /// The value type.
     type Value;
-    type Iter;
+
+    /// The owned key type. May be uninhabited.
+    // TODO replace with GATs.
+    type Key;
+
+    /// Returns an iterator over key-value pairs contained within an item, or
+    /// None if this operation is unsupported for the given value.
+    fn pairs<'b>(
+        item: &'b Self::Value
+    ) -> Option<Box<dyn Iterator<Item=(&'b Self::Value, &'b Self::Value)> + 'b>> {
+        // TODO remove these default impls that only exist for testing purposes
+        let x = None;
+        Some(Box::new(x.into_iter()))
+    }
+
+    /// Returns an optional key-value pair keyed by the given key, or None if
+    /// this operation is unsupported for the given value.
+    fn get<'a, 'b>(
+        item: &'b Self::Value,
+        key: &'a str
+    ) -> Option<Option<(&'b Self::Value, &'b Self::Value)>> {
+        // TODO remove these default impls that only exist for testing purposes
+        Some(None)
+    }
+
+    /// Returns whether two values are the same/equivalent. This must provide
+    /// the same guarantees as PartialEq. In fact, this is a replacement for
+    /// PartialEq for cases where it's not possible to just use PartialEq.
+    fn matches(left: &Self::Value, right: &Self::Value) -> bool;
 }
 
 // TODO
-pub type Predicate<T> = dyn (Fn(&<T as PatternTypes>::Value) -> bool) + Send + Sync;
+type Predicate<T> = dyn (Fn(&<T as PatternTypes>::Value) -> bool) + Send + Sync;
diff --git a/src/pattern.rs b/src/pattern.rs
index 8c57f00..0368a88 100644
--- a/src/pattern.rs
+++ b/src/pattern.rs
@@ -6,17 +6,20 @@ use crate::vm::PatternConstants;
 use crate::vm::MAX_CALLS;
 
 pub struct Pattern<T: PatternTypes> {
-    constants: PatternConstants<T>,
+    consts: PatternConstants<T>,
 }
 
 impl<T: PatternTypes> Pattern<T> {
     pub fn compile(s: &str) -> Result<Self, PatternError> {
         Ok(Self {
-            constants: parse(s)?
+            consts: parse(s)?
         })
     }
 
-    pub fn attempt_match<'a, 'b>(&'a self, value: &'b T::Value) -> Matcher<'a, 'b, T> {
-        Matcher::new(value, &self.constants, self.constants.protos.len() - 1, MAX_CALLS).ok().expect("datafu internal error: MAX_CALLS must not be 0")
+    pub fn attempt_match<'a, 'b>(
+        &'a self,
+        value: &'b T::Value
+    ) -> Matcher<'a, 'b, T> {
+        Matcher::new(value, &self.consts, self.consts.protos.len() - 1, MAX_CALLS).ok().expect("datafu internal error: MAX_CALLS must not be 0")
     }
 }
diff --git a/src/vm.rs b/src/vm.rs
index 3131571..595fac1 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -19,6 +19,10 @@ pub(crate) struct PatternConstants<T: PatternTypes> {
     pub(crate) strings: Vec<String>,
     pub(crate) regices: Vec<()/* TODO */>,
     pub(crate) predicates: Vec<Box<Predicate<T>>>,
+    // NOTE these are part of the constant pool and so have lifetime analogous
+    // to 'a (consistently used to indicate constant pool lifetime) when used
+    // elsewhere. In particular, they can't be yielded by the iterator.
+    pub(crate) defs: Vec<T::Value>,
 }
 
 /// A pattern element.
@@ -132,18 +136,16 @@ struct Holder<'a, 'b, T: PatternTypes> {
      name: Option<&'a str>,
      value: HolderState<'a, 'b, T>,
      parent: Option<&'b T::Value>,
-     //iterator: Box<dyn Iterator<Item=Result<HolderState<'a, 'b, T>, MatchError>> + Capture<'a> + Capture<'b>>,
-     iterator: Box<dyn FnMut() -> Option<Result<HolderState<'a, 'b, T>, MatchError>>>,
-     //iterator: T::Iter,
+     iterator: Box<dyn Iterator<Item=(&'b T::Value, &'b T::Value)> + 'b>,
+     filters: Vec<Box<dyn for<'c> Fn(&'c mut HolderState<'a, 'b, T>) + 'a>>,
 }
 
 impl<'a, 'b, T: PatternTypes> Holder<'a, 'b, T> {
     fn next(&mut self) -> Option<Result<(), MatchError>> {
         if let Self { value: ref mut v, iterator: ref mut it, .. } = self {
             let is_subtree = v.is_subtree();
-            *v = match it() {
-                Some(Ok(pair)) => pair,
-                Some(Err(e)) => return Some(Err(e)),
+            *v = match it.next() {
+                Some(pair) => HolderState::Key(pair),
                 None => return None
             };
             // just try to make sure the type doesn't change.
@@ -160,7 +162,9 @@ impl<'a, 'b, T: PatternTypes> Default for Holder<'a, 'b, T> {
             name: Default::default(),
             value: HolderState::EmptyKey,
             parent: Default::default(),
-            iterator: Box::new(|| None),
+            // TODO https://github.com/rust-lang/rfcs/issues/3059
+            iterator: Box::new(std::iter::empty()),
+            filters: Default::default(),
         }
     }
 }