/* 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_ptr())).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::*;
struct GcTester<'a>(&'a mut bool);
// clone implementation leaks memory... but its for test
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")
}
}
// in non test code Id have to get the box back
impl Drop for GcTester<'_> {
fn drop(&mut self) {
*self.0 = true;
}
}
#[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)
}
// TODO: test deep copy
}