brain-rs 0.1.0

Embeddable brainfuck interpreter
Documentation
use std::io::{self, Read, Write};
use std::fmt::{self, Display};
use instruction::{Instruction, InstructionType};

pub struct Machine<'a, R: Read, W: Write> {
    code: &'a [Instruction],
    ip: usize,
    memory: [usize; 30000],
    dp: usize,
    input: R,
    output: W,
    read_buf: Vec<u8>,
}

impl<'a, R: Read, W: Write> Display for Machine<'a, R, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
            "Machine ( code: {:?}, dp: {:?}, buf: {:?} )",
            self.code, self.dp, self.read_buf
        )
    }
}

impl<'a, R: Read, W: Write> Machine<'a, R, W> {
    pub fn new(ins: &'a [Instruction], inp: R, out: W) -> Self {
        Machine {
            code: ins,
            ip: 0,
            memory: [0; 30000],
            dp: 0,
            input: inp,
            output: out,
            read_buf: Vec::new(),
        }
    }

    pub fn execute(&mut self) -> Result<(), String> {
        let mut line_count = (1, 1);
        while self.ip < self.code.len() {
            let ins = &self.code[self.ip];

            match ins.ins_type {
                InstructionType::PLUS => self.memory[self.dp] += ins.argument,
                InstructionType::MINUS => self.memory[self.dp] -= ins.argument,
                InstructionType::RIGHT => self.dp += ins.argument,
                InstructionType::LEFT => self.dp -= ins.argument,
                InstructionType::PUT_CHAR => {
                    for _ in 0..ins.argument {
                        match self.put_char() {
                            Ok(_) => continue,
                            Err(e) => panic!(e),
                        }
                    }
                },
                InstructionType::READ_CHAR => {
                   for _ in 0..ins.argument {
                        self.read_char();
                    }
                },
                InstructionType::JMP_IF_ZERO => {
                    if self.memory[self.dp] == 0 {
                        self.ip = ins.argument as usize;
                        continue
                    }
                },
                InstructionType::JMP_IF_NOT_ZERO => {
                    if self.memory[self.dp] != 0 {
                        self.ip = ins.argument as usize;
                        continue
                    }
                },
                InstructionType::NEW_LINE => {
                    self.ip += ins.argument;
                    line_count.0 += ins.argument;
                    line_count.1 = ins.position;
                    continue
                },
                InstructionType::INVALID => {
                    return Err(format!("Invalid token at line {}, char {}", line_count.0, ins.position - line_count.1));
                },
            }
            self.ip += 1;
        }
        Ok(())
    }

    fn read_char(&mut self) {
        let c = self.input.by_ref()
            .bytes()
            .next().and_then(|result| result.ok())
            .map(|c| c as u8).unwrap();

        self.read_buf.push(c);
        self.memory[self.dp] = *self.read_buf.last().unwrap() as usize;
    }

    fn put_char(&mut self) -> io::Result<usize> {
        self.read_buf.push(self.memory[self.dp] as u8);

        match self.read_buf.last() {
            Some(c) => self.output.write(&[c.clone()]),
            None => Ok(0),
        }
    }
}