bug fixes and performance improvements

This commit is contained in:
2023-05-07 22:35:38 +01:00
parent f3ce910f66
commit a604e40bad
167 changed files with 5965 additions and 4229 deletions

View File

@@ -1,203 +0,0 @@
use std::iter;
use std::rc::Rc;
use hashbrown::HashMap;
use mappable_rc::Mrc;
use crate::unwrap_or;
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice};
use crate::utils::iter::{box_once, into_boxed_iter};
use crate::ast::{Expr, Clause};
use super::slice_matcher::SliceMatcherDnC;
use super::state::{State, Entry};
use super::super::RuleError;
use super::update_first_seq_rec;
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>| {
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_opt, typ, body) => {
if let Some(name) = name_opt.as_ref() {
if let Clause::Placeh { key, vec } = name.as_ref() {
if vec.is_some() || is_vec.get(key) == Some(&true) {
return Err(key.to_string())
}
is_vec.insert(key.to_owned(), false);
}
}
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 Clause::Placeh { key, vec } = name.as_ref() {
if vec.is_some() || is_vec.get(key) == Some(&true) {
return Err(key.to_string())
}
is_vec.insert(key.to_owned(), false);
}
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) = pattern;
verify_clause(val, is_vec)?;
for typ in typ.as_ref() {
verify_clause(typ, is_vec)?;
}
Ok(())
}
/// Ensure that src starts and ends with a vectorial placeholder without
/// modifying the meaning of the substitution rule
fn slice_to_vec(src: &mut Rc<Vec<Expr>>, tgt: &mut Rc<Vec<Expr>>) {
let prefix_expr = Expr(Clause::Placeh{
key: "::prefix".to_string(),
vec: Some((0, false))
}, Rc::default());
let postfix_expr = Expr(Clause::Placeh{
key: "::postfix".to_string(),
vec: Some((0, false))
}, Rc::default());
// 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 = Rc::new(
prefix_vec.iter()
.chain(src.iter())
.chain(postfix_vec.iter())
.cloned().collect()
);
*tgt = Rc::new(
prefix_vec.iter()
.chain(tgt.iter())
.chain(postfix_vec.iter())
.cloned().collect()
);
}
/// keep re-probing the input with pred until it stops matching
fn update_all_seqs<F>(input: Rc<Vec<Expr>>, pred: &mut F)
-> Option<Rc<Vec<Expr>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
let mut tmp = update_first_seq_rec::exprv(input, pred);
while let Some(xv) = tmp {
tmp = update_first_seq_rec::exprv(xv.clone(), pred);
if tmp.is_none() {return Some(xv)}
}
None
}
fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr)
-> Box<dyn Iterator<Item = Expr>> {
let out_typ = tpl_typ.iter()
.flat_map(|c| write_expr_rec(state, &c.clone().into_expr()))
.map(Expr::into_clause)
.collect::<Mrc<[Clause]>>();
match tpl_clause {
Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
name_opt.as_ref().and_then(|name| {
if let Clause::Placeh { key, .. } = name {
match &state[key] {
Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned())
}
}
if let Some(state_key) = name.strip_prefix('$') {
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")
}
} else {
Some(name.to_owned())
}
}),
write_slice_rec(state, typ),
write_slice_rec(state, body)
), out_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_rec(state, typ),
write_slice_rec(state, body)
), out_typ.to_owned())),
Clause::S(c, body) => box_once(Expr(Clause::S(
*c,
write_slice_rec(state, body)
), out_typ.to_owned())),
Clause::Placeh{key, vec: None} => {
let real_key = unwrap_or!(key.strip_prefix('_'); key);
match &state[real_key] {
Entry::Scalar(x) => box_once(x.as_ref().to_owned()),
Entry::Name(n) => box_once(Expr(Clause::Name {
local: Some(n.as_ref().to_owned()),
qualified: one_mrc_slice(n.as_ref().to_owned())
}, mrc_empty_slice())),
_ => 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")},
Clause::Explicit(param) => {
assert!(out_typ.len() == 0, "Explicit should never have a type annotation");
box_once(Clause::Explicit(Mrc::new(
Clause::from_exprv(write_expr_rec(state, param).collect())
.expect("Result shorter than template").into_expr()
)).into_expr())
},
// Explicit base case so that we get an error if Clause gets new values
c@Clause::P(_) | c@Clause::Name { .. } => box_once(Expr(c.to_owned(), out_typ.to_owned()))
}
}
/// Fill in a template from a state as produced by a pattern
fn write_slice_rec(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
tpl.iter().flat_map(|xpr| write_expr_rec(state, xpr)).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);
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_rec(&state, &tgt))
}))
}

View File

@@ -1,9 +0,0 @@
mod slice_matcher;
mod state;
mod execute;
mod split_at_max_vec;
mod update_first_seq_rec;
use state::State;
pub use execute::execute;

View File

