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