jetcrab\vm\executor\instruction_handlers/
arithmetic.rs

1//! # Arithmetic Handler
2//!
3//! Handles all arithmetic operations in the VM including basic math operations,
4//! increment/decrement operations, and power operations.
5//!
6//! ## Operations Supported
7//!
8//! - **Basic Arithmetic**: add, subtract, multiply, divide, modulo
9//! - **Power Operations**: power (exponentiation)
10//! - **Increment/Decrement**: increment, decrement
11//! - **Unary Operations**: negate
12//!
13//! ## Error Handling
14//!
15//! All operations return `Result<(), ExecutionError>` and handle:
16//! - Stack underflow (insufficient operands)
17//! - Division by zero
18//! - Invalid numeric operations
19//!
20//! ## Usage
21//!
22//! ```rust
23//! use jetcrab::vm::executor::instruction_handlers::ArithmeticHandler;
24//! use jetcrab::vm::executor::traits::StackOperations;
25//!
26//! let mut stack = MyStack::new();
27//! stack.push(Value::Number(5.0));
28//! stack.push(Value::Number(3.0));
29//! ArithmeticHandler::add(&mut stack)?;
30//! // Stack now contains: [8.0]
31//! ```
32
33use crate::vm::executor::error_handler::ExecutionError;
34use crate::vm::executor::traits::StackOperations;
35use crate::vm::value::Value;
36
37/// Handles arithmetic operations for the VM
38pub struct ArithmeticHandler;
39
40impl ArithmeticHandler {
41    /// Adds the top two values on the stack
42    ///
43    /// Pops two values from the stack, adds them, and pushes the result.
44    /// Supports numeric addition and string concatenation.
45    ///
46    /// # Arguments
47    /// * `stack` - The stack to operate on
48    ///
49    /// # Returns
50    /// * `Ok(())` on success
51    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
52    ///
53    /// # Examples
54    ///
55    /// ```rust
56    /// let mut stack = MyStack::new();
57    /// stack.push(Value::Number(3.0));
58    /// stack.push(Value::Number(5.0));
59    /// ArithmeticHandler::add(&mut stack)?;
60    /// assert_eq!(stack.pop(), Some(Value::Number(8.0)));
61    /// ```
62    pub fn add<S>(stack: &mut S) -> Result<(), ExecutionError>
63    where
64        S: StackOperations,
65    {
66        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
67        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
68
69        let result = match (a, b) {
70            (Value::Number(a), Value::Number(b)) => Value::Number(a + b),
71            (Value::String(a), Value::String(b)) => Value::String(a + &b),
72            (Value::String(a), Value::Number(b)) => Value::String(a + &b.to_string()),
73            (Value::Number(a), Value::String(b)) => Value::String(a.to_string() + &b),
74            _ => return Err(ExecutionError::TypeError("Cannot add non-numeric values".to_string())),
75        };
76
77        stack.push(result);
78        Ok(())
79    }
80
81    /// Subtracts the second value from the first value on the stack
82    ///
83    /// Pops two values from the stack, subtracts the second from the first,
84    /// and pushes the result.
85    ///
86    /// # Arguments
87    /// * `stack` - The stack to operate on
88    ///
89    /// # Returns
90    /// * `Ok(())` on success
91    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
92    pub fn subtract<S>(stack: &mut S) -> Result<(), ExecutionError>
93    where
94        S: StackOperations,
95    {
96        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
97        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
98
99        let result = match (a, b) {
100            (Value::Number(a), Value::Number(b)) => Value::Number(a - b),
101            _ => return Err(ExecutionError::TypeError("Cannot subtract non-numeric values".to_string())),
102        };
103
104        stack.push(result);
105        Ok(())
106    }
107
108    /// Multiplies the top two values on the stack
109    ///
110    /// Pops two values from the stack, multiplies them, and pushes the result.
111    ///
112    /// # Arguments
113    /// * `stack` - The stack to operate on
114    ///
115    /// # Returns
116    /// * `Ok(())` on success
117    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
118    pub fn multiply<S>(stack: &mut S) -> Result<(), ExecutionError>
119    where
120        S: StackOperations,
121    {
122        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
123        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
124
125        let result = match (a, b) {
126            (Value::Number(a), Value::Number(b)) => Value::Number(a * b),
127            _ => return Err(ExecutionError::TypeError("Cannot multiply non-numeric values".to_string())),
128        };
129
130        stack.push(result);
131        Ok(())
132    }
133
134    /// Divides the first value by the second value on the stack
135    ///
136    /// Pops two values from the stack, divides the first by the second,
137    /// and pushes the result. Handles division by zero.
138    ///
139    /// # Arguments
140    /// * `stack` - The stack to operate on
141    ///
142    /// # Returns
143    /// * `Ok(())` on success
144    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
145    /// * `Err(ExecutionError::RuntimeError)` on division by zero
146    pub fn divide<S>(stack: &mut S) -> Result<(), ExecutionError>
147    where
148        S: StackOperations,
149    {
150        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
151        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
152
153        let result = match (a, b) {
154            (Value::Number(a), Value::Number(b)) => {
155                if b == 0.0 {
156                    return Err(ExecutionError::RuntimeError("Division by zero".to_string()));
157                }
158                Value::Number(a / b)
159            }
160            _ => return Err(ExecutionError::TypeError("Cannot divide non-numeric values".to_string())),
161        };
162
163        stack.push(result);
164        Ok(())
165    }
166
167    /// Computes the remainder of division
168    ///
169    /// Pops two values from the stack, computes a % b, and pushes the result.
170    ///
171    /// # Arguments
172    /// * `stack` - The stack to operate on
173    ///
174    /// # Returns
175    /// * `Ok(())` on success
176    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
177    /// * `Err(ExecutionError::RuntimeError)` on division by zero
178    pub fn modulo<S>(stack: &mut S) -> Result<(), ExecutionError>
179    where
180        S: StackOperations,
181    {
182        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
183        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
184
185        let result = match (a, b) {
186            (Value::Number(a), Value::Number(b)) => {
187                if b == 0.0 {
188                    return Err(ExecutionError::RuntimeError("Modulo by zero".to_string()));
189                }
190                Value::Number(a % b)
191            }
192            _ => return Err(ExecutionError::TypeError("Cannot compute modulo of non-numeric values".to_string())),
193        };
194
195        stack.push(result);
196        Ok(())
197    }
198
199    /// Computes the power of the first value raised to the second value
200    ///
201    /// Pops two values from the stack, computes a^b, and pushes the result.
202    ///
203    /// # Arguments
204    /// * `stack` - The stack to operate on
205    ///
206    /// # Returns
207    /// * `Ok(())` on success
208    /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
209    pub fn power<S>(stack: &mut S) -> Result<(), ExecutionError>
210    where
211        S: StackOperations,
212    {
213        let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
214        let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
215
216        let result = match (a, b) {
217            (Value::Number(a), Value::Number(b)) => Value::Number(a.powf(b)),
218            _ => return Err(ExecutionError::TypeError("Cannot compute power of non-numeric values".to_string())),
219        };
220
221        stack.push(result);
222        Ok(())
223    }
224
225    /// Negates the top value on the stack
226    ///
227    /// Pops one value from the stack, negates it, and pushes the result.
228    ///
229    /// # Arguments
230    /// * `stack` - The stack to operate on
231    ///
232    /// # Returns
233    /// * `Ok(())` on success
234    /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
235    pub fn negate<S>(stack: &mut S) -> Result<(), ExecutionError>
236    where
237        S: StackOperations,
238    {
239        let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
240
241        let result = match value {
242            Value::Number(n) => Value::Number(-n),
243            _ => return Err(ExecutionError::TypeError("Cannot negate non-numeric value".to_string())),
244        };
245
246        stack.push(result);
247        Ok(())
248    }
249
250    /// Increments the top value on the stack by 1
251    ///
252    /// Pops one value from the stack, adds 1, and pushes the result.
253    ///
254    /// # Arguments
255    /// * `stack` - The stack to operate on
256    ///
257    /// # Returns
258    /// * `Ok(())` on success
259    /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
260    pub fn increment<S>(stack: &mut S) -> Result<(), ExecutionError>
261    where
262        S: StackOperations,
263    {
264        let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
265
266        let result = match value {
267            Value::Number(n) => Value::Number(n + 1.0),
268            _ => return Err(ExecutionError::TypeError("Cannot increment non-numeric value".to_string())),
269        };
270
271        stack.push(result);
272        Ok(())
273    }
274
275    /// Decrements the top value on the stack by 1
276    ///
277    /// Pops one value from the stack, subtracts 1, and pushes the result.
278    ///
279    /// # Arguments
280    /// * `stack` - The stack to operate on
281    ///
282    /// # Returns
283    /// * `Ok(())` on success
284    /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
285    pub fn decrement<S>(stack: &mut S) -> Result<(), ExecutionError>
286    where
287        S: StackOperations,
288    {
289        let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
290
291        let result = match value {
292            Value::Number(n) => Value::Number(n - 1.0),
293            _ => return Err(ExecutionError::TypeError("Cannot decrement non-numeric value".to_string())),
294        };
295
296        stack.push(result);
297        Ok(())
298    }
299}