compiler/compiler/nodes.py

320 lines
10 KiB
Python

from __future__ import annotations
import functools
from abc import abstractmethod, ABC
from typing import Any, Iterable
from beartype import beartype
from . import ir, semantic, tokenizer
from .errors import SemanticAnalysisError, OverrideMandatoryError
from .logger import Logger
from .source import SourceLocation
logger = Logger(__name__)
class Node:
def __init__(self):
self.context: semantic.Context | None = None
self._repr_guard: bool = False
@abstractmethod
def _values(self) -> list[Node | Any]:
raise OverrideMandatoryError()
@functools.cache
def location(self) -> SourceLocation:
locations = [v.location() for v in self._values()]
begin = min([loc.begin for loc in locations])
end = max([loc.end for loc in locations])
loc = SourceLocation(begin=begin, end=end)
return loc
def __repr__(self):
if self._repr_guard:
return self.__class__.__name__
self._repr_guard = True
vals = self._values()
if type(vals) == list:
vals = ", ".join(repr(val) for val in vals)
else:
vals = repr(vals)
self._repr_guard = False
return f"{self.__class__.__name__}({vals})"
def pprint(self, depth: int | None = None, indent: str = "\t"):
print("\n".join(self._pprint(depth=depth, indent=indent)))
def _pprint(self, depth: int | None, indent: str, _depth: int = 0) -> list[str]:
if depth is not None and _depth >= depth:
return [f"{indent}{self.__class__.__name__} {{ ... }}"]
vals = self._values()
try:
vals = (val for val in vals)
except TypeError as e:
vals = (vals,)
result = [f"{self.__class__.__name__} {{"]
for val in vals:
if isinstance(val, Node):
result += val._pprint(depth=depth, indent=indent, _depth=_depth + 1)
else:
result += [f"{indent}{repr(val)}"]
result += [f"}} // {self.__class__.__name__}"]
for i, line in enumerate(result):
result[i] = indent + line
return result
@abstractmethod
def intermediate_representation(self) -> list[ir.IRItem]:
raise OverrideMandatoryError()
def semantic_analysis(self, context: semantic.Context):
logger.debug(f"Doing semantic analysis in {self}")
for value in self._values():
if isinstance(value, Node):
value.semantic_analysis(context)
self.context = context
@staticmethod
def _prepare_sources_ir(result: list[ir.IRAction], values: Iterable[Value]) -> list[ir.IRValue]:
vals = [value.intermediate_representation() for value in values]
for value in vals:
result += value
return [value[-1].destination() for value in vals]
class Literal(Node, ABC):
def __init__(self, location: SourceLocation, value: Any):
super().__init__()
self.value = value
self.loc = location
def location(self) -> SourceLocation:
return self.loc
def _values(self) -> list[Node | Any]:
return [self.value]
def _pprint(self, depth: int | None, indent: str, _depth: int = 0) -> list[str]:
return [f"{indent}{repr(self)}"]
def intermediate_representation(self) -> list[ir.IRItem]:
dest = ir.IRRegister(location=self.location())
immediate = ir.IRImmediate(location=self.location(), value=self.value)
result = [ir.IRMove(location=self.location(), dest=dest, source=immediate)]
return result
class PseudoNode(Literal):
"""
Only used for better diagnostics
"""
def __init__(self, token: tokenizer.Token):
super().__init__(token.loc, token.value)
self.token = token
def intermediate_representation(self) -> list[ir.IRItem]:
return []
def semantic_analysis(self, context: semantic.Context):
pass
class Sum(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
result: list[ir.IRAction] = []
values_results = Node._prepare_sources_ir(result=result, values=self.values)
dest = ir.IRRegister(location=self.location())
result += [ir.IRAdd(self.location(), dest, *values_results)]
return result
def __init__(self, *values: Value):
super().__init__()
self.values = values
def _values(self) -> list[Node | Any]:
return list(self.values)
class Sub(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
result: list[ir.IRAction] = []
first_val = self.first_value.intermediate_representation()
result += first_val
values_results = Node._prepare_sources_ir(result=result, values=self.values)
for i, value_result in enumerate(values_results):
d = ir.IRRegister(location=self.location())
result += [ir.IRNegation(location=self.location(), dest=d, source=value_result)]
values_results[i] = result[-1].destination()
values_results += [first_val[-1].destination()]
dest = ir.IRRegister(location=self.location())
result += [ir.IRAdd(self.location(), dest, *values_results)]
return result
def __init__(self, first_value: Value, *values: Value):
super().__init__()
self.first_value = first_value
self.values = values
def _values(self) -> list[Node | Any]:
return [self.first_value] + list(self.values)
class Product(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
result: list[ir.IRAction] = []
values_results = Node._prepare_sources_ir(result=result, values=self.values)
dest = ir.IRRegister(location=self.location())
result += [ir.IRMul(self.location(), dest, *values_results)]
return result
def __init__(self, *values: Value):
super().__init__()
self.values = values
def _values(self) -> list[Node | Any]:
return list(self.values)
class Division(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
result: list[ir.IRAction] = []
first_val = self.first_value.intermediate_representation()
result += first_val
values_results = Node._prepare_sources_ir(result=result, values=self.values)
for i, value_result in enumerate(values_results):
d = ir.IRRegister(location=self.location())
result += [ir.IRInvert(location=self.location(), dest=d, source=value_result)]
values_results[i] = result[-1].destination()
values_results += [first_val[-1].destination()]
dest = ir.IRRegister(location=self.location())
result += [ir.IRMul(self.location(), dest, *values_results)]
return result
def __init__(self, first_value: Value, *values: Value):
super().__init__()
self.first_value = first_value
self.values = values
def _values(self) -> list[Node | Any]:
return [self.first_value] + list(self.values)
BinaryOperation = Sum | Sub | Product | Division
@beartype
class Float(Literal):
def __init__(self, location: SourceLocation, value: float):
super().__init__(location, value)
@beartype
class Integer(Literal):
def __init__(self, location: SourceLocation, value: int):
super().__init__(location, value)
class Expression(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
return self.node.intermediate_representation()
def __init__(self, node: Node):
super().__init__()
self.node = node
def _values(self) -> list[Node | Any]:
return [self.node]
def semantic_analysis(self, context: semantic.Context):
return self.node.semantic_analysis(context)
def location(self) -> SourceLocation:
return self.node.location()
class Identifier(Literal):
def __init__(self, location: SourceLocation, name: str):
super().__init__(location, name)
self.value: str
class Variable(Node):
def __init__(self, identifier: Identifier):
super().__init__()
self.value: semantic.Variable | None = None
self.identifier = identifier
def _values(self) -> list[Node | Any]:
return [self.identifier]
def semantic_analysis(self, context: semantic.Context):
variable = context.get_variable(self.identifier.value)
if variable is None:
raise SemanticAnalysisError(location=self.location(), message=f"Unknown variable '{self.identifier.value}'")
self.value = variable
logger.debug(f"Linked variable reference to var {variable}")
def intermediate_representation(self) -> list[ir.IRItem]:
assert self.value is not None
dest = ir.IRRegister(location=self.location())
immediate = ir.IRVariable(location=self.location(), fq_identifier=self.value.fully_qualified_name())
result = [ir.IRMove(location=self.location(), dest=dest, source=immediate)]
return result
class Assignment(Node):
def intermediate_representation(self) -> list[ir.IRItem]:
assert self.variable is not None
result: list[ir.IRItem] = []
value = self.value.intermediate_representation()
result += value
dest = ir.IRVariable(location=self.location(), fq_identifier=self.variable.fully_qualified_name())
result += [ir.IRMove(location=self.location(), dest=dest.destination(), source=value[-1].destination())]
return result
def __init__(self, identifier: Identifier, value: Value):
super().__init__()
self.identifier = identifier
self.value = value
self.variable: semantic.Variable | None = None
def _values(self) -> list[Node | Any]:
return [self.identifier, self.value]
def semantic_analysis(self, context: semantic.Context):
super(Assignment, self).semantic_analysis(context)
name = self.identifier.value
variable = context.set_variable(name, value=self.value)
self.variable = variable
logger.debug(f"Added variable {variable} to context {context.fully_qualified_name()}")
Number = Float | Integer
Value = BinaryOperation | Number | Variable