Add vm and compile modules

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-05-16 12:56:52 -04:00
parent a60471f526
commit a15dde0fc2
13 changed files with 392 additions and 46 deletions

49
src/vm/frame.rs Normal file
View File

@@ -0,0 +1,49 @@
use crate::obj::prelude::*;
use std::collections::BTreeMap;
#[derive(Default)]
pub struct Frame {
stack: Vec<DynRef>,
ip: usize,
return_value: Option<DynRef>,
locals: BTreeMap<Sym, DynRef>,
}
impl Frame {
pub fn new() -> Self {
Default::default()
}
pub fn push(&mut self, obj_ref: DynRef) {
self.stack.push(obj_ref)
}
pub fn pop(&mut self) -> Option<DynRef> {
self.stack.pop()
}
pub fn ip(&self) -> usize {
self.ip
}
pub fn set_ip(&mut self, ip: usize) {
self.ip = ip;
}
pub fn return_value(&self) -> Option<DynRef> {
self.return_value
}
pub fn set_return_value(&mut self, obj_ref: DynRef) {
self.return_value = Some(obj_ref);
}
pub fn get_local(&self, sym: Sym) -> Option<DynRef> {
self.locals.get(&sym).copied()
}
pub fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> {
self.locals.insert(sym, obj_ref)
}
}

131
src/vm/mod.rs Normal file
View File

@@ -0,0 +1,131 @@
pub mod op;
pub mod frame;
use crate::{
obj::prelude::*,
vm::{
op::Op,
frame::Frame,
}
};
use std::rc::Rc;
pub struct State {
frames: Vec<Frame>,
}
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<DynRef> {
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<DynRef> {
self.frame().get_local(sym)
}
fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> {
self.frame_mut().set_local(sym, obj_ref)
}
pub fn run(&mut self, ops: Rc<Vec<Op>>) {
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::<Fun>() {
} else if let Some(fun) = fun.borrow().as_any().downcast_ref::<NativeFun>() {
} else {
todo!("TODO - not a function")
}
todo!()
}
}
self.set_ip(next_ip);
}
self.frames.pop();
}
}

22
src/vm/op.rs Normal file
View File

@@ -0,0 +1,22 @@
use crate::obj::Sym;
// VM execution model:
// * every function call has its own stack frame
// * stack frames keep track of locals
// * ops deal with symbols directly
// * stack values are either obj references or symbols
pub enum Op {
/// Push a value from a symbol
Push(Sym),
/// Pop the top stack value into a local (if supplied)
Pop(Option<Sym>),
/// Pop the top stack value, getting an attribute and replacing pushing that value to the
/// stack.
GetAttr(Sym),
/// Pop the top stack value and the number of arguments, and attempt to call the function.
Call(usize),
}