@@ -1,314 +0,0 @@
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)
// }
// }
// ^^^^
// This has been removed because the slice-based version needs no custom
// cloning logic. In the next iteration, remove the this altogether.
/// 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<Clause>,
/// Matcher for the parts of the pattern right from us
right_subm: Option<Box<SliceMatcherDnC>>,
/// Matcher for the parts of the pattern left from us
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>>,
/// Matcher for the type of this expression if it has one (Auto usually does)
/// Optional
typ_subm: Option<Box<SliceMatcherDnC>>,
}
impl PartialEq for SliceMatcherDnC {
fn eq(&self, other: &Self) -> bool {
self.pattern == other.pattern
}
}
impl std::hash::Hash for SliceMatcherDnC {
fn hash<H: std::hash::Hasher>(&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<Rc<Vec<Spur>>> {
if let Clause::Name(name) = self.clause.as_ref() {Some(name.clone())} 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<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<'a>(&'a self,
range: &'a [Expr]
) -> impl Iterator<Item = (Mrc<[Expr]>, 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<CacheEntry<'a>, Option<State>>
) -> Option<State> {
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<CacheEntry<'a>, Option<State>>
) -> Option<State> {
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<CacheEntry<'a>, Option<State>>
) -> Option<State> {
match &self.side(side) {
None => {
if !range.is_empty() {None}
else {Some(State::new())}
},
Some(m) => cache.find(&CacheEntry(range, m))
}
}
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, 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::P(val), Clause::P(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<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) {
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<CacheEntry<'a>, Option<State>>
) -> Option<State> {
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<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())
}
}
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()
}
}

View File

@@ -1,33 +0,0 @@
use mappable_rc::Mrc;
use itertools::Itertools;
use crate::ast::{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,141 +0,0 @@
use std::{ops::{Add, Index}, rc::Rc, fmt::Debug};
use hashbrown::HashMap;
use lasso::Spur;
use crate::ast::Expr;
#[derive(PartialEq, Eq)]
pub enum Entry {
Vec(Rc<Vec<Expr>>),
Scalar(Rc<Expr>),
Name(Rc<Vec<Spur>>),
NameOpt(Option<Rc<Vec<Spur>>>)
}
/// A bucket of indexed expression fragments. Addition may fail if there's a conflict.
#[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 Entry {
fn clone(&self) -> Self {
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))
}
}
}
impl State {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn insert_vec<S>(mut self, k: &S, v: &[Expr]) -> Option<Self>
where S: AsRef<str> + ToString + ?Sized + Debug {
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 let Entry::Scalar(val) = old {
if val.as_ref() != v {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned())));
}
Some(self)
}
pub fn insert_name<S1>(mut self, k: &S1, v: &[Spur]) -> Option<Self>
where
S1: AsRef<str> + ToString + ?Sized
{
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::Name(val) = old {
if val.as_ref() != v.as_ref() {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_vec())));
}
Some(self)
}
pub fn insert_name_opt<S1, S2>(mut self, k: &S1, v: Option<&[Spur]>)
-> Option<Self>
where S1: 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_slice()) != v {
return None
}
} else {return None}
} else {
let data = v.map(|s| Rc::new(s.to_vec()));
self.0.insert(k.to_string(), Entry::NameOpt(data));
}
Some(self)
}
/// Insert a new entry, return None on conflict
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);
}
Some(self)
}
/// Returns `true` if the state contains no data
pub fn empty(&self) -> bool {
self.0.is_empty()
}
}
impl Add for State {
type Output = Option<State>;
fn add(mut self, rhs: Self) -> Self::Output {
if self.empty() {
return Some(rhs)
}
for pair in rhs.0 {
self = self.insert_pair(pair)?
}
Some(self)
}
}
impl Add<Option<State>> for State {
type Output = Option<State>;
fn add(self, rhs: Option<State>) -> Self::Output {
rhs.and_then(|s| self + s)
}
}
impl<S> Index<S> for State where S: AsRef<str> {
type Output = Entry;
fn index(&self, index: S) -> &Self::Output {
return &self.0[index.as_ref()]
}
}
impl IntoIterator for State {
type Item = (String, Entry);
type IntoIter = hashbrown::hash_map::IntoIter<String, Entry>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

View File

@@ -1,54 +0,0 @@
use std::rc::Rc;
use crate::utils::replace_first;
use crate::ast::{Expr, Clause};
/// 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
pub fn exprv<F>(input: Rc<Vec<Expr>>, pred: &mut F) -> Option<Rc<Vec<Expr>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let o@Some(_) = pred(input.clone()) {return o}
replace_first(input.as_ref(), |ex| expr(ex, pred))
.map(|i| Rc::new(i.collect()))
}
pub fn expr<F>(Expr(cls, typ): &Expr, pred: &mut F) -> Option<Expr>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let Some(t) = clausev(typ.clone(), pred) {return Some(Expr(cls.clone(), t))}
if let Some(c) = clause(cls, pred) {return Some(Expr(c, typ.clone()))}
None
}
pub fn clausev<F>(input: Rc<Vec<Clause>>, pred: &mut F) -> Option<Rc<Vec<Clause>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
replace_first(input.as_ref(), |c| clause(c, pred))
.map(|i| Rc::new(i.collect()))
}
pub fn clause<F>(c: &Clause, pred: &mut F) -> Option<Clause>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
match c {
Clause::P(_) | Clause::Placeh {..} | Clause::Name {..} => None,
Clause::Lambda(n, typ, body) => {
if let Some(b) = exprv(body.clone(), pred) {
return Some(Clause::Lambda(n.clone(), typ.clone(), b))
}
if let Some(t) = exprv(typ.clone(), pred) {
return Some(Clause::Lambda(n.clone(), t, body.clone()))
}
None
}
Clause::Auto(n, typ, body) => {
if let Some(b) = exprv(body.clone(), pred) {
return Some(Clause::Auto(n.clone(), typ.clone(), b))
}
if let Some(t) = exprv(typ.clone(), pred) {
return Some(Clause::Auto(n.clone(), t, body.clone()))
}
None
}
Clause::S(c, body) => Some(Clause::S(*c, exprv(body.clone(), pred)?)),
Clause::Explicit(t) => Some(Clause::Explicit(Rc::new(expr(t, pred)?)))
}
}

