expressive/function/
builtin.rs

1#[cfg(feature = "regex_support")]
2use regex::Regex;
3
4use crate::{
5    value::{FloatType, IntType},
6    EvalexprError, Function, Value, ValueType,
7};
8use std::{
9    collections::HashSet,
10    ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr},
11};
12
13macro_rules! simple_math {
14    ($func:ident) => {
15        Some(Function::new(|argument| {
16            let num = argument.as_number()?;
17            Ok(Value::Float(num.$func()))
18        }))
19    };
20    ($func:ident, 2) => {
21        Some(Function::new(|argument| {
22            let tuple = argument.as_fixed_len_tuple(2)?;
23            let (a, b) = (tuple[0].as_number()?, tuple[1].as_number()?);
24            Ok(Value::Float(a.$func(b)))
25        }))
26    };
27}
28
29fn float_is(func: fn(FloatType) -> bool) -> Option<Function> {
30    Some(Function::new(move |argument| {
31        Ok(func(argument.as_number()?).into())
32    }))
33}
34
35macro_rules! int_function {
36    ($func:ident) => {
37        Some(Function::new(|argument| {
38            let int = argument.as_int()?;
39            Ok(Value::Int(int.$func()))
40        }))
41    };
42    ($func:ident, 2) => {
43        Some(Function::new(|argument| {
44            let tuple = argument.as_fixed_len_tuple(2)?;
45            let (a, b) = (tuple[0].as_int()?, tuple[1].as_int()?);
46            Ok(Value::Int(a.$func(b)))
47        }))
48    };
49}
50
51pub fn builtin_function(identifier: &str) -> Option<Function> {
52    match identifier {
53        // Log
54        "math::ln" => simple_math!(ln),
55        "math::log" => simple_math!(log, 2),
56        "math::log2" => simple_math!(log2),
57        "math::log10" => simple_math!(log10),
58        // Exp
59        "math::exp" => simple_math!(exp),
60        "math::exp2" => simple_math!(exp2),
61        // Pow
62        "math::pow" => simple_math!(powf, 2),
63        // Cos
64        "math::cos" => simple_math!(cos),
65        "math::acos" => simple_math!(acos),
66        "math::cosh" => simple_math!(cosh),
67        "math::acosh" => simple_math!(acosh),
68        // Sin
69        "math::sin" => simple_math!(sin),
70        "math::asin" => simple_math!(asin),
71        "math::sinh" => simple_math!(sinh),
72        "math::asinh" => simple_math!(asinh),
73        // Tan
74        "math::tan" => simple_math!(tan),
75        "math::atan" => simple_math!(atan),
76        "math::tanh" => simple_math!(tanh),
77        "math::atanh" => simple_math!(atanh),
78        "math::atan2" => simple_math!(atan2, 2),
79        // Root
80        "math::sqrt" => simple_math!(sqrt),
81        "math::cbrt" => simple_math!(cbrt),
82        // Hypotenuse
83        "math::hypot" => simple_math!(hypot, 2),
84        // Rounding
85        "floor" => simple_math!(floor),
86        "round" => simple_math!(round),
87        "ceil" => simple_math!(ceil),
88        // Float special values
89        "math::is_nan" => float_is(FloatType::is_nan),
90        "math::is_finite" => float_is(FloatType::is_finite),
91        "math::is_infinite" => float_is(FloatType::is_infinite),
92        "math::is_normal" => float_is(FloatType::is_normal),
93        // Absolute
94        "math::abs" => Some(Function::new(|argument| match argument {
95            Value::Float(num) => Ok(Value::Float(num.abs())),
96            Value::Int(num) => Ok(Value::Int(num.abs())),
97            _ => Err(EvalexprError::expected_number(argument.clone())),
98        })),
99        // Other
100        "typeof" => Some(Function::new(move |argument| {
101            Ok(match argument {
102                Value::String(_) => "string",
103                Value::Float(_) => "float",
104                Value::Int(_) => "int",
105                Value::Boolean(_) => "boolean",
106                Value::Tuple(_) => "tuple",
107                Value::Empty => "empty",
108                Value::Map(_) => "map",
109            }
110            .into())
111        })),
112        "min" => Some(Function::new(|argument| {
113            let arguments = argument.as_tuple()?;
114            let mut min_int = IntType::max_value();
115            let mut min_float: FloatType = 1.0 / 0.0;
116            debug_assert!(min_float.is_infinite());
117
118            for argument in arguments {
119                if let Value::Float(float) = argument {
120                    min_float = min_float.min(float);
121                } else if let Value::Int(int) = argument {
122                    min_int = min_int.min(int);
123                } else {
124                    return Err(EvalexprError::expected_number(argument));
125                }
126            }
127
128            if (min_int as FloatType) < min_float {
129                Ok(Value::Int(min_int))
130            } else {
131                Ok(Value::Float(min_float))
132            }
133        })),
134        "max" => Some(Function::new(|argument| {
135            let arguments = argument.as_tuple()?;
136            let mut max_int = IntType::min_value();
137            let mut max_float: FloatType = -1.0 / 0.0;
138            debug_assert!(max_float.is_infinite());
139
140            for argument in arguments {
141                if let Value::Float(float) = argument {
142                    max_float = max_float.max(float);
143                } else if let Value::Int(int) = argument {
144                    max_int = max_int.max(int);
145                } else {
146                    return Err(EvalexprError::expected_number(argument));
147                }
148            }
149
150            if (max_int as FloatType) > max_float {
151                Ok(Value::Int(max_int))
152            } else {
153                Ok(Value::Float(max_float))
154            }
155        })),
156        "if" => Some(Function::new(|argument| {
157            let mut arguments = argument.as_fixed_len_tuple(3)?;
158            let result_index = if arguments[0].as_boolean()? { 1 } else { 2 };
159            Ok(arguments.swap_remove(result_index))
160        })),
161        "contains" => Some(Function::new(move |argument| {
162            let arguments = argument.as_fixed_len_tuple(2)?;
163            if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
164                if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
165                    Ok(a.contains(b).into())
166                } else {
167                    Err(EvalexprError::type_error(
168                        b.clone(),
169                        vec![
170                            ValueType::String,
171                            ValueType::Int,
172                            ValueType::Float,
173                            ValueType::Boolean,
174                        ],
175                    ))
176                }
177            } else {
178                Err(EvalexprError::expected_tuple(arguments[0].clone()))
179            }
180        })),
181        "contains_any" => Some(Function::new(move |argument| {
182            let arguments = argument.as_fixed_len_tuple(2)?;
183            if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
184                if let Value::Tuple(b) = b {
185                    let mut contains = false;
186                    for value in b {
187                        if let Value::String(_)
188                        | Value::Int(_)
189                        | Value::Float(_)
190                        | Value::Boolean(_) = value
191                        {
192                            if a.contains(value) {
193                                contains = true;
194                            }
195                        } else {
196                            return Err(EvalexprError::type_error(
197                                value.clone(),
198                                vec![
199                                    ValueType::String,
200                                    ValueType::Int,
201                                    ValueType::Float,
202                                    ValueType::Boolean,
203                                ],
204                            ));
205                        }
206                    }
207                    Ok(contains.into())
208                } else {
209                    Err(EvalexprError::expected_tuple(b.clone()))
210                }
211            } else {
212                Err(EvalexprError::expected_tuple(arguments[0].clone()))
213            }
214        })),
215        "len" => Some(Function::new(|argument| {
216            if let Ok(subject) = argument.as_string() {
217                Ok(Value::from(subject.len() as IntType))
218            } else if let Ok(subject) = argument.as_tuple() {
219                Ok(Value::from(subject.len() as IntType))
220            } else {
221                Err(EvalexprError::type_error(
222                    argument.clone(),
223                    vec![ValueType::String, ValueType::Tuple],
224                ))
225            }
226        })),
227        // String functions
228        #[cfg(feature = "regex_support")]
229        "str::regex_matches" => Some(Function::new(|argument| {
230            let arguments = argument.as_tuple()?;
231
232            let subject = arguments[0].as_string()?;
233            let re_str = arguments[1].as_string()?;
234            match Regex::new(&re_str) {
235                Ok(re) => Ok(Value::Boolean(re.is_match(&subject))),
236                Err(err) => Err(EvalexprError::invalid_regex(
237                    re_str.to_string(),
238                    format!("{}", err),
239                )),
240            }
241        })),
242        #[cfg(feature = "regex_support")]
243        "str::regex_replace" => Some(Function::new(|argument| {
244            let arguments = argument.as_tuple()?;
245
246            let subject = arguments[0].as_string()?;
247            let re_str = arguments[1].as_string()?;
248            let repl = arguments[2].as_string()?;
249            match Regex::new(&re_str) {
250                Ok(re) => Ok(Value::String(
251                    re.replace_all(&subject, repl.as_str()).to_string(),
252                )),
253                Err(err) => Err(EvalexprError::invalid_regex(
254                    re_str.to_string(),
255                    format!("{}", err),
256                )),
257            }
258        })),
259        "str::to_lowercase" => Some(Function::new(|argument| {
260            let subject = argument.as_string()?;
261            Ok(Value::from(subject.to_lowercase()))
262        })),
263        "str::to_uppercase" => Some(Function::new(|argument| {
264            let subject = argument.as_string()?;
265            Ok(Value::from(subject.to_uppercase()))
266        })),
267        "str::trim" => Some(Function::new(|argument| {
268            let subject = argument.as_string()?;
269            Ok(Value::from(subject.trim()))
270        })),
271        "str::from" => Some(Function::new(|argument| {
272            Ok(Value::String(argument.to_string()))
273        })),
274        #[cfg(feature = "rand")]
275        "random" => Some(Function::new(|argument| {
276            argument.as_empty()?;
277            Ok(Value::Float(rand::random()))
278        })),
279        // Bitwise operators
280        "bitand" => int_function!(bitand, 2),
281        "bitor" => int_function!(bitor, 2),
282        "bitxor" => int_function!(bitxor, 2),
283        "bitnot" => int_function!(not),
284        "shl" => int_function!(shl, 2),
285        "shr" => int_function!(shr, 2),
286        _ => None,
287    }
288}