tylisp/ops/
if_impl.rs

1use crate::engine::*;
2
3/// TyLisp primitive conditional operator 
4///
5/// Takes three parameters, `Cond`, `T`, and `F`.
6/// * If `Cond` is `typenum::True`, evaluates to `T`
7/// * If `Cond` is `typenum::False`, evaluates to `F`
8///
9/// The non-selected branch does not need to be valid.
10/// This statement is invalid if `Cond` evaluates to
11/// anything other than `True` or `False`.
12
13#[derive(Debug,Default)]
14pub struct If;
15literal!{If}
16
17impl Call for If { type Conv=cc::Syntax; }
18
19impl<Cond:Eval,T,F> SynCall< sexpr!{Cond,T,F} > for If
20where Cond::Result : IfImpl<T,F> {
21    type Result = <Cond::Result as IfImpl<T,F>>::Result;
22}
23
24impl<Cond:Eval,T,F,CQ,TQ,FQ> SynCalc< sexpr!{Cond,T,F}, sexpr!{CQ,TQ,FQ} > for If
25where Cond::Result : IfCalcImpl<T,F,TQ,FQ> {
26    type Result = <Cond::Result as IfCalcImpl<T,F,TQ,FQ>>::Result;
27    fn syn_calc(self, sexpr_pat!{_:CQ,tq:TQ,fq:FQ}:sexpr!{CQ,TQ,FQ})->Self::Result {
28        Cond::Result::calc(tq,fq)
29    }
30}
31
32pub trait IfImpl<T,F> {
33    type Result;
34}
35
36impl<T,F,Bool> IfImpl<T,F> for std::marker::PhantomData<Bool>
37where Bool: IfImpl<T,F> {
38    type Result = Bool::Result;
39}
40
41impl<T:Eval,F> IfImpl<T,F> for ::typenum::True {
42    type Result = T::Result;
43}
44
45impl<T, F:Eval> IfImpl<T,F> for ::typenum::False {
46    type Result = F::Result;
47}
48
49pub trait IfCalcImpl<T,F,QT,QF> {
50    type Result;
51    fn calc(qt:QT, qf:QF)->Self::Result;
52}
53
54impl<T,F,QT,QF,Bool> IfCalcImpl<T,F,QT,QF> for std::marker::PhantomData<Bool>
55where Bool: IfCalcImpl<T,F,QT,QF> {
56    type Result = Bool::Result;
57    fn calc(qt:QT, qf:QF)->Self::Result {
58        Bool::calc(qt,qf)
59    }
60}
61
62impl<T:Calc<QT>,F,QT,QF> IfCalcImpl<T,F,QT,QF> for ::typenum::True {
63    type Result = T::Result;
64    fn calc(qt:QT, qf:QF)->Self::Result {
65        //println!("Dropping {}", std::any::type_name::<QF>());
66        ::std::mem::drop(qf);
67        T::calc(qt)
68    }
69}
70
71impl<T,F:Calc<QF>,QT,QF> IfCalcImpl<T,F,QT,QF> for ::typenum::False {
72    type Result = F::Result;
73    fn calc(qt:QT, qf:QF)->Self::Result {
74        //println!("Dropping {}", std::any::type_name::<QT>());
75        ::std::mem::drop(qt);
76        F::calc(qf)
77    }
78}
79
80#[test]
81fn test() {
82    #[derive(Eq,PartialEq,Debug)] struct A;
83    #[derive(Eq,PartialEq,Debug)] struct B;
84
85    use typenum::{True,False};
86
87    // NB: The unselected term is not quoted,
88    //     and is also not a valid expression.
89    let _:eval!{If, True, @A, B} = A;
90    let _:eval!{If, False, A, @B} = B;
91
92    assert_eq!(A, calc!{If, {crate::ops::Is, False, False}, @A=A, B});
93}
94
95#[derive(Debug,Default)]
96pub struct CondNoMatch;
97literal!{ CondNoMatch }
98
99/// TyLisp operator for complex conditionals
100///
101/// Takes any number of arguments of the form `{Condition, Consequent}`
102/// Evaluates to the `Consequent` corresponding to the first `True`
103/// condition.
104///
105/// The expression is invalid if any `Condition` preceeding the selected
106/// one evaluates to anything other than `False` or if the selected
107/// `Consequent` is invalid.
108///
109/// If all conditions evaluate to `False`, the expression is valid but
110/// evaluates to a private type.
111
112#[derive(Debug,Default)]
113pub struct Cond;
114defmacro!{ Cond {
115    () { } => {super::Ret, CondNoMatch};
116    (Test, Res, Else) { {Test, Res}; Else } => {If, Test, Res, {Cond; Else}};
117}}
118
119impl<Test,Res,Else, QTest,QRes,QElse>
120SynCalc< sexpr!{{Test,Res}; Else}, sexpr!{{QTest,QRes};QElse} > for Cond
121where sexpr!{If,Test,Res,{Cond; Else}}: Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>
122{
123    type Result = <sexpr!{If,Test,Res,{Cond; Else}} as Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>>::Result;
124    fn syn_calc(self, sexpr_pat!{{qtest:QTest,qres:QRes}; qelse:QElse}: sexpr!{{QTest,QRes};QElse})->Self::Result {
125        <sexpr!{If,Test,Res,{Cond; Else}} as Calc<sexpr!{If,QTest,QRes,{Cond;QElse}}>>::calc(sexpr_val!{If,qtest,qres,@{Cond;qelse}})
126    }
127}
128
129#[test]
130fn test_cond() {
131    #[derive(Debug,Default,Eq,PartialEq)] struct A;
132    #[derive(Debug,Default,Eq,PartialEq)] struct B;
133    #[derive(Debug,Default,Eq,PartialEq)] struct C(u32);
134    struct NoEvalImpl;
135    literal!{A; B; C};
136
137    use typenum::{True,False};
138    use crate::ops::Is;
139
140    assert_type_eq!{ B, eval!{ Cond,
141        {False, NoEvalImpl},
142        {True, B},
143        {True, C}
144    }};
145
146    type Test=B;
147    assert_type_eq!{ C, eval!{ Cond,
148        {{Is, Test, A}, B},
149        {{Is, Test, B}, C},
150        {{Is, Test, C}, A}
151    }};
152
153    assert_eq!{ C(42), calc!{ Cond,
154        {{Is, Test, A}, B},
155        {{Is, Test, B}, @C=C(42)},
156        {{Is, Test, C}, A}
157    }};
158
159}
160