Merge branch 'main' into main
This commit is contained in:
commit
e63a99af80
33
CHANGELOG.md
33
CHANGELOG.md
@ -16,34 +16,29 @@
|
||||
|
||||
### 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
|
||||
|
||||
### Added
|
||||
|
||||
* Builtin functions `contains` and `contains_any` (#127)
|
||||
* Builtin functions `contains` and `contains_any` (#127)
|
||||
|
||||
### Contributors
|
||||
|
||||
My warmhearted thanks goes to:
|
||||
|
||||
* [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)
|
||||
* [nickisyourfan](https://github.com/nickisyourfan)
|
||||
|
||||
## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "evalexpr"
|
||||
version = "9.1.0"
|
||||
version = "10.0.0"
|
||||
description = "A powerful arithmetic and boolean expression evaluator"
|
||||
keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"]
|
||||
categories = ["parsing", "game-engines"]
|
||||
|
15
README.md
15
README.md
@ -334,7 +334,20 @@ For more information about user-defined functions, refer to the respective [sect
|
||||
|
||||
### 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 |
|
||||
|----------------------|-----------------|-------------------------------|-------------|
|
||||
|
@ -22,6 +22,13 @@ pub trait Context {
|
||||
/// 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`.
|
||||
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.
|
||||
@ -66,6 +73,7 @@ pub trait GetFunctionContext: Context {
|
||||
}*/
|
||||
|
||||
/// A context that returns `None` for each identifier.
|
||||
/// Builtin functions are disabled and cannot be enabled.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EmptyContext;
|
||||
|
||||
@ -79,6 +87,20 @@ impl Context for EmptyContext {
|
||||
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 {
|
||||
@ -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.
|
||||
///
|
||||
/// *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>,
|
||||
#[cfg_attr(feature = "serde_support", serde(skip))]
|
||||
functions: HashMap<String, Function>,
|
||||
|
||||
/// True if builtin functions are disabled.
|
||||
without_builtin_functions: bool,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -113,6 +113,12 @@ impl fmt::Display for EvalexprError {
|
||||
regex, message
|
||||
),
|
||||
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),
|
||||
CustomMessage(message) => write!(f, "Error: {}", message),
|
||||
}
|
||||
|
@ -203,6 +203,12 @@ pub enum EvalexprError {
|
||||
/// An escape sequence within a string literal is illegal.
|
||||
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.
|
||||
CustomMessage(String),
|
||||
}
|
||||
|
17
src/lib.rs
17
src/lib.rs
@ -317,7 +317,20 @@
|
||||
//!
|
||||
//! ### 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 |
|
||||
//! |----------------------|-----------------|-------------------------------|-------------|
|
||||
@ -539,7 +552,7 @@ extern crate serde_derive;
|
||||
pub use crate::{
|
||||
context::{
|
||||
Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext,
|
||||
HashMapContext, IterateVariablesContext,
|
||||
EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext,
|
||||
},
|
||||
error::{EvalexprError, EvalexprResult},
|
||||
function::Function,
|
||||
|
@ -459,7 +459,9 @@ impl Operator {
|
||||
let arguments = &arguments[0];
|
||||
|
||||
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) {
|
||||
builtin_function.call(arguments)
|
||||
} else {
|
||||
|
@ -1520,12 +1520,39 @@ fn test_type_errors_in_binary_operators() {
|
||||
|
||||
#[test]
|
||||
fn test_empty_context() {
|
||||
let context = EmptyContext;
|
||||
let mut context = EmptyContext;
|
||||
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),
|
||||
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]
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
#[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"
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user