Skip to main content

scheme_rs/
lists.rs

1//! Scheme pairs and lists.
2
3use hashbrown::HashSet;
4use indexmap::IndexMap;
5use parking_lot::RwLock;
6
7use crate::{
8    exceptions::Exception,
9    gc::{Gc, GcInner, Trace},
10    proc::{Application, DynamicState, Procedure},
11    registry::{bridge, cps_bridge},
12    runtime::{Runtime, RuntimeInner},
13    strings::WideString,
14    value::{UnpackedValue, Value, ValueType, write_value},
15    vectors::Vector,
16};
17use std::fmt;
18
19#[derive(Trace)]
20#[repr(align(16))]
21pub(crate) struct PairInner {
22    /// The head of the pair
23    car: RwLock<Value>,
24    /// The tail of the pair
25    cdr: RwLock<Value>,
26    /// Whether or not the pair can be modified post creation
27    mutable: bool,
28}
29
30/// A pair of Scheme [Values](Value). Has a head (the [car](Pair::car())) and a
31/// tail (the [cdr](Pair::cdr())).
32#[derive(Clone, Trace)]
33pub struct Pair(pub(crate) Gc<PairInner>);
34
35impl Pair {
36    /// Construct a new Pair from a car and cdr
37    pub fn new(car: Value, cdr: Value, mutable: bool) -> Self {
38        Self(Gc::new(PairInner {
39            car: RwLock::new(car),
40            cdr: RwLock::new(cdr),
41            mutable,
42        }))
43    }
44
45    /// Extract the car (aka the head) from the Pair.
46    pub fn car(&self) -> Value {
47        self.0.car.read().clone()
48    }
49
50    /// Alias for [`car`](Pair::car())
51    pub fn head(&self) -> Value {
52        self.car()
53    }
54
55    /// Extract the cdr (aka the tail) from the Pair.
56    pub fn cdr(&self) -> Value {
57        self.0.cdr.read().clone()
58    }
59
60    /// Alias for [`cdr`](Pair::cdr())
61    pub fn tail(&self) -> Value {
62        self.cdr()
63    }
64
65    /// Set the car of the Pair. Returns an error if pair is immutable.
66    pub fn set_car(&self, new_car: Value) -> Result<(), Exception> {
67        if self.0.mutable {
68            *self.0.car.write() = new_car;
69            Ok(())
70        } else {
71            Err(Exception::error("pair is not mutable"))
72        }
73    }
74
75    /// Set the cdr of the Pair. Returns an error if pair is immutable.
76    pub fn set_cdr(&self, new_cdr: Value) -> Result<(), Exception> {
77        if self.0.mutable {
78            *self.0.cdr.write() = new_cdr;
79            Ok(())
80        } else {
81            Err(Exception::error("pair is not mutable"))
82        }
83    }
84}
85
86impl From<Pair> for (Value, Value) {
87    fn from(value: Pair) -> Self {
88        (value.car(), value.cdr())
89    }
90}
91
92pub(crate) fn write_list(
93    car: &Value,
94    cdr: &Value,
95    fmt: fn(&Value, &mut IndexMap<Value, bool>, &mut fmt::Formatter<'_>) -> fmt::Result,
96    circular_values: &mut IndexMap<Value, bool>,
97    f: &mut fmt::Formatter<'_>,
98) -> fmt::Result {
99    match cdr.type_of() {
100        ValueType::Pair | ValueType::Null => (),
101        _ => {
102            // This is not a proper list
103            write!(f, "(")?;
104            write_value(car, fmt, circular_values, f)?;
105            write!(f, " . ")?;
106            write_value(cdr, fmt, circular_values, f)?;
107            write!(f, ")")?;
108            return Ok(());
109        }
110    }
111
112    write!(f, "(")?;
113    write_value(car, fmt, circular_values, f)?;
114    let mut stack = vec![cdr.clone()];
115
116    while let Some(head) = stack.pop() {
117        if let Some((idx, _, seen)) = circular_values.get_full_mut(&head) {
118            if *seen {
119                write!(f, " . #{idx}#")?;
120                continue;
121            } else {
122                write!(f, " #{idx}=")?;
123                *seen = true;
124            }
125        }
126        match &*head.unpacked_ref() {
127            UnpackedValue::Null => {
128                if !stack.is_empty() {
129                    write!(f, " ()")?;
130                }
131            }
132            UnpackedValue::Pair(pair) => {
133                let (car, cdr) = pair.clone().into();
134                write!(f, " ")?;
135                write_value(&car, fmt, circular_values, f)?;
136                stack.push(cdr);
137            }
138            x => {
139                let val = x.clone().into_value();
140                write!(f, " ")?;
141                if stack.is_empty() {
142                    write!(f, ". ")?;
143                }
144                write_value(&val, fmt, circular_values, f)?;
145            }
146        }
147    }
148
149    write!(f, ")")
150}
151
152/// A proper list.
153///
154/// Conversion to this type guarantees that a type is a proper list and allows
155/// for fast retrieval of the length or any individual element of the list.
156///
157/// # Performance
158///
159/// This is done by copying the list into a `Vec`, which can be a quite
160/// expensive operation, so only use this if you need all elements of the list.
161pub struct List {
162    head: Value,
163    items: Vec<Value>,
164}
165
166impl List {
167    pub fn as_slice(&self) -> &[Value] {
168        self.items.as_slice()
169    }
170
171    pub fn into_vec(self) -> Vec<Value> {
172        self.items
173    }
174}
175
176impl IntoIterator for List {
177    type Item = Value;
178    type IntoIter = std::vec::IntoIter<Value>;
179
180    fn into_iter(self) -> Self::IntoIter {
181        self.items.into_iter()
182    }
183}
184
185impl From<List> for Value {
186    fn from(value: List) -> Self {
187        value.head
188    }
189}
190
191impl From<&Value> for Option<List> {
192    fn from(value: &Value) -> Self {
193        let mut seen = HashSet::new();
194        let mut cdr = value.clone();
195        let mut items = Vec::new();
196        while !cdr.is_null() {
197            if !seen.insert(cdr.clone()) {
198                return None;
199            }
200            let (car, new_cdr) = cdr.cast_to_scheme_type()?;
201            items.push(car);
202            cdr = new_cdr;
203        }
204        Some(List {
205            head: value.clone(),
206            items,
207        })
208    }
209}
210
211impl TryFrom<&Value> for List {
212    type Error = Exception;
213
214    fn try_from(value: &Value) -> Result<Self, Self::Error> {
215        value
216            .cast_to_scheme_type::<List>()
217            .ok_or_else(|| Exception::error("value is not a proper list"))
218    }
219}
220
221/// Convert a slice of values to a proper list
222pub fn slice_to_list(items: &[Value]) -> Value {
223    match items {
224        [] => Value::null(),
225        [head, tail @ ..] => Value::from(Pair::new(head.clone(), slice_to_list(tail), false)),
226    }
227}
228
229pub fn list_to_vec(curr: &Value, out: &mut Vec<Value>) {
230    match &*curr.unpacked_ref() {
231        UnpackedValue::Pair(pair) => {
232            let (car, cdr) = pair.clone().into();
233            out.push(car);
234            list_to_vec(&cdr, out);
235        }
236        UnpackedValue::Null => (),
237        _ => out.push(curr.clone()),
238    }
239}
240
241pub fn list_to_vec_with_null(curr: &Value, out: &mut Vec<Value>) {
242    match &*curr.unpacked_ref() {
243        UnpackedValue::Pair(pair) => {
244            let (car, cdr) = pair.clone().into();
245            out.push(car);
246            list_to_vec_with_null(&cdr, out);
247        }
248        _ => out.push(curr.clone()),
249    }
250}
251
252pub fn is_list(curr: &Value, seen: &mut HashSet<Value>) -> bool {
253    if curr.is_null() {
254        return true;
255    }
256
257    if !seen.insert(curr.clone()) {
258        return false;
259    }
260
261    let Some(curr) = curr.cast_to_scheme_type::<Pair>() else {
262        return false;
263    };
264
265    is_list(&curr.cdr(), seen)
266}
267
268#[bridge(name = "list?", lib = "(rnrs base builtins (6))")]
269pub fn list_pred(arg: &Value) -> Result<Vec<Value>, Exception> {
270    Ok(vec![Value::from(is_list(arg, &mut HashSet::default()))])
271}
272
273#[bridge(name = "list", lib = "(rnrs base builtins (6))")]
274pub fn list(args: &[Value]) -> Result<Vec<Value>, Exception> {
275    // Construct the list in reverse
276    let mut cdr = Value::null();
277    for arg in args.iter().rev() {
278        cdr = Value::from(Pair::new(arg.clone(), cdr, true));
279    }
280    Ok(vec![cdr])
281}
282
283#[bridge(name = "cons", lib = "(rnrs base builtins (6))")]
284pub fn cons(car: &Value, cdr: &Value) -> Result<Vec<Value>, Exception> {
285    Ok(vec![Value::from(Pair::new(car.clone(), cdr.clone(), true))])
286}
287
288#[bridge(name = "car", lib = "(rnrs base builtins (6))")]
289pub fn car(val: &Value) -> Result<Vec<Value>, Exception> {
290    Ok(vec![val.try_to_scheme_type::<Pair>()?.car()])
291}
292
293#[bridge(name = "cdr", lib = "(rnrs base builtins (6))")]
294pub fn cdr(val: &Value) -> Result<Vec<Value>, Exception> {
295    Ok(vec![val.try_to_scheme_type::<Pair>()?.cdr()])
296}
297
298#[bridge(name = "set-car!", lib = "(rnrs mutable-pairs (6))")]
299pub fn set_car(var: &Value, val: &Value) -> Result<Vec<Value>, Exception> {
300    let pair: Pair = var.clone().try_into()?;
301    pair.set_car(val.clone())?;
302    Ok(Vec::new())
303}
304
305#[bridge(name = "set-cdr!", lib = "(rnrs mutable-pairs (6))")]
306pub fn set_cdr(var: &Value, val: &Value) -> Result<Vec<Value>, Exception> {
307    let pair: Pair = var.clone().try_into()?;
308    pair.set_cdr(val.clone())?;
309    Ok(Vec::new())
310}
311
312#[bridge(name = "length", lib = "(rnrs base builtins (6))")]
313pub fn length_builtin(arg: &Value) -> Result<Vec<Value>, Exception> {
314    Ok(vec![Value::from(length(arg)?)])
315}
316
317pub fn length(arg: &Value) -> Result<usize, Exception> {
318    let mut length = 0usize;
319    let mut arg = arg.clone();
320    loop {
321        arg = {
322            match &*arg.unpacked_ref() {
323                UnpackedValue::Pair(pair) => pair.cdr(),
324                UnpackedValue::Null => break,
325                _ => return Err(Exception::error("list must be proper".to_string())),
326            }
327        };
328        length += 1;
329    }
330    Ok(length)
331}
332
333#[bridge(name = "list->vector", lib = "(rnrs base builtins (6))")]
334pub fn list_to_vector(list: &Value) -> Result<Vec<Value>, Exception> {
335    let List { items, .. } = list.try_to_scheme_type()?;
336    Ok(vec![Value::from(items)])
337}
338
339#[bridge(name = "list->string", lib = "(rnrs base builtins (6))")]
340pub fn list_to_string(List { items, .. }: List) -> Result<Vec<Value>, Exception> {
341    let chars = items
342        .into_iter()
343        .map(char::try_from)
344        .collect::<Result<Vec<_>, _>>()?;
345    Ok(vec![Value::from(WideString::new_mutable(chars))])
346}
347
348#[bridge(name = "append", lib = "(rnrs base builtins (6))")]
349pub fn append(list: &Value, to_append: &Value) -> Result<Vec<Value>, Exception> {
350    let mut vec = Vec::new();
351    list_to_vec(list, &mut vec);
352    let mut list = to_append.clone();
353    for item in vec.into_iter().rev() {
354        list = Value::from(Pair::new(item, list, true));
355    }
356    Ok(vec![list])
357}
358
359#[cps_bridge(def = "map proc list1 . listn", lib = "(rnrs base builtins (6))")]
360pub fn map(
361    runtime: &Runtime,
362    _env: &[Value],
363    args: &[Value],
364    list_n: &[Value],
365    dyn_state: &mut DynamicState,
366    k: Value,
367) -> Result<Application, Exception> {
368    let [mapper, list_1] = args else {
369        unreachable!()
370    };
371    let mapper_proc: Procedure = mapper.clone().try_into()?;
372    let mut inputs = Some(list_1.clone())
373        .into_iter()
374        .chain(list_n.iter().cloned())
375        .collect::<Vec<_>>();
376    let mut args = Vec::new();
377
378    for input in inputs.iter_mut() {
379        if input.type_of() == ValueType::Null {
380            // TODO: Check if the rest are also empty and args is empty
381            return Ok(Application::new(k.try_into()?, vec![Value::null()]));
382        }
383
384        let (car, cdr) = input.try_to_scheme_type::<Pair>()?.into();
385
386        args.push(car);
387        *input = cdr;
388    }
389
390    let map_k = dyn_state.new_k(
391        runtime.clone(),
392        vec![
393            Value::from(Vec::<Value>::new()),
394            Value::from(inputs),
395            mapper.clone(),
396            k,
397        ],
398        map_k,
399        1,
400        false,
401    );
402
403    args.push(Value::from(map_k));
404
405    Ok(Application::new(mapper_proc, args))
406}
407
408unsafe extern "C" fn map_k(
409    runtime: *mut GcInner<RwLock<RuntimeInner>>,
410    env: *const Value,
411    args: *const Value,
412    dyn_state: *mut DynamicState,
413) -> *mut Application {
414    unsafe {
415        // TODO: Probably need to do this in a way that avoids mutable variables
416
417        // env[0] is the output list
418        let output: Vector = env.as_ref().unwrap().clone().try_into().unwrap();
419
420        output.0.vec.write().push(args.as_ref().unwrap().clone());
421
422        // env[1] is the input lists
423        let inputs: Vector = env.add(1).as_ref().unwrap().clone().try_into().unwrap();
424
425        // env[2] is the mapper function
426        let mapper: Procedure = env.add(2).as_ref().unwrap().clone().try_into().unwrap();
427
428        // env[3] is the continuation
429        let k: Procedure = env.add(3).as_ref().unwrap().clone().try_into().unwrap();
430
431        let mut args = Vec::new();
432
433        // TODO: We need to collect a new list
434        for input in inputs.0.vec.write().iter_mut() {
435            if input.type_of() == ValueType::Null {
436                // TODO: Check if the rest are also empty and args is empty
437                let output = slice_to_list(&output.0.vec.read());
438                let app = Application::new(k, vec![output]);
439                return Box::into_raw(Box::new(app));
440            }
441
442            let (car, cdr) = input.cast_to_scheme_type::<Pair>().unwrap().into();
443            args.push(car);
444            *input = cdr;
445        }
446
447        let map_k = dyn_state.as_mut().unwrap().new_k(
448            Runtime::from_raw_inc_rc(runtime),
449            vec![
450                Value::from(output),
451                Value::from(inputs),
452                Value::from(mapper.clone()),
453                Value::from(k),
454            ],
455            map_k,
456            1,
457            false,
458        );
459
460        args.push(Value::from(map_k));
461
462        Box::into_raw(Box::new(Application::new(mapper, args)))
463    }
464}
465
466#[bridge(name = "zip", lib = "(rnrs base builtins (6))")]
467pub fn zip(list1: &Value, listn: &[Value]) -> Result<Vec<Value>, Exception> {
468    let mut output: Option<Vec<Value>> = None;
469    for list in Some(list1).into_iter().chain(listn.iter()).rev() {
470        let List { items, .. } = list.try_to_scheme_type()?;
471        if let Some(output) = &output {
472            if output.len() != items.len() {
473                return Err(Exception::error("lists do not have the same length"));
474            }
475        } else {
476            output = Some(vec![Value::null(); items.len()]);
477        }
478
479        let output = output.as_mut().unwrap();
480        for (i, item) in items.into_iter().enumerate() {
481            output[i] = Value::from((item, output[i].clone()));
482        }
483    }
484
485    if let Some(output) = output {
486        Ok(vec![slice_to_list(&output)])
487    } else {
488        Ok(vec![Value::null()])
489    }
490}