forked from Orchid/orchid
transfer commit
This commit is contained in:
@@ -11,55 +11,55 @@ use super::super::RuleError;
|
||||
|
||||
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 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))?;
|
||||
}
|
||||
_ => ()
|
||||
};
|
||||
let Expr(val, typ) = pattern;
|
||||
verify_clause(val, is_vec)?;
|
||||
for typ in typ.as_ref() {
|
||||
verify_clause(typ, 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(())
|
||||
}
|
||||
|
||||
|
||||
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))}, to_mrc_slice(vec![]));
|
||||
let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![]));
|
||||
// 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());
|
||||
let prefix_expr = Expr(Clause::Placeh{key: "::prefix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![]));
|
||||
let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![]));
|
||||
// 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
|
||||
@@ -67,117 +67,117 @@ fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) {
|
||||
/// 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
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// fn write_clause_rec(state: &State, clause: &Clause) ->
|
||||
|
||||
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 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::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) =>
|
||||
box_once(Expr(c.to_owned(), out_typ.to_owned()))
|
||||
}
|
||||
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 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::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) =>
|
||||
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]> {
|
||||
eprintln!("Writing {tpl:?} with state {state:?}");
|
||||
tpl.iter().flat_map(|xpr| write_expr_rec(state, xpr)).collect()
|
||||
eprintln!("Writing {tpl:?} with state {state:?}");
|
||||
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))
|
||||
}))
|
||||
// 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))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ use super::split_at_max_vec::split_at_max_vec;
|
||||
#[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)
|
||||
}
|
||||
fn clone(&self) -> Self {
|
||||
let CacheEntry(mrc, matcher) = self;
|
||||
CacheEntry(Mrc::clone(mrc), matcher)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,281 +31,281 @@ impl<'a> Clone for CacheEntry<'a> {
|
||||
/// 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>>,
|
||||
/// 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
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 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<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<&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}
|
||||
}
|
||||
/// If clause is a name, the qualified name this can match
|
||||
pub fn clause_qual_name(&self) -> Option<Mrc<[String]>> {
|
||||
if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} 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<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)))
|
||||
}
|
||||
/// 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}
|
||||
}
|
||||
|
||||
/// 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.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned())
|
||||
}
|
||||
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}
|
||||
}
|
||||
|
||||
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::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
|
||||
}
|
||||
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<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)))
|
||||
}
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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)
|
||||
/// 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)}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
pub fn get_matcher_cache<'a>()
|
||||
-> Cache<'a, CacheEntry<'a>, Option<State>> {
|
||||
Cache::new(
|
||||
|CacheEntry(tgt, matcher), cache| {
|
||||
matcher.match_range_cached(tgt, cache)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// 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.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<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::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<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())
|
||||
}
|
||||
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()
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,27 +7,27 @@ 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
|
||||
})
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ use crate::ast::Expr;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Entry {
|
||||
Vec(Rc<Vec<Expr>>),
|
||||
Scalar(Rc<Expr>),
|
||||
Name(Rc<String>),
|
||||
NameOpt(Option<Rc<String>>)
|
||||
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.
|
||||
@@ -19,129 +19,129 @@ 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))
|
||||
}
|
||||
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 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())));
|
||||
}
|
||||
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, 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
|
||||
}
|
||||
Some(self)
|
||||
} else {return None}
|
||||
} else {
|
||||
self.0.insert(k.to_string(), Entry::NameOpt(v.map(|s| Rc::new(s.to_string()))));
|
||||
}
|
||||
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, 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, 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()
|
||||
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>;
|
||||
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)
|
||||
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>;
|
||||
type Output = Option<State>;
|
||||
|
||||
fn add(self, rhs: Option<State>) -> Self::Output {
|
||||
rhs.and_then(|s| self + s)
|
||||
}
|
||||
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;
|
||||
type Output = Entry;
|
||||
|
||||
fn index(&self, index: S) -> &Self::Output {
|
||||
return &self.0[index.as_ref()]
|
||||
}
|
||||
fn index(&self, index: S) -> &Self::Output {
|
||||
return &self.0[index.as_ref()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = (String, Entry);
|
||||
type Item = (String, Entry);
|
||||
|
||||
type IntoIter = hashbrown::hash_map::IntoIter<String, Entry>;
|
||||
type IntoIter = hashbrown::hash_map::IntoIter<String, Entry>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -9,44 +9,44 @@ use super::{super::ast::Rule, executor::execute, RuleError};
|
||||
/// 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 {
|
||||
rules.sort_by_key(|r| r.prio);
|
||||
Self(rules)
|
||||
}
|
||||
pub fn new(mut rules: Vec<Rule>) -> Self {
|
||||
rules.sort_by_key(|r| r.prio);
|
||||
Self(rules)
|
||||
}
|
||||
|
||||
/// Attempt to run each rule in priority order once
|
||||
pub fn step(&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;
|
||||
}
|
||||
}
|
||||
Ok(if ran_once {Some(code)} else {None})
|
||||
/// Attempt to run each rule in priority order once
|
||||
pub fn step(&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;
|
||||
}
|
||||
}
|
||||
Ok(if ran_once {Some(code)} 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.step(Mrc::clone(&code))? {
|
||||
if 0 >= limit {break}
|
||||
limit -= 1;
|
||||
code = tmp
|
||||
}
|
||||
Ok((code, limit))
|
||||
/// 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.step(Mrc::clone(&code))? {
|
||||
if 0 >= limit {break}
|
||||
limit -= 1;
|
||||
code = tmp
|
||||
}
|
||||
Ok((code, limit))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Repository {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for rule in self.0.iter() {
|
||||
writeln!(f, "{rule:?}")?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for rule in self.0.iter() {
|
||||
writeln!(f, "{rule:?}")?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@ use std::{fmt, error::Error};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum RuleError {
|
||||
BadState(String),
|
||||
ScalarVecMismatch(String)
|
||||
BadState(String),
|
||||
ScalarVecMismatch(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)
|
||||
}
|
||||
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 Error for RuleError {}
|
||||
Reference in New Issue
Block a user