diff --git a/sleepy/core/__init__.py b/sleepy/core/__init__.py index faa7bdc..0ba7111 100644 --- a/sleepy/core/__init__.py +++ b/sleepy/core/__init__.py @@ -1,2 +1,3 @@ from .error import SleepyError +from .meta_table import UID, Identifiable, MetaTable from .source_location import SourceLocation diff --git a/sleepy/core/meta_table.py b/sleepy/core/meta_table.py new file mode 100644 index 0000000..e1b3ba5 --- /dev/null +++ b/sleepy/core/meta_table.py @@ -0,0 +1,30 @@ +from abc import ABC, abstractproperty +from typing import Generic, NewType, TypeVar + +UID = NewType("UID", int) + +T = TypeVar("T") + + +class Identifiable(ABC): + @abstractproperty + def uid(self) -> UID: + raise NotImplementedError + + +class MetaTable(Generic[T]): + def __init__(self) -> None: + self.entries: dict[UID, T] = {} + + def __getitem__(self, key: Identifiable) -> T: + try: + return self.entries[key.uid] + except KeyError as e: + e.add_note(f"key: {key!r}") + raise + + def __setitem__(self, key: Identifiable, value: T) -> None: + if key.uid in self.entries: + message = f"can't assign {value}, to key with id {key.uid}: {key!r}" + raise KeyError(message) + self.entries[key.uid] = value diff --git a/sleepy/program/__init__.py b/sleepy/program/__init__.py index de1ca2e..9e6f6d7 100644 --- a/sleepy/program/__init__.py +++ b/sleepy/program/__init__.py @@ -21,7 +21,6 @@ Program, ProgramNode, Symbol, - SymbolId, ) from .unit import ProgramUnit from .visitor import Visitor diff --git a/sleepy/program/bindings.py b/sleepy/program/bindings.py index cbb5be0..7bc03bb 100644 --- a/sleepy/program/bindings.py +++ b/sleepy/program/bindings.py @@ -1,6 +1,8 @@ from abc import ABC, abstractmethod from typing import override +from sleepy.core import MetaTable + from .representation import Expression, Symbol @@ -17,14 +19,14 @@ def resolve(self, symbol: Symbol) -> Expression: class BasicBindings(Bindings): def __init__( self, - expressions: dict[int, Expression] | None = None, + expressions: MetaTable[Expression] | None = None, ) -> None: - self.expressions = expressions or {} + self.expressions = expressions or MetaTable() @override def bind(self, symbol: Symbol, expression: Expression) -> None: - self.expressions[symbol.uid] = expression + self.expressions[symbol] = expression @override def resolve(self, symbol: Symbol) -> Expression: - return self.expressions[symbol.uid] + return self.expressions[symbol] diff --git a/sleepy/program/builtin.py b/sleepy/program/builtin.py index 54f6965..4dd4636 100644 --- a/sleepy/program/builtin.py +++ b/sleepy/program/builtin.py @@ -98,7 +98,5 @@ def __init__(self) -> None: self.namespace = LocalNamespace() self.bindings = BasicBindings() for intrinsic in intrinsics: - self.bindings.bind( - self.namespace.define(intrinsic.name), - intrinsic, - ) + defined = self.namespace.define(intrinsic.name) + self.bindings.bind(defined, intrinsic) diff --git a/sleepy/program/exception.py b/sleepy/program/exception.py new file mode 100644 index 0000000..cfd7403 --- /dev/null +++ b/sleepy/program/exception.py @@ -0,0 +1,19 @@ +from sleepy.core import SleepyError + + +class SemanticError(SleepyError): + pass + + +class NamespaceError(SleepyError): + pass + + +class DefinitionError(NamespaceError): + def __init__(self, name: str, reason: str) -> None: + super().__init__(f"failed to define a name {name}: {reason}") + + +class NameNotFoundError(NamespaceError): + def __init__(self, name: str) -> None: + super().__init__(f"failed to resolve a name {name}: not found") diff --git a/sleepy/program/namespace.py b/sleepy/program/namespace.py index 3ebb9b8..50d002c 100644 --- a/sleepy/program/namespace.py +++ b/sleepy/program/namespace.py @@ -1,29 +1,10 @@ from abc import ABC, abstractmethod from typing import override -from sleepy.core import SleepyError - +from .exception import DefinitionError, NameNotFoundError from .representation import Symbol -class NamespaceError(SleepyError): - pass - - -class DefinitionError(NamespaceError): - def __init__(self, name: str, reason: str) -> None: - self.name = name - super().__init__(f"Failed to define a name {name}: {reason}") - - -class NameNotFoundError(NamespaceError): - def __init__(self, name: str) -> None: - self.name = name - super().__init__( - f"Failed to resolve a name {name}: not found", - ) - - class Namespace(ABC): @abstractmethod def resolved(self, name: str) -> Symbol: diff --git a/sleepy/program/representation.py b/sleepy/program/representation.py index f73a007..a251a42 100644 --- a/sleepy/program/representation.py +++ b/sleepy/program/representation.py @@ -1,12 +1,14 @@ from dataclasses import dataclass +from typing import override -SymbolId = int +from sleepy.core import UID, Identifiable -class ProgramNode: +class ProgramNode(Identifiable): @property - def uid(self) -> SymbolId: - return id(self) + @override + def uid(self) -> UID: + return UID(id(self)) class Expression(ProgramNode): diff --git a/sleepy/syntax/s2p.py b/sleepy/syntax/s2p.py index 94849b8..0656950 100644 --- a/sleepy/syntax/s2p.py +++ b/sleepy/syntax/s2p.py @@ -55,9 +55,7 @@ def visit_conditional(self, tree: IfExpressionAST) -> Conditional: @override def visit_application(self, tree: ApplicationAST) -> ProgramNode: return Application( - invokable=self.visit_expression( - tree.invokable.expression, - ), + invokable=self.visit_expression(tree.invokable.expression), args=[self.visit_expression(arg) for arg in tree.args.expressions], ) @@ -81,10 +79,7 @@ def visit_lambda(self, tree: LambdaAST) -> Closure: self.bindings.bind(parameter.name, parameter) closure = Closure(parameters, statements=[]) - self.bindings.bind( - self.namespace.define(Symbol("self")), - closure, - ) + self.bindings.bind(self.namespace.define(Symbol("self")), closure) closure.statements = [ self.visit_expression(expression) @@ -103,7 +98,8 @@ def visit_kind(self, tree: KindAST) -> Kind: match tree.name.name: case "int": return Kind("int") - raise NotImplementedError + case _: + raise NotImplementedError @override def visit_integer(self, tree: IntegerAST) -> Integer: diff --git a/sleepy/tafka/emit.py b/sleepy/tafka/emit.py index 933883d..f50f916 100644 --- a/sleepy/tafka/emit.py +++ b/sleepy/tafka/emit.py @@ -1,5 +1,5 @@ from collections.abc import Generator -from typing import override +from typing import TYPE_CHECKING, override from sleepy.program import ( Application, @@ -11,7 +11,6 @@ Kind, Program, Symbol, - SymbolId, Visitor, ) from sleepy.program.unit import ProgramUnit @@ -40,6 +39,9 @@ from sleepy.tafka.representation import Kind as TafKind from sleepy.tafka.representation.rvalue import And, Invokation, Or +if TYPE_CHECKING: + from sleepy.core import UID + UniqueNameSequence = Generator[str, None, None] @@ -52,7 +54,7 @@ def __init__(self, unit: ProgramUnit) -> None: self.var_names = map(str, range(10000000)) self.lbl_names = map(str, range(10000000)) - self.vars: dict[SymbolId, Var] = {} + self.vars: dict[UID, Var] = {} self.procedures: list[Procedure] = [] self.current_block = self.main