Rule execution now runs, no tests tho

This commit is contained in:
2022-08-19 12:55:02 +02:00
parent 329dea72b7
commit 891d78c112
30 changed files with 925 additions and 560 deletions

View File

@@ -0,0 +1,151 @@
use hashbrown::HashMap;
use mappable_rc::Mrc;
use crate::{expression::{Expr, Clause}, utils::{iter::{box_once, into_boxed_iter}, to_mrc_slice}};
use super::{super::RuleError, state::{State, Entry}, slice_matcher::SliceMatcherDnC};
fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
-> Result<(), String> {
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
match clause {
Clause::Placeh{key, vec} => {
if let Some(known) = is_vec.get(key) {
if known != &vec.is_some() { return Err(key.to_string()) }
} else {
is_vec.insert(key.clone(), vec.is_some());
}
}
Clause::Auto(name, typ, body) => {
if let Some(key) = name.as_ref().and_then(|key| key.strip_prefix('$')) {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
Clause::Lambda(name, typ, body) => {
if let Some(key) = name.strip_prefix('$') {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
Clause::S(_, body) => {
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
_ => ()
};
Ok(())
};
let Expr(val, typ_opt) = pattern;
verify_clause(val, is_vec)?;
if let Some(typ) = typ_opt {
verify_scalar_vec(typ, is_vec)?;
}
Ok(())
}
fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) {
let prefix_expr = Expr(Clause::Placeh{key: "::prefix".to_string(), vec: Some((0, false))}, None);
let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, None);
// Prefix or postfix to match the full vector
let head_multi = matches!(src.first().expect("Src can never be empty!").0, Clause::Placeh{vec: Some(_), ..});
let tail_multi = matches!(src.last().expect("Impossible branch!").0, Clause::Placeh{vec: Some(_), ..});
let prefix_vec = if head_multi {vec![]} else {vec![prefix_expr]};
let postfix_vec = if tail_multi {vec![]} else {vec![postfix_expr]};
*src = to_mrc_slice(prefix_vec.iter().chain(src.iter()).chain(postfix_vec.iter()).cloned().collect());
*tgt = to_mrc_slice(prefix_vec.iter().chain(tgt.iter()).chain(postfix_vec.iter()).cloned().collect());
}
/// Traverse the tree, calling pred on every sibling list until it returns some vec
/// then replace the sibling list with that vec and return true
/// return false if pred never returned some
fn update_first_seq_rec<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let o@Some(_) = pred(Mrc::clone(&input)) {o} else {
for Expr(cls, _) in input.iter() {
if let Some(t) = cls.typ() {
if let o@Some(_) = update_first_seq_rec(t, pred) {return o}
}
if let Some(b) = cls.body() {
if let o@Some(_) = update_first_seq_rec(b, pred) {return o}
}
}
None
}
}
/// keep re-probing the input with pred until it stops matching
fn update_all_seqs<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
let mut tmp = update_first_seq_rec(input, pred);
while let Some(xv) = tmp {
tmp = update_first_seq_rec(Mrc::clone(&xv), pred);
if tmp.is_none() {return Some(xv)}
}
None
}
/// Fill in a template from a state as produced by a pattern
fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
tpl.iter().flat_map(|Expr(clause, xpr_typ)| match clause {
Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
name_opt.as_ref().and_then(|name| {
let state_key = name.strip_prefix('$')
.expect("Auto template may only reference, never enforce the name");
match &state[state_key] {
Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned()),
Entry::Name(name) => Some(name.as_ref().to_owned()),
_ => panic!("Auto template name may only be derived from Auto or Lambda name")
}
}),
write_slice(state, typ),
write_slice(state, body)
), xpr_typ.to_owned())),
Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda(
if let Some(state_key) = name.strip_prefix('$') {
if let Entry::Name(name) = &state[state_key] {
name.as_ref().to_owned()
} else {panic!("Lambda template name may only be derived from Lambda name")}
} else {
name.to_owned()
},
write_slice(state, typ),
write_slice(state, body)
), xpr_typ.to_owned())),
Clause::S(c, body) => box_once(Expr(Clause::S(
*c,
write_slice(state, body)
), xpr_typ.to_owned())),
Clause::Placeh{key, vec: None} => if let Entry::Scalar(x) = &state[key] {
box_once(x.as_ref().to_owned())
} else {panic!("Scalar template may only be derived from scalar placeholder")},
Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] {
into_boxed_iter(v.as_ref().to_owned())
} else {panic!("Vectorial template may only be derived from vectorial placeholder")},
// Explicit base case so that we get an error if Clause gets new values
c@Clause::Literal(_) | c@Clause::Name { .. } =>
box_once(Expr(c.to_owned(), xpr_typ.to_owned()))
}).collect()
}
/// Apply a rule (a pair of pattern and template) to an expression
pub fn execute(mut src: Mrc<[Expr]>, mut tgt: Mrc<[Expr]>, input: Mrc<[Expr]>)
-> Result<Option<Mrc<[Expr]>>, RuleError> {
// Dimension check
let mut is_vec_db = HashMap::new();
src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
.map_err(RuleError::ScalarVecMismatch)?;
tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
.map_err(RuleError::ScalarVecMismatch)?;
// Padding
slice_to_vec(&mut src, &mut tgt);
// Generate matcher
let matcher = SliceMatcherDnC::new(src);
println!("Matcher: {matcher:#?}");
let matcher_cache = SliceMatcherDnC::get_matcher_cache();
Ok(update_all_seqs(Mrc::clone(&input), &mut |p| {
let state = matcher.match_range_cached(p, &matcher_cache)?;
Some(write_slice(&state, &tgt))
}))
}

