Failed attempt, just for reference
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
src/__pycache__
|
||||||
|
test.bf
|
||||||
|
tests/test.basm
|
||||||
207
src/compiler.py
Normal file
207
src/compiler.py
Normal 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
5
src/error.py
Normal 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
33
src/lexer.py
Normal 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
22
src/main.py
Normal 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))
|
||||||
Reference in New Issue
Block a user