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
use std::{fmt::Debug, mem, str::FromStr};
use smallvec::SmallVec;
use crate::{
data_type::DataType,
definitions::{N_BINOPS_OF_DEEPEX_ON_STACK, N_UNARYOPS_OF_DEEPEX_ON_STACK},
operators::OperateBinary,
parser, ExResult, MakeOperators,
};
use self::{deep::DeepEx, number_tracker::NumberTracker};
pub mod calculate;
pub mod deep;
pub mod flat;
mod number_tracker;
#[cfg(feature = "partial")]
pub mod partial;
#[cfg(feature = "serde")]
mod serde;
/// Expressions implementing this trait can be parsed from stings,
/// evaluated for specific variable values, and unparsed, i.e.,
/// transformed into a string representation.
pub trait Express<'a, T>: Clone
where
T: Clone,
{
type OperatorFactory: MakeOperators<T>;
type LiteralMatcher: MatchLiteral;
/// Evaluates an expression with the given variable values and returns the computed
/// result.
///
/// # Arguments
///
/// * `vars` - Values of the variables of the expression; the n-th value corresponds to
/// the n-th variable in alphabetical order.
/// Thereby, only the first occurrence of the variable in the string is relevant.
/// If an expression has been created by partial derivation, the variables always
/// coincide with those of the antiderivatives even in cases where variables are
/// irrelevant such as `(x)'=1`.
///
/// # Errors
///
/// If the number of variables in the parsed expression are different from the length of
/// the variable slice, we return an [`ExError`](super::result::ExError).
///
fn eval(&self, vars: &[T]) -> ExResult<T>;
/// Evaluates an expression with the given variable values and returns the computed
/// result. If more variables are passed than necessary the unnecessary ones are ignored.
///
/// # Arguments
///
/// * `vars` - Values of the variables of the expression; the n-th value corresponds to
/// the n-th variable in alphabetical order.
/// Thereby, only the first occurrence of the variable in the string is relevant.
/// If an expression has been created by partial derivation, the variables always
/// coincide with those of the antiderivatives even in cases where variables are
/// irrelevant such as `(x)'=1`.
///
/// # Errors
///
/// If the number of variables in the parsed expression is larger than the length of
/// the variable slice, we return an [`ExError`](super::result::ExError).
///
fn eval_relaxed(&self, vars: &[T]) -> ExResult<T>;
/// Creates an expression string that corresponds to the `FlatEx` instance.
/// ```rust
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use exmex::prelude::*;
/// let flatex = FlatEx::<f64>::parse("--sin ( z) + {another var} + 1 + 2")?;
/// assert_eq!(format!("{}", flatex), "--sin ( z) + {another var} + 1 + 2");
/// #
/// # Ok(())
/// # }
/// ```
///
fn unparse(&self) -> &str;
/// Returns the variables of the expression
fn var_names(&self) -> &[String];
/// Conversion to a deep expression necessary for computations with expressions
fn to_deepex(self) -> ExResult<DeepEx<'a, T, Self::OperatorFactory, Self::LiteralMatcher>>
where
Self: Sized,
T: DataType,
<T as FromStr>::Err: Debug;
fn from_deepex(
deepex: DeepEx<'a, T, Self::OperatorFactory, Self::LiteralMatcher>,
) -> ExResult<Self>
where
Self: Sized,
T: DataType,
<T as FromStr>::Err: Debug;
fn parse(text: &'a str) -> ExResult<Self>
where
Self: Sized;
/// Returns an alphabetically sorted list of all binary operators without duplicates in the expression.
fn binary_reprs(&self) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK]>;
/// Returns an alphabetically sorted list of all unary operators without duplicates in the expression.
fn unary_reprs(&self) -> SmallVec<[String; N_UNARYOPS_OF_DEEPEX_ON_STACK]>;
/// Returns an alphabetically sorted list of all operators without duplicates in the expression.
fn operator_reprs(
&self,
) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK + N_UNARYOPS_OF_DEEPEX_ON_STACK]>;
}
pub fn eval_binary<T, O, N>(
numbers: &mut [T],
binary_ops: &[O],
prio_indices: &[usize],
tracker: &mut N,
) -> T
where
T: Clone + Default,
O: OperateBinary<T>,
N: NumberTracker + ?Sized,
{
for &idx in prio_indices {
let shift_left = tracker.get_previous(idx);
let shift_right = tracker.consume_next(idx);
let num_1_idx = idx - shift_left;
let num_2_idx = idx + shift_right;
numbers[num_1_idx] = binary_ops[idx].apply(
mem::take(&mut numbers[num_1_idx]),
mem::take(&mut numbers[num_2_idx]),
);
}
mem::take(numbers.iter_mut().next().unwrap())
}
/// Implement this trait to create a matcher for custom literals of operands.
pub trait MatchLiteral: Clone + Debug {
/// This method is expected to return `Some(matching_str)` in case of a match of
/// a literal at the beginning of the input and `None` otherwise.
fn is_literal(text: &str) -> Option<&str>;
}
/// Default factory to match numeric literals.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct NumberMatcher;
impl MatchLiteral for NumberMatcher {
fn is_literal(text: &str) -> Option<&str> {
parser::is_numeric_text(text)
}
}
/// Helper to implement a struct called `$matcher_name` that implements
/// [`MatchLiteral`](MatchLiteral) and matches the regex pattern `$regex_pattern`.
///
/// For instance, to match only boolean literals one can create a struct with name
/// `BooleanMatcher` via
/// ```rust
/// use exmex::{literal_matcher_from_pattern, MatchLiteral};
/// literal_matcher_from_pattern!(BooleanMatcher, "^(true|false)");
/// ```
#[macro_export]
macro_rules! literal_matcher_from_pattern {
($matcher_name:ident, $regex_pattern:expr) => {
/// Literal matcher type that was created with the macro
/// [`literal_matcher_from_pattern`](literal_matcher_from_pattern).
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Default)]
pub struct $matcher_name;
impl MatchLiteral for $matcher_name {
fn is_literal(text: &str) -> Option<&str> {
static RE_VAR_NAME_EXACT: std::sync::LazyLock<$crate::regex::Regex> =
std::sync::LazyLock::new(|| $crate::regex::Regex::new($regex_pattern).unwrap());
RE_VAR_NAME_EXACT.find(text).map(|m| m.as_str())
}
}
};
}