aboutsummaryrefslogtreecommitdiffstats
path: root/bsfs
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-01-20 14:36:11 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-01-20 14:36:11 +0100
commita4789394e40aaa3152ad6009955709a6c7d277c2 (patch)
tree404e645ad07c90bff7e6a54f4d8f8e6fee612d3b /bsfs
parente12cd52ad267563c8046a593ad551b1dd089a702 (diff)
downloadbsfs-a4789394e40aaa3152ad6009955709a6c7d277c2.tar.gz
bsfs-a4789394e40aaa3152ad6009955709a6c7d277c2.tar.bz2
bsfs-a4789394e40aaa3152ad6009955709a6c7d277c2.zip
fetch AST
Diffstat (limited to 'bsfs')
-rw-r--r--bsfs/query/ast/__init__.py4
-rw-r--r--bsfs/query/ast/fetch.py175
-rw-r--r--bsfs/query/ast/filter_.py1
3 files changed, 179 insertions, 1 deletions
diff --git a/bsfs/query/ast/__init__.py b/bsfs/query/ast/__init__.py
index 704d051..66b097d 100644
--- a/bsfs/query/ast/__init__.py
+++ b/bsfs/query/ast/__init__.py
@@ -1,6 +1,6 @@
"""Query AST components.
-The query AST consists of a Filter syntax tree.
+The query AST consists of a Filter and a Fetch syntax trees.
Classes beginning with an underscore (_) represent internal type hierarchies
and should not be used for parsing. Note that the AST structures do not
@@ -14,10 +14,12 @@ Author: Matthias Baumgartner, 2022
import typing
# inner-module imports
+from . import fetch
from . import filter_ as filter # pylint: disable=redefined-builtin
# exports
__all__: typing.Sequence[str] = (
+ 'fetch',
'filter',
)
diff --git a/bsfs/query/ast/fetch.py b/bsfs/query/ast/fetch.py
new file mode 100644
index 0000000..5e603a1
--- /dev/null
+++ b/bsfs/query/ast/fetch.py
@@ -0,0 +1,175 @@
+"""
+
+Part of the BlackStar filesystem (bsfs) module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+from collections import abc
+import typing
+
+# bsfs imports
+from bsfs.utils import URI, typename, normalize_args
+
+# exports
+__all__ : typing.Sequence[str] = (
+ 'All',
+ 'Fetch',
+ 'FetchExpression',
+ 'Node',
+ 'This',
+ 'Value',
+ )
+
+
+## code ##
+
+class FetchExpression(abc.Hashable):
+ """Generic Fetch expression."""
+
+ def __repr__(self) -> str:
+ """Return the expressions's string representation."""
+ return f'{typename(self)}()'
+
+ def __hash__(self) -> int:
+ """Return the expression's integer representation."""
+ return hash(type(self))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ """Return True if *self* and *other* are equivalent."""
+ return isinstance(other, type(self))
+
+
+class All(FetchExpression):
+ """Fetch all child expressions."""
+
+ # child expressions.
+ expr: typing.Set[FetchExpression]
+
+ def __init__(self, *expr):
+ # unpack child expressions
+ unfolded = set(normalize_args(*expr))
+ # check child expressions
+ if len(unfolded) == 0:
+ raise AttributeError('expected at least one expression, found none')
+ if not all(isinstance(itm, FetchExpression) for itm in unfolded):
+ raise TypeError(expr)
+ # initialize
+ super().__init__()
+ # assign members
+ self.expr = unfolded
+
+ def __iter__(self) -> typing.Iterator[FetchExpression]:
+ return iter(self.expr)
+
+ def __len__(self) -> int:
+ return len(self.expr)
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self.expr})'
+
+ def __hash__(self) -> int:
+ # FIXME: Produces different hashes for different orders of self.expr
+ return hash((super().__hash__(), tuple(self.expr)))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return super().__eq__(other) and self.expr == other.expr
+
+
+class _Branch(FetchExpression):
+ """Branch along a predicate."""
+
+ # FIXME: Use a Predicate (like in ast.filter) so that we can also reverse them!
+
+ # predicate to follow.
+ predicate: URI
+
+ def __init__(self, predicate: URI):
+ if not isinstance(predicate, URI):
+ raise TypeError(predicate)
+ self.predicate = predicate
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self.predicate})'
+
+ def __hash__(self) -> int:
+ return hash((super().__hash__(), self.predicate))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return super().__eq__(other) and self.predicate == other.predicate
+
+
+class Fetch(_Branch):
+ """Follow a predicate before evaluating a child epxression."""
+
+ # child expression.
+ expr: FetchExpression
+
+ def __init__(self, predicate: URI, expr: FetchExpression):
+ # check child expressions
+ if not isinstance(expr, FetchExpression):
+ raise TypeError(expr)
+ # initialize
+ super().__init__(predicate)
+ # assign members
+ self.expr = expr
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self.predicate}, {self.expr})'
+
+ def __hash__(self) -> int:
+ return hash((super().__hash__(), self.expr))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return super().__eq__(other) and self.expr == other.expr
+
+
+class _Named(_Branch):
+ """Fetch a (named) symbol at a predicate."""
+
+ # symbol name.
+ name: str
+
+ def __init__(self, predicate: URI, name: str):
+ super().__init__(predicate)
+ self.name = str(name)
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self.predicate}, {self.name})'
+
+ def __hash__(self) -> int:
+ return hash((super().__hash__(), self.name))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return super().__eq__(other) and self.name == other.name
+
+
+class Node(_Named): # pylint: disable=too-few-public-methods
+ """Fetch a Node at a predicate."""
+ # FIXME: Is this actually needed?
+
+
+class Value(_Named): # pylint: disable=too-few-public-methods
+ """Fetch a Literal at a predicate."""
+
+
+class This(FetchExpression):
+ """Fetch the current Node."""
+
+ # symbol name.
+ name: str
+
+ def __init__(self, name: str):
+ super().__init__()
+ self.name = str(name)
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self.name})'
+
+ def __hash__(self) -> int:
+ return hash((super().__hash__(), self.name))
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return super().__eq__(other) and self.name == other.name
+
+## EOF ##
diff --git a/bsfs/query/ast/filter_.py b/bsfs/query/ast/filter_.py
index 2f0270c..81b0de2 100644
--- a/bsfs/query/ast/filter_.py
+++ b/bsfs/query/ast/filter_.py
@@ -153,6 +153,7 @@ class _Agg(FilterExpression, abc.Collection):
# check type
if not all(isinstance(e, FilterExpression) for e in unfolded):
raise TypeError(expr)
+ # FIXME: Require at least one child expression?
# assign member
self.expr = unfolded