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
|
||||
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
26
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__":
|
||||
|
13
test2.grnd
13
test2.grnd
@@ -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
|
@@ -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)
|
||||
|
||||
|
Reference in New Issue
Block a user