Skip to main content

sim_kernel/
list.rs

1//! The list contract: the pluggable [`ListValue`] backend protocol.
2//!
3//! The kernel defines the list-value protocol, force bounds, and a backend
4//! registry; concrete list representations are libs loaded against it. A
5//! `VecList` is provided as a baseline backend, not as kernel-fixed behavior.
6
7use std::{cmp::Ordering, collections::BTreeMap, sync::Arc};
8
9use crate::{
10    capability::list_force_unbounded_capability,
11    env::Cx,
12    error::{Error, Result},
13    expr::Expr,
14    id::{CORE_LIST_CLASS_ID, Symbol},
15    object::{ClassRef, Object},
16    value::{RuntimeObject, Value},
17};
18
19/// Default maximum number of elements an encoder or expr conversion will force
20/// from a lazy or endless list before erroring.
21pub const DEFAULT_FORCE_BOUND: usize = 1 << 20;
22
23/// Result of asking a list for its length.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum LengthResult {
26    /// The list is finite and its length is known.
27    Known(usize),
28    /// The list is (or may be) endless; an exact length is not available.
29    Unknown,
30}
31
32/// Shared behaviour for every list backend.
33pub trait ListValue: RuntimeObject {
34    /// Whether the list has no elements.
35    fn is_empty(&self, cx: &mut Cx) -> Result<bool>;
36
37    /// The first element, or `Ok(None)` when empty.
38    fn car(&self, cx: &mut Cx) -> Result<Option<Value>>;
39
40    /// The tail list after the first element, or `Ok(None)` when empty.
41    fn cdr(&self, cx: &mut Cx) -> Result<Option<Value>>;
42
43    /// The length, finite or [`LengthResult::Unknown`] for endless lists.
44    fn len(&self, cx: &mut Cx) -> Result<LengthResult>;
45
46    /// Compares the spine length against `n` without fully forcing the list.
47    fn len_cmp(&self, cx: &mut Cx, n: usize) -> Result<Ordering> {
48        spine_len_cmp_impl(cx, self, n)
49    }
50
51    /// The element at `index`, or `Ok(None)` when out of range.
52    fn get(&self, cx: &mut Cx, index: usize) -> Result<Option<Value>> {
53        let mut node = self.cdr_self(cx)?;
54        let mut i = index;
55        while let Some(list) = node.as_ref().and_then(|value| value.object().as_list()) {
56            if list.is_empty(cx)? {
57                return Ok(None);
58            }
59            if i == 0 {
60                return list.car(cx);
61            }
62            i -= 1;
63            node = list.cdr(cx)?;
64        }
65        Ok(None)
66    }
67
68    /// Visits up to `limit` elements in order, walking the list spine.
69    fn for_each(
70        &self,
71        cx: &mut Cx,
72        limit: Option<usize>,
73        visit: &mut dyn FnMut(&Value),
74    ) -> Result<()> {
75        if matches!(limit, Some(0)) {
76            return Ok(());
77        }
78
79        let mut count = 0usize;
80        let mut head = self.car(cx)?;
81        let mut tail = self.cdr(cx)?;
82        while let Some(value) = head {
83            if matches!(limit, Some(max) if count >= max) {
84                return Ok(());
85            }
86            visit(&value);
87            count += 1;
88            match tail.as_ref().and_then(|node| node.object().as_list()) {
89                Some(list) if !list.is_empty(cx)? => {
90                    head = list.car(cx)?;
91                    tail = list.cdr(cx)?;
92                }
93                _ => break,
94            }
95        }
96        Ok(())
97    }
98
99    /// Collects up to `limit` elements into a vector.
100    fn to_vec(&self, cx: &mut Cx, limit: Option<usize>) -> Result<Vec<Value>> {
101        let mut out = Vec::new();
102        self.for_each(cx, limit, &mut |value| out.push(value.clone()))?;
103        Ok(out)
104    }
105
106    /// The value to start spine walks from; the receiver itself by default.
107    fn cdr_self(&self, _cx: &mut Cx) -> Result<Option<Value>> {
108        Ok(self.as_self_value())
109    }
110
111    /// The receiver re-wrapped as a [`Value`], when it can produce one.
112    fn as_self_value(&self) -> Option<Value> {
113        None
114    }
115}
116
117/// Compares the spine length of `head` against `n` without full forcing.
118pub fn spine_len_cmp(cx: &mut Cx, head: &dyn ListValue, n: usize) -> Result<Ordering> {
119    spine_len_cmp_impl(cx, head, n)
120}
121
122/// The force bound for the current context, or `None` if unbounded forcing
123/// is permitted by capability.
124pub fn force_list_bound(cx: &mut Cx) -> Option<usize> {
125    if cx.require(&list_force_unbounded_capability()).is_ok() {
126        None
127    } else {
128        Some(DEFAULT_FORCE_BOUND)
129    }
130}
131
132/// Materializes a list to a vector, erroring if it exceeds the force bound.
133pub fn force_list_to_vec(cx: &mut Cx, list: &dyn ListValue, context: &str) -> Result<Vec<Value>> {
134    let bound = force_list_bound(cx);
135    if let Some(max) = bound
136        && list.len_cmp(cx, max)? == Ordering::Greater
137    {
138        return Err(Error::Eval(format!(
139            "{context}: list exceeds force bound {max}; lazy/endless list cannot be materialized in v1"
140        )));
141    }
142    list.to_vec(cx, bound)
143}
144
145fn spine_len_cmp_impl<T: ListValue + ?Sized>(cx: &mut Cx, head: &T, n: usize) -> Result<Ordering> {
146    if head.is_empty(cx)? {
147        return Ok(0usize.cmp(&n));
148    }
149
150    let mut count = 1usize;
151    if count > n {
152        return Ok(Ordering::Greater);
153    }
154
155    let mut node = head.cdr(cx)?;
156    while let Some(value) = node {
157        let Some(list) = value.object().as_list() else {
158            return Err(Error::Eval("list cdr did not yield a list".to_owned()));
159        };
160        if list.is_empty(cx)? {
161            break;
162        }
163        count += 1;
164        if count > n {
165            return Ok(Ordering::Greater);
166        }
167        node = list.cdr(cx)?;
168    }
169    Ok(count.cmp(&n))
170}
171
172/// Baseline eager list backend backed by a shared slice of values.
173///
174/// # Examples
175///
176/// ```
177/// use sim_kernel::VecList;
178///
179/// let empty = VecList::from_vec(Vec::new());
180/// assert!(empty.as_slice().is_empty());
181/// ```
182// sim-non-citizen(reason = "kernel list backing object; canonical form is native Expr::List data", kind = "private", descriptor = "")
183// VecList is the built-in bootstrap list backend. It implements the same
184// ListValue/ListBackend contracts as loadable list backends.
185#[derive(Clone)]
186pub struct VecList {
187    values: Arc<[Value]>,
188}
189
190impl VecList {
191    /// Builds a list from an owned vector of values.
192    pub fn from_vec(values: Vec<Value>) -> Self {
193        Self {
194            values: Arc::from(values),
195        }
196    }
197
198    /// Builds a list from an already-shared slice of values.
199    pub fn from_arc(values: Arc<[Value]>) -> Self {
200        Self { values }
201    }
202
203    /// Borrows the backing slice of values.
204    pub fn as_slice(&self) -> &[Value] {
205        &self.values
206    }
207}
208
209impl Object for VecList {
210    fn display(&self, _cx: &mut Cx) -> Result<String> {
211        Ok(format!("list[{}]", self.values.len()))
212    }
213
214    fn as_any(&self) -> &dyn std::any::Any {
215        self
216    }
217}
218
219impl crate::ObjectCompat for VecList {
220    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
221        let symbol = Symbol::qualified("core", "List");
222        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
223            return Ok(value.clone());
224        }
225        cx.factory().class_stub(CORE_LIST_CLASS_ID, symbol)
226    }
227    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
228        Ok(Expr::List(
229            self.values
230                .iter()
231                .map(|value| value.object().as_expr(cx))
232                .collect::<Result<Vec<_>>>()?,
233        ))
234    }
235    fn truth(&self, _cx: &mut Cx) -> Result<bool> {
236        Ok(!self.values.is_empty())
237    }
238    fn as_list(&self) -> Option<&dyn ListValue> {
239        Some(self)
240    }
241}
242
243impl ListValue for VecList {
244    fn is_empty(&self, _cx: &mut Cx) -> Result<bool> {
245        Ok(self.values.is_empty())
246    }
247
248    fn car(&self, _cx: &mut Cx) -> Result<Option<Value>> {
249        Ok(self.values.first().cloned())
250    }
251
252    fn cdr(&self, cx: &mut Cx) -> Result<Option<Value>> {
253        if self.values.is_empty() {
254            return Ok(None);
255        }
256
257        let tail = self.values[1..].to_vec();
258        Ok(Some(cx.factory().opaque(Arc::new(Self::from_vec(tail)))?))
259    }
260
261    fn len(&self, _cx: &mut Cx) -> Result<LengthResult> {
262        Ok(LengthResult::Known(self.values.len()))
263    }
264
265    fn len_cmp(&self, _cx: &mut Cx, n: usize) -> Result<Ordering> {
266        Ok(self.values.len().cmp(&n))
267    }
268
269    fn get(&self, _cx: &mut Cx, index: usize) -> Result<Option<Value>> {
270        Ok(self.values.get(index).cloned())
271    }
272
273    fn for_each(
274        &self,
275        _cx: &mut Cx,
276        limit: Option<usize>,
277        visit: &mut dyn FnMut(&Value),
278    ) -> Result<()> {
279        let stop = limit.unwrap_or(self.values.len()).min(self.values.len());
280        for value in &self.values[..stop] {
281            visit(value);
282        }
283        Ok(())
284    }
285
286    fn to_vec(&self, _cx: &mut Cx, limit: Option<usize>) -> Result<Vec<Value>> {
287        match limit {
288            Some(max) => Ok(self.values.iter().take(max).cloned().collect()),
289            None => Ok(self.values.to_vec()),
290        }
291    }
292}
293
294/// Factory protocol for constructing lists in a particular representation.
295pub trait ListBackend: Send + Sync {
296    /// Stable name the backend is registered and selected under.
297    fn name(&self) -> &str;
298
299    /// Builds a list from an ordered set of items.
300    fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value>;
301
302    /// Builds a cons cell prepending `car` onto the list `cdr`.
303    fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value>;
304}
305
306/// Registry of named list backends with one active default.
307pub struct ListRegistry {
308    backends: BTreeMap<String, Arc<dyn ListBackend>>,
309    active: String,
310}
311
312impl ListRegistry {
313    /// Builds a registry preloaded with the `vec` backend as active.
314    pub fn new() -> Self {
315        let mut registry = Self {
316            backends: BTreeMap::new(),
317            active: "vec".to_owned(),
318        };
319        registry.register(Arc::new(VecBackend));
320        registry
321    }
322
323    /// Registers a backend under its own name, replacing any prior one.
324    pub fn register(&mut self, backend: Arc<dyn ListBackend>) {
325        self.backends.insert(backend.name().to_owned(), backend);
326    }
327
328    /// Selects the active backend by name, erroring if it is unknown.
329    pub fn set_active(&mut self, name: &str) -> Result<()> {
330        if self.backends.contains_key(name) {
331            self.active = name.to_owned();
332            Ok(())
333        } else {
334            Err(Error::Eval(format!("unknown list backend: {name}")))
335        }
336    }
337
338    /// Name of the currently active backend.
339    pub fn active(&self) -> &str {
340        &self.active
341    }
342
343    /// Builds a list using the active backend.
344    pub fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
345        self.backend()?.new_list(cx, items)
346    }
347
348    /// Builds a cons cell using the active backend.
349    pub fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value> {
350        self.backend()?.new_cons(cx, car, cdr)
351    }
352
353    fn backend(&self) -> Result<&Arc<dyn ListBackend>> {
354        self.backends
355            .get(&self.active)
356            .ok_or_else(|| Error::Eval("active list backend missing".to_owned()))
357    }
358}
359
360impl Default for ListRegistry {
361    fn default() -> Self {
362        Self::new()
363    }
364}
365
366struct VecBackend;
367
368impl ListBackend for VecBackend {
369    fn name(&self) -> &str {
370        "vec"
371    }
372
373    fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
374        cx.factory().opaque(Arc::new(VecList::from_vec(items)))
375    }
376
377    fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value> {
378        let Some(list) = cdr.object().as_list() else {
379            return Err(Error::TypeMismatch {
380                expected: "list",
381                found: "non-list",
382            });
383        };
384        let mut items = vec![car];
385        items.extend(list.to_vec(cx, None)?);
386        cx.factory().opaque(Arc::new(VecList::from_vec(items)))
387    }
388}
389
390#[cfg(test)]
391#[path = "list_tests.rs"]
392mod list_tests;