ITS GENERATING AN EXECUTABLE LETS GOOOO

This commit is contained in:
SpookyDervish
2025-09-01 18:00:49 +10:00
parent 62e95a24ed
commit 88cdcfa54f
7 changed files with 91 additions and 16 deletions

26
generators/generator.py Normal file
View 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
View 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")

View File

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

26
main.py
View File

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

BIN
out Normal file

Binary file not shown.

View File

@@ -1,12 +1 @@
set &myName "Nathaniel"
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
end 123

View File

@@ -19,7 +19,8 @@ class TokenType(Enum):
COMMENT = 11 # example: # hi there
LINE_REFERENCE = 12 # example: %12
LABEL_REFERENCE = 13 # example: %myLabel
EOF = 14
BOOL = 14 # example: true
EOF = 15
@dataclass
class Token:
@@ -397,6 +398,11 @@ def tokenize(input_string: str):
TokenType.INSTRUCTION,
value=current_token
))
elif current_token in ["true", "false"]:
tokens.append(Token(
TokenType.BOOL,
value=current_token == "true"
))
else:
traceback(input_string, "SyntaxError", f"\"{current_token}\" isn't a valid instruction.", line, start_col, column)