uni_core/primitives/
vector.rs

1// RUST CONCEPT: Vector (array) primitives inspired by Scheme vectors
2// Provides dense, indexed collections alongside existing list structures
3use crate::compat::{format, Rc, ToString, Vec};
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7#[cfg(not(target_os = "none"))]
8use std::cell::RefCell;
9#[cfg(target_os = "none")]
10use core::cell::RefCell;
11
12#[cfg(target_os = "none")]
13use num_traits::Float;
14
15fn expect_array(value: Value, op_name: &str) -> Result<Rc<RefCell<Vec<Value>>>, RuntimeError> {
16    match value {
17        Value::Array(array) => Ok(array),
18        _ => Err(RuntimeError::TypeError(format!(
19            "{} expects an array",
20            op_name
21        ))),
22    }
23}
24
25fn expect_index(index: f64, op_name: &str) -> Result<usize, RuntimeError> {
26    if index < 0.0 || index.fract() != 0.0 {
27        return Err(RuntimeError::TypeError(format!(
28            "{} index must be a non-negative integer",
29            op_name
30        )));
31    }
32    Ok(index as usize)
33}
34
35fn collect_list_elements(list: Value) -> Result<Vec<Value>, RuntimeError> {
36    let mut elements = Vec::new();
37    let mut current = list;
38
39    loop {
40        match current {
41            Value::Pair(car, cdr) => {
42                elements.push((*car).clone());
43                current = (*cdr).clone();
44            }
45            Value::Nil => break,
46            _ => {
47                return Err(RuntimeError::TypeError(
48                    "list->vector expects a proper list".to_string(),
49                ));
50            }
51        }
52    }
53
54    Ok(elements)
55}
56
57// Stack effect: ( elementN ... element1 count -- vector )
58// Collects top count items (in insertion order) into a new vector
59pub fn vector_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
60    let count_value = interp.pop_number()?;
61    if count_value < 0.0 || count_value.fract() != 0.0 {
62        return Err(RuntimeError::TypeError(
63            "vector count must be a non-negative integer".to_string(),
64        ));
65    }
66
67    let count = count_value as usize;
68    let mut elements = Vec::with_capacity(count);
69    for _ in 0..count {
70        elements.push(interp.pop()?);
71    }
72    elements.reverse();
73
74    interp.push(interp.make_array(elements));
75    Ok(())
76}
77
78// Stack effect: ( count fill -- vector )
79// Creates vector with count copies of fill value
80pub fn make_vector_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
81    let fill_value = interp.pop()?;
82    let count_value = interp.pop_number()?;
83    if count_value < 0.0 || count_value.fract() != 0.0 {
84        return Err(RuntimeError::TypeError(
85            "make-vector count must be a non-negative integer".to_string(),
86        ));
87    }
88    let count = count_value as usize;
89
90    let mut elements = Vec::with_capacity(count);
91    for _ in 0..count {
92        elements.push(fill_value.clone());
93    }
94
95    interp.push(interp.make_array(elements));
96    Ok(())
97}
98
99// Stack effect: ( vector -- length )
100pub fn vector_length_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
101    let vector_value = interp.pop()?;
102    let array = expect_array(vector_value, "vector-length")?;
103    let len = array.borrow().len();
104    interp.push(Value::Number(len as f64));
105    Ok(())
106}
107
108// Stack effect: ( vector index -- element )
109pub fn vector_ref_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
110    let index_value = interp.pop_number()?;
111    let index = expect_index(index_value, "vector-ref")?;
112    let vector_value = interp.pop()?;
113    let array = expect_array(vector_value, "vector-ref")?;
114
115    let elements = array.borrow();
116    let element = elements.get(index).cloned().ok_or_else(|| {
117        RuntimeError::TypeError(format!(
118            "vector-ref index {} out of bounds for length {}",
119            index,
120            elements.len()
121        ))
122    })?;
123
124    interp.push(element);
125    Ok(())
126}
127
128// Stack effect: ( value vector index -- )
129// Following Forth convention where value comes first
130pub fn vector_set_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
131    let index_value = interp.pop_number()?;
132    let index = expect_index(index_value, "vector-set!")?;
133    let vector_value = interp.pop()?;
134    let array = expect_array(vector_value, "vector-set!")?;
135    let new_value = interp.pop()?;
136
137    let mut elements = array.borrow_mut();
138    if index >= elements.len() {
139        return Err(RuntimeError::TypeError(format!(
140            "vector-set! index {} out of bounds for length {}",
141            index,
142            elements.len()
143        )));
144    }
145    elements[index] = new_value;
146    Ok(())
147}
148
149// Stack effect: ( vector -- list )
150pub fn vector_to_list_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
151    let vector_value = interp.pop()?;
152    let array = expect_array(vector_value, "vector->list")?;
153    let elements = array.borrow();
154    let list_elements: Vec<Value> = elements.iter().cloned().collect();
155    interp.push(interp.make_list(list_elements));
156    Ok(())
157}
158
159// Stack effect: ( list -- vector )
160pub fn list_to_vector_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
161    let list_value = interp.pop()?;
162    let elements = collect_list_elements(list_value)?;
163    interp.push(interp.make_array(elements));
164    Ok(())
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    fn setup_interpreter() -> Interpreter {
172        Interpreter::new()
173    }
174
175    fn unwrap_array(value: Value) -> Rc<RefCell<Vec<Value>>> {
176        match value {
177            Value::Array(array) => array,
178            _ => panic!("Expected array"),
179        }
180    }
181
182    #[test]
183    fn test_vector_builtin_basic() {
184        let mut interp = setup_interpreter();
185
186        interp.push(Value::Number(1.0));
187        interp.push(Value::Number(2.0));
188        interp.push(Value::Number(3.0));
189        interp.push(Value::Number(3.0));
190        vector_builtin(&mut interp).unwrap();
191
192        let array_rc = unwrap_array(interp.pop().unwrap());
193        let array = array_rc.borrow();
194        assert_eq!(array.len(), 3);
195        assert!(matches!(array[0], Value::Number(n) if n == 1.0));
196        assert!(matches!(array[1], Value::Number(n) if n == 2.0));
197        assert!(matches!(array[2], Value::Number(n) if n == 3.0));
198    }
199
200    #[test]
201    fn test_make_vector_builtin() {
202        let mut interp = setup_interpreter();
203
204        interp.push(Value::Number(4.0));
205        interp.push(Value::String("fill".into()));
206        make_vector_builtin(&mut interp).unwrap();
207
208        let array_rc = unwrap_array(interp.pop().unwrap());
209        let array = array_rc.borrow();
210        assert_eq!(array.len(), 4);
211        for element in array.iter() {
212            assert!(matches!(element, Value::String(s) if s.as_ref() == "fill"));
213        }
214    }
215
216    #[test]
217    fn test_vector_length_builtin() {
218        let mut interp = setup_interpreter();
219
220        interp.push(Value::Number(5.0));
221        interp.push(Value::Number(6.0));
222        interp.push(Value::Number(2.0));
223        vector_builtin(&mut interp).unwrap();
224        vector_length_builtin(&mut interp).unwrap();
225
226        let len_value = interp.pop().unwrap();
227        assert!(matches!(len_value, Value::Number(n) if n == 2.0));
228    }
229
230    #[test]
231    fn test_vector_ref_builtin() {
232        let mut interp = setup_interpreter();
233
234        interp.push(Value::Number(7.0));
235        interp.push(Value::Number(8.0));
236        interp.push(Value::Number(2.0));
237        vector_builtin(&mut interp).unwrap();
238
239        let vector_value = interp.pop().unwrap();
240        interp.push(vector_value.clone());
241        interp.push(Value::Number(1.0));
242        vector_ref_builtin(&mut interp).unwrap();
243
244        let result = interp.pop().unwrap();
245        assert!(matches!(result, Value::Number(n) if n == 8.0));
246    }
247
248    #[test]
249    fn test_vector_set_builtin() {
250        let mut interp = setup_interpreter();
251
252        interp.push(Value::Number(1.0));
253        interp.push(Value::Number(1.0));
254        vector_builtin(&mut interp).unwrap();
255
256        let vector_value = interp.pop().unwrap();
257        interp.push(Value::Number(42.0));
258        interp.push(vector_value.clone());
259        interp.push(Value::Number(0.0));
260        vector_set_builtin(&mut interp).unwrap();
261
262        let array_rc = unwrap_array(vector_value);
263        let array = array_rc.borrow();
264        assert!(matches!(array[0], Value::Number(n) if n == 42.0));
265    }
266
267    #[test]
268    fn test_vector_to_list_builtin() {
269        let mut interp = setup_interpreter();
270
271        interp.push(Value::Number(1.0));
272        interp.push(Value::Number(2.0));
273        interp.push(Value::Number(2.0));
274        vector_builtin(&mut interp).unwrap();
275
276        let vector_value = interp.pop().unwrap();
277        interp.push(vector_value);
278        vector_to_list_builtin(&mut interp).unwrap();
279
280        let list_value = interp.pop().unwrap();
281        match list_value {
282            Value::Pair(car, cdr) => {
283                assert!(matches!(car.as_ref(), Value::Number(n) if *n == 1.0));
284                match cdr.as_ref() {
285                    Value::Pair(car2, cdr2) => {
286                        assert!(matches!(car2.as_ref(), Value::Number(n) if *n == 2.0));
287                        assert!(matches!(cdr2.as_ref(), Value::Nil));
288                    }
289                    _ => panic!("Expected second element in list"),
290                }
291            }
292            _ => panic!("Expected list"),
293        }
294    }
295
296    #[test]
297    fn test_list_to_vector_builtin() {
298        let mut interp = setup_interpreter();
299
300        let list = interp.make_list(vec![Value::Number(5.0), Value::Boolean(true)]);
301        interp.push(list);
302        list_to_vector_builtin(&mut interp).unwrap();
303
304        let array_rc = unwrap_array(interp.pop().unwrap());
305        let array = array_rc.borrow();
306        assert_eq!(array.len(), 2);
307        assert!(matches!(array[0], Value::Number(n) if n == 5.0));
308        assert!(matches!(array[1], Value::Boolean(true)));
309    }
310
311    #[test]
312    fn test_list_to_vector_requires_proper_list() {
313        let mut interp = setup_interpreter();
314
315        interp.push(Value::Number(10.0));
316        let result = list_to_vector_builtin(&mut interp);
317        assert!(matches!(
318            result,
319            Err(RuntimeError::TypeError(msg)) if msg.contains("proper list")
320        ));
321    }
322}