use crate::{IrId, IrKind, IrPool};
#[cfg(test)]
pub fn deriv(pool: &mut IrPool, root: IrId, c: char) -> IrId {
deriv_at(pool, root, c, 1, 2)
}
pub fn deriv_at(pool: &mut IrPool, root: IrId, c: char, position: usize, input_len: usize) -> IrId {
let key = (root, c, position == 0, position == input_len);
if let Some(id) = pool.deriv_memo.get(&key) {
return *id;
}
let kind = pool.kind(root).clone();
let result = match kind {
IrKind::Zero | IrKind::One | IrKind::Anchor(..) => pool.zero(),
IrKind::Class(set, _) => {
if pool.get_class_set(set).contains(c) {
pool.one()
} else {
pool.zero()
}
}
IrKind::Alt(left, right, prov, ordered) => {
let left = deriv_at(pool, left, c, position, input_len);
let right = deriv_at(pool, right, c, position, input_len);
pool.alt(left, right, prov, ordered)
}
IrKind::Seq(left, right, prov) => {
let left_deriv = deriv_at(pool, left, c, position, input_len);
let sequence = pool.seq(left_deriv, right, prov);
if nullable_at(pool, left, position, input_len) {
let right_deriv = deriv_at(pool, right, c, position, input_len);
if pool.prefers_empty(left) {
pool.alt(right_deriv, sequence, prov, false)
} else {
pool.alt(sequence, right_deriv, prov, false)
}
} else {
sequence
}
}
IrKind::Star(inner, prov, _) => {
let derivative = deriv_at(pool, inner, c, position, input_len);
pool.seq(derivative, root, prov)
}
};
pool.deriv_memo.insert(key, result);
result
}
pub fn nullable_at(pool: &mut IrPool, root: IrId, position: usize, input_len: usize) -> bool {
match pool.kind(root).clone() {
IrKind::Zero | IrKind::Class(..) => false,
IrKind::One | IrKind::Star(..) => true,
IrKind::Anchor(regast_syntax::AnchorKind::Start, _) => position == 0,
IrKind::Anchor(regast_syntax::AnchorKind::End, _) => position == input_len,
IrKind::Alt(left, right, _, _) => {
nullable_at(pool, left, position, input_len)
|| nullable_at(pool, right, position, input_len)
}
IrKind::Seq(left, right, _) => {
nullable_at(pool, left, position, input_len)
&& nullable_at(pool, right, position, input_len)
}
}
}
#[cfg(test)]
mod tests {
use regast_syntax::ClassSet;
use super::*;
use crate::Prov;
#[test]
fn derivative_definitions_hold_for_atoms_and_star() {
let mut pool = IrPool::new();
let set_id = pool.class_set(ClassSet::singleton('a'));
let atom = pool.class(set_id, Prov::Synth(regast_syntax::NodeId(0)));
assert_eq!(deriv(&mut pool, atom, 'a'), pool.one());
assert_eq!(deriv(&mut pool, atom, 'b'), pool.zero());
let star = pool.star(atom, Prov::Synth(regast_syntax::NodeId(0)), true);
let derivative = deriv(&mut pool, star, 'a');
assert!(matches!(pool.kind(derivative), IrKind::Seq(_, id, _) if *id == star));
}
}