Merge branch 'main' into main
This commit is contained in:
commit
e63a99af80
29
CHANGELOG.md
29
CHANGELOG.md
@ -16,6 +16,18 @@
|
|||||||
|
|
||||||
### 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
|
||||||
@ -28,23 +40,6 @@ 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
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -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"]
|
||||||
|
15
README.md
15
README.md
@ -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 |
|
||||||
|----------------------|-----------------|-------------------------------|-------------|
|
|----------------------|-----------------|-------------------------------|-------------|
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
17
src/lib.rs
17
src/lib.rs
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user