View File

@@ -1,4 +1,8 @@
mod slice_matcher;
mod state;
mod execute;
mod split_at_max_vec;
use state::State;
use state::State;
pub use execute::execute;

View File

@@ -1,28 +1,27 @@
use hashbrown::HashMap;
use itertools::Itertools;
use std::fmt::Debug;
use mappable_rc::Mrc;
use crate::expression::{Expr, Clause};
use crate::unwrap_or_continue;
use crate::utils::{Side, Cache};
use super::super::RuleError;
use super::State;
use crate::utils::iter::box_empty;
use crate::utils::{Side, Cache, mrc_derive, mrc_try_derive, to_mrc_slice};
fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr])> {
let rngidx = pattern.iter().position_max_by_key(|ex| {
if let Expr(Clause::Placeh(_, Some(prio)), _) = ex { *prio as i64 } else { -1 }
})?;
let (left, not_left) = pattern.split_at(rngidx);
let (placeh, right) = if rngidx == pattern.len() {
(&not_left[0].0, [].as_slice())
} else {
let (placeh_unary_slice, right) = pattern.split_at(rngidx + 1);
(&placeh_unary_slice[0].0, right)
};
if let Clause::Placeh(name, Some(prio)) = placeh {
Some((left, (name, *prio), right))
} else {None}
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
@@ -31,134 +30,171 @@ fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr]
///
/// Upon matching, it uses a cache to accelerate the process of executing
/// a pattern on the entire tree.
#[derive(Debug, Clone, Eq)]
pub struct SliceMatcherDnC<'a> {
#[derive(Clone, Eq)]
pub struct SliceMatcherDnC {
/// The entire pattern this will match
pattern: &'a [Expr],
pattern: Mrc<[Expr]>,
/// The exact clause this can match
clause: &'a Clause,
clause: Mrc<Clause>,
/// Matcher for the parts of the pattern right from us
right_subm: Option<Box<SliceMatcherDnC<'a>>>,
right_subm: Option<Box<SliceMatcherDnC>>,
/// Matcher for the parts of the pattern left from us
left_subm: Option<Box<SliceMatcherDnC<'a>>>,
left_subm: Option<Box<SliceMatcherDnC>>,
/// Matcher for the body of this clause if it has one.
/// Must be Some if pattern is (Auto, Lambda or S)
body_subm: Option<Box<SliceMatcherDnC<'a>>>,
body_subm: Option<Box<SliceMatcherDnC>>,
/// Matcher for the type of this expression if it has one (Auto usually does)
/// Optional
typ_subm: Option<Box<SliceMatcherDnC<'a>>>,
typ_subm: Option<Box<SliceMatcherDnC>>,
}
impl<'a> PartialEq for SliceMatcherDnC<'a> {
impl PartialEq for SliceMatcherDnC {
fn eq(&self, other: &Self) -> bool {
self.pattern == other.pattern
}
}
impl<'a> std::hash::Hash for SliceMatcherDnC<'a> {
impl std::hash::Hash for SliceMatcherDnC {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.pattern.hash(state);
}
}
impl<'a> SliceMatcherDnC<'a> {
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 {
if let Clause::Placeh(_, Some(_)) = self.clause {true} else {false}
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<&'a Vec<String>> {
if let Clause::Name { qualified, .. } = self.clause {Some(qualified)} else {None}
pub fn clause_qual_name(&self) -> Option<Mrc<[String]>> {
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<&'a String> {
if let Clause::Placeh(key, _) = self.clause {Some(key)} else {None}
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) -> usize {
if !self.clause_is_vectorial() {return self.len()}
return total - self.min(Side::Left) - self.min(Side::Right)
pub fn own_max_size(&self, total: usize) -> Option<usize> {
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<'b>(&self,
range: &'b [Expr]
) -> impl Iterator<Item = (&'b [Expr], &'b [Expr], &'b [Expr])> {
let own_size = self.own_max_size(range.len());
pub fn valid_subdivisions(&self,
range: Mrc<[Expr]>
) -> impl Iterator<Item = (Mrc<[Expr]>, Mrc<[Expr]>, Mrc<[Expr]>)> {
let own_max = {
if let Some(x) = self.own_max_size(range.len()) {x}
else {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 _lmax = self.max(Side::Left, range.len());
let rmin = self.min(Side::Right);
let rmax = self.max(Side::Right, range.len());
let _rmax = self.max(Side::Right, range.len());
let full_len = range.len();
(1..=own_size).rev().flat_map(move |own_len| {
Box::new((own_min..=own_max).rev().flat_map(move |own_len| {
let wiggle = full_len - lmin - rmin - own_len;
(0..wiggle).map(move |offset| {
let range = Mrc::clone(&range);
(0..=wiggle).map(move |offset| {
let first_break = lmin + offset;
let (left, rest) = range.split_at(first_break);
let (mid, right) = rest.split_at(own_len);
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: &'a [Expr]) -> Self {
let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
(&pattern[0], None, None)
} else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
&pattern[left.len()],
Some(Box::new(Self::new(left))),
Some(Box::new(Self::new(right)))
)} else {(
&pattern[0],
None,
Some(Box::new(Self::new(&pattern[1..])))
)};
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..]))))
));
// let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
// (&pattern[0], None, None)
// } else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
// &pattern[left.len()],
// Some(Box::new(Self::new(left))),
// Some(Box::new(Self::new(right)))
// )} else {(
// &pattern[0],
// None,
// Some(Box::new(Self::new(&pattern[1..])))
// )};
Self {
pattern, right_subm, left_subm, clause,
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 {self.pattern.len()}
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<&Box<SliceMatcherDnC<'a>>> {
fn side(&self, side: Side) -> Option<&SliceMatcherDnC> {
match side {
Side::Left => &self.left_subm,
Side::Right => &self.right_subm
}.as_ref()
}.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()) - 1
total - self.min(side.opposite()) - self.own_min_size()
} else {m.len()})
}
/// Take the smallest possible slice from the given side
fn slice_min<'b>(&self, side: Side, range: &'b [Expr]) -> &'b [Expr] {
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<'b>(&'a self,
range: &'b [Expr], cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
fn match_body<'a>(&'a self,
range: Mrc<[Expr]>, cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
self.body_subm.as_ref().unwrap().match_range_cached(range, cache)
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<'b>(&'a self,
typ_range: &'b [Expr], body_range: &'b [Expr],
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
fn match_parts<'a>(&'a self,
typ_range: Mrc<[Expr]>, body_range: Mrc<[Expr]>,
cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
let typ_state = if let Some(typ) = &self.typ_subm {
typ.match_range_cached(&typ_range, cache)?
typ.match_range_cached(typ_range, cache)?
} else {State::new()};
let body_state = self.match_body(body_range, cache)?;
typ_state + body_state
@@ -166,181 +202,124 @@ impl<'a> SliceMatcherDnC<'a> {
/// 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<'b>(&'a self,
side: Side, range: &'b [Expr],
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
fn apply_side_with_cache<'a>(&'a self,
side: Side, range: Mrc<[Expr]>,
cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
match &self.side(side) {
None => {
if range.len() != 0 {None}
if !range.is_empty() {None}
else {Some(State::new())}
},
Some(m) => cache.try_find(&(range, &m)).map(|s| s.as_ref().to_owned())
Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned())
}
}
fn match_range_scalar_cached<'b>(&'a self,
target: &'b [Expr],
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
fn match_range_scalar_cached<'a>(&'a self,
target: Mrc<[Expr]>,
cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
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, &target[0..pos], cache)?
+ self.apply_side_with_cache(Side::Right, &target[pos+1..], cache)
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, &target[pos].0) {
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(name, None), _) => {
own_state.insert(name, &[target[pos].clone()])
(Clause::Placeh{key, vec: None}, _) => {
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(&[], body_range, cache)
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(
state_key,
&[Expr(Clause::Name{
local: Some(name_tgt.clone()),
qualified: vec![name_tgt.clone()]
}, None)]
)?
// But if you're weird like that, it can also work as a constraint
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}
own_state + self.match_parts(typ_tgt, body_tgt, cache)
// ^ 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 {
if let Some(state_name) = name.strip_prefix("$") {
own_state = own_state.insert(
state_name,
&[Expr(Clause::Name{
local: name_range.clone(),
qualified: name_range.as_ref()
.map(|s| vec![s.clone()])
.unwrap_or_default()
}, None)]
)?
// TODO: Enforce this at construction, on a type system level
} else {panic!("Auto patterns may only reference, never enforce the name")}
// 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(typ_range, body_range, cache)
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<'b>(&'a self,
fn match_range_vectorial_cached<'a>(&'a self,
name: &str,
target: &'b [Expr],
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
target: Mrc<[Expr]>,
cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
// 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) {
let left_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Left, left, cache));
let right_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Right, right, cache));
let sides_result = unwrap_or_continue!(
self.apply_side_with_cache(Side::Left, left, cache)
) + self.apply_side_with_cache(Side::Right, right, cache);
return Some(unwrap_or_continue!(
right_result.clone()
+ left_result.insert(name, own)
unwrap_or_continue!(sides_result)
.insert_vec(name, own.as_ref())
))
}
return None
None
}
/// Try and match the specified range
pub fn match_range_cached<'b>(&'a self,
target: &'b [Expr],
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
pub fn match_range_cached<'a>(&'a self,
target: Mrc<[Expr]>,
cache: &Cache<CacheEntry<'a>, Option<State>>
) -> Option<State> {
if self.pattern.len() == 0 {
return if target.len() == 0 {Some(State::new())} else {None}
eprintln!("Matching {target:?} with {:?}", self.pattern);
if self.pattern.is_empty() {
return if target.is_empty() {Some(State::new())} else {None}
}
match self.clause {
Clause::Placeh(name, Some(_)) => self.match_range_vectorial_cached(name, target, cache),
match self.clause.as_ref() {
Clause::Placeh{key, vec: Some(_)} =>
self.match_range_vectorial_cached(key, target, cache),
_ => self.match_range_scalar_cached(target, cache)
}
}
pub fn match_range(&self, target: &[Expr]) -> Option<State> {
self.match_range_cached(target,&Cache::<(&[Expr], &SliceMatcherDnC), _>::new(
|(tgt, matcher), cache| {
pub fn get_matcher_cache<'a>()
-> Cache<'a, CacheEntry<'a>, Option<State>> {
Cache::new(
|CacheEntry(tgt, matcher), cache| {
matcher.match_range_cached(tgt, cache)
}
))
)
}
pub fn match_range(&self, target: Mrc<[Expr]>) -> Option<State> {
self.match_range_cached(target, &Self::get_matcher_cache())
}
}
pub fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
-> Result<(), String> {
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
match clause {
Clause::Placeh(name, prio) => {
if let Some(known) = is_vec.get(name) {
if known != &prio.is_some() { return Err(name.to_string()) }
} else {
is_vec.insert(name.clone(), prio.is_some());
}
}
Clause::Auto(name, typ, body) => {
if let Some(key) = name.as_ref().map(|key| key.strip_prefix("$")).flatten() {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
Clause::Lambda(name, typ, body) => {
if let Some(key) = name.strip_prefix("$") {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
Clause::S(_, body) => {
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
_ => ()
};
Ok(())
};
let Expr(val, typ_opt) = pattern;
verify_clause(val, is_vec)?;
if let Some(typ) = typ_opt {
verify_scalar_vec(typ, is_vec)?;
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()
}
return Ok(())
}
pub fn execute(mut src: Vec<Expr>, mut tgt: Vec<Expr>, mut input: Vec<Expr>)
-> Result<(Vec<Expr>, bool), RuleError> {
// Static values
let prefix_expr = Expr(Clause::Placeh("::prefix".to_string(), Some(0)), None);
let postfix_expr = Expr(Clause::Placeh("::postfix".to_string(), Some(0)), None);
// Dimension check
let mut is_vec_db = HashMap::new();
src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
.map_err(RuleError::ScalarVecMismatch)?;
tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
.map_err(RuleError::ScalarVecMismatch)?;
// Prefix or postfix to match the full vector
let head_multi = if let Clause::Placeh(_, Some(_)) = src.first().unwrap().0 {true} else {false};
let tail_multi = if let Clause::Placeh(_, Some(_)) = src.last().unwrap().0 {true} else {false};
if !head_multi {
src.insert(0, prefix_expr.clone());
tgt.insert(0, prefix_expr.clone());
}
if !tail_multi {
src.push(postfix_expr.clone());
tgt.push(postfix_expr.clone());
}
todo!()
}

View File

@@ -0,0 +1,33 @@
use mappable_rc::Mrc;
use itertools::Itertools;
use crate::expression::{Expr, Clause};
use crate::utils::{mrc_derive, mrc_try_derive};
pub type MaxVecSplit = (Mrc<[Expr]>, (Mrc<str>, usize, bool), Mrc<[Expr]>);
/// Derive the details of the central vectorial and the two sides from a slice of Expr's
pub fn split_at_max_vec(pattern: Mrc<[Expr]>) -> Option<MaxVecSplit> {
let rngidx = pattern.iter().position_max_by_key(|ex| {
if let Expr(Clause::Placeh{vec: Some((prio, _)), ..}, _) = ex {
*prio as i64
} else { -1 }
})?;
let left = mrc_derive(&pattern, |p| &p[0..rngidx]);
let placeh = mrc_derive(&pattern, |p| &p[rngidx].0);
let right = if rngidx == pattern.len() {
mrc_derive(&pattern, |x| &x[0..1])
} else {
mrc_derive(&pattern, |x| &x[rngidx + 1..])
};
mrc_try_derive(&placeh, |p| {
if let Clause::Placeh{key, vec: Some(_)} = p {
Some(key)
} else {None} // Repeated below on unchanged data
}).map(|key| {
let key = mrc_derive(&key, String::as_str);
if let Clause::Placeh{vec: Some((prio, nonzero)), ..} = placeh.as_ref() {
(left, (key, *prio, *nonzero), right)
}
else {panic!("Impossible branch")} // Duplicate of above
})
}

View File

@@ -1,21 +1,31 @@
use std::ops::{Add, Index};
use std::{ops::{Add, Index}, rc::Rc, fmt::Debug};
use hashbrown::{HashMap, hash_map::IntoIter};
use mappable_rc::Mrc;
use hashbrown::HashMap;
use crate::expression::Expr;
#[derive(Debug, PartialEq, Eq)]
pub enum Entry {
Vec(Rc<Vec<Expr>>),
Scalar(Rc<Expr>),
Name(Rc<String>),
NameOpt(Option<Rc<String>>)
}
/// A bucket of indexed expression fragments. Addition may fail if there's a conflict.
#[derive(PartialEq, Eq)]
pub struct State(HashMap<String, Mrc<Vec<Expr>>>);
#[derive(PartialEq, Eq, Clone)]
pub struct State(HashMap<String, Entry>);
/// Clone without also cloning arbitrarily heavy Expr objects.
/// Key is expected to be a very short string with an allocator overhead close to zero.
impl Clone for State {
impl Clone for Entry {
fn clone(&self) -> Self {
Self(HashMap::from_iter(
self.0.iter().map(|(k, v)| (k.clone(), Mrc::clone(v)))
))
match self {
Self::Name(n) => Self::Name(Rc::clone(n)),
Self::Scalar(x) => Self::Scalar(Rc::clone(x)),
Self::Vec(v) => Self::Vec(Rc::clone(v)),
Self::NameOpt(o) => Self::NameOpt(o.as_ref().map(Rc::clone))
}
}
}
@@ -23,24 +33,65 @@ impl State {
pub fn new() -> Self {
Self(HashMap::new())
}
/// Insert a new element, return None on conflict, clone only on success
pub fn insert<S>(mut self, k: &S, v: &[Expr]) -> Option<State>
pub fn insert_vec<S>(mut self, k: &S, v: &[Expr]) -> Option<Self>
where S: AsRef<str> + ToString + ?Sized + Debug {
eprintln!("{:?} + {k:?}-{v:?}", self.0);
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::Vec(val) = old {
if val.as_slice() != v {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::Vec(Rc::new(v.to_vec())));
}
Some(self)
}
pub fn insert_scalar<S>(mut self, k: &S, v: &Expr) -> Option<Self>
where S: AsRef<str> + ToString + ?Sized {
if let Some(old) = self.0.get(k.as_ref()) {
if old.as_ref() != v {return None}
if let Entry::Scalar(val) = old {
if val.as_ref() != v {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Mrc::new(v.to_vec()));
self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned())));
}
return Some(self)
Some(self)
}
pub fn insert_name<S1, S2>(mut self, k: &S1, v: &S2) -> Option<Self>
where
S1: AsRef<str> + ToString + ?Sized,
S2: AsRef<str> + ToString + ?Sized
{
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::Name(val) = old {
if val.as_str() != v.as_ref() {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_string())));
}
Some(self)
}
pub fn insert_name_opt<S1, S2>(mut self, k: &S1, v: Option<&S2>) -> Option<Self>
where
S1: AsRef<str> + ToString + ?Sized,
S2: AsRef<str> + ToString + ?Sized
{
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::NameOpt(val) = old {
if val.as_ref().map(|s| s.as_ref().as_str()) != v.map(|s| s.as_ref()) {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::NameOpt(v.map(|s| Rc::new(s.to_string()))));
}
Some(self)
}
/// Insert a new entry, return None on conflict
pub fn insert_pair(mut self, (k, v): (String, Mrc<Vec<Expr>>)) -> Option<State> {
pub fn insert_pair(mut self, (k, v): (String, Entry)) -> Option<State> {
if let Some(old) = self.0.get(&k) {
if old != &v {return None}
} else {
self.0.insert(k, v);
}
return Some(self)
Some(self)
}
/// Returns `true` if the state contains no data
pub fn empty(&self) -> bool {
@@ -58,7 +109,7 @@ impl Add for State {
for pair in rhs.0 {
self = self.insert_pair(pair)?
}
return Some(self);
Some(self)
}
}
@@ -70,20 +121,20 @@ impl Add<Option<State>> for State {
}
}
impl<'a, S> Index<&S> for State where S: AsRef<str> {
type Output = Vec<Expr>;
impl<S> Index<S> for State where S: AsRef<str> {
type Output = Entry;
fn index(&self, index: &S) -> &Self::Output {
fn index(&self, index: S) -> &Self::Output {
return &self.0[index.as_ref()]
}
}
impl IntoIterator for State {
type Item = (String, Mrc<Vec<Expr>>);
type Item = (String, Entry);
type IntoIter = IntoIter<String, Mrc<Vec<Expr>>>;
type IntoIter = hashbrown::hash_map::IntoIter<String, Entry>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
}