use crate::spool::Spool;
use crate::*;
#[derive(Debug)]
pub struct Wire4VM {
pc: usize,
code: Prog,
stack: Stack,
scope: ScopeStack,
vtable: VTable,
vars: Vars,
spool: Spool,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Cond {
Active,
Skip,
Bypass,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Scope {
Call(usize),
Cond(Cond),
Loop(usize),
CondLoop(usize, i32, i32),
}
impl Default for vm::Wire4VM {
fn default() -> Self {
Self::new()
}
}
impl Wire4VM {
pub fn new() -> Wire4VM {
Wire4VM {
pc: 0,
code: Prog::new(),
stack: Stack::new(),
vtable: VTable::new(),
scope: ScopeStack::new(),
vars: Vars::new(),
spool: Spool::new(),
}
}
pub fn reset(&mut self) {
self.pc = 0;
self.spool.clear();
self.vars.clear();
self.code.clear();
self.stack.clear();
self.scope.clear();
self.vtable.clear();
}
pub fn load(&mut self, prog: &str) -> Result<(), VMError> {
compile(prog, &mut self.code, &mut self.vtable, &mut self.spool)
.map_err(VMError::CompileError)?;
Ok(())
}
pub fn set_var(&mut self, key: u32, val: Value) -> Result<(), VMError> {
self.vars
.insert(key, val)
.map_err(|_| VMError::TooManyVars)?;
Ok(())
}
pub fn get_var(&self, key: u32) -> Option<&Value> {
self.vars.get(&key)
}
pub fn get_stack(&self) -> &Stack {
&self.stack
}
pub fn get_string(&self, key: u32) -> Option<&String> {
self.spool.load(key)
}
pub fn pop(&mut self) -> Result<Value, VMError> {
self.stack.pop().ok_or(VMError::StackUnderflow)
}
pub fn push(&mut self, value: Value) -> Result<(), VMError> {
self.stack.push(value).map_err(|_| VMError::StackOverflow)
}
pub fn spin(&mut self) -> Result<VMRequest, VMError> {
while self.pc < self.code.len() {
if let Some(req) = self.tick()? {
return Ok(req);
}
}
Ok(VMRequest::Idle)
}
#[allow(clippy::cognitive_complexity)]
pub fn tick(&mut self) -> Result<Option<VMRequest>, VMError> {
if self.pc >= self.code.len() {
return Err(VMError::ProgramHalted);
}
let op = self.code.get(self.pc).cloned().ok_or(VMError::InvalidPtr)?;
let scope = self.scope.get(self.scope.len() - 1).cloned();
self.pc = self.pc.saturating_add(1);
match (op, scope) {
(Word::Proc(proc), _) => {
if let Some((_, end)) = self.vtable.get(&proc) {
self.pc = *end;
return Ok(None);
} else {
return Err(VMError::InvalidPtr);
}
}
(Word::Call(proc), _) => {
if let Some((addr, _)) = self.vtable.get(&proc) {
self.scope
.push(Scope::Call(self.pc))
.map_err(|_| VMError::StackOverflow)?;
self.pc = *addr;
return Ok(None);
} else {
return Ok(Some(VMRequest::CallProc(proc)));
}
}
(Word::Ret, _) => match self.scope.pop() {
Some(Scope::Call(ra)) => {
self.pc = ra;
return Ok(None);
}
_ => return Err(VMError::InvalidScope),
},
(Word::Else, Some(Scope::Cond(Cond::Bypass))) => {
return Ok(None);
}
(Word::Else, Some(Scope::Cond(Cond::Active))) => {
self.patch_scope(Scope::Cond(Cond::Skip))?;
return Ok(None);
}
(Word::Else, Some(Scope::Cond(Cond::Skip))) => {
self.patch_scope(Scope::Cond(Cond::Active))?;
return Ok(None);
}
(Word::Else, _) => return Err(VMError::InvalidScope),
(Word::Then, Some(Scope::Cond(_))) => {
self.scope.pop();
return Ok(None);
}
(Word::Then, _) => {
return Err(VMError::InvalidScope);
}
(Word::If, scope) => {
if let Value::Num(flag) = self.pop()? {
let cond = match (scope, flag) {
(Some(Scope::Cond(Cond::Skip)), _)
| (Some(Scope::Cond(Cond::Bypass)), _) => Cond::Bypass,
(_, 0) => Cond::Skip,
_ => Cond::Active,
};
self.scope
.push(Scope::Cond(cond))
.map_err(|_| VMError::StackOverflow)?;
return Ok(None);
}
return Err(VMError::InvalidArguments(Word::If));
}
(_, Some(Scope::Cond(Cond::Skip))) | (_, Some(Scope::Cond(Cond::Bypass))) => {
return Ok(None)
}
(Word::Do, _) => {
let start = self.pop()?;
let end = self.pop()?;
if let (Value::Num(end), Value::Num(start)) = (end, start) {
if start == end {
return Err(VMError::InvalidArguments(Word::Do));
}
self.scope
.push(Scope::CondLoop(self.pc, start, end))
.map_err(|_| VMError::StackOverflow)?;
return Ok(None);
} else {
return Err(VMError::InvalidArguments(Word::Do));
}
}
(Word::I, scope) => {
if let Some(Scope::CondLoop(_, i, _)) = scope {
self.push(Value::Num(i))?;
return Ok(None);
}
for scope in self.scope.iter().rev() {
match scope {
Scope::CondLoop(_, i, _) => {
#[allow(mutable_borrow_reservation_conflict)]
self.push(Value::Num(*i))?;
return Ok(None);
}
Scope::Call(_) => break,
_ => {}
}
}
return Err(VMError::InvalidScope);
}
(Word::Loop, Some(Scope::CondLoop(ra, val, end))) => {
let next = val.saturating_add(1);
if next < end {
self.patch_scope(Scope::CondLoop(ra, next, end))?;
self.pc = ra;
} else {
self.scope.pop();
}
return Ok(None);
}
(Word::Loop, _) => return Err(VMError::InvalidScope),
(Word::Begin, _) => {
self.scope
.push(Scope::Loop(self.pc))
.map_err(|_| VMError::StackOverflow)?;
return Ok(None);
}
(Word::Until, Some(Scope::Loop(ra))) => {
if let Value::Num(flag) = self.pop()? {
if flag != 0 {
self.pc = ra;
} else {
self.scope.pop();
}
return Ok(None);
}
return Err(VMError::InvalidArguments(Word::Until));
}
(Word::Until, _) => return Err(VMError::InvalidScope),
(op, _) => match op {
Word::Drop => {
self.pop()?;
}
Word::Dup => {
let fst = self.pop()?;
self.push(fst.clone())?;
self.push(fst)?;
}
Word::Swap => {
let fst = self.pop()?;
let snd = self.pop()?;
self.push(fst)?;
self.push(snd)?;
}
Word::Over => {
let fst = self.pop()?;
let snd = self.pop()?;
self.push(snd.clone())?;
self.push(fst)?;
self.push(snd)?;
}
Word::Rot => {
let fst = self.pop()?;
let snd = self.pop()?;
let thr = self.pop()?;
self.push(fst)?;
self.push(snd)?;
self.push(thr)?;
}
Word::RRot => {
let fst = self.pop()?;
let snd = self.pop()?;
let thr = self.pop()?;
self.push(fst)?;
self.push(thr)?;
self.push(snd)?;
}
Word::Nip => {
let fst = self.pop()?;
self.pop()?;
self.push(fst)?;
}
Word::Tuck => {
let fst = self.pop()?;
let snd = self.pop()?;
self.push(fst.clone())?;
self.push(snd)?;
self.push(fst)?;
}
Word::Inc
| Word::Dec
| Word::EqZero
| Word::LtZero
| Word::GtZero
| Word::NotEqZero
| Word::Invert => {
if let Value::Num(num) = self.pop()? {
let num = match op {
Word::Inc => num.saturating_add(1),
Word::Dec => num.saturating_sub(1),
Word::EqZero => bool_enc(num == 0),
Word::LtZero => bool_enc(num < 0),
Word::GtZero => bool_enc(num > 0),
Word::NotEqZero => bool_enc(num != 0),
Word::Invert => bool_enc(num == 0),
_ => unreachable!(),
};
self.push(Value::Num(num))?;
} else {
return Err(VMError::InvalidArguments(op));
}
}
Word::Plus
| Word::Minus
| Word::Div
| Word::Mul
| Word::Mod
| Word::And
| Word::Or
| Word::Xor
| Word::Lt
| Word::Gt
| Word::Lte
| Word::Gte
| Word::Eq
| Word::NotEq => {
let fst = self.pop()?;
let snd = self.pop()?;
if let (Value::Num(a), Value::Num(b)) = (snd, fst) {
let num = match op {
Word::Plus => a + b,
Word::Minus => a - b,
Word::Div => {
if b == 0 {
return Err(VMError::DivisionByZero);
} else {
a / b
}
}
Word::Mul => a * b,
Word::And => a & b,
Word::Mod => a % b,
Word::Or => a | b,
Word::Xor => a ^ b,
Word::Lt => bool_enc(a < b),
Word::Gt => bool_enc(a > b),
Word::Lte => bool_enc(a <= b),
Word::Gte => bool_enc(a >= b),
Word::Eq => bool_enc(a == b),
Word::NotEq => bool_enc(a != b),
_ => unreachable!(),
};
self.push(Value::Num(num))?;
} else {
return Err(VMError::InvalidArguments(op));
}
}
Word::NumImm(num) => self.push(Value::Num(num as i32))?,
Word::PortImm(port) => self.push(Value::Port(port))?,
Word::NetImm(net) => self.push(Value::Net(net))?,
Word::StrImm(key) => self.push(Value::Str(key))?,
Word::VarImm(net) => self.push(Value::Var(net))?,
Word::SetVar => {
if let Value::Var(var) = self.pop()? {
let val = self.pop()?;
self.set_var(var, val)?;
} else {
return Err(VMError::InvalidArguments(op));
}
}
Word::GetVar => {
if let Value::Var(var) = self.pop()? {
#[allow(mutable_borrow_reservation_conflict)]
match self.get_var(var) {
Some(val) => self.push(val.clone())?,
None => return Err(VMError::UnknownVar),
}
} else {
return Err(VMError::InvalidArguments(op));
}
}
Word::IO(io) => return Ok(Some(VMRequest::IO(io))),
Word::Call(_)
| Word::Proc(_)
| Word::Ret
| Word::If
| Word::Then
| Word::Else
| Word::Begin
| Word::Until
| Word::I
| Word::Do
| Word::Loop => unreachable!(),
},
}
Ok(None)
}
fn patch_scope(&mut self, scope: Scope) -> Result<(), VMError> {
if self.scope.pop().is_none() {
return Err(VMError::InvalidScope);
}
self.scope.push(scope).map_err(|_| VMError::StackOverflow)?;
Ok(())
}
}