use std::fmt::Debug; use mappable_rc::Mrc; use crate::ast::{Expr, Clause}; use crate::unwrap_or; use crate::utils::iter::box_empty; use crate::utils::{Side, Cache, mrc_derive, mrc_try_derive, to_mrc_slice}; use super::State; use super::split_at_max_vec::split_at_max_vec; /// Tuple with custom cloning logic #[derive(Debug, Eq, PartialEq, Hash)] pub struct CacheEntry<'a>(Mrc<[Expr]>, &'a SliceMatcherDnC); impl<'a> Clone for CacheEntry<'a> { fn clone(&self) -> Self { let CacheEntry(mrc, matcher) = self; CacheEntry(Mrc::clone(mrc), matcher) } } /// Matcher that applies a pattern to a slice via divide-and-conquer /// /// Upon construction, it selects the clause of highest priority, then /// initializes its internal state for matching that clause and delegates /// the left and right halves of the pattern to two submatchers. /// /// Upon matching, it uses a cache to accelerate the process of executing /// a pattern on the entire tree. #[derive(Clone, Eq)] pub struct SliceMatcherDnC { /// The entire pattern this will match pattern: Mrc<[Expr]>, /// The exact clause this can match clause: Mrc, /// Matcher for the parts of the pattern right from us right_subm: Option>, /// Matcher for the parts of the pattern left from us left_subm: Option>, /// Matcher for the body of this clause if it has one. /// Must be Some if pattern is (Auto, Lambda or S) body_subm: Option>, /// Matcher for the type of this expression if it has one (Auto usually does) /// Optional typ_subm: Option>, } impl PartialEq for SliceMatcherDnC { fn eq(&self, other: &Self) -> bool { self.pattern == other.pattern } } impl std::hash::Hash for SliceMatcherDnC { fn hash(&self, state: &mut H) { self.pattern.hash(state); } } impl SliceMatcherDnC { /// If this is true, `clause`, `typ_subm`, `body_subm` and `clause_qual_name` are meaningless. /// If it's false, it's also false for both side matchers. pub fn clause_is_vectorial(&self) -> bool { matches!(self.clause.as_ref(), Clause::Placeh{vec: Some(..), ..}) } /// If clause is a name, the qualified name this can match pub fn clause_qual_name(&self) -> Option> { if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} else {None} } /// If clause is a Placeh, the key in the state the match will be stored at pub fn state_key(&self) -> Option<&String> { if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None} } pub fn own_max_size(&self, total: usize) -> Option { if !self.clause_is_vectorial() { if total == self.len() {Some(total)} else {None} } else { let margin = self.min(Side::Left) + self.min(Side::Right); if margin + self.own_min_size() <= total {Some(total - margin)} else {None} } } pub fn own_min_size(&self) -> usize { if let Clause::Placeh { vec: Some((_, nonzero)), .. } = self.clause.as_ref() { if *nonzero {1} else {0} } else {self.len()} } /// Enumerate all valid subdivisions based on the reported size constraints of self and /// the two subranges pub fn valid_subdivisions(&self, range: Mrc<[Expr]> ) -> impl Iterator, Mrc<[Expr]>, Mrc<[Expr]>)> { let own_max = unwrap_or!(self.own_max_size(range.len()); return box_empty()); let own_min = self.own_min_size(); let lmin = self.min(Side::Left); let _lmax = self.max(Side::Left, range.len()); let rmin = self.min(Side::Right); let _rmax = self.max(Side::Right, range.len()); let full_len = range.len(); Box::new((own_min..=own_max).rev().flat_map(move |own_len| { let wiggle = full_len - lmin - rmin - own_len; let range = Mrc::clone(&range); (0..=wiggle).map(move |offset| { let first_break = lmin + offset; let second_break = first_break + own_len; let left = mrc_derive(&range, |p| &p[0..first_break]); let mid = mrc_derive(&range, |p| &p[first_break..second_break]); let right = mrc_derive(&range, |p| &p[second_break..]); (left, mid, right) }) })) } pub fn new(pattern: Mrc<[Expr]>) -> Self { let (clause, left_subm, right_subm) = mrc_try_derive(&pattern, |p| { if p.len() == 1 {Some(&p[0].0)} else {None} }).map(|e| (e, None, None)) .or_else(|| split_at_max_vec(Mrc::clone(&pattern)).map(|(left, _, right)| ( mrc_derive(&pattern, |p| &p[left.len()].0), if !left.is_empty() {Some(Box::new(Self::new(left)))} else {None}, if !right.is_empty() {Some(Box::new(Self::new(right)))} else {None} ))) .unwrap_or_else(|| ( mrc_derive(&pattern, |p| &p[0].0), None, Some(Box::new(Self::new(mrc_derive(&pattern, |p| &p[1..])))) )); Self { pattern, right_subm, left_subm, clause: Mrc::clone(&clause), body_subm: clause.body().map(|b| Box::new(Self::new(b))), typ_subm: clause.typ().map(|t| Box::new(Self::new(t))) } } /// The shortest slice this pattern can match fn len(&self) -> usize { if self.clause_is_vectorial() { self.min(Side::Left) + self.min(Side::Right) + self.own_min_size() } else {self.pattern.len()} } /// Pick a subpattern based on the parameter fn side(&self, side: Side) -> Option<&SliceMatcherDnC> { match side { Side::Left => &self.left_subm, Side::Right => &self.right_subm }.as_ref().map(|b| b.as_ref()) } /// The shortest slice the given side can match fn min(&self, side: Side) -> usize {self.side(side).map_or(0, |right| right.len())} /// The longest slice the given side can match fn max(&self, side: Side, total: usize) -> usize { self.side(side).map_or(0, |m| if m.clause_is_vectorial() { total - self.min(side.opposite()) - self.own_min_size() } else {m.len()}) } /// Take the smallest possible slice from the given side fn slice_min<'a>(&self, side: Side, range: &'a [Expr]) -> &'a [Expr] { side.slice(self.min(side), range) } /// Matches the body on a range /// # Panics /// when called on an instance that does not have a body (not Auto, Lambda or S) fn match_body<'a>(&'a self, range: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { self.body_subm.as_ref() .expect("Missing body matcher") .match_range_cached(range, cache) } /// Matches the type and body on respective ranges /// # Panics /// when called on an instance that does not have a body (not Auto, Lambda or S) fn match_parts<'a>(&'a self, typ_range: Mrc<[Expr]>, body_range: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { let typ_state = if let Some(typ) = &self.typ_subm { typ.match_range_cached(typ_range, cache)? } else {State::new()}; let body_state = self.match_body(body_range, cache)?; typ_state + body_state } /// Match the specified side-submatcher on the specified range with the cache /// In absence of a side-submatcher empty ranges are matched to empty state fn apply_side_with_cache<'a>(&'a self, side: Side, range: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { match &self.side(side) { None => { if !range.is_empty() {None} else {Some(State::new())} }, Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned()) } } fn match_range_scalar_cached<'a>(&'a self, target: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { let pos = self.min(Side::Left); if target.len() != self.pattern.len() {return None} let mut own_state = ( self.apply_side_with_cache(Side::Left, mrc_derive(&target, |t| &t[0..pos]), cache)? + self.apply_side_with_cache(Side::Right, mrc_derive(&target, |t| &t[pos+1..]), cache) )?; match (self.clause.as_ref(), &target.as_ref()[pos].0) { (Clause::Literal(val), Clause::Literal(tgt)) => { if val == tgt {Some(own_state)} else {None} } (Clause::Placeh{key, vec: None}, tgt_clause) => { if let Some(real_key) = key.strip_prefix('_') { if let Clause::Name { local: Some(value), .. } = tgt_clause { own_state.insert_name(real_key, value) } else {None} } else {own_state.insert_scalar(&key, &target[pos])} } (Clause::S(c, _), Clause::S(c_tgt, body_range)) => { if c != c_tgt {return None} own_state + self.match_parts(to_mrc_slice(vec![]), Mrc::clone(body_range), cache) } (Clause::Name{qualified, ..}, Clause::Name{qualified: q_tgt, ..}) => { if qualified == q_tgt {Some(own_state)} else {None} } (Clause::Lambda(name, _, _), Clause::Lambda(name_tgt, typ_tgt, body_tgt)) => { // Primarily, the name works as a placeholder if let Some(state_key) = name.strip_prefix('$') { own_state = own_state.insert_name(state_key, name_tgt)? } else if name != name_tgt {return None} // ^ But if you're weird like that, it can also work as a constraint own_state + self.match_parts(Mrc::clone(typ_tgt), Mrc::clone(body_tgt), cache) } (Clause::Auto(name_opt, _, _), Clause::Auto(name_range, typ_range, body_range)) => { if let Some(name) = name_opt { // TODO: Enforce this at construction, on a type system level let state_key = name.strip_prefix('$') .expect("Auto patterns may only reference, never enforce the name"); own_state = own_state.insert_name_opt(state_key, name_range.as_ref())? } own_state + self.match_parts(Mrc::clone(typ_range), Mrc::clone(body_range), cache) }, _ => None } } /// Match the range with a vectorial _assuming we are a vectorial_ fn match_range_vectorial_cached<'a>(&'a self, name: &str, target: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { // Step through valid slicings based on reported size constraints in order // from longest own section to shortest and from left to right for (left, own, right) in self.valid_subdivisions(target) { return Some(unwrap_or!( self.apply_side_with_cache(Side::Left, left, cache) .and_then(|lres| lres + self.apply_side_with_cache(Side::Right, right, cache)) .and_then(|side_res| side_res.insert_vec(name, own.as_ref())); continue )) } None } /// Try and match the specified range pub fn match_range_cached<'a>(&'a self, target: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { if self.pattern.is_empty() { return if target.is_empty() {Some(State::new())} else {None} } if self.clause_is_vectorial() { let key = self.state_key().expect("Vectorial implies key"); self.match_range_vectorial_cached(key, target, cache) } else {self.match_range_scalar_cached(target, cache)} } pub fn get_matcher_cache<'a>() -> Cache<'a, CacheEntry<'a>, Option> { Cache::new( |CacheEntry(tgt, matcher), cache| { matcher.match_range_cached(tgt, cache) } ) } pub fn match_range(&self, target: Mrc<[Expr]>) -> Option { self.match_range_cached(target, &Self::get_matcher_cache()) } } impl Debug for SliceMatcherDnC { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Matcher") .field("clause", &self.clause) .field("vectorial", &self.clause_is_vectorial()) .field("min", &self.len()) .field("left", &self.left_subm) .field("right", &self.right_subm) .field("lmin", &self.min(Side::Left)) .field("rmin", &self.min(Side::Right)) .finish() } }