Skip to main content
Bumped by Community user
Tweeted twitter.com/StackCodeReview/status/1192003832022274050
Bumped by Community user
Bumped by Community user
Bumped by Community user
added 119 characters in body
Source Link
DimChtz
  • 173
  • 6

Just in case it's hard to read code from post:

https://github.com/DimChtz/brainfuck/tree/master/rust-brainpreter

Just in case it's hard to read code from post:

https://github.com/DimChtz/brainfuck/tree/master/rust-brainpreter

Source Link
DimChtz
  • 173
  • 6

Simple Brainfuck Interpreter in Rust

Not a long time ago, I got into Rust and I made a simple Brainfuck Interpreter. Now I want to get back to Rust and I would like some comments on my code:

lib.rs

mod core;

use core::parser::Parser;
use core::program::Program;
use core::instruction::Instruction;
use core::error::Error;
use core::memory::Memory;

use std::io;
use std::io::prelude::*;
use std::path::Path;

// Struct for the bf Interpreter (brainpreter)
pub struct Inter {

    tape:Memory,
    program:Program,
    parser:Parser,

}

impl Inter {

    // Function to create and return a new Interpreter
    pub fn new() -> Inter {

        Inter {

            tape: Memory::new(),
            program: Program::new(),
            parser: Parser::new(),

        }

    }

    // Function to load code into the parser (text)
    pub fn load<S: Into<String>>(&mut self, p:S) -> Result<(), Error> {

        self.parser.load(p)

    }

    // Function to load code into the parser (file)
    pub fn load_from_file<P: AsRef<Path>>(&mut self, src:P) -> Result<(), Error> {

        self.parser.load_from_file(src)

    }

    // Function to compile the code
    pub fn parse(&mut self) -> Result<(), Error> {

        // Call the parser
        match self.parser.parse() {

            Ok(p) => {

                self.program = p;

                Ok(())

            }

            Err(e) => Result::Err(e),

        }

    }

    // Function to execute bf program
    pub fn run(&mut self) -> Result<(), Error> {

        // Check if a program is properly loaded
        if self.program.get_size() == 0 {
            return Result::Err(Error::EmptyProgram);
        }

        // Execute instructions
        while let Some(instr) = self.program.get() {

            match instr {

                Instruction::IncPtr => { match self.inc_ptr() { Ok(_) => {  } Err(e) => return Result::Err(e), } }
                Instruction::DecPtr => { match self.dec_ptr() { Ok(_) => {  } Err(e) => return Result::Err(e), } }
                Instruction::IncVal => { match self.inc_val() { Ok(_) => {  } Err(e) => return Result::Err(e), } }
                Instruction::DecVal => { match self.dec_val() { Ok(_) => {  } Err(e) => return Result::Err(e), } }
                Instruction::Input  => { self.input(); }
                Instruction::Output => { self.output(); }
                Instruction::OpenBracket(n)  => { self.open_bracket(n); }
                Instruction::CloseBracket(n) => { self.close_bracket(n); }

            };

            // If this was the last instruction
            if !self.program.inc_ptr() {
                break;
            }

        };

        return Result::Ok(());

    }

    // Function to increase the value on the memory (tape)
    fn inc_val(&mut self) -> Result<(), Error> {

        self.tape.inc_val()

    }

    // Function to decrease the value on the memory (tape)
    fn dec_val(&mut self) -> Result<(), Error> {

        self.tape.dec_val()

    }

    // Function to increase the pointer on the memory (tape)
    fn inc_ptr(&mut self) -> Result<(), Error> {

        self.tape.inc_ptr()

    }

    // Function to decrease the pointer on the memory (tape)
    fn dec_ptr(&mut self) -> Result<(), Error> {

        self.tape.dec_ptr()

    }

    // Function to read into the memory (tape)
    fn input(&mut self) {

        match io::stdin().bytes().next() {

            Some(v) => self.tape.set_val(v.unwrap()),

            None => {  }

        }

    }

    // Function to print a char from memory (tape)
    fn output(&mut self) {

        print!("{}", self.tape.get_val() as char);
        
    }

