Files
GroundPY/generators/generator.py

102 lines
3.1 KiB
Python
Raw Permalink Normal View History

from ground_ast import RootNode
2025-09-04 07:45:20 +10:00
from typing import Any
2025-09-07 20:00:35 +10:00
from ground_ast import *
from error import warning
2025-09-10 18:56:45 +10:00
class SymbolTable:
def __init__(self, parent=None):
self.table = {} # variable name -> info (e.g., stack offset, type)
self.parent = parent
def define(self, name, value):
self.table[name] = value
def lookup(self, name):
scope = self
while scope:
if name in scope.table:
return scope.table[name]
scope = scope.parent
return None
#raise KeyError(f"Undefined variable: {name}")
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
2025-09-10 18:56:45 +10:00
self.global_scope = SymbolTable()
self.current_var_scope = self.global_scope
2025-09-04 07:45:20 +10:00
self.constants = {}
2025-09-13 15:48:41 +10:00
self.buffers = {}
self.structs = {}
self.functions: dict[str, dict] = {}
2025-09-07 11:58:01 +10:00
self.labels = []
2025-09-10 18:56:45 +10:00
self.arg_list = []
2025-09-04 07:45:20 +10:00
self.constants_reverse = {}
self.constant_counter = 0
2025-09-13 15:48:41 +10:00
self.buffer_counter = 0
self.included_functions = []
2025-09-04 07:45:20 +10:00
def ground_type_to_node(self, ground_type: str):
if ground_type == "string":
return StringNode
elif ground_type == "int":
return IntNode
elif ground_type == "float":
return FloatNode
elif ground_type == "bool":
return BoolNode
else:
return ground_type
def add_constant(self, value: Any, no_string: bool = False):
2025-09-04 07:45:20 +10:00
existing_constant_name = self.constants_reverse.get(value, None)
2025-09-13 07:08:11 +10:00
if existing_constant_name != None: return f"[{existing_constant_name}]"
self.constants["LC" + str(self.constant_counter)] = {"value": value, "no_string": no_string}
2025-09-04 07:45:20 +10:00
self.constants_reverse[value] = "LC" + str(self.constant_counter)
self.constant_counter += 1
2025-09-13 07:08:11 +10:00
return "[LC" + str(self.constant_counter-1) + "]"
2025-09-07 20:00:35 +10:00
2025-09-13 15:48:41 +10:00
def add_buffer(self, size: int):
buffer_name = "BUF" + str(self.buffer_counter)
self.buffers[buffer_name] = size
self.buffer_counter += 1
return buffer_name
def add_function(self, node: FunctionNode):
self.functions[node.name] = {
"func": node,
"scope": SymbolTable(self.current_var_scope)
}
return self.functions[node.name]
2025-09-07 20:00:35 +10:00
def clamp_instruction_args(self, instruction: InstructionNode, min_args: int, max_args: int):
if len(instruction.arguments) < min_args:
traceback(self.code, "TypeError", f"{instruction.instruction} expects at least {min_args} arguments.")
elif len(instruction.arguments) > max_args:
traceback(self.code, "TypeError", f"{instruction.instruction} expects at most {max_args} arguments.")
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}"):
2025-09-02 06:42:58 +10:00
raise NotImplementedError(f"Generator has no generate method for {node_type}.")
2025-09-10 07:30:58 +10:00
getattr(self, f"generate_{node_type}")(node, self.lines)
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.")
2025-09-02 06:42:58 +10:00
def write(self):
with open(self.output_path + ".asm", "w") as f:
f.write(self.lines)