Failed attempt, just for reference

This commit is contained in:
2025-12-19 08:34:04 +11:00
commit 78ce4f18f8
5 changed files with 270 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
src/__pycache__
test.bf
tests/test.basm

207
src/compiler.py Normal file
View File

@@ -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) <type: bool, char, int> <name> <bytes>"'))
# 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 <var> <list index(s)> <value>'))
# 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

5
src/error.py Normal file
View File

@@ -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"

33
src/lexer.py Normal file
View File

@@ -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

22
src/main.py Normal file
View File

@@ -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))