I've made an interpreted language that's like Brainfuck except it has keywords instead of characters. Here's an explanation of the commands, and how to run it.
add_ostream
- Add the ASCII value of the current cell to the output stream.chg_size & [position];
- Change the current position on the stack.reset_stack;
- Reset the stack and all its cells to the defaults.chg_size & [size];
- Change the size of the stack. This resets all cell values.reset_pos;
- Reset the current position on the stack.chg_cell & [value];
- Change the value of the current cell.out_stream;
- Output the output stream.reset_ostream;
- Reset the output stream.
To run the program simply type this into your command prompt:
python NotBF.py /path/to/notbffile.txt
"""
NotBF v0.1.0
---------------------------------------
NotBF is an interpreted Brainfuck-like
language. NotBF is has many similarites
to regular Brainfuck, except without
all the confusing characters.
---------------------------------------
"""
from sys import exit, argv
class NotBFError(object):
"""
This is the base NotBF error class from which
all NotBF errors are derived from.
"""
def __init__(self, message, name, code):
self.message = message
self.name = name
self.code = code
def raise_error(self):
"""
Raise an error if something goes wrong.
"""
print "{0}::{1} -> {2}".format(self.code, self.name, self.message)
exit(0)
class Environment(object):
"""
This class provides the data and functions
for managing a NotBF environment during runtime.
"""
def __init__(self, stack_size, output_stream,):
self.stack_size = stack_size
self.output_stream = output_stream
self.stack = [0 for _ in range(self.stack_size)]
self.stack_position = 0
self.max_stack_pos = len(self.stack)-1
self.min_stack_pos = 0
self.max_cell_value = 255
self.min_cell_value = 0
def add_output_stream(self, character):
"""
Add a character to the output stream.
"""
self.output_stream += character
def reset_stack(self):
"""
Reset the stack to it's default length,
self.stack_size, and reset all cells.
"""
self.stack = [0 for _ in range(self.stack_size)]
def change_stack_size(self, new_size):
"""
Change the size of the stack. WARNING,
this operation resets all cell values.
"""
self.stack = [0 for _ in range(new_size)]
def reset_stack_position(self):
"""
Reset the stack_position to zero.
"""
self.stack_position = 0
def change_stack_position(self, new_position):
"""
Move the stack_position to a new position.
"""
self.stack_position = new_position
def change_cell_value(self, new_value):
"""
Change the value of a cell.
"""
self.stack[self.stack_position] = new_value
def output_output_stream(self):
"""
Output the output stream.
"""
print self.output_stream
def reset_output_stream(self):
"""
Reset the output stream.
"""
self.output_stream = ""
class NotBFCommand(object):
"""
A base command class where tokenized input
is inputted into and then run. All NotBF
command classes are derived from this base
class.
"""
def __init__(self, tokenized_string):
self.tokenized_string = tokenized_string
def debug_input(self):
print self.tokenized_string
"""
Initalize various variables and other
items to make sure that command classes
work the way they should.
"""
runtime_env = Environment(256, "")
integer_error = NotBFError("Invalid integer.", "int_error", "e01")
no_cell_error = NotBFError("Cell doesn't exist.", "no_cell_error", "e02")
bad_value_error = NotBFError("Invalid ASCII code.", "bad_value_error", "e03")
command_error = NotBFError("Invalid command.", "cmd_error", "e04")
class AddOutputStream(NotBFCommand):
def execute(self):
character = chr(runtime_env.stack[runtime_env.stack_position])
runtime_env.add_output_stream(character)
class ResetStack(NotBFCommand):
def execute(self):
runtime_env.reset_stack()
class ChangeStackSize(NotBFCommand):
def execute(self):
try:
new_size = int(self.tokenized_string[1])
runtime_env.change_stack_size(new_size)
except ValueError:
integer_error.raise_error()
class ResetStackPosition(NotBFCommand):
def execute(self):
runtime_env.reset_stack_position()
class ChangeStackPosition(NotBFCommand):
def execute(self):
try:
new_position = int(self.tokenized_string[1])
try:
runtime_env.change_stack_position(new_position)
except IndexError:
no_cell_error.raise_error()
except ValueError:
integer_error.raise_error()
class ChangeCellValue(NotBFCommand):
def execute(self):
try:
new_value = int(self.tokenized_string[1])
if new_value <= runtime_env.max_cell_value and new_value >= runtime_env.min_cell_value:
runtime_env.change_cell_value(new_value)
else:
bad_value_error.raise_error()
except ValueError:
integer_error.raise_error()
class OutputOutputStream(NotBFCommand):
def execute(self):
runtime_env.output_output_stream()
class ResetOutputStream(NotBFCommand):
def execute(self):
runtime_env.reset_output_stream()
class GetCodeInput(object):
"""
Get code file input from a path.
"""
def __init__(self, code_file_path):
self.code_file_path = code_file_path
def return_file(self):
""""""
with open(self.code_file_path, "r") as code_file:
return code_file.read().replace("\n", "").replace(" ", "").replace("\t", "")
class Tokenizer(object):
"""
Tokenize given input into a format that is readable
by the interpreter class. Here's the format.
tokenized_string = [ [ keyword, arg, ... ], [ ... ], ... ]
"""
def __init__(self, input_string, line_split=";", arg_split="&"):
self.input_string = input_string
self.line_split = line_split
self.arg_split = arg_split
def tokenize(self):
"""
Tokenize the string input into the
correct format.
"""
tokenized_string = self.input_string.split(self.line_split)
tokenized_string = [string.split(self.arg_split) for string in tokenized_string]
tokenized_string.remove([""])
print tokenized_string
return tokenized_string
class Interpreter(object):
def __init__(self, tokenized_input):
self.tokenized_input = tokenized_input
self.COMMAND_KEYS = {
"add_ostream": AddOutputStream,
"chg_pos": ChangeStackPosition,
"reset_stack": ResetStack,
"chg_size": ChangeStackSize,
"reset_pos": ResetStackPosition,
"chg_cell": ChangeCellValue,
"out_stream": OutputOutputStream,
"reset_ostream": ResetOutputStream,
}
def execute_input(self):
for line in self.tokenized_input:
token = line[0]
if token in self.COMMAND_KEYS:
command_to_execute = self.COMMAND_KEYS[token](line)
command_to_execute.execute()
else:
command_error.raise_error()
if __name__ == "__main__":
code_input = GetCodeInput(argv[1]).return_file()
tokenized_code = Tokenizer(code_input).tokenize()
interpreter = Interpreter(tokenized_code).execute_input()
Here's some example code and its output:
chg_cell & 48; add_ostream; chg_pos & 1; chg_cell & 49; add_ostream; chg_pos & 1; reset_stack; chg_size & 512; chg_cell & 65; add_ostream; chg_pos & 1; chg_cell & 66; add_ostream; chg_pos & 1; out_stream; reset_stack; reset_ostream;
Here's the output of this program:
[['chg_cell', '48'], ['add_ostream'], ['chg_pos', '1'], ['chg_cell', '49'], ['add_ostream'], ['chg_pos', '1'], ['reset_stack'], ['chg_size', '512'], ['chg_cell', '65'], ['add_ostream'], ['chg_pos', '1'], ['chg_cell', '66'], ['add_ostream'], ['chg_pos', '1'], ['out_stream'], ['reset_stack'],['reset_ostream']] 01AB