    // Function to handle the open bracket
    fn open_bracket(&mut self, pos:usize) {
        
        if self.tape.get_val() == 0 {
            self.program.set_ptr(pos);
        }

    }

    // Function to handle the close bracket
    fn close_bracket(&mut self, pos:usize) {

        if self.tape.get_val() != 0 {
            self.program.set_ptr(pos);
        }

    }

}

core/mod.rs

pub mod instruction;
pub mod program;
pub mod parser;
pub mod error;
pub mod memory;

core/error.rs

use std::error;
use std::fmt;

#[derive(Debug)]
pub enum Error {

    NoSuchFile,
    EmptyProgram,
    MissingOpenBracket,
    MissingCloseBracket,
    PtrOverflow,
    PtrUnderflow,
    ValOverflow,
    ValUnderflow,

}

impl error::Error for Error {

    fn description(&self) -> &str {

        match *self {

            Error::NoSuchFile => "Failed to open file",
            Error::EmptyProgram => "The program is empty",
            Error::MissingOpenBracket => "Missing open bracket(s)",
            Error::MissingCloseBracket => "Missing close bracket(s)",
            Error::PtrOverflow => "Memory pointer overflow",
            Error::PtrUnderflow => "Memory pointer underflow",
            Error::ValOverflow => "Memory value overflow",
            Error::ValUnderflow => "Memory value underflow",

        }

    }

}

impl fmt::Display for Error {

    fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result {

        match *self {

            Error::NoSuchFile => write!(f, "Couldn't open file."),
            Error::EmptyProgram => write!(f, "The program is empty."),
            Error::MissingOpenBracket => write!(f, "Missing open bracket(s)."),
            Error::MissingCloseBracket => write!(f, "Missing close bracket(s)."),
            Error::PtrOverflow => write!(f,"Memory pointer overflow."),
            Error::PtrUnderflow => write!(f,"Memory pointer underflow."),
            Error::ValOverflow => write!(f,"Memory value overflow."),
            Error::ValUnderflow => write!(f,"Memory value underflow."),

        }

    }

}

core/instruction.rs

use std::fmt::{self, Display};

#[derive(Copy, Clone)]
pub enum Instruction {

    IncPtr,
    DecPtr,
    IncVal,
    DecVal,
    Input,
    Output,
    OpenBracket(usize),
    CloseBracket(usize),

}

impl Display for Instruction {

    fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result {
        
        match *self {

            Instruction::IncPtr => write!(f, ">"),
            Instruction::DecPtr => write!(f, "<"),
            Instruction::IncVal => write!(f, "+"),
            Instruction::DecVal => write!(f, "-"),
            Instruction::Input  => write!(f, ","),
            Instruction::Output => write!(f, "."),
            Instruction::OpenBracket(_) => write!(f, "["),
            Instruction::CloseBracket(_) => write!(f, "]"),

        }

    }

}

pub mod mapper {

    use super::Instruction;

    pub fn get_instr(c:char) -> Option<super::Instruction> {

        match c {

            '>' => Option::Some(Instruction::IncPtr),
            '<' => Option::Some(Instruction::DecPtr),
            '+' => Option::Some(Instruction::IncVal),
            '-' => Option::Some(Instruction::DecVal),
            ',' => Option::Some(Instruction::Input),
            '.' => Option::Some(Instruction::Output),
            '[' => Option::Some(Instruction::OpenBracket(0usize)),
            ']' => Option::Some(Instruction::CloseBracket(0usize)),
            _   => Option::None,

        }

    }

    #[allow(unused)]
    pub fn get_char(instr:Instruction) -> char {

        match instr {

            Instruction::IncPtr => '>',
            Instruction::DecPtr => '<',
            Instruction::IncVal => '+',
            Instruction::DecVal => '-',
            Instruction::Input => ',',
            Instruction::Output => '.',
            Instruction::OpenBracket(_) => '[',
            Instruction::CloseBracket(_) => ']',

        }

    }

}

core/memory.rs

use super::error::Error;

pub const MEMORY_SIZE:usize = 30000;

// Memory tape
#[derive(Debug)]
pub struct Memory {
    
