uni_core/primitives/
tail.rs1use crate::compat::ToString;
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7pub fn tail_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
13 let list = interp.pop()?;
14
15 match list {
16 Value::Pair(_, cdr) => {
17 interp.push((*cdr).clone());
19 Ok(())
20 }
21 Value::Nil => {
22 interp.push(Value::Nil);
24 Ok(())
25 }
26 _ => {
27 Err(RuntimeError::TypeError("tail expects a list".to_string()))
29 }
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::value::Value;
37
38 fn setup_interpreter() -> Interpreter {
39 Interpreter::new()
40 }
41
42 #[test]
43 fn test_tail_builtin_basic() {
44 let mut interp = setup_interpreter();
45
46 let list = interp.make_list(vec![
48 Value::Number(1.0),
49 Value::Number(2.0),
50 Value::Number(3.0),
51 ]);
52 interp.push(list);
53 tail_builtin(&mut interp).unwrap();
54
55 let result = interp.pop().unwrap();
56
57 match result {
59 Value::Pair(car, cdr) => {
60 assert!(matches!(car.as_ref(), Value::Number(n) if *n == 2.0));
61 match cdr.as_ref() {
62 Value::Pair(car2, cdr2) => {
63 assert!(matches!(car2.as_ref(), Value::Number(n) if *n == 3.0));
64 assert!(matches!(cdr2.as_ref(), Value::Nil));
65 }
66 _ => panic!("Expected second element in tail"),
67 }
68 }
69 _ => panic!("Expected list structure for tail result"),
70 }
71 }
72
73 #[test]
74 fn test_tail_builtin_single_element() {
75 let mut interp = setup_interpreter();
76
77 let single = interp.make_list(vec![Value::Number(42.0)]);
79 interp.push(single);
80 tail_builtin(&mut interp).unwrap();
81
82 let result = interp.pop().unwrap();
83 assert!(matches!(result, Value::Nil));
84 }
85
86 #[test]
87 fn test_tail_builtin_empty_list() {
88 let mut interp = setup_interpreter();
89
90 interp.push(Value::Nil);
92 tail_builtin(&mut interp).unwrap();
93
94 let result = interp.pop().unwrap();
95 assert!(matches!(result, Value::Nil));
96 }
97
98 #[test]
99 fn test_tail_builtin_mixed_types() {
100 let mut interp = setup_interpreter();
101
102 let mixed_list = interp.make_list(vec![
104 Value::String("hello".into()),
105 Value::Number(42.0),
106 Value::Boolean(true),
107 ]);
108 interp.push(mixed_list);
109 tail_builtin(&mut interp).unwrap();
110
111 let result = interp.pop().unwrap();
112
113 match result {
115 Value::Pair(car, cdr) => {
116 assert!(matches!(car.as_ref(), Value::Number(n) if *n == 42.0));
117 match cdr.as_ref() {
118 Value::Pair(car2, cdr2) => {
119 assert!(matches!(car2.as_ref(), Value::Boolean(true)));
120 assert!(matches!(cdr2.as_ref(), Value::Nil));
121 }
122 _ => panic!("Expected second element in tail"),
123 }
124 }
125 _ => panic!("Expected list structure for tail result"),
126 }
127 }
128
129 #[test]
130 fn test_tail_builtin_nested_lists() {
131 let mut interp = setup_interpreter();
132
133 let inner_list = interp.make_list(vec![Value::Number(2.0), Value::Number(3.0)]);
135 let outer_list = interp.make_list(vec![Value::Number(1.0), inner_list, Value::Number(4.0)]);
136
137 interp.push(outer_list);
138 tail_builtin(&mut interp).unwrap();
139
140 let result = interp.pop().unwrap();
141
142 match result {
144 Value::Pair(car, cdr) => {
145 assert!(matches!(car.as_ref(), Value::Pair(_, _)));
147 match cdr.as_ref() {
148 Value::Pair(car2, cdr2) => {
149 assert!(matches!(car2.as_ref(), Value::Number(n) if *n == 4.0));
150 assert!(matches!(cdr2.as_ref(), Value::Nil));
151 }
152 _ => panic!("Expected second element in tail"),
153 }
154 }
155 _ => panic!("Expected list structure for tail result"),
156 }
157 }
158
159 #[test]
160 fn test_tail_builtin_improper_list() {
161 let mut interp = setup_interpreter();
162
163 use crate::compat::Rc;
165 let improper = Value::Pair(Rc::new(Value::Number(1.0)), Rc::new(Value::Number(2.0)));
166
167 interp.push(improper);
168 tail_builtin(&mut interp).unwrap();
169
170 let result = interp.pop().unwrap();
171 assert!(matches!(result, Value::Number(n) if n == 2.0));
172 }
173
174 #[test]
175 fn test_tail_builtin_non_list_error() {
176 let mut interp = setup_interpreter();
177
178 interp.push(Value::Number(42.0));
180 let result = tail_builtin(&mut interp);
181 assert!(
182 matches!(result, Err(RuntimeError::TypeError(msg)) if msg.contains("tail expects a list"))
183 );
184
185 let atom = interp.intern_atom("test");
187 interp.push(Value::Atom(atom));
188 let result = tail_builtin(&mut interp);
189 assert!(
190 matches!(result, Err(RuntimeError::TypeError(msg)) if msg.contains("tail expects a list"))
191 );
192
193 interp.push(Value::String("hello".into()));
195 let result = tail_builtin(&mut interp);
196 assert!(
197 matches!(result, Err(RuntimeError::TypeError(msg)) if msg.contains("tail expects a list"))
198 );
199 }
200
201 #[test]
202 fn test_tail_builtin_stack_underflow() {
203 let mut interp = setup_interpreter();
204
205 let result = tail_builtin(&mut interp);
207 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
208 }
209
210 #[test]
211 fn test_tail_builtin_preserves_structure() {
212 let mut interp = setup_interpreter();
213
214 let original_list = interp.make_list(vec![
216 Value::Number(1.0),
217 Value::Number(2.0),
218 Value::Number(3.0),
219 ]);
220
221 interp.push(original_list);
223 tail_builtin(&mut interp).unwrap();
224 let tail_result = interp.pop().unwrap();
225
226 match tail_result {
228 Value::Pair(car, cdr) => {
229 assert!(matches!(car.as_ref(), Value::Number(n) if *n == 2.0));
230 match cdr.as_ref() {
231 Value::Pair(car2, cdr2) => {
232 assert!(matches!(car2.as_ref(), Value::Number(n) if *n == 3.0));
233 assert!(matches!(cdr2.as_ref(), Value::Nil));
234 }
235 _ => panic!("Tail structure incorrect"),
236 }
237 }
238 _ => panic!("Expected proper tail structure"),
239 }
240 }
241}