Skip to content

Commit

Permalink
Added back reference classes
Browse files Browse the repository at this point in the history
  • Loading branch information
knassre-bodo committed Oct 30, 2024
1 parent 3a3255a commit f4d5031
Show file tree
Hide file tree
Showing 9 changed files with 396 additions and 0 deletions.
69 changes: 69 additions & 0 deletions pydough/pydough_ast/collections/back_reference_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
TODO: add file-level docstring
"""

__all__ = ["BackReferenceCollection"]


from pydough.pydough_ast.abstract_pydough_ast import PyDoughAST
from .collection_ast import PyDoughCollectionAST
from .table_collection import TableCollection
from pydough.pydough_ast.errors import PyDoughASTException


class BackReferenceCollection(TableCollection):
"""
The AST node implementation class representing a subcollection of an
ancestor collection.
"""

def __init__(
self,
parent: PyDoughCollectionAST,
term_name: str,
back_levels: int,
):
self._parent: PyDoughAST = parent
self._back_levels: int = back_levels
self._ancestor: PyDoughCollectionAST = parent
for _ in range(back_levels):
self._ancestor = self._ancestor.ancestor_context
if self._ancestor is None:
raise PyDoughASTException(
f"Cannot reference back {back_levels} levels above {parent!r}"
)
super.__init__(self._ancestor.get_term(term_name))

@property
def parent(self) -> PyDoughCollectionAST:
"""
The parent node that the collection node is a subcollection of.
"""
return self._parent

@property
def back_levels(self) -> int:
"""
The number of levels upward that the backreference refers to.
"""
return self._back_levels

@property
def ancestor(self) -> PyDoughCollectionAST:
"""
The specific ancestor collection that the ancestor refers to.
"""
return self._ancestor

@property
def ancestor_context(self) -> PyDoughCollectionAST | None:
return self.parent

def to_string(self) -> str:
return f"BackReference[{self.back_levels}.{self.collection.to_string()}"

def to_tree_string(self) -> str:
raise NotImplementedError

def equals(self, other: "BackReferenceCollection") -> bool:
return super().equals(other) and self.ancestor == other.ancestor
22 changes: 22 additions & 0 deletions pydough/pydough_ast/collections/compound_sub_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
from pydough.pydough_ast.abstract_pydough_ast import PyDoughAST
from .collection_ast import PyDoughCollectionAST
from .sub_collection import SubCollection
from .hidden_back_reference_collection import HiddenBackReferenceCollection
from pydough.pydough_ast.expressions.hidden_back_reference_expression import (
HiddenBackReferenceExpression,
)


class CompoundSubCollection(SubCollection):
Expand Down Expand Up @@ -117,6 +121,24 @@ def properties(self) -> Dict[str, Tuple[int | None, PyDoughAST]]:
raise PyDoughASTException(
f"Undefined inherited properties: {undefined_inherited}"
)
for alias, (location, original_name) in self._inheritance_sources.items():
calc_idx, expr = self._properties[alias]
ancestor: PyDoughCollectionAST = self._subcollection_chain[location]
back_levels: int = len(self.subcollection_chain) - location
if isinstance(expr, PyDoughCollectionAST):
self._properties[alias] = (
calc_idx,
HiddenBackReferenceCollection(
self, ancestor, original_name, back_levels
),
)
else:
self._properties[alias] = (
calc_idx,
HiddenBackReferenceExpression(
self, ancestor, original_name, back_levels
),
)
return self._properties

def to_string(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
TODO: add file-level docstring
"""

__all__ = ["HiddenBackReferenceCollection"]


from .collection_ast import PyDoughCollectionAST
from .back_reference_collection import BackReferenceCollection


class HiddenBackReferenceCollection(BackReferenceCollection):
"""
The AST node implementation class representing a subcollection of an
ancestor collection.
"""

def __init__(
self,
collection: PyDoughCollectionAST,
ancestor: PyDoughCollectionAST,
term_name: str,
back_levels: int,
):
self._collection: PyDoughCollectionAST = collection
self._term_name: str = term_name
self._back_levels: int = back_levels
self._ancestor: PyDoughCollectionAST = ancestor
super(BackReferenceCollection, self).__init__(
self._ancestor.get_term(term_name)
)

def to_string(self) -> str:
return f"HiddenBackReferenceCollection[{self.back_levels}.{self.collection.to_string()}"

def to_tree_string(self) -> str:
raise NotImplementedError
2 changes: 2 additions & 0 deletions pydough/pydough_ast/expressions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"Literal",
"ExpressionFunctionCall",
"Reference",
"BackReferenceExpression",
]

