Last active
April 25, 2024 21:20
-
-
Save innateessence/726ac93ede9d7c48cc8bffa1fc6f5384 to your computer and use it in GitHub Desktop.
Esoteric Language Interpreter [brainfuck language] [naive implementation]
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
#!/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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 :)