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};
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.raw_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
38pub 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 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 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 #[inline]
71 pub const fn code(&self) -> Cons {
72 self.code
73 }
74
75 #[inline]
77 pub fn set_code(&mut self, value: Cons) {
78 self.code = value;
79 }
80
81 #[inline]
83 pub const fn register(&self) -> Cons {
84 self.register
85 }
86
87 #[inline]
89 pub fn set_register(&mut self, value: Cons) {
90 self.register = value;
91 }
92
93 #[inline]
95 pub const fn stack(&self) -> Cons {
96 self.stack
97 }
98
99 #[inline]
101 pub fn set_stack(&mut self, value: Cons) {
102 self.stack = value;
103 }
104
105 #[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 #[inline]
117 pub const fn null(&self) -> Cons {
118 self.car(self.r#false).assume_cons()
119 }
120
121 #[inline]
123 pub(crate) fn set_false(&mut self, cons: Cons) {
124 self.r#false = cons;
125 }
126
127 #[inline]
129 pub fn push(&mut self, value: Value) -> Result<(), Error> {
130 self.stack = self.cons(value, self.stack)?;
131
132 Ok(())
133 }
134
135 #[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 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 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 #[inline]
169 pub fn top(&mut self) -> Value {
170 debug_assert_ne!(self.stack, self.null());
171
172 self.car(self.stack)
173 }
174
175 #[inline]
177 pub fn cons(&mut self, car: Value, cdr: Cons) -> Result<Cons, Error> {
178 self.allocate(car, cdr.set_tag(Type::Pair as Tag).into())
179 }
180
181 #[inline]
183 pub fn allocate(&mut self, car: Value, cdr: Value) -> Result<Cons, Error> {
184 let mut cons = self.allocate_unchecked(car, cdr)?;
185
186 debug_assert_eq!(cons.tag(), Type::default() as Tag);
187 assert_heap_cons!(self, cons);
188 assert_heap_value!(self, car);
189 assert_heap_value!(self, cdr);
190
191 if self.is_out_of_memory() || cfg!(feature = "gc_always") {
192 self.collect_garbages(Some(&mut cons))?;
193 }
194
195 Ok(cons)
196 }
197
198 #[inline]
199 fn allocate_unchecked(&mut self, car: Value, cdr: Value) -> Result<Cons, Error> {
200 if self.is_out_of_memory() {
201 return Err(Error::OutOfMemory);
202 }
203
204 let cons = Cons::new(self.allocation_end() as u64);
205 self.allocation_index += CONS_FIELD_COUNT;
206
207 assert_heap_cons!(self, cons);
208
209 self.set_raw_car(cons, car);
210 self.set_raw_cdr(cons, cdr);
211
212 debug_assert!(self.allocation_index <= self.space_size());
213
214 Ok(cons)
215 }
216
217 #[inline]
218 const fn is_out_of_memory(&self) -> bool {
219 self.allocation_index >= self.space_size()
220 }
221
222 #[inline]
224 pub const fn size(&self) -> usize {
225 self.heap.len()
226 }
227
228 #[inline]
229 const fn space_size(&self) -> usize {
230 self.size() / 2
231 }
232
233 #[inline]
235 pub const fn allocation_index(&self) -> usize {
236 self.allocation_index
237 }
238
239 #[inline]
241 pub const fn allocation_start(&self) -> usize {
242 if self.space { self.space_size() } else { 0 }
243 }
244
245 #[inline]
247 pub const fn allocation_end(&self) -> usize {
248 self.allocation_start() + self.allocation_index
249 }
250
251 #[inline]
252 const fn get(&self, index: usize) -> Value {
253 assert_heap_access!(self, index);
254 self.heap[index]
255 }
256
257 #[inline]
258 fn set(&mut self, index: usize, value: Value) {
259 assert_heap_access!(self, index);
260 self.heap[index] = value
261 }
262
263 #[inline]
265 pub const fn car(&self, cons: Cons) -> Value {
266 self.get(cons.index())
267 }
268
269 #[inline]
271 pub const fn cdr(&self, cons: Cons) -> Value {
272 self.get(cons.index() + 1)
273 }
274
275 #[inline]
276 const fn unchecked_car(&self, cons: Cons) -> Value {
277 self.heap[cons.index()]
278 }
279
280 #[inline]
281 const fn unchecked_cdr(&self, cons: Cons) -> Value {
282 self.heap[cons.index() + 1]
283 }
284
285 #[inline]
287 pub const fn car_value(&self, cons: Value) -> Value {
288 self.car(cons.assume_cons())
289 }
290
291 #[inline]
293 pub const fn cdr_value(&self, cons: Value) -> Value {
294 self.cdr(cons.assume_cons())
295 }
296
297 #[inline]
298 fn set_raw_field(&mut self, cons: Cons, index: usize, value: Value) {
299 self.set(cons.index() + index, value);
300 }
301
302 #[inline]
303 fn set_raw_car(&mut self, cons: Cons, value: Value) {
304 self.set_raw_field(cons, 0, value)
305 }
306
307 #[inline]
308 fn set_raw_cdr(&mut self, cons: Cons, value: Value) {
309 self.set_raw_field(cons, 1, value)
310 }
311
312 #[inline]
313 fn set_field(&mut self, cons: Cons, index: usize, value: Value) {
314 self.set_raw_field(
315 cons,
316 index,
317 value.set_tag(self.get(cons.index() + index).tag()),
318 )
319 }
320
321 #[inline]
323 pub fn set_car(&mut self, cons: Cons, value: Value) {
324 self.set_field(cons, 0, value)
325 }
326
327 #[inline]
329 pub fn set_cdr(&mut self, cons: Cons, value: Value) {
330 self.set_field(cons, 1, value)
331 }
332
333 #[inline]
334 fn set_unchecked_car(&mut self, cons: Cons, value: Value) {
335 self.heap[cons.index()] = value
336 }
337
338 #[inline]
339 fn set_unchecked_cdr(&mut self, cons: Cons, value: Value) {
340 self.heap[cons.index() + 1] = value;
341 }
342
343 #[inline]
345 pub fn set_car_value(&mut self, cons: Value, value: Value) {
346 self.set_car(cons.assume_cons(), value);
347 }
348
349 #[inline]
351 pub fn set_cdr_value(&mut self, cons: Value, value: Value) {
352 self.set_cdr(cons.assume_cons(), value);
353 }
354
355 pub const fn tail(&self, mut list: Cons, mut index: usize) -> Cons {
357 while index > 0 {
358 list = self.cdr(list).assume_cons();
359 index -= 1;
360 }
361
362 list
363 }
364
365 pub fn build_string(&mut self, string: &str) -> Result<Cons, Error> {
367 let mut list = self.null();
368
369 for character in string.chars().rev() {
370 list = self.cons(Number::from_i64(character as _).into(), list)?;
371 }
372
373 Ok(list)
374 }
375
376 pub fn operate_top(&mut self, operate: impl Fn(&Self, Value) -> Value) -> Result<(), Error> {
378 let value = self.pop();
379 self.push(operate(self, value))?;
380 Ok(())
381 }
382
383 pub fn operate_unary(&mut self, operate: fn(Number) -> Number) -> Result<(), Error> {
385 let [x] = self.pop_numbers();
386
387 self.push(operate(x).into())?;
388
389 Ok(())
390 }
391
392 pub fn operate_binary(&mut self, operate: fn(Number, Number) -> Number) -> Result<(), Error> {
394 let [x, y] = self.pop_numbers();
395
396 self.push(operate(x, y).into())?;
397
398 Ok(())
399 }
400
401 pub fn operate_option(
403 &mut self,
404 mut operate: impl FnMut(&mut Self) -> Option<Value>,
405 ) -> Result<(), Error> {
406 let value = operate(self).unwrap_or_else(|| self.boolean(false).into());
407 self.push(value)?;
408 Ok(())
409 }
410
411 fn collect_garbages(&mut self, cons: Option<&mut Cons>) -> Result<(), Error> {
414 self.allocation_index = 0;
415 self.space = !self.space;
416
417 self.code = self.copy_cons(self.code)?;
418 self.stack = self.copy_cons(self.stack)?;
419 self.r#false = self.copy_cons(self.r#false)?;
420 self.register = self.copy_cons(self.register)?;
421
422 if let Some(cons) = cons {
423 *cons = self.copy_cons(*cons)?;
424 }
425
426 let mut index = self.allocation_start();
427
428 while index < self.allocation_end() {
429 let value = self.copy_value(self.get(index))?;
430 self.set(index, value);
431 index += 1;
432 }
433
434 Ok(())
435 }
436
437 fn copy_value(&mut self, value: Value) -> Result<Value, Error> {
438 Ok(if let Some(cons) = value.to_cons() {
439 self.copy_cons(cons)?.into()
440 } else {
441 value
442 })
443 }
444
445 fn copy_cons(&mut self, cons: Cons) -> Result<Cons, Error> {
446 Ok(if cons.raw_eq(NEVER) {
447 NEVER
448 } else if self.unchecked_car(cons).raw_eq(NEVER.into()) {
449 self.unchecked_cdr(cons).assume_cons()
451 } else {
452 let copy =
453 self.allocate_unchecked(self.unchecked_car(cons), self.unchecked_cdr(cons))?;
454
455 self.set_unchecked_car(cons, NEVER.into());
457 self.set_unchecked_cdr(cons, copy.into());
458
459 copy
460 }
461 .set_tag(cons.tag()))
462 }
463}
464
465impl Display for Memory<'_> {
466 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
467 writeln!(formatter, "code: {}", self.code)?;
468 writeln!(formatter, "stack: {}", self.stack)?;
469
470 for index in 0..self.allocation_index / 2 {
471 let index = self.allocation_start() + 2 * index;
472 let cons = Cons::new(index as u64);
473
474 write!(
475 formatter,
476 "{:02x}: {} {}",
477 index,
478 self.car(cons),
479 self.cdr(cons)
480 )?;
481
482 for (cons, name) in [
483 (self.code, "code"),
484 (self.register, "register"),
485 (self.stack, "stack"),
486 ] {
487 if index == cons.index() && !cons.raw_eq(NEVER) {
488 write!(formatter, " <- {name}")?;
489 }
490 }
491
492 writeln!(formatter)?;
493 }
494
495 Ok(())
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502
503 const HEAP_SIZE: usize = 1 << 9;
504
505 fn create_heap() -> [Value; HEAP_SIZE] {
506 [Default::default(); HEAP_SIZE]
507 }
508
509 macro_rules! assert_snapshot {
510 ($memory:expr) => {
511 #[cfg(not(feature = "gc_always"))]
512 insta::assert_snapshot!($memory);
513
514 let _ = $memory;
515 };
516 }
517
518 #[test]
519 fn create() {
520 let mut heap = create_heap();
521 let memory = Memory::new(&mut heap).unwrap();
522
523 assert_snapshot!(memory);
524 }
525
526 #[test]
527 fn create_list() {
528 let mut heap = create_heap();
529 let mut memory = Memory::new(&mut heap).unwrap();
530
531 let list = memory
532 .cons(Number::from_i64(1).into(), memory.null())
533 .unwrap();
534
535 assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
536 assert_snapshot!(memory);
537
538 let list = memory.cons(Number::from_i64(2).into(), list).unwrap();
539
540 assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
541 assert_snapshot!(memory);
542
543 let list = memory.cons(Number::from_i64(3).into(), list).unwrap();
544
545 assert_eq!(memory.cdr(list).tag(), Type::Pair as Tag);
546 assert_snapshot!(memory);
547 }
548
549 #[test]
550 fn convert_false() {
551 let mut heap = create_heap();
552 let memory = Memory::new(&mut heap).unwrap();
553
554 assert_eq!(
555 Value::from(memory.boolean(false)).to_cons().unwrap(),
556 memory.boolean(false)
557 );
558 }
559
560 #[test]
561 fn convert_true() {
562 let mut heap = create_heap();
563 let memory = Memory::new(&mut heap).unwrap();
564
565 assert_eq!(
566 Value::from(memory.boolean(true)).to_cons().unwrap(),
567 memory.boolean(true)
568 );
569 }
570
571 #[test]
572 fn convert_null() {
573 let mut heap = create_heap();
574 let memory = Memory::new(&mut heap).unwrap();
575
576 assert_eq!(Value::from(memory.null()).to_cons().unwrap(), memory.null());
577 }
578
579 mod stack {
580 use super::*;
581
582 #[test]
583 fn push_and_pop() {
584 let mut heap = create_heap();
585 let mut memory = Memory::new(&mut heap).unwrap();
586
587 memory.stack = memory.null();
588 memory.push(Number::from_i64(42).into()).unwrap();
589
590 assert_eq!(memory.pop(), Number::from_i64(42).into());
591 }
592
593 #[test]
594 fn push_and_pop_twice() {
595 let mut heap = create_heap();
596 let mut memory = Memory::new(&mut heap).unwrap();
597
598 memory.stack = memory.null();
599 memory.push(Number::from_i64(1).into()).unwrap();
600 memory.push(Number::from_i64(2).into()).unwrap();
601
602 assert_eq!(memory.pop(), Number::from_i64(2).into());
603 assert_eq!(memory.pop(), Number::from_i64(1).into());
604 }
605 }
606
607 mod garbage_collection {
608 use super::*;
609
610 #[test]
611 fn collect_cons() {
612 let mut heap = create_heap();
613 let mut memory = Memory::new(&mut heap).unwrap();
614
615 memory
616 .allocate(Number::default().into(), Number::default().into())
617 .unwrap();
618 memory.collect_garbages(None).unwrap();
619
620 assert_snapshot!(memory);
621 }
622
623 #[test]
624 fn collect_stack() {
625 let mut heap = create_heap();
626 let mut memory = Memory::new(&mut heap).unwrap();
627
628 memory.stack = memory.null();
629 memory.push(Number::from_i64(42).into()).unwrap();
630 memory.collect_garbages(None).unwrap();
631
632 assert_snapshot!(memory);
633 }
634
635 #[test]
636 fn collect_deep_stack() {
637 let mut heap = create_heap();
638 let mut memory = Memory::new(&mut heap).unwrap();
639
640 memory.stack = memory.null();
641 memory.push(Number::from_i64(1).into()).unwrap();
642 memory.push(Number::from_i64(2).into()).unwrap();
643 memory.collect_garbages(None).unwrap();
644
645 assert_snapshot!(memory);
646 }
647
648 #[test]
649 fn collect_cycle() {
650 let mut heap = create_heap();
651 let mut memory = Memory::new(&mut heap).unwrap();
652
653 let cons = memory
654 .allocate(Number::default().into(), Number::default().into())
655 .unwrap();
656 memory.set_cdr(cons, cons.into());
657
658 memory.collect_garbages(None).unwrap();
659
660 assert_snapshot!(memory);
661 }
662 }
663}