diff --git a/docs/syntax.md b/docs/syntax.md index 97936bd..29500cf 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -1,18 +1,11 @@ +## Work in Progress + To create a comment, begin a line with a hash (#) symbol, like python. -So far, the supported types are `bool` and `string`. +Create a string: `string name value bytes` -## create name type bytes value +Create a boolean: `bool name value` -Creates the variable "var" with a set number of bytes allocated to it, and initialises its value. -**IMPORTANT:** The number of bytes cannot be modified later. +Create an integer: `int name value` -Example: `create var bool 1 true` - -To set a variable to an input byte, use: `create var string 1 i[idx]`. - -## print var - -Prints the value of `var` to the console, where `var` is a variable. - -Example: `print var` \ No newline at end of file +**Note**: A string of dynamic length can be created by setting the bytes to -1. \ No newline at end of file diff --git a/readme.md b/readme.md index fabb996..7974206 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,12 @@ BrainAssembly is a language that compiles to Brainfuck. Similar to Assembly languages, it has simple syntax, and is designed to make Brainfuck easier to write code for. Heavily inspired by Ground. Enjoy! +## Features + +- Simple syntax: The language is designed to be very simple, and a framework for larger projects. This also means that the syntax is quite easy to learn. + +**NOTE:** Due to the current lack of optimisation in the compiled code, we recommend an optimising intepreter such as [this one](https://copy.sh/brainfuck). + To run: - First clone this repository and install python. @@ -10,10 +16,21 @@ git clone https://chookspace.com/DiamondNether90/BrainAssembly ``` - Next, run the following command to compile a .basm file to Brainfuck: -``` +```bash python3 src/main.py path/to/file.basm path/to/file.bf ``` (Replace path/to/file.basm with the actual path to your desired .basm file, and replace path/to/file.bf with where you want the compiled file to be created.) -This will create or replace the contents of path/to/file.bf with the compiled BrainAssembly code. \ No newline at end of file +This will create or replace the contents of path/to/file.bf with the compiled BrainAssembly code. + +## Test the compiler with our in-built test programs! + +You can access our test programs in the test folder. + +Example: +```bash +python src/main.py tests/create.basm test.bf +``` + +This will create a Brainfuck file named test.bf that contains the compiled code of our test programs. \ No newline at end of file diff --git a/src/codegen.py b/src/codegen.py index 8848c97..61a82a5 100644 --- a/src/codegen.py +++ b/src/codegen.py @@ -2,115 +2,121 @@ from error import error import math as m class Variable: - def __init__(self, name: str, bytes: int, startByte: int, type: int): + def __init__(self, name: str, bytes: int, type: int): self.name: str = name self.bytes: int = bytes - self.startByte: int = startByte self.type: int = type def __repr__(self) -> str: - return f"[{self.name}, {self.bytes}, {self.startByte}, {self.type}]" + return f"[{self.name}, {self.bytes}, {self.type}]" variables: list[Variable] = [] bytesum: int = 0 -types = { - "bool": 0, - "string": 1, - "int": 2, -} - bool = { "true": 1, "false": 0, } def generateCode(line: list[str], offset: int) -> str: - try: - bytesum = variables[-1].startByte + variables[-1].bytes - except IndexError: - bytesum = 0 keyword = line[0] - if keyword == 'create': - if len(line) != 5: - error(f"Create command requires 4 arguments (got {len(line)-1})") - name = line[1] - try: - type = types[line[2]] - except KeyError: - error(f"Invalid type: {line[2]}") - quit() - bytes = int(line[3]) - value = line[4] - - if type == 0: - variables.append(Variable(name, bytes, bytesum, type)) - if (value == 'true'): - return '>' * offset + '[>]' + '>' * (bytesum + 1) + '+' + '<' * (bytesum + 1) + '<[<]' + '<' * (offset - 1) - elif (value == 'false'): - return '' - else: - error(f'Invalid bool: {value}') - elif type == 1: - if value[0] == value[-1] == '"': - value = value[1:-1] - variables.append(Variable(name, bytes, bytesum, type)) - returnval = '>' * offset + '[>]' + '>' * (bytesum + 1) - for i in range(bytes): - try: - returnval += '+' * ord(value[i]) + '>' - except IndexError: - returnval += '>' - returnval += '<' * (bytes+bytesum+2) + '[<]' + '<' * (offset - 1) - return returnval - elif (value[:2] == 'i[') & (value[-1] == ']'): - try: - validx: int = int(value[2:-1]) - except ValueError: - error(f'Invalid input index: {value[2:-1]}') - quit() - variables.append(Variable(name, bytes, bytesum, type)) - returnval = '>' * (offset + validx) - returnval += '[[>]' + '>' * (bytesum+1) + '+' + '<' * (bytesum+2) + '[<]<+' + '>' * (validx+2) + '-]' - returnval += '<[<]<[' + '>' * (validx+2) + '+<[<]<-]<<<' - - return returnval - else: - error(f"Invalid string: {value}") - elif type == 2: - # I hate integers - error('Integers are not yet implemented') - else: - error(f"Type {type} not yet implemented") - - error() - quit() - elif keyword == 'print': - if len(line) != 2: - error(f"Print command requires 1 argument (got {len(line)-1})") - - # Find value of variable - var: Variable | str = '' - for v in variables: - if v.name == line[1]: - var = v - - if var == '': - error(f'Could not find variable {line[1]}') - quit() - - if var.type == 0: - # Create copy - returnval = '>' * offset + '[>]' + '>' * (var.startByte + 1) + '[' + '<' * (var.startByte+2) + '[<]<+<+>>>' + '[>]>' + '>' * var.startByte + '-]' + '<' * (var.startByte + 2) + '[<]<' + '[>>[>]' + '>' * (var.startByte + 1) + '+' + '<' * (var.startByte + 2) + '[<]<-]' - # If true - returnval += '+<[>-<' + '+' * 115 + '.--.+++.----------------.[-]' + ']' - # else false - returnval += '>[' + '+' * 101 + '.-----.+++++++++++.+++++++.--------------.[-]' + ']<<<' + if keyword == 'bool': + if len(line) != 3: + error(f'Bool command requires 2 arguments (got {len(line)-1})') + variables.append(Variable(line[1], 1, 0)) + if line[2] == 'true': + returnval = '>' * offset + '[>]' + for var in variables[:-1]: + if var.bytes >= 0: + returnval += '>' * (var.bytes+1) + elif var.bytes == -1: + returnval += '>[>]' + else: + error() + returnval += '>+<' + for var in reversed(variables[:-1]): + if var.bytes >= 0: + returnval += '<' * (var.bytes+1) + elif var.bytes == -1: + returnval += '<[<]' + else: + error() + returnval += '<[<]' + '<' * (offset-1) return returnval - elif var.type == 1: - return '>' * offset + '[>]' + '>' * (var.startByte + 1) + '.>' * var.bytes + '<' * (var.startByte+var.bytes+2) + '[<]' + '<' * (offset - 1) + elif line[2] == 'false': + return '' else: - error('Type not yet supported') - quit() - else: - error(f"Invalid token: {keyword}") - quit() \ No newline at end of file + error(f"Invalid bool: {line[2]}") + elif keyword == 'string': + if len(line) != 4: + error(f'String command requires 3 arguments (got {len(line)-1})') + try: + variables.append(Variable(line[1], int(line[2]), 1)) + except ValueError: + error(f'Unexpected bytes for string: {line[2]}') + returnval = '>' * offset + '[>]' + for var in variables[:-1]: + if var.bytes >= 0: + returnval += '>' * (var.bytes + 1) + elif var.bytes == -1: + returnval += '>[>]' + else: + error('Cannot have negative byte count (excluding -1)') + + returnval += '>' + # Normal string + if line[3][0] == line[3][-1] == '"': + line[3] = line[3][1:-1] + if (len(line[3]) > int(line[2])) & (line[2] != '-1'): + line[3] = line[3][:(int(line[2])-len(line[3]))] + for char in line[3]: + returnval += '+' * ord(char) + '>' + else: + error(f'Invalid string: {line[3]}') + + # Return to start + for var in reversed(variables): + if var.bytes >= 0: + returnval += '<' * (var.bytes + 1) + elif var.bytes == -1: + returnval += '<[<]' + else: + error() + returnval += '<[<]' + '<' * (offset-1) + + return returnval + elif keyword == 'int': + if len(line) != 3: + error(f'Int requires 2 arguments (got {len(line)-1})') + variables.append(Variable(line[1], 4, 2)) + returnval: str = '>' * offset + '[>]' + for var in variables[:-1]: + if var.bytes >= 0: + returnval += '>' * (var.bytes+1) + elif var.bytes == -1: + returnval += '>[>]' + else: + error() + returnval += '>' + try: + input: int = int(line[2]) % 4294967296 + except ValueError: + error(f'Invalid integer: {line[2]}') + + values: list[int] = [] + for i in reversed(range(4)): + values.append(m.floor(input/(256**i))) + input -= values[-1] * (256**i) + for num in values: + returnval += '+' * num + '>' + returnval += '<<<<<' + for var in reversed(variables[:-1]): + if var.bytes >= 0: + returnval += '<' * (var.bytes + 1) + elif var.bytes == -1: + returnval += '<[<]' + else: + error() + returnval += '<[<]' + '<' * (offset-1) + + return returnval + error() \ No newline at end of file diff --git a/src/error.py b/src/error.py index 4a0f3df..1fb7803 100644 --- a/src/error.py +++ b/src/error.py @@ -1,2 +1,5 @@ def error(message: str = "This is a bugged error message. Please report this issue."): - exit(f"\033[91mError: \033[0m{message}") \ No newline at end of file + exit(f"\033[91mError: \033[0m{message}") + +def warn(message: str): + print(f"\033[33mWarning: {message}") \ No newline at end of file diff --git a/src/main.py b/src/main.py index 46e93fa..f48931d 100644 --- a/src/main.py +++ b/src/main.py @@ -31,11 +31,7 @@ code = preprocess(content) offset: int = 5 bfcode: str = '>' * offset + ',[>,]<[<]' + '<' * (offset-1) -def find(str: str, char: str): - if (char in str): - return str.find(char) - else: - return len(str) +open(argv[2], 'w').write('') for line in code: bfcode += generateCode(line, offset) diff --git a/tests/create.basm b/tests/create.basm index 40748cf..c696e3a 100644 --- a/tests/create.basm +++ b/tests/create.basm @@ -1,2 +1,8 @@ -create i3 string 1 i[3] -print i3 \ No newline at end of file +bool var true +bool var2 false +string str1 -1 "Hell" +bool var3 true +int num1 -1736671613 +int num2 9182364129 +string str2 5 "World!" +bool var4 true \ No newline at end of file