1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
///! This crate contains the macros for the fluent-comparisons crate

#[macro_export]
#[doc(hidden)]
/// # Internal Macro
/// This macro checks that the comparison operator in the expression is indeed allowed. If it is
/// allowed this macro evaluates to a unit/void statement. Otherwise it will give a compile error
/// explaining which operators are allowed
//#[macro_export]
#[doc(hidden)]
macro_rules! __check_operator {
    // these are the allowed comparison operators
    (==) => {};
    (<=) => {};
    (>=) => {};
    (!=) => {};
    (<) => {};
    (>) => {};
    // everything else is not allowed, including &&, ||, and such
    ($other:tt) => {
        std::compile_error!(
            "This operator is not allowed. The only allowed operators are ==, !=, <=, >=, <, >"
        );
    };
}

/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *any of the values* in the set.
///
/// # Lazy Evaluation
///
/// If we write `any_of!({a,b}<c)`, this is equivalent to the hand coded `a<c && b<c`. That means that the comparisons are
/// evaluated [lazily](https://doc.rust-lang.org/reference/expressions/operator-expr.html#lazy-boolean-operators) from left to right. Once
/// the truth value of the expression can be determined, the evaluation stops. That means that e.g. for the expression `any_of!({1,some_func()}<5)`,
/// the function `some_func()` is not invoked.
///
/// # Usage
///
/// ## Basic Usage
///
/// For the basic use case we compare a set of values against a common right hand side. Invoke the macro using
/// `any_of!({/*list of expressions*/} operator rhs)`, where operator can be any of the binary comparison operators, i.e.
/// `==`, `!=`, `<=`, `<`, `>`, and `>=`. The list of expressions on the left hand side is comma separated without a trailing comma. The right hand side
/// is an expression as well.
///
/// The list of expressions can have a variadic number of elements but must have at least one. It must always be enclosed in
/// curly braces. The expressions on the left hand side need not be of the same type, but the comparison with the right hand side must be valid. In particular,
/// the expressions need not be numeric.
///
/// ```
/// # use fluent_comparisons_macros::any_of;
/// use rand::prelude::*;
/// // given:
/// let square = |val|val*val;
/// let v = vec![1, 2,3];
/// let mut rng = rand::thread_rng();
/// // the following assertions hold
/// assert!(any_of!({1,2,3}>2));
/// assert!(any_of!({4+4+1,square(7*2),120_i32.pow(2)}>8));
/// assert!(any_of!( {rng.gen::<usize>(),v.len(),2,1+1,"hello world".len()} == v.len()));
/// ```
///
/// ## Usage with Transformations
///
/// We can also apply a transformation to the list on the left hand side before comparing to the right hand side.
/// For that, simply append `.map(...)` to the list and give an argument that transforms the values. The argument
/// to map can be any kind of invokable of a single argument, like a function or closure. Here the type requirements
/// are a bit stricter and all values on the left hand side must be of the same type.
///
/// ```
/// # use fluent_comparisons_macros::any_of;
/// // given
/// let square = |x|x*x;
/// // the following assertions hold
/// assert!(any_of!({4,square(2),2_i32.pow(2)}.map(|x|x+5)>8));
/// assert!(any_of!({4+1,3,5}.map(square)==9));
/// ```
///
/// ## Usage with Predicates
///
/// This is a special case where the transformation maps to a boolean predicate. Instead of writing
/// `any_of!({...}.map(/*predicate f:x -> bool*/)==true)`, we can use the syntax `any_of!({...}.satisfy(/*predicate f:x -> bool*/))`,
/// which saves us the comparison with `true` on the right hand side. Don't use a predicate which
/// compares values with one of the comparison operators, because then you are better served with the
/// syntax above. Rather use it for more complex predicates:
///
/// ```
/// # use fluent_comparisons_macros::any_of;
/// fn is_prime_number(x:i32) -> bool {
///     /*some interesting math*/
/// # true
/// };
/// //this assertion holds
/// assert!(any_of!({12,14,5}.satisfy(is_prime_number)));
/// ```
///
///
#[macro_export]
macro_rules! any_of {
    // variant with a predicate (does not use a comparison operator and rhs)
    ( {$($lh_sides:expr),+}.satisfy($($func:tt)+) ) => {
        any_of!({$($lh_sides),+}.map($($func)+)==true)
    };

    // variant with a transformation of the set
    ( {$($lh_sides:expr),+}.map($($func:tt)+) $operator:tt $rhs:expr) => {
        {
            $crate::__check_operator!($operator);
            //by fixing this here, we have more type deduction powers but also less
            //flexibility in generic arguments. We could also pass the expanded tt func to a single
            //tt in a submacro (by putting (...) around it) and then use that function, which is more
            //powerful when passing generic functions, but less intuitive when passing lambdas
            //so this map is more akin to a map in a collection. The other is more akin to a C++
            //transform of a heterogeneous collection. For that we might want to pass a path or ident
            //instead of the token tree. Because the token tree is just a trick to get lambdas
            //as well. But since the other way isn't great for lambdas anyway we can just skip it.
            let map_func = $($func)+;
            $( (map_func($lh_sides) $operator $rhs) )||+
        }
    };

    //variant without map (requires a comparison operator and rhs)
    ( {$($lh_sides:expr),+} $operator:tt $rhs:expr)=> {
        {
            $crate::__check_operator!($operator);
            $( ($lh_sides $operator $rhs) )||+
        }
    };
}

