use std::io::{BufReader, BufWriter, Read, Write}; #[derive(Debug)] pub enum InstructionType { Increment, Decrement, Leftshift, Rightshift, PutChar, GetChar, JumpIfZero, JumpIfNotZero, } #[derive(Debug)] pub struct Instruction { pub itype: InstructionType, pub argument: usize, } pub struct Machine<'a> { ip: usize, mp: usize, buf: [u8; 1], memory: [u32; 50000], code: Vec, input: &'a mut BufReader>, output: &'a mut BufWriter>, } impl<'a> Machine<'a> { pub fn new(code: Vec, input: &'a mut BufReader>, output: &'a mut BufWriter>) -> Self { Machine { ip: 0, mp: 0, buf: [0; 1], memory: [0; 50000], code, input, output, } } pub fn execute(&mut self){ // prevent ip from getting out of bounds while self.ip < self.code.len() { // get current instruction let instruction = &self.code[self.ip]; // match type and execute match instruction.itype { InstructionType::Increment => { self.memory[self.mp] += instruction.argument as u32 } InstructionType::Decrement => { self.memory[self.mp] -= instruction.argument as u32 } InstructionType::Leftshift => { self.mp += instruction.argument } InstructionType::Rightshift => { self.mp -= instruction.argument } InstructionType::PutChar => { for _ in 0..instruction.argument { self.put_char() } } InstructionType::GetChar => { for _ in 0..instruction.argument { self.get_char() } } InstructionType::JumpIfZero => { if self.memory[self.mp] == 0 { self.ip = instruction.argument; continue; } } InstructionType::JumpIfNotZero => { if self.memory[self.mp] != 0 { self.ip = instruction.argument; continue; } } } } } pub fn get_char(&mut self){ let byte_input = match self.input.read(&mut self.buf) { Ok(byte_input) => byte_input, Err(e) => panic!(e), }; if byte_input != 1 { panic!("input read error") } self.memory[self.mp] = self.buf[0] as u32; } pub fn put_char(&mut self){ self.buf[0] = self.memory[self.mp] as u8; let byte_write = match self.output.write(&self.buf) { Ok(byte_write) => byte_write, Err(e) => panic!(e), }; if byte_write != 1 { panic!("output write error") } self.output.flush(); } }