use std::{fmt::Debug, rc::Rc};
use super::{State, Unify};
use crate::core::{LVar, Value, Value::*, VarId};
pub type ResolveFn = Box<dyn FnOnce(State) -> Option<State>>;
#[derive(Debug)]
pub struct VarWatch(pub(crate) Vec<VarId>);
impl VarWatch {
pub fn one<A>(a: LVar<A>) -> Self {
VarWatch(vec![a.id])
}
pub fn two<A, B>(a: LVar<A>, b: LVar<B>) -> Self {
VarWatch(vec![a.id, b.id])
}
}
pub trait Constraint {
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch>;
}
pub fn resolve_1<A: Unify>(val: &Value<A>, state: &State) -> Result<Rc<A>, VarWatch> {
match state.resolve(val) {
Resolved(a) => Ok(a),
Var(var) => Err(VarWatch::one(var)),
}
}
pub fn resolve_2<A: Unify, B: Unify>(
a: &Value<A>,
b: &Value<B>,
state: &State,
) -> Result<(Rc<A>, Rc<B>), VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), Resolved(b)) => Ok((a, b)),
(Var(var), _) => Err(VarWatch::one(var)),
(_, Var(var)) => Err(VarWatch::one(var)),
}
}
pub enum OneOfTwo<A: Unify, B: Unify> {
A(Rc<A>, Value<B>),
B(Value<A>, Rc<B>),
}
impl<A: Unify, B: Unify> OneOfTwo<A, B> {
pub fn resolve(a: &Value<A>, b: &Value<B>, state: &State) -> Result<OneOfTwo<A, B>, VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), b) => Ok(OneOfTwo::A(a, b)),
(a, Resolved(b)) => Ok(OneOfTwo::B(a, b)),
(Var(a), Var(b)) => Err(VarWatch::two(a, b)),
}
}
}
pub enum TwoOfThree<A: Unify, B: Unify, C: Unify> {
AB(Rc<A>, Rc<B>, Value<C>),
BC(Value<A>, Rc<B>, Rc<C>),
AC(Rc<A>, Value<B>, Rc<C>),
}
impl<A: Unify, B: Unify, C: Unify> TwoOfThree<A, B, C> {
pub fn resolve(
a: &Value<A>,
b: &Value<B>,
c: &Value<C>,
state: &State,
) -> Result<TwoOfThree<A, B, C>, VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
let c = state.resolve(c);
match (a, b, c) {
(Resolved(a), Resolved(b), c) => Ok(TwoOfThree::AB(a, b, c)),
(a, Resolved(b), Resolved(c)) => Ok(TwoOfThree::BC(a, b, c)),
(Resolved(a), b, Resolved(c)) => Ok(TwoOfThree::AC(a, b, c)),
(Var(a), Var(b), _) => Err(VarWatch::two(a, b)),
(Var(a), _, Var(c)) => Err(VarWatch::two(a, c)),
(_, Var(b), Var(c)) => Err(VarWatch::two(b, c)),
}
}
}