arithmetic_eval/values/
variable_map.rs

1//! `VariableMap` trait and implementations.
2
3#![allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
4// ^ `missing_panics_doc` is newer than MSRV, and `clippy::unknown_clippy_lints` is removed
5// since Rust 1.51.
6
7use arithmetic_parser::{grammars::Grammar, Block};
8
9use core::{cmp::Ordering, fmt};
10
11use crate::{fns, Environment, Error, ExecutableModule, ModuleId, ModuleImports, Value};
12
13/// Encapsulates read access to named variables.
14pub trait VariableMap<'a, T> {
15    /// Returns value of the named variable, or `None` if it is not defined.
16    fn get_variable(&self, name: &str) -> Option<Value<'a, T>>;
17
18    /// Creates a module based on imports solely from this map.
19    ///
20    /// The default implementation is reasonable for most cases.
21    fn compile_module<Id, G>(
22        &self,
23        id: Id,
24        block: &Block<'a, G>,
25    ) -> Result<ExecutableModule<'a, T>, Error<'a>>
26    where
27        Id: ModuleId,
28        G: Grammar<'a, Lit = T>,
29        T: Clone + fmt::Debug,
30    {
31        ExecutableModule::builder(id, block)?
32            .with_imports_from(self)
33            .try_build()
34    }
35}
36
37impl<'a, T: Clone> VariableMap<'a, T> for Environment<'a, T> {
38    fn get_variable(&self, name: &str) -> Option<Value<'a, T>> {
39        self.get(name).cloned()
40    }
41}
42
43impl<'a, T: Clone> VariableMap<'a, T> for ModuleImports<'a, T> {
44    fn get_variable(&self, name: &str) -> Option<Value<'a, T>> {
45        self.get(name).cloned()
46    }
47}
48
49/// Commonly used constants and functions from the [`fns` module](fns).
50///
51/// # Contents
52///
53/// - All functions from the `fns` module, except for [`Compare`](fns::Compare)
54///   (contained in [`Comparisons`]) and assertion functions (contained in [`Assertions`]).
55///   All functions are named in lowercase, e.g., `if`, `map`.
56/// - `true` and `false` Boolean constants.
57#[derive(Debug, Clone, Copy, Default)]
58pub struct Prelude;
59
60impl Prelude {
61    /// Creates an iterator over contained values and the corresponding names.
62    #[allow(clippy::missing_panics_doc)] // false positive; `unwrap()` never panics
63    pub fn iter<T: Clone>(self) -> impl Iterator<Item = (&'static str, Value<'static, T>)> {
64        const VAR_NAMES: &[&str] = &[
65            "false", "true", "if", "loop", "while", "map", "filter", "fold", "push", "merge",
66        ];
67
68        VAR_NAMES
69            .iter()
70            .map(move |&var_name| (var_name, self.get_variable(var_name).unwrap()))
71    }
72}
73
74impl<'a, T: Clone> VariableMap<'a, T> for Prelude {
75    fn get_variable(&self, name: &str) -> Option<Value<'a, T>> {
76        Some(match name {
77            "false" => Value::Bool(false),
78            "true" => Value::Bool(true),
79            "if" => Value::native_fn(fns::If),
80            "loop" => Value::native_fn(fns::Loop),
81            "while" => Value::native_fn(fns::While),
82            "map" => Value::native_fn(fns::Map),
83            "filter" => Value::native_fn(fns::Filter),
84            "fold" => Value::native_fn(fns::Fold),
85            "push" => Value::native_fn(fns::Push),
86            "merge" => Value::native_fn(fns::Merge),
87            _ => return None,
88        })
89    }
90}
91
92/// Container for assertion functions: `assert` and `assert_eq`.
93#[derive(Debug, Clone, Copy, Default)]
94pub struct Assertions;
95
96impl Assertions {
97    /// Creates an iterator over contained values and the corresponding names.
98    #[allow(clippy::missing_panics_doc)] // false positive; `unwrap()` never panics
99    pub fn iter<T: fmt::Display>(self) -> impl Iterator<Item = (&'static str, Value<'static, T>)> {
100        const VAR_NAMES: &[&str] = &["assert", "assert_eq"];
101
102        VAR_NAMES
103            .iter()
104            .map(move |&var_name| (var_name, self.get_variable(var_name).unwrap()))
105    }
106}
107
108impl<'a, T: fmt::Display> VariableMap<'a, T> for Assertions {
109    fn get_variable(&self, name: &str) -> Option<Value<'a, T>> {
110        Some(match name {
111            "assert" => Value::native_fn(fns::Assert),
112            "assert_eq" => Value::native_fn(fns::AssertEq),
113            _ => return None,
114        })
115    }
116}
117
118/// Container with the comparison functions: `cmp`, `min` and `max`.
119#[derive(Debug, Clone, Copy, Default)]
120pub struct Comparisons;
121
122impl<'a, T> VariableMap<'a, T> for Comparisons {
123    fn get_variable(&self, name: &str) -> Option<Value<'a, T>> {
124        Some(match name {
125            "LESS" => Value::opaque_ref(Ordering::Less),
126            "EQUAL" => Value::opaque_ref(Ordering::Equal),
127            "GREATER" => Value::opaque_ref(Ordering::Greater),
128            "cmp" => Value::native_fn(fns::Compare::Raw),
129            "min" => Value::native_fn(fns::Compare::Min),
130            "max" => Value::native_fn(fns::Compare::Max),
131            _ => return None,
132        })
133    }
134}
135
136impl Comparisons {
137    /// Creates an iterator over contained values and the corresponding names.
138    #[allow(clippy::missing_panics_doc)] // false positive; `unwrap()` never panics
139    pub fn iter<T>(self) -> impl Iterator<Item = (&'static str, Value<'static, T>)> {
140        const VAR_NAMES: &[&str] = &["LESS", "EQUAL", "GREATER", "cmp", "min", "max"];
141
142        VAR_NAMES
143            .iter()
144            .map(move |&var_name| (var_name, self.get_variable(var_name).unwrap()))
145    }
146}