/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *all of the values* in the set.
///
/// # Usage
/// The usage is analogous to the [any_of](crate::any_of) macro and is documented in more detail there.
/// Just like `any_of`, this macro also performs lazy evaluation.
///
/// ## Examples
/// The following examples show how to use the macro.
/// ```
/// # use fluent_comparisons_macros::all_of;
///
/// let square = |val|val*val;
/// // the following assertion holds
/// assert!(all_of!({4+4+1,square(7*2),120_i32.pow(2)}>0));
///
/// let v = vec![1, 2,3,4,5];
/// // the following assertion holds
/// assert!(all_of!( {square(2),v.len() as i32,2,1+1,"hello".len() as i32} <= v.len() as i32));
/// ```
#[macro_export]
macro_rules! all_of {

    ( {$($lh_sides:expr),+}.satisfy($($func:tt)+) ) => {
        all_of!({$($lh_sides),+}.map($($func)+)==true)
    };

    ( {$($lh_sides:expr),+}.map($($func:tt)+) $operator:tt $rhs:expr) => {
        {
            $crate::__check_operator!($operator);
            let map_func = $($func)+;
            $( (map_func($lh_sides) $operator $rhs) )&&+
        }
    };

    ( {$($lh_sides:expr),+} $operator:tt $rhs:expr)=> {
        {
            $crate::__check_operator!($operator);
            $( ($lh_sides $operator $rhs) )&&+
        }
    };
}

/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *none of the values* in the set.
///
/// # Usage
/// The usage is analogous to the [any_of](crate::any_of) macro and is documented in more detail there.
/// Just like `any_of`, this macro also performs lazy evaluation.
///
/// ## Examples
/// The following examples show how to use the macro.
/// ```
/// # use fluent_comparisons_macros::none_of;
///
/// let square = |val|val*val;
/// // the following assertion holds
/// assert!(none_of!({4+4+1,square(7*2),120_i32.pow(2)}<0));
///
/// let v = vec![1, 2,3,4,5];
/// // the following assertion holds
/// assert!(none_of!( {square(2),v.len() as i32,2,1+1,"hello".len() as i32} > v.len() as i32));
/// ```
#[macro_export]
macro_rules! none_of {
    ( {$($lh_sides:expr),+}.satisfy($($func:tt)+) ) => {
        none_of!({$($lh_sides),+}.map($($func)+)==true)
    };

    ( {$($lh_sides:expr),+}.map($($func:tt)+) $operator:tt $rhs:expr) => {
        {
            $crate::__check_operator!($operator);
            let map_func = $($func)+;
            $( !(map_func($lh_sides) $operator $rhs) )&&+
        }
    };

    ( {$($lh_sides:expr),+} $operator:tt $rhs:expr)=> {
        {
            $crate::__check_operator!($operator);
            $( !($lh_sides $operator $rhs) )&&+
        }
    };
}

// TODO FINISH THIS UP, TEST IT AND DOCUMENT IT
// TODO: make this as simple as the ones above w/o recursion
// TODO maybe make the syntax exactly!( 12 of {a,b,...} <= 5) possible. Maybe we can even allow
// an ident instead of the literal. We probably can't allow an expression, but that is fine...
// #[macro_export]
// macro_rules! exactly_one_of {
//     //TODO CAUTION: THIS COULD BE CALLED WITH ONE ARGUMENT. MAKE SURE THAT THIS PRODUCES A VALID RESULT
//     // expression like any_of!( {1,v.len(),4} < 3)
//     ( {$($lh_sides:expr),+} $operator:tt $rhs:expr)=> {
//         {
//             $crate::__check_operator!($operator);
//             1u32 == exactly_one_of!(@internal lhs={$($lh_sides),+}, op=[$operator], rhs = $rhs, expanded = {0u32})
//         }
//     };
//
//     // internal rules, recursion final case
//     (@internal lhs = {$head:expr},op = [$op:tt], rhs = $rhs:expr, expanded = {$expanded:expr}) => {
//         $expanded + {if $head $op $rhs {1u32}else{0u32}}
//     };
//
//     // internal rules, recursion case
//     (@internal lhs = {$head:expr, $($tail:expr),*}, op = [$op:tt], rhs = $rhs:expr, expanded = {$expanded:expr}) =>{
//         exactly_one_of!(@internal lhs={$($tail),*}, op=[$op], rhs = $rhs, expanded = {$expanded + {if $head $op $rhs {1u32}else{0u32}}})
//     };
// }