From 88cdcfa54f18fc8bf05b123411fd811e6f37c4d7 Mon Sep 17 00:00:00 2001 From: SpookyDervish <78246495+SpookyDervish@users.noreply.github.com> Date: Mon, 1 Sep 2025 18:00:49 +1000 Subject: [PATCH] ITS GENERATING AN EXECUTABLE LETS GOOOO --- generators/generator.py | 26 ++++++++++++++++++++++++++ generators/x86_64.py | 22 ++++++++++++++++++++++ ground_ast.py | 12 ++++++++++++ main.py | 26 +++++++++++++++++++++++--- out | Bin 0 -> 4648 bytes test2.grnd | 13 +------------ tokenizer.py | 8 +++++++- 7 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 generators/generator.py create mode 100644 generators/x86_64.py create mode 100644 out diff --git a/generators/generator.py b/generators/generator.py new file mode 100644 index 0000000..9208b1e --- /dev/null +++ b/generators/generator.py @@ -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) \ No newline at end of file diff --git a/generators/x86_64.py b/generators/x86_64.py new file mode 100644 index 0000000..e152fbd --- /dev/null +++ b/generators/x86_64.py @@ -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") \ No newline at end of file diff --git a/ground_ast.py b/ground_ast.py index aeaf139..79641ab 100644 --- a/ground_ast.py +++ b/ground_ast.py @@ -52,6 +52,9 @@ class LabelRefNode: @dataclass class LineRefNode: line: int +@dataclass +class BoolNode: + value: bool def generate_ast(tokens: list[Token], code: str) -> RootNode: root_node = RootNode([]) @@ -97,6 +100,12 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode: else: 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: if current_node_type == "inst": current_node.arguments.append(NumberNode(token.value)) @@ -154,6 +163,9 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode: elif token.type == TokenType.LABEL_DECLERATION: scope.statements.append(LabelDecNode(token.value)) + + elif token.type == TokenType.COMMENT: + continue elif token.type == TokenType.EOF: root_node.statements.append(current_node) diff --git a/main.py b/main.py index 6de7d9f..9b32859 100644 --- a/main.py +++ b/main.py @@ -2,19 +2,39 @@ from tokenizer import tokenize from ground_ast import generate_ast from rich import print from time import time +from generators import x86_64 +from os import system, remove +from error import traceback def main(): + in_path = "test2.grnd" + out_path = "out" + arch = "x86_64" + start = time() - file = open("test2.grnd", "r") + file = open(in_path, "r") code = file.read() file.close() tokens = tokenize(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 - print(ast) - print(f"Compiled in {compile_time} seconds.") + print(f"Compiled in {round(compile_time, 1)} seconds.") if __name__ == "__main__": diff --git a/out b/out new file mode 100644 index 0000000000000000000000000000000000000000..e63e2e03969758f8d77da63d880dea12a050e56b GIT binary patch literal 4648 zcmb<-^>JfjWMqH=CI&kOFi*e%ECeAL7+wg#R4_0&Fjz1!0ZCQ{7O+|tgcOhk(*dD3 zK;>XG1JoRl9jqWZn7L5<7@#x{R3D6ntAmPa2qlU`Tn2|YLw;$AUSe@BLwtNvadCWcNn%k6LwssVVo4&9%1dEj&?~OYElErQ z(j`R@Is?WkNv$YBv7LO5G*Bxj4A@{s!2AmxzkrWZK;>b>8ZhM`y-Y9;5-k8#H~}ig k3{}Sg