Skip to content

Instantly share code, notes, and snippets.

@cedws
Last active July 15, 2018 16:38
Show Gist options
  • Save cedws/482f72634cc0bdda6324ae1bfc5efd40 to your computer and use it in GitHub Desktop.
Save cedws/482f72634cc0bdda6324ae1bfc5efd40 to your computer and use it in GitHub Desktop.
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