arithmetic_eval/env/
mod.rs

1//! [`Environment`] and other types related to [`Value`] collections.
2
3use core::{iter, ops};
4
5pub use self::variable_map::{Assertions, Comparisons, Prelude};
6use crate::{
7    NativeFn, Value,
8    alloc::{Arc, HashMap, String, ToOwned, hash_map},
9    arith::{OrdArithmetic, StdArithmetic},
10    exec::Operations,
11    fns,
12};
13
14mod variable_map;
15
16/// Environment containing named `Value`s.
17///
18/// Note that the environment implements the [`Index`](ops::Index) trait, which allows to eloquently
19/// access or modify environment. Similarly, [`IntoIterator`] / [`Extend`] traits
20/// allow to construct environments.
21///
22/// # Examples
23///
24/// ```
25/// use arithmetic_eval::{env::{Comparisons, Prelude}, Environment, Value};
26///
27/// // Load environment from the standard containers.
28/// let mut env = Environment::<f64>::new();
29/// env.extend(Prelude::iter().chain(Comparisons::iter()));
30/// // Add a custom variable for a good measure.
31/// env.insert("x", Value::Prim(1.0));
32///
33/// assert_eq!(env["true"], Value::Bool(true));
34/// assert_eq!(env["x"], Value::Prim(1.0));
35/// for (name, value) in &env {
36///     println!("{name} -> {value:?}");
37/// }
38///
39/// // It's possible to base an environment on other env, as well.
40/// let mut other_env = Environment::new();
41/// other_env.extend(
42///     env.into_iter().filter(|(_, val)| val.is_function()),
43/// );
44/// assert!(other_env.get("x").is_none());
45/// ```
46#[derive(Debug, Clone)]
47pub struct Environment<T> {
48    variables: HashMap<String, Value<T>>,
49    arithmetic: Arc<dyn OrdArithmetic<T>>,
50}
51
52impl<T> Default for Environment<T>
53where
54    StdArithmetic: OrdArithmetic<T>,
55{
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61/// Compares environments by variables; arithmetics are ignored.
62impl<T: PartialEq> PartialEq for Environment<T> {
63    fn eq(&self, other: &Self) -> bool {
64        self.variables == other.variables
65    }
66}
67
68impl<T> Environment<T>
69where
70    StdArithmetic: OrdArithmetic<T>,
71{
72    /// Creates a new environment.
73    pub fn new() -> Self {
74        Self {
75            variables: HashMap::new(),
76            arithmetic: Arc::new(StdArithmetic),
77        }
78    }
79}
80
81impl<T> Environment<T> {
82    /// Creates an environment with the specified arithmetic.
83    pub fn with_arithmetic<A>(arithmetic: A) -> Self
84    where
85        A: OrdArithmetic<T> + 'static,
86    {
87        Self {
88            variables: HashMap::new(),
89            arithmetic: Arc::new(arithmetic),
90        }
91    }
92
93    pub(crate) fn operations(&self) -> Operations<'_, T> {
94        Operations::from(&*self.arithmetic)
95    }
96
97    /// Gets a variable by name.
98    pub fn get(&self, name: &str) -> Option<&Value<T>> {
99        self.variables.get(name)
100    }
101
102    /// Checks if this environment contains a variable with the specified name.
103    pub fn contains(&self, name: &str) -> bool {
104        self.variables.contains_key(name)
105    }
106
107    /// Iterates over variables.
108    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value<T>)> + '_ {
109        self.variables
110            .iter()
111            .map(|(name, value)| (name.as_str(), value))
112    }
113
114    /// Inserts a variable with the specified name.
115    pub fn insert(&mut self, name: &str, value: Value<T>) -> &mut Self {
116        self.variables.insert(name.to_owned(), value);
117        self
118    }
119
120    /// Inserts a native function with the specified name.
121    pub fn insert_native_fn(
122        &mut self,
123        name: &str,
124        native_fn: impl NativeFn<T> + 'static,
125    ) -> &mut Self {
126        self.insert(name, Value::native_fn(native_fn))
127    }
128
129    /// Inserts a [wrapped function](fns::FnWrapper) with the specified name.
130    ///
131    /// Calling this method is equivalent to [`wrap`](fns::wrap)ping a function and calling
132    /// [`insert_native_fn()`](Self::insert_native_fn) on it. Thanks to type inference magic,
133    /// the Rust compiler will usually be able to extract the `Args` type param
134    /// from the function definition, provided that type of function arguments and its return type
135    /// are defined explicitly or can be unequivocally inferred from the declaration.
136    pub fn insert_wrapped_fn<const CTX: bool, Args, F>(
137        &mut self,
138        name: &str,
139        fn_to_wrap: F,
140    ) -> &mut Self
141    where
142        fns::FnWrapper<Args, F, CTX>: NativeFn<T> + 'static,
143    {
144        let wrapped = fns::wrap::<CTX, Args, _>(fn_to_wrap);
145        self.insert(name, Value::native_fn(wrapped))
146    }
147}
148
149impl<T> ops::Index<&str> for Environment<T> {
150    type Output = Value<T>;
151
152    fn index(&self, index: &str) -> &Self::Output {
153        self.get(index)
154            .unwrap_or_else(|| panic!("Variable `{index}` is not defined"))
155    }
156}
157
158impl<T> IntoIterator for Environment<T> {
159    type Item = (String, Value<T>);
160    type IntoIter = IntoIter<T>;
161
162    fn into_iter(self) -> Self::IntoIter {
163        IntoIter {
164            inner: self.variables.into_iter(),
165        }
166    }
167}
168
169/// Result of converting `Environment` into an iterator.
170#[derive(Debug)]
171pub struct IntoIter<T> {
172    inner: hash_map::IntoIter<String, Value<T>>,
173}
174
175impl<T> Iterator for IntoIter<T> {
176    type Item = (String, Value<T>);
177
178    fn next(&mut self) -> Option<Self::Item> {
179        self.inner.next()
180    }
181
182    fn size_hint(&self) -> (usize, Option<usize>) {
183        self.inner.size_hint()
184    }
185}
186
187impl<T> ExactSizeIterator for IntoIter<T> {
188    fn len(&self) -> usize {
189        self.inner.len()
190    }
191}
192
193impl<'r, T> IntoIterator for &'r Environment<T> {
194    type Item = (&'r str, &'r Value<T>);
195    type IntoIter = Iter<'r, T>;
196
197    fn into_iter(self) -> Self::IntoIter {
198        Iter {
199            inner: self
200                .variables
201                .iter()
202                .map(|(name, value)| (name.as_str(), value)),
203        }
204    }
205}
206
207type MapFn<'r, T> = fn((&'r String, &'r Value<T>)) -> (&'r str, &'r Value<T>);
208
209/// Iterator over references of the `Environment` entries.
210#[derive(Debug)]
211pub struct Iter<'r, T> {
212    inner: iter::Map<hash_map::Iter<'r, String, Value<T>>, MapFn<'r, T>>,
213}
214
215impl<'r, T> Iterator for Iter<'r, T> {
216    type Item = (&'r str, &'r Value<T>);
217
218    fn next(&mut self) -> Option<Self::Item> {
219        self.inner.next()
220    }
221
222    fn size_hint(&self) -> (usize, Option<usize>) {
223        self.inner.size_hint()
224    }
225}
226
227impl<T> ExactSizeIterator for Iter<'_, T> {
228    fn len(&self) -> usize {
229        self.inner.len()
230    }
231}
232
233impl<T, S, V> Extend<(S, V)> for Environment<T>
234where
235    S: Into<String>,
236    V: Into<Value<T>>,
237{
238    fn extend<I: IntoIterator<Item = (S, V)>>(&mut self, iter: I) {
239        let variables = iter
240            .into_iter()
241            .map(|(var_name, value)| (var_name.into(), value.into()));
242        self.variables.extend(variables);
243    }
244}