use crate::cost;
use crate::lang::Lang;
use crate::walk::Walk;
#[derive(Debug)]
pub enum Edit<L: Lang> {
Replace { target: L::Id, with: L::Id },
Delete { node: L::Id },
Splice {
node: L::Id,
index: usize,
remove: usize,
insert: Vec<L::Id>,
},
Multi(Vec<Edit<L>>),
}
impl<L: Lang> Edit<L> {
pub fn cost_reduction(&self, lang: &mut L) -> u64 {
match self {
Edit::Replace { target, with } => {
let a = cost::subtree_cost(lang, *target);
let b = cost::subtree_cost(lang, *with);
a.saturating_sub(b)
}
Edit::Delete { node } => cost::subtree_cost(lang, *node),
Edit::Splice {
node,
index,
remove,
insert,
} => {
let children = lang.children(*node);
let mut removed = 0u64;
for &c in &children[*index..(*index + remove).min(children.len())] {
removed += cost::subtree_cost(lang, c);
}
let mut added = 0u64;
for &n in insert.iter() {
added += cost::subtree_cost(lang, n);
}
removed.saturating_sub(added)
}
Edit::Multi(edits) => edits.iter().map(|e| e.cost_reduction(lang)).sum(),
}
}
}
pub struct RewriteCtx<'a, L: Lang> {
pub lang: &'a mut L,
pub(crate) walk: &'a Walk<L>,
}
impl<'a, L: Lang> RewriteCtx<'a, L> {
pub fn parent(&self, id: L::Id) -> Option<L::Id> {
self.walk.parent(id)
}
pub fn index(&self, id: L::Id) -> Option<usize> {
self.walk.index(id)
}
pub fn root(&self) -> L::Id {
self.walk.root
}
}
pub trait Rule<L: Lang> {
fn name(&self) -> &'static str;
fn kinds(&self) -> &'static [crate::kind::NodeKind] {
&[]
}
fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>>;
fn structural(&self) -> bool {
false
}
}
pub(crate) fn apply_edit<L: Lang>(
lang: &mut L,
walk: &Walk<L>,
edit: &Edit<L>,
) -> Result<(), &'static str> {
match edit {
Edit::Replace { target, with } => {
let (parent, index) = walk
.parents
.get(target)
.copied()
.ok_or("cannot replace root")?;
lang.set_child(parent, index, *with);
Ok(())
}
Edit::Delete { node } => {
let (parent, index) = walk.parents.get(node).copied().ok_or("cannot delete root")?;
lang.remove_child(parent, index);
Ok(())
}
Edit::Splice {
node,
index,
remove,
insert,
} => {
lang.splice(*node, *index, *remove, insert.clone());
Ok(())
}
Edit::Multi(edits) => {
for e in edits {
apply_edit(lang, walk, e)?;
}
Ok(())
}
}
}
pub(crate) fn is_replace_only<L: Lang>(edit: &Edit<L>) -> bool {
match edit {
Edit::Replace { .. } => true,
Edit::Multi(es) => es.iter().all(|e| matches!(e, Edit::Replace { .. })),
_ => false,
}
}
pub(crate) fn collect_footprint<L: Lang>(lang: &L, edit: &Edit<L>, out: &mut Vec<L::Id>) {
match edit {
Edit::Replace { target, .. } => collect_subtree(lang, *target, out),
Edit::Delete { node } => collect_subtree(lang, *node, out),
Edit::Splice {
node, index, remove, ..
} => {
let ch = lang.children(*node);
if *index <= ch.len() && *index + *remove <= ch.len() {
for &c in &ch[*index..*index + *remove] {
collect_subtree(lang, c, out);
}
}
}
Edit::Multi(es) => {
for e in es {
collect_footprint(lang, e, out);
}
}
}
}
fn collect_subtree<L: Lang>(lang: &L, id: L::Id, out: &mut Vec<L::Id>) {
let mut stack = vec![id];
while let Some(n) = stack.pop() {
out.push(n);
for &c in lang.children(n) {
stack.push(c);
}
}
}
pub(crate) fn collect_splice_expectations<L: Lang>(
lang: &L,
edit: &Edit<L>,
out: &mut Vec<Vec<L::Id>>,
) {
match edit {
Edit::Splice {
node, index, remove, ..
} => {
let ch = lang.children(*node);
if *index <= ch.len() && *index + *remove <= ch.len() {
out.push(ch[*index..*index + *remove].to_vec());
} else {
out.push(Vec::new());
}
}
Edit::Multi(es) => {
for e in es {
collect_splice_expectations(lang, e, out);
}
}
_ => {}
}
}
pub(crate) fn verify_deferred<L: Lang>(
lang: &L,
snap: &Walk<L>,
edit: &Edit<L>,
expects: &mut &[Vec<L::Id>],
) -> bool {
match edit {
Edit::Replace { target, .. } => match snap.parents.get(target) {
Some((p, _)) => lang.children(*p).contains(target),
None => false,
},
Edit::Delete { node } => match snap.parents.get(node) {
Some((p, _)) => lang.children(*p).contains(node),
None => false,
},
Edit::Splice {
node, index, remove, ..
} => {
let Some(exp) = expects.first() else {
return false;
};
*expects = &expects[1..];
let ch = lang.children(*node);
*index <= ch.len()
&& *index + *remove <= ch.len()
&& &ch[*index..*index + *remove] == exp
}
Edit::Multi(es) => es.iter().all(|e| verify_deferred(lang, snap, e, expects)),
}
}
pub(crate) fn apply_deferred<L: Lang>(lang: &mut L, snap: &Walk<L>, edit: &Edit<L>) {
match edit {
Edit::Replace { target, with } => {
if let Some((p, _)) = snap.parents.get(target) {
if let Some(i) = lang.children(*p).iter().position(|&c| c == *target) {
lang.set_child(*p, i, *with);
}
}
}
Edit::Delete { node } => {
if let Some((p, _)) = snap.parents.get(node) {
if let Some(i) = lang.children(*p).iter().position(|&c| c == *node) {
lang.remove_child(*p, i);
}
}
}
Edit::Splice {
node, index, remove, insert,
} => {
lang.splice(*node, *index, *remove, insert.clone());
}
Edit::Multi(es) => {
for e in es {
apply_deferred(lang, snap, e);
}
}
}
}
pub(crate) fn structural_sort_key<L: Lang>(snap: &Walk<L>, edit: &Edit<L>) -> (L::Id, usize) {
fn first_pos<L: Lang>(snap: &Walk<L>, e: &Edit<L>) -> Option<(L::Id, usize)> {
match e {
Edit::Splice { node, index, .. } => Some((*node, *index)),
Edit::Delete { node } => snap
.parents
.get(node)
.map(|(p, i)| (*p, *i)),
Edit::Replace { target, .. } => snap
.parents
.get(target)
.map(|(p, i)| (*p, *i)),
Edit::Multi(es) => es.first().and_then(|x| first_pos(snap, x)),
}
}
first_pos(snap, edit).unwrap_or((snap.root, 0))
}