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