flowquery 1.0.16 → 1.0.17
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/.github/workflows/python-publish.yml +97 -0
- package/dist/compute/runner.d.ts +3 -2
- package/dist/compute/runner.d.ts.map +1 -1
- package/dist/compute/runner.js +7 -7
- package/dist/compute/runner.js.map +1 -1
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/data.d.ts +31 -0
- package/dist/graph/data.d.ts.map +1 -0
- package/dist/graph/data.js +110 -0
- package/dist/graph/data.js.map +1 -0
- package/dist/graph/database.d.ts +20 -0
- package/dist/graph/database.d.ts.map +1 -0
- package/dist/graph/database.js +77 -0
- package/dist/graph/database.js.map +1 -0
- package/dist/graph/hops.d.ts +11 -0
- package/dist/graph/hops.d.ts.map +1 -0
- package/dist/graph/hops.js +25 -0
- package/dist/graph/hops.js.map +1 -0
- package/dist/graph/node.d.ts +35 -0
- package/dist/graph/node.d.ts.map +1 -0
- package/dist/graph/node.js +113 -0
- package/dist/graph/node.js.map +1 -0
- package/dist/graph/node_data.d.ts +11 -0
- package/dist/graph/node_data.d.ts.map +1 -0
- package/dist/graph/node_data.js +20 -0
- package/dist/graph/node_data.js.map +1 -0
- package/dist/graph/node_reference.d.ts +10 -0
- package/dist/graph/node_reference.d.ts.map +1 -0
- package/dist/graph/node_reference.js +52 -0
- package/dist/graph/node_reference.js.map +1 -0
- package/dist/graph/pattern.d.ts +18 -0
- package/dist/graph/pattern.d.ts.map +1 -0
- package/dist/graph/pattern.js +114 -0
- package/dist/graph/pattern.js.map +1 -0
- package/dist/graph/pattern_expression.d.ts +14 -0
- package/dist/graph/pattern_expression.d.ts.map +1 -0
- package/dist/graph/pattern_expression.js +58 -0
- package/dist/graph/pattern_expression.js.map +1 -0
- package/dist/graph/patterns.d.ts +11 -0
- package/dist/graph/patterns.d.ts.map +1 -0
- package/dist/graph/patterns.js +49 -0
- package/dist/graph/patterns.js.map +1 -0
- package/dist/graph/physical_node.d.ts +10 -0
- package/dist/graph/physical_node.d.ts.map +1 -0
- package/dist/graph/physical_node.js +40 -0
- package/dist/graph/physical_node.js.map +1 -0
- package/dist/graph/physical_relationship.d.ts +10 -0
- package/dist/graph/physical_relationship.d.ts.map +1 -0
- package/dist/graph/physical_relationship.js +40 -0
- package/dist/graph/physical_relationship.js.map +1 -0
- package/dist/graph/relationship.d.ts +40 -0
- package/dist/graph/relationship.d.ts.map +1 -0
- package/dist/graph/relationship.js +124 -0
- package/dist/graph/relationship.js.map +1 -0
- package/dist/graph/relationship_data.d.ts +12 -0
- package/dist/graph/relationship_data.d.ts.map +1 -0
- package/dist/graph/relationship_data.js +40 -0
- package/dist/graph/relationship_data.js.map +1 -0
- package/dist/graph/relationship_match_collector.d.ts +19 -0
- package/dist/graph/relationship_match_collector.d.ts.map +1 -0
- package/dist/graph/relationship_match_collector.js +55 -0
- package/dist/graph/relationship_match_collector.js.map +1 -0
- package/dist/graph/relationship_reference.d.ts +8 -0
- package/dist/graph/relationship_reference.d.ts.map +1 -0
- package/dist/graph/relationship_reference.js +37 -0
- package/dist/graph/relationship_reference.js.map +1 -0
- package/dist/parsing/base_parser.d.ts +1 -0
- package/dist/parsing/base_parser.d.ts.map +1 -1
- package/dist/parsing/base_parser.js +4 -1
- package/dist/parsing/base_parser.js.map +1 -1
- package/dist/parsing/context.d.ts +2 -2
- package/dist/parsing/context.js +5 -5
- package/dist/parsing/expressions/boolean.d.ts +8 -0
- package/dist/parsing/expressions/boolean.d.ts.map +1 -0
- package/dist/parsing/expressions/boolean.js +26 -0
- package/dist/parsing/expressions/boolean.js.map +1 -0
- package/dist/parsing/expressions/expression.d.ts +4 -1
- package/dist/parsing/expressions/expression.d.ts.map +1 -1
- package/dist/parsing/expressions/expression.js +15 -8
- package/dist/parsing/expressions/expression.js.map +1 -1
- package/dist/parsing/expressions/operator.d.ts +1 -1
- package/dist/parsing/expressions/operator.d.ts.map +1 -1
- package/dist/parsing/expressions/operator.js.map +1 -1
- package/dist/parsing/functions/function_factory.d.ts +13 -13
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +20 -18
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/operations/create_node.d.ts +14 -0
- package/dist/parsing/operations/create_node.d.ts.map +1 -0
- package/dist/parsing/operations/create_node.js +51 -0
- package/dist/parsing/operations/create_node.js.map +1 -0
- package/dist/parsing/operations/create_relationship.d.ts +14 -0
- package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
- package/dist/parsing/operations/create_relationship.js +51 -0
- package/dist/parsing/operations/create_relationship.js.map +1 -0
- package/dist/parsing/operations/match.d.ts +15 -0
- package/dist/parsing/operations/match.d.ts.map +1 -0
- package/dist/parsing/operations/match.js +45 -0
- package/dist/parsing/operations/match.js.map +1 -0
- package/dist/parsing/operations/operation.d.ts +1 -0
- package/dist/parsing/operations/operation.d.ts.map +1 -1
- package/dist/parsing/operations/operation.js +6 -0
- package/dist/parsing/operations/operation.js.map +1 -1
- package/dist/parsing/operations/return.d.ts +1 -0
- package/dist/parsing/operations/return.d.ts.map +1 -1
- package/dist/parsing/operations/return.js +7 -1
- package/dist/parsing/operations/return.js.map +1 -1
- package/dist/parsing/operations/where.d.ts +1 -1
- package/dist/parsing/operations/where.d.ts.map +1 -1
- package/dist/parsing/operations/where.js +4 -0
- package/dist/parsing/operations/where.js.map +1 -1
- package/dist/parsing/parser.d.ts +10 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +344 -5
- package/dist/parsing/parser.js.map +1 -1
- package/dist/parsing/token_to_node.d.ts.map +1 -1
- package/dist/parsing/token_to_node.js +7 -0
- package/dist/parsing/token_to_node.js.map +1 -1
- package/dist/tokenization/keyword.d.ts +1 -0
- package/dist/tokenization/keyword.d.ts.map +1 -1
- package/dist/tokenization/keyword.js +1 -0
- package/dist/tokenization/keyword.js.map +1 -1
- package/dist/tokenization/token.d.ts +4 -0
- package/dist/tokenization/token.d.ts.map +1 -1
- package/dist/tokenization/token.js +14 -1
- package/dist/tokenization/token.js.map +1 -1
- package/dist/tokenization/token_type.d.ts +1 -0
- package/dist/tokenization/token_type.d.ts.map +1 -1
- package/dist/tokenization/token_type.js +1 -0
- package/dist/tokenization/token_type.js.map +1 -1
- package/dist/tokenization/tokenizer.d.ts +2 -1
- package/dist/tokenization/tokenizer.d.ts.map +1 -1
- package/dist/tokenization/tokenizer.js +25 -12
- package/dist/tokenization/tokenizer.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/README.md +166 -0
- package/flowquery-py/pyproject.toml +75 -0
- package/flowquery-py/setup_env.ps1 +92 -0
- package/flowquery-py/setup_env.sh +87 -0
- package/flowquery-py/src/__init__.py +34 -0
- package/flowquery-py/src/__main__.py +10 -0
- package/flowquery-py/src/compute/__init__.py +5 -0
- package/flowquery-py/src/compute/runner.py +60 -0
- package/flowquery-py/src/extensibility.py +52 -0
- package/flowquery-py/src/graph/__init__.py +31 -0
- package/flowquery-py/src/graph/data.py +118 -0
- package/flowquery-py/src/graph/database.py +82 -0
- package/flowquery-py/src/graph/hops.py +43 -0
- package/flowquery-py/src/graph/node.py +112 -0
- package/flowquery-py/src/graph/node_data.py +26 -0
- package/flowquery-py/src/graph/node_reference.py +49 -0
- package/flowquery-py/src/graph/pattern.py +125 -0
- package/flowquery-py/src/graph/pattern_expression.py +62 -0
- package/flowquery-py/src/graph/patterns.py +42 -0
- package/flowquery-py/src/graph/physical_node.py +40 -0
- package/flowquery-py/src/graph/physical_relationship.py +36 -0
- package/flowquery-py/src/graph/relationship.py +135 -0
- package/flowquery-py/src/graph/relationship_data.py +33 -0
- package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
- package/flowquery-py/src/graph/relationship_reference.py +21 -0
- package/flowquery-py/src/io/__init__.py +5 -0
- package/flowquery-py/src/io/command_line.py +67 -0
- package/flowquery-py/src/parsing/__init__.py +17 -0
- package/flowquery-py/src/parsing/alias.py +20 -0
- package/flowquery-py/src/parsing/alias_option.py +11 -0
- package/flowquery-py/src/parsing/ast_node.py +146 -0
- package/flowquery-py/src/parsing/base_parser.py +84 -0
- package/flowquery-py/src/parsing/components/__init__.py +19 -0
- package/flowquery-py/src/parsing/components/csv.py +8 -0
- package/flowquery-py/src/parsing/components/from_.py +10 -0
- package/flowquery-py/src/parsing/components/headers.py +12 -0
- package/flowquery-py/src/parsing/components/json.py +8 -0
- package/flowquery-py/src/parsing/components/null.py +10 -0
- package/flowquery-py/src/parsing/components/post.py +8 -0
- package/flowquery-py/src/parsing/components/text.py +8 -0
- package/flowquery-py/src/parsing/context.py +50 -0
- package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
- package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
- package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
- package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
- package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
- package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
- package/flowquery-py/src/parsing/expressions/expression.py +138 -0
- package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
- package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
- package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
- package/flowquery-py/src/parsing/expressions/number.py +32 -0
- package/flowquery-py/src/parsing/expressions/operator.py +169 -0
- package/flowquery-py/src/parsing/expressions/reference.py +47 -0
- package/flowquery-py/src/parsing/expressions/string.py +27 -0
- package/flowquery-py/src/parsing/functions/__init__.py +75 -0
- package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
- package/flowquery-py/src/parsing/functions/async_function.py +62 -0
- package/flowquery-py/src/parsing/functions/avg.py +55 -0
- package/flowquery-py/src/parsing/functions/collect.py +75 -0
- package/flowquery-py/src/parsing/functions/function.py +68 -0
- package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
- package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
- package/flowquery-py/src/parsing/functions/functions.py +59 -0
- package/flowquery-py/src/parsing/functions/join.py +47 -0
- package/flowquery-py/src/parsing/functions/keys.py +34 -0
- package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
- package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
- package/flowquery-py/src/parsing/functions/rand.py +28 -0
- package/flowquery-py/src/parsing/functions/range_.py +34 -0
- package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
- package/flowquery-py/src/parsing/functions/replace.py +37 -0
- package/flowquery-py/src/parsing/functions/round_.py +32 -0
- package/flowquery-py/src/parsing/functions/size.py +32 -0
- package/flowquery-py/src/parsing/functions/split.py +47 -0
- package/flowquery-py/src/parsing/functions/stringify.py +47 -0
- package/flowquery-py/src/parsing/functions/sum.py +51 -0
- package/flowquery-py/src/parsing/functions/to_json.py +33 -0
- package/flowquery-py/src/parsing/functions/type_.py +47 -0
- package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
- package/flowquery-py/src/parsing/logic/__init__.py +15 -0
- package/flowquery-py/src/parsing/logic/case.py +29 -0
- package/flowquery-py/src/parsing/logic/else_.py +12 -0
- package/flowquery-py/src/parsing/logic/end.py +8 -0
- package/flowquery-py/src/parsing/logic/then.py +12 -0
- package/flowquery-py/src/parsing/logic/when.py +10 -0
- package/flowquery-py/src/parsing/operations/__init__.py +35 -0
- package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
- package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
- package/flowquery-py/src/parsing/operations/call.py +74 -0
- package/flowquery-py/src/parsing/operations/create_node.py +34 -0
- package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
- package/flowquery-py/src/parsing/operations/group_by.py +130 -0
- package/flowquery-py/src/parsing/operations/limit.py +22 -0
- package/flowquery-py/src/parsing/operations/load.py +140 -0
- package/flowquery-py/src/parsing/operations/match.py +29 -0
- package/flowquery-py/src/parsing/operations/operation.py +69 -0
- package/flowquery-py/src/parsing/operations/projection.py +21 -0
- package/flowquery-py/src/parsing/operations/return_op.py +50 -0
- package/flowquery-py/src/parsing/operations/unwind.py +37 -0
- package/flowquery-py/src/parsing/operations/where.py +41 -0
- package/flowquery-py/src/parsing/operations/with_op.py +18 -0
- package/flowquery-py/src/parsing/parser.py +1011 -0
- package/flowquery-py/src/parsing/token_to_node.py +109 -0
- package/flowquery-py/src/tokenization/__init__.py +23 -0
- package/flowquery-py/src/tokenization/keyword.py +48 -0
- package/flowquery-py/src/tokenization/operator.py +29 -0
- package/flowquery-py/src/tokenization/string_walker.py +158 -0
- package/flowquery-py/src/tokenization/symbol.py +19 -0
- package/flowquery-py/src/tokenization/token.py +659 -0
- package/flowquery-py/src/tokenization/token_mapper.py +52 -0
- package/flowquery-py/src/tokenization/token_type.py +21 -0
- package/flowquery-py/src/tokenization/tokenizer.py +214 -0
- package/flowquery-py/src/tokenization/trie.py +124 -0
- package/flowquery-py/src/utils/__init__.py +6 -0
- package/flowquery-py/src/utils/object_utils.py +20 -0
- package/flowquery-py/src/utils/string_utils.py +113 -0
- package/flowquery-py/tests/__init__.py +1 -0
- package/flowquery-py/tests/compute/__init__.py +1 -0
- package/flowquery-py/tests/compute/test_runner.py +1335 -0
- package/flowquery-py/tests/graph/__init__.py +1 -0
- package/flowquery-py/tests/graph/test_create.py +56 -0
- package/flowquery-py/tests/graph/test_data.py +73 -0
- package/flowquery-py/tests/graph/test_match.py +40 -0
- package/flowquery-py/tests/parsing/__init__.py +1 -0
- package/flowquery-py/tests/parsing/test_context.py +34 -0
- package/flowquery-py/tests/parsing/test_expression.py +49 -0
- package/flowquery-py/tests/parsing/test_parser.py +674 -0
- package/flowquery-py/tests/test_extensibility.py +611 -0
- package/flowquery-py/tests/tokenization/__init__.py +1 -0
- package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
- package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
- package/flowquery-py/tests/tokenization/test_trie.py +30 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/package.json +1 -1
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
- package/misc/apps/RAG/src/components/index.ts +19 -10
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +70 -140
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +12 -0
- package/package.json +1 -1
- package/src/compute/runner.ts +24 -19
- package/src/graph/data.ts +112 -0
- package/src/graph/database.ts +63 -0
- package/src/graph/hops.ts +22 -0
- package/src/graph/node.ts +99 -0
- package/src/graph/node_data.ts +18 -0
- package/src/graph/node_reference.ts +33 -0
- package/src/graph/pattern.ts +101 -0
- package/src/graph/pattern_expression.ts +37 -0
- package/src/graph/patterns.ts +36 -0
- package/src/graph/physical_node.ts +23 -0
- package/src/graph/physical_relationship.ts +23 -0
- package/src/graph/relationship.ts +116 -0
- package/src/graph/relationship_data.ts +27 -0
- package/src/graph/relationship_match_collector.ts +58 -0
- package/src/graph/relationship_reference.ts +24 -0
- package/src/parsing/base_parser.ts +20 -14
- package/src/parsing/context.ts +14 -14
- package/src/parsing/expressions/boolean.ts +21 -0
- package/src/parsing/expressions/expression.ts +34 -26
- package/src/parsing/expressions/operator.ts +19 -1
- package/src/parsing/functions/function_factory.ts +45 -45
- package/src/parsing/operations/create_node.ts +39 -0
- package/src/parsing/operations/create_relationship.ts +38 -0
- package/src/parsing/operations/match.ts +31 -0
- package/src/parsing/operations/operation.ts +3 -0
- package/src/parsing/operations/return.ts +11 -7
- package/src/parsing/operations/where.ts +10 -6
- package/src/parsing/parser.ts +346 -8
- package/src/parsing/token_to_node.ts +6 -0
- package/src/tokenization/keyword.ts +41 -40
- package/src/tokenization/token.ts +21 -1
- package/src/tokenization/token_type.ts +2 -1
- package/src/tokenization/tokenizer.ts +52 -31
- package/tests/compute/runner.test.ts +654 -0
- package/tests/extensibility.test.ts +97 -93
- package/tests/graph/create.test.ts +36 -0
- package/tests/graph/data.test.ts +58 -0
- package/tests/graph/match.test.ts +29 -0
- package/tests/parsing/parser.test.ts +273 -2
- package/tests/tokenization/tokenizer.test.ts +90 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Base class for parsers providing common token manipulation functionality."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from ..tokenization.token import Token
|
|
6
|
+
from ..tokenization.tokenizer import Tokenizer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseParser:
|
|
10
|
+
"""Base class for parsers providing common token manipulation functionality.
|
|
11
|
+
|
|
12
|
+
This class handles tokenization and provides utility methods for navigating
|
|
13
|
+
through tokens, peeking ahead, and checking token sequences.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, tokens: Optional[List[Token]] = None):
|
|
17
|
+
self._tokens: List[Token] = tokens or []
|
|
18
|
+
self._token_index: int = 0
|
|
19
|
+
|
|
20
|
+
def tokenize(self, statement: str) -> None:
|
|
21
|
+
"""Tokenizes a statement and initializes the token array.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
statement: The input statement to tokenize
|
|
25
|
+
"""
|
|
26
|
+
self._tokens = Tokenizer(statement).tokenize()
|
|
27
|
+
self._token_index = 0
|
|
28
|
+
|
|
29
|
+
def set_next_token(self) -> None:
|
|
30
|
+
"""Advances to the next token in the sequence."""
|
|
31
|
+
self._token_index += 1
|
|
32
|
+
|
|
33
|
+
def peek(self) -> Optional[Token]:
|
|
34
|
+
"""Peeks at the next token without advancing the current position.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The next token, or None if at the end of the token stream
|
|
38
|
+
"""
|
|
39
|
+
if self._token_index + 1 >= len(self._tokens):
|
|
40
|
+
return None
|
|
41
|
+
return self._tokens[self._token_index + 1]
|
|
42
|
+
|
|
43
|
+
def ahead(self, tokens: List[Token], skip_whitespace_and_comments: bool = True) -> bool:
|
|
44
|
+
"""Checks if a sequence of tokens appears ahead in the token stream.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
tokens: The sequence of tokens to look for
|
|
48
|
+
skip_whitespace_and_comments: Whether to skip whitespace and comments when matching
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if the token sequence is found ahead, False otherwise
|
|
52
|
+
"""
|
|
53
|
+
j = 0
|
|
54
|
+
for i in range(self._token_index, len(self._tokens)):
|
|
55
|
+
if skip_whitespace_and_comments and self._tokens[i].is_whitespace_or_comment():
|
|
56
|
+
continue
|
|
57
|
+
if not self._tokens[i].equals(tokens[j]):
|
|
58
|
+
return False
|
|
59
|
+
j += 1
|
|
60
|
+
if j == len(tokens):
|
|
61
|
+
break
|
|
62
|
+
return j == len(tokens)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def token(self) -> Token:
|
|
66
|
+
"""Gets the current token.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The current token, or EOF if at the end
|
|
70
|
+
"""
|
|
71
|
+
if self._token_index >= len(self._tokens):
|
|
72
|
+
return Token.EOF
|
|
73
|
+
return self._tokens[self._token_index]
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def previous_token(self) -> Token:
|
|
77
|
+
"""Gets the previous token.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
The previous token, or EOF if at the beginning
|
|
81
|
+
"""
|
|
82
|
+
if self._token_index - 1 < 0:
|
|
83
|
+
return Token.EOF
|
|
84
|
+
return self._tokens[self._token_index - 1]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Components module for FlowQuery parsing."""
|
|
2
|
+
|
|
3
|
+
from .csv import CSV
|
|
4
|
+
from .json import JSON
|
|
5
|
+
from .text import Text
|
|
6
|
+
from .from_ import From
|
|
7
|
+
from .headers import Headers
|
|
8
|
+
from .post import Post
|
|
9
|
+
from .null import Null
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"CSV",
|
|
13
|
+
"JSON",
|
|
14
|
+
"Text",
|
|
15
|
+
"From",
|
|
16
|
+
"Headers",
|
|
17
|
+
"Post",
|
|
18
|
+
"Null",
|
|
19
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Maintains a stack of AST nodes to track parsing context."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional, Type
|
|
4
|
+
|
|
5
|
+
from .ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Context:
|
|
9
|
+
"""Maintains a stack of AST nodes to track parsing context.
|
|
10
|
+
|
|
11
|
+
Used during parsing to maintain the current context and check for specific node types
|
|
12
|
+
in the parsing hierarchy, which helps with context-sensitive parsing decisions.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
context = Context()
|
|
16
|
+
context.push(node)
|
|
17
|
+
has_return = context.contains_type(Return)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self._nodes: List[ASTNode] = []
|
|
22
|
+
|
|
23
|
+
def push(self, node: ASTNode) -> None:
|
|
24
|
+
"""Pushes a node onto the context stack.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
node: The AST node to push
|
|
28
|
+
"""
|
|
29
|
+
self._nodes.append(node)
|
|
30
|
+
|
|
31
|
+
def pop(self) -> Optional[ASTNode]:
|
|
32
|
+
"""Pops the top node from the context stack.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The popped node, or None if the stack is empty
|
|
36
|
+
"""
|
|
37
|
+
if len(self._nodes) == 0:
|
|
38
|
+
return None
|
|
39
|
+
return self._nodes.pop()
|
|
40
|
+
|
|
41
|
+
def contains_type(self, type_: Type[ASTNode]) -> bool:
|
|
42
|
+
"""Checks if the nodes stack contains a node of the specified type.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
type_: The class of the node type to search for
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
True if a node of the specified type is found in the stack, False otherwise
|
|
49
|
+
"""
|
|
50
|
+
return any(isinstance(v, type_) for v in self._nodes)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Data structures module for FlowQuery parsing."""
|
|
2
|
+
|
|
3
|
+
from .associative_array import AssociativeArray
|
|
4
|
+
from .json_array import JSONArray
|
|
5
|
+
from .key_value_pair import KeyValuePair
|
|
6
|
+
from .lookup import Lookup
|
|
7
|
+
from .range_lookup import RangeLookup
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AssociativeArray",
|
|
11
|
+
"JSONArray",
|
|
12
|
+
"KeyValuePair",
|
|
13
|
+
"Lookup",
|
|
14
|
+
"RangeLookup",
|
|
15
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Represents an associative array (object/dictionary) in the AST."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
from .key_value_pair import KeyValuePair
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AssociativeArray(ASTNode):
|
|
10
|
+
"""Represents an associative array (object/dictionary) in the AST.
|
|
11
|
+
|
|
12
|
+
Associative arrays map string keys to values, similar to JSON objects.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
# For { name: "Alice", age: 30 }
|
|
16
|
+
obj = AssociativeArray()
|
|
17
|
+
obj.add_key_value(KeyValuePair("name", name_expr))
|
|
18
|
+
obj.add_key_value(KeyValuePair("age", age_expr))
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def add_key_value(self, key_value_pair: KeyValuePair) -> None:
|
|
22
|
+
"""Adds a key-value pair to the associative array.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
key_value_pair: The key-value pair to add
|
|
26
|
+
"""
|
|
27
|
+
self.add_child(key_value_pair)
|
|
28
|
+
|
|
29
|
+
def __str__(self) -> str:
|
|
30
|
+
return 'AssociativeArray'
|
|
31
|
+
|
|
32
|
+
def _value(self):
|
|
33
|
+
for child in self.children:
|
|
34
|
+
key_value = child
|
|
35
|
+
yield {key_value.key: key_value._value}
|
|
36
|
+
|
|
37
|
+
def value(self) -> Dict[str, Any]:
|
|
38
|
+
result = {}
|
|
39
|
+
for item in self._value():
|
|
40
|
+
result.update(item)
|
|
41
|
+
return result
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Represents a JSON array in the AST."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JSONArray(ASTNode):
|
|
9
|
+
"""Represents a JSON array in the AST.
|
|
10
|
+
|
|
11
|
+
JSON arrays are ordered collections of values.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
# For [1, 2, 3]
|
|
15
|
+
arr = JSONArray()
|
|
16
|
+
arr.add_value(Number("1"))
|
|
17
|
+
arr.add_value(Number("2"))
|
|
18
|
+
arr.add_value(Number("3"))
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def add_value(self, value: ASTNode) -> None:
|
|
22
|
+
"""Adds a value to the array.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
value: The AST node representing the value to add
|
|
26
|
+
"""
|
|
27
|
+
self.add_child(value)
|
|
28
|
+
|
|
29
|
+
def value(self) -> List[Any]:
|
|
30
|
+
return [child.value() for child in self.children]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Represents a key-value pair in an associative array."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
from ..expressions.string import String
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class KeyValuePair(ASTNode):
|
|
10
|
+
"""Represents a key-value pair in an associative array.
|
|
11
|
+
|
|
12
|
+
Used to build object literals in FlowQuery.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
kvp = KeyValuePair("name", String("Alice"))
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, key: str, value: ASTNode):
|
|
19
|
+
"""Creates a new key-value pair.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
key: The key string
|
|
23
|
+
value: The AST node representing the value
|
|
24
|
+
"""
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.add_child(String(key))
|
|
27
|
+
self.add_child(value)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def key(self) -> str:
|
|
31
|
+
return self.children[0].value()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def _value(self) -> Any:
|
|
35
|
+
return self.children[1].value()
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
return "KeyValuePair"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Represents a lookup operation (array/object indexing) in the AST."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Lookup(ASTNode):
|
|
9
|
+
"""Represents a lookup operation (array/object indexing) in the AST.
|
|
10
|
+
|
|
11
|
+
Lookups access elements from arrays or properties from objects using an index or key.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
# For array[0] or obj.property or obj["key"]
|
|
15
|
+
lookup = Lookup()
|
|
16
|
+
lookup.variable = array_or_obj_node
|
|
17
|
+
lookup.index = index_node
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def index(self) -> ASTNode:
|
|
22
|
+
return self.children[0]
|
|
23
|
+
|
|
24
|
+
@index.setter
|
|
25
|
+
def index(self, index: ASTNode) -> None:
|
|
26
|
+
self.add_child(index)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def variable(self) -> ASTNode:
|
|
30
|
+
return self.children[1]
|
|
31
|
+
|
|
32
|
+
@variable.setter
|
|
33
|
+
def variable(self, variable: ASTNode) -> None:
|
|
34
|
+
self.add_child(variable)
|
|
35
|
+
|
|
36
|
+
def is_operand(self) -> bool:
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
def value(self) -> Any:
|
|
40
|
+
obj = self.variable.value()
|
|
41
|
+
key = self.index.value()
|
|
42
|
+
# Try dict-like access first, then fall back to attribute access for objects
|
|
43
|
+
try:
|
|
44
|
+
return obj[key]
|
|
45
|
+
except (TypeError, KeyError):
|
|
46
|
+
# For objects with attributes (like dataclasses), use getattr
|
|
47
|
+
if hasattr(obj, key):
|
|
48
|
+
return getattr(obj, key)
|
|
49
|
+
raise
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Represents a range lookup operation in the AST."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RangeLookup(ASTNode):
|
|
9
|
+
"""Represents a range lookup (array slicing) operation in the AST."""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def from_(self) -> ASTNode:
|
|
13
|
+
return self.children[0]
|
|
14
|
+
|
|
15
|
+
@from_.setter
|
|
16
|
+
def from_(self, from_: ASTNode) -> None:
|
|
17
|
+
self.add_child(from_)
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def to(self) -> ASTNode:
|
|
21
|
+
return self.children[1]
|
|
22
|
+
|
|
23
|
+
@to.setter
|
|
24
|
+
def to(self, to: ASTNode) -> None:
|
|
25
|
+
self.add_child(to)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def variable(self) -> ASTNode:
|
|
29
|
+
return self.children[2]
|
|
30
|
+
|
|
31
|
+
@variable.setter
|
|
32
|
+
def variable(self, variable: ASTNode) -> None:
|
|
33
|
+
self.add_child(variable)
|
|
34
|
+
|
|
35
|
+
def is_operand(self) -> bool:
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
def value(self) -> List[Any]:
|
|
39
|
+
array = self.variable.value()
|
|
40
|
+
from_val = self.from_.value() or 0
|
|
41
|
+
to_val = self.to.value() or len(array)
|
|
42
|
+
return array[from_val:to_val]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Expressions module for FlowQuery parsing."""
|
|
2
|
+
|
|
3
|
+
from .expression import Expression
|
|
4
|
+
from .boolean import Boolean
|
|
5
|
+
from .number import Number
|
|
6
|
+
from .string import String
|
|
7
|
+
from .identifier import Identifier
|
|
8
|
+
from .reference import Reference
|
|
9
|
+
from .f_string import FString
|
|
10
|
+
from .expression_map import ExpressionMap
|
|
11
|
+
from .operator import (
|
|
12
|
+
Operator,
|
|
13
|
+
Add,
|
|
14
|
+
Subtract,
|
|
15
|
+
Multiply,
|
|
16
|
+
Divide,
|
|
17
|
+
Modulo,
|
|
18
|
+
Power,
|
|
19
|
+
Equals,
|
|
20
|
+
NotEquals,
|
|
21
|
+
GreaterThan,
|
|
22
|
+
LessThan,
|
|
23
|
+
GreaterThanOrEqual,
|
|
24
|
+
LessThanOrEqual,
|
|
25
|
+
And,
|
|
26
|
+
Or,
|
|
27
|
+
Not,
|
|
28
|
+
Is,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Expression",
|
|
33
|
+
"Boolean",
|
|
34
|
+
"Number",
|
|
35
|
+
"String",
|
|
36
|
+
"Identifier",
|
|
37
|
+
"Reference",
|
|
38
|
+
"FString",
|
|
39
|
+
"ExpressionMap",
|
|
40
|
+
"Operator",
|
|
41
|
+
"Add",
|
|
42
|
+
"Subtract",
|
|
43
|
+
"Multiply",
|
|
44
|
+
"Divide",
|
|
45
|
+
"Modulo",
|
|
46
|
+
"Power",
|
|
47
|
+
"Equals",
|
|
48
|
+
"NotEquals",
|
|
49
|
+
"GreaterThan",
|
|
50
|
+
"LessThan",
|
|
51
|
+
"GreaterThanOrEqual",
|
|
52
|
+
"LessThanOrEqual",
|
|
53
|
+
"And",
|
|
54
|
+
"Or",
|
|
55
|
+
"Not",
|
|
56
|
+
"Is",
|
|
57
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Represents a boolean literal in the AST."""
|
|
2
|
+
|
|
3
|
+
from ..ast_node import ASTNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Boolean(ASTNode):
|
|
7
|
+
"""Represents a boolean literal in the AST."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, value: str):
|
|
10
|
+
super().__init__()
|
|
11
|
+
_value = value.upper()
|
|
12
|
+
if _value == "TRUE":
|
|
13
|
+
self._value = True
|
|
14
|
+
elif _value == "FALSE":
|
|
15
|
+
self._value = False
|
|
16
|
+
else:
|
|
17
|
+
raise ValueError(f"Invalid boolean value: {value}")
|
|
18
|
+
|
|
19
|
+
def value(self) -> bool:
|
|
20
|
+
return self._value
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Represents an expression in the FlowQuery AST."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List, Optional, Generator, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..functions.aggregate_function import AggregateFunction
|
|
9
|
+
from ...graph.pattern_expression import PatternExpression
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Expression(ASTNode):
|
|
13
|
+
"""Represents an expression in the FlowQuery AST.
|
|
14
|
+
|
|
15
|
+
Expressions are built using the Shunting Yard algorithm to handle operator
|
|
16
|
+
precedence and associativity. They can contain operands (numbers, strings, identifiers)
|
|
17
|
+
and operators (arithmetic, logical, comparison).
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
expr = Expression()
|
|
21
|
+
expr.add_node(number_node)
|
|
22
|
+
expr.add_node(plus_operator)
|
|
23
|
+
expr.add_node(another_number_node)
|
|
24
|
+
expr.finish()
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self._operators: List[ASTNode] = []
|
|
30
|
+
self._output: List[ASTNode] = []
|
|
31
|
+
self._alias: Optional[str] = None
|
|
32
|
+
self._overridden: Any = None
|
|
33
|
+
self._reducers: Optional[List['AggregateFunction']] = None
|
|
34
|
+
self._patterns: Optional[List['PatternExpression']] = None
|
|
35
|
+
|
|
36
|
+
def add_node(self, node: ASTNode) -> None:
|
|
37
|
+
"""Adds a node (operand or operator) to the expression.
|
|
38
|
+
|
|
39
|
+
Uses the Shunting Yard algorithm to maintain correct operator precedence.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
node: The AST node to add (operand or operator)
|
|
43
|
+
"""
|
|
44
|
+
# Implements the Shunting Yard algorithm
|
|
45
|
+
if node.is_operand():
|
|
46
|
+
self._output.append(node)
|
|
47
|
+
elif node.is_operator():
|
|
48
|
+
operator1 = node
|
|
49
|
+
while len(self._operators) > 0:
|
|
50
|
+
operator2 = self._operators[-1]
|
|
51
|
+
if (operator2.precedence > operator1.precedence or
|
|
52
|
+
(operator2.precedence == operator1.precedence and operator1.left_associative)):
|
|
53
|
+
self._output.append(operator2)
|
|
54
|
+
self._operators.pop()
|
|
55
|
+
else:
|
|
56
|
+
break
|
|
57
|
+
self._operators.append(operator1)
|
|
58
|
+
|
|
59
|
+
def finish(self) -> None:
|
|
60
|
+
"""Finalizes the expression by converting it to a tree structure.
|
|
61
|
+
|
|
62
|
+
Should be called after all nodes have been added.
|
|
63
|
+
"""
|
|
64
|
+
while self._operators:
|
|
65
|
+
self._output.append(self._operators.pop())
|
|
66
|
+
self.add_child(self._to_tree())
|
|
67
|
+
|
|
68
|
+
def _to_tree(self) -> ASTNode:
|
|
69
|
+
if not self._output:
|
|
70
|
+
return ASTNode()
|
|
71
|
+
node = self._output.pop()
|
|
72
|
+
if node.is_operator():
|
|
73
|
+
rhs = self._to_tree()
|
|
74
|
+
lhs = self._to_tree()
|
|
75
|
+
node.add_child(lhs)
|
|
76
|
+
node.add_child(rhs)
|
|
77
|
+
return node
|
|
78
|
+
|
|
79
|
+
def nodes_added(self) -> bool:
|
|
80
|
+
return len(self._operators) > 0 or len(self._output) > 0
|
|
81
|
+
|
|
82
|
+
def value(self) -> Any:
|
|
83
|
+
if self._overridden is not None:
|
|
84
|
+
return self._overridden
|
|
85
|
+
if self.child_count() != 1:
|
|
86
|
+
raise ValueError("Expected one child")
|
|
87
|
+
return self.children[0].value()
|
|
88
|
+
|
|
89
|
+
def set_alias(self, alias: str) -> None:
|
|
90
|
+
self._alias = alias
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def alias(self) -> Optional[str]:
|
|
94
|
+
from .reference import Reference
|
|
95
|
+
if isinstance(self.first_child(), Reference) and self._alias is None:
|
|
96
|
+
return self.first_child().identifier
|
|
97
|
+
return self._alias
|
|
98
|
+
|
|
99
|
+
@alias.setter
|
|
100
|
+
def alias(self, value: str) -> None:
|
|
101
|
+
self._alias = value
|
|
102
|
+
|
|
103
|
+
def __str__(self) -> str:
|
|
104
|
+
if self._alias is not None:
|
|
105
|
+
return f"Expression ({self._alias})"
|
|
106
|
+
return "Expression"
|
|
107
|
+
|
|
108
|
+
def reducers(self) -> List['AggregateFunction']:
|
|
109
|
+
if self._reducers is None:
|
|
110
|
+
from ..functions.aggregate_function import AggregateFunction
|
|
111
|
+
self._reducers = list(self._extract(self, AggregateFunction))
|
|
112
|
+
return self._reducers
|
|
113
|
+
|
|
114
|
+
def patterns(self) -> List['PatternExpression']:
|
|
115
|
+
if self._patterns is None:
|
|
116
|
+
from ...graph.pattern_expression import PatternExpression
|
|
117
|
+
self._patterns = list(self._extract(self, PatternExpression))
|
|
118
|
+
return self._patterns
|
|
119
|
+
|
|
120
|
+
def _extract(self, node: ASTNode, of_type: type) -> Generator[Any, None, None]:
|
|
121
|
+
if isinstance(node, of_type):
|
|
122
|
+
yield node
|
|
123
|
+
for child in node.get_children():
|
|
124
|
+
yield from self._extract(child, of_type)
|
|
125
|
+
|
|
126
|
+
def mappable(self) -> bool:
|
|
127
|
+
return len(self.reducers()) == 0
|
|
128
|
+
|
|
129
|
+
def has_reducers(self) -> bool:
|
|
130
|
+
return len(self.reducers()) > 0
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def overridden(self) -> Any:
|
|
134
|
+
return self._overridden
|
|
135
|
+
|
|
136
|
+
@overridden.setter
|
|
137
|
+
def overridden(self, value: Any) -> None:
|
|
138
|
+
self._overridden = value
|