use std::rc::Rc;
use super::Goal;
use crate::{
constraints::{Constraint, ResolveFn},
LVarList, State, StateIterator,
};
#[derive(Debug)]
pub enum Not {
Fail,
Maybe(Rc<NotConstraint>),
}
pub fn not(goal: impl Goal) -> Not {
let mut inner_states = goal.apply(State::new()).into_states().peekable();
if inner_states.peek().is_none() {
Not::Fail
} else {
Not::Maybe(Rc::new(NotConstraint {
goal: Rc::new(goal),
vars: LVarList::flatten(inner_states.map(|s| s.vars())),
}))
}
}
impl Goal for Not {
fn apply(&self, state: State) -> Option<State> {
match self {
Not::Fail => Some(state),
Not::Maybe(constraint) => {
let vars = constraint.vars.without_resolved_in(&state);
state.constrain(Rc::new(NotConstraint {
goal: constraint.goal.clone(),
vars,
}))
}
}
}
}
#[derive(Debug)]
pub struct NotConstraint {
goal: Rc<dyn Goal>,
vars: LVarList,
}
fn any_succeed(state: Option<State>) -> bool {
state.into_states().next().is_some()
}
impl Constraint for NotConstraint {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
if any_succeed(self.goal.apply(state.clone())) {
let open_vars = self.vars.without_resolved_in(state);
if open_vars.is_empty() {
Ok(Box::new(|_| None))
} else {
Err(open_vars)
}
} else {
Ok(Box::new(Some))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
any, goal_vec,
goals::{assert_1, fail::Fail, succeed::Succeed},
unify, LVar,
};
#[test]
fn succeeds_when_child_fails() {
let state = State::new();
let goal = not(Fail);
let result = goal.apply(state);
assert!(result.is_some());
}
#[test]
fn fails_when_child_succeeds() {
let state = State::new();
let goal = not(Succeed);
let result = goal.apply(state);
assert!(result.is_none());
}
#[test]
fn succeeds_with_unify() {
let x = LVar::new();
let goals = goal_vec![not(unify(x, 2)), unify(x, 1)];
goals.assert_permutations_resolve_to(&x, vec![1]);
}
#[test]
fn succeeds_with_constraints() {
let x = LVar::new();
let goals = goal_vec![not(assert_1(x, |x| *x == 2)), unify(x, 1)];
goals.assert_permutations_resolve_to(&x, vec![1]);
}
#[test]
fn fails_with_unify() {
let x = LVar::new();
let goals = goal_vec![not(unify(x, 1)), unify(x, 1)];
goals.assert_permutations_resolve_to(&x, vec![]);
}
#[test]
fn fails_with_constraints() {
let x = LVar::new();
let goals = goal_vec![not(assert_1(x, |x| *x == 1)), unify(x, 1)];
goals.assert_permutations_resolve_to(&x, vec![]);
}
#[test]
fn succeeds_with_forking_goals() {
let x = LVar::new();
let goals = goal_vec![unify(x, 1), not(any![unify(x, 2), unify(x, 3)])];
goals.assert_permutations_resolve_to(&x, vec![1]);
}
#[test]
fn fails_with_forking_goals() {
let x = LVar::new();
let goals = goal_vec![not(any![unify(x, 1), unify(x, 2)]), unify(x, 1)];
goals.assert_permutations_resolve_to(&1, vec![]);
}
#[test]
fn succeeds_with_adjacent_forking_goals() {
let x = LVar::new();
let goals = goal_vec![
any![unify(x, 1), unify(x, 2)],
not(any![unify(x, 1), unify(x, 3)])
];
goals.assert_permutations_resolve_to(&x, vec![2]);
}
#[test]
fn fails_with_adjacent_forking_goals() {
let x = LVar::new();
let y = LVar::new();
let goals = goal_vec![
not(any![unify(x, 1), unify(y, 1)]),
any![unify(x, 1), unify(x, 1)],
unify(y, 1)
];
goals.assert_permutations_resolve_to(&x, vec![]);
}
}