Skip to content

Instantly share code, notes, and snippets.

@innateessence
Last active April 25, 2024 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save innateessence/726ac93ede9d7c48cc8bffa1fc6f5384 to your computer and use it in GitHub Desktop.
Save innateessence/726ac93ede9d7c48cc8bffa1fc6f5384 to your computer and use it in GitHub Desktop.
Esoteric Language Interpreter [brainfuck language] [naive implementation]
#!/usr/bin/env python
'''
author: JackofSpades
I watched a 33 second video on the brainfuck
language, which originally looked like a horrid
abomination, however after learning how it works
It's still an abomination, but it's kind of cute
This is my quick and dirty interpretation of how it works
based off watching a 33 second video and spending maybe
90 minutes writing code give or take :)
video link: https://www.youtube.com/watch?v=tcHaMWktCYE
Also, It'd be cool to say I wrote an interpreter :p
NOTE: I didn't look anything up in order to implement this rough draft
What works:
- incrementing/decrementing bytes
- shifting the instruction pointer
- underflows (and theorically overflows)
Limitations:
- Nested brainfuck loops won't work by this design (This was an implementation oversight)
Bugs:
- Currently, loops are broken, it appears the instruction pointer
isn't moving to the next instruction and getting stuck at the
beggining of a for loop, will probably be easy to fix.
TODO:
- Fix bugs (duh.jpg)
Unknown:
- Python list[-1] will get the last item in the list, Brainfuck probably doesn't behave this way
Which means we should either error or do nothing (not sure how Brainfuck handles this)
Possible Improvements:
- Lazy-expanding array? :D (IE: start with 8 bytes, and expand the array on-demand?)
This is fun, I'll probably come back to this, but I should get back to work :p
'''
import sys
from queue import Queue
memory_pointer = 0
instruction_pointer = 0
# we don't have actual pointers in python
# so we use an index value of a list as the 'memory' 'pointer'
# and an index value of a string as the interpreter 'pointer'
# This is sufficient for brainfuck
memory = []
# memory is a list of ints ranging from 0-255 to mimick a byte
instructions = ""
# a string of arity operators (operators that take no parameters)
# this string will serve as our languages instructions
# our interpreter will interpret these instructions
opcodes = {
'>': lambda: shift_memory_pointer(increment=True),
'<': lambda: shift_memory_pointer(increment=False),
'+': lambda: shift_byte(increment=True),
'-': lambda: shift_byte(increment=False),
'[': lambda: while_not(),
'.': lambda: read_mem(),
}
op_queue = Queue()
def alloc_mem(number_of_bytes=1024):
'''
here we create an 'array' of 'bytes'
represented as integers between 0-255
'''
global memory
memory = list(bytes(number_of_bytes))
def free_mem():
global memory
del(memory)
memory = []
# Op codes
def shift_memory_pointer(increment=True):
'''
This simulated the behavior of the operations:
> - shifts pointer to the right, or +1
< - shifts pointer to the left, or -1
'''
val = 1
if not increment:
val = val * -1
# we invert the value to decrement
# instead of increment here
global memory_pointer
memory_pointer += 1
def shift_byte(increment=True):
global memory
global memory_pointer
val = 1
if not increment:
val = val * -1
memory[memory_pointer] += val
if memory[memory_pointer] > 255:
memory[memory_pointer] -= 256
if memory[memory_pointer] < 0:
memory[memory_pointer] += 256
assert memory[memory_pointer] >= 0 and memory[memory_pointer] <= 255
# TODO: We should perform overflows and underflows
# Since that's what brainfuck does
def while_not():
global memory
global memory_pointer
global instruction_pointer
global instructions
global op_queue
operations = ''
found_end_loop = False
try:
assert instructions[instruction_pointer] == '['
except AssertionError:
crash_report()
instruction_pointer_start_pos = instruction_pointer
instruction_pointer_end_pos = instruction_pointer
for char in instructions[instruction_pointer:]:
operations += char
instruction_pointer_end_pos += 1
if char == ']':
# set instruction_pointer_end_pos to the end of the loop +1
# to ensure we're at the operation immediately preceeding the loop
instruction_pointer_end_pos += 1
break
if not found_end_loop:
raise Exception
loop_running = True
while loop_running:
for operation in operations:
op_queue.put(operation)
instruction_pointer = instruction_pointer_start_pos # restart the loop
if memory[memory_pointer] == 0:
loop_running = False
instruction_pointer = instruction_pointer_end_pos # jump to end of the loop
def crash_report():
global memory
global memory_pointer
global instruction_pointer
global op_queue
print("###########################")
print("Interpreter info")
print("op:", instructions[instruction_pointer])
print("ptr:", instruction_pointer)
print("===========================")
print("Memory info\n")
print("memory:", memory)
print("ptr:", memory_pointer)
print("byte: int {} | char {}".format(memory[memory_pointer], chr(memory[memory_pointer])))
print("===========================")
print("OpCode Queue info (FIFO callstack)")
print("Queue:", op_queue.queue)
print("###########################")
def execute():
global instructions
global instruction_pointer
try:
operation = instructions[instruction_pointer]
opcodes[operation]()
instruction_pointer += 1
except Exception:
crash_report()
def execute_queue():
global op_queue
for i in range(op_queue.qsize()):
op = op_queue.get()
opcodes[op]()
def read_mem():
global memory
output = ""
for byte in memory:
output = "{}{}".format(output, chr(byte))
print(output)
def parse_instructions():
global instructions
instructions = sys.stdin.read().strip()
def execute_instructions():
global instructions
for instruction in instructions:
execute()
if __name__ == '__main__':
alloc_mem(8) # Allocate 8 bytes
parse_instructions() # parse operations from stdin
execute_instructions() # execute operations
@innateessence
Copy link
Author

innateessence commented May 18, 2021

I'll probably come back to this eventually. This needs to be refactored and debugged as it stands, but wasn't work related and had a lot of fun so shrug.jpg

Hope I come back to it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment