Initial Commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
src/__pycache__
|
||||||
|
tests/test.grnd
|
||||||
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Brain Assembly
|
||||||
|
|
||||||
|
Brain Assembly is a ground-like language that compiles to Brainfuck. It provides variable creation, C-like pointers, and other tools that make it feel more familiar to use.
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://chookspace.com/DiamondNether90/BrainAssemblyV2 BrainAssembly
|
||||||
|
cd BrainAssembly
|
||||||
|
sudo ln -s (pwd)/src/main.py /usr/local/bin/basm
|
||||||
|
```
|
||||||
|
|
||||||
|
To compile a file, run:
|
||||||
|
```bash
|
||||||
|
basm (filename) (writefile) (flags)
|
||||||
|
```
|
||||||
|
Example usage:
|
||||||
|
```bash
|
||||||
|
# Compile tests/test.basm and write code to test.bf:
|
||||||
|
basm tests/test.basm test.bf
|
||||||
|
```
|
||||||
|
|
||||||
|
# Syntax
|
||||||
|
Syntax can be found in `docs/syntax.md`
|
||||||
2
docs/syntax.md
Normal file
2
docs/syntax.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Implemented Commands
|
||||||
|
|
||||||
5
src/error.py
Normal file
5
src/error.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
def error(errorType: str = 'Error', message: str = "This is a bugged error message. Please report this issue."):
|
||||||
|
return f"\033[91m{errorType}: \033[0m{message}"
|
||||||
|
|
||||||
|
def warn(message: str):
|
||||||
|
return f"\033[33mWarning: {message}\033[0m"
|
||||||
13
src/interpreter.py
Normal file
13
src/interpreter.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import parser as p
|
||||||
|
import error as err
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Feel free to replace the return type
|
||||||
|
def run_program(parsed: list[p.ParsedLine]) -> None:
|
||||||
|
for line in parsed:
|
||||||
|
match line.keyword:
|
||||||
|
case p.Keyword.Default:
|
||||||
|
sys.exit(err.error('unknownError', 'This is a bugged error message that should never be reached.'))
|
||||||
|
# Add implementation for other commands here
|
||||||
|
case _:
|
||||||
|
sys.exit(err.error('notImplementedError', f'{line.keyword.value} keyword not implemented yet'))
|
||||||
39
src/lexer.py
Normal file
39
src/lexer.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
def tokenise(code: str) -> list[list[str]]:
|
||||||
|
token: str = ''
|
||||||
|
tokens: list[list[str]] = [[]]
|
||||||
|
|
||||||
|
isString = False
|
||||||
|
isComment = False
|
||||||
|
|
||||||
|
for char in code:
|
||||||
|
if isString:
|
||||||
|
token += char
|
||||||
|
if char == '"' or char == "'":
|
||||||
|
isString = False
|
||||||
|
elif isComment and char != '\n':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
match char:
|
||||||
|
case '\n':
|
||||||
|
isComment = False
|
||||||
|
if token != '' or tokens[-1] != []:
|
||||||
|
if token != '':
|
||||||
|
tokens[-1].append(token)
|
||||||
|
token = ''
|
||||||
|
tokens.append([])
|
||||||
|
case ' ':
|
||||||
|
tokens[-1].append(token)
|
||||||
|
token = ''
|
||||||
|
case '\'':
|
||||||
|
isString = True
|
||||||
|
token += '\''
|
||||||
|
case '\"':
|
||||||
|
isString = True
|
||||||
|
token += '\"'
|
||||||
|
case '#':
|
||||||
|
isComment = True
|
||||||
|
case _:
|
||||||
|
token += char
|
||||||
|
if token:
|
||||||
|
tokens[-1].append(token)
|
||||||
|
return tokens
|
||||||
25
src/main.py
Executable file
25
src/main.py
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import lexer
|
||||||
|
import parser
|
||||||
|
import interpreter
|
||||||
|
import error as err
|
||||||
|
|
||||||
|
def main(argc: int, argv: list[str]):
|
||||||
|
if argc < 3:
|
||||||
|
return f'Usage: {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)
|
||||||
|
nodes = parser.parse(tokens)
|
||||||
|
interpreter.run_program(nodes)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(len(sys.argv), sys.argv))
|
||||||
145
src/parser.py
Normal file
145
src/parser.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
from enum import Enum
|
||||||
|
import error as err
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Keyword(Enum):
|
||||||
|
Default = -1
|
||||||
|
# All below can be replaced
|
||||||
|
If = 'if'
|
||||||
|
Jump = 'jump'
|
||||||
|
End = 'end'
|
||||||
|
Stdin = 'stdin'
|
||||||
|
Stdout = 'stdout'
|
||||||
|
Stdlnout = 'stdlnout'
|
||||||
|
Set = 'set'
|
||||||
|
Gettype = 'gettype'
|
||||||
|
Exists = 'exists'
|
||||||
|
Setlist = 'setlist'
|
||||||
|
Setlistat = 'setlistat'
|
||||||
|
Getlistat = 'getlistat'
|
||||||
|
Getlistsize = 'getlistsize'
|
||||||
|
Listappend = 'listappend'
|
||||||
|
Getstrsize = 'getstrsize'
|
||||||
|
Getstrcharat = 'getstrcharat'
|
||||||
|
Add = 'add'
|
||||||
|
Subtract = 'subtract'
|
||||||
|
Multiply = 'multiply'
|
||||||
|
Divide = 'divide'
|
||||||
|
Equal = 'equal'
|
||||||
|
Inequal = 'inequal'
|
||||||
|
Not = 'not'
|
||||||
|
Greater = 'greater'
|
||||||
|
Lesser = 'lesser'
|
||||||
|
Stoi = 'stoi'
|
||||||
|
Stod = 'stod'
|
||||||
|
Tostring = 'tostring'
|
||||||
|
Fun = 'fun'
|
||||||
|
Return = 'return'
|
||||||
|
Endfun = 'endfun'
|
||||||
|
Pusharg = 'pusharg'
|
||||||
|
Call = 'call'
|
||||||
|
Struct = 'struct'
|
||||||
|
Endstruct = 'endstruct'
|
||||||
|
Init = 'init'
|
||||||
|
Use = 'use'
|
||||||
|
Extern = 'extern'
|
||||||
|
|
||||||
|
class ArgType(Enum):
|
||||||
|
Keyword = 0 # e.g. end if
|
||||||
|
Value = 1 # e.g. println $var
|
||||||
|
Direct = 2 # e.g. setlist &var
|
||||||
|
Line = 3 # e.g. jump %start
|
||||||
|
Function = 4 # e.g. call !doStuff
|
||||||
|
Type = 5 # e.g. call -int ...
|
||||||
|
Literal_String = 6 # e.g. println "Hello, world!"
|
||||||
|
Literal_Int = 7 # e.g. println 97104
|
||||||
|
Literal_Char = 8 # e.g. println 'a'
|
||||||
|
|
||||||
|
keywords: dict[str, Keyword] = {
|
||||||
|
'': Keyword.Default,
|
||||||
|
# All below can be replaced
|
||||||
|
'if': Keyword.If,
|
||||||
|
'jump': Keyword.Jump,
|
||||||
|
'end': Keyword.End,
|
||||||
|
'input': Keyword.Stdin,
|
||||||
|
'stdin': Keyword.Stdin,
|
||||||
|
'print': Keyword.Stdout,
|
||||||
|
'stdout': Keyword.Stdout,
|
||||||
|
'println': Keyword.Stdlnout,
|
||||||
|
'stdlnout': Keyword.Stdlnout,
|
||||||
|
'set': Keyword.Set,
|
||||||
|
'gettype': Keyword.Gettype,
|
||||||
|
'exists': Keyword.Exists,
|
||||||
|
'setlist': Keyword.Setlist,
|
||||||
|
'setlistat': Keyword.Setlistat,
|
||||||
|
'getlistat': Keyword.Getlistat,
|
||||||
|
'getlistsize': Keyword.Getlistsize,
|
||||||
|
'listappend': Keyword.Listappend,
|
||||||
|
'getstrsize': Keyword.Getstrsize,
|
||||||
|
'getstrcharat': Keyword.Getstrcharat,
|
||||||
|
'add': Keyword.Add,
|
||||||
|
'subtract': Keyword.Subtract,
|
||||||
|
'multiply': Keyword.Multiply,
|
||||||
|
'divide': Keyword.Divide,
|
||||||
|
'equal': Keyword.Equal,
|
||||||
|
'inequal': Keyword.Inequal,
|
||||||
|
'not': Keyword.Not,
|
||||||
|
'greater': Keyword.Greater,
|
||||||
|
'lesser': Keyword.Lesser,
|
||||||
|
'stoi': Keyword.Stoi,
|
||||||
|
'stod': Keyword.Stod,
|
||||||
|
'tostring': Keyword.Tostring,
|
||||||
|
'fun': Keyword.Fun,
|
||||||
|
'return': Keyword.Return,
|
||||||
|
'endfun': Keyword.Endfun,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParsedLine:
|
||||||
|
def __init__(self, keyword = '', args=[]):
|
||||||
|
self.keyword: Keyword = keywords[keyword]
|
||||||
|
self.args: list[tuple[ArgType, str]] = args
|
||||||
|
def __repr__(self):
|
||||||
|
return f'({self.keyword}, {self.args})'
|
||||||
|
|
||||||
|
def parse(tokens: list[list[str]]) -> list[ParsedLine]:
|
||||||
|
parsed_lines: list[ParsedLine] = []
|
||||||
|
for line in tokens:
|
||||||
|
parsed = ParsedLine('', [])
|
||||||
|
try:
|
||||||
|
parsed.keyword = keywords[line[0]]
|
||||||
|
except KeyError:
|
||||||
|
sys.exit(err.error('syntaxError', f'Invalid keyword: {line[0]}'))
|
||||||
|
|
||||||
|
for arg in line[1:]:
|
||||||
|
match arg[0]:
|
||||||
|
case '$':
|
||||||
|
parsed.args.append((ArgType.Value, arg[1:]))
|
||||||
|
case '&':
|
||||||
|
parsed.args.append((ArgType.Direct, arg[1:]))
|
||||||
|
case '%':
|
||||||
|
parsed.args.append((ArgType.Line, arg[1:]))
|
||||||
|
case '!':
|
||||||
|
parsed.args.append((ArgType.Function, arg[1:]))
|
||||||
|
case '-':
|
||||||
|
parsed.args.append((ArgType.Type, arg[1:]))
|
||||||
|
case _:
|
||||||
|
# Todo
|
||||||
|
if arg[0] == arg[-1] == '\'':
|
||||||
|
if len(arg) != 3:
|
||||||
|
sys.exit(err.error('typeError', f'Invalid char: {arg}'))
|
||||||
|
parsed.args.append((ArgType.Literal_Char, arg[1]))
|
||||||
|
elif arg[0] == arg[-1] == '\"':
|
||||||
|
parsed.args.append((ArgType.Literal_String, arg[1:-1]))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
parsed.args.append((ArgType.Literal_Int, str(int(arg))))
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
parsed.args.append((ArgType.Keyword, arg))
|
||||||
|
except AttributeError:
|
||||||
|
sys.exit(err.error('syntaxError', f'Invalid literal: {arg}'))
|
||||||
|
|
||||||
|
parsed_lines.append(parsed)
|
||||||
|
|
||||||
|
return parsed_lines
|
||||||
Reference in New Issue
Block a user