55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
from __future__ import annotations
|
|
from typing import Any, Callable, Optional
|
|
from string import whitespace
|
|
|
|
from core.primitives.symbol import symbolKey, isSymbol
|
|
from core.printer import Representation
|
|
from core.primitives.list import NIL
|
|
|
|
class Environment:
|
|
"""Environment for variable bindings with lexical scoping."""
|
|
|
|
frame: dict[str, Any]
|
|
parent: Environment | None
|
|
|
|
def __init__(self, parent: Environment | None) -> None:
|
|
self.frame: dict[str, Any] = {}
|
|
self.parent: Environment | None = parent
|
|
|
|
def add(self, symbol_key: Any, value: Any) -> None:
|
|
"""Add a new binding to the current environment frame."""
|
|
self.frame[symbolKey(symbol_key)] = value
|
|
|
|
def set(self, symbol_key: Any, value: Any) -> None:
|
|
"""Set a binding in the environment chain."""
|
|
key: str = symbolKey(symbol_key)
|
|
currentEnvironment: Environment = self
|
|
while currentEnvironment is not None:
|
|
if key in currentEnvironment.frame:
|
|
currentEnvironment.frame[key] = value
|
|
return
|
|
assert currentEnvironment is Environment
|
|
currentEnvironment = currentEnvironment.parent
|
|
raise SyntaxError(f"no such symbol: {key}")
|
|
|
|
def get(self, symbol_key: Any) -> Any:
|
|
"""Get a binding from the environment chain."""
|
|
key: str = symbolKey(symbol_key)
|
|
currentEnvironment: Environment | None = self
|
|
while currentEnvironment is not None:
|
|
if key in currentEnvironment.frame:
|
|
return currentEnvironment.frame[key]
|
|
currentEnvironment = currentEnvironment.parent
|
|
return symbol_key
|
|
|
|
EVAL_NIL: Callable[[], Any] = lambda: NIL
|
|
ENV: Environment = Environment(None)
|
|
|
|
MEANINGFUL: str = "\"();'"
|
|
IGNORE: str = whitespace + ","
|
|
|
|
RepresentateAST: Callable[[Any], str] = lambda value: (
|
|
value if type(value) is str and not isSymbol(value) else Representation(value)
|
|
)
|
|
TCO: bool = True
|