exmex 0.6.0

fast extendable mathematical expression evaluator
Documentation
exmex-0.6.0 has been yanked.

Exmex is a fast extendable mathematical expression evaluator.

# use std::error::Error;
# fn main() -> Result<(), Box<dyn Error>> {
#
use exmex::{eval_str};
assert!((eval_str("1.5 * ((cos(0) + 23.0) / 2.0)")? - 18.0).abs() < 1e-12);
#
#     Ok(())
# }

For floats, we have a list of predifined operators, namely ^, *, /, +, -, sin, cos, tan, exp, log, and log2. These are defined in make_default_operators.

Variables

For variables we can use curly brackets as shown in the following expression. Variables' values are passed as slices to eval.

# use std::error::Error;
# fn main() -> Result<(), Box<dyn Error>> {
#
use exmex::{make_default_operators, parse};
let to_be_parsed = "log({x}) + 2* (-{x}^2 + sin(4*{y}))";
let expr = parse::<f64>(to_be_parsed, make_default_operators::<f64>())?;
assert!((expr.eval(&[2.5, 3.7]) - 14.992794866624788 as f64).abs() < 1e-12);
#
#     Ok(())
# }

The n-th number in the slice corresponds to the n-th variable. Thereby only the first occurence of the variables is relevant. In this example, we have x=2.5 and y=3.7.

Extendability

Library users can also define a different set of operators as shown in the following.

# use std::error::Error;
# fn main() -> Result<(), Box<dyn Error>> {
#
use exmex::{parse, BinOp, Operator};
let ops = vec![
Operator {
repr: "%",
bin_op: Some(BinOp{op: |a: i32, b: i32| a % b, prio: 1}),
unary_op: None,
},
Operator {
repr: "/",
bin_op: Some(BinOp{op: |a: i32, b: i32| a / b, prio: 1}),
unary_op: None,
},
];
let to_be_parsed = "19 % 5 / 2";
let expr = parse::<i32>(to_be_parsed, ops)?;
assert_eq!(expr.eval(&[1, 0]), 2);
#
#     Ok(())
# }

Operators

Operators are instances of the struct Operator that has its representation in the field repr, a binary and a unary operator of type Option<BinOp<T>> and Option<fn(T) -> T>, respectively, as members. BinOp contains in addition to the operator op of type fn(T, T) -> T an integer prio. Operators can be both, binary and unary such as - as defined in the list of default operators. Note that we expect a unary operator to be always on the left of a number.

Data Types of Numbers

You can use any type that implements Copy and FromStr. In case you do not pass a number that matches the regex r"\.?[0-9]+(\.[0-9]+)?", you have to pass a suitable regex and use the function parse_with_number_pattern instead of parse. Here is an example for bool.

# use std::error::Error;
# fn main() -> Result<(), Box<dyn Error>> {
#
use exmex::{parse_with_number_pattern, BinOp, Operator};
let ops = vec![
Operator {
repr: "&&",
bin_op: Some(BinOp{op: |a: bool, b: bool| a && b, prio: 1}),
unary_op: None,
},
Operator {
repr: "||",
bin_op: Some(BinOp{op: |a: bool, b: bool| a || b, prio: 1}),
unary_op: None,
},
Operator {
repr: "!",
bin_op: None,
unary_op: Some(|a: bool| !a),
},
];
let to_be_parsed = "!(true && false) || (!false || (true && false))";
let expr = parse_with_number_pattern::<bool>(to_be_parsed, ops.clone(), "true|false")?;
assert_eq!(expr.eval(&[]), true);
#
#     Ok(())
# }

Priorities and Parentheses

In Exmex-land, unary operators always have higher priority than binary operators, e.g., -2^2=4 instead of -2^2=-4. Moreover, we are not too strict regarding parentheses. For instance "---1" will evalute to -1. If you want to be on the safe side, we suggest using parentheses.