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
35pub 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 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 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 #[inline]
68 pub const fn code(&self) -> Cons {
69 self.code
70 }
71
72 #[inline]
74 pub const fn set_code(&mut self, value: Cons) {
75 self.code = value;
76 }
77
78 #[inline]
80 pub const fn register(&self) -> Cons {
81 self.register
82 }
83
84 #[inline]
86 pub const fn set_register(&mut self, value: Cons) {
87 self.register = value;
88 }
89
90 #[inline]
92 pub const fn stack(&self) -> Cons {
93 self.stack
94 }
95
96 #[inline]
98 pub const fn set_stack(&mut self, value: Cons) {
99 self.stack = value;
100 }
101
102 #[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 #[inline]
114 pub const fn null(&self) -> Cons {
115 self.car(self.r#false).assume_cons()
116 }
117
118 #[inline]
120 pub(crate) const fn set_false(&mut self, cons: Cons) {
121 self.r#false = cons;
122 }
123
124 #[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 #[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 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 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 #[inline]
166 pub fn top(&self) -> Value {
167 debug_assert_ne!(self.stack, self.null());
168
169 self.car(self.stack)
170 }
171
172 #[inline]
174 pub const fn set_top(&mut self, value: Value) {
175 self.set_car(self.stack, value);
176 }
177
178 #[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 #[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 #[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 #[inline]
238 pub const fn allocation_index(&self) -> usize {
239 self.allocation_index
240 }
241
242 #[inline]
244 pub const fn allocation_start(&self) -> usize {
245 if self.space { self.space_size() } else { 0 }
246 }
247
248 #[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 #[inline]
268 pub const fn car(&self, cons: Cons) -> Value {
269 self.get(cons.index())
270 }
271
272 #[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 #[inline]
290 pub const fn car_value(&self, cons: Value) -> Value {
291 self.car(cons.assume_cons())
292 }
293
294 #[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 #[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 #[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 #[inline]
328 pub const fn set_car(&mut self, cons: Cons, value: Value) {
329 self.set_field(cons, 0, value)
330 }
331
332 #[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 #[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 #[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 #[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 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 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 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 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 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 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 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 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 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}