Added virtual machine and basic running
This commit is contained in:
parent
0f45350bec
commit
b3fa498f73
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
|
37
src/main.rs
37
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);
|
||||
}
|
||||
}
|
||||
|
148
src/vm.rs
Normal file
148
src/vm.rs
Normal file
@ -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<Value>; 8]>,
|
||||
registry: [Option<Value>; 8],
|
||||
|
||||
stack: Vec<Value>,
|
||||
heap: HashMap<HeapID, AllocatedVar>,
|
||||
|
||||
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<Value>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Value {
|
||||
StringVal(String),
|
||||
}
|
Loading…
Reference in New Issue
Block a user