use crate::engine::*;
#[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 {
::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 {
::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};
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 }
#[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}
}};
}