10
src/rule/matcher.rs Normal file
View File

@@ -0,0 +1,10 @@
use std::rc::Rc;
use crate::ast::Expr;
use super::state::State;
pub trait Matcher {
fn new(pattern: Rc<Vec<Expr>>) -> Self;
fn apply<'a>(&self, source: &'a [Expr]) -> Option<State<'a>>;
}

View File

@@ -0,0 +1,19 @@
use crate::{ast::Expr, rule::state::State};
use super::{shared::AnyMatcher, scal_match::scalv_match, vec_match::vec_match};
pub fn any_match<'a>(matcher: &AnyMatcher, seq: &'a [Expr])
-> Option<State<'a>>
{
match matcher {
AnyMatcher::Scalar(scalv) => scalv_match(scalv, seq),
AnyMatcher::Vec{ left, mid, right } => {
let left_split = left.len();
let right_split = seq.len() - right.len();
let mut state = scalv_match(left, &seq[..left_split])?;
state.extend(scalv_match(right, &seq[right_split..])?);
state.extend(vec_match(mid, &seq[left_split..right_split])?);
Some(state)
}
}
}

View File

@@ -0,0 +1,159 @@
use itertools::Itertools;
use crate::{rule::vec_attrs::vec_attrs, ast::Clause};
use crate::utils::Side;
use crate::interner::Token;
use crate::ast::{Expr, Placeholder, PHClass};
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
pub type MaxVecSplit<'a> = (&'a [Expr], (Token<String>, u64, bool), &'a [Expr]);
/// Derive the details of the central vectorial and the two sides from a slice of Expr's
fn split_at_max_vec(pattern: &[Expr]) -> Option<MaxVecSplit> {
let rngidx = pattern.iter()
.position_max_by_key(|expr| {
vec_attrs(expr)
.map(|attrs| attrs.1 as i64)
.unwrap_or(-1)
})?;
let (left, not_left) = pattern.split_at(rngidx);
let (placeh, right) = not_left.split_first()
.expect("The index of the greatest element must be less than the length");
vec_attrs(placeh)
.map(|attrs| (left, attrs, right))
}
fn scal_cnt<'a>(iter: impl Iterator<Item = &'a Expr>) -> usize {
iter
.take_while(|expr| vec_attrs(expr).is_none())
.count()
}
/// Recursively convert this pattern into a matcher that can be
/// efficiently applied to slices.
pub fn mk_matcher(pattern: &[Expr]) -> AnyMatcher {
println!("matcher from {:?}", pattern);
let left_split = scal_cnt(pattern.iter());
if pattern.len() <= left_split {
return AnyMatcher::Scalar(mk_scalv(pattern))
}
let (left, not_left) = pattern.split_at(left_split);
let right_split = not_left.len() - scal_cnt(pattern.iter().rev());
let (mid, right) = not_left.split_at(right_split);
AnyMatcher::Vec {
left: mk_scalv(left),
mid: mk_vec(mid),
right: mk_scalv(right),
}
}
/// Pattern MUST NOT contain vectorial placeholders
fn mk_scalv(pattern: &[Expr]) -> Vec<ScalMatcher> {
println!("Scalv from {:?}", pattern);
pattern.iter().map(mk_scalar).collect()
}
/// Pattern MUST start and end with a vectorial placeholder
fn mk_vec(pattern: &[Expr]) -> VecMatcher {
println!("Vec from {:?}", pattern);
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial");
let (left, (key, prio, nonzero), right) = split_at_max_vec(pattern)
.expect("pattern must have vectorial placeholders at least at either end");
if prio >= 1 {println!("Nondefault priority {} found", prio)}
let r_sep_size = scal_cnt(right.iter());
let (r_sep, r_side) = right.split_at(r_sep_size);
let l_sep_size = scal_cnt(left.iter().rev());
let (l_side, l_sep) = left.split_at(left.len() - l_sep_size);
let main = VecMatcher::Placeh { key, nonzero };
match (left, right) {
(&[], &[]) => VecMatcher::Placeh { key, nonzero },
(&[], _) => VecMatcher::Scan {
direction: Side::Left,
left: Box::new(main),
sep: mk_scalv(r_sep),
right: Box::new(mk_vec(r_side)),
},
(_, &[]) => VecMatcher::Scan {
direction: Side::Right,
left: Box::new(mk_vec(l_side)),
sep: mk_scalv(l_sep),
right: Box::new(main),
},
(_, _) => {
let mut key_order = l_side.iter()
.chain(r_side.iter())
.filter_map(|e| vec_attrs(e))
.collect::<Vec<_>>();
key_order.sort_by_key(|(_, prio, _)| -(*prio as i64));
VecMatcher::Middle {
left: Box::new(mk_vec(l_side)),
left_sep: mk_scalv(l_sep),
mid: Box::new(main),
right_sep: mk_scalv(r_sep),
right: Box::new(mk_vec(r_side)),
key_order: key_order.into_iter().map(|(n, ..)| n).collect()
}
}
}
}
/// Pattern MUST NOT be a vectorial placeholder
fn mk_scalar(pattern: &Expr) -> ScalMatcher {
println!("Scalar from {:?}", pattern);
match &pattern.value {
Clause::P(p) => ScalMatcher::P(p.clone()),
Clause::Name(n) => ScalMatcher::Name(*n),
Clause::Placeh(Placeholder{ name, class }) => {
debug_assert!(!matches!(class, PHClass::Vec{..}), "Scalar matcher cannot be built from vector pattern");
ScalMatcher::Placeh(*name)
}
Clause::S(c, body) => ScalMatcher::S(*c, Box::new(mk_matcher(&body))),
Clause::Lambda(arg, body) => ScalMatcher::Lambda(
Box::new(mk_scalar(&arg)),
Box::new(mk_matcher(&body))
)
}
}
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::interner::{Interner, InternedDisplay};
use crate::ast::{Clause, Placeholder, PHClass};
use super::mk_matcher;
#[test]
fn test_scan() {
let i = Interner::new();
let pattern = vec![
Clause::Placeh(Placeholder{
class: PHClass::Vec{ nonzero: false, prio: 0 },
name: i.i("::prefix"),
}).into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i("do")][..])).into_expr(),
Clause::S('(', Rc::new(vec![
Clause::Placeh(Placeholder{
class: PHClass::Vec{ nonzero: false, prio: 0 },
name: i.i("expr"),
}).into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i(";")][..])).into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec{ nonzero: false, prio: 1 },
name: i.i("rest"),
}).into_expr()
])).into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec{ nonzero: false, prio: 0 },
name: i.i("::suffix"),
}).into_expr(),
];
let matcher = mk_matcher(&pattern);
println!("{}", matcher.bundle(&i));
}
}

