flowquery 1.0.18 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +3 -0
- package/.github/workflows/python-publish.yml +56 -4
- package/.github/workflows/release.yml +26 -19
- package/.husky/pre-commit +26 -0
- package/README.md +37 -32
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/data.d.ts +5 -4
- package/dist/graph/data.d.ts.map +1 -1
- package/dist/graph/data.js +38 -20
- package/dist/graph/data.js.map +1 -1
- package/dist/graph/node.d.ts +2 -0
- package/dist/graph/node.d.ts.map +1 -1
- package/dist/graph/node.js +23 -0
- package/dist/graph/node.js.map +1 -1
- package/dist/graph/node_data.js +1 -1
- package/dist/graph/node_data.js.map +1 -1
- package/dist/graph/pattern.d.ts.map +1 -1
- package/dist/graph/pattern.js +11 -4
- package/dist/graph/pattern.js.map +1 -1
- package/dist/graph/relationship.d.ts +6 -1
- package/dist/graph/relationship.d.ts.map +1 -1
- package/dist/graph/relationship.js +43 -5
- package/dist/graph/relationship.js.map +1 -1
- package/dist/graph/relationship_data.d.ts +2 -0
- package/dist/graph/relationship_data.d.ts.map +1 -1
- package/dist/graph/relationship_data.js +8 -1
- package/dist/graph/relationship_data.js.map +1 -1
- package/dist/graph/relationship_match_collector.js +2 -2
- package/dist/graph/relationship_match_collector.js.map +1 -1
- package/dist/graph/relationship_reference.d.ts.map +1 -1
- package/dist/graph/relationship_reference.js +2 -1
- package/dist/graph/relationship_reference.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/parsing/parser.d.ts +6 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +139 -72
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/misc/data/test.json +10 -0
- package/flowquery-py/misc/data/users.json +242 -0
- package/flowquery-py/notebooks/TestFlowQuery.ipynb +440 -0
- package/flowquery-py/pyproject.toml +48 -2
- package/flowquery-py/src/__init__.py +7 -5
- package/flowquery-py/src/compute/runner.py +14 -10
- package/flowquery-py/src/extensibility.py +8 -8
- package/flowquery-py/src/graph/__init__.py +7 -7
- package/flowquery-py/src/graph/data.py +38 -20
- package/flowquery-py/src/graph/database.py +10 -20
- package/flowquery-py/src/graph/node.py +50 -19
- package/flowquery-py/src/graph/node_data.py +1 -1
- package/flowquery-py/src/graph/node_reference.py +10 -11
- package/flowquery-py/src/graph/pattern.py +27 -37
- package/flowquery-py/src/graph/pattern_expression.py +13 -11
- package/flowquery-py/src/graph/patterns.py +2 -2
- package/flowquery-py/src/graph/physical_node.py +4 -3
- package/flowquery-py/src/graph/physical_relationship.py +5 -5
- package/flowquery-py/src/graph/relationship.py +62 -14
- package/flowquery-py/src/graph/relationship_data.py +7 -2
- package/flowquery-py/src/graph/relationship_match_collector.py +15 -10
- package/flowquery-py/src/graph/relationship_reference.py +4 -4
- package/flowquery-py/src/io/command_line.py +13 -14
- package/flowquery-py/src/parsing/__init__.py +2 -2
- package/flowquery-py/src/parsing/alias_option.py +1 -1
- package/flowquery-py/src/parsing/ast_node.py +21 -20
- package/flowquery-py/src/parsing/base_parser.py +7 -7
- package/flowquery-py/src/parsing/components/__init__.py +3 -3
- package/flowquery-py/src/parsing/components/from_.py +3 -1
- package/flowquery-py/src/parsing/components/headers.py +2 -2
- package/flowquery-py/src/parsing/components/null.py +2 -2
- package/flowquery-py/src/parsing/context.py +7 -7
- package/flowquery-py/src/parsing/data_structures/associative_array.py +7 -7
- package/flowquery-py/src/parsing/data_structures/json_array.py +3 -3
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +4 -4
- package/flowquery-py/src/parsing/data_structures/lookup.py +2 -2
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +2 -2
- package/flowquery-py/src/parsing/expressions/__init__.py +16 -16
- package/flowquery-py/src/parsing/expressions/expression.py +16 -13
- package/flowquery-py/src/parsing/expressions/expression_map.py +9 -9
- package/flowquery-py/src/parsing/expressions/f_string.py +3 -3
- package/flowquery-py/src/parsing/expressions/identifier.py +4 -3
- package/flowquery-py/src/parsing/expressions/number.py +3 -3
- package/flowquery-py/src/parsing/expressions/operator.py +16 -16
- package/flowquery-py/src/parsing/expressions/reference.py +3 -3
- package/flowquery-py/src/parsing/expressions/string.py +2 -2
- package/flowquery-py/src/parsing/functions/__init__.py +17 -17
- package/flowquery-py/src/parsing/functions/aggregate_function.py +8 -8
- package/flowquery-py/src/parsing/functions/async_function.py +12 -9
- package/flowquery-py/src/parsing/functions/avg.py +4 -4
- package/flowquery-py/src/parsing/functions/collect.py +6 -6
- package/flowquery-py/src/parsing/functions/function.py +6 -6
- package/flowquery-py/src/parsing/functions/function_factory.py +31 -34
- package/flowquery-py/src/parsing/functions/function_metadata.py +10 -11
- package/flowquery-py/src/parsing/functions/functions.py +14 -6
- package/flowquery-py/src/parsing/functions/join.py +3 -3
- package/flowquery-py/src/parsing/functions/keys.py +3 -3
- package/flowquery-py/src/parsing/functions/predicate_function.py +8 -7
- package/flowquery-py/src/parsing/functions/predicate_sum.py +12 -7
- package/flowquery-py/src/parsing/functions/rand.py +2 -2
- package/flowquery-py/src/parsing/functions/range_.py +9 -4
- package/flowquery-py/src/parsing/functions/replace.py +2 -2
- package/flowquery-py/src/parsing/functions/round_.py +2 -2
- package/flowquery-py/src/parsing/functions/size.py +2 -2
- package/flowquery-py/src/parsing/functions/split.py +9 -4
- package/flowquery-py/src/parsing/functions/stringify.py +3 -3
- package/flowquery-py/src/parsing/functions/sum.py +4 -4
- package/flowquery-py/src/parsing/functions/to_json.py +2 -2
- package/flowquery-py/src/parsing/functions/type_.py +3 -3
- package/flowquery-py/src/parsing/functions/value_holder.py +1 -1
- package/flowquery-py/src/parsing/logic/__init__.py +2 -2
- package/flowquery-py/src/parsing/logic/case.py +0 -1
- package/flowquery-py/src/parsing/logic/when.py +3 -1
- package/flowquery-py/src/parsing/operations/__init__.py +10 -10
- package/flowquery-py/src/parsing/operations/aggregated_return.py +3 -5
- package/flowquery-py/src/parsing/operations/aggregated_with.py +4 -4
- package/flowquery-py/src/parsing/operations/call.py +6 -7
- package/flowquery-py/src/parsing/operations/create_node.py +5 -4
- package/flowquery-py/src/parsing/operations/create_relationship.py +5 -4
- package/flowquery-py/src/parsing/operations/group_by.py +18 -16
- package/flowquery-py/src/parsing/operations/load.py +21 -19
- package/flowquery-py/src/parsing/operations/match.py +8 -7
- package/flowquery-py/src/parsing/operations/operation.py +3 -3
- package/flowquery-py/src/parsing/operations/projection.py +6 -6
- package/flowquery-py/src/parsing/operations/return_op.py +9 -5
- package/flowquery-py/src/parsing/operations/unwind.py +3 -2
- package/flowquery-py/src/parsing/operations/where.py +9 -7
- package/flowquery-py/src/parsing/operations/with_op.py +2 -2
- package/flowquery-py/src/parsing/parser.py +178 -114
- package/flowquery-py/src/parsing/token_to_node.py +2 -2
- package/flowquery-py/src/tokenization/__init__.py +4 -4
- package/flowquery-py/src/tokenization/keyword.py +1 -1
- package/flowquery-py/src/tokenization/operator.py +1 -1
- package/flowquery-py/src/tokenization/string_walker.py +4 -4
- package/flowquery-py/src/tokenization/symbol.py +1 -1
- package/flowquery-py/src/tokenization/token.py +11 -11
- package/flowquery-py/src/tokenization/token_mapper.py +10 -9
- package/flowquery-py/src/tokenization/token_type.py +1 -1
- package/flowquery-py/src/tokenization/tokenizer.py +19 -19
- package/flowquery-py/src/tokenization/trie.py +18 -17
- package/flowquery-py/src/utils/__init__.py +1 -1
- package/flowquery-py/src/utils/object_utils.py +3 -3
- package/flowquery-py/src/utils/string_utils.py +12 -12
- package/flowquery-py/tests/compute/test_runner.py +214 -7
- package/flowquery-py/tests/parsing/test_parser.py +41 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/graph/data.ts +38 -20
- package/src/graph/node.ts +23 -0
- package/src/graph/node_data.ts +1 -1
- package/src/graph/pattern.ts +13 -4
- package/src/graph/relationship.ts +45 -5
- package/src/graph/relationship_data.ts +8 -1
- package/src/graph/relationship_match_collector.ts +1 -1
- package/src/graph/relationship_reference.ts +2 -1
- package/src/index.ts +5 -5
- package/src/parsing/parser.ts +139 -71
- package/tests/compute/runner.test.ts +249 -79
- package/tests/parsing/parser.test.ts +32 -0
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
"""Represents a node in the Abstract Syntax Tree (AST)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from typing import Any, Generator, List, Optional
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class ASTNode:
|
|
8
9
|
"""Represents a node in the Abstract Syntax Tree (AST).
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
The AST is a tree representation of the parsed FlowQuery statement structure.
|
|
11
12
|
Each node can have children and maintains a reference to its parent.
|
|
12
|
-
|
|
13
|
+
|
|
13
14
|
Example:
|
|
14
15
|
root = ASTNode()
|
|
15
16
|
child = ASTNode()
|
|
16
17
|
root.add_child(child)
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
|
-
def __init__(self):
|
|
20
|
+
def __init__(self) -> None:
|
|
20
21
|
self._parent: Optional[ASTNode] = None
|
|
21
22
|
self.children: List[ASTNode] = []
|
|
22
23
|
|
|
23
24
|
def add_child(self, child: ASTNode) -> None:
|
|
24
25
|
"""Adds a child node to this node and sets the child's parent reference.
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
Args:
|
|
27
28
|
child: The child node to add
|
|
28
29
|
"""
|
|
@@ -31,10 +32,10 @@ class ASTNode:
|
|
|
31
32
|
|
|
32
33
|
def first_child(self) -> ASTNode:
|
|
33
34
|
"""Returns the first child node.
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
Returns:
|
|
36
37
|
The first child node
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
Raises:
|
|
39
40
|
ValueError: If the node has no children
|
|
40
41
|
"""
|
|
@@ -44,10 +45,10 @@ class ASTNode:
|
|
|
44
45
|
|
|
45
46
|
def last_child(self) -> ASTNode:
|
|
46
47
|
"""Returns the last child node.
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
Returns:
|
|
49
50
|
The last child node
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
Raises:
|
|
52
53
|
ValueError: If the node has no children
|
|
53
54
|
"""
|
|
@@ -57,7 +58,7 @@ class ASTNode:
|
|
|
57
58
|
|
|
58
59
|
def get_children(self) -> List[ASTNode]:
|
|
59
60
|
"""Returns all child nodes.
|
|
60
|
-
|
|
61
|
+
|
|
61
62
|
Returns:
|
|
62
63
|
Array of child nodes
|
|
63
64
|
"""
|
|
@@ -65,7 +66,7 @@ class ASTNode:
|
|
|
65
66
|
|
|
66
67
|
def child_count(self) -> int:
|
|
67
68
|
"""Returns the number of child nodes.
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
Returns:
|
|
70
71
|
The count of children
|
|
71
72
|
"""
|
|
@@ -73,7 +74,7 @@ class ASTNode:
|
|
|
73
74
|
|
|
74
75
|
def value(self) -> Any:
|
|
75
76
|
"""Returns the value of this node. Override in subclasses to provide specific values.
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
Returns:
|
|
78
79
|
The node's value, or None if not applicable
|
|
79
80
|
"""
|
|
@@ -81,7 +82,7 @@ class ASTNode:
|
|
|
81
82
|
|
|
82
83
|
def is_operator(self) -> bool:
|
|
83
84
|
"""Checks if this node represents an operator.
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
Returns:
|
|
86
87
|
True if this is an operator node, False otherwise
|
|
87
88
|
"""
|
|
@@ -89,7 +90,7 @@ class ASTNode:
|
|
|
89
90
|
|
|
90
91
|
def is_operand(self) -> bool:
|
|
91
92
|
"""Checks if this node represents an operand (the opposite of an operator).
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
Returns:
|
|
94
95
|
True if this is an operand node, False otherwise
|
|
95
96
|
"""
|
|
@@ -98,7 +99,7 @@ class ASTNode:
|
|
|
98
99
|
@property
|
|
99
100
|
def precedence(self) -> int:
|
|
100
101
|
"""Gets the operator precedence for this node. Higher values indicate higher precedence.
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
Returns:
|
|
103
104
|
The precedence value (0 for non-operators)
|
|
104
105
|
"""
|
|
@@ -107,7 +108,7 @@ class ASTNode:
|
|
|
107
108
|
@property
|
|
108
109
|
def left_associative(self) -> bool:
|
|
109
110
|
"""Indicates whether this operator is left-associative.
|
|
110
|
-
|
|
111
|
+
|
|
111
112
|
Returns:
|
|
112
113
|
True if left-associative, False otherwise
|
|
113
114
|
"""
|
|
@@ -115,7 +116,7 @@ class ASTNode:
|
|
|
115
116
|
|
|
116
117
|
def print(self) -> str:
|
|
117
118
|
"""Prints a string representation of the AST tree starting from this node.
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
Returns:
|
|
120
121
|
A formatted string showing the tree structure
|
|
121
122
|
"""
|
|
@@ -123,10 +124,10 @@ class ASTNode:
|
|
|
123
124
|
|
|
124
125
|
def _print(self, indent: int) -> Generator[str, None, None]:
|
|
125
126
|
"""Generator function for recursively printing the tree structure.
|
|
126
|
-
|
|
127
|
+
|
|
127
128
|
Args:
|
|
128
129
|
indent: The current indentation level
|
|
129
|
-
|
|
130
|
+
|
|
130
131
|
Yields:
|
|
131
132
|
Lines representing each node in the tree
|
|
132
133
|
"""
|
|
@@ -139,7 +140,7 @@ class ASTNode:
|
|
|
139
140
|
|
|
140
141
|
def __str__(self) -> str:
|
|
141
142
|
"""Returns a string representation of this node. Override in subclasses for custom formatting.
|
|
142
|
-
|
|
143
|
+
|
|
143
144
|
Returns:
|
|
144
145
|
The string representation
|
|
145
146
|
"""
|
|
@@ -8,7 +8,7 @@ from ..tokenization.tokenizer import Tokenizer
|
|
|
8
8
|
|
|
9
9
|
class BaseParser:
|
|
10
10
|
"""Base class for parsers providing common token manipulation functionality.
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
This class handles tokenization and provides utility methods for navigating
|
|
13
13
|
through tokens, peeking ahead, and checking token sequences.
|
|
14
14
|
"""
|
|
@@ -19,7 +19,7 @@ class BaseParser:
|
|
|
19
19
|
|
|
20
20
|
def tokenize(self, statement: str) -> None:
|
|
21
21
|
"""Tokenizes a statement and initializes the token array.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
Args:
|
|
24
24
|
statement: The input statement to tokenize
|
|
25
25
|
"""
|
|
@@ -32,7 +32,7 @@ class BaseParser:
|
|
|
32
32
|
|
|
33
33
|
def peek(self) -> Optional[Token]:
|
|
34
34
|
"""Peeks at the next token without advancing the current position.
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
Returns:
|
|
37
37
|
The next token, or None if at the end of the token stream
|
|
38
38
|
"""
|
|
@@ -42,11 +42,11 @@ class BaseParser:
|
|
|
42
42
|
|
|
43
43
|
def ahead(self, tokens: List[Token], skip_whitespace_and_comments: bool = True) -> bool:
|
|
44
44
|
"""Checks if a sequence of tokens appears ahead in the token stream.
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
Args:
|
|
47
47
|
tokens: The sequence of tokens to look for
|
|
48
48
|
skip_whitespace_and_comments: Whether to skip whitespace and comments when matching
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
Returns:
|
|
51
51
|
True if the token sequence is found ahead, False otherwise
|
|
52
52
|
"""
|
|
@@ -64,7 +64,7 @@ class BaseParser:
|
|
|
64
64
|
@property
|
|
65
65
|
def token(self) -> Token:
|
|
66
66
|
"""Gets the current token.
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
Returns:
|
|
69
69
|
The current token, or EOF if at the end
|
|
70
70
|
"""
|
|
@@ -75,7 +75,7 @@ class BaseParser:
|
|
|
75
75
|
@property
|
|
76
76
|
def previous_token(self) -> Token:
|
|
77
77
|
"""Gets the previous token.
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
Returns:
|
|
80
80
|
The previous token, or EOF if at the beginning
|
|
81
81
|
"""
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Components module for FlowQuery parsing."""
|
|
2
2
|
|
|
3
3
|
from .csv import CSV
|
|
4
|
-
from .json import JSON
|
|
5
|
-
from .text import Text
|
|
6
4
|
from .from_ import From
|
|
7
5
|
from .headers import Headers
|
|
8
|
-
from .
|
|
6
|
+
from .json import JSON
|
|
9
7
|
from .null import Null
|
|
8
|
+
from .post import Post
|
|
9
|
+
from .text import Text
|
|
10
10
|
|
|
11
11
|
__all__ = [
|
|
12
12
|
"CSV",
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""From component node."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
from ..ast_node import ASTNode
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class From(ASTNode):
|
|
7
9
|
"""Represents a FROM clause in LOAD operations."""
|
|
8
10
|
|
|
9
|
-
def value(self) ->
|
|
11
|
+
def value(self) -> Any:
|
|
10
12
|
return self.children[0].value()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Headers component node."""
|
|
2
2
|
|
|
3
|
-
from typing import Dict
|
|
3
|
+
from typing import Any, Dict
|
|
4
4
|
|
|
5
5
|
from ..ast_node import ASTNode
|
|
6
6
|
|
|
@@ -8,5 +8,5 @@ from ..ast_node import ASTNode
|
|
|
8
8
|
class Headers(ASTNode):
|
|
9
9
|
"""Represents a HEADERS clause in LOAD operations."""
|
|
10
10
|
|
|
11
|
-
def value(self) -> Dict:
|
|
11
|
+
def value(self) -> Dict[str, Any]:
|
|
12
12
|
return self.first_child().value() or {}
|
|
@@ -7,22 +7,22 @@ from .ast_node import ASTNode
|
|
|
7
7
|
|
|
8
8
|
class Context:
|
|
9
9
|
"""Maintains a stack of AST nodes to track parsing context.
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
Used during parsing to maintain the current context and check for specific node types
|
|
12
12
|
in the parsing hierarchy, which helps with context-sensitive parsing decisions.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
Example:
|
|
15
15
|
context = Context()
|
|
16
16
|
context.push(node)
|
|
17
17
|
has_return = context.contains_type(Return)
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
def __init__(self):
|
|
20
|
+
def __init__(self) -> None:
|
|
21
21
|
self._nodes: List[ASTNode] = []
|
|
22
22
|
|
|
23
23
|
def push(self, node: ASTNode) -> None:
|
|
24
24
|
"""Pushes a node onto the context stack.
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
Args:
|
|
27
27
|
node: The AST node to push
|
|
28
28
|
"""
|
|
@@ -30,7 +30,7 @@ class Context:
|
|
|
30
30
|
|
|
31
31
|
def pop(self) -> Optional[ASTNode]:
|
|
32
32
|
"""Pops the top node from the context stack.
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
Returns:
|
|
35
35
|
The popped node, or None if the stack is empty
|
|
36
36
|
"""
|
|
@@ -40,10 +40,10 @@ class Context:
|
|
|
40
40
|
|
|
41
41
|
def contains_type(self, type_: Type[ASTNode]) -> bool:
|
|
42
42
|
"""Checks if the nodes stack contains a node of the specified type.
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
Args:
|
|
45
45
|
type_: The class of the node type to search for
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Returns:
|
|
48
48
|
True if a node of the specified type is found in the stack, False otherwise
|
|
49
49
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Represents an associative array (object/dictionary) in the AST."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict
|
|
3
|
+
from typing import Any, Dict, Generator
|
|
4
4
|
|
|
5
5
|
from ..ast_node import ASTNode
|
|
6
6
|
from .key_value_pair import KeyValuePair
|
|
@@ -8,9 +8,9 @@ from .key_value_pair import KeyValuePair
|
|
|
8
8
|
|
|
9
9
|
class AssociativeArray(ASTNode):
|
|
10
10
|
"""Represents an associative array (object/dictionary) in the AST.
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
Associative arrays map string keys to values, similar to JSON objects.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
Example:
|
|
15
15
|
# For { name: "Alice", age: 30 }
|
|
16
16
|
obj = AssociativeArray()
|
|
@@ -20,7 +20,7 @@ class AssociativeArray(ASTNode):
|
|
|
20
20
|
|
|
21
21
|
def add_key_value(self, key_value_pair: KeyValuePair) -> None:
|
|
22
22
|
"""Adds a key-value pair to the associative array.
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
Args:
|
|
25
25
|
key_value_pair: The key-value pair to add
|
|
26
26
|
"""
|
|
@@ -29,10 +29,10 @@ class AssociativeArray(ASTNode):
|
|
|
29
29
|
def __str__(self) -> str:
|
|
30
30
|
return 'AssociativeArray'
|
|
31
31
|
|
|
32
|
-
def _value(self):
|
|
32
|
+
def _value(self) -> Generator[Dict[str, Any], None, None]:
|
|
33
33
|
for child in self.children:
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
if isinstance(child, KeyValuePair):
|
|
35
|
+
yield {child.key: child._value}
|
|
36
36
|
|
|
37
37
|
def value(self) -> Dict[str, Any]:
|
|
38
38
|
result = {}
|
|
@@ -7,9 +7,9 @@ from ..ast_node import ASTNode
|
|
|
7
7
|
|
|
8
8
|
class JSONArray(ASTNode):
|
|
9
9
|
"""Represents a JSON array in the AST.
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
JSON arrays are ordered collections of values.
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
Example:
|
|
14
14
|
# For [1, 2, 3]
|
|
15
15
|
arr = JSONArray()
|
|
@@ -20,7 +20,7 @@ class JSONArray(ASTNode):
|
|
|
20
20
|
|
|
21
21
|
def add_value(self, value: ASTNode) -> None:
|
|
22
22
|
"""Adds a value to the array.
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
Args:
|
|
25
25
|
value: The AST node representing the value to add
|
|
26
26
|
"""
|
|
@@ -8,16 +8,16 @@ from ..expressions.string import String
|
|
|
8
8
|
|
|
9
9
|
class KeyValuePair(ASTNode):
|
|
10
10
|
"""Represents a key-value pair in an associative array.
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
Used to build object literals in FlowQuery.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
Example:
|
|
15
15
|
kvp = KeyValuePair("name", String("Alice"))
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
def __init__(self, key: str, value: ASTNode):
|
|
19
19
|
"""Creates a new key-value pair.
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
Args:
|
|
22
22
|
key: The key string
|
|
23
23
|
value: The AST node representing the value
|
|
@@ -27,7 +27,7 @@ class KeyValuePair(ASTNode):
|
|
|
27
27
|
self.add_child(value)
|
|
28
28
|
|
|
29
29
|
@property
|
|
30
|
-
def key(self) ->
|
|
30
|
+
def key(self) -> Any:
|
|
31
31
|
return self.children[0].value()
|
|
32
32
|
|
|
33
33
|
@property
|
|
@@ -7,9 +7,9 @@ from ..ast_node import ASTNode
|
|
|
7
7
|
|
|
8
8
|
class Lookup(ASTNode):
|
|
9
9
|
"""Represents a lookup operation (array/object indexing) in the AST.
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
Lookups access elements from arrays or properties from objects using an index or key.
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
Example:
|
|
14
14
|
# For array[0] or obj.property or obj["key"]
|
|
15
15
|
lookup = Lookup()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Represents a range lookup operation in the AST."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from ..ast_node import ASTNode
|
|
6
6
|
|
|
@@ -35,7 +35,7 @@ class RangeLookup(ASTNode):
|
|
|
35
35
|
def is_operand(self) -> bool:
|
|
36
36
|
return True
|
|
37
37
|
|
|
38
|
-
def value(self) ->
|
|
38
|
+
def value(self) -> Any:
|
|
39
39
|
array = self.variable.value()
|
|
40
40
|
from_val = self.from_.value() or 0
|
|
41
41
|
to_val = self.to.value() or len(array)
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
"""Expressions module for FlowQuery parsing."""
|
|
2
2
|
|
|
3
|
-
from .expression import Expression
|
|
4
3
|
from .boolean import Boolean
|
|
5
|
-
from .
|
|
6
|
-
from .string import String
|
|
7
|
-
from .identifier import Identifier
|
|
8
|
-
from .reference import Reference
|
|
9
|
-
from .f_string import FString
|
|
4
|
+
from .expression import Expression
|
|
10
5
|
from .expression_map import ExpressionMap
|
|
6
|
+
from .f_string import FString
|
|
7
|
+
from .identifier import Identifier
|
|
8
|
+
from .number import Number
|
|
11
9
|
from .operator import (
|
|
12
|
-
Operator,
|
|
13
10
|
Add,
|
|
14
|
-
|
|
15
|
-
Multiply,
|
|
11
|
+
And,
|
|
16
12
|
Divide,
|
|
17
|
-
Modulo,
|
|
18
|
-
Power,
|
|
19
13
|
Equals,
|
|
20
|
-
NotEquals,
|
|
21
14
|
GreaterThan,
|
|
22
|
-
LessThan,
|
|
23
15
|
GreaterThanOrEqual,
|
|
16
|
+
Is,
|
|
17
|
+
LessThan,
|
|
24
18
|
LessThanOrEqual,
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
Modulo,
|
|
20
|
+
Multiply,
|
|
27
21
|
Not,
|
|
28
|
-
|
|
22
|
+
NotEquals,
|
|
23
|
+
Operator,
|
|
24
|
+
Or,
|
|
25
|
+
Power,
|
|
26
|
+
Subtract,
|
|
29
27
|
)
|
|
28
|
+
from .reference import Reference
|
|
29
|
+
from .string import String
|
|
30
30
|
|
|
31
31
|
__all__ = [
|
|
32
32
|
"Expression",
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"""Represents an expression in the FlowQuery AST."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generator, List, Optional
|
|
4
6
|
|
|
5
7
|
from ..ast_node import ASTNode
|
|
8
|
+
from ..functions.aggregate_function import AggregateFunction
|
|
9
|
+
from .reference import Reference
|
|
6
10
|
|
|
7
11
|
if TYPE_CHECKING:
|
|
8
|
-
from ..functions.aggregate_function import AggregateFunction
|
|
9
12
|
from ...graph.pattern_expression import PatternExpression
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class Expression(ASTNode):
|
|
13
16
|
"""Represents an expression in the FlowQuery AST.
|
|
14
|
-
|
|
17
|
+
|
|
15
18
|
Expressions are built using the Shunting Yard algorithm to handle operator
|
|
16
19
|
precedence and associativity. They can contain operands (numbers, strings, identifiers)
|
|
17
20
|
and operators (arithmetic, logical, comparison).
|
|
18
|
-
|
|
21
|
+
|
|
19
22
|
Example:
|
|
20
23
|
expr = Expression()
|
|
21
24
|
expr.add_node(number_node)
|
|
@@ -24,7 +27,7 @@ class Expression(ASTNode):
|
|
|
24
27
|
expr.finish()
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
|
-
def __init__(self):
|
|
30
|
+
def __init__(self) -> None:
|
|
28
31
|
super().__init__()
|
|
29
32
|
self._operators: List[ASTNode] = []
|
|
30
33
|
self._output: List[ASTNode] = []
|
|
@@ -35,9 +38,9 @@ class Expression(ASTNode):
|
|
|
35
38
|
|
|
36
39
|
def add_node(self, node: ASTNode) -> None:
|
|
37
40
|
"""Adds a node (operand or operator) to the expression.
|
|
38
|
-
|
|
41
|
+
|
|
39
42
|
Uses the Shunting Yard algorithm to maintain correct operator precedence.
|
|
40
|
-
|
|
43
|
+
|
|
41
44
|
Args:
|
|
42
45
|
node: The AST node to add (operand or operator)
|
|
43
46
|
"""
|
|
@@ -58,7 +61,7 @@ class Expression(ASTNode):
|
|
|
58
61
|
|
|
59
62
|
def finish(self) -> None:
|
|
60
63
|
"""Finalizes the expression by converting it to a tree structure.
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
Should be called after all nodes have been added.
|
|
63
66
|
"""
|
|
64
67
|
while self._operators:
|
|
@@ -91,9 +94,9 @@ class Expression(ASTNode):
|
|
|
91
94
|
|
|
92
95
|
@property
|
|
93
96
|
def alias(self) -> Optional[str]:
|
|
94
|
-
|
|
95
|
-
if isinstance(
|
|
96
|
-
return
|
|
97
|
+
first = self.first_child()
|
|
98
|
+
if isinstance(first, Reference) and self._alias is None:
|
|
99
|
+
return first.identifier
|
|
97
100
|
return self._alias
|
|
98
101
|
|
|
99
102
|
@alias.setter
|
|
@@ -106,14 +109,14 @@ class Expression(ASTNode):
|
|
|
106
109
|
return "Expression"
|
|
107
110
|
|
|
108
111
|
def reducers(self) -> List['AggregateFunction']:
|
|
112
|
+
from ..functions.aggregate_function import AggregateFunction
|
|
109
113
|
if self._reducers is None:
|
|
110
|
-
from ..functions.aggregate_function import AggregateFunction
|
|
111
114
|
self._reducers = list(self._extract(self, AggregateFunction))
|
|
112
115
|
return self._reducers
|
|
113
116
|
|
|
114
117
|
def patterns(self) -> List['PatternExpression']:
|
|
118
|
+
from ...graph.pattern_expression import PatternExpression
|
|
115
119
|
if self._patterns is None:
|
|
116
|
-
from ...graph.pattern_expression import PatternExpression
|
|
117
120
|
self._patterns = list(self._extract(self, PatternExpression))
|
|
118
121
|
return self._patterns
|
|
119
122
|
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
"""Expression map for managing named expressions."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
from .expression import Expression
|
|
5
|
+
from typing import Any, List, Optional
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class ExpressionMap:
|
|
10
9
|
"""Maps expression aliases to their corresponding Expression objects."""
|
|
11
10
|
|
|
12
|
-
def __init__(self):
|
|
13
|
-
self._map: dict[str,
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
self._map: dict[str, Any] = {}
|
|
14
13
|
|
|
15
|
-
def get(self, alias: str) -> Optional[
|
|
14
|
+
def get(self, alias: str) -> Optional[Any]:
|
|
16
15
|
return self._map.get(alias)
|
|
17
16
|
|
|
18
17
|
def has(self, alias: str) -> bool:
|
|
19
18
|
return alias in self._map
|
|
20
19
|
|
|
21
|
-
def set_map(self, expressions: List[
|
|
20
|
+
def set_map(self, expressions: List[Any]) -> None:
|
|
22
21
|
self._map.clear()
|
|
23
22
|
for expr in expressions:
|
|
24
|
-
|
|
23
|
+
alias = getattr(expr, 'alias', None)
|
|
24
|
+
if alias is None:
|
|
25
25
|
continue
|
|
26
|
-
self._map[
|
|
26
|
+
self._map[alias] = expr
|
|
@@ -5,15 +5,15 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from ..ast_node import ASTNode
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
|
|
8
|
+
pass
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class FString(ASTNode):
|
|
12
12
|
"""Represents a formatted string (f-string) in the AST.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
F-strings allow embedding expressions within string literals.
|
|
15
15
|
Child nodes represent the parts of the f-string (literal strings and expressions).
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
Example:
|
|
18
18
|
# For f"Hello {name}!"
|
|
19
19
|
fstr = FString()
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""Represents an identifier in the AST."""
|
|
2
2
|
|
|
3
|
-
from .string import String
|
|
4
3
|
from typing import Any
|
|
5
4
|
|
|
5
|
+
from .string import String
|
|
6
|
+
|
|
6
7
|
|
|
7
8
|
class Identifier(String):
|
|
8
9
|
"""Represents an identifier in the AST.
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
Identifiers are used for variable names, property names, and similar constructs.
|
|
11
|
-
|
|
12
|
+
|
|
12
13
|
Example:
|
|
13
14
|
id = Identifier("myVariable")
|
|
14
15
|
"""
|
|
@@ -5,9 +5,9 @@ from ..ast_node import ASTNode
|
|
|
5
5
|
|
|
6
6
|
class Number(ASTNode):
|
|
7
7
|
"""Represents a numeric literal in the AST.
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
Parses string representations of numbers into integer or float values.
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
Example:
|
|
12
12
|
num = Number("42")
|
|
13
13
|
print(num.value()) # 42
|
|
@@ -15,7 +15,7 @@ class Number(ASTNode):
|
|
|
15
15
|
|
|
16
16
|
def __init__(self, value: str):
|
|
17
17
|
"""Creates a new Number node by parsing the string value.
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
Args:
|
|
20
20
|
value: The string representation of the number
|
|
21
21
|
"""
|