/* Mycelium Scheme * Copyright (C) 2025 Ava Affine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ use core::ops::{Index, Deref, DerefMut}; use core::ptr::NonNull; use core::cell::RefCell; use alloc::rc::Rc; use alloc::vec::Vec; use alloc::boxed::Box; use alloc::string::String; use organelle::Number; /* NOTE * decided not to implement a cache or a singleton heap manager * because I did not want to involve a datatype that would add * unneeded logic to where and how the Rcs get allocated or that * would require relocation if more Rcs were allocated. Any * ADT containing the source data referenced by Gc would add * overhead without value. * * Meanwhile, just using allocated-at-site Rcs provides accurate * reference counting garbage collection. We hack the Box::into_raw * function to pass around heap allocated Rcs. */ /* Gc * This is a heap allocated Rc passed around such that it fits into * a physical register. The pointer is to a Box>, but custom * deref implementation will ensure that deref always points to the * encapsulated T */ #[repr(transparent)] pub struct Gc(NonNull>); impl From> for Gc { fn from(src: Rc) -> Self { Gc(NonNull::new(Box::into_raw(Box::new(src.clone()))) .expect("GC obj from rc nonnull ptr check")) } } impl From for Gc { fn from(value: T) -> Self { Gc(NonNull::new(Box::into_raw(Box::new(Rc::from(value)))) .expect("GC obj from datum nonnull ptr check")) } } impl PartialEq for Gc { fn eq(&self, other: &Self) -> bool { self.deref().eq(other.deref()) } fn ne(&self, other: &Self) -> bool { self.deref().ne(other.deref()) } } impl Deref for Gc { type Target = T; fn deref(&self) -> &Self::Target { unsafe { Rc::::as_ptr(self.0.as_ref()) .as_ref() .expect("GC obj deref inconsistent rc ptr") } } } impl DerefMut for Gc { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { (Rc::::as_ptr(self.0.as_mut()) as *mut T) .as_mut() .expect("GC obj inconsistent rc ptr") } } } // takes a pointer to target Rc macro_rules! shallow_copy_rc { ( $src:expr ) => { unsafe { NonNull::new(Box::into_raw(Box::new((*$src).clone()))) .expect("GC obj shallow copy nonnull ptr check") } } } impl Clone for Gc { fn clone(&self) -> Self { Gc(shallow_copy_rc!(self.0.as_ptr())) } fn clone_from(&mut self, source: &Self) { self.0 = shallow_copy_rc!(source.0.as_ptr()); } } impl Drop for Gc { fn drop(&mut self) { unsafe { let a = Box::>::from_raw(self.0.as_ptr()); drop(a) } } } impl Gc { #[inline] pub fn deep_copy(&self) -> Gc { Gc(unsafe { NonNull::new(Box::into_raw(Box::new(Rc::from( self.0.as_ref().clone())))) .expect("GC obj deep copy nonnull ptr check") }) } } #[derive(PartialEq)] pub enum Datum { Number(Number), Bool(bool), Cons(Cons), Symbol(String), Char(u8), String(Vec), Vector(RefCell>>), ByteVector(RefCell>), None } // implemented by hand to force deep copy on Cons datum impl Clone for Datum { fn clone(&self) -> Datum { match self { Datum::Number(n) => Datum::Number(n.clone()), Datum::Bool(n) => Datum::Bool(n.clone()), Datum::Cons(n) => Datum::Cons(n.deep_copy()), Datum::Symbol(n) => Datum::Symbol(n.clone()), Datum::Char(n) => Datum::Char(n.clone()), Datum::String(n) => Datum::String(n.clone()), Datum::Vector(n) => Datum::Vector(n.clone()), Datum::ByteVector(n) => Datum::ByteVector(n.clone()), Datum::None => Datum::None, } } fn clone_from(&mut self, source: &Self) { *self = source.clone(); } } #[derive(Clone, PartialEq)] pub struct Cons(pub Option>, pub Option>); impl Cons { pub fn deep_copy(&self) -> Cons { macro_rules! car_cpy { () => { if let Some(ref car) = self.0 { if let Datum::Cons(ref car) = **car { Some(Datum::Cons(car.deep_copy()).into()) } else { self.0.as_ref().map(|x| x.deep_copy()) } } else { None } } } if let Some(ref next) = self.1 { if let Datum::Cons(ref next) = **next { Cons(car_cpy!(), Some(Datum::Cons(next.deep_copy()).into())) } else { Cons(car_cpy!(), self.1.as_ref().map(|x| x.deep_copy())) } } else { Cons(car_cpy!(), None) } } pub fn subsl(&self, start: isize, end: isize) -> Cons { if end - start == 1 { return Cons(Some(self[start as usize].clone()), None) } if end == 0 { return Cons( self.0.clone(), None ) } let Some(ref next) = self.1 else { panic!("out of bounds subsl of cons list") }; let Datum::Cons(ref next) = **next else { panic!("subsl of cons list not in standard form") }; if start <= 0 { Cons(self.0.clone(), Some(Datum::Cons(next.subsl(start - 1, end - 1)) .into())) } else { next.subsl(start - 1, end - 1) } } pub fn len(&self) -> usize { let Some(_) = self.0 else { return 0 }; let Some(ref next) = self.1 else { return 1 }; let Datum::Cons(ref next) = **next else { // weird list but okay return 2 }; 1 + next.len() } pub fn append(&mut self, arg: Gc) { let Some(_) = self.0 else { self.0 = Some(arg); return }; if let Some(next) = &mut self.1 { if let Datum::Cons(next) = next.deref_mut() { next.append(arg); return; } } self.1 = Some(Datum::Cons(Cons(Some(arg), None)).into()); } } impl Index for Cons { type Output = Gc; fn index(&self, index: usize) -> &Self::Output { if index == 0 { if let Some(data) = &self.0 { data } else { panic!("out of bounds indexing into cons list") } } else { let Some(ref next) = self.1 else { panic!("out of bounds indexing into cons list") }; let Datum::Cons(ref next) = **next else { panic!("cons list not in standard form") }; &next[index - 1] } } } #[cfg(test)] mod tests { use super::*; #[derive(Debug)] struct GcTester<'a>(pub &'a mut bool); impl Clone for GcTester<'_> { fn clone(&self) -> Self { unsafe { GcTester(Box::into_raw(Box::new(self.0.clone())) .as_mut() .expect("Gc Test obj mut ref fail")) } } fn clone_from(&mut self, _: &Self) { unimplemented!("test impl") } } impl Drop for GcTester<'_> { fn drop(&mut self) { *self.0 = true; } } impl PartialEq for GcTester<'_> { fn eq(&self, other: &Self) -> bool { *(self.0) == *(other.0) } fn ne(&self, other: &Self) -> bool { *(self.0) != *(other.0) } } #[test] fn test_gc_basic_behavior() { let mut flag = false; let a = Into::>::into(GcTester(&mut flag)); drop(a); assert!(flag) } #[test] fn test_gc_shallow_copy() { let mut flag = false; let a = Into::>::into(GcTester(&mut flag)).clone(); drop(a); assert!(flag) } #[test] fn test_gc_deep_copy() { let mut flag = false; let reference_holder = Into::>::into(GcTester(&mut flag)).clone(); let copied_data = reference_holder.deep_copy(); assert_eq!(*reference_holder, *copied_data); drop(reference_holder); assert!(!*(*copied_data).0); } }