View File

@@ -0,0 +1,22 @@
/*
Construction:
convert pattern into hierarchy of plain, scan, middle
- plain: accept any sequence or any non-empty sequence
- scan: a single scalar pattern moves LTR or RTL, submatchers on either
side
- middle: two scalar patterns walk over all permutations of matches
while getting progressively closer to each other
Application:
walk over the current matcher's valid options and poll the submatchers
for each of them
*/
mod shared;
mod vec_match;
mod scal_match;
mod any_match;
mod build;
pub use shared::AnyMatcher;
pub use build::mk_matcher;

View File

@@ -0,0 +1,32 @@
use crate::{rule::state::{State, StateEntry}, ast::{Expr, Clause}};
use super::{shared::ScalMatcher, any_match::any_match};
pub fn scal_match<'a>(matcher: &ScalMatcher, expr: &'a Expr)
-> Option<State<'a>> {
match (matcher, &expr.value) {
(ScalMatcher::P(p1), Clause::P(p2)) if p1 == p2 => Some(State::new()),
(ScalMatcher::Name(n1), Clause::Name(n2)) if n1 == n2
=> Some(State::new()),
(ScalMatcher::Placeh(key), _)
=> Some(State::from([(*key, StateEntry::Scalar(expr))])),
(ScalMatcher::S(c1, b_mat), Clause::S(c2, body)) if c1 == c2
=> any_match(b_mat, &body[..]),
(ScalMatcher::Lambda(arg_mat, b_mat), Clause::Lambda(arg, body)) => {
let mut state = scal_match(&*arg_mat, &*arg)?;
state.extend(any_match(&*b_mat, &*body)?);
Some(state)
}
_ => None
}
}
pub fn scalv_match<'a>(matchers: &[ScalMatcher], seq: &'a [Expr])
-> Option<State<'a>> {
if seq.len() != matchers.len() {return None}
let mut state = State::new();
for (matcher, expr) in matchers.iter().zip(seq.iter()) {
state.extend(scal_match(matcher, expr)?);
}
Some(state)
}

View File

