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}