stak_vm/
memory.rs

1use crate::{
2    Error,
3    cons::{Cons, NEVER, Tag},
4    number::Number,
5    r#type::Type,
6    value::Value,
7};
8use core::fmt::{self, Display, Formatter};
9
10const CONS_FIELD_COUNT: usize = 2;
11
12macro_rules! assert_heap_access {
13    ($self:expr, $index:expr) => {
14        assert_heap_cons!(
15            $self,
16            Cons::new(($index / CONS_FIELD_COUNT * CONS_FIELD_COUNT) as u64)
17        );
18    };
19}
20
21macro_rules! assert_heap_cons {
22    ($self:expr, $cons:expr) => {
23        if $cons.raw_eq(NEVER) {
24            debug_assert!($self.allocation_start() <= $cons.index());
25            debug_assert!($cons.index() < $self.allocation_end());
26        }
27    };
28}
29
30macro_rules! assert_heap_value {
31    ($self:expr, $cons:expr) => {
32        if let Some(cons) = $cons.to_cons() {
33            assert_heap_cons!($self, cons);
34        }
35    };
36}
37
38/// A memory on a virtual machine.
39pub struct Memory<'a> {
40    code: Cons,
41    stack: Cons,
42    r#false: Cons,
43    register: Cons,
44    allocation_index: usize,
45    space: bool,
46    heap: &'a mut [Value],
47}
48
49impl<'a> Memory<'a> {
50    /// Creates a memory.
51    pub fn new(heap: &'a mut [Value]) -> Result<Self, Error> {
52        let mut memory = Self {
53            code: NEVER,
54            stack: NEVER,
55            r#false: NEVER,
56            register: NEVER,
57            allocation_index: 0,
58            space: false,
59            heap,
60        };
61
62        // Initialize a fake false value.
63        let cons = memory.allocate_unchecked(Default::default(), Default::default())?;
64        memory.r#false = memory.allocate_unchecked(cons.into(), cons.into())?;
65
66        Ok(memory)
67    }
68
69    /// Returns a code.
70    #[inline]
71    pub const fn code(&self) -> Cons {
72        self.code
73    }
74
75    /// Sets a code.
76    #[inline]
77    pub fn set_code(&mut self, value: Cons) {
78        self.code = value;
79    }
80
81    /// Returns a register.
82    #[inline]
83    pub const fn register(&self) -> Cons {
84        self.register
85    }
86
87    /// Sets a register.
88    #[inline]
89    pub fn set_register(&mut self, value: Cons) {
90        self.register = value;
91    }
92
93    /// Returns a stack.
94    #[inline]
95    pub const fn stack(&self) -> Cons {
96        self.stack
97    }
98
99    /// Sets a stack.
100    #[inline]
101    pub fn set_stack(&mut self, value: Cons) {
102        self.stack = value;
103    }
104
105    /// Returns a boolean value.
106    #[inline]
107    pub const fn boolean(&self, value: bool) -> Cons {
108        if value {
109            self.cdr(self.r#false).assume_cons()
110        } else {
111            self.r#false
112        }
113    }
114
115    /// Returns a null value.
116    #[inline]
117    pub const fn null(&self) -> Cons {
118        self.car(self.r#false).assume_cons()
119    }
120
121    /// Sets a false value.
122    #[inline]
123    pub(crate) fn set_false(&mut self, cons: Cons) {
124        self.r#false = cons;
125    }
126
127    /// Pushes a value to a stack.
128    #[inline]
129    pub fn push(&mut self, value: Value) -> Result<(), Error> {
130        self.stack = self.cons(value, self.stack)?;
131
132        Ok(())
133    }
134
135    /// Pops a value from a stack.
136    #[inline]
137    pub fn pop(&mut self) -> Value {
138        debug_assert_ne!(self.stack, self.null());
139
140        let value = self.car(self.stack);
141        self.stack = self.cdr(self.stack).assume_cons();
142        value
143    }
144
145    /// Pops values from a stack.
146    pub fn pop_many<const M: usize>(&mut self) -> [Value; M] {
147        let mut values = [Default::default(); M];
148
149        for index in 0..=M - 1 {
150            values[M - 1 - index] = self.pop();
151        }
152
153        values
154    }
155
156    /// Pops numbers from a stack.
157    pub fn pop_numbers<const M: usize>(&mut self) -> [Number; M] {
158        let mut numbers = [Default::default(); M];
159
160        for (index, value) in self.pop_many::<M>().into_iter().enumerate() {
161            numbers[index] = value.assume_number();
162        }
163
164        numbers
165    }
166
167    /// Peeks a value at the top of a stack.
168    #[inline]
169    pub fn top(&mut self) -> Value {
170        debug_assert_ne!(self.stack, self.null());
171
172        self.car(self.stack)
173    }
174
175    /// Allocates a cons with a default tag of [`Type::Pair`].
176    #[inline]
177    pub fn cons(&mut self, car: Value, cdr: Cons) -> Result<Cons, Error> {
178        self.allocate(car, cdr.set_tag(Type::Pair as Tag).into())
179    }
180
181    /// Allocates a cons.
182    #[inline]
183    pub fn allocate(&mut self, car: Value, cdr: Value) -> Result<Cons, Error> {
184        let mut cons = self.allocate_unchecked(car, cdr)?;
185
186        debug_assert_eq!(cons.tag(), Type::default() as Tag);
187        assert_heap_cons!(self, cons);
188        assert_heap_value!(self, car);
189        assert_heap_value!(self, cdr);
190
191        if self.is_out_of_memory() || cfg!(feature = "gc_always") {
192            self.collect_garbages(Some(&mut cons))?;
193        }
194
195        Ok(cons)
196    }
197
198    #[inline]
199    fn allocate_unchecked(&mut self, car: Value, cdr: Value) -> Result<Cons, Error> {
200        if self.is_out_of_memory() {
201            return Err(Error::OutOfMemory);
202        }
203
204        let cons = Cons::new(self.allocation_end() as u64);
205        self.allocation_index += CONS_FIELD_COUNT;
206
207        assert_heap_cons!(self, cons);
208
209        self.set_raw_car(cons, car);
210        self.set_raw_cdr(cons, cdr);
211
212        debug_assert!(self.allocation_index <= self.space_size());
213
214        Ok(cons)
215    }
216
217    #[inline]
218    const fn is_out_of_memory(&self) -> bool {
219        self.allocation_index >= self.space_size()
220    }
221
222    /// Returns a heap size.
223    #[inline]
224    pub const fn size(&self) -> usize {
225        self.heap.len()
226    }
227
228    #[inline]
229    const fn space_size(&self) -> usize {
230        self.size() / 2
231    }
232
233    /// Returns the current allocation index relative an allocation start index.
234    #[inline]
235    pub const fn allocation_index(&self) -> usize {
236        self.allocation_index
237    }
238
239    /// Returns an allocation start index.
240    #[inline]
241    pub const fn allocation_start(&self) -> usize {
242        if self.space { self.space_size() } else { 0 }
243    }
244
245    /// Returns an allocation end index.
246    #[inline]
247    pub const fn allocation_end(&self) -> usize {
248        self.allocation_start() + self.allocation_index
249    }
250
251    #[inline]
252    const fn get(&self, index: usize) -> Value {
253        assert_heap_access!(self, index);
254        self.heap[index]
255    }
256
257    #[inline]
258    fn set(&mut self, index: usize, value: Value) {
259        assert_heap_access!(self, index);
260        self.heap[index] = value
261    }
262
263    /// Returns a value of a `car` field in a cons.
264    #[inline]
265    pub const fn car(&self, cons: Cons) -> Value {
266        self.get(cons.index())
267    }
268
269    /// Returns a value of a `cdr` field in a cons.
270    #[inline]
271    pub const fn cdr(&self, cons: Cons) -> Value {
272        self.get(cons.index() + 1)
273    }
274
275    #[inline]
276    const fn unchecked_car(&self, cons: Cons) -> Value {
277        self.heap[cons.index()]
278    }
279
280    #[inline]
281    const fn unchecked_cdr(&self, cons: Cons) -> Value {
282        self.heap[cons.index() + 1]
283    }
284
285    /// Returns a value of a `car` field in a value assumed as a cons.
286    #[inline]
287    pub const fn car_value(&self, cons: Value) -> Value {
288        self.car(cons.assume_cons())
289    }
290
291    /// Returns a value of a `cdr` field in a value assumed as a cons.
292    #[inline]
293    pub const fn cdr_value(&self, cons: Value) -> Value {
294        self.cdr(cons.assume_cons())
295    }
296
297    #[inline]
298    fn set_raw_field(&mut self, cons: Cons, index: usize, value: Value) {
299        self.set(cons.index() + index, value);
300    }
301
302    #[inline]
303    fn set_raw_car(&mut self, cons: Cons, value: Value) {
304        self.set_raw_field(cons, 0, value)
305    }
306
307    #[inline]
308    fn set_raw_cdr(&mut self, cons: Cons, value: Value) {
309        self.set_raw_field(cons, 1, value)
310    }
311
312    #[inline]
313    fn set_field(&mut self, cons: Cons, index: usize, value: Value) {
314        self.set_raw_field(
315            cons,
316            index,
317            value.set_tag(self.get(cons.index() + index).tag()),
318        )
319    }
320
321    /// Sets a value to a `car` field in a cons.
322    #[inline]
323    pub fn set_car(&mut self, cons: Cons, value: Value) {
324        self.set_field(cons, 0, value)
325    }
326
327    /// Sets a value to a `cdr` field in a cons.
328    #[inline]
329    pub fn set_cdr(&mut self, cons: Cons, value: Value) {
330        self.set_field(cons, 1, value)
331    }
332
333    #[inline]
334    fn set_unchecked_car(&mut self, cons: Cons, value: Value) {
335        self.heap[cons.index()] = value
336    }
337
338    #[inline]
339    fn set_unchecked_cdr(&mut self, cons: Cons, value: Value) {
340        self.heap[cons.index() + 1] = value;
341    }
342
343    /// Sets a value to a `car` field in a value assumed as a cons.
344    #[inline]
345    pub fn set_car_value(&mut self, cons: Value, value: Value) {
346        self.set_car(cons.assume_cons(), value);
347    }
348
349    /// Sets a value to a `cdr` field in a value assumed as a cons.
350    #[inline]
351    pub fn set_cdr_value(&mut self, cons: Value, value: Value) {
352        self.set_cdr(cons.assume_cons(), value);
353    }
354
355    /// Returns a tail of a list.
356    pub const fn tail(&self, mut list: Cons, mut index: usize) -> Cons {
357        while index > 0 {
358            list = self.cdr(list).assume_cons();
359            index -= 1;
360        }
361
362        list
363    }
364
365    /// Builds a string.
366    pub fn build_string(&mut self, string: &str) -> Result<Cons, Error> {
367        let mut list = self.null();
368
369        for character in string.chars().rev() {
370            list = self.cons(Number::from_i64(character as _).into(), list)?;
371        }
372
373        Ok(list)
374    }
375
376    /// Executes an operation against a value at the top of a stack.
377    pub fn operate_top(&mut self, operate: impl Fn(&Self, Value) -> Value) -> Result<(), Error> {
378        let value = self.pop();
379        self.push(operate(self, value))?;
380        Ok(())
381    }
382
383    /// Executes an unary number operation.
384    pub fn operate_unary(&mut self, operate: fn(Number) -> Number) -> Result<(), Error> {
385        let [x] = self.pop_numbers();
386
387        self.push(operate(x).into())?;
388
389        Ok(())
390    }
391
392    /// Executes a binary number operation.
393    pub fn operate_binary(&mut self, operate: fn(Number, Number) -> Number) -> Result<(), Error> {
394        let [x, y] = self.pop_numbers();
395
396        self.push(operate(x, y).into())?;
397
398        Ok(())
399    }
400
401    /// Executes an operation that returns `Option<Value>`.
402    pub fn operate_option(
403        &mut self,
404        mut operate: impl FnMut(&mut Self) -> Option<Value>,
405    ) -> Result<(), Error> {
406        let value = operate(self).unwrap_or_else(|| self.boolean(false).into());
407        self.push(value)?;
408        Ok(())
409    }
410
411    // Garbage collection
412
413    fn collect_garbages(&mut self, cons: Option<&mut Cons>) -> Result<(), Error> {
414        self.allocation_index = 0;
415        self.space = !self.space;
416
417        self.code = self.copy_cons(self.code)?;
418        self.stack = self.copy_cons(self.stack)?;
419        self.r#false = self.copy_cons(self.r#false)?;
420        self.register = self.copy_cons(self.register)?;
421
422        if let Some(cons) = cons {
423            *cons = self.copy_cons(*cons)?;
424        }
425
426        let mut index = self.allocation_start();
427
428        while index < self.allocation_end() {
429            let value = self.copy_value(self.get(index))?;
430            self.set(index, value);
431            index += 1;
432        }
433
434        Ok(())
435    }
436
437    fn copy_value(&mut self, value: Value) -> Result<Value, Error> {
438        Ok(if let Some(cons) = value.to_cons() {
439            self.copy_cons(cons)?.into()
440        } else {
441            value
442        })
443    }
444
445    fn copy_cons(&mut self, cons: Cons) -> Result<Cons, Error> {
446        Ok(if cons.raw_eq(NEVER) {
447            NEVER
448        } else if self.unchecked_car(cons).raw_eq(NEVER.into()) {
449            // Get a forward pointer.
450            self.unchecked_cdr(cons).assume_cons()
451        } else {
452            let copy =
453                self.allocate_unchecked(self.unchecked_car(cons), self.unchecked_cdr(cons))?;
454
455            // Set a forward pointer.
456            self.set_unchecked_car(cons, NEVER.into());
457            self.set_unchecked_cdr(cons, copy.into());
458
459            copy
460        }
461        .set_tag(cons.tag()))
462    }
463}
464
465impl Display for Memory<'_> {
466    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
467        writeln!(formatter, "code: {}", self.code)?;
468        writeln!(formatter, "stack: {}", self.stack)?;
469
470        for index in 0..self.allocation_index / 2 {
471            let index = self.allocation_start() + 2 * index;
472            let cons = Cons::new(index as u64);
473
474            write!(
475                formatter,
476                "{:02x}: {} {}",
477                index,
478                self.car(cons),
479                self.cdr(cons)
480            )?;
481
482            for (cons, name) in [
483                (self.code, "code"),
484                (self.register, "register"),
485                (self.stack, "stack"),
486            ] {
487                if index == cons.index() && !cons.raw_eq(NEVER) {
488                    write!(formatter, " <- {name}")?;
489                }
490            }
491
492            writeln!(formatter)?;
493        }
494
495        Ok(())
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502
503    const HEAP_SIZE: usize = 1 << 9;
504
505    fn create_heap() -> [Value; HEAP_SIZE] {
506        [Default::default(); HEAP_SIZE]
507    }
508
509    macro_rules! assert_snapshot {
510        ($memory:expr) => {
511            #[cfg(not(feature = "gc_always"))]
512            insta::assert_snapshot!($memory);
513
514            let _ = $memory;
515        };
516    }
517
518    #[test]
519    fn create() {
520        let mut heap = create_heap();
521        let memory = Memory::new(&mut heap).unwrap();
522
523        assert_snapshot!(memory);
524    }
525
526    #[test]
527    fn create_list() {
528        let mut heap = create_heap();
529        let mut memory = Memory::new(&mut heap).unwrap();
530
531        let list = memory
532            .cons(Number::from_i64(1).into(), memory.null())
533            .unwrap();
534
535        assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
536        assert_snapshot!(memory);
537
538        let list = memory.cons(Number::from_i64(2).into(), list).unwrap();
539
540        assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
541        assert_snapshot!(memory);
542
543        let list = memory.cons(Number::from_i64(3).into(), list).unwrap();
544
545        assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
546        assert_snapshot!(memory);
547    }
548
549    #[test]
550    fn convert_false() {
551        let mut heap = create_heap();
552        let memory = Memory::new(&mut heap).unwrap();
553
554        assert_eq!(
555            Value::from(memory.boolean(false)).to_cons().unwrap(),
556            memory.boolean(false)
557        );
558    }
559
560    #[test]
561    fn convert_true() {
562        let mut heap = create_heap();
563        let memory = Memory::new(&mut heap).unwrap();
564
565        assert_eq!(
566            Value::from(memory.boolean(true)).to_cons().unwrap(),
567            memory.boolean(true)
568        );
569    }
570
571    #[test]
572    fn convert_null() {
573        let mut heap = create_heap();
574        let memory = Memory::new(&mut heap).unwrap();
575
576        assert_eq!(Value::from(memory.null()).to_cons().unwrap(), memory.null());
577    }
578
579    mod stack {
580        use super::*;
581
582        #[test]
583        fn push_and_pop() {
584            let mut heap = create_heap();
585            let mut memory = Memory::new(&mut heap).unwrap();
586
587            memory.stack = memory.null();
588            memory.push(Number::from_i64(42).into()).unwrap();
589
590            assert_eq!(memory.pop(), Number::from_i64(42).into());
591        }
592
593        #[test]
594        fn push_and_pop_twice() {
595            let mut heap = create_heap();
596            let mut memory = Memory::new(&mut heap).unwrap();
597
598            memory.stack = memory.null();
599            memory.push(Number::from_i64(1).into()).unwrap();
600            memory.push(Number::from_i64(2).into()).unwrap();
601
602            assert_eq!(memory.pop(), Number::from_i64(2).into());
603            assert_eq!(memory.pop(), Number::from_i64(1).into());
604        }
605    }
606
607    mod garbage_collection {
608        use super::*;
609
610        #[test]
611        fn collect_cons() {
612            let mut heap = create_heap();
613            let mut memory = Memory::new(&mut heap).unwrap();
614
615            memory
616                .allocate(Number::default().into(), Number::default().into())
617                .unwrap();
618            memory.collect_garbages(None).unwrap();
619
620            assert_snapshot!(memory);
621        }
622
623        #[test]
624        fn collect_stack() {
625            let mut heap = create_heap();
626            let mut memory = Memory::new(&mut heap).unwrap();
627
628            memory.stack = memory.null();
629            memory.push(Number::from_i64(42).into()).unwrap();
630            memory.collect_garbages(None).unwrap();
631
632            assert_snapshot!(memory);
633        }
634
635        #[test]
636        fn collect_deep_stack() {
637            let mut heap = create_heap();
638            let mut memory = Memory::new(&mut heap).unwrap();
639
640            memory.stack = memory.null();
641            memory.push(Number::from_i64(1).into()).unwrap();
642            memory.push(Number::from_i64(2).into()).unwrap();
643            memory.collect_garbages(None).unwrap();
644
645            assert_snapshot!(memory);
646        }
647
648        #[test]
649        fn collect_cycle() {
650            let mut heap = create_heap();
651            let mut memory = Memory::new(&mut heap).unwrap();
652
653            let cons = memory
654                .allocate(Number::default().into(), Number::default().into())
655                .unwrap();
656            memory.set_cdr(cons, cons.into());
657
658            memory.collect_garbages(None).unwrap();
659
660            assert_snapshot!(memory);
661        }
662    }
663}