1use crate::compat::ToString;
2use crate::interpreter::Interpreter;
3use crate::value::{RuntimeError, Value};
4
5pub fn shl_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
6 let shift_amount = interp.pop_number()?;
7 let value = interp.pop_number()?;
8
9 let value_int = value as i64;
11 let shift_int = shift_amount as u32;
12
13 if shift_int > 63 {
15 return Err(RuntimeError::DomainError(
16 "shift amount too large".to_string(),
17 ));
18 }
19
20 let result = value_int << shift_int;
21 interp.push(Value::Number(result as f64));
22 Ok(())
23}
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28 use crate::value::Value;
29
30 fn setup_interpreter() -> Interpreter {
31 Interpreter::new()
32 }
33
34 #[test]
35 fn test_shl_basic() {
36 let mut interp = setup_interpreter();
37 interp.push(Value::Number(1.0));
38 interp.push(Value::Number(3.0)); shl_builtin(&mut interp).unwrap();
41
42 let result = interp.pop().unwrap();
43 assert!(matches!(result, Value::Number(n) if n == 8.0)); }
45
46 #[test]
47 fn test_shl_zero_shift() {
48 let mut interp = setup_interpreter();
49 interp.push(Value::Number(42.0));
50 interp.push(Value::Number(0.0)); shl_builtin(&mut interp).unwrap();
53
54 let result = interp.pop().unwrap();
55 assert!(matches!(result, Value::Number(n) if n == 42.0)); }
57
58 #[test]
59 fn test_shl_powers_of_two() {
60 let mut interp = setup_interpreter();
61
62 let test_cases = [
63 (1.0, 1.0, 2.0), (1.0, 2.0, 4.0), (1.0, 4.0, 16.0), (1.0, 8.0, 256.0), ];
68
69 for (value, shift, expected) in test_cases {
70 interp.push(Value::Number(value));
71 interp.push(Value::Number(shift));
72 shl_builtin(&mut interp).unwrap();
73 let result = interp.pop().unwrap();
74 assert!(
75 matches!(result, Value::Number(n) if n == expected),
76 "{} << {} should be {}, got {:?}",
77 value,
78 shift,
79 expected,
80 result
81 );
82 }
83 }
84
85 #[test]
86 fn test_shl_multiple_bits() {
87 let mut interp = setup_interpreter();
88
89 let test_cases = [
90 (3.0, 2.0, 12.0), (5.0, 1.0, 10.0), (7.0, 3.0, 56.0), ];
94
95 for (value, shift, expected) in test_cases {
96 interp.push(Value::Number(value));
97 interp.push(Value::Number(shift));
98 shl_builtin(&mut interp).unwrap();
99 let result = interp.pop().unwrap();
100 assert!(
101 matches!(result, Value::Number(n) if n == expected),
102 "{} << {} should be {}, got {:?}",
103 value,
104 shift,
105 expected,
106 result
107 );
108 }
109 }
110
111 #[test]
112 fn test_shl_zero_value() {
113 let mut interp = setup_interpreter();
114 interp.push(Value::Number(0.0));
115 interp.push(Value::Number(5.0)); shl_builtin(&mut interp).unwrap();
118
119 let result = interp.pop().unwrap();
120 assert!(matches!(result, Value::Number(n) if n == 0.0)); }
122
123 #[test]
124 fn test_shl_negative_numbers() {
125 let mut interp = setup_interpreter();
126
127 let test_cases = [
128 (-1.0, 1.0, -2.0), (-2.0, 2.0, -8.0), (-4.0, 1.0, -8.0), ];
132
133 for (value, shift, expected) in test_cases {
134 interp.push(Value::Number(value));
135 interp.push(Value::Number(shift));
136 shl_builtin(&mut interp).unwrap();
137 let result = interp.pop().unwrap();
138 assert!(
139 matches!(result, Value::Number(n) if n == expected),
140 "{} << {} should be {}, got {:?}",
141 value,
142 shift,
143 expected,
144 result
145 );
146 }
147 }
148
149 #[test]
150 fn test_shl_large_shifts() {
151 let mut interp = setup_interpreter();
152
153 let test_cases = [
154 (1.0, 10.0, 1024.0), (1.0, 20.0, 1048576.0), ];
157
158 for (value, shift, expected) in test_cases {
159 interp.push(Value::Number(value));
160 interp.push(Value::Number(shift));
161 shl_builtin(&mut interp).unwrap();
162 let result = interp.pop().unwrap();
163 assert!(
164 matches!(result, Value::Number(n) if n == expected),
165 "{} << {} should be {}, got {:?}",
166 value,
167 shift,
168 expected,
169 result
170 );
171 }
172 }
173
174 #[test]
175 fn test_shl_fractional_truncation() {
176 let mut interp = setup_interpreter();
177
178 interp.push(Value::Number(3.7));
180 interp.push(Value::Number(2.9)); shl_builtin(&mut interp).unwrap();
183
184 let result = interp.pop().unwrap();
185 assert!(matches!(result, Value::Number(n) if n == 12.0)); }
187
188 #[test]
189 fn test_shl_multiplication_equivalence() {
190 let mut interp = setup_interpreter();
191
192 let test_cases = [
194 (5.0, 1.0), (7.0, 2.0), (3.0, 4.0), ];
198
199 for (value, shift) in test_cases {
200 interp.push(Value::Number(value));
201 interp.push(Value::Number(shift));
202 shl_builtin(&mut interp).unwrap();
203 let shift_result = interp.pop().unwrap();
204
205 let expected = value * (2.0_f64.powf(shift));
206
207 assert!(
208 matches!(shift_result, Value::Number(n) if n == expected),
209 "{} << {} should equal {} * 2^{} = {}, got {:?}",
210 value,
211 shift,
212 value,
213 shift,
214 expected,
215 shift_result
216 );
217 }
218 }
219
220 #[test]
221 fn test_shl_excessive_shift_error() {
222 let mut interp = setup_interpreter();
223 interp.push(Value::Number(1.0));
224 interp.push(Value::Number(100.0)); let result = shl_builtin(&mut interp);
227 assert!(matches!(result, Err(RuntimeError::DomainError(_))));
228 }
229
230 #[test]
231 fn test_shl_boundary_shift() {
232 let mut interp = setup_interpreter();
233 interp.push(Value::Number(1.0));
234 interp.push(Value::Number(63.0)); let result = shl_builtin(&mut interp);
237 assert!(result.is_ok());
239 }
240
241 #[test]
242 fn test_shl_bit_pattern_preservation() {
243 let mut interp = setup_interpreter();
244
245 interp.push(Value::Number(170.0)); interp.push(Value::Number(1.0)); shl_builtin(&mut interp).unwrap();
250
251 let result = interp.pop().unwrap();
252 assert!(matches!(result, Value::Number(n) if n == 340.0)); }
254
255 #[test]
256 fn test_shl_stack_underflow() {
257 let mut interp = setup_interpreter();
258
259 let result = shl_builtin(&mut interp);
260 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
261
262 interp.push(Value::Number(5.0));
263 let result = shl_builtin(&mut interp);
264 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
265 }
266
267 #[test]
268 fn test_shl_type_error() {
269 let mut interp = setup_interpreter();
270 interp.push(Value::String("hello".into()));
271 interp.push(Value::Number(2.0));
272
273 let result = shl_builtin(&mut interp);
274 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
275 }
276}