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, elprop};
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]
66#[elprop(u8, _)]
67fn make_vector(length: usize, init: Object) -> Vec<Object> {
68    vec![init; length]
69}
70
71#[defun]
72fn vector<'ob>(objects: &[Object<'ob>]) -> Vec<Object<'ob>> {
73    objects.into()
74}
75
76#[defun]
77fn record<'ob>(type_: Object<'ob>, slots: &[Object<'ob>], cx: &'ob Context) -> RecordBuilder<'ob> {
78    let mut record = cx.vec_with_capacity(1 + slots.len());
79    record.push(type_);
80    record.extend_from_slice(slots);
81    RecordBuilder(record)
82}
83
84#[defun]
85fn purecopy(obj: Object) -> Object {
86    obj
87}
88
89#[defun]
90fn make_symbol<'ob>(name: &str, cx: &'ob Context) -> Symbol<'ob> {
91    Symbol::new_uninterned(name, cx)
92}
93
94#[defun]
95fn garbage_collect(cx: &mut Context) -> bool {
96    cx.garbage_collect(true);
97    true
98}
99
100#[cfg(test)]
101mod test {
102    use rune_core::macros::root;
103
104    use crate::core::{env::intern, gc::RootSet, object::ObjectType};
105
106    use super::*;
107
108    #[test]
109    fn build_record() {
110        let roots = &RootSet::default();
111        let cx = &mut Context::new(roots);
112        let type_ = intern("dummy-type", cx);
113        let slots = vec![cx.add("slot1"), cx.add("slot2")];
114        let record = record(type_.into(), &slots, cx);
115        assert_eq!(record.0.len(), 3);
116        assert_eq!(record.0[0], type_);
117        assert_eq!(record.0[1], slots[0]);
118        assert_eq!(record.0[2], slots[1]);
119        let x = cx.add(record);
120        root!(x, cx);
121        cx.garbage_collect(true);
122        let ObjectType::Record(record) = x.bind(cx).untag() else { unreachable!() };
123        assert_eq!(record.len(), 3);
124        assert_eq!(record[1].get(), "slot1");
125        assert_eq!(record[2].get(), "slot2");
126    }
127}