uni_core/primitives/
pick.rs1use crate::interpreter::Interpreter;
4use crate::value::RuntimeError;
5
6pub fn pick_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 let stack_len = interp.stack.len();
22 let item = interp.stack[stack_len - n - 1].clone();
23
24 interp.stack.push(item);
26
27 Ok(())
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33 use crate::value::Value;
34
35 fn setup_interpreter() -> Interpreter {
36 Interpreter::new()
37 }
38
39 #[test]
40 fn test_pick_builtin_dup() {
41 let mut interp = setup_interpreter();
42
43 interp.push(Value::Number(1.0));
45 interp.push(Value::Number(2.0));
46 interp.push(Value::Number(3.0));
47 interp.push(Value::Number(0.0));
48 pick_builtin(&mut interp).unwrap();
49
50 let top = interp.pop().unwrap();
52 assert!(matches!(top, Value::Number(n) if n == 3.0));
53
54 let second = interp.pop().unwrap();
55 assert!(matches!(second, Value::Number(n) if n == 3.0));
56
57 let third = interp.pop().unwrap();
58 assert!(matches!(third, Value::Number(n) if n == 2.0));
59
60 let fourth = interp.pop().unwrap();
61 assert!(matches!(fourth, Value::Number(n) if n == 1.0));
62 }
63
64 #[test]
65 fn test_pick_builtin_over() {
66 let mut interp = setup_interpreter();
67
68 interp.push(Value::Number(1.0));
70 interp.push(Value::Number(2.0));
71 interp.push(Value::Number(3.0));
72 interp.push(Value::Number(1.0));
73 pick_builtin(&mut interp).unwrap();
74
75 let top = interp.pop().unwrap();
77 assert!(matches!(top, Value::Number(n) if n == 2.0));
78
79 let second = interp.pop().unwrap();
80 assert!(matches!(second, Value::Number(n) if n == 3.0));
81
82 let third = interp.pop().unwrap();
83 assert!(matches!(third, Value::Number(n) if n == 2.0));
84
85 let fourth = interp.pop().unwrap();
86 assert!(matches!(fourth, Value::Number(n) if n == 1.0));
87 }
88
89 #[test]
90 fn test_pick_builtin_deep() {
91 let mut interp = setup_interpreter();
92
93 interp.push(Value::Number(1.0));
95 interp.push(Value::Number(2.0));
96 interp.push(Value::Number(3.0));
97 interp.push(Value::Number(4.0));
98 interp.push(Value::Number(5.0));
99 interp.push(Value::Number(3.0));
100 pick_builtin(&mut interp).unwrap();
101
102 let top = interp.pop().unwrap();
103 assert!(matches!(top, Value::Number(n) if n == 2.0));
104
105 let second = interp.pop().unwrap();
106 assert!(matches!(second, Value::Number(n) if n == 5.0));
107
108 let third = interp.pop().unwrap();
110 assert!(matches!(third, Value::Number(n) if n == 4.0));
111
112 let fourth = interp.pop().unwrap();
113 assert!(matches!(fourth, Value::Number(n) if n == 3.0));
114
115 let fifth = interp.pop().unwrap();
116 assert!(matches!(fifth, Value::Number(n) if n == 2.0));
117
118 let sixth = interp.pop().unwrap();
119 assert!(matches!(sixth, Value::Number(n) if n == 1.0));
120 }
121
122 #[test]
123 fn test_pick_builtin_mixed_types() {
124 let mut interp = setup_interpreter();
125
126 interp.push(Value::String("hello".into()));
128 interp.push(Value::Boolean(true));
129 interp.push(Value::Number(42.0));
130 interp.push(Value::Number(2.0)); pick_builtin(&mut interp).unwrap();
132
133 let top = interp.pop().unwrap();
135 assert!(matches!(top, Value::String(s) if s.as_ref() == "hello"));
136
137 let second = interp.pop().unwrap();
138 assert!(matches!(second, Value::Number(n) if n == 42.0));
139
140 let third = interp.pop().unwrap();
141 assert!(matches!(third, Value::Boolean(true)));
142
143 let fourth = interp.pop().unwrap();
145 assert!(matches!(fourth, Value::String(s) if s.as_ref() == "hello"));
146 }
147
148 #[test]
149 fn test_pick_builtin_single_element() {
150 let mut interp = setup_interpreter();
151
152 interp.push(Value::Number(42.0));
154 interp.push(Value::Number(0.0));
155 pick_builtin(&mut interp).unwrap();
156
157 let top = interp.pop().unwrap();
158 assert!(matches!(top, Value::Number(n) if n == 42.0));
159
160 let second = interp.pop().unwrap();
161 assert!(matches!(second, Value::Number(n) if n == 42.0));
162 }
163
164 #[test]
165 fn test_pick_builtin_stack_underflow() {
166 let mut interp = setup_interpreter();
167
168 let result = pick_builtin(&mut interp);
170 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
171
172 interp.push(Value::Number(1.0));
174 interp.push(Value::Number(1.0)); let result = pick_builtin(&mut interp);
176 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
177 }
178
179 #[test]
180 fn test_pick_builtin_type_error() {
181 let mut interp = setup_interpreter();
182
183 interp.push(Value::Number(1.0));
185 interp.push(Value::Number(2.0));
186 interp.push(Value::String("not a number".into()));
187 let result = pick_builtin(&mut interp);
188 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
189 }
190
191 #[test]
192 fn test_pick_builtin_preserves_original_stack() {
193 let mut interp = setup_interpreter();
194
195 interp.push(Value::Number(10.0));
197 interp.push(Value::Number(20.0));
198 interp.push(Value::Number(30.0));
199 interp.push(Value::Number(1.0)); pick_builtin(&mut interp).unwrap();
201
202 let copied = interp.pop().unwrap();
204 assert!(matches!(copied, Value::Number(n) if n == 20.0));
205
206 let original_top = interp.pop().unwrap();
208 assert!(matches!(original_top, Value::Number(n) if n == 30.0));
209
210 let original_second = interp.pop().unwrap();
211 assert!(matches!(original_second, Value::Number(n) if n == 20.0));
212
213 let original_third = interp.pop().unwrap();
214 assert!(matches!(original_third, Value::Number(n) if n == 10.0));
215 }
216}