diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2021-02-11 00:52:25 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2021-02-11 00:52:25 -0300 |
commit | abaee464936a821568c3fdbd52b9aadd3adb6d0f (patch) | |
tree | 8a8b5c271f0b8b67eeb77e94a68ecf73ffc4e1f9 /src | |
parent | 69652efe8ad9738a94fef571c8b81e342f96e7b4 (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.rs | 5 | ||||
-rw-r--r-- | src/parser.rs | 9 | ||||
-rw-r--r-- | src/vm.rs | 218 |
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") } |