219 lines
6.9 KiB
Rust
219 lines
6.9 KiB
Rust
pub mod functions;
|
|
|
|
use std::collections::HashMap;
|
|
use std::fmt;
|
|
use std::fmt::Display;
|
|
|
|
use super::compiler::{Command, CompiledReid, HeapID};
|
|
use super::errors::RuntimePanic;
|
|
|
|
pub use functions::*;
|
|
|
|
pub struct VirtualMachine {
|
|
registry_stack: Vec<[Option<Value>; 8]>,
|
|
registry: [Option<Value>; 8],
|
|
|
|
stack: Vec<Value>,
|
|
heap: HashMap<HeapID, AllocatedVar>,
|
|
functions: Vec<FunctionDef>,
|
|
|
|
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<T: Into<Vec<BuiltinFunctionDef>>>(&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::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<Value>);
|
|
|
|
impl AllocatedVar {
|
|
fn try_set(&mut self, new_val: Option<Value>) -> 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),
|
|
}
|
|
|
|
impl Value {
|
|
fn get_type(&self) -> VariableType {
|
|
match self {
|
|
Value::StringVal(_) => VariableType::TypeString,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum VariableType {
|
|
TypeString,
|
|
}
|
|
|
|
impl ToString for VariableType {
|
|
fn to_string(&self) -> String {
|
|
match self {
|
|
VariableType::TypeString => "String".to_string(),
|
|
}
|
|
}
|
|
}
|