from .expression_ast import PyDoughExpressionAST
from .column_property import ColumnProperty
from .literal import Literal
from .expression_function_call import ExpressionFunctionCall
from .reference import Reference
from .back_reference_expression import BackReferenceExpression
71 changes: 71 additions & 0 deletions pydough/pydough_ast/expressions/back_reference_expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""
TODO: add file-level docstring
"""

__all__ = ["BackReferenceExpression"]

from . import PyDoughExpressionAST
from pydough.types import PyDoughType
from pydough.pydough_ast.collections import PyDoughCollectionAST
from pydough.pydough_ast.errors import PyDoughASTException
from .reference import Reference


class BackReferenceExpression(Reference):
"""
The AST node implementation class representing a reference to a term in
the ancestor context.
"""

def __init__(
self, collection: PyDoughCollectionAST, term_name: str, back_levels: int
):
self._collection: PyDoughCollectionAST = collection
self._term_name: str = term_name
self._back_levels: int = back_levels
self._ancestor: PyDoughCollectionAST = collection
for _ in range(back_levels):
self._ancestor = self._ancestor.ancestor_context
if self._ancestor is None:
raise PyDoughASTException(
f"Cannot reference back {back_levels} levels above {collection!r}"
)
self._expression: PyDoughExpressionAST = self._ancestor.get_term(term_name)

@property
def back_levels(self) -> int:
"""
The number of levels upward that the backreference refers to.
"""
return self._back_levels

@property
def ancestor(self) -> PyDoughCollectionAST:
"""
The specific ancestor collection that the ancestor refers to.
"""
return self._ancestor

@property
def expression(self) -> PyDoughExpressionAST:
"""
The original expression that the reference refers to.
"""
return self._expression

@property
def pydough_type(self) -> PyDoughType:
return self.expression.pydough_type

@property
def is_aggregation(self) -> bool:
return self.expression.is_aggregation

def requires_enclosing_parens(self, parent: PyDoughExpressionAST) -> bool:
return False

def to_string(self) -> str:
return f"BackReferenceExpression[{self.back_levels}:{self.term_name}]"

def equals(self, other: "BackReferenceExpression") -> bool:
return super().equals(other) and self.ancestor.equals(other.ancestor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
TODO: add file-level docstring
"""

__all__ = ["HiddenBackReferenceExpression"]

from . import PyDoughExpressionAST
from pydough.pydough_ast.collections import PyDoughCollectionAST
from .back_reference_expression import BackReferenceExpression


class HiddenBackReferenceExpression(BackReferenceExpression):
"""
The AST node implementation class representing a reference to a term in
the ancestor context through the lens of a compound relationship's
inherited properties.
"""

def __init__(
self,
collection: PyDoughCollectionAST,
ancestor: PyDoughCollectionAST,
term_name: str,
back_levels: int,
):
self._collection: PyDoughCollectionAST = collection
self._term_name: str = term_name
self._back_levels: int = back_levels
self._ancestor: PyDoughCollectionAST = ancestor
self._expression: PyDoughExpressionAST = self._ancestor.get_term(term_name)

def to_string(self) -> str:
return f"HiddenBackReferenceExpression[{self.back_levels}:{self.term_name}]"
23 changes: 23 additions & 0 deletions pydough/pydough_ast/node_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ExpressionFunctionCall,
ColumnProperty,
Reference,
BackReferenceExpression,
PyDoughExpressionAST,
)
from .pydough_operators import PyDoughOperatorAST, builtin_registered_operators
Expand Down Expand Up @@ -133,6 +134,28 @@ def build_reference(self, collection: PyDoughCollectionAST, name: str) -> Refere
"""
return Reference(collection, name)

def build_back_reference_expression(
self, collection: PyDoughCollectionAST, name: str, levels: int
) -> Reference:
"""
Creates a new reference to an expression from an ancestor collection.
Args:
`collection`: the collection that the back reference comes from.
`name`: the name of the expression being referenced.
`levels`: the number of levels back from `collection` the reference
refers to.
Returns:
The newly created PyDough Back Reference.
Raises:
`PyDoughASTException`: if `name` does not refer to an expression in
the ancestor collection, or the collection does not have `levels`
many ancestors.
"""
return BackReferenceExpression(collection, name, levels)

def build_table_collection(self, name: str) -> TableCollection:
"""
Creates a new table collection invocation.
Expand Down
Loading

0 comments on commit f4d5031

Please sign in to comment.