use std::{
fmt::{self, Display, Formatter},
mem::size_of,
thread::sleep,
time::Duration,
};
use crate::{
instruction::{Instruction, Program},
value::{Pointer, Value},
};
pub struct Vm {
stack: Vec<Value>,
mem: Vec<Option<Value>>,
mem_ptr: isize,
call_stack: Vec<CallFrame>,
}
#[derive(Debug, Default)]
struct CallFrame {
ip: usize,
fp: usize,
}
const STACK_LIMIT: usize = 1024;
const CALL_STACK_LIMIT: usize = 1024;
pub type Result<T> = std::result::Result<T, RuntimeError>;
#[derive(Debug)]
pub struct RuntimeError {
pub kind: RuntimeErrorKind,
pub msg: String,
}
impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.kind, self.msg)
}
}
impl RuntimeError {
pub fn new(kind: RuntimeErrorKind, msg: String) -> Self {
Self { kind, msg }
}
}
#[derive(Debug)]
pub enum RuntimeErrorKind {
StackOverflow,
Arithmetic,
}
impl Display for RuntimeErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::StackOverflow => "StackOverflowError",
Self::Arithmetic => "ArithmeticError",
}
)
}
}
impl Default for Vm {
fn default() -> Self {
Self::new()
}
}
impl Vm {
pub fn new() -> Self {
Self {
stack: vec![],
mem: vec![],
mem_ptr: 0,
call_stack: vec![CallFrame::default()],
}
}
fn push(&mut self, val: Value) -> Result<()> {
match self.stack.len() >= STACK_LIMIT {
true => Err(RuntimeError::new(
RuntimeErrorKind::StackOverflow,
format!("maximum stack size of {STACK_LIMIT} exceeded"),
)),
false => {
self.stack.push(val);
Ok(())
}
}
}
#[inline]
fn pop(&mut self) -> Value {
self.stack.pop().expect("pop is always called safely")
}
#[inline]
fn call_frame(&self) -> &CallFrame {
self.call_stack
.last()
.expect("there is always at least one call frame")
}
#[inline]
fn call_frame_mut(&mut self) -> &mut CallFrame {
self.call_stack
.last_mut()
.expect("there is always at least one call frame")
}
pub fn debug_run(&mut self, program: Program, clock_hz: u64) -> Result<i64> {
while self.call_frame().ip < program.0[self.call_frame().fp].len() {
let instruction = &program.0[self.call_frame().fp][self.call_frame().ip];
println!(
"[{fp:02}/{ip:02}] {instr:15} | {stack:>40} | mp = {mp} | used = {used_mem:4}b | {mem}",
fp = self.call_frame().fp,
ip = self.call_frame().ip,
instr = instruction.to_string(),
stack = self
.stack
.iter()
.rev()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", "),
mp = self.mem_ptr,
used_mem = self.mem.len() * size_of::<Value>(),
mem = self.mem[self.mem_ptr as usize..]
.iter()
.map(|v| match v {
Some(v) => v.to_string(),
None => "NONE".to_string(),
})
.collect::<Vec<String>>()
.join(", ")
);
sleep(Duration::from_millis(1000 / clock_hz));
if let Some(code) = self.run_instruction(instruction)? {
return Ok(code);
};
}
Ok(0)
}
pub fn run(&mut self, program: Program) -> Result<i64> {
while self.call_frame().ip < program.0[self.call_frame().fp].len() {
let instruction = &program.0[self.call_frame().fp][self.call_frame().ip];
if let Some(code) = self.run_instruction(instruction)? {
return Ok(code);
};
}
Ok(0)
}
fn run_instruction(&mut self, inst: &Instruction) -> Result<Option<i64>> {
match inst {
Instruction::Nop => {}
Instruction::Push(value) => self.push(*value)?,
Instruction::Drop => {
self.pop();
}
Instruction::Clone => self
.stack
.push(*self.stack.last().expect("clone is always called safely")),
Instruction::Jmp(idx) => {
self.call_frame_mut().ip = *idx;
return Ok(None);
}
Instruction::JmpFalse(idx) => {
if !self.pop().unwrap_bool() {
self.call_frame_mut().ip = *idx;
return Ok(None);
}
}
Instruction::SetVarImm(_) | Instruction::SetVar => {
let val = self.pop();
let addr = match inst {
Instruction::SetVarImm(Pointer::Rel(offset)) => {
(self.mem_ptr + offset) as usize
}
Instruction::SetVarImm(Pointer::Abs(addr)) => *addr,
_ => match self.pop().unwrap_ptr() {
Pointer::Rel(offset) => (self.mem_ptr + offset) as usize,
Pointer::Abs(addr) => addr,
},
};
match self.mem.get_mut(addr) {
Some(var) => *var = Some(val),
None => {
self.mem.resize(addr + 1, None);
self.mem[addr] = Some(val)
}
}
}
Instruction::GetVar => {
let addr = match self.pop().unwrap_ptr() {
Pointer::Rel(offset) => (self.mem_ptr + offset) as usize,
Pointer::Abs(addr) => addr,
};
self.push(self.mem[addr].expect("variables are always initialized"))?
}
Instruction::Call(idx) => {
if self.call_stack.len() >= CALL_STACK_LIMIT {
return Err(RuntimeError::new(
RuntimeErrorKind::StackOverflow,
format!("maximum call-stack size of {CALL_STACK_LIMIT} was exceeded"),
));
}
self.call_stack.push(CallFrame { ip: 0, fp: *idx });
return Ok(None);
}
Instruction::SetMp(offset) => {
self.mem_ptr += offset;
self.mem.resize(self.mem_ptr as usize + 1, None);
}
Instruction::RelToAddr(offset) => {
let addr = (self.mem_ptr + offset) as usize;
self.push(Value::Ptr(Pointer::Abs(addr)))?;
}
Instruction::Ret => {
self.call_stack.pop();
}
Instruction::Cast(to) => {
let from = self.pop();
self.push(from.cast(*to))?
}
Instruction::Exit => {
return Ok(Some(self.pop().unwrap_int()));
}
Instruction::Neg => {
let top = self.pop();
self.push(top.neg())?;
}
Instruction::Not => {
let top = self.pop();
self.push(top.not())?;
}
Instruction::Add => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.add(rhs))?;
}
Instruction::Sub => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.sub(rhs))?;
}
Instruction::Mul => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.mul(rhs))?;
}
Instruction::Pow => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.pow(rhs))?;
}
Instruction::Div => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.div(rhs)?)?;
}
Instruction::Rem => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.rem(rhs)?)?;
}
Instruction::Eq => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.eq(rhs))?;
}
Instruction::Ne => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.ne(rhs))?;
}
Instruction::Lt => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.lt(rhs))?;
}
Instruction::Le => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.le(rhs))?;
}
Instruction::Gt => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.gt(rhs))?;
}
Instruction::Ge => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.ge(rhs))?;
}
Instruction::Shl => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.shl(rhs)?)?;
}
Instruction::Shr => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.shr(rhs)?)?;
}
Instruction::BitOr => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.bit_or(rhs))?;
}
Instruction::BitAnd => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.bit_and(rhs))?;
}
Instruction::BitXor => {
let rhs = self.pop();
let lhs = self.pop();
self.push(lhs.bit_xor(rhs))?;
}
}
self.call_frame_mut().ip += 1;
Ok(None)
}
}