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