375 lines
12 KiB
Python
375 lines
12 KiB
Python
from __future__ import annotations
|
|
|
|
import functools
|
|
from abc import abstractmethod, ABC
|
|
from typing import Any, Iterable
|
|
|
|
from . import ir, semantic, lexer
|
|
from .errors import SemanticAnalysisError, OverrideMandatoryError
|
|
from .logger import Logger
|
|
from .source import SourceLocation
|
|
from .typechecking import typecheck
|
|
|
|
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:
|
|
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: lexer.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
|
|
|
|
|
|
@typecheck
|
|
class Float(Literal):
|
|
def __init__(self, location: SourceLocation, value: float):
|
|
super().__init__(location, value)
|
|
|
|
|
|
@typecheck
|
|
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 Statement(Node):
|
|
|
|
def __init__(self, *nodes: Node):
|
|
super().__init__()
|
|
self.nodes = list(nodes)
|
|
|
|
def _values(self) -> list[Node | Any]:
|
|
return self.nodes
|
|
|
|
def intermediate_representation(self) -> list[ir.IRItem]:
|
|
result: list[ir.IRItem] = []
|
|
for node in self.nodes:
|
|
result += node.intermediate_representation()
|
|
return result
|
|
|
|
|
|
class Block(Statement):
|
|
def __init__(self, name: str, *nodes: Node):
|
|
super().__init__(*nodes)
|
|
self.name = name
|
|
|
|
def semantic_analysis(self, context: semantic.Context | None):
|
|
child_context = semantic.Context(name=self.name, parent=context)
|
|
if context is not None:
|
|
context.add_context(child_context)
|
|
logger.debug(f"Created new context: {child_context}")
|
|
super().semantic_analysis(child_context)
|
|
|
|
|
|
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
|
|
assert self.value 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 | None):
|
|
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"Assigned variable {variable.fully_qualified_name()}")
|
|
|
|
|
|
class Definition(Assignment):
|
|
|
|
def __init__(self, identifier: Identifier, type_identifier: Identifier, value: Value | None,
|
|
*pseudo_nodes: PseudoNode):
|
|
super().__init__(identifier, value)
|
|
self.type_identifier = type_identifier
|
|
self.pseudo_nodes = list(pseudo_nodes)
|
|
|
|
def intermediate_representation(self) -> list[ir.IRItem]:
|
|
if self.value is None:
|
|
return [ir.IRVoid(self.location())]
|
|
|
|
return super(Definition, self).intermediate_representation()
|
|
|
|
def _values(self) -> list[Node | Any]:
|
|
v = [self.identifier, self.type_identifier]
|
|
if self.value is not None:
|
|
v += [self.value]
|
|
v += self.pseudo_nodes
|
|
return v
|
|
|
|
@property
|
|
def pure(self) -> bool:
|
|
return self.value is None
|
|
|
|
|
|
Number = Float | Integer
|
|
Value = BinaryOperation | Number | Variable | Expression
|