compiler/compiler/semantic.py

110 lines
3.5 KiB
Python

from __future__ import annotations
import abc
from typing import Literal
from . import nodes
from .logger import Logger
from .typechecking import typecheck
logger = Logger(__name__)
class SymbolABC(abc.ABC):
def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None):
self.context = context
self.name = name
self.definitions = [value]
self._repr_guard: bool = False
def fully_qualified_name(self) -> str:
return f"{self.context.fully_qualified_name()}.{self.name}"
def __str__(self):
return f"{self.__class__.__name__}({self.name})"
def __repr__(self):
if self._repr_guard:
return str(self)
self._repr_guard = True
definitions = [str(d.location().begin) for d in self.definitions if d is not None]
self._repr_guard = False
return f"{str(self)} [definitions: {', '.join(definitions)}]"
class Type(SymbolABC):
def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None):
super().__init__(context, name, value)
class Variable(SymbolABC):
@typecheck
def __init__(self, context: Context, name: str, value: nodes.Value | None = None):
super().__init__(context, name, value)
def __str__(self):
return f"{self.__class__.__name__}({self.name})"
class Context:
_id_sequence = 0
def __init__(self, name: str | None = None, parent: Context | None = None):
self.parent = parent
self.variables: dict[str, Variable] = {}
self.child_contexts: dict[str, Context] = {}
self.name = str(Context._id_sequence)
if name is not None:
self.name = f"{name}_{Context._id_sequence}"
Context._id_sequence += 1
def fully_qualified_name(self) -> str:
if self.parent is None:
return str(self.name)
return f"{self.parent.fully_qualified_name()}::{self.name}"
def get_variable(self, name: str) -> Variable | None:
if name in self.variables:
return self.variables[name]
elif self.parent is None:
return None
elif (var := self.parent.get_variable(name)) is not None:
return var
return None
def add_context(self, context: Context) -> None:
self.child_contexts[context.name] = context
def set_variable(self, name: str, value: nodes.Value) -> Variable:
variable: Variable
if name in self.variables:
variable = self.variables[name]
variable.definitions += [value]
else:
variable = Variable(self, name, value)
self.variables[name] = variable
return variable
def __str__(self):
return f"{self.__class__.__name__}(id={repr(self.name)})"
def _pprint(self, depth: int = 0) -> str:
result = [str(self)]
if self.parent is not None:
result += [f"\tParent ID: {self.parent.name}"]
if len(self.variables) > 0:
result += [f"\tVariables ({len(self.variables)}):"]
for key, value in self.variables.items():
result += [f"\t\t- {repr(value)}"]
if len(self.child_contexts) > 0:
result += [f"\tChild contexts ({len(self.child_contexts)}):"]
for key, value in self.child_contexts.items():
ctx_repr = value._pprint(depth=depth + 1).splitlines(keepends=False)
result += [f"\t\t{line}" for line in ctx_repr]
return "\n".join(result)
def __repr__(self) -> str:
return self._pprint()