contains + contains_any builtins

This commit is contained in:
Nick 2023-05-10 12:31:33 -05:00
parent 76d480c465
commit 8b8af31eb8
3 changed files with 135 additions and 11 deletions

View File

@ -148,23 +148,59 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
let result_index = if arguments[0].as_boolean()? { 1 } else { 2 };
Ok(arguments.swap_remove(result_index))
})),
"some" => Some(Function::new(move |argument| {
let arguments = argument.as_tuple()?;
"contains" => Some(Function::new(move |argument| {
let arguments = argument.as_fixed_len_tuple(2)?;
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
Ok(a.contains(b).into())
} else {
Err(EvalexprError::type_error(
b.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
))
}
} else {
Err(EvalexprError::expected_tuple(arguments[0].clone()))
}
})),
"contains_any" => Some(Function::new(move |argument| {
let arguments = argument.as_fixed_len_tuple(2)?;
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
if let Value::Tuple(b) = b {
for item in b {
if a.contains(item) {
return Ok(Value::Boolean(true));
}
let mut contains = false;
for value in b {
//if value is not String, Int, Bool, Float error it out
if let Value::String(_)
| Value::Int(_)
| Value::Float(_)
| Value::Boolean(_) = value
{
if a.contains(value) {
contains = true;
}
} else {
if a.contains(&b) {
return Ok(Value::Boolean(true));
return Err(EvalexprError::type_error(
value.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
));
}
}
Ok(Value::Boolean(false))
Ok(contains.into())
} else {
Ok(Value::Boolean(false))
Err(EvalexprError::expected_tuple(b.clone()))
}
} else {
Err(EvalexprError::expected_tuple(arguments[0].clone()))
}
})),
"len" => Some(Function::new(|argument| {

View File

@ -328,7 +328,8 @@
//! | `round` | 1 | Numeric | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 |
//! | `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number |
//! | `if` | 3 | Boolean, Any, Any | If the first argument is true, returns the second argument, otherwise, returns the third |
//! | `some` | 2 | Tuple, Any | Returns true if second argument exists in first argument(tuple). If second argument is tuple, checks if any exist in first argument. |
//! | `contains` | 2 | Tuple, Any non Tuple | Returns true if second argument exists in first argument. |
//! | `contains_any` | 2 | Tuple, Tuple of Any Non Tuple | Returns true if one of the values in the tuple of second argument exists in first argument(tuple). |
//! | `typeof` | 1 | Any | returns "string", "float", "int", "boolean", "tuple", or "empty" depending on the type of the argument |
//! | `math::is_nan` | 1 | Numeric | Returns true if the argument is the floating-point value NaN, false if it is another floating-point value, and throws an error if it is not a number |
//! | `math::is_finite` | 1 | Numeric | Returns true if the argument is a finite floating-point number, false otherwise |

View File

@ -395,6 +395,93 @@ fn test_builtin_functions() {
assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0)));
assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6)));
assert_eq!(eval("len(\"a\", \"b\")"), Ok(Value::Int(2)));
//Contians
assert_eq!(
eval("contains(1, 2, 3)"),
Err(EvalexprError::expected_fixed_len_tuple(
2,
Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
))
);
assert_eq!(
eval("contains((\"foo\", \"bar\"), \"bar\")"),
Ok(Value::Boolean(true))
);
assert_eq!(
eval("contains((\"foo\", \"bar\"), \"buzz\")"),
Ok(Value::Boolean(false)),
);
assert_eq!(
eval("contains(\"foo\", \"bar\")"),
Err(EvalexprError::expected_tuple(Value::String("foo".into())))
);
assert_eq!(
eval("contains((\"foo\", \"bar\", 123), 123)"),
Ok(Value::Boolean(true))
);
assert_eq!(
eval("contains((\"foo\", \"bar\"), (\"buzz\", \"bazz\"))"),
Err(EvalexprError::type_error(
Value::Tuple(vec![
Value::String("buzz".into()),
Value::String("bazz".into())
]),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean
]
))
);
//Contains Any
assert_eq!(
eval("contains_any(1, 2, 3)"),
Err(EvalexprError::expected_fixed_len_tuple(
2,
Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
))
);
assert_eq!(
eval("contains_any((\"foo\", \"bar\"), (\"bar\", \"buzz\"))"),
Ok(Value::Boolean(true))
);
assert_eq!(
eval("contains_any((\"foo\", \"bar\"), (\"buzz\", \"bazz\"))"),
Ok(Value::Boolean(false)),
);
assert_eq!(
eval("contains_any((1,2,3), (3,4,5))"),
Ok(Value::Boolean(true))
);
assert_eq!(
eval("contains_any((1,2,3), (4,5,6))"),
Ok(Value::Boolean(false))
);
assert_eq!(
eval("contains_any((true, false, true, true), (false, false, false))"),
Ok(Value::Boolean(true))
);
assert_eq!(
eval("contains_any(\"foo\", \"bar\")"),
Err(EvalexprError::expected_tuple(Value::String("foo".into())))
);
assert_eq!(
eval("contains_any((\"foo\", \"bar\"), \"buzz\")"),
Err(EvalexprError::expected_tuple(Value::String("buzz".into())))
);
assert_eq!(
eval("contains_any((\"foo\", \"bar\"), (\"buzz\", (1, 2, 3)))"),
Err(EvalexprError::type_error(
Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean
]
))
);
// String
assert_eq!(
eval("str::to_lowercase(\"FOOBAR\")"),