uni_core/primitives/
roll.rs1use crate::interpreter::Interpreter;
4use crate::value::RuntimeError;
5
6pub fn roll_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
11 let n = interp.pop_integer()?;
12
13 if interp.stack.len() < n + 1 {
16 return Err(RuntimeError::StackUnderflow);
17 }
18
19 if n == 0 {
20 return Ok(());
22 }
23
24 let stack_len = interp.stack.len();
27 let item = interp.stack.remove(stack_len - n - 1);
28
29 interp.stack.push(item);
31
32 Ok(())
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::value::Value;
39
40 fn setup_interpreter() -> Interpreter {
41 Interpreter::new()
42 }
43
44 #[test]
45 fn test_roll_builtin_basic() {
46 let mut interp = setup_interpreter();
47
48 interp.push(Value::Number(1.0));
50 interp.push(Value::Number(2.0));
51 interp.push(Value::Number(3.0));
52 interp.push(Value::Number(4.0));
53 interp.push(Value::Number(2.0));
54 roll_builtin(&mut interp).unwrap();
55
56 let top = interp.pop().unwrap();
58 assert!(matches!(top, Value::Number(n) if n == 2.0));
59
60 let second = interp.pop().unwrap();
61 assert!(matches!(second, Value::Number(n) if n == 4.0));
62
63 let third = interp.pop().unwrap();
64 assert!(matches!(third, Value::Number(n) if n == 3.0));
65
66 let fourth = interp.pop().unwrap();
67 assert!(matches!(fourth, Value::Number(n) if n == 1.0));
68 }
69
70 #[test]
71 fn test_roll_builtin_no_op() {
72 let mut interp = setup_interpreter();
73
74 interp.push(Value::Number(1.0));
76 interp.push(Value::Number(2.0));
77 interp.push(Value::Number(3.0));
78 interp.push(Value::Number(4.0));
79 interp.push(Value::Number(0.0));
80 roll_builtin(&mut interp).unwrap();
81
82 let top = interp.pop().unwrap();
84 assert!(matches!(top, Value::Number(n) if n == 4.0));
85
86 let second = interp.pop().unwrap();
87 assert!(matches!(second, Value::Number(n) if n == 3.0));
88
89 let third = interp.pop().unwrap();
90 assert!(matches!(third, Value::Number(n) if n == 2.0));
91
92 let fourth = interp.pop().unwrap();
93 assert!(matches!(fourth, Value::Number(n) if n == 1.0));
94 }
95
96 #[test]
97 fn test_roll_builtin_swap() {
98 let mut interp = setup_interpreter();
99
100 interp.push(Value::Number(1.0));
102 interp.push(Value::Number(2.0));
103 interp.push(Value::Number(1.0));
104 roll_builtin(&mut interp).unwrap();
105
106 let top = interp.pop().unwrap();
107 assert!(matches!(top, Value::Number(n) if n == 1.0));
108
109 let second = interp.pop().unwrap();
110 assert!(matches!(second, Value::Number(n) if n == 2.0));
111 }
112
113 #[test]
114 fn test_roll_builtin_mixed_types() {
115 let mut interp = setup_interpreter();
116
117 interp.push(Value::String("hello".into()));
119 interp.push(Value::Boolean(true));
120 interp.push(Value::Number(42.0));
121 interp.push(Value::Number(2.0)); roll_builtin(&mut interp).unwrap();
123
124 let top = interp.pop().unwrap();
126 assert!(matches!(top, Value::String(s) if s.as_ref() == "hello"));
127
128 let second = interp.pop().unwrap();
129 assert!(matches!(second, Value::Number(n) if n == 42.0));
130
131 let third = interp.pop().unwrap();
132 assert!(matches!(third, Value::Boolean(true)));
133 }
134
135 #[test]
136 fn test_roll_builtin_stack_underflow() {
137 let mut interp = setup_interpreter();
138
139 let result = roll_builtin(&mut interp);
141 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
142
143 interp.push(Value::Number(1.0));
145 interp.push(Value::Number(2.0)); let result = roll_builtin(&mut interp);
147 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
148 }
149
150 #[test]
151 fn test_roll_builtin_type_error() {
152 let mut interp = setup_interpreter();
153
154 interp.push(Value::Number(1.0));
156 interp.push(Value::Number(2.0));
157 interp.push(Value::String("not a number".into()));
158 let result = roll_builtin(&mut interp);
159 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
160 }
161}