Skip to main content

aver/types/
list.rs

1/// List namespace — list manipulation helpers.
2///
3/// Methods:
4///   List.len(list)           → Int                    — number of elements
5///   List.get(list, index)    → Option<T>              — element at index
6///   List.push(list, val)     → List<T>                — append element (returns new list)
7///   List.head(list)          → Option<T>              — first element
8///   List.tail(list)          → Option<List<T>>        — all but first
9///   List.contains(list, val) → Bool                   — membership by `==`
10///   List.zip(a, b)           → List<(A, B)>           — pair elements from two lists
11///
12/// Note: List.map, List.filter, List.fold, List.find, List.any, List.flatMap are handled
13/// directly in the interpreter because they need to invoke closures via
14/// `self.call_value`.
15///
16/// No effects required.
17use std::collections::HashMap;
18
19use crate::value::{
20    RuntimeError, Value, list_from_vec, list_head, list_len, list_slice, list_tail_view,
21    list_to_vec,
22};
23
24pub fn register(global: &mut HashMap<String, Value>) {
25    let mut members = HashMap::new();
26    for method in &[
27        "len", "map", "filter", "fold", "get", "push", "head", "tail", "find", "any", "zip",
28        "flatMap", "contains",
29    ] {
30        members.insert(
31            method.to_string(),
32            Value::Builtin(format!("List.{}", method)),
33        );
34    }
35    global.insert(
36        "List".to_string(),
37        Value::Namespace {
38            name: "List".to_string(),
39            members,
40        },
41    );
42}
43
44pub fn effects(_name: &str) -> &'static [&'static str] {
45    &[]
46}
47
48/// Returns `Some(result)` when `name` is owned by this namespace, `None` otherwise.
49/// Note: List.map, List.filter, List.fold are NOT handled here (they need interpreter access).
50pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
51    match name {
52        "List.len" => Some(len(&args)),
53        "List.get" => Some(get(&args)),
54        "List.push" => Some(push(&args)),
55        "List.head" => Some(head(&args)),
56        "List.tail" => Some(tail(&args)),
57        "List.contains" => Some(contains(&args)),
58        "List.zip" => Some(zip(&args)),
59        _ => None,
60    }
61}
62
63// ─── Implementations ────────────────────────────────────────────────────────
64
65fn len(args: &[Value]) -> Result<Value, RuntimeError> {
66    if args.len() != 1 {
67        return Err(RuntimeError::Error(format!(
68            "List.len() takes 1 argument, got {}",
69            args.len()
70        )));
71    }
72    list_len(&args[0])
73        .map(|n| Value::Int(n as i64))
74        .ok_or_else(|| RuntimeError::Error("List.len() argument must be a List".to_string()))
75}
76
77fn get(args: &[Value]) -> Result<Value, RuntimeError> {
78    if args.len() != 2 {
79        return Err(RuntimeError::Error(format!(
80            "List.get() takes 2 arguments (list, index), got {}",
81            args.len()
82        )));
83    }
84    let list = list_slice(&args[0]).ok_or_else(|| {
85        RuntimeError::Error("List.get() first argument must be a List".to_string())
86    })?;
87    let index = match &args[1] {
88        Value::Int(i) => *i,
89        _ => {
90            return Err(RuntimeError::Error(
91                "List.get() index must be an Int".to_string(),
92            ));
93        }
94    };
95    if index < 0 || index as usize >= list.len() {
96        Ok(Value::None)
97    } else {
98        Ok(Value::Some(Box::new(list[index as usize].clone())))
99    }
100}
101
102fn push(args: &[Value]) -> Result<Value, RuntimeError> {
103    if args.len() != 2 {
104        return Err(RuntimeError::Error(format!(
105            "List.push() takes 2 arguments (list, val), got {}",
106            args.len()
107        )));
108    }
109    let mut list = list_to_vec(&args[0]).ok_or_else(|| {
110        RuntimeError::Error("List.push() first argument must be a List".to_string())
111    })?;
112    list.push(args[1].clone());
113    Ok(list_from_vec(list))
114}
115
116fn head(args: &[Value]) -> Result<Value, RuntimeError> {
117    if args.len() != 1 {
118        return Err(RuntimeError::Error(format!(
119            "List.head() takes 1 argument, got {}",
120            args.len()
121        )));
122    }
123    match list_head(&args[0]) {
124        Some(v) => Ok(Value::Some(Box::new(v))),
125        None if list_len(&args[0]).is_some() => Ok(Value::None),
126        None => Err(RuntimeError::Error(
127            "List.head() argument must be a List".to_string(),
128        )),
129    }
130}
131
132fn tail(args: &[Value]) -> Result<Value, RuntimeError> {
133    if args.len() != 1 {
134        return Err(RuntimeError::Error(format!(
135            "List.tail() takes 1 argument, got {}",
136            args.len()
137        )));
138    }
139    match list_tail_view(&args[0]) {
140        Some(v) => Ok(Value::Some(Box::new(v))),
141        None if list_len(&args[0]).is_some() => Ok(Value::None),
142        None => Err(RuntimeError::Error(
143            "List.tail() argument must be a List".to_string(),
144        )),
145    }
146}
147
148fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
149    if args.len() != 2 {
150        return Err(RuntimeError::Error(format!(
151            "List.contains() takes 2 arguments (list, value), got {}",
152            args.len()
153        )));
154    }
155    let list = list_slice(&args[0]).ok_or_else(|| {
156        RuntimeError::Error("List.contains() first argument must be a List".to_string())
157    })?;
158    let target = &args[1];
159    Ok(Value::Bool(list.iter().any(|item| item == target)))
160}
161
162fn zip(args: &[Value]) -> Result<Value, RuntimeError> {
163    if args.len() != 2 {
164        return Err(RuntimeError::Error(format!(
165            "List.zip() takes 2 arguments (list, list), got {}",
166            args.len()
167        )));
168    }
169    let a = list_slice(&args[0]).ok_or_else(|| {
170        RuntimeError::Error("List.zip() first argument must be a List".to_string())
171    })?;
172    let b = list_slice(&args[1]).ok_or_else(|| {
173        RuntimeError::Error("List.zip() second argument must be a List".to_string())
174    })?;
175    let pairs: Vec<Value> = a
176        .iter()
177        .zip(b.iter())
178        .map(|(x, y)| Value::Tuple(vec![x.clone(), y.clone()]))
179        .collect();
180    Ok(list_from_vec(pairs))
181}