pub mod op; pub mod frame; use crate::{ obj::prelude::*, vm::{ op::Op, frame::Frame, } }; use std::rc::Rc; pub struct State { frames: Vec, } impl State { pub fn new() -> Self { State { frames: Default::default(), } } fn frame(&self) -> &Frame { self.frames .last() .unwrap() } fn frame_mut(&mut self) -> &mut Frame { self.frames .last_mut() .unwrap() } fn push_stack(&mut self, value: DynRef) { self.frame_mut() .push(value); } fn pop_stack(&mut self) -> Option { self.frame_mut() .pop() } fn ip(&self) -> usize { self.frame().ip() } fn set_ip(&mut self, ip: usize) { self.frame_mut().set_ip(ip); } fn get_local(&self, sym: Sym) -> Option { self.frame().get_local(sym) } fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option { self.frame_mut().set_local(sym, obj_ref) } pub fn run(&mut self, ops: Rc>) { self.frames.push(Default::default()); while self.ip() < ops.len() { let mut next_ip = self.ip() + 1; match &ops[self.ip()] { Op::Push(sym) => { // TODO - local not found let obj_ref = self.get_local(*sym) .expect("TODO - local not found"); self.push_stack(obj_ref); } Op::Pop(sym) => { // stack should not underflow let obj_ref = self.pop_stack() .expect("misaligned stack for pop"); if let Some(sym) = *sym { self.set_local(sym, obj_ref); } } Op::GetAttr(sym) => { let top = self.pop_stack() .expect("misaligned stack for getattrs"); let obj_ref = { let top_ref = top.borrow(); let attrs = top_ref.attrs(); let attrs_ref = attrs.borrow(); // TODO - local not found attrs_ref.get(*sym) .expect("TODO - local not found") }; self.push_stack(obj_ref); } Op::Call(argc) => { let fun = self.pop_stack() .expect("misaligned stack for function call"); let mut argv = Vec::with_capacity(*argc); for i in 0 .. *argc { let arg = self.pop_stack() .expect("misaligned stack for argv"); argv.push(arg); } // reverse since arguments are pushed in order of being passed argv.reverse(); // call function // TODO - call the function indirectly? // // problem: downcast_ref returns a reference bound by the lifetime of the // function (because of the ref cell). This keeps the fun object borrowed, // which means any attempts to modify the function while it is running will // cause the VM to panic. // // possible solution: // pop the function object, and then determine which function to call on the // object (possibly after downcasting?) // // TODO // Figure out a way to either call a native function with NativeFun::call, or // call a user function with Fun::code() if let Some(fun) = fun.borrow().as_any().downcast_ref::() { } else if let Some(fun) = fun.borrow().as_any().downcast_ref::() { } else { todo!("TODO - not a function") } todo!() } } self.set_ip(next_ip); } self.frames.pop(); } }