    cells: Vec<u8>,
    ptr:usize,

}

impl Memory {
     
    // Function to create and return a new Memory tape
    pub fn new() -> Memory {

        let mut v = Vec::<u8>::new();
        v.push(0);

        Memory {

            cells: v,
            ptr:0,

        }

    }

    // Function to move the pointer to the right
    pub fn inc_ptr(&mut self) -> Result<(), Error> {

        self.ptr += 1;

        match self.ptr {

            n if n >= MEMORY_SIZE => {

                Result::Err(Error::PtrOverflow)

            }

            _ => {
                
                if self.ptr >= self.cells.len() {

                    self.cells.push(0);

                }

                Result::Ok(())

            }

        }

    }

    // Function to move the poiner to the left
    pub fn dec_ptr(&mut self) -> Result<(), Error> {

        match self.ptr {

            n if n == 0 => {

                Result::Err(Error::PtrUnderflow)

            }

            _ => {

                self.ptr -= 1;

                Result::Ok(())

            }

        }

    }

    // Function to increase the value
    pub fn inc_val(&mut self) -> Result<(), Error> {

        match self.cells[self.ptr].checked_add(1) {
            
            Some(v) => {

                self.cells[self.ptr] = v;

                Result::Ok(())

            }

            None => Result::Err(Error::ValOverflow),

        }

    }

    // Function to decrease the value
    pub fn dec_val(&mut self) -> Result<(), Error> {

        match self.cells[self.ptr].checked_sub(1) {

            Some(v) => {

                self.cells[self.ptr] = v;

                Result::Ok(())

            }

            None => Result::Err(Error::ValUnderflow),

        }

    }
     
    // Function to get the current value
    pub fn get_val(&self) -> u8 {

        self.cells[self.ptr]

    }

    // Function to set the current value
    pub fn set_val(&mut self, v:u8) {

        self.cells[self.ptr] = v;

    }

}

core/parser.rs

use super::instruction::{mapper, Instruction};
use super::program::Program;
use super::error::Error;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::convert::AsRef;

pub struct Parser {

    code_buffer:Vec<u8>,

}

impl Parser {
     
    // Function to create and return a new Parser
    pub fn new() -> Parser {

        Parser {
            code_buffer:Vec::<u8>::new(),
        }

    }

    // Function to load code from simple text
    pub fn load<S: Into<String>>(&mut self, buffer:S) -> Result<(), Error> {

        self.code_buffer = buffer.into().into_bytes();

        match self.code_buffer.len() {

            l if l > 0 => Result::Ok(()),

            _ => Result::Err(Error::EmptyProgram),

        }

    }

    // Function to load code from a file
    pub fn load_from_file<P: AsRef<Path>>(&mut self, src:P) -> Result<(), Error> {

        match File::open(src) {

            Ok(mut file) => {
                
                let mut content = String::new();

                match file.read_to_string(&mut content) {

                    _ => self.load(content)

                }

            }

            Err(_) => Result::Err(Error::NoSuchFile),

        }

    }

    // Function to compile the code into intermidiate code (program)
    pub fn parse(&mut self) -> Result<Program, Error> {

        // Create a new program
        let mut prog = Program::new();

        // Create a stack for the loop brackets
        let mut loop_stack = Vec::<usize>::new();

        // Create a code buffer ptr
        let mut code_ptr:usize = 0;

        // Compile the code buffer and feed the program
        if self.code_buffer.len() != 0 {
            
            loop {

                match self.code_buffer[code_ptr] as char {

                    b @ '+' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ '-' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ '>' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ '<' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ ',' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ '.' => { prog.push(mapper::get_instr(b).unwrap()); }
                    b @ '[' => { 

                        // Add the instruction to the program (leave 0 for now)
                        prog.push(mapper::get_instr(b).unwrap());

                        // Update the loop stack
                        loop_stack.push(prog.get_size() - 1);

                     }
                    ']' => { 

                        if loop_stack.is_empty() {

                            // Missmatch `]` bracket found
                            return Result::Err(Error::MissingOpenBracket);

                        }

                        let curr_ptr = prog.get_size();

                        let last_open_bracket_ptr = loop_stack.pop().unwrap();

                        prog.update_instr(last_open_bracket_ptr, Instruction::OpenBracket(curr_ptr));

                        prog.push(Instruction::CloseBracket(last_open_bracket_ptr));

                     }
                    _   => { /* IGNORE UNUSED SYMBOLS */ }

                };

                code_ptr += 1;

                if code_ptr >= self.code_buffer.len() {
                    break;
                }

            }

            // Check if loop_stack is empty or not -> not = error (missmatched brackets)
            if !loop_stack.is_empty() {
                return Result::Err(Error::MissingCloseBracket);
            }

        } else {

            // The code buffer is empty -> empty code loaded or just never loaded
            return Result::Err(Error::EmptyProgram);

        }
        
        // Program is empty return Error (code buffer contains only unused symbols)
        if prog.get_size() == 0 {
            return Result::Err(Error::EmptyProgram);
        }

        return Result::Ok(prog);

    }

}

