use std::io::{Read, Write};
use tape::Tape;
use program::Program;
use super::{CYCLE_LIMIT, Error, Instruction};
#[derive(Default)]
pub struct Interpreter<'a, T: Tape + Default> {
program: Option<Program>,
reader: Option<&'a mut Read>,
writer: Option<&'a mut Write>,
tape: Box<T>,
pc: usize,
cycles: u64,
}
impl<'a, T: Tape + Default> Interpreter<'a, T> {
pub fn new<R: Read, W: Write>(program: Program, reader: &'a mut R, writer: &'a mut W) -> Interpreter<'a, T> {
let mut interp = Self::default();
interp.load(program);
interp.read_from(reader);
interp.write_to(writer);
interp
}
pub fn load(&mut self, program: Program) -> &mut Self {
self.pc = 0;
self.program = Some(program);
self
}
pub fn read_from<R: Read>(&mut self, reader: &'a mut R) -> &mut Self {
self.reader = Some(reader);
self
}
pub fn write_to<W: Write>(&mut self, writer: &'a mut W) -> &mut Self {
self.writer = Some(writer);
self
}
pub fn run(&mut self) -> Result<(), Error> {
while let Some(r) = try!(self.step()) {
if let Err(e) = r {
return Err(e)
}
};
Ok(())
}
pub fn run_with_callback<F>(&mut self, mut hook: F) -> Result<(), Error>
where F: FnMut(&mut Self, &Instruction) {
while let Some(r) = try!(self.step()) {
match r {
Ok(i) => hook(self, &i),
Err(e) => return Err(e),
}
};
Ok(())
}
fn step(&mut self) -> Result<Option<Result<Instruction, Error>>, Error> {
if self.cycles >= CYCLE_LIMIT {
return Ok(Some(Err(Error::CycleLimit)))
}
let instruction = match self.program {
Some(ref p) => match p.get(self.pc) {
Some(i) => i,
None => return Ok(None),
},
None => return Err(Error::NoProgram),
};
match self.execute(instruction) {
Ok(_) => {
self.cycles += 1;
Ok(Some(Ok(instruction)))
},
Err(e) => Ok(Some(Err(e))),
}
}
fn execute(&mut self, instruction: Instruction) -> Result<Instruction, Error> {
match instruction {
Instruction::IncPtr => {
try!(self.tape.inc_ptr());
},
Instruction::DecPtr => {
try!(self.tape.dec_ptr());
},
Instruction::IncVal => {
try!(self.tape.inc_val());
},
Instruction::DecVal => {
try!(self.tape.dec_val());
},
Instruction::Output => {
if let Some(ref mut w) = self.writer {
try!(w.write(&[**self.tape]));
}
},
Instruction::Input => {
if let Some(ref mut r) = self.reader {
if let Some(b) = r.bytes().next() {
**self.tape = try!(b);
}
}
},
Instruction::SkipForward(iptr) => {
if **self.tape == 0 {
self.pc = iptr;
}
},
Instruction::SkipBackward(iptr) => {
if **self.tape != 0 {
self.pc = iptr;
}
},
};
self.pc = self.pc + 1;
Ok(instruction)
}
}
#[cfg(test)]
mod tests {
use std::io;
use Instruction;
use program::Program;
use tape::VecTape;
use super::*;
#[test]
fn new() {
let program = Program::parse("++>+.").unwrap();
let mut reader = io::empty();
let mut writer = Vec::<u8>::new();
let _ = Interpreter::<VecTape>::new(program, &mut reader, &mut writer);
}
#[test]
fn load() {
let program = Program::parse("++>+.");
let mut interp = Interpreter::<VecTape>::default();
interp.load(program.unwrap());
}
#[test]
fn run() {
let program = Program::parse("++>+.");
let mut interp = Interpreter::<VecTape>::default();
interp.load(program.unwrap());
assert!(interp.run().is_ok());
}
#[test]
fn run_with_callback() {
let program = Program::parse("++>+.");
let mut interp = Interpreter::<VecTape>::default();
interp.load(program.unwrap());
let mut count = 0;
assert!(interp.run_with_callback(|_, _| {
count = count + 1
}).is_ok());
assert_eq!(count, 5);
}
#[test]
fn step() {
let program = Program::parse("++>+.");
let mut interp = Interpreter::<VecTape>::default();
interp.load(program.unwrap());
let result = interp.step();
assert!(result.is_ok());
let run = result.unwrap();
assert!(run.is_some());
assert!(run.unwrap().is_ok());
}
#[test]
fn execute() {
let mut interp = Interpreter::<VecTape>::default();
let instruction = Instruction::IncVal;
let result = interp.execute(instruction);
assert!(result.is_ok());
}
#[test]
fn single_step() {
let program = Program::parse(">");
let mut interp = Interpreter::<VecTape>::default();
interp.load(program.unwrap());
interp.step().unwrap().unwrap().unwrap();
}
#[test]
fn empty_io() {
let mut reader = io::empty();
let mut writer = Vec::<u8>::new();
let program = Program::parse("+,.");
{
let mut interp = Interpreter::<VecTape>::default();
interp.read_from(&mut reader);
interp.write_to(&mut writer);
interp.load(program.unwrap());
interp.run().unwrap();
}
assert_eq!(writer, [1]);
}
}