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