Add quickstart guide and detailed explanation of features
This commit is contained in:
parent
a51680da08
commit
2b9d50909e
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 fengcen
|
||||
Copyright (c) 2019 Sebastian Schmidt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
205
README.md
205
README.md
@ -9,151 +9,138 @@ Evalexpr is a powerful arithmetic and boolean expression evaluator.
|
||||
<!-- cargo-sync-readme start -->
|
||||
|
||||
|
||||
## Features
|
||||
## Quickstart
|
||||
|
||||
### Operators
|
||||
|
||||
Supported binary operators:
|
||||
|
||||
| Operator | Precedence | Description | | Operator | Precedence | Description |
|
||||
|----------|------------|-------------|---|----------|------------|-------------|
|
||||
| + | 95 | Sum | | < | 80 | Lower than |
|
||||
| - | 95 | Difference | | \> | 80 | Greater than |
|
||||
| * | 100 | Product | | <= | 80 | Lower than or equal |
|
||||
| / | 100 | Division | | \>= | 80 | Greater than or equal |
|
||||
| % | 100 | Modulo | | == | 80 | Equal |
|
||||
| && | 75 | Logical and | | != | 80 | Not equal |
|
||||
| || | 70 | Logical or | | | |
|
||||
|
||||
Supported unary operators:
|
||||
|
||||
| Operator | Precedence | Description |
|
||||
|----------|------------|-------------|
|
||||
| - | 110 | Negation |
|
||||
| ! | 110 | Logical not |
|
||||
|
||||
### Values
|
||||
|
||||
Operators take values as arguments and produce values as results.
|
||||
Values can be boolean, integer or floating point numbers.
|
||||
Strings are supported as well, but there are no operations defined for them yet.
|
||||
|
||||
Integers are internally represented as `i64`, and floating point numbers are represented as `f64`.
|
||||
Operators that take numbers as arguments can either take integers or floating point numbers.
|
||||
If one of the arguments is a floating point number, all others are converted to floating point numbers as well, and the resulting value is a floating point number as well.
|
||||
Otherwise, the result is an integer.
|
||||
|
||||
### Variables
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Supported binary operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
||||
`+` unary/binary `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
|
||||
Supported unary operators: ``
|
||||
|
||||
Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||
See the `builtin` module for a detailed description of each.
|
||||
|
||||
Where can eval be used?
|
||||
-----------------------
|
||||
|
||||
* Template engine
|
||||
* Scripting language
|
||||
* ...
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Add dependency to Cargo.toml
|
||||
Add `evalexpr` as dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
evalexpr = "0.4"
|
||||
evalexpr = "0.5"
|
||||
```
|
||||
|
||||
In your `main.rs` or `lib.rs`:
|
||||
Add the `extern crate` definition to your `main.rs` or `lib.rs`:
|
||||
|
||||
```rust
|
||||
extern crate evalexpr as eval;
|
||||
extern crate evalexpr;
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
You can do mathematical calculations with supported operators:
|
||||
Then you can use `evalexpr` to evaluate expressions like this:
|
||||
|
||||
```rust
|
||||
use eval::{eval, to_value};
|
||||
use evalexpr::*;
|
||||
|
||||
assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
|
||||
assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
|
||||
assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
|
||||
assert_eq!(eval("2 / 2 + 3 / 3"), Ok(to_value(2.0)));
|
||||
assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
||||
assert_eq!(eval("1 - 2 * 3"), Ok(Value::from(-5)));
|
||||
assert_eq!(eval("1.0 + 2 * 3"), Ok(Value::from(7.0)));
|
||||
assert_eq!(eval("true && 4 > 2"), Ok(Value::from(true)));
|
||||
```
|
||||
|
||||
You can eval with context:
|
||||
And you can use variables and functions in expressions like this:
|
||||
|
||||
```rust
|
||||
use eval::{Expr, to_value};
|
||||
use evalexpr::*;
|
||||
|
||||
assert_eq!(Expr::new("foo == bar")
|
||||
.value("foo", true)
|
||||
.value("bar", true)
|
||||
.exec(),
|
||||
Ok(to_value(true)));
|
||||
let mut configuration = HashMapConfiguration::new();
|
||||
configuration.insert_variable("five", 5);
|
||||
configuration.insert_variable("twelve", 12);
|
||||
configuration.insert_function("f", Function::new(1 /* argument amount */, Box::new(|arguments| {
|
||||
if let Value::Int(int) = arguments[0] {
|
||||
Ok(Value::Int(int / 2))
|
||||
} else if let Value::Float(float) = arguments[0] {
|
||||
Ok(Value::Float(float / 2.0))
|
||||
} else {
|
||||
Err(Error::expected_number(arguments[0].clone()))
|
||||
}
|
||||
})));
|
||||
|
||||
assert_eq!(eval_with_configuration("five + 8 > f(twelve)", &configuration), Ok(Value::from(true)));
|
||||
```
|
||||
|
||||
You can access data like javascript by using `.` and `[]`. `[]` supports expression.
|
||||
You can also precompile expressions like this:
|
||||
|
||||
```rust
|
||||
use eval::{Expr, to_value};
|
||||
use std::collections::HashMap;
|
||||
use evalexpr::*;
|
||||
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foos", vec!["Hello", "world", "!"]);
|
||||
let precompiled = build_operator_tree("a * b - c > 5").unwrap();
|
||||
|
||||
assert_eq!(Expr::new("object.foos[1-1] == 'Hello'")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(true)));
|
||||
let mut configuration = HashMapConfiguration::new();
|
||||
configuration.insert_variable("a", 6);
|
||||
configuration.insert_variable("b", 2);
|
||||
configuration.insert_variable("c", 3);
|
||||
assert_eq!(precompiled.eval(&configuration), Ok(Value::from(true)));
|
||||
|
||||
configuration.insert_variable("c", 8);
|
||||
assert_eq!(precompiled.eval(&configuration), Ok(Value::from(false)));
|
||||
```
|
||||
|
||||
You can eval with function:
|
||||
## Features
|
||||
|
||||
```rust
|
||||
use eval::{Expr, to_value};
|
||||
### Operators
|
||||
|
||||
assert_eq!(Expr::new("say_hello()")
|
||||
.function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
.exec(),
|
||||
Ok(to_value("Hello world!")));
|
||||
```
|
||||
Supported binary operators:
|
||||
|
||||
You can create an array with `array()`:
|
||||
| Operator | Precedence | Description | | Operator | Precedence | Description |
|
||||
|----------|------------|-------------|---|----------|------------|-------------|
|
||||
| + | 95 | Sum | | < | 80 | Lower than |
|
||||
| - | 95 | Difference | | \> | 80 | Greater than |
|
||||
| * | 100 | Product | | <= | 80 | Lower than or equal |
|
||||
| / | 100 | Division | | \>= | 80 | Greater than or equal |
|
||||
| % | 100 | Modulo | | == | 80 | Equal |
|
||||
| && | 75 | Logical and | | != | 80 | Not equal |
|
||||
| || | 70 | Logical or | | | |
|
||||
|
||||
```rust
|
||||
use eval::{eval, to_value};
|
||||
Supported unary operators:
|
||||
|
||||
assert_eq!(eval("array(1, 2, 3, 4, 5)"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||
```
|
||||
| Operator | Precedence | Description |
|
||||
|----------|------------|-------------|
|
||||
| - | 110 | Negation |
|
||||
| ! | 110 | Logical not |
|
||||
|
||||
You can create an integer array with `n..m`:
|
||||
### Values
|
||||
|
||||
```rust
|
||||
use eval::{eval, to_value};
|
||||
Operators take values as arguments and produce values as results.
|
||||
Values can be boolean, integer or floating point numbers.
|
||||
Strings are supported as well, but there are no operations defined for them yet.
|
||||
Values are denoted as displayed in the following table.
|
||||
|
||||
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
```
|
||||
| Value type | Example |
|
||||
|------------|---------|
|
||||
| `Value::Boolean` | `true`, `false` |
|
||||
| `Value::Int` | `3`, `-9`, `0`, `135412` |
|
||||
| `Value::Float` | `3.`, `.35`, `1.00`, `0.5`, `123.554` |
|
||||
|
||||
License
|
||||
-------
|
||||
Integers are internally represented as `i64`, and floating point numbers are represented as `f64`.
|
||||
Operators that take numbers as arguments can either take integers or floating point numbers.
|
||||
If one of the arguments is a floating point number, all others are converted to floating point numbers as well, and the resulting value is a floating point number as well.
|
||||
Otherwise, the result is an integer.
|
||||
|
||||
evalexpr is primarily distributed under the terms of the MIT license.
|
||||
See [LICENSE](LICENSE) for details.
|
||||
### Variables
|
||||
|
||||
This crate allows to compile parameterizable formulas by using variables.
|
||||
A variable is a literal in the formula, that does not contain whitespace or can be parsed as value.
|
||||
The user needs to provide bindings to the variables for evaluation.
|
||||
This is done with the `Configuration` trait.
|
||||
Two structs implementing this trait are predefined.
|
||||
There is `EmptyConfiguration`, that returns `None` for each request, and `HashMapConfiguration`, that stores mappings from literals to variables in a hash map.
|
||||
|
||||
Variables do not have fixed types in the expression itself, but aer typed by the configuration.
|
||||
The `Configuration` trait contains a function that takes a string literal and returns a `Value` enum.
|
||||
The variant of this enum decides the type on evaluation.
|
||||
|
||||
### Functions
|
||||
|
||||
This crate also allows to define arbitrary functions to be used in parsed expressions.
|
||||
A function is defined as a `Function` instance.
|
||||
It contains two properties, the `argument_amount` and the `function`.
|
||||
The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`.
|
||||
The `argument_amount` is verified on execution by the crate and does not need to be verified by the `function`.
|
||||
It determines the length of the slice that is passed to `function`.
|
||||
See the examples section above for examples on how to construct a function instance.
|
||||
|
||||
## License
|
||||
|
||||
This crate is primarily distributed under the terms of the MIT license.
|
||||
See [LICENSE](LICENSE) for details.
|
||||
|
||||
|
||||
<!-- cargo-sync-readme end -->
|
||||
|
@ -33,12 +33,12 @@ impl HashMapConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_variable(&mut self, identifier: String, value: Value) {
|
||||
self.variables.insert(identifier, value);
|
||||
pub fn insert_variable<S: Into<String>, V: Into<Value>>(&mut self, identifier: S, value: V) {
|
||||
self.variables.insert(identifier.into(), value.into());
|
||||
}
|
||||
|
||||
pub fn insert_function(&mut self, identifier: String, function: Function) {
|
||||
self.functions.insert(identifier, function);
|
||||
pub fn insert_function<S: Into<String>>(&mut self, identifier: S, function: Function) {
|
||||
self.functions.insert(identifier.into(), function);
|
||||
}
|
||||
}
|
||||
|
||||
|
197
src/lib.rs
197
src/lib.rs
@ -1,4 +1,68 @@
|
||||
//!
|
||||
//! ## Quickstart
|
||||
//!
|
||||
//! Add `evalexpr` as dependency to your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! evalexpr = "0.5"
|
||||
//! ```
|
||||
//!
|
||||
//! Add the `extern crate` definition to your `main.rs` or `lib.rs`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate evalexpr;
|
||||
//! ```
|
||||
//!
|
||||
//! Then you can use `evalexpr` to evaluate expressions like this:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use evalexpr::*;
|
||||
//!
|
||||
//! assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
||||
//! assert_eq!(eval("1 - 2 * 3"), Ok(Value::from(-5)));
|
||||
//! assert_eq!(eval("1.0 + 2 * 3"), Ok(Value::from(7.0)));
|
||||
//! assert_eq!(eval("true && 4 > 2"), Ok(Value::from(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! And you can use variables and functions in expressions like this:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use evalexpr::*;
|
||||
//!
|
||||
//! let mut configuration = HashMapConfiguration::new();
|
||||
//! configuration.insert_variable("five", 5);
|
||||
//! configuration.insert_variable("twelve", 12);
|
||||
//! configuration.insert_function("f", Function::new(1 /* argument amount */, Box::new(|arguments| {
|
||||
//! if let Value::Int(int) = arguments[0] {
|
||||
//! Ok(Value::Int(int / 2))
|
||||
//! } else if let Value::Float(float) = arguments[0] {
|
||||
//! Ok(Value::Float(float / 2.0))
|
||||
//! } else {
|
||||
//! Err(Error::expected_number(arguments[0].clone()))
|
||||
//! }
|
||||
//! })));
|
||||
//!
|
||||
//! assert_eq!(eval_with_configuration("five + 8 > f(twelve)", &configuration), Ok(Value::from(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can also precompile expressions like this:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use evalexpr::*;
|
||||
//!
|
||||
//! let precompiled = build_operator_tree("a * b - c > 5").unwrap();
|
||||
//!
|
||||
//! let mut configuration = HashMapConfiguration::new();
|
||||
//! configuration.insert_variable("a", 6);
|
||||
//! configuration.insert_variable("b", 2);
|
||||
//! configuration.insert_variable("c", 3);
|
||||
//! assert_eq!(precompiled.eval(&configuration), Ok(Value::from(true)));
|
||||
//!
|
||||
//! configuration.insert_variable("c", 8);
|
||||
//! assert_eq!(precompiled.eval(&configuration), Ok(Value::from(false)));
|
||||
//! ```
|
||||
//!
|
||||
//! ## Features
|
||||
//!
|
||||
//! ### Operators
|
||||
@ -27,6 +91,13 @@
|
||||
//! Operators take values as arguments and produce values as results.
|
||||
//! Values can be boolean, integer or floating point numbers.
|
||||
//! Strings are supported as well, but there are no operations defined for them yet.
|
||||
//! Values are denoted as displayed in the following table.
|
||||
//!
|
||||
//! | Value type | Example |
|
||||
//! |------------|---------|
|
||||
//! | `Value::Boolean` | `true`, `false` |
|
||||
//! | `Value::Int` | `3`, `-9`, `0`, `135412` |
|
||||
//! | `Value::Float` | `3.`, `.35`, `1.00`, `0.5`, `123.554` |
|
||||
//!
|
||||
//! Integers are internally represented as `i64`, and floating point numbers are represented as `f64`.
|
||||
//! Operators that take numbers as arguments can either take integers or floating point numbers.
|
||||
@ -35,115 +106,31 @@
|
||||
//!
|
||||
//! ### Variables
|
||||
//!
|
||||
//! This crate allows to compile parameterizable formulas by using variables.
|
||||
//! A variable is a literal in the formula, that does not contain whitespace or can be parsed as value.
|
||||
//! The user needs to provide bindings to the variables for evaluation.
|
||||
//! This is done with the `Configuration` trait.
|
||||
//! Two structs implementing this trait are predefined.
|
||||
//! There is `EmptyConfiguration`, that returns `None` for each request, and `HashMapConfiguration`, that stores mappings from literals to variables in a hash map.
|
||||
//!
|
||||
//! Variables do not have fixed types in the expression itself, but aer typed by the configuration.
|
||||
//! The `Configuration` trait contains a function that takes a string literal and returns a `Value` enum.
|
||||
//! The variant of this enum decides the type on evaluation.
|
||||
//!
|
||||
//! ### Functions
|
||||
//!
|
||||
//! This crate also allows to define arbitrary functions to be used in parsed expressions.
|
||||
//! A function is defined as a `Function` instance.
|
||||
//! It contains two properties, the `argument_amount` and the `function`.
|
||||
//! The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`.
|
||||
//! The `argument_amount` is verified on execution by the crate and does not need to be verified by the `function`.
|
||||
//! It determines the length of the slice that is passed to `function`.
|
||||
//! See the examples section above for examples on how to construct a function instance.
|
||||
//!
|
||||
//!
|
||||
//!Supported binary operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
||||
//!`+` unary/binary `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
//! ## License
|
||||
//!
|
||||
//!Supported unary operators: ``
|
||||
//!
|
||||
//!Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||
//!See the `builtin` module for a detailed description of each.
|
||||
//!
|
||||
//!Where can eval be used?
|
||||
//!-----------------------
|
||||
//!
|
||||
//!* Template engine
|
||||
//!* Scripting language
|
||||
//!* ...
|
||||
//!
|
||||
//!Usage
|
||||
//!-----
|
||||
//!
|
||||
//!Add dependency to Cargo.toml
|
||||
//!
|
||||
//!```toml
|
||||
//![dependencies]
|
||||
//!evalexpr = "0.4"
|
||||
//!```
|
||||
//!
|
||||
//!In your `main.rs` or `lib.rs`:
|
||||
//!
|
||||
//!```rust
|
||||
//!extern crate evalexpr as eval;
|
||||
//!```
|
||||
//!
|
||||
//!Examples
|
||||
//!--------
|
||||
//!
|
||||
//!You can do mathematical calculations with supported operators:
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{eval, to_value};
|
||||
//!
|
||||
//!assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
|
||||
//!assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
|
||||
//!assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
|
||||
//!assert_eq!(eval("2 / 2 + 3 / 3"), Ok(to_value(2.0)));
|
||||
//!```
|
||||
//!
|
||||
//!You can eval with context:
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{Expr, to_value};
|
||||
//!
|
||||
//!assert_eq!(Expr::new("foo == bar")
|
||||
//! .value("foo", true)
|
||||
//! .value("bar", true)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//!```
|
||||
//!
|
||||
//!You can access data like javascript by using `.` and `[]`. `[]` supports expression.
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{Expr, to_value};
|
||||
//!use std::collections::HashMap;
|
||||
//!
|
||||
//!let mut object = HashMap::new();
|
||||
//!object.insert("foos", vec!["Hello", "world", "!"]);
|
||||
//!
|
||||
//!assert_eq!(Expr::new("object.foos[1-1] == 'Hello'")
|
||||
//! .value("object", object)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//!```
|
||||
//!
|
||||
//!You can eval with function:
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{Expr, to_value};
|
||||
//!
|
||||
//!assert_eq!(Expr::new("say_hello()")
|
||||
//! .function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
//! .exec(),
|
||||
//! Ok(to_value("Hello world!")));
|
||||
//!```
|
||||
//!
|
||||
//!You can create an array with `array()`:
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{eval, to_value};
|
||||
//!
|
||||
//!assert_eq!(eval("array(1, 2, 3, 4, 5)"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||
//!```
|
||||
//!
|
||||
//!You can create an integer array with `n..m`:
|
||||
//!
|
||||
//!```rust
|
||||
//!use eval::{eval, to_value};
|
||||
//!
|
||||
//!assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
//!```
|
||||
//!
|
||||
//!License
|
||||
//!-------
|
||||
//!
|
||||
//!evalexpr is primarily distributed under the terms of the MIT license.
|
||||
//!See [LICENSE](LICENSE) for details.
|
||||
//! This crate is primarily distributed under the terms of the MIT license.
|
||||
//! See [LICENSE](LICENSE) for details.
|
||||
//!
|
||||
|
||||
mod configuration;
|
||||
@ -316,6 +303,8 @@ mod test {
|
||||
Box::new(|arguments| {
|
||||
if let Value::Int(int) = arguments[0] {
|
||||
Ok(Value::Int(int - 2))
|
||||
} else if let Value::Float(float) = arguments[0] {
|
||||
Ok(Value::Float(float - 2.0))
|
||||
} else {
|
||||
Err(Error::expected_number(arguments[0].clone()))
|
||||
}
|
||||
|
@ -41,3 +41,39 @@ impl Value {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(string: String) -> Self {
|
||||
Value::String(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(string: &str) -> Self {
|
||||
Value::String(string.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FloatType> for Value {
|
||||
fn from(float: FloatType) -> Self {
|
||||
Value::Float(float)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IntType> for Value {
|
||||
fn from(int: IntType) -> Self {
|
||||
Value::Int(int)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(boolean: bool) -> Self {
|
||||
Value::Boolean(boolean)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for Result<Value, Error> {
|
||||
fn from(value: Value) -> Self {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user