interpreter/
value.rs

1//! This module contains the [`Value`] struct, which represents a variable or function
2//! stored within an `Environment`. It also contains all the logic for manipulating
3//! values (such as addition, comparison or conversion between types).
4use std::fmt::{self, Debug};
5
6use ast::statement::Statement;
7use shared::err;
8use shared::error::{Error, ErrorKind};
9
10use crate::runtime::EnvRef;
11
12/// This struct is how the interpreter stores functions. It contains the function's
13/// parameters, body and a reference to the environment in which it was defined.
14///
15/// By keeping a reference to the environment where the function was created, the
16/// interpreter can access the variables within that environment when the function is
17/// called. This also enables the interpreter to create closures.
18///
19/// Closures are functions that retain access to the environment in which they were
20/// defined. Even if that environment goes out of scope, the function holds onto that
21/// environment, keeping it alive, allowing it to still access the variables within.
22#[derive(PartialEq)]
23pub struct Function {
24    pub parameters: Vec<String>,
25    // TODO: Use StatementList instead of Vec<Statement>
26    pub body: Vec<Statement>,
27    pub env: EnvRef,
28}
29
30/// Implementing the `Debug` trait for [`Function`] allows printing of the function's
31/// parameters and the number of statements in its body.
32impl Debug for Function {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(
35            f,
36            "params: ({}), body: [{}]",
37            self.parameters.join(", "),
38            self.body.len()
39        )
40    }
41}
42
43/// The [`Value`] enum represents a data type that can be stored in an `Environment`. Each
44/// type contains the data it represents (e.g. a [`Value::Integer`] contains an `i64`).
45///
46/// There are 6 types of values: `Integer`, `Float`, `Boolean`, `String`, `Function` and
47/// `Null`.
48#[derive(Debug, PartialEq)]
49pub enum Value {
50    Integer(i64),
51    Float(f64),
52    Boolean(bool),
53    String(String),
54    Function(Function),
55    /// The `Null` variant is used to represent the absence of a value. It is used to
56    /// represent the result of an expression that doesn't return anything (e.g. a
57    /// function call that doesn't return anything).
58    Null,
59}
60
61/// This macro generates a method for the [`Value`] enum that allows for comparison
62/// operations. It takes in a function name, operator symbol, and a [`std::cmp::Ordering`]
63/// pattern to match against.
64///
65/// # Arguments
66///
67/// * `method_name` - The name of the method to generate.
68/// * `op` - The operator symbol to use in the error message if the comparison fails.
69/// * `ordering` - A pattern consisting of [`std::cmp::Ordering`] variants to match
70///   against the result of the comparison.
71///
72/// # Expands to
73///
74/// ```compile_fail ignore
75/// pub fn method_name(&self, other: &Self) -> Result<Self, Error> {
76///    ...
77/// }
78/// ```
79macro_rules! generate_comparison_op {
80    ($method_name:ident, $op:tt, $ordering:pat) => {
81        pub fn $method_name(&self, other: &Self) -> Result<Self, Error> {
82            match self.compare(other) {
83                Some($ordering) => Ok(Self::Boolean(true)),
84                Some(_) => Ok(Self::Boolean(false)),
85                None => err!(
86                    ErrorKind::TypeError,
87                    "invalid operation '{}' between {} and {}",
88                    stringify!($op),
89                    self.variant_name(),
90                    other.variant_name()
91                ),
92            }
93        }
94    };
95}
96
97/// This macro generates a method for the [`Value`] enum that allows for binary arithmetic
98/// operations. It takes in a function name, a `checked_op` and a symbol operator.
99///
100/// # Arguments
101///
102/// * `method_name` - The name of the method to generate.
103/// * `checked_op` - The name of the method to use on integer operations. Must be one of:
104///   `checked_add`, `checked_sub`, `checked_mul` or `checked_div`.
105/// * `symbol_op` - The symbol operator to use on float operations. Must be a valid
106///   operator for two [`f64`] values. Also used in the error message if the operation
107///   fails.
108/// 
109/// # Expands to
110/// 
111/// ```compile_fail ignore
112/// pub fn method_name(&self, other: &Self) -> Result<Self, Error> {
113///    ...  
114/// }
115/// ```
116macro_rules! generate_binary_arithmetic_op {
117    ($method_name:ident, $checked_op:tt, $symbol_op:tt) => {
118        pub fn $method_name(&self, other: &Self) -> Result<Self, Error> {
119
120            let overflow_error = Error::new(
121                "this arithmetic operation overflows",
122                ErrorKind::OverflowError
123            );
124
125            match (&self, &other) {
126                (Self::Integer(a), Self::Integer(b)) => {
127                    Ok(Self::Integer(a.$checked_op(*b).ok_or(overflow_error)?))
128                },
129                (Self::Float(a), Self::Float(b)) => Ok(Self::Float(a $symbol_op b)),
130                _ => err!(
131                    ErrorKind::TypeError,
132                    "invalid operation '{}' between {} and {}",
133                    stringify!($symbol_op),
134                    self.variant_name(),
135                    other.variant_name()
136                ),
137            }
138        }
139    };
140}
141
142impl Value {
143    /// Returns the display name of the variant of the [`Value`] enum.
144    pub fn variant_name(&self) -> &'static str {
145        match &self {
146            Self::Integer(_) => "Integer",
147            Self::Float(_) => "Float",
148            Self::Boolean(_) => "Boolean",
149            Self::String(_) => "String",
150            Self::Function(_) => "Function",
151            Self::Null => "null",
152        }
153    }
154
155    // region: type coercion
156
157    /// This method converts any [`Value`] to a [`Value::Boolean`]. It returns an error if
158    /// the conversion is not possible.
159    pub fn cast_to_boolean(&self) -> Result<Self, Error> {
160        match self {
161            Self::Integer(i) => Ok(Self::Boolean(*i != 0)),
162            Self::Float(f) => Ok(Self::Boolean(*f != 0.0)),
163            // goofy ahh code, it just obtians a reference to the internal value and wraps
164            // it back inside itself.
165            Self::Boolean(b) => Ok(Self::Boolean(*b)),
166            _ => err!(
167                ErrorKind::TypeError,
168                "cannot convert {} to {}",
169                self.variant_name(),
170                Self::Boolean(true).variant_name() // i hate this i hate this i hate this
171            ),
172        }
173    }
174
175    /// This method converts any [`Value`] to a [`Value::Integer`]. It returns an error if
176    /// the conversion is not possible. (For now, all conversions are possible.)
177    pub fn cast_to_string(&self) -> Self {
178        match self {
179            Self::Integer(i) => Self::String(i.to_string()),
180            Self::Float(f) => Self::String(f.to_string()),
181            Self::Boolean(b) => Self::String(b.to_string()),
182            // I suppose type coercion *should* provide ownership of the new value, so this is okay.
183            Self::String(s) => Self::String(s.clone()),
184            Self::Null => Self::String("null".to_string()),
185            Self::Function(_) => Self::String("<function reference>".to_string()),
186        }
187    }
188
189    // endregion: type coercion
190
191    // region: comparitive operations
192
193    pub fn eq(&self, other: &Self) -> Result<Self, Error> {
194        match (self, other) {
195            (Self::Integer(a), Self::Integer(b)) => Ok(Self::Boolean(*a == *b)),
196            (Self::Float(a), Self::Float(b)) => Ok(Self::Boolean(*a == *b)),
197            (Self::Boolean(a), Self::Boolean(b)) => Ok(Self::Boolean(*a == *b)),
198            (Self::String(a), Self::String(b)) => Ok(Self::Boolean(*a == *b)),
199            (Self::Null, Self::Null) => Ok(Self::Boolean(true)),
200            _ => err!(
201                ErrorKind::TypeError,
202                "invalid operation '==' between {} and {}",
203                self.variant_name(),
204                other.variant_name()
205            ),
206        }
207    }
208
209    pub fn ne(&self, other: &Self) -> Result<Self, Error> {
210        match self.eq(other) {
211            Ok(value) => value.not(),
212            Err(mut old_err) => {
213                old_err.message = format!(
214                    "invalid operation '!=' between {} and {}",
215                    self.variant_name(),
216                    other.variant_name()
217                );
218                Err(old_err)
219            }
220        }
221    }
222
223    /// This is a helper method for the comparison operations. it returns a `partial_cmp`
224    /// of the two internal values of the [`Value`] enum. Primarily used for the comparison
225    /// functions defined after this function.
226    fn compare(&self, other: &Self) -> Option<std::cmp::Ordering> {
227        match (self, other) {
228            (Self::Integer(a), Self::Integer(b)) => a.partial_cmp(b),
229            (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
230            (Self::Boolean(a), Self::Boolean(b)) => a.partial_cmp(b),
231            _ => None,
232        }
233    }
234
235    // Generate the remaining comparison operations using the `generate_comparison_op` macro.
236    generate_comparison_op!(lt, >, std::cmp::Ordering::Less);
237    generate_comparison_op!(le, >=, std::cmp::Ordering::Less | std::cmp::Ordering::Equal);
238    generate_comparison_op!(gt, <, std::cmp::Ordering::Greater);
239    generate_comparison_op!(ge, <=, std::cmp::Ordering::Greater | std::cmp::Ordering::Equal);
240
241    // endregion: comparitive operations
242
243    // region: boolean operations
244
245    /// This method returns the logical negation of a [`Value::Boolean`]. It returns an
246    /// error for all other types.
247    pub fn not(&self) -> Result<Self, Error> {
248        match self {
249            Self::Boolean(b) => Ok(Self::Boolean(!b)),
250            _ => err!(
251                ErrorKind::TypeError,
252                "invalid operation 'not' for type {}",
253                self.variant_name(),
254            ),
255        }
256    }
257
258    /// This method returns the logical and of two [`Value::Boolean`]s. It returns an error
259    /// for all other types.
260    pub fn and(&self, other: &Self) -> Result<Self, Error> {
261        match (&self, &other) {
262            (Self::Boolean(a), Self::Boolean(b)) => Ok(Self::Boolean(*a && *b)),
263            _ => err!(
264                ErrorKind::TypeError,
265                "invalid operation 'and' between {} and {}",
266                self.variant_name(),
267                other.variant_name(),
268            ),
269        }
270    }
271
272    /// This method returns the logical or of two [`Value::Boolean`]s. It returns an error
273    /// for all other types.
274    pub fn or(&self, other: &Self) -> Result<Self, Error> {
275        match (&self, &other) {
276            (Self::Boolean(a), Self::Boolean(b)) => Ok(Self::Boolean(*a || *b)),
277            _ => err!(
278                ErrorKind::TypeError,
279                "invalid operation 'or' between {} and {}",
280                self.variant_name(),
281                other.variant_name(),
282            ),
283        }
284    }
285
286    // endregion: boolean operations
287
288    // region: arithmetic operations
289
290    // Generate the arithmetic operations for subtract, multiply and modulo (rem) using
291    // the `generate_binary_arithmetic_op` macro.
292    generate_binary_arithmetic_op!(sub, checked_sub, -);
293    generate_binary_arithmetic_op!(mul, checked_mul, *);
294    generate_binary_arithmetic_op!(rem, checked_rem, %);
295
296    // add is implemented separately because it also includes string concatenation.
297
298    /// This method returns the sum of two [`Value`]s. It returns an error if the operation
299    /// is not possible.
300    pub fn add(&self, other: &Self) -> Result<Self, Error> {
301        let overflow_error = Error::new(
302            "this arithmetic operation overflows",
303            ErrorKind::OverflowError,
304        );
305
306        match (&self, &other) {
307            (Self::Integer(a), Self::Integer(b)) => {
308                Ok(Self::Integer(a.checked_add(*b).ok_or(overflow_error)?))
309            }
310            (Self::Float(a), Self::Float(b)) => Ok(Self::Float(a + b)),
311            (Self::String(a), Self::String(b)) => Ok(Self::String(format!("{}{}", a, b))),
312            _ => err!(
313                ErrorKind::TypeError,
314                "invalid operation '+' between {} and {}",
315                self.variant_name(),
316                other.variant_name()
317            ),
318        }
319    }
320
321    // div is implemented separately because it also includes division by zero checks.
322
323    /// This method returns the result of dividing two [`Value`]s. It returns an error if
324    /// the operation is not possible, or if a division by zero occurs.
325    pub fn div(&self, other: &Self) -> Result<Self, Error> {
326        // Check for division by zero
327        #[allow(illegal_floating_point_literal_pattern)]
328        if matches!(&other, Self::Integer(0) | Self::Float(0.0)) {
329            return err!(ErrorKind::DivisionByZero, "cannot divide by zero");
330        }
331
332        let overflow_error = Error::new(
333            "this arithmetic operation overflows",
334            ErrorKind::OverflowError,
335        );
336
337        match (&self, &other) {
338            (Self::Integer(a), Self::Integer(b)) => {
339                Ok(Self::Integer(a.checked_div(*b).ok_or(overflow_error)?))
340            }
341            (Self::Float(a), Self::Float(b)) => Ok(Self::Float(a / b)),
342            _ => err!(
343                ErrorKind::TypeError,
344                "invalid operation '/' between {} and {}",
345                self.variant_name(),
346                other.variant_name()
347            ),
348        }
349    }
350
351    /// This method returns the negation of a [`Value`]. It returns an error if the
352    /// operation is not possible.
353    pub fn neg(&self) -> Result<Self, Error> {
354        match self {
355            Self::Integer(n) => Ok(Self::Integer(-n)),
356            Self::Float(n) => Ok(Self::Float(-n)),
357            _ => err!(
358                ErrorKind::TypeError,
359                "invalid operation '-' for type {}",
360                self.variant_name(),
361            ),
362        }
363    }
364
365    // endregion: arithmetic operations
366}
367
368/// Implementing the `Display` trait for [`Value`] allows for the pretty printing of
369/// values.
370impl fmt::Display for Value {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        match self {
373            Self::Integer(i) => write!(f, "{}", i),
374            Self::Float(fl) => write!(f, "{}", fl),
375            Self::Boolean(b) => write!(f, "{}", b),
376            Self::String(s) => write!(f, "\"{}\"", s),
377            Self::Function(_) => write!(f, "<function reference>"),
378            Self::Null => write!(f, "null"),
379        }
380    }
381}