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