-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7f591a9
commit f6746ff
Showing
4 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import sys | ||
|
||
import stack | ||
import tape | ||
""" | ||
Brainfuck interpreter | ||
Uses a bre-built map of where to jump as an optimization. Code must be | ||
preprocessed, this is not a repl. | ||
""" | ||
|
||
|
||
class BFInterpreter(): | ||
def __init__(self): | ||
self.tape = tape.Tape() | ||
#self.stack = stack.Stack() # Stack for loops | ||
self.instruction_pointer = 0 # Current instruction | ||
self.program = [] # The program | ||
self.instructions = { ">":self.tape.move_forwards, \ | ||
"<":self.tape.move_backwards, \ | ||
"+":self.tape.increment, \ | ||
"-":self.tape.decrement, \ | ||
"[":self.enter_loop, \ | ||
"]":self.end_loop, \ | ||
".":self.output, \ | ||
",":self.input, \ | ||
"#":self.debug \ | ||
} | ||
self.jump_map = {} # A place to look for corresponding braces | ||
|
||
def preprocess_program(self, input_code=None): | ||
""" | ||
Preprocess the brainfuck | ||
Remove comments, split each individual instruction into the | ||
program tape. Clear the memory tape for the next execution. | ||
""" | ||
if (input_code == None): | ||
return; | ||
map(self.program.append, filter((lambda (char): char in self.instructions), input_code)) | ||
#self.program.append(False) # Causes interpretation to stop after the program has finished. | ||
self.build_jump_map(self.program) | ||
|
||
def build_jump_map(self, input_program): | ||
""" | ||
Build a map of where each "]" has an opening "[". | ||
input_program must be a list of bf commands. | ||
""" | ||
open_bracket_stack = stack.Stack() | ||
for inst, command in enumerate(input_program): | ||
if command in (">","<","+","-",".",",","#",): | ||
# Ignore all normal brainfuck characters. | ||
pass | ||
elif (command=="["): | ||
# We've found one, push it's location onto the stack. | ||
open_bracket_stack.push(inst) | ||
elif (command=="]"): | ||
# We've found a closing one. Map this location to the location | ||
# on the top of the stack | ||
try: | ||
previous_bracket = open_bracket_stack.pop() | ||
self.jump_map[previous_bracket]=inst | ||
self.jump_map[inst] = previous_bracket | ||
except: | ||
print "Missing open bracket." | ||
#print self.jump_map | ||
|
||
def output(self): | ||
""" | ||
Outputs the value of the current cell. | ||
""" | ||
try: | ||
sys.stdout.write(chr(self.tape.current_cell()%256)) # Wrapping fits it into ascii codes | ||
except: | ||
print "Error -001" | ||
|
||
def input(self): | ||
""" | ||
Set the current cell to a new value | ||
""" | ||
try: | ||
temp = ord(raw_input()) | ||
self.tape.replace(temp) | ||
except: | ||
print "Error -002" | ||
|
||
def enter_loop(self): | ||
""" | ||
Do the code goodness for entering a loop. | ||
""" | ||
if (self.tape.current_cell()==0): | ||
# Jump past the end. | ||
self.instruction_pointer = (self.jump_map[self.instruction_pointer]) | ||
else: | ||
pass | ||
|
||
|
||
def end_loop(self): | ||
""" | ||
Jump to the start of the loop if the current cell is not 0. | ||
""" | ||
# if (not self.tape.current_cell()): | ||
# Jump to the start of the loop | ||
self.instruction_pointer = (self.jump_map[self.instruction_pointer]-1) | ||
#else: | ||
# pass | ||
|
||
|
||
def execute(self): | ||
""" | ||
Execute the cleaned brainfuck program | ||
Will only correctly run after preprocess_program() has been run. | ||
""" | ||
while len(self.program)>(self.instruction_pointer): | ||
self.step() | ||
#self.tape = tape.Tape() # Clear for next time. | ||
|
||
def step(self): | ||
""" | ||
Do a single step in a brainfuck program | ||
""" | ||
try: | ||
self.instructions[self.program[self.instruction_pointer]]() | ||
self.instruction_pointer+=1 | ||
except tape.TapeError: | ||
print "Tape underflow, instruction number: "+str(self.instruction_pointer) | ||
raise | ||
|
||
def debug(self): | ||
""" | ||
Print debugging information. | ||
""" | ||
print "Tape: "+str(self.tape.tape[:10])+" Current Pointer: "+str(self.tape.pointer)+" Instruction Pointer: "+str(self.instruction_pointer) | ||
|
||
def clear_tape(self): | ||
""" | ||
Clear the tape for next round. | ||
""" | ||
self.tape = tape.Tape() | ||
|
||
def __str__(self): | ||
""" | ||
A string representation of the interpreter | ||
""" | ||
return str((self.instruction_pointer, self.program,)) | ||
|
||
def __len__(self): | ||
""" | ||
The length of this brainfuck interpreter | ||
""" | ||
return len(self.tape) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import bfinterpreter | ||
import time | ||
|
||
class Sandbox(object): | ||
""" | ||
This is a sandbox for running limited brainfuck programs | ||
""" | ||
def __init__(self, max_time=None, max_mem=None): | ||
""" | ||
Setup the sandbox. | ||
This can accept a maximum time for running, a maximum memory | ||
allocation allowed. | ||
""" | ||
if (max_time==None): | ||
self.max_time=10000 | ||
else: | ||
self.max_time = max_time | ||
if (max_mem==None): | ||
self.max_mem=10000 | ||
else: | ||
self.max_mem = max_mem | ||
self.start_time = None | ||
|
||
def run(self, code=None): | ||
""" | ||
Run some code in this sandbox | ||
""" | ||
if (code==None): | ||
return False | ||
self.bfi = bfinterpreter.BFInterpreter() | ||
self.bfi. preprocess_program(code) | ||
self.start_time = int(time.time()) | ||
while len(self.bfi.program)>(self.bfi.instruction_pointer): | ||
if (not self.exceeded()): | ||
self.bfi.step() | ||
else: | ||
return False | ||
return True | ||
|
||
def exceeded(self): | ||
""" | ||
Return true if this code has exceeded it's memory or time allowance | ||
""" | ||
return (self.max_mem < len(self.bfi.tape)) or (self.max_time < (int(time.time()) - self.start_time)) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
""" | ||
A nice little wrapper for a stack class | ||
""" | ||
|
||
class Stack(): | ||
def __init__(self): | ||
""" | ||
Initialize an empty stack | ||
""" | ||
self.stack=[] | ||
|
||
def pop(self): | ||
""" | ||
Get an element of the top of the stack | ||
""" | ||
try: | ||
return self.stack.pop() | ||
except: | ||
raise Exception("Stack underflow.") | ||
|
||
def push(self, val=None): | ||
""" | ||
Push an element onto the top of the stack | ||
""" | ||
if (val == None): | ||
raise ValueError("Must put a none-None value on the stack") | ||
else: | ||
self.stack.append(val) | ||
return True | ||
|
||
def __len__(self): | ||
""" | ||
Find the size of this stack | ||
""" | ||
return len(self.stack) | ||
|
||
def __str__(self): | ||
""" | ||
Show a str representation of this object | ||
""" | ||
return str(self.stack[:5]) # First 5 elements |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
""" | ||
A Tape | ||
Represents a tape, initialized to 0s with a few functions predefined on it. | ||
""" | ||
|
||
class TapeError( Exception ): | ||
pass | ||
|
||
class Tape(object): | ||
""" | ||
A Tape Object | ||
This tape object will automatically expand to the right should an | ||
overflow be detected. Default size is 30,000. | ||
""" | ||
def __init__(self): | ||
""" | ||
Initalize the tape | ||
This tape initially has 30,000 cells and all are 0 | ||
""" | ||
self.tape = [0 for x in range(1,30001,1)] | ||
self.pointer = 0 | ||
|
||
def increment(self): | ||
""" | ||
Increment the value under the pointer | ||
""" | ||
try: | ||
self.tape[self.pointer]+=1 | ||
except: | ||
self.expand() | ||
self.tape[self.pointer]+=1 | ||
|
||
def decrement(self): | ||
""" | ||
Decrement the value under the pointer | ||
""" | ||
try: | ||
self.tape[self.pointer]-=1 | ||
except: | ||
self.expand() | ||
self.tape[self.pointer]-=1 | ||
|
||
def move_forwards(self): | ||
""" | ||
Move the tape forwards | ||
This actually just increments the internal pointer. Also deals with if the tape is | ||
all used, declares more tape. Doubles the length of the tape when more is needed. | ||
""" | ||
if ((self.pointer) == len(self.tape)): | ||
self.expand() | ||
self.pointer+=1 | ||
|
||
def move_backwards(self): | ||
""" | ||
Move the tape backwards. | ||
As in the move_forwards, just decrements an internal pointer. | ||
""" | ||
if (self.pointer == 0): | ||
raise TapeError | ||
self.pointer-=1 | ||
|
||
def replace(self, value=None): | ||
""" | ||
Replace the value of the current cell | ||
The current cell will be replaced the value given. This raises an | ||
error on value not being specified | ||
""" | ||
if (value == None): | ||
raise TapeError | ||
else: | ||
self.tape[self.pointer] = value # No value wrapping to make large | ||
# value calculations easier. | ||
|
||
def current_cell(self): | ||
""" | ||
Get the value of the current cell | ||
""" | ||
return self.tape[self.pointer] | ||
|
||
def __getitem__(self, i=None): | ||
""" | ||
Get the i-th element of this tape | ||
""" | ||
if (i!=None): | ||
return self.tape[i] | ||
|
||
def __eq__(self, other_tape): | ||
""" | ||
Test that this tape is equal to another. | ||
""" | ||
return (self.tape == other_tape.tape) | ||
|
||
def __ne__(self, other_tape): | ||
""" | ||
Test if this tape is not equal to another. | ||
""" | ||
return (self.tape != other_tape.tape) | ||
|
||
def __len__(self): | ||
""" | ||
Return the length of this tape. | ||
""" | ||
return len(self.tape) | ||
def expand(self): | ||
""" | ||
Double the length of this tape with fresh 0s | ||
""" | ||
self.tape += [0 for x in range(len(self.tape))] |