2025-07-24 19:44:43 +00:00
|
|
|
/* 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 <https://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
use core::ops::{Index, Deref, DerefMut};
|
|
|
|
|
use core::ptr::NonNull;
|
2025-07-24 19:44:43 +00:00
|
|
|
use core::cell::RefCell;
|
|
|
|
|
|
|
|
|
|
use alloc::rc::Rc;
|
|
|
|
|
use alloc::vec::Vec;
|
2025-07-26 01:13:13 +00:00
|
|
|
use alloc::boxed::Box;
|
2025-07-24 19:44:43 +00:00
|
|
|
use alloc::string::String;
|
|
|
|
|
|
|
|
|
|
use organelle::Number;
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
/* 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<Rc<T>>, but custom
|
|
|
|
|
* deref implementation will ensure that deref always points to the
|
|
|
|
|
* encapsulated T
|
|
|
|
|
*/
|
|
|
|
|
#[repr(transparent)]
|
|
|
|
|
pub struct Gc<T>(NonNull<Rc<T>>);
|
|
|
|
|
|
|
|
|
|
impl From<Rc<Datum>> for Gc<Datum> {
|
|
|
|
|
fn from(src: Rc<Datum>) -> Self {
|
|
|
|
|
Gc(NonNull::new(Box::into_raw(Box::new(src.clone())))
|
|
|
|
|
.expect("GC obj from rc nonnull ptr check"))
|
|
|
|
|
}
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl From<Datum> for Gc<Datum> {
|
|
|
|
|
fn from(value: Datum) -> Self {
|
|
|
|
|
Gc(NonNull::new(Box::into_raw(Box::new(Rc::from(value))))
|
|
|
|
|
.expect("GC obj from datum nonnull ptr check"))
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl<T: PartialEq> PartialEq for Gc<T> {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.deref().eq(other.deref())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn ne(&self, other: &Self) -> bool {
|
|
|
|
|
self.deref().ne(other.deref())
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl<T> Deref for Gc<T> {
|
|
|
|
|
type Target = T;
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
unsafe {
|
|
|
|
|
Rc::<T>::as_ptr(self.0.as_ref())
|
|
|
|
|
.as_ref()
|
|
|
|
|
.expect("GC obj deref inconsistent rc ptr")
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl<T> DerefMut for Gc<T> {
|
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
|
unsafe {
|
|
|
|
|
(Rc::<T>::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")
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl<T: Clone> Clone for Gc<T> {
|
|
|
|
|
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<T> Drop for Gc<T> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
drop(Box::from_raw(self.0.as_ptr() as *mut Box<Rc<T>>))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Clone> Gc<T> {
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn deep_copy(&self) -> Gc<T> {
|
|
|
|
|
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(Clone, PartialEq)]
|
|
|
|
|
pub enum Datum {
|
|
|
|
|
Number(Number),
|
|
|
|
|
Bool(bool),
|
|
|
|
|
Cons(Cons),
|
|
|
|
|
Symbol(String),
|
|
|
|
|
Char(u8),
|
|
|
|
|
String(Vec<u8>),
|
|
|
|
|
Vector(RefCell<Vec<Gc<Datum>>>),
|
|
|
|
|
ByteVector(RefCell<Vec<u8>>),
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
|
|
|
pub struct Cons(pub Option<Gc<Datum>>, pub Option<Gc<Datum>>);
|
2025-07-24 19:44:43 +00:00
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl Cons {
|
|
|
|
|
pub fn deep_copy(&self) -> Cons {
|
|
|
|
|
// TODO: recursive deep copy through the whole list
|
|
|
|
|
Cons(self.0.as_ref().map(|x| x.deep_copy()),
|
|
|
|
|
self.1.as_ref().map(|x| x.deep_copy()))
|
|
|
|
|
}
|
2025-07-24 19:44:43 +00:00
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
pub fn subsl(&self, start: isize, end: isize) -> Cons {
|
2025-07-24 19:44:43 +00:00
|
|
|
if end - start == 1 {
|
2025-07-26 01:13:13 +00:00
|
|
|
return Cons(Some(self[start as usize].clone()), None)
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if end == 0 {
|
2025-07-26 01:13:13 +00:00
|
|
|
return Cons(
|
|
|
|
|
self.0.clone(),
|
|
|
|
|
None
|
2025-07-24 19:44:43 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
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")
|
2025-07-24 19:44:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if start <= 0 {
|
2025-07-26 01:13:13 +00:00
|
|
|
Cons(self.0.clone(),
|
|
|
|
|
Some(Datum::Cons(next.subsl(start - 1, end - 1))
|
|
|
|
|
.into()))
|
2025-07-24 19:44:43 +00:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
next.subsl(start - 1, end - 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
2025-07-26 01:13:13 +00:00
|
|
|
let Some(_) = self.0 else {
|
|
|
|
|
return 0
|
2025-07-24 19:44:43 +00:00
|
|
|
};
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
let Some(ref next) = self.1 else {
|
|
|
|
|
return 1
|
|
|
|
|
};
|
2025-07-24 19:44:43 +00:00
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
let Datum::Cons(ref next) = **next else {
|
|
|
|
|
// weird list but okay
|
|
|
|
|
return 2
|
|
|
|
|
};
|
2025-07-24 19:44:43 +00:00
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
1 + next.len()
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
|
|
|
|
|
impl Index<usize> for Cons {
|
|
|
|
|
type Output = Gc<Datum>;
|
2025-07-24 19:44:43 +00:00
|
|
|
fn index(&self, index: usize) -> &Self::Output {
|
|
|
|
|
if index == 0 {
|
2025-07-26 01:13:13 +00:00
|
|
|
if let Some(data) = &self.0 {
|
|
|
|
|
data
|
2025-07-24 19:44:43 +00:00
|
|
|
} else {
|
2025-07-26 01:13:13 +00:00
|
|
|
panic!("out of bounds indexing into cons list")
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-07-26 01:13:13 +00:00
|
|
|
let Some(ref next) = self.1 else {
|
|
|
|
|
panic!("out of bounds indexing into cons list")
|
2025-07-24 19:44:43 +00:00
|
|
|
};
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
let Datum::Cons(ref next) = **next else {
|
|
|
|
|
panic!("cons list not in standard form")
|
|
|
|
|
};
|
2025-07-24 19:44:43 +00:00
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
&next[index - 1]
|
2025-07-24 19:44:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|