Merge branch 'main' into main

This commit is contained in:
ISibboI 2023-05-27 10:42:40 +03:00 committed by GitHub
commit e63a99af80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 25 deletions

View File

@ -16,34 +16,29 @@
### Contributors ### Contributors
## [10.0.0](https://github.com/ISibboI/evalexpr/compare/9.1.0...10.0.0) - 2023-05-21
### Added
* Builtin functions can now be disabled (#129)
### Contributors
My warmhearted thanks goes to:
* [hexofyore](https://github.com/hexofyore)
## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16 ## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16
### Added ### Added
* Builtin functions `contains` and `contains_any` (#127) * Builtin functions `contains` and `contains_any` (#127)
### Contributors ### Contributors
My warmhearted thanks goes to: My warmhearted thanks goes to:
* [nickisyourfan](https://github.com/nickisyourfan) * [nickisyourfan](https://github.com/nickisyourfan)
## [8.2.0](https://github.com/ISibboI/evalexpr/compare/8.1.0...8.2.0) - 2023-04-13
### Added
* `EvalExprError` now derives `Clone` (#116)
### Changed
* Occurrences of `f64` and `i64` have been replaced with the type aliases `FloatType` and `IntType` where applicable (#113)
### Contributors
My warmhearted thanks goes to:
* [Natan Freeman](https://github.com/NatanFreeman)
* [Claus Matzinger](https://github.com/celaus)
## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13 ## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13

View File

@ -1,6 +1,6 @@
[package] [package]
name = "evalexpr" name = "evalexpr"
version = "9.1.0" version = "10.0.0"
description = "A powerful arithmetic and boolean expression evaluator" description = "A powerful arithmetic and boolean expression evaluator"
keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"] keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"]
categories = ["parsing", "game-engines"] categories = ["parsing", "game-engines"]

View File

@ -334,7 +334,20 @@ For more information about user-defined functions, refer to the respective [sect
### Builtin Functions ### Builtin Functions
This crate offers a set of builtin functions. This crate offers a set of builtin functions (see below for a full list).
They can be disabled if needed as follows:
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3)));
context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here
assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max"))));
```
Not all contexts support enabling or disabling builtin functions.
Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled.
Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled.
| Identifier | Argument Amount | Argument Types | Description | | Identifier | Argument Amount | Argument Types | Description |
|----------------------|-----------------|-------------------------------|-------------| |----------------------|-----------------|-------------------------------|-------------|

View File

@ -22,6 +22,13 @@ pub trait Context {
/// Calls the function that is linked to the given identifier with the given argument. /// Calls the function that is linked to the given identifier with the given argument.
/// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`. /// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`.
fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value>; fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value>;
/// Checks if builtin functions are disabled.
fn are_builtin_functions_disabled(&self) -> bool;
/// Disables builtin functions if `disabled` is `true`, and enables them otherwise.
/// If the context does not support enabling or disabling builtin functions, an error is returned.
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()>;
} }
/// A context that allows to assign to variables. /// A context that allows to assign to variables.
@ -66,6 +73,7 @@ pub trait GetFunctionContext: Context {
}*/ }*/
/// A context that returns `None` for each identifier. /// A context that returns `None` for each identifier.
/// Builtin functions are disabled and cannot be enabled.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct EmptyContext; pub struct EmptyContext;
@ -79,6 +87,20 @@ impl Context for EmptyContext {
identifier.to_string(), identifier.to_string(),
)) ))
} }
/// Builtin functions are always disabled for `EmptyContext`.
fn are_builtin_functions_disabled(&self) -> bool {
true
}
/// Builtin functions can't be enabled for `EmptyContext`.
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> {
if disabled {
Ok(())
} else {
Err(EvalexprError::BuiltinFunctionsCannotBeEnabled)
}
}
} }
impl<'a> IterateVariablesContext<'a> for EmptyContext { impl<'a> IterateVariablesContext<'a> for EmptyContext {
@ -94,6 +116,50 @@ impl<'a> IterateVariablesContext<'a> for EmptyContext {
} }
} }
/// A context that returns `None` for each identifier.
/// Builtin functions are enabled and cannot be disabled.
#[derive(Debug, Default)]
pub struct EmptyContextWithBuiltinFunctions;
impl Context for EmptyContextWithBuiltinFunctions {
fn get_value(&self, _identifier: &str) -> Option<&Value> {
None
}
fn call_function(&self, identifier: &str, _argument: &Value) -> EvalexprResult<Value> {
Err(EvalexprError::FunctionIdentifierNotFound(
identifier.to_string(),
))
}
/// Builtin functions are always enabled for EmptyContextWithBuiltinFunctions.
fn are_builtin_functions_disabled(&self) -> bool {
false
}
/// Builtin functions can't be disabled for EmptyContextWithBuiltinFunctions.
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> {
if disabled {
Err(EvalexprError::BuiltinFunctionsCannotBeDisabled)
} else {
Ok(())
}
}
}
impl<'a> IterateVariablesContext<'a> for EmptyContextWithBuiltinFunctions {
type VariableIterator = iter::Empty<(String, Value)>;
type VariableNameIterator = iter::Empty<String>;
fn iter_variables(&self) -> Self::VariableIterator {
iter::empty()
}
fn iter_variable_names(&self) -> Self::VariableNameIterator {
iter::empty()
}
}
/// A context that stores its mappings in hash maps. /// A context that stores its mappings in hash maps.
/// ///
/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.* /// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.*
@ -105,6 +171,9 @@ pub struct HashMapContext {
variables: HashMap<String, Value>, variables: HashMap<String, Value>,
#[cfg_attr(feature = "serde_support", serde(skip))] #[cfg_attr(feature = "serde_support", serde(skip))]
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
/// True if builtin functions are disabled.
without_builtin_functions: bool,
} }
impl HashMapContext { impl HashMapContext {
@ -128,6 +197,15 @@ impl Context for HashMapContext {
)) ))
} }
} }
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> {
self.without_builtin_functions = disabled;
Ok(())
}
fn are_builtin_functions_disabled(&self) -> bool {
self.without_builtin_functions
}
} }
impl ContextWithMutableVariables for HashMapContext { impl ContextWithMutableVariables for HashMapContext {

View File

@ -113,6 +113,12 @@ impl fmt::Display for EvalexprError {
regex, message regex, message
), ),
ContextNotMutable => write!(f, "Cannot manipulate context"), ContextNotMutable => write!(f, "Cannot manipulate context"),
BuiltinFunctionsCannotBeEnabled => {
write!(f, "This context does not allow enabling builtin functions")
},
BuiltinFunctionsCannotBeDisabled => {
write!(f, "This context does not allow disabling builtin functions")
},
IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string), IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string),
CustomMessage(message) => write!(f, "Error: {}", message), CustomMessage(message) => write!(f, "Error: {}", message),
} }

