1 module evx.meta.predicate;
2 
3 private {//imports
4 	import std.typetuple: templateAnd, templateOr;
5     import std.functional: compose, adjoin;
6     import std.algorithm: all, any;
7 }
8 
9 /** 
10     string-mixin-based anonymous templates.
11     mixing this in allows the λ/Λ definitions reference symbols in the mixed-in scope
12 */
13 template LambdaCapture ()
14 {
15     /**
16         Λ is an alias
17     */
18 	static template Λ (string op)
19 	{
20 		mixin(q{
21 			alias Λ } ~ op ~ q{;
22 		});
23 	}
24     /**
25         λ is an enum
26     */
27 	static template λ (string op)
28 	{
29 		mixin(q{
30 			enum λ } ~ op ~ q{;
31 		});
32 	}
33 }
34 ///
35 unittest {
36     import std.typetuple: Map = staticMap, Cons = TypeTuple;
37 
38     static assert (
39         Map!(λ!q{(T) = T.sizeof},
40             int, bool, long
41         ) == Cons!(
42             4,1,8
43         )
44     );
45     static assert (is (
46         Map!(Λ!q{(T) = T[]},
47             int, bool, long
48         ) == Cons!(
49             int[], bool[], long[]
50         )
51     ));
52 }
53 
54 /* mixin captures local symbols
55 */
56 mixin LambdaCapture;
57 
58 /** 
59     combine several template predicates with a logical conjunctive
60 */
61 alias And = templateAnd;
62 /**
63     ditto
64 */
65 alias Or  = templateOr;
66 
67 /**
68     invert a template predicate
69 */
70 static template Not (alias predicate)
71 {
72 	enum Not (Args...) = !predicate!Args;
73 }
74 
75 /**
76     named logical not operator (!),
77     runtime predicate inversion,
78     and boolean symbol inversion
79 */
80 template not ()
81 {
82 	bool not (T)(T value)
83 	{
84 		return !value;
85 	}
86 }
87 /**
88     ditto
89 */
90 template not (alias predicate)
91 {
92 	bool not (Args...)(Args args)
93 	if (is(typeof(predicate (args) == true)))
94 	{
95 		return !(predicate (args));
96 	}
97 
98 	bool not (Args...)()
99 	if (is(typeof(predicate == true)) && !(is(typeof(predicate(Args.init)))))
100 	{
101 		return !predicate;
102 	}
103 
104 	bool not (Args...)()
105 	if (__traits(compiles, {enum x = predicate!Args;}))
106 	{
107 		return !(predicate!Args);
108 	}
109 }
110 ///
111 unittest {
112     assert (not (false));
113     assert (not!false);
114 
115     auto a = false;
116 
117     assert (not (a));
118     assert (not!a);
119 
120     enum b = false;
121     assert (not (b));
122     assert (not!b);
123 
124     auto c () {return false;}
125 
126     assert (not (c));
127     assert (not!c);
128 
129     auto d (int x){return x == 1;}
130 
131     assert (not (d(0)));
132     assert (not!d (0));
133 
134     alias e = not!d;
135 
136     assert (e(0));
137     assert (not!e (1));
138 
139     assert (not!(x => x % 2 == 0)(1));
140 }
141 
142 template funcs_to_list (funcs...)
143 {
144     import std.range: only;
145 
146     alias funcs_to_list = compose!(result => result.expand.only, adjoin!funcs);
147 }
148 
149 /**
150     combine several runtime predicates with a logical conjunctive
151 */
152 alias and (funcs...) = compose!(all, funcs_to_list!funcs);
153 /**
154     ditto
155 */
156 alias or  (funcs...) = compose!(any, funcs_to_list!funcs);
157 ///
158 unittest {
159     static f (int x){return x > 5;}
160     static g (int x){return x < 10;}
161     static h (int x){return x == 7;}
162 
163     alias q = and!(f,g,h);
164     alias p = or!(f,g,h);
165 
166     assert (q(5) == false);
167     assert (q(6) == false);
168     assert (q(7) == true);
169     assert (q(10) == false);
170 
171     assert (p(5) == true);
172     assert (p(6) == true);
173     assert (p(7) == true);
174     assert (p(10) == true);
175 }