pub mod functions; use std::collections::HashMap; use super::compiler::{Command, CompiledReid, HeapID}; use super::errors::RuntimePanic; pub use functions::*; pub struct VirtualMachine { registry_stack: Vec<[Option; 8]>, registry: [Option; 8], stack: Vec, heap: HashMap, functions: Vec, position: usize, compiled: CompiledReid, } impl VirtualMachine { pub fn from(compiled: CompiledReid) -> VirtualMachine { VirtualMachine { registry_stack: Vec::new(), registry: Default::default(), stack: Vec::new(), heap: HashMap::new(), functions: Vec::new(), position: 0, compiled, } } pub fn add_builtin_functions>>(&mut self, list: T) { for func in list.into() { self.functions.push(FunctionDef::Builtin(func)); } } pub fn run(&mut self) -> Result<(), RuntimePanic> { let mut error = None; while error.is_none() { if let Some(command) = self.compiled.list.get(self.position).map(|v| v.clone()) { self.position += 1; if let Err(err) = self.execute(command.clone()) { error = Some(err); } if self.remaining() == 0 { break; } } else { error = Some(RuntimePanic::InvalidCommandIdx(self.position)); } } if let Some(error) = error { Err(error) } else { Ok(()) } } fn remaining(&self) -> usize { let len = self.compiled.list.len(); len - self.position.max(0).min(len) } fn execute(&mut self, command: Command) -> Result<(), RuntimePanic> { match command { Command::InitializeVariable(heapid, vtype) => { self.heap.insert(heapid, AllocatedVar(vtype, None)); //dbg!("Initialized new variable to heap", &self.heap); Ok(()) } Command::BeginScope => { self.registry_stack.push(self.registry.clone()); self.registry = Default::default(); //dbg!("Begun new scope"); Ok(()) } Command::EndScope => { if let Some(reg) = self.registry_stack.pop() { self.registry = reg; //dbg!("Scope ended"); Ok(()) } else { Err(RuntimePanic::ScopeStackUnderflow) } } Command::Pop(regid) => { if let Some(val) = self.stack.pop() { self.registry[regid] = Some(val); //dbg!("Registry popped", regid, &self.stack, &self.registry); Ok(()) } else { Err(RuntimePanic::StackUnderflow) } } Command::Push(regid) => { if let Some(reg) = &self.registry[regid] { if self.stack.len() < usize::MAX { self.stack.push(reg.clone()); //dbg!("Registry pushed", regid, &self.stack); Ok(()) } else { Err(RuntimePanic::StackOverflow) } } else { Err(RuntimePanic::RegistryNotDefined) } } Command::AssignVariable(heapid, regid) => { if let Some(reg) = &self.registry[regid] { if let Some(var) = self.heap.get_mut(&heapid) { var.try_set(Some(reg.clone()))?; //dbg!("Variable assigned", heapid, regid, &self.heap); Ok(()) } else { Err(RuntimePanic::InvalidHeapAddress) } } else { Err(RuntimePanic::RegistryNotDefined) } } Command::VarToReg(heapid, regid) => { if let Some(var) = self.heap.get(&heapid) { if let Some(val) = &var.1 { self.registry[regid] = Some(val.clone()); //dbg!("Variable pushed to registry", heapid, regid, &self.registry); Ok(()) } else { Err(RuntimePanic::ValueNotInitialized) } } else { Err(RuntimePanic::InvalidHeapAddress) } } Command::StringLit(string) => { if self.stack.len() < usize::MAX { self.stack.push(Value::StringVal(string.clone())); //dbg!("String literal added to stack", string, &self.stack); Ok(()) } else { Err(RuntimePanic::StackOverflow) } } Command::I32Lit(val) => { if self.stack.len() < usize::MAX { self.stack.push(Value::I32Val(val)); Ok(()) } else { Err(RuntimePanic::StackOverflow) } } Command::FunctionCall(funcid) => { if self.functions.len() <= funcid { Err(RuntimePanic::InvalidFuncAddress) } else { match &self.functions[funcid] { FunctionDef::Builtin(f) => { let mut params = Vec::new(); for _ in 0..f.signature.parameters.len() { if let Some(val) = self.stack.pop() { params.push(val); } else { return Err(RuntimePanic::StackUnderflow); } } (f.function)(params); } } Ok(()) } } } } } #[derive(Clone, Debug)] struct AllocatedVar(VariableType, Option); impl AllocatedVar { fn try_set(&mut self, new_val: Option) -> Result<(), RuntimePanic> { if let Some(val) = new_val { if val.get_type() == self.0 { self.1 = Some(val); Ok(()) } else { Err(RuntimePanic::InvalidTypeAssign) } } else { self.1 = None; Ok(()) } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Value { StringVal(String), I32Val(i32), } impl Value { fn get_type(&self) -> VariableType { match self { Value::StringVal(_) => VariableType::TypeString, Value::I32Val(_) => VariableType::TypeI32, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum VariableType { TypeString, TypeI32, } impl ToString for VariableType { fn to_string(&self) -> String { match self { VariableType::TypeString => "String".to_string(), VariableType::TypeI32 => "i32".to_string(), } } }