Change the way values work

Change the parsing for values to make them more reusable, in particular
for when I implement extensions.
This commit is contained in:
James Westman 2023-01-12 13:19:15 -06:00
parent 6938267952
commit 1df46b5a06
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
30 changed files with 707 additions and 291 deletions

View file

@ -24,6 +24,8 @@ import typing as T
from .errors import *
from .lsp_utils import SemanticToken
TType = T.TypeVar("TType")
class Children:
"""Allows accessing children by type using array syntax."""
@ -34,6 +36,14 @@ class Children:
def __iter__(self):
return iter(self._children)
@T.overload
def __getitem__(self, key: T.Type[TType]) -> T.List[TType]:
...
@T.overload
def __getitem__(self, key: int) -> "AstNode":
...
def __getitem__(self, key):
if isinstance(key, int):
return self._children[key]
@ -41,6 +51,27 @@ class Children:
return [child for child in self._children if isinstance(child, key)]
TCtx = T.TypeVar("TCtx")
TAttr = T.TypeVar("TAttr")
class Ctx:
"""Allows accessing values from higher in the syntax tree."""
def __init__(self, node: "AstNode") -> None:
self.node = node
def __getitem__(self, key: T.Type[TCtx]) -> T.Optional[TCtx]:
attrs = self.node._attrs_by_type(Context)
for name, attr in attrs:
if attr.type == key:
return getattr(self.node, name)
if self.node.parent is not None:
return self.node.parent.context[key]
else:
return None
class AstNode:
"""Base class for nodes in the abstract syntax tree."""
@ -62,6 +93,10 @@ class AstNode:
getattr(cls, f) for f in dir(cls) if hasattr(getattr(cls, f), "_validator")
]
@cached_property
def context(self):
return Ctx(self)
@property
def root(self):
if self.parent is None:
@ -105,7 +140,9 @@ class AstNode:
for child in self.children:
yield from child._get_errors()
def _attrs_by_type(self, attr_type):
def _attrs_by_type(
self, attr_type: T.Type[TAttr]
) -> T.Iterator[T.Tuple[str, TAttr]]:
for name in dir(type(self)):
item = getattr(type(self), name)
if isinstance(item, attr_type):
@ -217,3 +254,23 @@ def docs(*args, **kwargs):
return Docs(func, *args, **kwargs)
return decorator
class Context:
def __init__(self, type: T.Type[TCtx], func: T.Callable[[AstNode], TCtx]) -> None:
self.type = type
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
return self.func(instance)
def context(type: T.Type[TCtx]):
"""Decorator for functions that return a context object, which is passed down to ."""
def decorator(func: T.Callable[[AstNode], TCtx]) -> Context:
return Context(type, func)
return decorator