diff --git a/Cargo.lock b/Cargo.lock index 7e5eb99..ecdcdf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,6 +166,7 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" name = "mycelium" version = "0.1.0" dependencies = [ + "hyphae", "organelle", ] diff --git a/hyphae/instructions.toml b/hyphae/instructions.toml index fa85567..c65a533 100644 --- a/hyphae/instructions.toml +++ b/hyphae/instructions.toml @@ -53,7 +53,13 @@ description = "delete current stack frame" name = "load" args = ["src", "dest"] output = "" -description = "copies src into dest" +description = "shallow copies src into dest" + +[[instructions]] +name = "dupl" +args = ["src", "dest"] +output = "" +description = "deep copies src into dest" [[instructions]] name = "clear" @@ -247,6 +253,12 @@ args = ["src"] output = "" description = "mutates a number datum into its inexact form" +[[instructions]] +name = "const" +args = ["dst", "data"] +output = "" +description = "sets dst location to constant integer data" + [[instructions]] name = "mkvec" args = [] @@ -300,4 +312,3 @@ name = "cdr" args = ["list"] output = "returns last element in cons cell" description = "takes an AST and returns last element in top level cons cell" - diff --git a/hyphae/src/heap.rs b/hyphae/src/heap.rs index b1b49ae..f49e9a1 100644 --- a/hyphae/src/heap.rs +++ b/hyphae/src/heap.rs @@ -15,123 +15,170 @@ * along with this program. If not, see . */ -use core::fmt::{self, Formatter}; -use core::ops::Index; +use core::ops::{Index, Deref, DerefMut}; +use core::ptr::NonNull; use core::cell::RefCell; -use alloc::format; use alloc::rc::Rc; use alloc::vec::Vec; +use alloc::boxed::Box; use alloc::string::String; use organelle::Number; -#[derive(Default, Clone, PartialEq)] +/* 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: Datum) -> 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 { + drop(Box::from_raw(self.0.as_ptr() as *mut Box>)) + } + } +} + +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(Clone, PartialEq)] pub enum Datum { Number(Number), Bool(bool), - List(Rc), + Cons(Cons), Symbol(String), Char(u8), String(Vec), - Vector(RefCell>>), + Vector(RefCell>>), ByteVector(RefCell>), - #[default] - None, + None } -fn byte_to_escaped_char(b: u8) -> String { - // alarm, backspace, delete - match b { - _ if b > 31 && b < 127 => String::from(b as char), - _ => format!("x{:x}", b), +#[derive(Clone, PartialEq)] +pub struct Cons(pub Option>, pub Option>); + +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())) } -} -fn fmt_vec(ve: &RefCell>) -> String { - let v = ve.borrow(); - if v.len() == 0 { - return String::new() - } - let mut s = format!("{}", v[0]); - let mut i = v.iter(); - i.next(); // discard - i.for_each(|e| { - s = format!("{} {}", s, e); - }); - - s -} - -impl fmt::Display for Datum { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Datum::Number(n) => write!(f, "{}", Into::::into(*n)), - Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}), - Datum::List(n) => write!(f, "{n}"), - Datum::Symbol(n) => write!(f, "{n}"), - Datum::Char(n) => write!(f, "#\\{}", - byte_to_escaped_char(*n)), - Datum::String(n) => - write!(f, "\"{}\"", String::from_utf8_lossy(&*n)), - Datum::Vector(n) => write!(f, "#({})", fmt_vec(n)), - Datum::ByteVector(n) => write!(f, "#u8({})", fmt_vec(n)), - Datum::None => Ok(()) - } - } -} - -/* WARNING - * This is in a sense overloaded. - * Instead of using this to print debugging information for the - * Rust code, I have instead overloaded it to print the most - * maximal expanded valid syntax for this Datum - */ -impl fmt::Debug for Datum { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Datum::Number(n) => write!(f, "{}", Into::::into(*n)), - Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}), - Datum::List(n) => write!(f, "{n}"), - Datum::Char(n) => write!(f, "{}", - byte_to_escaped_char(*n)), - Datum::Symbol(n) => write!(f, "{n}"), - Datum::String(n) => - write!(f, "\"{}\"", String::from_utf8_lossy(&*n)), - Datum::Vector(n) => write!(f, "#({n:?})"), - Datum::ByteVector(n) => write!(f, "#u8({n:?})"), - Datum::None => Ok(()) - } - } -} - - -#[derive(Default, Clone, PartialEq)] -pub struct Ast(pub Rc, pub Rc); - -impl Ast { - pub fn subsl(&self, start: isize, end: isize) -> Ast { + pub fn subsl(&self, start: isize, end: isize) -> Cons { if end - start == 1 { - return Ast(Rc::from(self[start as usize].clone()), Rc::from(Datum::None)) + return Cons(Some(self[start as usize].clone()), None) } if end == 0 { - return Ast( - Rc::from((*(self.0)).clone()), - Rc::from(Datum::None) + return Cons( + self.0.clone(), + None ) } - let Datum::List(ref next) = *self.1 else { - panic!("index into improper list form") + 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 { - Ast( - Rc::from((*(self.0)).clone()), - Rc::from(Datum::List( - Rc::from(next.subsl(start - 1, end - 1)))) - ) + Cons(self.0.clone(), + Some(Datum::Cons(next.subsl(start - 1, end - 1)) + .into())) } else { next.subsl(start - 1, end - 1) @@ -139,81 +186,43 @@ impl Ast { } pub fn len(&self) -> usize { - let Datum::List(ref next) = *self.1 else { + 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() } } -impl Iterator for Ast { - type Item = Rc; - fn next(&mut self) -> Option { - if let Datum::List(n) = &*self.1 { - let tmp_pair = n; - self.0 = tmp_pair.0.clone(); - self.1 = tmp_pair.1.clone(); - return Some(self.0.clone()); - } - - if let Datum::None = *self.1 { - return None; - } - - let tmp = self.1.clone(); - self.0 = Rc::from(Datum::None); - self.1 = Rc::from(Datum::None); - return Some(tmp); - } -} - -impl Index for Ast { - type Output = Datum; +impl Index for Cons { + type Output = Gc; fn index(&self, index: usize) -> &Self::Output { if index == 0 { - if let Datum::None = *self.0 { - panic!("out of bounds indexing into AST") + if let Some(data) = &self.0 { + data } else { - self.0.as_ref() + panic!("out of bounds indexing into cons list") } } else { - let Datum::List(ref next) = *self.1 else { - panic!("out of bounds indexing into AST") + let Some(ref next) = self.1 else { + panic!("out of bounds indexing into cons list") }; - next.index(index - 1) + let Datum::Cons(ref next) = **next else { + panic!("cons list not in standard form") + }; + + &next[index - 1] } } } - -impl fmt::Display for Ast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}", self.0)?; - let mut cur = self; - while let Datum::List(next) = &*cur.1 { - cur = &next; - write!(f, " {}", cur.0)?; - } - - if let Datum::None = &*cur.1 { - write!(f, ")") - } else { - write!(f, " . {})", cur.1) - } - } -} - -impl fmt::Debug for Ast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}", self.0)?; - let mut cur = self; - let mut end = 1; - while let Datum::List(next) = &*cur.1 { - cur = &next; - end += 1; - write!(f, "({} . ", cur.0)? - } - write!(f, "{}{}", cur.1, ")".repeat(end)) - } -} diff --git a/hyphae/src/vm.rs b/hyphae/src/vm.rs index 8a932fa..5245af9 100644 --- a/hyphae/src/vm.rs +++ b/hyphae/src/vm.rs @@ -22,12 +22,11 @@ use crate::hmap::QuickMap; use crate::stackstack::StackStack; use crate::instr as i; use crate::util::{Operand, Program, Address}; -use crate::heap::Datum; +use crate::heap::{Gc, Datum}; use core::cell::RefCell; use alloc::vec; -use alloc::rc::Rc; use alloc::vec::Vec; use alloc::sync::Arc; use alloc::borrow::ToOwned; @@ -39,20 +38,20 @@ const NUM_OPERAND_REGISTERS: usize = 4; pub struct VM { // execution environment - pub stack: StackStack, + pub stack: StackStack>, pub symtab: QuickMap, pub prog: Program, pub fds: Vec, pub traps: Vec>, // data registers - pub expr: Datum, - pub oper: [Datum; NUM_OPERAND_REGISTERS], + pub expr: Gc, + pub oper: [Gc; NUM_OPERAND_REGISTERS], // control flow registers pub retn: usize, pub ictr: usize, - pub errr: Datum, + pub errr: Gc, // state pub running: bool, @@ -86,13 +85,14 @@ impl VM { { self.running = false; self.err_state = true; - self.errr = Datum::String($err.as_bytes().to_vec()); + self.errr = Datum::String($err.as_bytes().to_vec()).into(); return; } } } - macro_rules! deref { + // get or set according to addressing mode + macro_rules! access { ( $oper:expr ) => { match $oper.0 { Address::Expr => &self.expr, @@ -101,23 +101,19 @@ impl VM { Address::Oper3 => &self.oper[2], Address::Oper4 => &self.oper[3], Address::Stack => &self.stack[$oper.1], - Address::Numer => e!("attempt to dereference constant numeric data"), + Address::Numer => e!("cannot access constant numeric"), Address::Instr => e!("bad access to instruction data"), } - } - } + }; - macro_rules! deref_mut { - ( $oper:expr ) => { - match $oper.0 { - Address::Expr => &mut self.expr, - Address::Oper1 => &mut self.oper[0], - Address::Oper2 => &mut self.oper[1], - Address::Oper3 => &mut self.oper[2], - Address::Oper4 => &mut self.oper[3], - Address::Instr => e!("bad mutable access to instruction data"), - // Stack, Numer - _ => e!("mutable access to immutable data"), + ( $data:expr, $target:expr ) => { + match $data.0 { + Address::Expr => self.expr = $target, + Address::Oper1 => self.oper[0] = $target, + Address::Oper2 => self.oper[1] = $target, + Address::Oper3 => self.oper[2] = $target, + Address::Oper4 => self.oper[3] = $target, + _ => e!("attempted mutation of immutable address"), } } } @@ -138,13 +134,13 @@ impl VM { macro_rules! lr_oper { ( $in_type:ident, $oper:tt, $out_type:ident ) => { - self.expr = Datum::$out_type(*match deref!(&instr.1[0]){ + self.expr = Datum::$out_type(match **access!(&instr.1[0]){ Datum::$in_type(l) => l, _ => e!("illegal argument to instruction"), - } $oper *match deref!(&instr.1[1]){ + } $oper match **access!(&instr.1[1]){ Datum::$in_type(l) => l, _ => e!("illegal argument to instruction"), - }) + }).into() } } @@ -163,38 +159,40 @@ impl VM { // symtable ops i::BIND => { - let Datum::String(tag) = deref!(&instr.1[0]) else { + let Datum::String(ref tag) = **access!(&instr.1[0]) else { e!("illegal argument to BIND instruction"); }; - let tag = unsafe { str::from_utf8_unchecked(&tag).to_owned() }; + let tag = unsafe { str::from_utf8_unchecked(tag).to_owned() }; self.symtab.insert(tag, instr.1[1].clone()); }, i::UNBIND => { - let Datum::String(tag) = deref!(&instr.1[0]) else { + let Datum::String(ref tag) = **access!(&instr.1[0]) else { e!("illegal argument to UNBIND instruction"); }; - let tag = unsafe { str::from_utf8_unchecked(&tag) }; - self.symtab.remove(&tag); + let tag = unsafe { str::from_utf8_unchecked(tag) }; + self.symtab.remove(tag); }, i::BOUND => { - let Datum::String(tag) = deref!(&instr.1[0]) else { + let Datum::String(ref tag) = **access!(&instr.1[0]) else { e!("illegal argument to BOUND instruction"); }; - let tag = unsafe { str::from_utf8_unchecked(&tag) }; - self.symtab.contains_key(&tag); + let tag = unsafe { str::from_utf8_unchecked(tag) }; + self.symtab.contains_key(tag); }, // stack ops - i::PUSH => self.stack.push_current_stack(deref!(&instr.1[0]).clone()), + i::PUSH => self.stack.push_current_stack( + access!(&instr.1[0]).clone()), i::POP => _ = self.stack.pop_current_stack(), i::ENTER => self.stack.add_stack(), i::EXIT => self.stack.destroy_top_stack(), // movement ops - i::LOAD => *deref_mut!(&instr.1[1]) = deref!(&instr.1[0]).clone(), - i::CLEAR => *deref_mut!(&instr.1[0]) = Datum::None, + i::LOAD => access!(&instr.1[1], access!(&instr.1[0]).clone()), + i::DUPL => access!(&instr.1[1], access!(&instr.1[0]).deep_copy()), + i::CLEAR => access!(&instr.1[0], Datum::None.into()), // control flow ops i::NOP => (), @@ -202,7 +200,7 @@ impl VM { i::PANIC => { self.running = false; self.err_state = false; - self.errr = deref!(&instr.1[0]).clone() + self.errr = access!(&instr.1[0]).clone(); }, i::JMP => { @@ -210,24 +208,25 @@ impl VM { }, i::JMPIF => { - if let Datum::Bool(true) = self.expr { + if let Datum::Bool(true) = *self.expr { do_jmp!(0); } }, // boolean ops - i::EQ => self.expr = Datum::Bool(*deref!(&instr.1[0]) == *deref!(&instr.1[1])), + i::EQ => self.expr = + Datum::Bool(*access!(&instr.1[0]) == *access!(&instr.1[1])).into(), i::LT => lr_oper!(Number, <, Bool), i::GT => lr_oper!(Number, >, Bool), i::LTE => lr_oper!(Number, <=, Bool), i::GTE => lr_oper!(Number, >=, Bool), i::BOOL_NOT => { self.expr = Datum::Bool(!{ - let Datum::Bool(a) = self.expr else { + let Datum::Bool(a) = *self.expr else { e!("illegal argument to BOOL_NOT instruction"); }; a - }); + }).into(); }, i::BOOL_AND => lr_oper!(Bool, &&, Bool), @@ -239,11 +238,11 @@ impl VM { i::XOR => lr_oper!(Char, ^, Char), i::BYTE_NOT => { self.expr = Datum::Char(!{ - let Datum::Char(a) = self.expr else { + let Datum::Char(a) = *self.expr else { e!("illegal argument to BYTE_NOT instruction"); }; a - }); + }).into(); }, // numeric ops @@ -252,11 +251,11 @@ impl VM { i::MUL => lr_oper!(Number, *, Number), i::FDIV => lr_oper!(Number, /, Number), i::IDIV => { - let Datum::Number(l) = deref!(&instr.1[0]) else { + let Datum::Number(ref l) = **access!(&instr.1[0]) else { e!("illegal argument to IDIV instruction"); }; - let Datum::Number(r) = deref!(&instr.1[1]) else { + let Datum::Number(ref r) = **access!(&instr.1[1]) else { e!("illgal argument to IDIV instruction"); }; @@ -268,61 +267,87 @@ impl VM { e!("integer division on non integer value"); }; - self.expr = Datum::Number(Number::Fra(Fraction(l / r, 1))); + self.expr = Datum::Number(Number::Fra(Fraction(l / r, 1))).into(); }, i::POW => { - let Datum::Number(l) = deref!(&instr.1[0]) else { + let Datum::Number(ref l) = **access!(&instr.1[0]) else { e!("illegal argument to POW instruction"); }; - let Datum::Number(r) = deref!(&instr.1[1]) else { + let Datum::Number(ref r) = **access!(&instr.1[1]) else { e!("illgal argument to POW instruction"); }; - self.expr = Datum::Number((*l).pow(*r)); + self.expr = Datum::Number(l.clone().pow(r.clone())).into(); }, - i::INC => if let Datum::Number(src) = deref_mut!(&instr.1[0]) { - *src = *src + Number::Fra(Fraction(1, 1)); - } else { + i::INC => access!(&instr.1[0], { + if let Datum::Number(src) = **access!(&instr.1[0]) { + Datum::Number(src + Number::Fra(Fraction(1, 1))).into() + } else { e!("illegal argument to INC instruction"); - }, + } + }), - i::DEC => if let Datum::Number(src) = deref_mut!(&instr.1[0]) { - *src = *src - Number::Fra(Fraction(1, 1)); - } else { + i::DEC => access!(&instr.1[0], { + if let Datum::Number(src) = **access!(&instr.1[0]) { + Datum::Number(src - Number::Fra(Fraction(1, 1))).into() + } else { e!("illegal argument to INC instruction"); - }, + } + }), // byte/char to and from number conversions - i::CTON => { - let src = deref_mut!(&instr.1[0]); - if let Datum::Char(schr) = src { - *src = Datum::Number(Number::Fra(Fraction(*schr as isize, 1))); + i::CTON => access!(&instr.1[0], { + if let Datum::Char(schr) = **access!(&instr.1[0]) { + Datum::Number(Number::Fra(Fraction(schr as isize, 1))).into() } else { - e!("illegal argument to CTON instruction"); + e!("illegal argument to INC instruction"); } - }, + }), - i::NTOC => { - let src = deref_mut!(&instr.1[0]); - if let Datum::Number(snum) = src { + i::NTOC => access!(&instr.1[0], { + if let Datum::Number(snum) = **access!(&instr.1[0]) { let n = snum.make_inexact(); - if !snum.is_exact() || n.0.fract() != 0.0 || n.0 > u8::MAX.into() || n.0 < 0.0 { - e!("input to NTOC cannot cleanly convert"); - } - *src = Datum::Char(n.0.trunc() as u64 as u8); - + if !snum.is_exact() || n.0.fract() != 0.0 || + n.0 > u8::MAX.into() || n.0 < 0.0 { + e!("input to NTOC cannot cleanly convert"); + } + Datum::Char(n.0.trunc() as u64 as u8).into() } else { - e!("illegal argument to NTOC instruction"); + e!("illegal argument to INC instruction"); + } + }), + + i::NTOI => { + let src = access!(&instr.1[0]); + if let Datum::Number(snum) = **src { + access!(&instr.1[0], + Datum::Number(snum.make_inexact().into()).into()) } }, - i::MKVEC => self.expr = Datum::Vector(RefCell::from(vec![])), - i::MKBVEC => self.expr = Datum::ByteVector(RefCell::from(vec![])), + i::NTOE => { + let src = access!(&instr.1[0]); + if let Datum::Number(snum) = **src { + access!(&instr.1[0], Datum::Number(snum.make_inexact().into()) + .into()) + } + }, + + i::CONST => access!(&instr.1[0], { + let Operand(Address::Numer, num) = instr.1[0] else { + e!("illegal argument to CONST instruction"); + }; + + Datum::Number(Number::Fra(Fraction(num as isize, 1))).into() + }), + + i::MKVEC => self.expr = Datum::Vector(RefCell::from(vec![])).into(), + i::MKBVEC => self.expr = Datum::ByteVector(RefCell::from(vec![])).into(), i::INDEX => { - let Datum::Number(idx) = deref!(&instr.1[1]) else { + let Datum::Number(ref idx) = **access!(&instr.1[1]) else { e!("illegal argument to INDEX instruction"); }; let idx = idx.make_inexact(); @@ -331,40 +356,43 @@ impl VM { } let idx = idx.0.trunc() as usize; - match deref!(&instr.1[0]) { - Datum::Vector(v) => { + match **access!(&instr.1[0]) { + Datum::Vector(ref v) => { let a = (*v.borrow()[idx].clone()).clone(); - self.expr = a; + self.expr = a.into(); }, - Datum::ByteVector(bv) => { + Datum::ByteVector(ref bv) => { let a = Datum::Char(bv.borrow()[idx]); - self.expr = a; + self.expr = a.into(); }, - Datum::List(l) => self.expr = l[idx].clone(), + Datum::Cons(ref l) => self.expr = l[idx].clone(), _ => e!("illegal argument to INDEX instruction") }; }, - i::LENGTH => match deref!(&instr.1[0]) { - Datum::Vector(v) => { - let a = Datum::Number(Number::Fra(Fraction(v.borrow().len() as isize, 1))); - self.expr = a; + i::LENGTH => match **access!(&instr.1[0]) { + Datum::Vector(ref v) => { + let a = Datum::Number(Number::Fra(Fraction( + v.borrow().len() as isize, 1))); + self.expr = a.into(); }, - Datum::ByteVector(bv) => { - let a = Datum::Number(Number::Fra(Fraction(bv.borrow().len() as isize, 1))); - self.expr = a; + Datum::ByteVector(ref bv) => { + let a = Datum::Number(Number::Fra(Fraction( + bv.borrow().len() as isize, 1))); + self.expr = a.into(); }, - Datum::List(l) => - self.expr = Datum::Number(Number::Fra(Fraction(l.len() as isize, 1))), + Datum::Cons(ref l) => self.expr = + Datum::Number(Number::Fra(Fraction(l.len() as isize, 1))) + .into(), _ => e!("illegal argument to LENGTH instruction"), }, i::SUBSL => { - let Datum::Number(st) = deref!(&instr.1[1]) else { + let Datum::Number(ref st) = **access!(&instr.1[1]) else { e!("illegal argument to SUBSL instruction"); }; - let Datum::Number(ed) = deref!(&instr.1[2]) else { + let Datum::Number(ref ed) = **access!(&instr.1[2]) else { e!("illegal argument to SUBSL instruction"); }; @@ -382,24 +410,24 @@ impl VM { let st = st.0.trunc() as usize; let ed = ed.0.trunc() as usize; - match deref!(&instr.1[0]) { - Datum::Vector(v) => { + match **access!(&instr.1[0]) { + Datum::Vector(ref v) => { let a = Datum::Vector(RefCell::from(v.borrow()[st..ed].to_vec())); - self.expr = a; + self.expr = a.into(); }, - Datum::ByteVector(bv) => { + Datum::ByteVector(ref bv) => { let a = Datum::ByteVector(RefCell::from(bv.borrow()[st..ed].to_vec())); - self.expr = a; + self.expr = a.into(); }, - Datum::List(a) => - self.expr = Datum::List(Rc::new( - (**a).subsl(st as isize, ed as isize))), + + Datum::Cons(ref a) => self.expr = + Datum::Cons(a.subsl(st as isize, ed as isize)).into(), _ => e!("illegal argument to SUBSL instruction") }; } i::INSER => { - let Datum::Number(idx) = deref!(&instr.1[2]) else { + let Datum::Number(ref idx) = **access!(&instr.1[2]) else { e!("illegal argument to INSER instruction"); }; @@ -410,34 +438,40 @@ impl VM { let idx = idx.0.trunc() as usize; - match deref!(&instr.1[0]) { - Datum::Vector(v) => { - v.borrow_mut().insert(idx, deref!(&instr.1[1]).clone().into()); + match **access!(&instr.1[0]) { + Datum::Vector(ref v) => { + v.borrow_mut() + .insert(idx, access!(&instr.1[1]) + .deep_copy()); }, - Datum::ByteVector(bv) => { - let Datum::Char(b) = deref!(&instr.1[1]) else { + Datum::ByteVector(ref bv) => { + let Datum::Char(b) = **access!(&instr.1[1]) else { e!("INSER instruction can only insert a byte into a bytevector"); }; - bv.borrow_mut().insert(idx, *b); + bv.borrow_mut().insert(idx, b); }, _ => e!("illegal argument to INSER instruction") } }, i::CAR => { - let Datum::List(arg) = deref!(&instr.1[0]) else { + let Datum::Cons(ref arg) = **access!(&instr.1[0]) else { e!("illegal argument to CAR instruction"); }; - self.expr = (*arg.0).clone(); + self.expr = arg.clone().0 + .or(Some(Datum::None.into())) + .expect("CAR instruction option consistency"); }, i::CDR => { - let Datum::List(arg) = deref!(&instr.1[0]) else { + let Datum::Cons(ref arg) = **access!(&instr.1[0]) else { e!("illegal argument to CAR instruction"); }; - self.expr = (*arg.1).clone(); + self.expr = arg.clone().1 + .or(Some(Datum::None.into())) + .expect("CDR instruction option consistency"); }, i::CONS => { @@ -447,10 +481,6 @@ impl VM { */ }, - // in order to maintain a language agnostic VM these must be traps - //i::PARSE => todo!("implement AST API"), - //i::EVAL => todo!("implement AST API"), - _ => { e!("illegal instruction"); }, diff --git a/mycelium/Cargo.toml b/mycelium/Cargo.toml index 206b304..1ee891e 100644 --- a/mycelium/Cargo.toml +++ b/mycelium/Cargo.toml @@ -5,4 +5,4 @@ edition = "2024" [dependencies] organelle = { path = "../organelle" } - +hyphae = { path = "../hyphae" } diff --git a/mycelium/src/sexpr.rs b/mycelium/src/sexpr.rs index b1b49ae..fbff86f 100644 --- a/mycelium/src/sexpr.rs +++ b/mycelium/src/sexpr.rs @@ -110,34 +110,6 @@ impl fmt::Debug for Datum { pub struct Ast(pub Rc, pub Rc); impl Ast { - pub fn subsl(&self, start: isize, end: isize) -> Ast { - if end - start == 1 { - return Ast(Rc::from(self[start as usize].clone()), Rc::from(Datum::None)) - } - - if end == 0 { - return Ast( - Rc::from((*(self.0)).clone()), - Rc::from(Datum::None) - ) - } - - let Datum::List(ref next) = *self.1 else { - panic!("index into improper list form") - }; - - if start <= 0 { - Ast( - Rc::from((*(self.0)).clone()), - Rc::from(Datum::List( - Rc::from(next.subsl(start - 1, end - 1)))) - ) - - } else { - next.subsl(start - 1, end - 1) - } - } - pub fn len(&self) -> usize { let Datum::List(ref next) = *self.1 else { return 1 diff --git a/organelle/src/lib.rs b/organelle/src/lib.rs index 3703d35..cec5dc5 100644 --- a/organelle/src/lib.rs +++ b/organelle/src/lib.rs @@ -73,6 +73,12 @@ pub enum Number { Sym(SymbolicNumber) } +impl Default for Number { + fn default() -> Self { + Number::Fra(Fraction(0, 1)) + } +} + impl From for Number { fn from(value: SymbolicNumber) -> Self { Number::Sym(value)