rune/
alloc.rs

1//! builtin lisp data structures.
2use crate::core::cons::Cons;
3use crate::core::gc::Context;
4use crate::core::object::{
5    ByteFn, ByteString, FnArgs, Gc, IntoObject, LispVec, NIL, Object, RecordBuilder, Symbol,
6};
7use anyhow::{Result, ensure};
8use rune_macros::defun;
9
10#[defun]
11pub(crate) fn list<'ob>(objects: &[Object<'ob>], cx: &'ob Context) -> Object<'ob> {
12    let mut head = NIL;
13    for object in objects.iter().rev() {
14        head = Cons::new(*object, head, cx).into();
15    }
16    head
17}
18
19/// Convert a function to closure by replacing the first N elements with their
20/// closure values.
21#[defun]
22pub(crate) fn make_closure<'ob>(
23    prototype: &ByteFn,
24    closure_vars: &[Object<'ob>],
25    cx: &'ob Context,
26) -> Result<Gc<&'ob ByteFn>> {
27    let const_len = prototype.consts().len();
28    let vars = closure_vars.len();
29    ensure!(vars <= const_len, "Closure vars do not fit in const vec");
30    let mut constants = prototype.consts().to_vec();
31    let zipped = constants.iter_mut().zip(closure_vars.iter());
32    for (cnst, var) in zipped {
33        *cnst = *var;
34    }
35
36    unsafe {
37        Ok(ByteFn::make(
38            prototype.codes(),
39            constants.into_obj(cx).untag(),
40            prototype.args,
41            prototype.depth,
42        )
43        .into_obj(cx))
44    }
45}
46
47#[defun]
48#[expect(clippy::too_many_arguments)]
49pub(crate) fn make_byte_code<'ob>(
50    arglist: i64,
51    byte_code: &'ob ByteString,
52    constants: &'ob LispVec,
53    depth: usize,
54    _docstring: Option<Object>,
55    _interactive_spec: Option<Object>,
56    _elements: &[Object],
57    cx: &'ob Context,
58) -> Result<&'ob ByteFn> {
59    unsafe {
60        let bytefn = ByteFn::make(byte_code, constants, FnArgs::from_arg_spec(arglist)?, depth);
61        Ok(bytefn.into_obj(cx).untag())
62    }
63}
64
65#[defun]
66fn make_vector(length: usize, init: Object) -> Vec<Object> {
67    vec![init; length]
68}
69
70#[defun]
71fn vector<'ob>(objects: &[Object<'ob>]) -> Vec<Object<'ob>> {
72    objects.into()
73}
74
75#[defun]
76fn record<'ob>(type_: Object<'ob>, slots: &[Object<'ob>], cx: &'ob Context) -> RecordBuilder<'ob> {
77    let mut record = cx.vec_with_capacity(1 + slots.len());
78    record.push(type_);
79    record.extend_from_slice(slots);
80    RecordBuilder(record)
81}
82
83#[defun]
84fn purecopy(obj: Object) -> Object {
85    obj
86}
87
88#[defun]
89fn make_symbol<'ob>(name: &str, cx: &'ob Context) -> Symbol<'ob> {
90    Symbol::new_uninterned(name, cx)
91}
92
93#[defun]
94fn garbage_collect(cx: &mut Context) -> bool {
95    cx.garbage_collect(true);
96    true
97}
98
99#[cfg(test)]
100mod test {
101    use proptest::prelude::*;
102    use rune_core::macros::root;
103
104    use crate::{
105        assert_elprop,
106        core::{env::intern, gc::RootSet, object::ObjectType},
107        library::elprop::arb_byte,
108    };
109
110    use super::*;
111
112    #[test]
113    fn build_record() {
114        let roots = &RootSet::default();
115        let cx = &mut Context::new(roots);
116        let type_ = intern("dummy-type", cx);
117        let slots = vec![cx.add("slot1"), cx.add("slot2")];
118        let record = record(type_.into(), &slots, cx);
119        assert_eq!(record.0.len(), 3);
120        assert_eq!(record.0[0], type_);
121        assert_eq!(record.0[1], slots[0]);
122        assert_eq!(record.0[2], slots[1]);
123        let x = cx.add(record);
124        root!(x, cx);
125        cx.garbage_collect(true);
126        let ObjectType::Record(record) = x.bind(cx).untag() else { unreachable!() };
127        assert_eq!(record.len(), 3);
128        assert_eq!(record[1].get(), "slot1");
129        assert_eq!(record[2].get(), "slot2");
130    }
131
132    #[test]
133    #[cfg_attr(miri, ignore)]
134    fn test_make_vector() {
135        proptest!(|(size in arb_byte())| {
136            assert_elprop!["(make-vector {} ?A)", size];
137        });
138    }
139}