Published .

brainfuck-interpreter.tar

src/main.rs

mod parser;
mod interpreter;

fn main() {
  let source = std::fs::read_to_string("helloworld.bf").expect("Missing file: helloworld.bf");
  let parsed = parser::parse(&source);
  let mut interpreter = interpreter::Interpreter::new();
  interpreter.run(&parsed);
}

src/parser.rs

#[derive(Debug)]
pub enum Instruction {
  IncrementPointer,
  DecrementPointer,
  IncrementValue,
  DecrementValue,
  OutputValue,
  ReadValue,
  Branch(Vec<Instruction>),
}

struct Input<'a> {
  source: &'a str,
  position: usize,
}

impl Input<'_> {
  fn read_all(&mut self) -> Vec<Instruction> {
    let mut result: Vec<Instruction> = vec!();
    while !self.done_reading() {
      if let Some(next) = self.read_next() {
        result.push(next);
      } else {
        break;
      }
    }
    result
  }

  fn read_next(&mut self) -> Option<Instruction> {
    while !self.done_reading() && self.next_is_comment() {
      self.skip_next_char();
    }
    if self.peek_char() == Some('[') {
      return self.read_branch();
    } else {
      return self.read_instruction_char()
    }
  }

  fn read_branch(&mut self) -> Option<Instruction> {
    let mut branch: Vec<Instruction> = vec!();
    self.skip_next_char(); // [
    while self.peek_char() != Some(']') {
      if self.next_is_comment() {
        self.skip_next_char()
      } else if let Some(next) = self.read_next() {
        branch.push(next);
      } else {
        return None;
      }
    }
    self.skip_next_char(); // ]
    Some(Instruction::Branch(branch))
  }

  fn peek_char(&self) -> Option<char> {
    self.source.chars().collect::<Vec<_>>().get(self.position).copied()
  }

  fn next_is_comment(&self) -> bool {
    if let Some(next) = self.peek_char() {
      !vec!['>', '<', '+', '-', '.', ',', '['].contains(&next)
    } else {
      false
    }
  }

  fn read_instruction_char(&mut self) -> Option<Instruction> {
    let c = self.peek_char();
    self.skip_next_char();
    match c {
      Some('>') => Some(Instruction::IncrementPointer),
      Some('<') => Some(Instruction::DecrementPointer),
      Some('+') => Some(Instruction::IncrementValue),
      Some('-') => Some(Instruction::DecrementValue),
      Some('.') => Some(Instruction::OutputValue),
      Some(',') => Some(Instruction::ReadValue),
      _ => None,
    }
  }

  fn done_reading(&self) -> bool {
    self.position >= self.source.chars().count()
  }

  fn skip_next_char(&mut self) {
    if !self.done_reading() {
      self.position += 1;
    }
  }
}

pub fn parse(source: &str) -> Vec<Instruction> {
  let mut input = Input { source, position: 0 };
  input.read_all()
}

src/interpreter.rs

use crate::parser::Instruction;

pub struct Interpreter {
  pointer: isize,
  cells: std::collections::HashMap<isize, u8>,
}

impl Interpreter {
  pub fn new() -> Interpreter {
    Interpreter {
      pointer: 0,
      cells: std::collections::HashMap::new(),
    }
  }

  pub fn run(&mut self, instructions: &Vec<Instruction>) {
    for instruction in instructions {
      self.run_instruction(instruction);
    }
  }

  fn run_instruction(&mut self, instruction: &Instruction) {
    match instruction {
      Instruction::IncrementPointer => self.increment_pointer(),
      Instruction::DecrementPointer => self.decrement_pointer(),
      Instruction::IncrementValue => self.increment_value(),
      Instruction::DecrementValue => self.decrement_value(),
      Instruction::OutputValue => self.output_value(),
      Instruction::ReadValue => self.read_value(),
      Instruction::Branch(body) => self.branch(&body),
    }
  }

  fn increment_pointer(&mut self) {
    self.pointer += 1;
  }

  fn decrement_pointer(&mut self) {
    self.pointer -= 1;
  }

  fn increment_value(&mut self) {
    self.set_current_cell(self.current_cell().wrapping_add(1))
  }

  fn decrement_value(&mut self) {
    self.set_current_cell(self.current_cell().wrapping_sub(1))
  }

  fn output_value(&mut self) {
    print!("{}", self.current_cell() as char);
  }

  fn read_value(&mut self) {
  }

  fn branch(&mut self, body: &Vec<Instruction>) {
    while self.current_cell() != 0 {
      self.run(body);
    }
  }

  fn current_cell(&self) -> u8 {
    *self.cells.get(&self.pointer).unwrap_or(&0)
  }

  fn set_current_cell(&mut self, value: u8) {
    self.cells.insert(self.pointer, value);
  }
}

helloworld.bf

Hello, world!
Source: [https://codegolf.stackexchange.com/a/163590]
+[-->-[>>+>-----<<]<--<---]>-.>>>+.>>..+++[.>]<<<<.+++.------.<<-.>>>>+.