core/program.rs

use super::instruction::Instruction;

// Struct for the program (a series of instructions)
pub struct Program {
    
    instr:Vec<Instruction>,
    ptr:usize,

}

impl Program {

    // Function to create and return a new program
    pub fn new() -> Program {

        Program {

            instr:Vec::<Instruction>::new(),
            ptr:0,

        }

    }

    // Function to increase the pointer
    pub fn inc_ptr(&mut self) -> bool {

        if self.ptr < self.instr.len() - 1 {
            self.ptr += 1;
            return true;
        }

        false

    }

    // Function to decrease the pointer
    #[allow(unused)]
    pub fn dec_ptr(&mut self) -> bool {

        if self.ptr > 0 {
            self.ptr -= 1;
            return true;
        }

        false

    }

    // Function to move the pointer
    pub fn set_ptr(&mut self, pos:usize) -> bool {

        if pos >= self.get_size() {
            return false;
        }

        self.ptr = pos;

        true

    }

    // Function to get the current instruction (if a program is already loaded and the instruction exists)
    pub fn get(&self) -> Option<Instruction> {

        if self.instr.len() > 0 { Option::Some(self.instr[self.ptr]) } else { Option::None }

    }

    // Function to increase the pointer and get the instruction
    #[allow(unused)]
    pub fn get_next(&mut self) -> Option<Instruction> {

        if self.inc_ptr() { self.get() } else { Option::None }

    }

    // Function to decrease the pointer and get the instruction
    #[allow(unused)]
    pub fn get_prev(&mut self) -> Option<Instruction> {

        if self.dec_ptr() { self.get() } else { Option::None }

    }

    // Function to get an instruction
    #[allow(unused)]
    pub fn get_at(&self, p:usize) -> Option<Instruction> {

        if p >= (0 as usize) && p < self.instr.len() { Option::Some(self.instr[p]) } else { Option::None }

    }

    // Function to add a new instruction at the end of the program
    pub fn push(&mut self, i:Instruction) -> &mut Program {

        self.instr.push(i);

        self

    }

    // Function to remove the last instruction from the program (if exists)
    #[allow(unused)]
    pub fn pop(&mut self) -> bool {
        
        return match self.instr.pop() {

            Some(_) => {
                
                if self.ptr >= self.instr.len() {
                    if self.ptr > 0 {
                        self.ptr -= 1;
                    }
                }

                true

            }

            None => false

        }

    }

    // Function to clear the program and set pointer to 0
    #[allow(unused)]
    pub fn clear(&mut self) -> &mut Program {

        self.instr.clear();
        self.ptr = 0;

        self

    }
    
    // Function to reset the pointer (set to 0)
    #[allow(unused)]
    pub fn reset_ptr(&mut self) -> &mut Program {

        self.ptr = 0;

        self

    }

    // Function to update a specific instruction
    pub fn update_instr(&mut self, pos:usize, new_instr:Instruction) -> bool {

        if self.instr.len() == 0 || pos >= self.instr.len() {
            return false;
        }

        self.instr[pos] = new_instr;

        return true;

    }

    // Function to get program's size
    pub fn get_size(&self) -> usize {

        self.instr.len()

    }

}