tylisp 0.1.0

A domain-specific language for expressing complex type bounds
Documentation
use crate::engine::*;

/// TyLisp primitive conditional operator 
///
/// Takes three parameters, `Cond`, `T`, and `F`.
/// * If `Cond` is `typenum::True`, evaluates to `T`
/// * If `Cond` is `typenum::False`, evaluates to `F`
///
/// The non-selected branch does not need to be valid.
/// This statement is invalid if `Cond` evaluates to
/// anything other than `True` or `False`.

#[derive(Debug,Default)]
pub struct If;
literal!{If}

impl Call for If { type Conv=cc::Syntax; }

impl<Cond:Eval,T,F> SynCall< sexpr!{Cond,T,F} > for If
where Cond::Result : IfImpl<T,F> {
    type Result = <Cond::Result as IfImpl<T,F>>::Result;
}

impl<Cond:Eval,T,F,CQ,TQ,FQ> SynCalc< sexpr!{Cond,T,F}, sexpr!{CQ,TQ,FQ} > for If
where Cond::Result : IfCalcImpl<T,F,TQ,FQ> {
    type Result = <Cond::Result as IfCalcImpl<T,F,TQ,FQ>>::Result;
    fn syn_calc(self, sexpr_pat!{_:CQ,tq:TQ,fq:FQ}:sexpr!{CQ,TQ,FQ})->Self::Result {
        Cond::Result::calc(tq,fq)
    }
}

pub trait IfImpl<T,F> {
    type Result;
}

impl<T,F,Bool> IfImpl<T,F> for std::marker::PhantomData<Bool>
where Bool: IfImpl<T,F> {
    type Result = Bool::Result;
}

impl<T:Eval,F> IfImpl<T,F> for ::typenum::True {
    type Result = T::Result;
}

impl<T, F:Eval> IfImpl<T,F> for ::typenum::False {
    type Result = F::Result;
}

pub trait IfCalcImpl<T,F,QT,QF> {
    type Result;
    fn calc(qt:QT, qf:QF)->Self::Result;
}

impl<T,F,QT,QF,Bool> IfCalcImpl<T,F,QT,QF> for std::marker::PhantomData<Bool>
where Bool: IfCalcImpl<T,F,QT,QF> {
    type Result = Bool::Result;
    fn calc(qt:QT, qf:QF)->Self::Result {
        Bool::calc(qt,qf)
    }
}

impl<T:Calc<QT>,F,QT,QF> IfCalcImpl<T,F,QT,QF> for ::typenum::True {
    type Result = T::Result;
    fn calc(qt:QT, qf:QF)->Self::Result {
        //println!("Dropping {}", std::any::type_name::<QF>());
        ::std::mem::drop(qf);
        T::calc(qt)
    }
}

impl<T,F:Calc<QF>,QT,QF> IfCalcImpl<T,F,QT,QF> for ::typenum::False {
    type Result = F::Result;
    fn calc(qt:QT, qf:QF)->Self::Result {
        //println!("Dropping {}", std::any::type_name::<QT>());
        ::std::mem::drop(qt);
        F::calc(qf)
    }
}

#[test]
fn test() {
    #[derive(Eq,PartialEq,Debug)] struct A;
    #[derive(Eq,PartialEq,Debug)] struct B;

    use typenum::{True,False};

    // NB: The unselected term is not quoted,
    //     and is also not a valid expression.
    let _:eval!{If, True, @A, B} = A;
    let _:eval!{If, False, A, @B} = B;

    assert_eq!(A, calc!{If, {crate::ops::Is, False, False}, @A=A, B});
}

#[derive(Debug,Default)]
pub struct CondNoMatch;
literal!{ CondNoMatch }

/// TyLisp operator for complex conditionals
///
/// Takes any number of arguments of the form `{Condition, Consequent}`
/// Evaluates to the `Consequent` corresponding to the first `True`
/// condition.
///
/// The expression is invalid if any `Condition` preceeding the selected
/// one evaluates to anything other than `False` or if the selected
/// `Consequent` is invalid.
///
/// If all conditions evaluate to `False`, the expression is valid but
/// evaluates to a private type.

#[derive(Debug,Default)]
pub struct Cond;
defmacro!{ Cond {
    () { } => {super::Ret, CondNoMatch};
    (Test, Res, Else) { {Test, Res}; Else } => {If, Test, Res, {Cond; Else}};
}}

impl<Test,Res,Else, QTest,QRes,QElse>
SynCalc< sexpr!{{Test,Res}; Else}, sexpr!{{QTest,QRes};QElse} > for Cond
where sexpr!{If,Test,Res,{Cond; Else}}: Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>
{
    type Result = <sexpr!{If,Test,Res,{Cond; Else}} as Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>>::Result;
    fn syn_calc(self, sexpr_pat!{{qtest:QTest,qres:QRes}; qelse:QElse}: sexpr!{{QTest,QRes};QElse})->Self::Result {
        <sexpr!{If,Test,Res,{Cond; Else}} as Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>>::calc(sexpr_val!{If,qtest,qres,@{Cond;qelse}})
    }
}

#[test]
fn test_cond() {
    #[derive(Debug,Default,Eq,PartialEq)] struct A;
    #[derive(Debug,Default,Eq,PartialEq)] struct B;
    #[derive(Debug,Default,Eq,PartialEq)] struct C(u32);
    struct NoEvalImpl;
    literal!{A; B; C};

    use typenum::{True,False};
    use crate::ops::Is;

    assert_type_eq!{ B, eval!{ Cond,
        {False, NoEvalImpl},
        {True, B},
        {True, C}
    }};

    type Test=B;
    assert_type_eq!{ C, eval!{ Cond,
        {{Is, Test, A}, B},
        {{Is, Test, B}, C},
        {{Is, Test, C}, A}
    }};

    assert_eq!{ C(42), calc!{ Cond,
        {{Is, Test, A}, B},
        {{Is, Test, B}, @C=C(42)},
        {{Is, Test, C}, A}
    }};

}