rune/
alloc.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! builtin lisp data structures.
use crate::core::cons::Cons;
use crate::core::gc::Context;
use crate::core::object::{
    ByteFn, ByteString, FnArgs, Gc, IntoObject, LispVec, Object, RecordBuilder, Symbol, NIL,
};
use anyhow::{ensure, Result};
use rune_macros::defun;

#[defun]
pub(crate) fn list<'ob>(objects: &[Object<'ob>], cx: &'ob Context) -> Object<'ob> {
    let mut head = NIL;
    for object in objects.iter().rev() {
        head = Cons::new(*object, head, cx).into();
    }
    head
}

/// Convert a function to closure by replacing the first N elements with their
/// closure values.
#[defun]
pub(crate) fn make_closure<'ob>(
    prototype: &ByteFn,
    closure_vars: &[Object<'ob>],
    cx: &'ob Context,
) -> Result<Gc<&'ob ByteFn>> {
    let const_len = prototype.consts().len();
    let vars = closure_vars.len();
    ensure!(vars <= const_len, "Closure vars do not fit in const vec");
    let mut constants = prototype.consts().to_vec();
    let zipped = constants.iter_mut().zip(closure_vars.iter());
    for (cnst, var) in zipped {
        *cnst = *var;
    }

    unsafe {
        Ok(ByteFn::make(
            prototype.codes(),
            constants.into_obj(cx).untag(),
            prototype.args,
            prototype.depth,
        )
        .into_obj(cx))
    }
}

#[defun]
#[expect(clippy::too_many_arguments)]
pub(crate) fn make_byte_code<'ob>(
    arglist: i64,
    byte_code: &'ob ByteString,
    constants: &'ob LispVec,
    depth: usize,
    _docstring: Option<Object>,
    _interactive_spec: Option<Object>,
    _elements: &[Object],
    cx: &'ob Context,
) -> Result<&'ob ByteFn> {
    unsafe {
        let bytefn = ByteFn::make(byte_code, constants, FnArgs::from_arg_spec(arglist)?, depth);
        Ok(bytefn.into_obj(cx).untag())
    }
}

#[defun]
fn make_vector(length: usize, init: Object) -> Vec<Object> {
    vec![init; length]
}

#[defun]
fn vector<'ob>(objects: &[Object<'ob>]) -> Vec<Object<'ob>> {
    objects.into()
}

#[defun]
fn record<'ob>(type_: Object<'ob>, slots: &[Object<'ob>], cx: &'ob Context) -> RecordBuilder<'ob> {
    let mut record = cx.vec_with_capacity(1 + slots.len());
    record.push(type_);
    record.extend_from_slice(slots);
    RecordBuilder(record)
}

#[defun]
fn purecopy(obj: Object) -> Object {
    obj
}

#[defun]
fn make_symbol<'ob>(name: &str, cx: &'ob Context) -> Symbol<'ob> {
    Symbol::new_uninterned(name, cx)
}

#[defun]
fn garbage_collect(cx: &mut Context) -> bool {
    cx.garbage_collect(true);
    true
}

#[cfg(test)]
mod test {
    use rune_core::macros::root;

    use crate::core::{env::intern, gc::RootSet, object::ObjectType};

    use super::*;

    #[test]
    fn build_record() {
        let roots = &RootSet::default();
        let cx = &mut Context::new(roots);
        let type_ = intern("dummy-type", cx);
        let slots = vec![cx.add("slot1"), cx.add("slot2")];
        let record = record(type_.into(), &slots, cx);
        assert_eq!(record.0.len(), 3);
        assert_eq!(record.0[0], type_);
        assert_eq!(record.0[1], slots[0]);
        assert_eq!(record.0[2], slots[1]);
        let x = cx.add(record);
        root!(x, cx);
        cx.garbage_collect(true);
        let ObjectType::Record(record) = x.bind(cx).untag() else { unreachable!() };
        assert_eq!(record.len(), 3);
        assert_eq!(record[1].get(), "slot1");
        assert_eq!(record[2].get(), "slot2");
    }
}