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 d303ce3..f49e9a1 100644 --- a/hyphae/src/heap.rs +++ b/hyphae/src/heap.rs @@ -39,18 +39,6 @@ use organelle::Number; * function to pass around heap allocated Rcs. */ -#[derive(Clone, PartialEq)] -pub enum Datum { - Number(Number), - Bool(bool), - Cons(Cons), - Symbol(String), - Char(u8), - String(Vec), - Vector(RefCell>>), - ByteVector(RefCell>), -} - /* 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 @@ -78,6 +66,7 @@ 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()) } @@ -143,6 +132,19 @@ impl Gc { } } +#[derive(Clone, PartialEq)] +pub enum Datum { + Number(Number), + Bool(bool), + Cons(Cons), + Symbol(String), + Char(u8), + String(Vec), + Vector(RefCell>>), + ByteVector(RefCell>), + None +} + #[derive(Clone, PartialEq)] pub struct Cons(pub Option>, pub Option>); diff --git a/hyphae/src/vm.rs b/hyphae/src/vm.rs index 7e244de..5245af9 100644 --- a/hyphae/src/vm.rs +++ b/hyphae/src/vm.rs @@ -27,7 +27,6 @@ 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,7 +38,7 @@ 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, @@ -52,7 +51,7 @@ pub struct VM { // 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,10 +134,10 @@ 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 => { @@ -217,7 +215,7 @@ impl VM { // boolean ops i::EQ => self.expr = - Datum::Bool(*deref!(&instr.1[0]) == *deref!(&instr.1[1])).into(), + 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), @@ -253,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"); }; @@ -273,57 +271,83 @@ impl VM { }, 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)).into(); + 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::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(); @@ -332,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.into(); }, - Datum::ByteVector(bv) => { + Datum::ByteVector(ref bv) => { let a = Datum::Char(bv.borrow()[idx]); self.expr = a.into(); }, - Datum::Cons(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))); + 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))); + Datum::ByteVector(ref bv) => { + let a = Datum::Number(Number::Fra(Fraction( + bv.borrow().len() as isize, 1))); self.expr = a.into(); }, - Datum::Cons(l) => self.expr = - Datum::Number(Number::Fra(Fraction(l.len() as isize, 1))).into(), + 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"); }; @@ -383,25 +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.into(); }, - Datum::ByteVector(bv) => { + Datum::ByteVector(ref bv) => { let a = Datum::ByteVector(RefCell::from(bv.borrow()[st..ed].to_vec())); self.expr = a.into(); }, - // TODO: do I deep copy the subslice? - Datum::Cons(a) => self.expr = + 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"); }; @@ -412,35 +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::Cons(arg) = deref!(&instr.1[0]) else { + let Datum::Cons(ref arg) = **access!(&instr.1[0]) else { e!("illegal argument to CAR instruction"); }; - // TODO: need a none type dont we now - self.expr = (*arg.0).clone(); + self.expr = arg.clone().0 + .or(Some(Datum::None.into())) + .expect("CAR instruction option consistency"); }, i::CDR => { - let Datum::Cons(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 => {