Last active
July 15, 2018 16:38
-
-
Save cedws/482f72634cc0bdda6324ae1bfc5efd40 to your computer and use it in GitHub Desktop.
This file contains 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
const PROGRAM: &str = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; | |
const MAX_SCOPES: usize = 16; | |
const ALLOC_BLOCK_SIZE: usize = 128; | |
#[derive(Copy, Clone)] | |
enum Instruction { | |
PointerLeft, | |
PointerRight, | |
Increment, | |
Decrement, | |
Output, | |
Input, | |
JumpForwardIfZero(usize), | |
JumpBackIfNonZero(usize), | |
JumpUnmatched, | |
Ignored, | |
} | |
struct Interpreter { | |
program: Vec<Instruction>, | |
memory: Vec<u8>, | |
dpointer: usize, | |
ppointer: usize, | |
} | |
#[derive(Debug)] | |
enum InterpreterError { | |
UnsupportedInstruction, | |
JumpUnmatchedError(usize), | |
MaximumScopesReached(usize), | |
} | |
use InterpreterError::*; | |
impl Interpreter { | |
fn new(program: Vec<Instruction>) -> Self { | |
Interpreter { | |
program, | |
memory: vec![0; ALLOC_BLOCK_SIZE], | |
dpointer: 0, | |
ppointer: 0, | |
} | |
} | |
fn execute(&mut self, instruction: Instruction) -> Result<(), InterpreterError> { | |
// Expand the interpreter memory if needed. | |
if self.memory.len() < self.dpointer { | |
self.memory.resize(self.dpointer + ALLOC_BLOCK_SIZE, 0); | |
} | |
match instruction { | |
Instruction::PointerRight => { | |
self.dpointer = self.dpointer.saturating_add(1); | |
} | |
Instruction::PointerLeft => { | |
self.dpointer = self.dpointer.saturating_sub(1); | |
} | |
Instruction::Increment => { | |
self.memory[self.dpointer] = self.memory[self.dpointer].saturating_add(1); | |
} | |
Instruction::Decrement => { | |
self.memory[self.dpointer] = self.memory[self.dpointer].saturating_sub(1); | |
} | |
Instruction::Output => { | |
print!("{}", self.memory[self.dpointer] as char); | |
} | |
Instruction::Input => return Err(UnsupportedInstruction), | |
Instruction::JumpForwardIfZero(pos) => { | |
if self.memory[self.dpointer] == 0 { | |
self.ppointer = pos; | |
} | |
} | |
Instruction::JumpBackIfNonZero(pos) => { | |
if self.memory[self.dpointer] != 0 { | |
self.ppointer = pos; | |
} | |
} | |
Instruction::JumpUnmatched => return Err(JumpUnmatchedError(self.ppointer)), | |
Instruction::Ignored => (), | |
}; | |
self.ppointer += 1; | |
Ok(()) | |
} | |
} | |
impl Iterator for Interpreter { | |
type Item = Result<(), InterpreterError>; | |
fn next(&mut self) -> Option<Self::Item> { | |
if self.ppointer < self.program.len() { | |
let instruction = self.program[self.ppointer]; | |
Some(self.execute(instruction)) | |
} else { | |
None | |
} | |
} | |
} | |
fn tokenise(program: &str) -> Result<Vec<Instruction>, InterpreterError> { | |
let mut scopes = Vec::new(); | |
let mut tokens = Vec::new(); | |
for (i, token) in program.chars().enumerate() { | |
let instr = match token { | |
'>' => Instruction::PointerRight, | |
'<' => Instruction::PointerLeft, | |
'+' => Instruction::Increment, | |
'-' => Instruction::Decrement, | |
'.' => Instruction::Output, | |
',' => Instruction::Input, | |
'[' => { | |
scopes.push(i); | |
Instruction::JumpUnmatched | |
} | |
']' => { | |
if let Some(pos) = scopes.pop() { | |
tokens[pos] = Instruction::JumpForwardIfZero(i + 1); | |
Instruction::JumpBackIfNonZero(pos) | |
} else { | |
return Err(JumpUnmatchedError(i)); | |
} | |
} | |
_ => Instruction::Ignored, | |
}; | |
if scopes.len().ge(&MAX_SCOPES) { | |
return Err(MaximumScopesReached(scopes.len())); | |
} | |
tokens.push(instr); | |
} | |
if scopes.len().eq(&0) { | |
// There are still unmatched jumps. | |
return Err(JumpUnmatchedError(scopes.pop().unwrap())); | |
} | |
Ok(tokens) | |
} | |
fn main() { | |
let tokens = tokenise(PROGRAM).expect("Failed to parse program"); | |
let interpreter = Interpreter::new(tokens); | |
// Run the program and unwrap the Result of each cycle. | |
interpreter.for_each(Result::unwrap); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment