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// FUTURE CLEANUP: this is the kernel's built-in REFERENCE list backend (the
184// bootstrap implementation). Once a default-loaded distribution lib can supply
185// a `ListBackend` before any user code runs, move `VecList` out of the kernel
186// and keep only the `ListValue`/`ListBackend` contracts here.
187#[derive(Clone)]
188pub struct VecList {
189    values: Arc<[Value]>,
190}
191
192impl VecList {
193    /// Builds a list from an owned vector of values.
194    pub fn from_vec(values: Vec<Value>) -> Self {
195        Self {
196            values: Arc::from(values),
197        }
198    }
199
200    /// Builds a list from an already-shared slice of values.
201    pub fn from_arc(values: Arc<[Value]>) -> Self {
202        Self { values }
203    }
204
205    /// Borrows the backing slice of values.
206    pub fn as_slice(&self) -> &[Value] {
207        &self.values
208    }
209}
210
211impl Object for VecList {
212    fn display(&self, _cx: &mut Cx) -> Result<String> {
213        Ok(format!("list[{}]", self.values.len()))
214    }
215
216    fn as_any(&self) -> &dyn std::any::Any {
217        self
218    }
219}
220
221impl crate::ObjectCompat for VecList {
222    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
223        let symbol = Symbol::qualified("core", "List");
224        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
225            return Ok(value.clone());
226        }
227        cx.factory().class_stub(CORE_LIST_CLASS_ID, symbol)
228    }
229    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
230        Ok(Expr::List(
231            self.values
232                .iter()
233                .map(|value| value.object().as_expr(cx))
234                .collect::<Result<Vec<_>>>()?,
235        ))
236    }
237    fn truth(&self, _cx: &mut Cx) -> Result<bool> {
238        Ok(!self.values.is_empty())
239    }
240    fn as_list(&self) -> Option<&dyn ListValue> {
241        Some(self)
242    }
243}
244
245impl ListValue for VecList {
246    fn is_empty(&self, _cx: &mut Cx) -> Result<bool> {
247        Ok(self.values.is_empty())
248    }
249
250    fn car(&self, _cx: &mut Cx) -> Result<Option<Value>> {
251        Ok(self.values.first().cloned())
252    }
253
254    fn cdr(&self, cx: &mut Cx) -> Result<Option<Value>> {
255        if self.values.is_empty() {
256            return Ok(None);
257        }
258
259        let tail = self.values[1..].to_vec();
260        Ok(Some(cx.factory().opaque(Arc::new(Self::from_vec(tail)))?))
261    }
262
263    fn len(&self, _cx: &mut Cx) -> Result<LengthResult> {
264        Ok(LengthResult::Known(self.values.len()))
265    }
266
267    fn len_cmp(&self, _cx: &mut Cx, n: usize) -> Result<Ordering> {
268        Ok(self.values.len().cmp(&n))
269    }
270
271    fn get(&self, _cx: &mut Cx, index: usize) -> Result<Option<Value>> {
272        Ok(self.values.get(index).cloned())
273    }
274
275    fn for_each(
276        &self,
277        _cx: &mut Cx,
278        limit: Option<usize>,
279        visit: &mut dyn FnMut(&Value),
280    ) -> Result<()> {
281        let stop = limit.unwrap_or(self.values.len()).min(self.values.len());
282        for value in &self.values[..stop] {
283            visit(value);
284        }
285        Ok(())
286    }
287
288    fn to_vec(&self, _cx: &mut Cx, limit: Option<usize>) -> Result<Vec<Value>> {
289        match limit {
290            Some(max) => Ok(self.values.iter().take(max).cloned().collect()),
291            None => Ok(self.values.to_vec()),
292        }
293    }
294}
295
296/// Factory protocol for constructing lists in a particular representation.
297pub trait ListBackend: Send + Sync {
298    /// Stable name the backend is registered and selected under.
299    fn name(&self) -> &str;
300
301    /// Builds a list from an ordered set of items.
302    fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value>;
303
304    /// Builds a cons cell prepending `car` onto the list `cdr`.
305    fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value>;
306}
307
308/// Registry of named list backends with one active default.
309pub struct ListRegistry {
310    backends: BTreeMap<String, Arc<dyn ListBackend>>,
311    active: String,
312}
313
314impl ListRegistry {
315    /// Builds a registry preloaded with the `vec` backend as active.
316    pub fn new() -> Self {
317        let mut registry = Self {
318            backends: BTreeMap::new(),
319            active: "vec".to_owned(),
320        };
321        registry.register(Arc::new(VecBackend));
322        registry
323    }
324
325    /// Registers a backend under its own name, replacing any prior one.
326    pub fn register(&mut self, backend: Arc<dyn ListBackend>) {
327        self.backends.insert(backend.name().to_owned(), backend);
328    }
329
330    /// Selects the active backend by name, erroring if it is unknown.
331    pub fn set_active(&mut self, name: &str) -> Result<()> {
332        if self.backends.contains_key(name) {
333            self.active = name.to_owned();
334            Ok(())
335        } else {
336            Err(Error::Eval(format!("unknown list backend: {name}")))
337        }
338    }
339
340    /// Name of the currently active backend.
341    pub fn active(&self) -> &str {
342        &self.active
343    }
344
345    /// Builds a list using the active backend.
346    pub fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
347        self.backend()?.new_list(cx, items)
348    }
349
350    /// Builds a cons cell using the active backend.
351    pub fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value> {
352        self.backend()?.new_cons(cx, car, cdr)
353    }
354
355    fn backend(&self) -> Result<&Arc<dyn ListBackend>> {
356        self.backends
357            .get(&self.active)
358            .ok_or_else(|| Error::Eval("active list backend missing".to_owned()))
359    }
360}
361
362impl Default for ListRegistry {
363    fn default() -> Self {
364        Self::new()
365    }
366}
367
368struct VecBackend;
369
370impl ListBackend for VecBackend {
371    fn name(&self) -> &str {
372        "vec"
373    }
374
375    fn new_list(&self, cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
376        cx.factory().opaque(Arc::new(VecList::from_vec(items)))
377    }
378
379    fn new_cons(&self, cx: &mut Cx, car: Value, cdr: Value) -> Result<Value> {
380        let Some(list) = cdr.object().as_list() else {
381            return Err(Error::TypeMismatch {
382                expected: "list",
383                found: "non-list",
384            });
385        };
386        let mut items = vec![car];
387        items.extend(list.to_vec(cx, None)?);
388        cx.factory().opaque(Arc::new(VecList::from_vec(items)))
389    }
390}
391
392#[cfg(test)]
393#[path = "list_tests.rs"]
394mod list_tests;