View File

@ -203,6 +203,12 @@ pub enum EvalexprError {
/// An escape sequence within a string literal is illegal. /// An escape sequence within a string literal is illegal.
IllegalEscapeSequence(String), IllegalEscapeSequence(String),
/// This context does not allow enabling builtin functions.
BuiltinFunctionsCannotBeEnabled,
/// This context does not allow disabling builtin functions.
BuiltinFunctionsCannotBeDisabled,
/// A custom error explained by its message. /// A custom error explained by its message.
CustomMessage(String), CustomMessage(String),
} }

View File

@ -317,7 +317,20 @@
//! //!
//! ### Builtin Functions //! ### Builtin Functions
//! //!
//! This crate offers a set of builtin functions. //! This crate offers a set of builtin functions (see below for a full list).
//! They can be disabled if needed as follows:
//!
//! ```rust
//! use evalexpr::*;
//! let mut context = HashMapContext::new();
//! assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3)));
//! context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here
//! assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max"))));
//! ```
//!
//! Not all contexts support enabling or disabling builtin functions.
//! Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled.
//! Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled.
//! //!
//! | Identifier | Argument Amount | Argument Types | Description | //! | Identifier | Argument Amount | Argument Types | Description |
//! |----------------------|-----------------|-------------------------------|-------------| //! |----------------------|-----------------|-------------------------------|-------------|
@ -539,7 +552,7 @@ extern crate serde_derive;
pub use crate::{ pub use crate::{
context::{ context::{
Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext, Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext,
HashMapContext, IterateVariablesContext, EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext,
}, },
error::{EvalexprError, EvalexprResult}, error::{EvalexprError, EvalexprResult},
function::Function, function::Function,

View File

@ -459,7 +459,9 @@ impl Operator {
let arguments = &arguments[0]; let arguments = &arguments[0];
match context.call_function(identifier, arguments) { match context.call_function(identifier, arguments) {
Err(EvalexprError::FunctionIdentifierNotFound(_)) => { Err(EvalexprError::FunctionIdentifierNotFound(_))
if !context.are_builtin_functions_disabled() =>
{
if let Some(builtin_function) = builtin_function(identifier) { if let Some(builtin_function) = builtin_function(identifier) {
builtin_function.call(arguments) builtin_function.call(arguments)
} else { } else {

View File

@ -1520,12 +1520,39 @@ fn test_type_errors_in_binary_operators() {
#[test] #[test]
fn test_empty_context() { fn test_empty_context() {
let context = EmptyContext; let mut context = EmptyContext;
assert_eq!(context.get_value("abc"), None); assert_eq!(context.get_value("abc"), None);
assert_eq!( assert_eq!(
context.call_function("abc", &Value::Empty), context.call_function("abc", &Value::Empty),
Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned())) Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned()))
); );
assert_eq!(
eval_with_context("max(1,3)", &context),
Err(EvalexprError::FunctionIdentifierNotFound(String::from(
"max"
)))
);
assert_eq!(context.set_builtin_functions_disabled(true), Ok(()));
assert_eq!(
context.set_builtin_functions_disabled(false),
Err(EvalexprError::BuiltinFunctionsCannotBeEnabled)
)
}
#[test]
fn test_empty_context_with_builtin_functions() {
let mut context = EmptyContextWithBuiltinFunctions;
assert_eq!(context.get_value("abc"), None);
assert_eq!(
context.call_function("abc", &Value::Empty),
Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned()))
);
assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::Int(3)));
assert_eq!(context.set_builtin_functions_disabled(false), Ok(()));
assert_eq!(
context.set_builtin_functions_disabled(true),
Err(EvalexprError::BuiltinFunctionsCannotBeDisabled)
);
} }
#[test] #[test]
@ -2248,3 +2275,19 @@ fn test_negative_power() {
assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0 / 9.0))); assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0 / 9.0)));
assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0 / 9.0))); assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0 / 9.0)));
} }
#[test]
fn test_builtin_functions_context() {
let mut context = HashMapContext::new();
// Builtin functions are enabled by default for HashMapContext.
assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3)));
// Disabling builtin function in Context.
context.set_builtin_functions_disabled(true).unwrap();
// Builtin functions are disabled and using them returns an error.
assert_eq!(
eval_with_context("max(1,3)", &context),
Err(EvalexprError::FunctionIdentifierNotFound(String::from(
"max"
)))
);
}