@@ -0,0 +1,163 @@
use std::fmt::Write;
use std::rc::Rc;
use crate::ast::Expr;
use crate::rule::{matcher::Matcher, state::State};
use crate::unwrap_or;
use crate::utils::{Side, print_nname};
use crate::interner::{Token, InternedDisplay, Interner};
use crate::representations::Primitive;
use super::{build::mk_matcher, any_match::any_match};
pub enum ScalMatcher {
P(Primitive),
Name(Token<Vec<Token<String>>>),
S(char, Box<AnyMatcher>),
Lambda(Box<ScalMatcher>, Box<AnyMatcher>),
Placeh(Token<String>),
}
pub enum VecMatcher {
Placeh{
key: Token<String>,
nonzero: bool
},
Scan{
left: Box<VecMatcher>,
sep: Vec<ScalMatcher>,
right: Box<VecMatcher>,
/// The separator traverses the sequence towards this side
direction: Side
},
Middle{
/// Matches the left outer region
left: Box<VecMatcher>,
/// Matches the left separator
left_sep: Vec<ScalMatcher>,
/// Matches the middle - can only ever be a plain placeholder
mid: Box<VecMatcher>,
/// Matches the right separator
right_sep: Vec<ScalMatcher>,
/// Matches the right outer region
right: Box<VecMatcher>,
/// Order of significance for sorting equally good solutions based on
/// the length of matches on either side.
///
/// Vectorial keys that appear on either side, in priority order
key_order: Vec<Token<String>>
}
}
pub enum AnyMatcher {
Scalar(Vec<ScalMatcher>),
Vec{
left: Vec<ScalMatcher>,
mid: VecMatcher,
right: Vec<ScalMatcher>
}
}
impl Matcher for AnyMatcher {
fn new(pattern: Rc<Vec<Expr>>) -> Self {
mk_matcher(&pattern)
}
fn apply<'a>(&self, source: &'a [Expr]) -> Option<State<'a>> {
any_match(self, source)
}
}
// ################ InternedDisplay ################
fn disp_scalv(
scalv: &Vec<ScalMatcher>,
f: &mut std::fmt::Formatter<'_>,
i: &Interner
) -> std::fmt::Result {
let (head, tail) = unwrap_or!(scalv.split_first(); return Ok(()));
head.fmt_i(f, i)?;
for s in tail.iter() {
write!(f, " ")?;
s.fmt_i(f, i)?;
}
Ok(())
}
impl InternedDisplay for ScalMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
match self {
Self::P(p) => write!(f, "{:?}", p),
Self::Placeh(n) => write!(f, "${}", i.r(*n)),
Self::Name(n) => write!(f, "{}", print_nname(*n, i)),
Self::S(c, body) => {
f.write_char(*c)?;
body.fmt_i(f, i)?;
f.write_char(match c {'('=>')','['=>']','{'=>'}',_=>unreachable!()})
},
Self::Lambda(arg, body) => {
f.write_char('\\')?;
arg.fmt_i(f, i)?;
f.write_char('.')?;
body.fmt_i(f, i)
}
}
}
}
impl InternedDisplay for VecMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
match self {
Self::Placeh { key, nonzero } => {
if *nonzero {f.write_char('.')?;};
write!(f, "..${}", i.r(*key))
}
Self::Scan { left, sep, right, direction } => {
let arrow = match direction {
Side::Left => " <== ",
Side::Right => " ==> "
};
write!(f, "Scan{{")?;
left.fmt_i(f, i)?;
f.write_str(arrow)?;
disp_scalv(sep, f, i)?;
f.write_str(arrow)?;
right.fmt_i(f, i)?;
write!(f, "}}")
},
Self::Middle { left, left_sep, mid, right_sep, right, .. } => {
write!(f, "Middle{{")?;
left.fmt_i(f, i)?;
f.write_str("|")?;
disp_scalv(left_sep, f, i)?;
f.write_str("|")?;
mid.fmt_i(f, i)?;
f.write_str("|")?;
disp_scalv(right_sep, f, i)?;
f.write_str("|")?;
right.fmt_i(f, i)?;
write!(f, "}}")
}
}
}
}
impl InternedDisplay for AnyMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
match self {
Self::Scalar(s) => {
write!(f, "(")?;
disp_scalv(s, f, i)?;
write!(f, ")")
}
Self::Vec { left, mid, right } => {
write!(f, "[")?;
disp_scalv(left, f, i)?;
write!(f, "|")?;
mid.fmt_i(f, i)?;
write!(f, "|")?;
disp_scalv(right, f, i)?;
write!(f, "]")
}
}
}
}

View File

@@ -0,0 +1,90 @@
use std::cmp::Ordering;
use itertools::Itertools;
use crate::unwrap_or;
use crate::ast::Expr;
use crate::rule::state::{State, StateEntry};
use super::scal_match::scalv_match;
use super::shared::VecMatcher;
pub fn vec_match<'a>(matcher: &VecMatcher, seq: &'a [Expr])
-> Option<State<'a>> {
match matcher {
VecMatcher::Placeh { key, nonzero } => {
if *nonzero && seq.len() == 0 {return None}
return Some(State::from([(*key, StateEntry::Vec(seq))]))
}
VecMatcher::Scan { left, sep, right, direction } => {
if seq.len() < sep.len() {return None}
for lpos in direction.walk(0..=seq.len() - sep.len()) {
let rpos = lpos + sep.len();
let mut state = unwrap_or!(vec_match(left, &seq[..lpos]); continue);
state.extend(unwrap_or!(scalv_match(sep, &seq[lpos..rpos]); continue));
state.extend(unwrap_or!(vec_match(right, &seq[rpos..]); continue));
return Some(state)
}
None
}
// XXX predict heap space usage and allocation count
VecMatcher::Middle { left, left_sep, mid, right_sep, right, key_order } => {
if seq.len() < left_sep.len() + right_sep.len() {return None}
// Valid locations for the left separator
let lposv = seq[..seq.len() - right_sep.len()]
.windows(left_sep.len())
.enumerate()
.filter_map(|(i, window)| {
scalv_match(left_sep, window).map(|s| (i, s))
})
.collect::<Vec<_>>();
// Valid locations for the right separator
let rposv = seq[left_sep.len()..]
.windows(right_sep.len())
.enumerate()
.filter_map(|(i, window)| {
scalv_match(right_sep, window).map(|s| (i, s))
})
.collect::<Vec<_>>();
// Valid combinations of locations for the separators
let mut pos_pairs = lposv.into_iter()
.cartesian_product(rposv.into_iter())
.filter(|((lpos, _), (rpos, _))| lpos + left_sep.len() <= *rpos)
.map(|((lpos, mut lstate), (rpos, rstate))| {
lstate.extend(rstate);
(lpos, rpos, lstate)
})
.collect::<Vec<_>>();
// In descending order of size
pos_pairs.sort_by_key(|(l, r, _)| -((r - l) as i64));
let eql_clusters = pos_pairs.into_iter()
.group_by(|(al, ar, _)| ar - al);
for (_gap_size, cluster) in eql_clusters.into_iter() {
let best_candidate = cluster.into_iter()
.filter_map(|(lpos, rpos, mut state)| {
state.extend(vec_match(left, &seq[..lpos])?);
state.extend(vec_match(mid, &seq[lpos + left_sep.len()..rpos])?);
state.extend(vec_match(right, &seq[rpos + right_sep.len()..])?);
Some(state)
})
.max_by(|a, b| {
for key in key_order {
let aslc = if let Some(StateEntry::Vec(s)) = a.get(key) {s}
else {panic!("key_order references scalar or missing")};
let bslc = if let Some(StateEntry::Vec(s)) = b.get(key) {s}
else {panic!("key_order references scalar or missing")};
match aslc.len().cmp(&bslc.len()) {
Ordering::Equal => (),
any => return any
}
}
Ordering::Equal
});
if let Some(state) = best_candidate {
return Some(state)
}
}
None
}
}
}

