ITS GENERATING AN EXECUTABLE LETS GOOOO
This commit is contained in:
26
generators/generator.py
Normal file
26
generators/generator.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from ground_ast import RootNode
|
||||||
|
|
||||||
|
|
||||||
|
class Generator:
|
||||||
|
def __init__(self, ast: RootNode, code: str, output_path: str):
|
||||||
|
self.ast = ast
|
||||||
|
self.lines: list[str] = []
|
||||||
|
self.code = code
|
||||||
|
self.output_path = output_path
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_node(self, node):
|
||||||
|
self.lines.append(f"; {node}\n\t")
|
||||||
|
node_type = str(type(node))[19:-2]
|
||||||
|
if not hasattr(self, f"generate_{node_type}"):
|
||||||
|
raise Exception(f"Generator has no generate method for {node_type}.")
|
||||||
|
getattr(self, f"generate_{node_type}")(node)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
for statement in self.ast.statements:
|
||||||
|
self.generate_node(statement)
|
||||||
|
|
||||||
|
with open(self.output_path + ".asm", "w") as f:
|
||||||
|
f.writelines(self.lines)
|
22
generators/x86_64.py
Normal file
22
generators/x86_64.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from generators.generator import Generator
|
||||||
|
from ground_ast import *
|
||||||
|
from error import traceback
|
||||||
|
|
||||||
|
class X86_64Generator(Generator):
|
||||||
|
def init(self):
|
||||||
|
self.lines.append("global _start\n\n")
|
||||||
|
self.lines.append("_start:\n\t")
|
||||||
|
self.generate()
|
||||||
|
|
||||||
|
def generate_InstructionNode(self, node: InstructionNode):
|
||||||
|
if node.instruction == "end":
|
||||||
|
if len(node.arguments) == 0:
|
||||||
|
traceback(self.code, "TypeError", "end expects atleast 1 argument.")
|
||||||
|
elif len(node.arguments) > 1:
|
||||||
|
traceback(self.code, "TypeError", "end expects only 1 argument.")
|
||||||
|
if not isinstance(node.arguments[0], NumberNode):
|
||||||
|
traceback(self.code, "TypeError", f"end expects an integer, not {type(node.arguments[0])}")
|
||||||
|
|
||||||
|
self.lines.append("mov rax, 60\n\t")
|
||||||
|
self.lines.append("mov rdi, " + str(node.arguments[0].value) + "\n\t")
|
||||||
|
self.lines.append("syscall\n\n\t")
|
@@ -52,6 +52,9 @@ class LabelRefNode:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class LineRefNode:
|
class LineRefNode:
|
||||||
line: int
|
line: int
|
||||||
|
@dataclass
|
||||||
|
class BoolNode:
|
||||||
|
value: bool
|
||||||
|
|
||||||
def generate_ast(tokens: list[Token], code: str) -> RootNode:
|
def generate_ast(tokens: list[Token], code: str) -> RootNode:
|
||||||
root_node = RootNode([])
|
root_node = RootNode([])
|
||||||
@@ -97,6 +100,12 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode:
|
|||||||
else:
|
else:
|
||||||
traceback(code, "SyntaxError", "Expected instruction, not string.")
|
traceback(code, "SyntaxError", "Expected instruction, not string.")
|
||||||
|
|
||||||
|
elif token.type == TokenType.BOOL:
|
||||||
|
if current_node_type == "inst":
|
||||||
|
current_node.arguments.append(BoolNode(token.value))
|
||||||
|
else:
|
||||||
|
traceback(code, "SyntaxError", "Expected instruction, not string.")
|
||||||
|
|
||||||
elif token.type == TokenType.INTEGER or token.type == TokenType.FLOAT:
|
elif token.type == TokenType.INTEGER or token.type == TokenType.FLOAT:
|
||||||
if current_node_type == "inst":
|
if current_node_type == "inst":
|
||||||
current_node.arguments.append(NumberNode(token.value))
|
current_node.arguments.append(NumberNode(token.value))
|
||||||
@@ -155,6 +164,9 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode:
|
|||||||
elif token.type == TokenType.LABEL_DECLERATION:
|
elif token.type == TokenType.LABEL_DECLERATION:
|
||||||
scope.statements.append(LabelDecNode(token.value))
|
scope.statements.append(LabelDecNode(token.value))
|
||||||
|
|
||||||
|
elif token.type == TokenType.COMMENT:
|
||||||
|
continue
|
||||||
|
|
||||||
elif token.type == TokenType.EOF:
|
elif token.type == TokenType.EOF:
|
||||||
root_node.statements.append(current_node)
|
root_node.statements.append(current_node)
|
||||||
last_token = token
|
last_token = token
|
||||||
|
26
main.py
26
main.py
@@ -2,19 +2,39 @@ from tokenizer import tokenize
|
|||||||
from ground_ast import generate_ast
|
from ground_ast import generate_ast
|
||||||
from rich import print
|
from rich import print
|
||||||
from time import time
|
from time import time
|
||||||
|
from generators import x86_64
|
||||||
|
from os import system, remove
|
||||||
|
from error import traceback
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
in_path = "test2.grnd"
|
||||||
|
out_path = "out"
|
||||||
|
arch = "x86_64"
|
||||||
|
|
||||||
start = time()
|
start = time()
|
||||||
file = open("test2.grnd", "r")
|
file = open(in_path, "r")
|
||||||
code = file.read()
|
code = file.read()
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
tokens = tokenize(code)
|
tokens = tokenize(code)
|
||||||
ast = generate_ast(tokens, code)
|
ast = generate_ast(tokens, code)
|
||||||
|
generator = None
|
||||||
|
|
||||||
|
if arch == "x86_64":
|
||||||
|
generator = x86_64.X86_64Generator(ast, code, out_path)
|
||||||
|
else:
|
||||||
|
traceback(code, "fatal error", f"unkown architecture \"{arch}\"")
|
||||||
|
|
||||||
|
generator.init()
|
||||||
|
|
||||||
|
system(f"nasm -felf64 {out_path}.asm")
|
||||||
|
system(f"ld -o {out_path} {out_path}.o -m elf_{arch}")
|
||||||
|
remove(out_path + ".o")
|
||||||
|
remove(out_path + ".asm")
|
||||||
|
|
||||||
compile_time = time()-start
|
compile_time = time()-start
|
||||||
print(ast)
|
print(f"Compiled in {round(compile_time, 1)} seconds.")
|
||||||
print(f"Compiled in {compile_time} seconds.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
13
test2.grnd
13
test2.grnd
@@ -1,12 +1 @@
|
|||||||
set &myName "Nathaniel"
|
end 123
|
||||||
set &myAge 10
|
|
||||||
|
|
||||||
fun -list !split -string &str -string &determiner
|
|
||||||
set &x 2
|
|
||||||
set &y 5
|
|
||||||
add $x $y &x
|
|
||||||
return $x
|
|
||||||
endfun
|
|
||||||
|
|
||||||
@myLabel
|
|
||||||
jump %myLabel
|
|
@@ -19,7 +19,8 @@ class TokenType(Enum):
|
|||||||
COMMENT = 11 # example: # hi there
|
COMMENT = 11 # example: # hi there
|
||||||
LINE_REFERENCE = 12 # example: %12
|
LINE_REFERENCE = 12 # example: %12
|
||||||
LABEL_REFERENCE = 13 # example: %myLabel
|
LABEL_REFERENCE = 13 # example: %myLabel
|
||||||
EOF = 14
|
BOOL = 14 # example: true
|
||||||
|
EOF = 15
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Token:
|
class Token:
|
||||||
@@ -397,6 +398,11 @@ def tokenize(input_string: str):
|
|||||||
TokenType.INSTRUCTION,
|
TokenType.INSTRUCTION,
|
||||||
value=current_token
|
value=current_token
|
||||||
))
|
))
|
||||||
|
elif current_token in ["true", "false"]:
|
||||||
|
tokens.append(Token(
|
||||||
|
TokenType.BOOL,
|
||||||
|
value=current_token == "true"
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
traceback(input_string, "SyntaxError", f"\"{current_token}\" isn't a valid instruction.", line, start_col, column)
|
traceback(input_string, "SyntaxError", f"\"{current_token}\" isn't a valid instruction.", line, start_col, column)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user