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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
//! # Xprs
//!
//! [<img alt="github" src="https://img.shields.io/badge/github-vic1707/xprs-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/vic1707/xprs)
//! [<img alt="crates.io" src="https://img.shields.io/crates/v/xprs.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/xprs)
//! [<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/vic1707/xprs/global_matrix.yml?branch=main&style=for-the-badge" height="20">](https://github.com/vic1707/xprs/actions?query=branch%3Amain)
//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-xprs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/xprs)
//! [<img alt="downloads" src="https://img.shields.io/crates/d/xprs.svg?style=for-the-badge&logo=docs.rs" height="20">](https://crates.io/crates/xprs)
//!
//! **Xprs** is a flexible and extensible mathematical expression parser and evaluator for Rust, designed for simplicity and ease of use (and ideally, speed).
//!
//! ## Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! xprs = "0.1.0"
//! ```
//!
//! or run this command in your terminal:
//!
//! ```bash
//! cargo add xprs
//! ```
//!
//! Make sure to check the [Crates.io](https://crates.io/crates/xprs) page for the latest version.
//!
//! ## MSRV (Minimum Supported Rust Version)
//!
//! Currently, the minimum supported Rust version is `1.70.0`.
//!
//! ## Crate Features
//!
//! - **`compile-time-optimizations`** _(enabled by default)_ :
//!
//! Enable optimization and evaluation during parsing.
//! This feature will automagically transform expressions like `1 + 2 * 3` into `7` during parsing allowing for faster evaluation.
//! It also works on functions (e.g. `sin(0)` will be transformed into `0`) and "logical" result like `(x - x) * (....)` will be transformed into `0` since `x - x` is `0` no matter what `x` is.
//!
//! Note: nightly channel enables even more optimizations thanks to `box_patterns` feature gate.
//!
//! <br />
//!
//! - **`pemdas`** _(enabled by default)_:
//!
//! Conflicts with the `pejmdas` feature.
//! Uses the PEMDAS order of operations.
//! This implies that implicit multiplication has the same precedence as explicit multiplication.
//! For example:
//!
//! - `6/2(2+1)` gets interpreted as `6/2*(2+1)` which gives `9` as a result.
//! - `1/2x` gets interpreted as `(1/2)*x` which, with `x` being `2`, gives `1` as a result.
//!
//! Note: `Display` and `Debug` shows additional parenthesis to make the order of operations more obvious.
//!
//! <br />
//!
//! - **`pejmdas`**:
//!
//! Conflicts with the `pemdas` feature.
//! Uses the PEJMDAS order of operations.
//! This implies that implicit multiplication has a higher precedence than explicit multiplication.
//! For example:
//!
//! - `6/2(2+1)` gets interpreted as `6/(2*(2+1))` which gives `1` as a result.
//! - `1/2x` gets interpreted as `1/(2*x)` which, with `x` being `2`, gives `0.25` as a result.
//!
//! Note: `Display` and `Debug` shows additional parenthesis to make the order of operations more obvious.
//!
//! ## Usage
//!
//! ### Simple examples
//!
//! If you want to evaluate a simple calculus that doesn't contains any variables, you can use the `eval_no_vars` method (or `eval_no_vars_unchecked` if you know for sure that no variables are present):
//!
//! ```rust
//! use xprs::Xprs;
//!
//! fn main() {
//! let xprs = Xprs::try_from("1 + sin(2) * 3").unwrap();
//! println!("1 + sin(2) * 3 = {}", xprs.eval_no_vars().unwrap());
//! }
//! ```
//!
//! Note: Numbers are parsed as [`f64`] so you can use scientific notation (e.g. `1e-3`) with underscores (e.g. `1_000_000e2`).
//!
//! If you want to evaluate a calculus that contains variables, you can use the `eval` method (or `eval_unchecked` if you know for sure you're not missing any variables):
//!
//! ```rust
//! use xprs::Xprs;
//!
//! fn main() {
//! let xprs = Xprs::try_from("1 + sin(2) * x").unwrap();
//! println!(
//! "1 + sin(2) * x = {}",
//! xprs.eval(&[("x", 3.0)].into()).unwrap()
//! );
//! }
//! ```
//!
//! You can also turn the calculus into a function and use it later:
//!
//! ```rust
//! use xprs::Xprs;
//!
//! fn main() {
//! let xprs = Xprs::try_from("1 + sin(2) * x").unwrap();
//! let fn_xprs = xprs.bind("x").unwrap();
//! println!("1 + sin(2) * 3 = {}", fn_xprs(3.0));
//! }
//! ```
//!
//! You can use functions `bind`, `bind2` etc up to `bind9` to bind variables to the calculus.
//! If you ever need more, you can use the `bind_n` and `bind_n_runtime` methods which takes an array of size N or a slice respectively.
//!
//! Notes:
//! All `bind` function (except `bind_n_runtime`) returns a [`Result`] of a function which is guaranteed to return a [`f64`].
//! `bind_n_runtime` returns a [`Result`] of a function which also returns a [`Result`] of a [`f64`] since there are no guarantees that the array/slice will be of the correct size.
//!
//! ### Context and Parser
//!
//! You can also create a [`Context`] and a [`Parser`] instance if you want to define your own functions and/or constants and use them repeatedly.
//!
//! Constants and Functions can have any name that starts with a letter (uppercase of not) and contains only `[A-Za-z0-9_']`.
//!
//! Functions need to have a signature of `fn(&[f64]) -> f64` so they all have the same signature and can be called the same way.
//! We also need a name and the number of arguments the function takes, which is an [`Option<usize>`], if [`None`] then the function can take any number of arguments.
//! You can define functions like so:
//!
//! ```rust
//! use xprs::{Function, xprs_fn};
//!
//! fn double(x: f64) -> f64 {
//! x * 2.0
//! }
//!
//! const DOUBLE: Function = Function::new_static("double", move |args| double(args[0]), Some(1));
//! // or with the macro (will do an automatic wrapping)
//! const DOUBLE_MACRO: Function = xprs_fn!("double", double, 1);
//!
//! fn variadic_sum(args: &[f64]) -> f64 {
//! args.iter().sum()
//! }
//!
//! const SUM: Function = Function::new_static("sum", variadic_sum, None);
//! // or with the macro (no wrapping is done for variadic functions)
//! const SUM_MACRO: Function = xprs_fn!("sum", variadic_sum);
//!
//! // if a functions captures a variable (cannot be coerced to a static function)
//! const X: f64 = 42.0;
//! fn show_capture() {
//! let captures = |arg: f64| { X + arg };
//!
//! let CAPTURES: Function = Function::new_dyn("captures", move |args| captures(args[0]), Some(1));
//! // or with the macro (will do an automatic wrapping)
//! let CAPTURES_MACRO: Function = xprs_fn!("captures", dyn captures, 1);
//! }
//! ```
//!
//! To use a [`Context`] and a [`Parser`] you can do the following:
//!
//! ```rust
//! use xprs::{xprs_fn, Context, Parser};
//!
//! fn main() {
//! let mut context = Context::default()
//! .with_fn(xprs_fn!("double", |x| 2. * x, 1))
//! .with_var("foo", 1.0);
//! context.set_var("bar", 2.0);
//!
//! let xprs = Parser::new_with_ctx(context)
//! .parse("double(foo) + bar")
//! .unwrap();
//! println!("double(foo) + bar = {}", xprs.eval_no_vars().unwrap());
//! }
//! ```
//!
//! Note: [`Context`] is just a wrapper around a `HashMap` so you cannot have a function and a constant with the same name (the last one will override the first one).
//!
//! You can also use the [`Context`] to restrict the allowed variables in the calculus:
//!
//! ```rust
//! use xprs::{Context, Parser};
//!
//! fn main() {
//! let context = Context::default()
//! .with_expected_vars(["x", "y"].into());
//!
//! let parser = Parser::new_with_ctx(context);
//!
//! let result = parser.parse("x + y"); // OK
//! let fail = parser.parse("x + z"); // Error
//!
//! println!("{result:#?} {fail:#?}");
//! }
//! ```
//!
//! ### Error handling
//!
//! All errors are implemented using the [`thiserror`](https://crates.io/crates/thiserror).
//! And parsing errors are implemented using the [`miette`](https://crates.io/crates/miette) crate.
//! <img src="https://raw.githubusercontent.com/vic1707/xprs/main/assets/error.png" alt="Error message" width="300"/>
//!
//! ### Supported operations, built-in constants & functions
//!
//! #### Operations
//!
//! Xprs supports the following operations:
//!
//! - Binary operations: `+`, `-`, `*`, `/`, `^`, `%`.
//! - Unary operations: `+`, `-`, `!`.
//!
//! Note: `!` (factorial) is only supported on positive integers. Calling it on a negative integer or a float will result in `f64::NAN`. Also `-4!` is interpreted as `-(4!)` and not `(-4)!`.
//!
//! #### Built-in constants
//!
//! | Constant | Value | Approximation |
//! | -------- | ----- | ------------------- |
//! | `PI` | `π` | `3.141592653589793` |
//! | `E` | `e` | `2.718281828459045` |
//!
//! #### Built-in functions
//!
//! Xprs supports a variety of functions:
//!
//! - trigonometric functions: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`.
//! - logarithmic functions: `ln` (base 2), `log` (base 10), `logn` (base n, used as `logn(num, base)`).
//! - power functions: `sqrt`, `cbrt`, `exp`.
//! - rounding functions: `floor`, `ceil`, `round`, `trunc`.
//! - other functions: `abs`, `min`, `max`, `hypot`, `fract`, `recip` (`invert` alias), `sum`, `mean`, `factorial` and `gamma`.
//!
//! Note: `min` and `max` can take any number of arguments (if none, returns `f64::INFINITY` and `-f64::INFINITY` respectively).
//! Note2: `sum` and `mean` can take any number of arguments (if none, returns `0` and `f64::NAN` respectively).
//!
//! ### Advanced examples
//!
//! ## Xprs simplification
//!
//! You can simplify an [`Xprs`], in-place or not, for a given variable (or set of variables) using the `simplify_for` or `simplify_for_multiple` methods.
//!
//! ```rust
//! use xprs::Xprs;
//!
//! fn main() {
//! let mut xprs = Xprs::try_from("w + sin(x + 2y) * (3 * z)").unwrap();
//!
//! println!("{xprs}"); // (w + (sin((x + (2 * y))) * (3 * z)))
//!
//! xprs.simplify_for_in_place(("z", 4.0));
//!
//! println!("{xprs}"); // (w + (sin((x + (2 * y))) * 12))
//!
//! let xprs = xprs.simplify_for_multiple(&[("x", 1.0), ("y", 2.0)]);
//!
//! println!("{xprs}"); // (w + -11.507091295957661)
//! }
//! ```
//!
//! ## Higher order functions
//!
//! You can define functions in a context based on a previously parsed expression.
//!
//! ```rust
//! use xprs::{xprs_fn, Context, Parser, Xprs};
//!
//! fn main() {
//! let xprs_hof = Xprs::try_from("2x + y").unwrap();
//! let fn_hof = xprs_hof.bind2("x", "y").unwrap();
//! let hof = xprs_fn!("hof", dyn fn_hof, 2);
//! let ctx = Context::default().with_fn(hof);
//! let parser = Parser::new_with_ctx(ctx);
//!
//! let xprs = parser.parse("hof(2, 3)").unwrap();
//!
//! println!("hof(2, 3) = {}", xprs.eval_no_vars().unwrap());
//! }
//! ```
//!
//! These examples and others can be found in the [examples](./examples) directory.
//!
//! ## Documentation
//!
//! Complete documentation can be found on [docs.rs](https://docs.rs/xprs).
//!
//! ## License
//!
//! Copyright © 2023 [Victor LEFEBVRE](contact@vic1707.xyz)
//! This work is free. You can redistribute it and/or modify it under the
//! terms of the Do What The Fuck You Want To Public License, Version 2,
//! as published by Sam Hocevar. See the [LICENSE](./LICENSE). file for more details.
//!
//! ## TODOs
//!
//! Here is a non-exhaustive list of the things I want to do/add in the future:
//!
//! - [ ] Better CI/CD.
//! - [ ] Remove lifetimes by replacing `&str` with something like [`byteyarn`](https://crates.io/crates/byteyarn).
//! - [ ] Complex numbers support.
//! - [ ] Macro for defining the [`Context`] like the one in [`evalexpr`](https://crates.io/crates/evalexpr).
//! - [ ] Support for dynamic [`Function`] name.
//! - [ ] Native variadics (when rust supports them in stable).
//! - [ ] Have [`Xprs`] be generic, taking float for its return type if that's even possible (regarding the dependency on [`Context`]).
//!
//! If one of them picks your interest feel free to open a PR!
/* NIGHTLY Features */
// box-patterns if nightly
/* Clippy config */
/* Modules */
/// The context of the parser.
/// The element of the abstract syntax tree (AST).
/// The parser module.
/// The token module.
/// The utilities module.
/// The expression module.
/* Tests */
/* Exports */
pub use crate;
pub use crate;
pub use crateFunction;
pub use crate;
/* Feature safety */
compile_error!;
compile_error!;