View File

@@ -1,7 +1,15 @@
mod executor;
// mod executor;
mod rule_error;
mod repository;
mod prepare_rule;
mod matcher;
mod update_first_seq;
mod state;
mod matcher_second;
mod vec_attrs;
// pub use rule::Rule;
pub use rule_error::RuleError;
pub use repository::Repository;
pub use repository::{Repository, Repo};
pub use matcher_second::AnyMatcher;

120
src/rule/prepare_rule.rs Normal file
View File

@@ -0,0 +1,120 @@
use std::rc::Rc;
use hashbrown::HashMap;
use itertools::Itertools;
use crate::representations::location::Location;
use crate::interner::{Token, Interner};
use crate::ast::{PHClass, Expr, Clause, Placeholder, Rule};
use super::RuleError;
use super::vec_attrs::vec_attrs;
/// Ensure that the rule's source begins and ends with a vectorial without
/// changing its meaning
fn pad(mut rule: Rule, i: &Interner) -> Rule {
let class: PHClass = PHClass::Vec { nonzero: false, prio: 0 };
let empty_exprv: &[Expr] = &[];
let prefix_exprv: &[Expr] = &[Expr{
location: Location::Unknown,
value: Clause::Placeh(Placeholder{
name: i.i("::prefix"),
class
}),
}];
let suffix_exprv: &[Expr] = &[Expr{
location: Location::Unknown,
value: Clause::Placeh(Placeholder{
name: i.i("::suffix"),
class
}),
}];
let rule_head = rule.source.first().expect("Src can never be empty!");
let head_explicit = vec_attrs(rule_head).is_some();
let rule_tail = rule.source.last().expect("Unreachable branch!");
let tail_explicit = vec_attrs(rule_tail).is_some();
let prefix_vec = if head_explicit {empty_exprv} else {prefix_exprv};
let suffix_vec = if tail_explicit {empty_exprv} else {suffix_exprv};
rule.source = Rc::new(
prefix_vec.iter()
.chain(rule.source.iter())
.chain(suffix_vec.iter())
.cloned()
.collect()
);
rule.target = Rc::new(
prefix_vec.iter()
.chain(rule.target.iter())
.chain(suffix_vec.iter())
.cloned()
.collect()
);
rule
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum PHType {
Scalar,
Vec{ nonzero: bool }
}
impl From<PHClass> for PHType {
fn from(value: PHClass) -> Self {
match value {
PHClass::Scalar => Self::Scalar,
PHClass::Vec { nonzero, .. } => Self::Vec{ nonzero }
}
}
}
fn check_rec_expr(
expr: &Expr,
types: &mut HashMap<Token<String>, PHType>,
in_template: bool
) -> Result<(), RuleError> {
match &expr.value {
Clause::Name(_) | Clause::P(_) => Ok(()),
Clause::Placeh(Placeholder{ name, class }) => {
let typ = class.clone().into();
// in a template, the type must be known and identical
// outside template (in pattern) the type must be unknown
if let Some(known) = types.insert(*name, typ) {
if !in_template { Err(RuleError::Multiple(*name)) }
else if known != typ { Err(RuleError::TypeMismatch(*name)) }
else { Ok(()) }
} else if in_template { Err(RuleError::Missing(*name)) }
else { Ok(()) }
}
Clause::Lambda(arg, body) => {
check_rec_expr(arg.as_ref(), types, in_template)?;
check_rec_exprv(&body, types, in_template)
}
Clause::S(_, body) => check_rec_exprv(&body, types, in_template)
}
}
fn check_rec_exprv(
exprv: &[Expr],
types: &mut HashMap<Token<String>, PHType>,
in_template: bool
) -> Result<(), RuleError> {
for (l, r) in exprv.iter().tuple_windows::<(_, _)>() {
check_rec_expr(l, types, in_template)?;
if !in_template { // in a pattern vectorials cannot follow each other
if let (Some(ld), Some(rd)) = (vec_attrs(l), vec_attrs(r)) {
return Err(RuleError::VecNeighbors(ld.0, rd.0))
}
}
}
if let Some(e) = exprv.last() {
check_rec_expr(e, types, in_template)
} else { Ok(()) }
}
pub fn prepare_rule(rule: Rule, i: &Interner) -> Result<Rule, RuleError> {
// Dimension check
let mut types = HashMap::new();
check_rec_exprv(&rule.source, &mut types, false)?;
check_rec_exprv(&rule.target, &mut types, true)?;
// Padding
Ok(pad(rule, i))
}

View File

@@ -1,62 +1,137 @@
use std::fmt::Debug;
use std::rc::Rc;
use std::fmt::{Debug, Write};
use mappable_rc::Mrc;
use hashbrown::HashSet;
use crate::representations::ast::Expr;
use crate::interner::{Token, Interner, InternedDisplay};
use crate::utils::Substack;
use crate::ast::{Expr, Rule};
use super::{super::ast::Rule, executor::execute, RuleError};
use super::state::apply_exprv;
use super::{update_first_seq, AnyMatcher};
use super::matcher::Matcher;
use super::prepare_rule::prepare_rule;
use super::RuleError;
#[derive(Debug)]
pub struct CachedRule<M: Matcher> {
matcher: M,
source: Rc<Vec<Expr>>,
template: Rc<Vec<Expr>>
}
impl<M: InternedDisplay + Matcher> InternedDisplay for CachedRule<M> {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
for item in self.source.iter() {
item.fmt_i(f, i)?;
f.write_char(' ')?;
}
write!(f, "is matched by ")?;
self.matcher.fmt_i(f, i)
}
}
/// Manages a priority queue of substitution rules and allows to apply them
pub struct Repository(Vec<Rule>);
impl Repository {
pub fn new(mut rules: Vec<Rule>) -> Self {
pub struct Repository<M: Matcher> {
cache: Vec<(CachedRule<M>, HashSet<Token<Vec<Token<String>>>>)>
}
impl<M: Matcher> Repository<M> {
pub fn new(mut rules: Vec<Rule>, i: &Interner)
-> Result<Self, (Rule, RuleError)>
{
rules.sort_by_key(|r| -r.prio);
Self(rules)
}
pub fn step(&self, code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
for rule in self.0.iter() {
if let Some(out) = execute(
Mrc::clone(&rule.source), Mrc::clone(&rule.target),
Mrc::clone(&code)
)? {return Ok(Some(out))}
}
Ok(None)
let cache = rules.into_iter()
.map(|r| {
let rule = prepare_rule(r.clone(), i)
.map_err(|e| (r, e))?;
let mut glossary = HashSet::new();
for e in rule.source.iter() {
e.visit_names(Substack::Bottom, &mut |op| {
glossary.insert(op);
})
}
let matcher = M::new(rule.source.clone());
let prep = CachedRule{
matcher,
source: rule.source,
template: rule.target
};
Ok((prep, glossary))
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self{cache})
}
/// Attempt to run each rule in priority order once
pub fn pass(&self, mut code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
let mut ran_once = false;
for rule in self.0.iter() {
if let Some(tmp) = execute(
Mrc::clone(&rule.source), Mrc::clone(&rule.target),
Mrc::clone(&code)
)? {
ran_once = true;
code = tmp;
}
pub fn step(&self, code: &Expr) -> Option<Expr> {
let mut glossary = HashSet::new();
code.visit_names(Substack::Bottom, &mut |op| { glossary.insert(op); });
// println!("Glossary for code: {:?}", print_nname_seq(glossary.iter(), i));
for (rule, deps) in self.cache.iter() {
if !deps.is_subset(&glossary) { continue; }
let product = update_first_seq::expr(code, &mut |exprv| {
let state = rule.matcher.apply(exprv.as_slice())?;
let result = apply_exprv(&rule.template, &state);
Some(Rc::new(result))
});
if let Some(newcode) = product {return Some(newcode)}
}
Ok(if ran_once {Some(code)} else {None})
None
}
/// Keep running the matching rule with the highest priority until no
/// rules match. WARNING: this function might not terminate
#[allow(unused)]
pub fn pass(&self, code: &Expr) -> Option<Expr> {
todo!()
// if let Some(mut processed) = self.step(code) {
// while let Some(out) = self.step(&processed) {
// processed = out
// }
// Some(processed)
// } else {None}
}
/// Attempt to run each rule in priority order `limit` times. Returns the final
/// tree and the number of iterations left to the limit.
pub fn long_step(&self, mut code: Mrc<[Expr]>, mut limit: usize)
-> Result<(Mrc<[Expr]>, usize), RuleError> {
while let Some(tmp) = self.pass(Mrc::clone(&code))? {
if 0 >= limit {break}
limit -= 1;
code = tmp
}
Ok((code, limit))
#[allow(unused)]
pub fn long_step(&self, code: &Expr, mut limit: usize)
-> Result<(Expr, usize), RuleError>
{
todo!()
// if limit == 0 {return Ok((code.clone(), 0))}
// if let Some(mut processed) = self.step(code) {
// limit -= 1;
// if limit == 0 {return Ok((processed.clone(), 0))}
// while let Some(out) = self.step(&processed) {
// limit -= 1;
// if limit == 0 { return Ok((out, 0)) }
// processed = out;
// }
// Ok((processed, limit))
// } else {Ok((code.clone(), limit))}
}
}
impl Debug for Repository {
impl<M: Debug + Matcher> Debug for Repository<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for rule in self.0.iter() {
for rule in self.cache.iter() {
writeln!(f, "{rule:?}")?
}
Ok(())
}
}
impl<M: InternedDisplay + Matcher> InternedDisplay for Repository<M> {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
writeln!(f, "Repository[")?;
for (item, _) in self.cache.iter() {
write!(f, "\t")?;
item.fmt_i(f, i)?;
writeln!(f)?;
}
write!(f, "]")
}
}
pub type Repo = Repository<AnyMatcher>;

View File

@@ -1,18 +1,36 @@
use std::{fmt, error::Error};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
use crate::interner::{Token, InternedDisplay, Interner};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleError {
BadState(String),
ScalarVecMismatch(String)
Missing(Token<String>),
TypeMismatch(Token<String>),
/// Multiple occurences of a placeholder in a pattern are no longer
/// supported.
Multiple(Token<String>),
VecNeighbors(Token<String>, Token<String>),
}
impl fmt::Display for RuleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadState(key) => write!(f, "Key {:?} not in match pattern", key),
Self::ScalarVecMismatch(key) =>
write!(f, "Key {:?} used inconsistently with and without ellipsis", key)
impl InternedDisplay for RuleError {
fn fmt_i(&self, f: &mut fmt::Formatter<'_>, i: &Interner) -> fmt::Result {
match *self {
Self::Missing(key) => write!(f,
"Key {:?} not in match pattern",
i.r(key)
),
Self::TypeMismatch(key) => write!(f,
"Key {:?} used inconsistently with and without ellipsis",
i.r(key)
),
Self::Multiple(key) => write!(f,
"Key {:?} appears multiple times in match pattern",
i.r(key)
),
Self::VecNeighbors(left, right) => write!(f,
"Keys {:?} and {:?} are two vectorials right next to each other",
i.r(left), i.r(right)
)
}
}
}
impl Error for RuleError {}
}

51
src/rule/state.rs Normal file
View File

@@ -0,0 +1,51 @@
use std::rc::Rc;
use hashbrown::HashMap;
use itertools::Itertools;
use crate::interner::Token;
use crate::ast::{Expr, Clause, Placeholder, PHClass};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StateEntry<'a> {
Vec(&'a[Expr]),
Scalar(&'a Expr)
}
pub type State<'a> = HashMap<Token<String>, StateEntry<'a>>;
pub fn apply_exprv(template: &[Expr], state: &State) -> Vec<Expr> {
template.iter()
.map(|e| apply_expr(e, state))
.flat_map(Vec::into_iter)
.collect()
}
pub fn apply_expr(template: &Expr, state: &State) -> Vec<Expr> {
let Expr{ location, value } = template;
match value {
Clause::P(_) | Clause::Name(_) => vec![template.clone()],
Clause::S(c, body) => vec![Expr{
location: location.clone(),
value: Clause::S(*c, Rc::new(apply_exprv(body.as_slice(), state)))
}],
Clause::Placeh(Placeholder{ name, class }) => {
let value = if let Some(&v) = state.get(name) {v}
else {panic!("Placeholder does not have a value in state")};
match (class, value) {
(PHClass::Scalar, StateEntry::Scalar(item)) => vec![item.clone()],
(PHClass::Vec{..}, StateEntry::Vec(chunk)) => chunk.to_vec(),
_ => panic!("Type mismatch between template and state")
}
}
Clause::Lambda(arg, body) => vec![Expr{
location: location.clone(),
value: Clause::Lambda(
Rc::new(apply_expr(arg.as_ref(), state).into_iter()
.exactly_one()
.expect("Lambda arguments can only ever be scalar")
),
Rc::new(apply_exprv(&body[..], state))
)
}]
}
}

View File

@@ -0,0 +1,36 @@
use std::rc::Rc;
use crate::utils::replace_first;
use crate::ast::{Expr, Clause};
/// 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
pub fn exprv<F>(input: Rc<Vec<Expr>>, pred: &mut F) -> Option<Rc<Vec<Expr>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let o@Some(_) = pred(input.clone()) {return o}
replace_first(input.as_ref(), |ex| expr(ex, pred))
.map(|i| Rc::new(i.collect()))
}
pub fn expr<F>(input: &Expr, pred: &mut F) -> Option<Expr>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let Some(value) = clause(&input.value, pred) {
Some(Expr{ value, location: input.location.clone() })
} else {None}
}
pub fn clause<F>(c: &Clause, pred: &mut F) -> Option<Clause>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
match c {
Clause::P(_) | Clause::Placeh {..} | Clause::Name {..} => None,
Clause::Lambda(arg, body) => {
if let Some(arg) = expr(arg.as_ref(), pred) {
Some(Clause::Lambda(Rc::new(arg), body.clone()))
} else if let Some(body) = exprv(body.clone(), pred) {
Some(Clause::Lambda(arg.clone(), body))
} else {None}
}
Clause::S(c, body) => Some(Clause::S(*c, exprv(body.clone(), pred)?)),
}
}

10
src/rule/vec_attrs.rs Normal file
View File

@@ -0,0 +1,10 @@
use crate::interner::Token;
use crate::ast::{Expr, PHClass, Placeholder, Clause};
/// Returns the name, priority and nonzero of the expression if it is
/// a vectorial placeholder
pub fn vec_attrs(expr: &Expr) -> Option<(Token<String>, u64, bool)> {
if let Clause::Placeh(
Placeholder{ class: PHClass::Vec{ prio, nonzero }, name }
) = expr.value {Some((name, prio, nonzero))} else {None}
}