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 0000000..5957937 Binary files /dev/null and b/out differ diff --git a/out.asm b/out.asm index 49492d4..b95c86d 100644 --- a/out.asm +++ b/out.asm @@ -1,52 +1,19 @@ ; ~~~ Auto generated by the GroundPY compiler for Linux x86_64 targets. ~~~ section .data -.LC0: db "test of returning strings INSIDE a variable!", 10, 0 -.LC1: equ $ - .LC0 section .text global _start _start: -; FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=RootNode(statements=[..., InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='string', name='idk') -; InstructionNode(instruction='endfun', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), ..., FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[]) -; FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), ..., InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='int', name='function2') -; InstructionNode(instruction='endfun', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), ..., InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[]) -; InstructionNode(instruction='call', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), ..., InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[FunctionCall, VariablePointer]) -sub rsp, 8 -call idk -; InstructionNode(instruction='stdout', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), ..., InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[VariableReference]) -mov rsi, rax -push rdi -mov rax, 1 mov rdi, 1 -syscall -pop rdi -; InstructionNode(instruction='call', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), ..., InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), arguments=[FunctionCall, VariablePointer]) -call function2 -; InstructionNode(instruction='end', parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), ...]), arguments=[VariableReference]) -mov rax, 60 +mov rsi, 4 +call add mov rdi, rax +mov rax, 60 syscall -idk: +add: push rbp mov rbp, rsp -; InstructionNode(instruction='set', parent=FunctionNode(args=[], statements=[..., InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=RootNode(statements=[..., InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='string', name='idk'), arguments=[VariablePointer, String]) -lea rax, [.LC0] -push rax -push .LC1 -; InstructionNode(instruction='return', parent=FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), ...], parent=RootNode(statements=[..., InstructionNode(instruction='endfun', parent=..., arguments=[]), FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='int', name='function2'), InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='string', name='idk'), arguments=[VariableReference]) -mov rax, [rsp + 8] -mov rdx, [rsp + 0] -pop rbp -pop rbp -pop rbp -ret -function2: -push rbp -mov rbp, rsp -; InstructionNode(instruction='set', parent=FunctionNode(args=[], statements=[..., InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), ..., InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='int', name='function2'), arguments=[VariablePointer, Int]) -push 123 -; InstructionNode(instruction='return', parent=FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, Int]), ...], parent=RootNode(statements=[FunctionNode(args=[], statements=[InstructionNode(instruction='set', parent=..., arguments=[VariablePointer, String]), InstructionNode(instruction='return', parent=..., arguments=[VariableReference])], parent=..., return_type='string', name='idk'), InstructionNode(instruction='endfun', parent=..., arguments=[]), ..., InstructionNode(instruction='endfun', parent=..., arguments=[]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='stdout', parent=..., arguments=[VariableReference]), InstructionNode(instruction='call', parent=..., arguments=[FunctionCall, VariablePointer]), InstructionNode(instruction='end', parent=..., arguments=[VariableReference])]), return_type='int', name='function2'), arguments=[VariableReference]) -mov rax, [rsp + 0] -pop rbp +mov rax, rdi +add rax, rsi pop rbp ret diff --git a/test2.grnd b/test2.grnd index 6c21404..164936e 100644 --- a/test2.grnd +++ b/test2.grnd @@ -1,14 +1,9 @@ -fun -string !idk - set &var "test of returning strings INSIDE a variable!\n" - return $var +fun -int !add -int &a -int &b + add $a $b &a + return $a endfun -fun -int !function2 - set &intvar 123 - return $intvar -endfun - -call !idk &var -stdout $var -call !function2 &lol -end $lol \ No newline at end of file +pusharg 1 +pusharg 4 +call !add &result +end $result \ No newline at end of file