From b3fa498f7331cd897f0207163eec82a1886f44c5 Mon Sep 17 00:00:00 2001 From: Teascade Date: Tue, 23 Jun 2020 01:13:01 +0300 Subject: [PATCH] Added virtual machine and basic running --- src/compiler.rs | 6 +- src/errors.rs | 15 ++++- src/main.rs | 37 ++++++------ src/vm.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 20 deletions(-) create mode 100644 src/vm.rs diff --git a/src/compiler.rs b/src/compiler.rs index 5ec938e..8592efe 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -4,10 +4,10 @@ use super::errors::CompilerError; use super::parser::{Expression, LiteralPattern, ParsedReid, Pattern, Position, Statement}; type Variable = (HeapID, VariableType); -type HeapID = u32; -type RegID = u32; +pub type HeapID = usize; +pub type RegID = usize; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Command { InitializeVariable(HeapID, VariableType), // Initializes new variable to HeapID at VariableType BeginScope, // Begins new Scope diff --git a/src/errors.rs b/src/errors.rs index e691467..c4d6da1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -75,6 +75,7 @@ impl CompilerError { } } } + impl Display for CompilerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let text = match self { @@ -83,7 +84,7 @@ impl Display for CompilerError { format!("Variable '{}' already exists, re-assign at {}", pos, name) } CompilerError::VariableNotExists(pos, name) => { - format!("Variable '{}' does not exist, at {}", pos, name) + format!("Variable '{}' does not exist, at {}", name, pos) } CompilerError::InvalidScopeExit(pos) => { format!("Attempted to escape a scope invalidly at {}", pos) @@ -96,3 +97,15 @@ impl Display for CompilerError { write!(f, "{}", text) } } + +#[derive(Debug)] +pub enum RuntimePanic { + Fatal, + InvalidCommandIdx(usize), + ScopeStackUnderflow, + StackUnderflow, + StackOverflow, + RegistryNotDefined, + InvalidHeapAddress, + ValueNotInitialized, +} diff --git a/src/main.rs b/src/main.rs index 67eb5dc..82ce3e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,28 +2,33 @@ mod compiler; mod errors; mod file_io; mod parser; +mod vm; + +use file_io::open_file; +use std::path::Path; use compiler::Compiler; -use file_io::open_file; use parser::Parser; -use std::path::Path; +use vm::VirtualMachine; fn main() { let path = Path::new("reid_src/test.reid"); let parsed = Parser::from(open_file(&path).ok().unwrap()).parse(); - match parsed { - Err(error) => { - eprintln!("Syntax error: {}", error); - std::process::exit(1); - } - Ok(parsed) => { - dbg!(&parsed); - let compiled = Compiler::from(parsed).compile(); - if let Err(error) = compiled { - eprintln!("Compilation error: {}", error); - std::process::exit(1); - } - dbg!(compiled); - } + if let Err(error) = parsed { + eprintln!("Syntax error: {}", error); + std::process::exit(1); + } + dbg!(&parsed); + let compiled = Compiler::from(parsed.unwrap()).compile(); + if let Err(error) = compiled { + eprintln!("Compilation error: {}", error); + std::process::exit(1); + } + dbg!(&compiled); + + let mut vm = VirtualMachine::from(compiled.unwrap()); + if let Err(error) = vm.run() { + eprintln!("Runtime panic: {:#?}", error); + std::process::exit(1); } } diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..4dbbfef --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,148 @@ +use std::collections::HashMap; + +use super::compiler::{Command, CompiledReid, HeapID, VariableType}; +use super::errors::RuntimePanic; + +pub struct VirtualMachine { + registry_stack: Vec<[Option; 8]>, + registry: [Option; 8], + + stack: Vec, + heap: HashMap, + + 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(), + + position: 0, + compiled, + } + } + + 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.1 = 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) + } + } + } + } +} + +#[derive(Clone, Debug)] +struct AllocatedVar(VariableType, Option); + +#[derive(Clone, Debug)] +enum Value { + StringVal(String), +}