pub mod consts; pub mod error; pub mod frame; pub mod inst; pub mod package; use crate::{ obj::{reserved::*, prelude::*}, vm::{error::*, frame::*, inst::*, package::*}, }; use shredder::{GcSafe, Scanner, Scan}; #[derive(Debug)] pub struct Vm<'p> { stack: Vec, frames: Vec, pc: usize, condition: bool, package: &'p Package, } unsafe impl Scan for Vm<'_> { fn scan(&self, scanner: &mut Scanner) { scanner.scan(&self.stack); scanner.scan(&self.frames); } } unsafe impl GcSafe for Vm<'_> {} impl<'p> Vm<'p> { pub fn new(package: &'p Package) -> Self { Self { stack: Default::default(), frames: vec![], pc: 0, condition: false, package, } } /// Gets the package that is currently loaded by this VM. pub fn package(&self) -> &'p Package { self.package } /// Gets the current stack frame, if any. pub fn frame(&self) -> Option<&Frame> { self.frames.last() } /// Gets the current stack frame mutably, if any. pub fn frame_mut(&mut self) -> Option<&mut Frame> { self.frames.last_mut() } /// Gets the list of stack frames. pub fn frames(&self) -> &Vec { &self.frames } /// Gets the stack. pub fn stack(&self) -> &Vec { &self.stack } /// Gets the stack, mutably. pub fn stack_mut(&mut self) -> &mut Vec { &mut self.stack } /// Pushes a value to the stack. pub fn push(&mut self, value: ObjRef) { self.stack_mut().push(value); } /// Pops a value from the stack. pub fn pop(&mut self) -> Option { self.stack_mut().pop() } /// Gets the current program counter address. pub fn pc(&self) -> usize { self.pc } /// Set the next program counter value. /// /// This may cause the running program to crash. Handle with care. pub fn set_pc(&mut self, pc: usize) { self.pc = pc; } /// Gets whether the condition flag has been set or not. pub fn condition(&self) -> bool { self.condition } /// Sets the condition flag to the specified value. pub fn set_condition(&mut self, condition: bool) { self.condition = condition; } /// Run a single instruction. pub fn tick(&mut self) -> Result<()> { self.do_tick() } /// Gets the current instruction. #[inline(always)] fn load_inst(&self) -> Inst { if let FrameKind::User { fun, .. } = self.frame().expect("frame").kind() { read_obj!(let fun = fun); fun.code()[self.pc()] } else { panic!("invalid stack frame"); } } /// Run a single instruction - inlined version. /// /// Since this is inlined, it is probably a bad idea to allow users to use it everywhere. The /// exposed API function `tick()` calls this function, but does not inline itself (unless the /// optimizer thinks it's a good idea to do so). #[inline] fn do_tick(&mut self) -> Result<()> { let inst = self.load_inst(); let mut next_pc = self.pc() + 1; match inst { Inst::PushSym(sym) => { let sym_ref = global_sym_ref(sym); self.push(sym_ref); } Inst::PushConst(hdl) => { let obj_ref = self.package() .const_pool() .get(hdl) .clone(); self.push(obj_ref); } Inst::LoadName(_sym) => todo!(), Inst::Pop(Some(_sym)) => todo!(), Inst::Pop(None) => todo!(), Inst::GetAttr(sym) => { let obj_ref = self.pop().expect("getattr object"); read_obj!(let obj = obj_ref); let attr = obj .get_attr(sym) .unwrap_or_else(|| global_sym_ref(NIL_NAME.sym)); self.push(attr); } Inst::SetAttr(_sym) => todo!(), Inst::Jump(addr) => { next_pc = addr; } Inst::JumpTrue(addr) => { if self.condition { next_pc = addr; } } Inst::Call(_argc) => todo!(), Inst::Index => todo!(), Inst::Return => todo!(), Inst::UnNeg => todo!(), Inst::UnPos => todo!(), Inst::BinPlus => todo!(), Inst::BinMinus => todo!(), Inst::BinMul => todo!(), Inst::BinDiv => todo!(), Inst::BinEq => todo!(), Inst::BinNeq => todo!(), Inst::BinLt => todo!(), Inst::BinLe => todo!(), Inst::BinGt => todo!(), Inst::BinGe => todo!(), Inst::BinAnd => todo!(), Inst::BinOr => todo!(), } self.set_pc(next_pc); //let mut next_index = self. Ok(()) } }