From 78ce4f18f8c1779b62cab4f47a02004dac4846fd Mon Sep 17 00:00:00 2001 From: DiamondNether90 Date: Fri, 19 Dec 2025 08:34:04 +1100 Subject: [PATCH] Failed attempt, just for reference --- .gitignore | 3 + src/compiler.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++++ src/error.py | 5 ++ src/lexer.py | 33 ++++++++ src/main.py | 22 +++++ 5 files changed, 270 insertions(+) create mode 100644 .gitignore create mode 100644 src/compiler.py create mode 100644 src/error.py create mode 100644 src/lexer.py create mode 100644 src/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b67b7e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +src/__pycache__ +test.bf +tests/test.basm \ No newline at end of file diff --git a/src/compiler.py b/src/compiler.py new file mode 100644 index 0000000..ec91e44 --- /dev/null +++ b/src/compiler.py @@ -0,0 +1,207 @@ +import error as err +from enum import Enum +import sys + +# Note: Layout of cells is as follows +# [0] [input...] [0] [data start] +# ptr_address is relative to the first cell of data + +def compile(code: list[list[str]]): + returnval = '>,[>,]>' + used_bytes = 0 + ptr_address = 0 + + class Types(Enum): + Bool = 'Boolean' + Char = 'Character' + Int = 'Integer' + + types = { + 'bool': Types.Bool, + 'char': Types.Char, + 'int': Types.Int + } + + bytes = { + Types.Bool: 1, + Types.Char: 1, + Types.Int: 4, + } + + class Variable: + def __init__(self, name: str, dimensions: int, type: Types, address: int, malloc: list[int]): + nonlocal used_bytes + self.address = address + self.malloc = malloc + self.name = name + self.dimensions = dimensions + self.type = type + used_bytes += malloc[0] + # If the type is li2 int, then malloc might be [4, 100, 1000] + # i.e. List has 1000 bytes, which stores lists that have 100 bytes, which stores 4-byte ints + def __repr__(self): + return f'({self.name}, {self.dimensions}, {self.type.value}, {hex(self.address)}, {self.malloc})' + + class Constant: + def __init__(self, name: str, type: Types): + self.name = name + self.type = type + def __repr__(self): + return f'({self.name}, {self.type.value})' + + var: dict[str, Variable] = { + + } + + # Actually start code + for line in code: + # Check keyword (first token) + keyword = line[0] + args = len(line) + if keyword[0] + keyword[1] == 'li': + if len(line) < 3: + sys.exit(err.error('li command syntax is "li(dim) "')) + # Check dimensions + try: + dim = int(keyword[2::]) + except ValueError: + exit(err.error(f'Invalid type: {keyword}')) + + malloc: list[int] = [] + i = 0 + try: + for i in range(dim): + malloc.append(int(line[3+i])) + malloc.append(bytes[types[line[1]]]) + except ValueError: + sys.exit(err.error(f'Invalid byte allocation for variable: {line[3+i]} (bytes)')) + except IndexError: + sys.exit(err.error(f'Not enough byte declarations for variable {line[2]}')) + except KeyError: + sys.exit(err.error(f'Invalid type: {line[1]}')) + + # Create new variable + var[line[2]] = Variable(line[2], dim, types[line[1]], used_bytes, malloc) + elif keyword == 'ptr': + match line[1]: + case 'mv': + if len(line) < 3: + sys.exit(err.error('ptr mv requires variable name or int')) + target: str | int = line[2] + try: + target = int(target) + except ValueError: + try: + vartarget = var[target] + target = vartarget.address + i = 0 + try: + for i in range(vartarget.dimensions): + target += vartarget.malloc[i+1] * int(line[3+i]) + except IndexError: + sys.exit(err.error(f'ptr mv requires the same number of list indexes as dimensions (got {i})')) + except ValueError: + sys.exit(err.error(f'ptr mv: Expected list index, got {list[3+i]}')) + except KeyError: + sys.exit(err.error(f'ptr mv requires variable name or int (got {target})')) + else: + pass + # Currently, target points to the start of the variable + # For lists, we may want the start of a specific item + + # Move pointer + if ptr_address < target: + returnval += '>' * (target - ptr_address) + elif ptr_address > target: + returnval += '<' * (ptr_address - target) + ptr_address = target + case 'add': + if len(line) < 3: + sys.exit(err.error('ptr add requires int')) + + try: + target = int(line[2]) + except ValueError: + sys.exit(err.error(f'ptr add: Expected int, got {line[2]}')) + + if target > 0: + ptr_address += target + returnval += '>' * target + elif target < 0: + ptr_address -= target + returnval += '<' * target + case 'subtract': + if len(line) < 3: + sys.exit(err.error('ptr subtract requires int')) + + try: + target = int(line[2]) + except ValueError: + sys.exit(err.error(f'ptr subtract: Expected int, got {line[2]}')) + + if target > 0: + ptr_address -= target + returnval += '<' * target + elif target < 0: + ptr_address += target + returnval += '>' * target + case 'back': + if len(line) != 2: + sys.exit(err.error(f'ptr back does not take additional arguments (got {line[2:]})')) + case _: + sys.exit(err.error(f'Expected pointer action, got {line[1]}')) + elif keyword == 'set': + # Get variable + try: + vartarget = var[line[1]] + except KeyError: + sys.exit(err.error(f'set: Expected var, got {line[1]}')) + + if args != 3 + vartarget.dimensions: + sys.exit(err.error('set syntax: set ')) + + # Target cell for ptr is var.address + line[2] * var.malloc[0] + line[3] * var.malloc[1] + ... + target = vartarget.address + for i in range(vartarget.dimensions): + try: + target += int(line[i+2]) * vartarget.malloc[i+1] + except ValueError: + sys.exit(f'set: Expected list index, got {line[i+2]}') + + # Move pointer + if ptr_address < target: + returnval += '>' * (target - ptr_address) + elif ptr_address > target: + returnval += '<' * (ptr_address - target) + ptr_address = target + + # Do stuff based on type + match vartarget.type: + case Types.Char: + returnval += '[-]' + '+' * ord(line[-1]) + case _: + sys.exit(err.error(f'set: {vartarget.type.value} type not implemented yet')) + elif keyword == 'setbyte': + if len(line) < 2: + sys.exit(err.error('setbyte command requires two arguments')) + try: + returnval += '[-]' + '+' * int(line[1]) + except ValueError: + try: + returnval += '[-]' + '+' * ord(line[1]) + except TypeError: + sys.exit(err.error(f'setbyte: Expected int or char, got {line[1]}')) + elif keyword == 'print': + try: + type = types[line[1]] + except KeyError: + sys.exit(err.error(f'print: Invalid type: {line[1]}')) + + if type == Types.Char: + returnval += '.' + else: + sys.exit(err.error(f'print: {line[1]} not implemented')) + else: + sys.exit(err.error(f'Invalid command: {keyword}')) + + return returnval \ No newline at end of file diff --git a/src/error.py b/src/error.py new file mode 100644 index 0000000..8b85c2e --- /dev/null +++ b/src/error.py @@ -0,0 +1,5 @@ +def error(message: str = "This is a bugged error message. Please report this issue."): + return f"\033[91mError: \033[0m{message}" + +def warn(message: str): + return f"\033[33mWarning: {message}\033[0m" \ No newline at end of file diff --git a/src/lexer.py b/src/lexer.py new file mode 100644 index 0000000..5bda05a --- /dev/null +++ b/src/lexer.py @@ -0,0 +1,33 @@ +def tokenise(code: str) -> list[list[str]]: + token: str = '' + tokens: list[list[str]] = [[]] + + isString = False + isComment = False + for char in code + ' ': + if isString and char != ('\''): + token += char + elif isComment: + if char == '\n': + isComment = False + else: + match char: + case ' ' | '\n': + if token: + tokens[-1].append(token) + token = '' + case '\'': + isString = not isString + case '#': + isComment = True + case ';': + if token: + tokens[-1].append(token) + tokens.append([]) + token = '' + case _: + token += char + + if not tokens[-1]: + tokens.pop() + return tokens \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..375e66d --- /dev/null +++ b/src/main.py @@ -0,0 +1,22 @@ +import sys +import lexer +import compiler +import error as err + +def main(argc: int, argv: list[str]): + if argc < 3: + return f'Usage: python {argv[0]} (filename) (writefile) (flags)' + + try: + with open(argv[1], 'r') as file: + content = file.read() + except FileNotFoundError: + return err.error(f'File {argv[1]} not found.') + + tokens = lexer.tokenise(content) + with open(argv[2], 'w') as f: + f.write(compiler.compile(tokens)) + return 0 + +if __name__ == "__main__": + sys.exit(main(len(sys.argv), sys.argv)) \ No newline at end of file