use super::err::*; use super::ins::*; use super::context::*; impl Instruction { pub fn execute(&self, context: &mut Context) -> Result<(), RuntimeError> { match self { Instruction::Push(i) => context.push(*i), Instruction::Dup => match context.pop() { Some(n) => { context.push(n); context.push(n); } None => return Err(RuntimeError::ErrorVmState(format!("Stack is empty when call dup!"))), }, Instruction::CopyN(i) => match context.get_at(*i as usize) { Some(n) => context.push(n), None => return Err(RuntimeError::ErrorVmState(format!("Stack is empty when call dup!"))), }, Instruction::Swap => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(a); context.push(b); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call swap!"))); } }, Instruction::Pop => { context.pop(); }, Instruction::PopN(i) => { for _ in 0..*i { context.pop(); } }, Instruction::Add => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(a + b); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call add!"))); } }, Instruction::Sub => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(b - a); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call sub!"))); } }, Instruction::Mul => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(a * b); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call mul!"))); } }, Instruction::Div => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(b / a); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call div!"))); } }, Instruction::Mod => { let a = context.pop(); let b = context.pop(); if let (Some(a), Some(b)) = (a, b) { context.push(b % a); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call mod!"))); } }, Instruction::Store => { let val = context.pop(); let addr = context.pop(); if let (Some(val), Some(addr)) = (val, addr) { context.put_mem(addr, val); } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call store!"))); } }, Instruction::Retrieve => { let addr = context.pop(); if let Some(addr) = addr { match context.get_mem(addr) { Some(val) => { context.push(val); }, None => return Err(RuntimeError::ErrorVmState(format!("Memory addr not found: {}!", addr))), } } else { return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call retrieve!"))); } } Instruction::DefineLabel(_) => (), // JUST SKIP! Instruction::CallAtLabel(_i) => return Err(RuntimeError::NotImplemented("CallAtLabel".into())), Instruction::GotoLabel(i) => match context.find_label(*i) { Some(ptr) => context.goto(ptr), None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))), }, Instruction::GotoLabelE0(i) => match context.pop() { Some(v) => if v == 0 { match context.find_label(*i) { Some(ptr) => context.goto(ptr), None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))), } }, None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call gotolabele0!"))), }, Instruction::GotoLabelL0(i) => match context.pop() { Some(v) => if v < 0 { match context.find_label(*i) { Some(ptr) => context.goto(ptr), None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))), } }, None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call gotolabell0!"))), }, Instruction::ReturnCallAt => return Err(RuntimeError::NotImplemented("ReturnCallAt".into())), Instruction::End => return Err(RuntimeError::EndVm), Instruction::StdOutChar => match context.pop() { Some(i) => print!("{}", i as u8 as char), None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call stdoutchar!"))), }, Instruction::StdOutNum => match context.pop() { Some(i) => print!("{}", i), None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call stdoutnum!"))), }, Instruction::StdInChar => return Err(RuntimeError::NotImplemented("StdInChar".into())), Instruction::StdInNum => return Err(RuntimeError::NotImplemented("StdInNum".into())), } Ok(()) } } #[derive(Debug)] pub struct Vm { instructions: Vec, } impl Vm { pub fn new(instructions: Vec) -> Vm { Vm { instructions, } } pub fn define_labels(&self, context: &mut Context) { for (ptr, ins) in self.instructions.iter().enumerate() { if let Instruction::DefineLabel(i) = ins { if context.is_debug() { println!("[DEBUG] Define label: {} -> {}", *i, ptr as isize); } context.define_label(*i, ptr as isize); } } } pub fn execute(&self, context: &mut Context) -> Result<(), RuntimeError> { loop { let ptr = context.get_ptr(); let ins = self.instructions.get(ptr as usize); match ins { Some(ins) => { if context.is_debug() { let is_define_label = if let Instruction::DefineLabel(_) = ins { true } else { false }; if !is_define_label { println!("[DEBUG] Context: {:?}", context); println!("[DEBUG] Execute instruction: {:?}", ins); } } match ins.execute(context) { Err(err) => return Err(err), Ok(_) => context.move_ptr_next(), } }, None => return Err(RuntimeError::ErrorVmState(format!("Instruction at {} not found", ptr))), } } } }