From 459f53a4e1ff8096d22b96a701f39ca0fd82bd6c Mon Sep 17 00:00:00 2001 From: SpookyDervish <78246495+SpookyDervish@users.noreply.github.com> Date: Thu, 4 Sep 2025 07:45:20 +1000 Subject: [PATCH] =?UTF-8?q?ok=20we=20have=20basic=20decimal=20math=20?= =?UTF-8?q?=F0=9F=91=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generators/generator.py | 12 +++ generators/x86_64.py | 174 +++++++++++++++++++++++++++++----------- ground_ast.py | 21 +++-- ground_types.py | 3 - main.py | 2 +- out | Bin 4664 -> 496 bytes out.asm | 18 ++--- test2.grnd | 4 +- 8 files changed, 162 insertions(+), 72 deletions(-) delete mode 100644 ground_types.py diff --git a/generators/generator.py b/generators/generator.py index a4d699b..48d9582 100644 --- a/generators/generator.py +++ b/generators/generator.py @@ -1,4 +1,5 @@ from ground_ast import RootNode +from typing import Any class Generator: @@ -8,6 +9,17 @@ class Generator: self.code = code self.output_path = output_path self.variables = {} + self.constants = {} + self.constants_reverse = {} + self.constant_counter = 0 + + def add_constant(self, value: Any): + existing_constant_name = self.constants_reverse.get(value, None) + if existing_constant_name != None: return f"[.{existing_constant_name}]" + self.constants["LC" + str(self.constant_counter)] = value + self.constants_reverse[value] = "LC" + str(self.constant_counter) + self.constant_counter += 1 + return "[.LC" + str(self.constant_counter-1) + "]" def init(self): pass diff --git a/generators/x86_64.py b/generators/x86_64.py index 8393558..54e1f6e 100644 --- a/generators/x86_64.py +++ b/generators/x86_64.py @@ -23,15 +23,30 @@ class X86_64Generator(Generator): self.lines.append("pop " + reg + "\n\t") self.stack_size -= 1 - def get_variable(self, var_name: str, reg: str): + def get_variable(self, var_name: str, reg: str, float: bool = False): var = self.variables.get(var_name, None) + var_pos = self.get_var_pos(var_name) try: - self.push( - f"QWORD [rsp + {(self.stack_size - var['stack_loc'] - 1) * 8}]" - ) + #print(var["type"]) + if var["type"] == FloatNode or float: + conversion = { + "rax": "xmm0", + "rbx": "xmm1", + "rdi": "xmm0" + # ... + } + + self.lines.append(f"movsd {conversion[reg]}, [rsp + {var_pos}]\n\t") + self.lines.append("add rsp, 8\n\t") + #self.stack_size -= 1 + elif var["type"] == IntNode: + self.push( + f"QWORD [rsp + {var_pos}]" + ) + self.pop(reg) + except TypeError: # variable doesnt exist traceback(self.code, "NameError", f"\"{var_name}\" is not defined.") - self.pop(reg) return var["type"] def get_var_pos(self, var_name: str): @@ -41,23 +56,39 @@ class X86_64Generator(Generator): if var_type == None: var_type = type(starting_value) - new_var = {"stack_loc": (self.stack_size), "type": var_type} - if type(starting_value) == NumberNode: + stack_location = self.stack_size + + if type(starting_value) == IntNode: self.lines.append(f"mov rax, {starting_value.value}\n\t") self.push("rax") elif type(starting_value) == VarRefNode: - self.get_variable(starting_value.var_name, "rax") - #self.lines.append(f"mov QWORD [rsp + {var_pos}], rax\n\t") - self.push("rax") + var_type = self.get_variable(starting_value.var_name, "rax") + + if var_type == FloatNode: + self.lines.append("sub rsp, 8\n\t") + self.lines.append("movsd [rsp], xmm0\n\t") + else: + self.push("rax") + elif type(starting_value) == FloatNode: + name = self.add_constant(starting_value.value) + self.lines.append("sub rsp, 8\n\t") # make space on the stack + self.lines.append(f"movsd xmm0, {name}\n\t") + self.lines.append("movsd [rsp], xmm0\n\t") + self.stack_size += 1 + elif type(starting_value) == str: - self.push(starting_value) - self.variables[var_name] = new_var + if starting_value.startswith("xmm"): # floating point stuff + self.lines.append("sub rsp, 8\n\t") # make space + self.lines.append(f"movsd [rsp], {starting_value}\n\t") + self.stack_size += 1 + else: + self.push(starting_value) + self.variables[var_name] = {"stack_loc": stack_location, "type": var_type} def change_variable(self, var_name: str, new_value): var_pos = self.get_var_pos(var_name) - if type(new_value) == NumberNode: # we're changing a variable to a number - #self.lines.append(f"mov rax, {new_value.value}\n\t") + if type(new_value) == IntNode: # we're changing a variable to a number self.lines.append(f"mov QWORD [rsp + {var_pos}], {new_value.value}\n\t") elif type(new_value) == VarRefNode: # we're changing a variable to the value of another variable @@ -73,14 +104,19 @@ class X86_64Generator(Generator): traceback(self.code, "TypeError", "end expects atleast 1 argument.") elif len(node.arguments) > 1: # example: "end 1 1" traceback(self.code, "TypeError", "end expects only 1 argument.") - if not type(node.arguments[0]) in [NumberNode, VarRefNode]: # example: "end true" + if not type(node.arguments[0]) in [IntNode, VarRefNode]: # example: "end true" traceback(self.code, "TypeError", f"end expects an integer, not {node.arguments[0]}") self.lines.append("mov rax, 60\n\t") - if isinstance(node.arguments[0], NumberNode): + if isinstance(node.arguments[0], IntNode): self.lines.append("mov rdi, " + str(node.arguments[0].value) + "\n\t") - elif isinstance(node.arguments[0], VarRefNode): - self.get_variable(node.arguments[0].var_name, "rdi") + elif isinstance(node.arguments[0], VarRefNode): + var_type = self.get_variable(node.arguments[0].var_name, "rdi") + if var_type == FloatNode: + self.lines.append("cvttsd2si rdi, xmm0\n\t") + else: + if var_type != IntNode: + traceback(self.code, "TypeError", f"end expects an integer, not \"{var_type}\"") #self.lines.append("mov rdi, " + str(self.get_variable(node.arguments[0].var_name)) + "\n\t") self.lines.append("syscall\n\t") @@ -91,7 +127,7 @@ class X86_64Generator(Generator): traceback(self.code, "TypeError", "set expects only 2 arguments.") if not isinstance(node.arguments[0], VarPointerNode): traceback(self.code, "TypeError", f"the first argument of set should be a variable pointer, not \"{node.arguments[0]}\"") - if type(node.arguments[1]) not in [NumberNode, VarRefNode]: + if type(node.arguments[1]) not in [IntNode, VarRefNode, FloatNode]: traceback(self.code, "TypeError", f"variables can't be of type \"{type(node.arguments[1])}\"") variable_exists = self.variables.get(node.arguments[0].var_name, None) != None @@ -113,34 +149,49 @@ class X86_64Generator(Generator): number1_type = None number2_type = None arg2 = "rbx" - - if isinstance(node.arguments[0], NumberNode): - number1_type = NumberNode + if isinstance(node.arguments[0], VarRefNode): + number1_type = self.get_variable(node.arguments[0].var_name, "rax") + elif isinstance(node.arguments[0], FloatNode) or isinstance(node.arguments[1], FloatNode): + number1_type = FloatNode + constant_name = self.add_constant(node.arguments[0].value) + self.lines.append(f"movsd xmm0, {constant_name}\n\t") + elif isinstance(node.arguments[0], IntNode): + number1_type = IntNode #arg1 = node.arguments[0].value self.lines.append(f"mov rax, {node.arguments[0].value}\n\t") - elif isinstance(node.arguments[0], VarRefNode): - number1_type = self.get_variable(node.arguments[0].var_name, "rax") else: traceback(self.code, "TypeError", f"expected a variable reference or number for argument 1 of add, got {node.arguments[0]}") - if isinstance(node.arguments[1], NumberNode): - number2_type = NumberNode + + if isinstance(node.arguments[1], VarRefNode): + number2_type = self.get_variable(node.arguments[1].var_name, "rbx") + elif number1_type == FloatNode or isinstance(node.arguments[1], FloatNode): + number2_type = FloatNode + constant_name = self.add_constant(node.arguments[1].value) + self.lines.append(f"movsd xmm1, {constant_name}\n\t") + elif isinstance(node.arguments[1], IntNode): + number2_type = IntNode arg2 = node.arguments[1].value #self.lines.append(f"mov rbx, {node.arguments[1].value}\n\t") - elif isinstance(node.arguments[1], VarRefNode): - number2_type = self.get_variable(node.arguments[1].var_name, "rbx") else: traceback(self.code, "TypeError", f"expected a variable reference or number for argument 2 of add, got {node.arguments[1]}") # TODO: numbers can be added to numbers, but numbers cant be added to strings. but strings can be added to strings, etc... - if number1_type not in [NumberNode] or number2_type not in [NumberNode]: + if number1_type not in [IntNode, FloatNode] or number2_type not in [IntNode, FloatNode]: traceback(self.code, "TypeError", f"Unsupported operation \"add\" for \"{node.arguments[0]}\" and \"{node.arguments[1]}\".") - self.lines.append(f"add rax, {arg2}\n\t") + if number1_type == IntNode and number2_type == IntNode: + self.lines.append(f"add rax, {arg2}\n\t") + else: + self.lines.append(f"addsd xmm0, xmm1\n\t") + + is_integer = number1_type == IntNode and number2_type == IntNode + starting_reg = "rax" if is_integer else "xmm0" if self.variables.get(node.arguments[2].var_name, None) == None: # we need to create a variable for the destination - self.create_variable(node.arguments[2].var_name, "rax", NumberNode) + self.create_variable(node.arguments[2].var_name, starting_reg, IntNode if is_integer else FloatNode) else: - self.change_variable(node.arguments[2].var_name, "rax") + self.change_variable(node.arguments[2].var_name, starting_reg) + elif node.instruction == "subtract": if len(node.arguments) < 3: # example: "subtract" or "subtract 1" or "subtract 1 2" traceback(self.code, "TypeError", "subtract expects atleast 3 arguments.") @@ -153,35 +204,64 @@ class X86_64Generator(Generator): number1_type = None number2_type = None arg2 = "rbx" - - if isinstance(node.arguments[0], NumberNode): - number1_type = NumberNode + if isinstance(node.arguments[0], VarRefNode): + number1_type = self.get_variable(node.arguments[0].var_name, "rax", isinstance(node.arguments[1], FloatNode)) + elif isinstance(node.arguments[0], FloatNode) or isinstance(node.arguments[1], FloatNode): + number1_type = FloatNode + constant_name = self.add_constant(node.arguments[0].value) + self.lines.append(f"movsd xmm0, {constant_name}\n\t") + elif isinstance(node.arguments[0], IntNode): + number1_type = IntNode #arg1 = node.arguments[0].value self.lines.append(f"mov rax, {node.arguments[0].value}\n\t") - elif isinstance(node.arguments[0], VarRefNode): - number1_type = self.get_variable(node.arguments[0].var_name, "rax") else: traceback(self.code, "TypeError", f"expected a variable reference or number for argument 1 of subtract, got {node.arguments[0]}") - if isinstance(node.arguments[1], NumberNode): - number2_type = NumberNode + + if isinstance(node.arguments[1], VarRefNode): + number2_type = self.get_variable(node.arguments[1].var_name, "rbx", number1_type == FloatNode) + elif number1_type == FloatNode or isinstance(node.arguments[1], FloatNode): + number2_type = FloatNode + constant_name = self.add_constant(node.arguments[1].value) + self.lines.append(f"movsd xmm1, {constant_name}\n\t") + elif isinstance(node.arguments[1], IntNode): + number2_type = IntNode arg2 = node.arguments[1].value #self.lines.append(f"mov rbx, {node.arguments[1].value}\n\t") - elif isinstance(node.arguments[1], VarRefNode): - number2_type = self.get_variable(node.arguments[1].var_name, "rbx") else: traceback(self.code, "TypeError", f"expected a variable reference or number for argument 2 of subtract, got {node.arguments[1]}") - print(number1_type, number2_type) - if number1_type not in [NumberNode] or number2_type not in [NumberNode]: + # TODO: numbers can be added to numbers, but numbers cant be added to strings. but strings can be added to strings, etc... + if number1_type not in [IntNode, FloatNode] or number2_type not in [IntNode, FloatNode]: traceback(self.code, "TypeError", f"Unsupported operation \"subtract\" for \"{node.arguments[0]}\" and \"{node.arguments[1]}\".") - self.lines.append(f"sub rax, {arg2}\n\t") + if number1_type == IntNode and number2_type == IntNode: + self.lines.append(f"sub rax, {arg2}\n\t") + else: + self.lines.append(f"subsd xmm0, xmm1\n\t") + + is_integer = number1_type == IntNode and number2_type == IntNode + starting_reg = "rax" if is_integer else "xmm0" if self.variables.get(node.arguments[2].var_name, None) == None: # we need to create a variable for the destination - self.create_variable(node.arguments[2].var_name, "rax", NumberNode) + self.create_variable(node.arguments[2].var_name, starting_reg, IntNode if is_integer else FloatNode) else: - self.change_variable(node.arguments[2].var_name, "rax") + self.change_variable(node.arguments[2].var_name, starting_reg) else: self.lines.append("; FUCK\n\t") - #raise NotImplementedError(f"A generate method hasn't been made for the \"{node.instruction}\" instruction.") \ No newline at end of file + #raise NotImplementedError(f"A generate method hasn't been made for the \"{node.instruction}\" instruction.") + + def write(self): + + with open(self.output_path + ".asm", "w") as f: + f.write("section .data\n") + for name, value in self.constants.items(): + f.write("." + name + ": ") + value_type = type(value) + if value_type == str: + f.write(f"db \"{value}\", 0") + elif value_type == float or value_type == int: + f.write(f"dq {float(value)}") + f.write("\n") + f.write("\nsection .text\n") + f.writelines(self.lines) \ No newline at end of file diff --git a/ground_ast.py b/ground_ast.py index eb2209c..9dc87e0 100644 --- a/ground_ast.py +++ b/ground_ast.py @@ -19,10 +19,15 @@ class StringNode: def __repr__(self): return "String" @dataclass -class NumberNode: +class IntNode: value: float def __repr__(self): - return "Number" + return "Int" +@dataclass +class FloatNode: + value: float + def __repr__(self): + return "Float" @dataclass class VarRefNode: var_name: str @@ -126,11 +131,17 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode: else: traceback(code, "SyntaxError", "Expected instruction, not string.") - elif token.type == TokenType.INTEGER or token.type == TokenType.FLOAT: + elif token.type == TokenType.INTEGER: if current_node_type == "inst": - current_node.arguments.append(NumberNode(token.value)) + current_node.arguments.append(IntNode(token.value)) else: - traceback(code, "SyntaxError", "Expected instruction, not number.") + traceback(code, "SyntaxError", "Expected instruction, not integer.") + + elif token.type == TokenType.FLOAT: + if current_node_type == "inst": + current_node.arguments.append(FloatNode(token.value)) + else: + traceback(code, "SyntaxError", "Expected instruction, not float.") elif token.type == TokenType.LINE_REFERENCE: if current_node_type == "inst": diff --git a/ground_types.py b/ground_types.py deleted file mode 100644 index 07a1fa0..0000000 --- a/ground_types.py +++ /dev/null @@ -1,3 +0,0 @@ -class String: - def __init__(self, value: str): - self.value = value \ No newline at end of file diff --git a/main.py b/main.py index 7974e8c..40e198c 100644 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ def main(): generator.init() compile_time = time()-start - print(f"Compiled in {round(compile_time, 1)} seconds.") + print(f"Compiled in {round(compile_time, 3)} seconds.") system(f"nasm -felf64 {out_path}.asm") system(f"ld -m elf_{arch} -o {out_path} {out_path}.o") diff --git a/out b/out index 07603fffc5c895ae87fa85a9c24e0fa94d70fa17..717b80d5091c08a7d2034776e3e4a94eb99ceb44 100644 GIT binary patch literal 496 zcmb<-^>JfjWMqH=CI&kOFi*e%CIqHGAn_nF4h$>|%t)fBOg11_fZ^kR1~hX(W@8iQ z#UU<&L!2Q#KB>4kzPKc@sDvRtH6^hm5lH2wFfiy9SLT)^CIRV^A_$!UWdV(%7=YZ* zgvAsGs8|J*2B`t5<$&@fpfo3t#ujQjpz@MX8b*QR0>%N-fJfjWMqH=CI&kOFi*e%ECeAL7=8%AR4_0&Fjz1!0ZCQ{7O+|tgcOhk(*dD3 zK;>XG1JoRl9jqWZn7L5<7@)K)R3D6ntAmP?63if00rXt!Toa_MuGb8EDRt2 zqf7Gv1q48&05?J{odIK&q*j!m*iJr28mJW%25c}RVE%=UXTZlTpz^R` z4w!O~UM3g^iB^CroB$PLhN|O$@+F`&C_F)O=wJg>zap9h$UbBs2;^YXf00Q203J{- ACjbBd diff --git a/out.asm b/out.asm index 518f7f7..fefaa5c 100644 --- a/out.asm +++ b/out.asm @@ -1,17 +1,9 @@ +section .data + +section .text global _start _start: - ; InstructionNode(instruction='set', parent=RootNode(statements=[..., InstructionNode(instruction='add', parent=..., arguments=[VariableReference, Number, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[VariablePointer, Number]) - mov rax, 100 - push rax - ; InstructionNode(instruction='add', parent=RootNode(statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Number]), ..., InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[VariableReference, Number, VariablePointer]) - push QWORD [rsp + 0] - pop rax - add rax, -5 - mov QWORD [rsp + 0], rax - ; InstructionNode(instruction='end', parent=RootNode(statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Number]), InstructionNode(instruction='add', parent=..., arguments=[VariableReference, Number, VariablePointer]), ...]), arguments=[VariableReference]) - mov rax, 60 - push QWORD [rsp + 0] - pop rdi - syscall + ; InstructionNode(instruction='divide', parent=RootNode(statements=[...]), arguments=[]) + ; FUCK \ No newline at end of file diff --git a/test2.grnd b/test2.grnd index c6d66d4..54d243b 100644 --- a/test2.grnd +++ b/test2.grnd @@ -1,3 +1 @@ -set &x 100 -add $x -5 &x -end $x \ No newline at end of file +divide \ No newline at end of file