1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
//! This [Rust] crate provides a simple math expression parsing and evaluation. Its main goal is to //! be convenient to use, while allowing for some flexibility. Currently works only with `f64` //! types. A typical use case is the configuration of numerical computations in //! Rust, think initial data and boundary conditions, via config files or command line arguments. //! //! # Documentation //! //! - [Full API documentation](https://docs.rs/meval) //! //! # Installation //! //! Simply add the corresponding entry to your `Cargo.toml` dependency list: //! //! ```toml //! [dependencies] //! meval = "0.2" //! ``` //! //! and add this to your crate root: //! //! ```rust //! extern crate meval; //! ``` //! //! **Requires Rust 1.26.** //! //! # Simple examples //! //! ```rust //! extern crate meval; //! //! fn main() { //! let r = meval::eval_str("1 + 2").unwrap(); //! //! println!("1 + 2 = {}", r); //! } //! ``` //! //! Need to define a Rust function from an expression? No problem, use [`Expr`][Expr] //! for this and more: //! //! ```rust //! extern crate meval; //! //! fn main() { //! let expr: meval::Expr = "sin(pi * x)".parse().unwrap(); //! let func = expr.bind("x").unwrap(); //! //! let vs: Vec<_> = (0..100+1).map(|i| func(i as f64 / 100.)).collect(); //! //! println!("sin(pi * x), 0 <= x <= 1: {:?}", vs); //! } //! ``` //! //! Custom constants and functions? Define a [`Context`][Context]! //! //! ```rust //! use meval::{Expr, Context}; //! //! let y = 1.; //! let expr: Expr = "phi(-2 * zeta + x)".parse().unwrap(); //! //! // create a context with function definitions and variables //! let mut ctx = Context::new(); // built-ins //! ctx.func("phi", |x| x + y) //! .var("zeta", -1.); //! // bind function with a custom context //! let func = expr.bind_with_context(ctx, "x").unwrap(); //! assert_eq!(func(2.), -2. * -1. + 2. + 1.); //! ``` //! //! For functions of 2, 3, and N variables use `Context::func2`, `Context::func3` and //! `Context::funcn`, //! respectively. See [`Context`][Context] for more options. //! //! If you need a custom function depending on mutable parameters, you will need to use a //! [`Cell`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html): //! //! ```rust //! use std::cell::Cell; //! use meval::{Expr, Context}; //! let y = Cell::new(0.); //! let expr: Expr = "phi(x)".parse().unwrap(); //! //! let mut ctx = Context::empty(); // no built-ins //! ctx.func("phi", |x| x + y.get()); //! //! let func = expr.bind_with_context(ctx, "x").unwrap(); //! assert_eq!(func(2.), 2.); //! y.set(3.); //! assert_eq!(func(2.), 5.); //! ``` //! //! # Supported expressions //! //! `meval` supports basic mathematical operations on floating point numbers: //! //! - binary operators: `+`, `-`, `*`, `/`, `%` (remainder), `^` (power) //! - unary operators: `+`, `-` //! //! It supports custom variables and functions like `x`, `weight`, `C_0`, `f(1)`, etc. A variable //! or function name must start with `[a-zA-Z_]` and can contain only `[a-zA-Z0-9_]`. Custom //! functions with a variable number of arguments are also supported. //! //! Build-ins (given by the context `Context::new()` and when no context provided) currently //! supported: //! //! - functions implemented using functions of the same name in [Rust std library][std-float]: //! //! - `sqrt`, `abs` //! - `exp`, `ln` //! - `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2` //! - `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh` //! - `floor`, `ceil`, `round` //! - `signum` //! //! - other functions: //! //! - `max(x, ...)`, `min(x, ...)`: maximum and minimumum of 1 or more numbers //! //! - constants: //! //! - `pi` //! - `e` //! //! # Deserialization //! //! [`Expr`][Expr] supports deserialization using the [serde] library to make flexible //! configuration easy to set up, if the feature `serde` is enabled (disable by default). //! #![cfg_attr(feature = "serde", doc = " ```rust")] #![cfg_attr(not(feature = "serde"), doc = " ```rust,ignore")] //! #[macro_use] //! extern crate serde_derive; //! extern crate toml; //! extern crate meval; //! use meval::{Expr, Context}; //! //! #[derive(Deserialize)] //! struct Ode { //! #[serde(deserialize_with = "meval::de::as_f64")] //! x0: f64, //! #[serde(deserialize_with = "meval::de::as_f64")] //! t0: f64, //! f: Expr, //! g: Expr, //! } //! //! fn main() { //! let config = r#" //! x0 = "cos(1.)" //! t0 = 2 //! f = "sin(x)" //! g = 2.5 //! "#; //! let ode: Ode = toml::from_str(config).unwrap(); //! //! assert_eq!(ode.x0, 1f64.cos()); //! assert_eq!(ode.t0, 2f64); //! assert_eq!(ode.f.bind("x").unwrap()(2.), 2f64.sin()); //! assert_eq!(ode.g.eval().unwrap(), 2.5f64); //! } //! //! ``` //! //! # Related projects //! //! This is a toy project of mine for learning Rust, and to be hopefully useful when writing //! command line scripts. There is no plan to make this anything more than _math expression -> //! number_ "converter". For more advanced scripting, see: //! //! - [dyon] -- A rusty dynamically typed scripting language //! - [gluon] -- A static, type inferred programming language for application embedding //! - [rodolf0/tox](https://github.com/rodolf0/tox) -- another shunting yard expression parser //! //! [Rust]: https://www.rust-lang.org/ //! [std-float]: http://doc.rust-lang.org/stable/std/primitive.f64.html //! //! [Expr]: struct.Expr.html //! [Expr::bind]: struct.Expr.html#method.bind //! [Context]: struct.Context.html //! [serde]: https://crates.io/crates/serde //! [dyon]: https://crates.io/crates/dyon //! [gluon]: https://crates.io/crates/gluon #[macro_use] extern crate nom; extern crate fnv; #[cfg(feature = "serde")] extern crate serde; #[cfg_attr(all(test, feature = "serde"), macro_use)] #[cfg(all(test, feature = "serde"))] extern crate serde_derive; #[cfg(test)] extern crate serde_json; #[cfg(test)] extern crate serde_test; use std::fmt; mod expr; pub mod shunting_yard; pub mod tokenizer; #[cfg(feature = "serde")] pub mod de; pub use expr::*; pub use shunting_yard::RPNError; pub use tokenizer::ParseError; /// An error produced during parsing or evaluation. #[derive(Debug, Clone, PartialEq)] pub enum Error { UnknownVariable(String), Function(String, FuncEvalError), /// An error returned by the parser. ParseError(ParseError), /// The shunting-yard algorithm returned an error. RPNError(RPNError), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::UnknownVariable(ref name) => { write!(f, "Evaluation error: unknown variable `{}`.", name) } Error::Function(ref name, ref e) => { write!(f, "Evaluation error: function `{}`: {}", name, e) } Error::ParseError(ref e) => { try!(write!(f, "Parse error: ")); e.fmt(f) } Error::RPNError(ref e) => { try!(write!(f, "RPN error: ")); e.fmt(f) } } } } impl From<ParseError> for Error { fn from(err: ParseError) -> Error { Error::ParseError(err) } } impl From<RPNError> for Error { fn from(err: RPNError) -> Error { Error::RPNError(err) } } impl std::error::Error for Error { fn description(&self) -> &str { match *self { Error::UnknownVariable(_) => "unknown variable", Error::Function(_, _) => "function evaluation error", Error::ParseError(ref e) => e.description(), Error::RPNError(ref e) => e.description(), } } fn cause(&self) -> Option<&std::error::Error> { match *self { Error::ParseError(ref e) => Some(e), Error::RPNError(ref e) => Some(e), Error::Function(_, ref e) => Some(e), _ => None, } } }