#![forbid(unsafe_code)]
use merc_aterm::Protected;
use merc_aterm::Term;
use merc_aterm::ThreadTermPool;
use merc_data::DataExpression;
use merc_data::DataExpressionRef;
use merc_data::is_data_application;
use super::DataPosition;
pub type DataSubstitutionBuilder = Protected<Vec<DataExpressionRef<'static>>>;
pub fn data_substitute(
tp: &ThreadTermPool,
t: &DataExpressionRef<'_>,
new_subterm: DataExpression,
position: &DataPosition,
) -> DataExpression {
let mut args = Protected::new(vec![]);
substitute_rec(tp, t, new_subterm, position.indices(), &mut args, 0)
}
pub fn data_substitute_with(
builder: &mut DataSubstitutionBuilder,
tp: &ThreadTermPool,
t: &DataExpressionRef<'_>,
new_subterm: DataExpression,
position: &DataPosition,
) -> DataExpression {
substitute_rec(tp, t, new_subterm, position.indices(), builder, 0)
}
fn substitute_rec(
tp: &ThreadTermPool,
t: &DataExpressionRef<'_>,
new_subterm: DataExpression,
p: &[usize],
args: &mut DataSubstitutionBuilder,
depth: usize,
) -> DataExpression {
if p.len() == depth {
new_subterm
} else {
let new_child_index = p[depth];
let new_child = substitute_rec(tp, &t.arg(new_child_index).into(), new_subterm, p, args, depth + 1);
debug_assert!(
is_data_application(t),
"Can only perform data substitution on DataApplications"
);
let mut write_args = args.write();
for (index, arg) in t.arguments().enumerate() {
if index == new_child_index {
let t = write_args.protect(&new_child);
write_args.push(t.into());
} else {
let t = write_args.protect(&arg);
write_args.push(t.into());
}
}
let result = tp.create_term(&t.get_head_symbol(), &write_args);
drop(write_args);
args.write().clear();
result.protect().into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use merc_aterm::THREAD_TERM_POOL;
use crate::utilities::DataPosition;
use crate::utilities::DataPositionIndexed;
#[test]
fn test_data_substitute() {
let t = DataExpression::from_string("s(s(a))").unwrap();
let t0 = DataExpression::from_string("0").unwrap();
let result =
THREAD_TERM_POOL.with_borrow(|tp| data_substitute(tp, &t.copy(), t0.clone(), &DataPosition::new(&[1, 1])));
assert_eq!(t0, result.get_data_position(&DataPosition::new(&vec![1, 1])).protect());
}
}