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)