Skip to main content

aver/types/
vector.rs

1/// Vector namespace — fixed-size indexed sequence helpers.
2///
3/// Methods:
4///   Vector.new(size, default)     → Vector<T>
5///   Vector.get(vec, idx)          → Option<T>
6///   Vector.set(vec, idx, val)     → Option<Vector<T>>
7///   Vector.len(vec)               → Int
8///   Vector.fromList(xs)           → Vector<T>
9///   List.fromVector(vec)            → List<T>
10///
11/// No effects required.
12use std::collections::HashMap;
13use std::sync::Arc as Rc;
14
15use aver_rt::AverVector;
16
17use crate::nan_value::{Arena, NanIntExt, NanValue};
18use crate::value::{RuntimeError, Value, list_from_vec, list_view};
19
20pub fn register(global: &mut HashMap<String, Value>) {
21    let mut members = HashMap::new();
22    for method in &["new", "get", "set", "len", "fromList"] {
23        members.insert(
24            method.to_string(),
25            Value::Builtin(format!("Vector.{}", method)),
26        );
27    }
28    global.insert(
29        "Vector".to_string(),
30        Value::Namespace {
31            name: "Vector".to_string(),
32            members,
33        },
34    );
35}
36
37pub fn effects(_name: &str) -> &'static [&'static str] {
38    &[]
39}
40
41pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
42    match name {
43        "Vector.new" => Some(vec_new(args)),
44        "Vector.get" => Some(vec_get(args)),
45        "Vector.set" => Some(vec_set(args)),
46        "Vector.len" => Some(vec_len(args)),
47        "Vector.fromList" => Some(vec_from_list(args)),
48        "List.fromVector" => Some(vec_to_list(args)),
49        _ => None,
50    }
51}
52
53fn vec_new(args: &[Value]) -> Result<Value, RuntimeError> {
54    if args.len() != 2 {
55        return Err(RuntimeError::Error(format!(
56            "Vector.new() takes 2 arguments (size, default), got {}",
57            args.len()
58        )));
59    }
60    let Value::Int(size) = &args[0] else {
61        return Err(RuntimeError::Error(
62            "Vector.new: size must be an Int".to_string(),
63        ));
64    };
65    let Some(size) = size.to_usize() else {
66        // Negative, or larger than a machine-sized integer can address: a
67        // vector of that size cannot be allocated.
68        return Err(RuntimeError::Error(
69            "Vector.new: size must be a non-negative, machine-sized Int".to_string(),
70        ));
71    };
72    Ok(Value::Vector(AverVector::new(size, args[1].clone())))
73}
74
75fn vec_get(args: &[Value]) -> Result<Value, RuntimeError> {
76    if args.len() != 2 {
77        return Err(RuntimeError::Error(format!(
78            "Vector.get() takes 2 arguments, got {}",
79            args.len()
80        )));
81    }
82    let Value::Vector(vec) = &args[0] else {
83        return Err(RuntimeError::Error(
84            "Vector.get: first argument must be a Vector".to_string(),
85        ));
86    };
87    let Value::Int(idx) = &args[1] else {
88        return Err(RuntimeError::Error(
89            "Vector.get: index must be an Int".to_string(),
90        ));
91    };
92    let Some(idx) = idx.to_usize() else {
93        return Ok(Value::None);
94    };
95    Ok(match vec.get(idx) {
96        Some(v) => Value::Some(Box::new(v.clone())),
97        None => Value::None,
98    })
99}
100
101fn vec_set(args: &[Value]) -> Result<Value, RuntimeError> {
102    if args.len() != 3 {
103        return Err(RuntimeError::Error(format!(
104            "Vector.set() takes 3 arguments, got {}",
105            args.len()
106        )));
107    }
108    let Value::Vector(vec) = &args[0] else {
109        return Err(RuntimeError::Error(
110            "Vector.set: first argument must be a Vector".to_string(),
111        ));
112    };
113    let Value::Int(idx) = &args[1] else {
114        return Err(RuntimeError::Error(
115            "Vector.set: index must be an Int".to_string(),
116        ));
117    };
118    let Some(idx) = idx.to_usize() else {
119        return Ok(Value::None);
120    };
121    Ok(match vec.set(idx, args[2].clone()) {
122        Some(new_vec) => Value::Some(Box::new(Value::Vector(new_vec))),
123        None => Value::None,
124    })
125}
126
127fn vec_len(args: &[Value]) -> Result<Value, RuntimeError> {
128    if args.len() != 1 {
129        return Err(RuntimeError::Error(format!(
130            "Vector.len() takes 1 argument, got {}",
131            args.len()
132        )));
133    }
134    let Value::Vector(vec) = &args[0] else {
135        return Err(RuntimeError::Error(
136            "Vector.len: argument must be a Vector".to_string(),
137        ));
138    };
139    Ok(Value::int(vec.len() as i64))
140}
141
142fn vec_from_list(args: &[Value]) -> Result<Value, RuntimeError> {
143    if args.len() != 1 {
144        return Err(RuntimeError::Error(format!(
145            "Vector.fromList() takes 1 argument, got {}",
146            args.len()
147        )));
148    }
149    let items = list_view(&args[0]).ok_or_else(|| {
150        RuntimeError::Error("Vector.fromList: argument must be a List".to_string())
151    })?;
152    Ok(Value::Vector(AverVector::from_list(items)))
153}
154
155fn vec_to_list(args: &[Value]) -> Result<Value, RuntimeError> {
156    if args.len() != 1 {
157        return Err(RuntimeError::Error(format!(
158            "List.fromVector() takes 1 argument, got {}",
159            args.len()
160        )));
161    }
162    let Value::Vector(vec) = &args[0] else {
163        return Err(RuntimeError::Error(
164            "List.fromVector: argument must be a Vector".to_string(),
165        ));
166    };
167    Ok(list_from_vec(vec.to_vec()))
168}
169
170// ─── NanValue-native API ─────────────────────────────────────────────────────
171
172pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
173    let methods = &["new", "get", "set", "len", "fromList"];
174    let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
175    for method in methods {
176        let idx = arena.push_builtin(&format!("Vector.{}", method));
177        members.push((Rc::from(*method), NanValue::new_builtin(idx)));
178    }
179    let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
180        name: Rc::from("Vector"),
181        members,
182    });
183    global.insert("Vector".to_string(), NanValue::new_namespace(ns_idx));
184}
185
186pub fn call_nv(
187    name: &str,
188    args: &[NanValue],
189    arena: &mut Arena,
190) -> Option<Result<NanValue, RuntimeError>> {
191    match name {
192        "Vector.new" => Some(vec_new_nv(args, arena)),
193        "Vector.get" => Some(vec_get_nv(args, arena)),
194        "Vector.set" => Some(vec_set_nv(args, arena)),
195        "Vector.len" => Some(vec_len_nv(args, arena)),
196        "Vector.fromList" => Some(vec_from_list_nv(args, arena)),
197        "List.fromVector" => Some(vec_to_list_nv(args, arena)),
198        _ => None,
199    }
200}
201
202fn vec_new_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
203    if args.len() != 2 {
204        return Err(RuntimeError::Error(format!(
205            "Vector.new() takes 2 arguments (size, default), got {}",
206            args.len()
207        )));
208    }
209    if !args[0].is_int() {
210        return Err(RuntimeError::Error(
211            "Vector.new: size must be an Int".to_string(),
212        ));
213    }
214    let Some(size) = args[0].as_aver_int(arena).to_usize() else {
215        // Negative, or larger than `usize` can address: cannot allocate.
216        return Err(RuntimeError::Error(
217            "Vector.new: size must be a non-negative, machine-sized Int".to_string(),
218        ));
219    };
220    let items = vec![args[1]; size];
221    if items.is_empty() {
222        Ok(NanValue::EMPTY_VECTOR)
223    } else {
224        Ok(NanValue::new_vector(arena.push_vector(items)))
225    }
226}
227
228fn vec_get_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
229    if args.len() != 2 {
230        return Err(RuntimeError::Error(format!(
231            "Vector.get() takes 2 arguments, got {}",
232            args.len()
233        )));
234    }
235    if !args[0].is_vector() {
236        return Err(RuntimeError::Error(
237            "Vector.get: first argument must be a Vector".to_string(),
238        ));
239    }
240    if !args[1].is_int() {
241        return Err(RuntimeError::Error(
242            "Vector.get: index must be an Int".to_string(),
243        ));
244    }
245    let Some(idx) = args[1].as_aver_int(arena).to_usize() else {
246        return Ok(NanValue::NONE);
247    };
248    let items = arena.vector_ref_value(args[0]);
249    match items.get(idx) {
250        Some(&v) => Ok(NanValue::new_some_value(v, arena)),
251        None => Ok(NanValue::NONE),
252    }
253}
254
255/// Vector.set with sole-owned first argument — takes instead of cloning.
256pub fn vec_set_nv_owned(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
257    if args.len() != 3 {
258        return Err(RuntimeError::Error(format!(
259            "Vector.set() takes 3 arguments, got {}",
260            args.len()
261        )));
262    }
263    if !args[0].is_vector() {
264        return Err(RuntimeError::Error(
265            "Vector.set: first argument must be a Vector".to_string(),
266        ));
267    }
268    if !args[1].is_int() {
269        return Err(RuntimeError::Error(
270            "Vector.set: index must be an Int".to_string(),
271        ));
272    }
273    let Some(uidx) = args[1].as_aver_int(arena).to_usize() else {
274        return Ok(NanValue::NONE);
275    };
276    let source = args[0];
277    let mut items = arena.take_vector_value(source);
278    if uidx >= items.len() {
279        return Ok(NanValue::NONE);
280    }
281    items[uidx] = args[2];
282    let new_vec_idx =
283        arena.push_inheriting_source_space(aver_memory::ArenaEntry::Vector(items), source);
284    Ok(NanValue::new_some_value(
285        NanValue::new_vector(new_vec_idx),
286        arena,
287    ))
288}
289
290fn vec_set_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
291    if args.len() != 3 {
292        return Err(RuntimeError::Error(format!(
293            "Vector.set() takes 3 arguments, got {}",
294            args.len()
295        )));
296    }
297    if !args[0].is_vector() {
298        return Err(RuntimeError::Error(
299            "Vector.set: first argument must be a Vector".to_string(),
300        ));
301    }
302    if !args[1].is_int() {
303        return Err(RuntimeError::Error(
304            "Vector.set: index must be an Int".to_string(),
305        ));
306    }
307    let Some(uidx) = args[1].as_aver_int(arena).to_usize() else {
308        return Ok(NanValue::NONE);
309    };
310    let mut items = arena.clone_vector_value(args[0]);
311    if uidx >= items.len() {
312        return Ok(NanValue::NONE);
313    }
314    items[uidx] = args[2];
315    let new_vec_idx = arena.push_vector(items);
316    Ok(NanValue::new_some_value(
317        NanValue::new_vector(new_vec_idx),
318        arena,
319    ))
320}
321
322fn vec_len_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
323    if args.len() != 1 {
324        return Err(RuntimeError::Error(format!(
325            "Vector.len() takes 1 argument, got {}",
326            args.len()
327        )));
328    }
329    if !args[0].is_vector() {
330        return Err(RuntimeError::Error(
331            "Vector.len: argument must be a Vector".to_string(),
332        ));
333    }
334    let items = arena.vector_ref_value(args[0]);
335    Ok(NanValue::new_int(items.len() as i64, arena))
336}
337
338fn vec_from_list_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
339    if args.len() != 1 {
340        return Err(RuntimeError::Error(format!(
341            "Vector.fromList() takes 1 argument, got {}",
342            args.len()
343        )));
344    }
345    if !args[0].is_list() {
346        return Err(RuntimeError::Error(
347            "Vector.fromList: argument must be a List".to_string(),
348        ));
349    }
350    let items = arena.list_to_vec_value(args[0]);
351    if items.is_empty() {
352        Ok(NanValue::EMPTY_VECTOR)
353    } else {
354        Ok(NanValue::new_vector(arena.push_vector(items)))
355    }
356}
357
358fn vec_to_list_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
359    if args.len() != 1 {
360        return Err(RuntimeError::Error(format!(
361            "List.fromVector() takes 1 argument, got {}",
362            args.len()
363        )));
364    }
365    if !args[0].is_vector() {
366        return Err(RuntimeError::Error(
367            "List.fromVector: argument must be a Vector".to_string(),
368        ));
369    }
370    let items = arena.clone_vector_value(args[0]);
371    if items.is_empty() {
372        Ok(NanValue::EMPTY_LIST)
373    } else {
374        let list_idx = arena.push_list(items);
375        Ok(NanValue::new_list(list_idx))
376    }
377}