bfbackend 0.1.0

Compiler backend for brainfuck
Documentation
#![cfg(test)]
use thiserror::Error;

const CELLS_LEN: usize = 30000;
pub struct BfInterpreter {
    pub cells: [u8; CELLS_LEN],
    pub memory_pointer: usize,
    pub code_pointer: usize,
    pub code: Vec<BfInstruction>,
}

impl BfInterpreter {
    pub fn new_with_code(code: String) -> Self {
        let mut parsed = Vec::new();
        for char in code.chars() {
            let parsed_char = match char {
                '+' => BfInstruction::Add,
                '-' => BfInstruction::Subtract,
                '>' => BfInstruction::PointRight,
                '<' => BfInstruction::PointLeft,
                '[' => BfInstruction::StartLoop,
                ']' => BfInstruction::EndLoop,
                ',' => BfInstruction::Input,
                '.' => BfInstruction::Output,
                _ => continue,
            };
            parsed.push(parsed_char)
        }
        Self {
            cells: [0; CELLS_LEN],
            memory_pointer: 0,
            code_pointer: 0,
            code: parsed,
        }
    }
    pub fn run(
        &mut self,
        output: &mut impl std::io::Write,
        input: &mut impl std::io::Read,
    ) -> Result<(), BfError> {
        let mut loop_starts: Vec<usize> = Vec::new();
        while let Some(instruction) = self.code.get(self.code_pointer) {
            let current_cell = self
                .cells
                .get_mut(self.memory_pointer)
                .ok_or(BfError::CellPointerOutOfRange)?;
            match instruction {
                BfInstruction::Add => *current_cell = current_cell.wrapping_add(1),
                BfInstruction::Subtract => *current_cell = current_cell.wrapping_sub(1),
                BfInstruction::PointRight => {
                    self.memory_pointer += 1;
                    if self.memory_pointer >= self.cells.len() {
                        return Err(BfError::CellPointerOutOfRange);
                    }
                }
                BfInstruction::PointLeft => {
                    self.memory_pointer = self
                        .memory_pointer
                        .checked_sub(1)
                        .ok_or(BfError::CellPointerOutOfRange)?;
                }
                BfInstruction::StartLoop => {
                    if *current_cell == 0 {
                        let mut open_brackets = 1;
                        while open_brackets != 0 {
                            self.code_pointer += 1;
                            match self.code.get(self.code_pointer) {
                                Some(BfInstruction::StartLoop) => open_brackets += 1,
                                Some(BfInstruction::EndLoop) => open_brackets -= 1,
                                None => return Err(BfError::MismatchedBracketsError),
                                _ => {}
                            }
                        }
                    } else {
                        loop_starts.push(self.code_pointer);
                    }
                }
                BfInstruction::EndLoop => {
                    if *current_cell != 0 {
                        match loop_starts.last() {
                            Some(&start) => {
                                self.code_pointer = start;
                            }
                            None => return Err(BfError::MismatchedBracketsError),
                        }
                    } else {
                        loop_starts.pop();
                    }
                }
                BfInstruction::Output => {
                    output
                        .write_all(&[*current_cell])
                        .map_err(BfError::IoError)?;
                }
                BfInstruction::Input => {
                    let mut to_read_to = [0];
                    match input.read_exact(&mut to_read_to) {
                        Ok(()) => *current_cell = to_read_to[0],
                        Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
                            *current_cell = 0
                        }
                        Err(e) => return Err(BfError::IoError(e)),
                    }
                }
            }
            self.code_pointer += 1;
        }
        if !loop_starts.is_empty() {
            return Err(BfError::MismatchedBracketsError);
        }
        Ok(())
    }
}
#[derive(Error, Debug)]
pub enum BfError {
    #[error("error reading or writing byte")]
    IoError(std::io::Error),
    #[error("cell pointer out of range")]
    CellPointerOutOfRange,
    #[error("no matching close bracket found for opening bracket")]
    MismatchedBracketsError,
}
pub enum BfInstruction {
    Add,
    Subtract,
    PointRight,
    PointLeft,
    StartLoop,
    EndLoop,
    Output,
    Input,
}