Created
June 16, 2016 04:36
-
-
Save mediocregopher/69a569c998d2b2147dfadc795415eff2 to your computer and use it in GitHub Desktop.
Simple implementation of a brainfuck interpreter in rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
[dependencies] | |
clap = "2" | |
*/ | |
extern crate clap; | |
use clap::{Arg, App}; | |
use std::num::Wrapping; | |
use std::io::Read; | |
use std::fs::File; | |
const TAPE_SIZE: usize = 64; | |
struct Tape(Vec<Option<Box<[u8;TAPE_SIZE]>>>); | |
/// Implements an infinite tape of values, each being one byte in size. Data is allocated sparsely | |
/// on the heap | |
impl Tape { | |
fn new() -> Tape { | |
Tape(Vec::new()) | |
} | |
fn indices(&self, i: u64) -> (usize, usize) { | |
let vec_i = (i / (TAPE_SIZE as u64)) as usize; | |
let inner_i = (i % (TAPE_SIZE as u64)) as usize; | |
(vec_i, inner_i) | |
} | |
fn get(&self, i: u64) -> u8 { | |
let (vec_i, inner_i) = self.indices(i); | |
match self.0.get(vec_i) { | |
None => 0, | |
Some(&None) => 0, | |
Some(&Some(ref res)) => res[inner_i], | |
} | |
} | |
fn map<F>(&mut self, i: u64, f: F) | |
where F : Fn(u8) -> u8 { | |
let (vec_i, inner_i) = self.indices(i); | |
while self.0.len() <= vec_i { | |
self.0.push(None) | |
} | |
let opt = &mut self.0[vec_i]; | |
if opt.is_none() { | |
*opt = Some(Box::new([0;TAPE_SIZE])); | |
} | |
if let Some(ref mut p) = *opt { | |
p[inner_i] = f(p[inner_i]); | |
} | |
} | |
} | |
// TODO these could be made into one generic function | |
// followup: fuck it | |
fn prev_matching_bracket(src: &Vec<u8>, mut ir: usize) -> usize { | |
let mut bc = 1; | |
loop { | |
ir = ir - 1; | |
// don't bother comparing for 0, it'll panic due to underflow | |
if src[ir] == '[' as u8 { | |
bc = bc - 1; | |
} else if src[ir] == ']' as u8 { | |
bc = bc + 1; | |
} | |
if bc == 0 { | |
return ir; | |
} | |
} | |
} | |
fn next_matching_bracket(src: &Vec<u8>, mut ir: usize) -> usize { | |
let mut bc = 1; | |
loop { | |
ir = ir + 1; | |
if ir >= src.len() { panic!("couldn't find next matching ']'") }; | |
if src[ir] == '[' as u8 { | |
bc = bc + 1; | |
} else if src[ir] == ']' as u8 { | |
bc = bc - 1; | |
} | |
if bc == 0 { | |
return ir; | |
} | |
} | |
} | |
fn main() { | |
let matches = App::new("brainrust") | |
.arg(Arg::with_name("INPUT") | |
.help("Sets the brainfuck source file to use") | |
.required(true) | |
.index(1)) | |
.arg(Arg::with_name("v") | |
.short("v") | |
.multiple(true) | |
.help("Sets the level of verbosity")) | |
.get_matches(); | |
let input = matches.value_of("INPUT").unwrap(); | |
let mut src = Vec::<u8>::new(); | |
File::open(input).expect("can't open file") | |
.read_to_end(&mut src).expect("couldn't read source file"); | |
let src = src; // src is immutable once read in | |
let verbosity = matches.occurrences_of("v"); | |
let mut t = Tape::new(); | |
let mut ir: usize = 0; // instruction pointer | |
let mut p: u64 = 0; // tape pointer | |
let mut maxp: u64 = 0; // max value p ever reaches, for final debug output | |
loop { | |
let c = src[ir] as char; | |
match c { | |
// we wrap these so they wrap around if out-of-bounds | |
'+' => t.map(p, |i: u8| (Wrapping(i)+Wrapping(1)).0), | |
'-' => t.map(p, |i: u8| (Wrapping(i)-Wrapping(1)).0), | |
'>' => p = p + 1, | |
'<' => p = p - 1, | |
'.' => print!("{}",t.get(p) as char), | |
',' => { | |
let mut buf = [0; 1]; | |
// TODO this just blocks if there's no stdin, should read a 0 | |
match std::io::stdin().read_exact(&mut buf) { | |
Ok(_) => t.map(p, |_: u8| buf[0]), | |
Err(_) => {}, | |
} | |
}, | |
'[' => if t.get(p) == 0 { ir = next_matching_bracket(&src, ir) }, | |
']' => if t.get(p) != 0 { ir = prev_matching_bracket(&src, ir) }, | |
_ => {}, | |
} | |
if verbosity >= 2 { println!("ir:{} c:{} p:{} t[p]:{}", ir, c, p, t.get(p)); } | |
if p > maxp { | |
maxp = p; | |
} | |
ir = ir + 1; | |
if ir >= src.len() { | |
break | |
} | |
} | |
if verbosity >= 1 { | |
println!("\nFinal tape:"); | |
for i in 0..maxp+1 { | |
println!("t[{}]: {}", i, t.get(i)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment