From 170272353cea1698ca6966072c1c7ee438fbccf1 Mon Sep 17 00:00:00 2001 From: SpookyDervish <78246495+SpookyDervish@users.noreply.github.com> Date: Sat, 13 Sep 2025 06:26:15 +1000 Subject: [PATCH] functions basically work now, but they're extremely inneficient --- error.py | 21 ++++++++++++++++- generators/generator.py | 6 ++++- generators/x86_64.py | 49 +++++++++++++++++++++++++++++----------- out | Bin 0 -> 4696 bytes out.asm | 45 +++++------------------------------- test2.grnd | 19 ++++++---------- 6 files changed, 74 insertions(+), 66 deletions(-) create mode 100644 out diff --git a/error.py b/error.py index 3c39f21..2de5a21 100644 --- a/error.py +++ b/error.py @@ -22,4 +22,23 @@ def traceback(code: str, error_type: str, error_message: str, line: Union[int, N console.print(f"[bold red]{error_type}: {error_message}") exit(1) - \ No newline at end of file + +def warning(code: str, warning_msg: str, note: str = None, line: Union[int, None] = None, start_column: Union[int, None] = None, end_column: Union[int, None] = None): + if line != None: + console.print(f"[bold cyan]warning:[/] {warning_msg}\n") + lines = code.split("\n")[line-1:line+2] + + console.print(f"{line } > " + lines[0], highlight=False) + if start_column != None and end_column != None: + console.print(" " + (" " * start_column) + "[bold red]" + ("^" * (end_column-start_column+1))) + + try: + console.print(f"{line+1} " + lines[1], highlight=False) + console.print(f"{line+2} " + lines[2], highlight=False) + console.print(" ...", highlight=False) + except IndexError: # the file is less than 3 lines i guess + pass + else: + console.print(f"[bold cyan]warning:[/] {warning_msg}") + if note != None: + console.print(f"[bold]note:[/] {note}") \ No newline at end of file diff --git a/generators/generator.py b/generators/generator.py index dd7e830..5f2cc18 100644 --- a/generators/generator.py +++ b/generators/generator.py @@ -1,6 +1,7 @@ from ground_ast import RootNode from typing import Any from ground_ast import * +from error import warning class SymbolTable: @@ -74,7 +75,7 @@ class Generator: pass def generate_node(self, node): - self.lines.append(f"; {node}\n\t") + #self.lines.append(f"; {node}\n\t") node_type = str(type(node))[19:-2] if not hasattr(self, f"generate_{node_type}"): raise NotImplementedError(f"Generator has no generate method for {node_type}.") @@ -83,6 +84,9 @@ class Generator: def generate(self): for statement in self.ast.statements: self.generate_node(statement) + for name, var in self.global_scope.table.items(): + if not var["used"]: + warning(self.code, f"warning: \"{name}\" was defined but never used.") def write(self): with open(self.output_path + ".asm", "w") as f: diff --git a/generators/x86_64.py b/generators/x86_64.py index 4f886d0..6eb7c9a 100644 --- a/generators/x86_64.py +++ b/generators/x86_64.py @@ -1,6 +1,6 @@ from generators.generator import Generator, SymbolTable from ground_ast import * -from error import traceback +from error import traceback, warning from optimizers.x86_64 import X86_64Optimizer class X86_64Generator(Generator): @@ -74,6 +74,8 @@ class X86_64Generator(Generator): except TypeError as e: # variable doesnt exist traceback(self.code, "NameError", f"\"{var_name}\" is not defined.") + scope.table[var_name]["used"] = True + return var["type"] def get_var_pos(self, var_name: str, scope: SymbolTable = None): @@ -131,12 +133,14 @@ class X86_64Generator(Generator): self.stack_size += 1 else: self.push(starting_value, lines) - scope.define(var_name, {"stack_loc": stack_location, "type": var_type}) + scope.define(var_name, {"stack_loc": stack_location, "type": var_type, "used": False}) def change_variable(self, lines, var_name: str, new_value, scope: SymbolTable = None): scope = scope or self.current_var_scope var_pos = self.get_var_pos(var_name, scope) + old_var_type = scope.table[var_name]["type"] + if isinstance(var_pos, str): if type(new_value) == IntNode: # we're changing a variable to a number #lines.append(f"mov QWORD [rsp + {var_pos}], {new_value.value}\n\t") @@ -213,6 +217,8 @@ class X86_64Generator(Generator): elif type(new_value) == str: # we're changing a variable to the value of a register lines.append(f"mov QWORD [rsp + {var_pos}], {new_value}\n\t") scope.table[var_name]["type"] = IntNode + if scope.table[var_name]["type"] != old_var_type: + warning(self.code, f"Changing the type of \"{var_name}\" at runtime is considered bad practice.") def generate_LabelDecNode(self, node: LabelDecNode, lines): self.labels.append(node.name) @@ -238,10 +244,10 @@ class X86_64Generator(Generator): except IndexError: traceback(self.code, "CallError", "Functions with more than 6 args aren't supported yet, sorry...") - self.current_var_scope.define(arg.name, {"stack_loc": stack_loc, "type": self.ground_type_to_node(arg.arg_type)}) + self.current_var_scope.define(arg.name, {"stack_loc": stack_loc, "type": self.ground_type_to_node(arg.arg_type), "used": False}) for inst in node.statements: - self.function_lines.append(f"; {inst}\n\t") + #self.function_lines.append(f"; {inst}\n\t") self.generate_InstructionNode(inst, self.function_lines) def generate_InstructionNode(self, node: InstructionNode, lines = None): @@ -253,7 +259,6 @@ class X86_64Generator(Generator): 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]}") - lines.append("mov rax, 60\n\t") if type(node.arguments[0]) in [IntNode,BoolNode]: lines.append("mov rdi, " + str(node.arguments[0].value) + "\n\t") elif isinstance(node.arguments[0], VarRefNode): @@ -263,7 +268,8 @@ class X86_64Generator(Generator): else: if var_type not in [IntNode,BoolNode]: traceback(self.code, "TypeError", f"end expects an integer, not \"{var_type}\"") - #lines.append("mov rdi, " + str(self.get_variable(lines, node.arguments[0].var_name)) + "\n\t") + + lines.append("mov rax, 60\n\t") lines.append("syscall\n\t") ### VARIABLE INSTRUCTIONS ### @@ -516,7 +522,7 @@ class X86_64Generator(Generator): length = self.add_constant(f"equ $ - {string_pointer[1:-1]}", no_string=True) lines.append(f"lea rax, [rel {string_pointer[1:-1]}]\n\t") - lines.append(f"lea rdx, {length}\n\t") + lines.append(f"mov rdx, {length[1:-1]}\n\t") #self.push("rax", lines) elif isinstance(node.arguments[0], VarRefNode): var = func_scope.lookup(node.arguments[0].var_name) @@ -529,9 +535,26 @@ class X86_64Generator(Generator): else: lines.append("mov rax, 0\n\t") - for _ in range(self.stack_size): # TODO: THIS IS THE WORST FUCKING BULLSHIT EVER, KILL IT NOW!!!! - self.pop("rbp", lines) + size = 0 + for name, var in self.current_var_scope.table.items(): + if type(var["stack_loc"]) == str: + continue # its in a register, we dont need to free up the stack + + if var["type"] == StringNode: + size += 2 + else: + size += 1 + + lines.append(f"add rsp, {8 * (size)}\n\t") + self.stack_size -= size - 1 + lines.append("pop rbp") lines.append("ret\n\t") + + # warn about unused variables + for name, var in self.current_var_scope.table.items(): + if not var["used"]: + warning(self.code, f"\"{name}\" was defined but never used.") + old_scope = self.current_var_scope self.current_var_scope = self.current_var_scope.parent del old_scope @@ -549,7 +572,7 @@ class X86_64Generator(Generator): traceback(self.code, "TypeError", f"Function \"{node.arguments[0].func_name}\" takes {len(func.args)} arguments, but got {len(self.arg_list)}") # stack alignment - if self.stack_size % 2 == 0: + if self.stack_size % 2 != 0: lines.append("sub rsp, 8\n\t") # align the stack to 16 bytes self.stack_size -= 1 @@ -587,8 +610,8 @@ class X86_64Generator(Generator): traceback(self.code, "CallError", "Functions with more than 6 args aren't supported yet, sorry...") lines.append(f"call {node.arguments[0].func_name}\n\t") - if len(self.arg_list) > 0: - self.lines.append(f"add rsp, {len(self.arg_list) * 8}") + #if len(self.arg_list) > 0: + # lines.append(f"add rsp, {len(self.arg_list) * 8}") self.stack_size += len(self.arg_list) self.arg_list.clear() @@ -599,7 +622,7 @@ class X86_64Generator(Generator): if self.current_var_scope.lookup(node.arguments[1].var_name): self.change_variable(lines, node.arguments[1].var_name, "rax") else: - self.current_var_scope.define(node.arguments[1].var_name, {"stack_loc": "rax", "type": self.ground_type_to_node(func.return_type)}) + self.current_var_scope.define(node.arguments[1].var_name, {"stack_loc": "rax", "type": self.ground_type_to_node(func.return_type), "used": False}) #self.create_variable(lines, node.arguments[1].var_name, "rax", self.ground_type_to_node(self.functions.get(node.arguments[0].func_name)["func"].return_type)) elif node.instruction == "pusharg": self.clamp_instruction_args(node, 1, 1) diff --git a/out b/out new file mode 100644 index 0000000000000000000000000000000000000000..5957937708978f3be5626d30261a51ae26ea2e9b GIT binary patch literal 4696 zcmeHLu}Z{15PgZsp@JB%y2Iuy1gonpMA9fJY%L}1kpr7kxU8U+b^gTC(n<>pTYo~Z z)IzY+o$SmRf&syH25#Q&n>QKmbvxGwM~7h$KqACG>RXc5l+VCmLli)YU4$;jh-z*0 zQ*tNi@OaBLK_s4_88IFC4xT5t?PU;L=$Kbf2h;&|KpjvA)B$xs9Z(0<0d+tfPzTh3 zzdP_i5Al0?!N0D$lvPi6dk)v*