flowquery 1.0.46 → 1.0.48
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/dist/flowquery.min.js +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/parsing/data_structures/list_comprehension.d.ts +56 -0
- package/dist/parsing/data_structures/list_comprehension.d.ts.map +1 -0
- package/dist/parsing/data_structures/list_comprehension.js +102 -0
- package/dist/parsing/data_structures/list_comprehension.js.map +1 -0
- package/dist/parsing/parser.d.ts +18 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +92 -0
- package/dist/parsing/parser.js.map +1 -1
- package/package.json +4 -1
- package/.editorconfig +0 -21
- package/.gitattributes +0 -3
- package/.github/workflows/npm-publish.yml +0 -32
- package/.github/workflows/python-publish.yml +0 -143
- package/.github/workflows/release.yml +0 -107
- package/.husky/pre-commit +0 -28
- package/.prettierrc +0 -22
- package/CODE_OF_CONDUCT.md +0 -10
- package/FlowQueryLogoIcon.png +0 -0
- package/SECURITY.md +0 -14
- package/SUPPORT.md +0 -13
- package/docs/flowquery.min.js +0 -1
- package/docs/index.html +0 -105
- package/flowquery-py/CONTRIBUTING.md +0 -127
- package/flowquery-py/README.md +0 -67
- package/flowquery-py/misc/data/test.json +0 -10
- package/flowquery-py/misc/data/users.json +0 -242
- package/flowquery-py/notebooks/TestFlowQuery.ipynb +0 -440
- package/flowquery-py/pyproject.toml +0 -121
- package/flowquery-py/setup_env.ps1 +0 -92
- package/flowquery-py/setup_env.sh +0 -87
- package/flowquery-py/src/__init__.py +0 -38
- package/flowquery-py/src/__main__.py +0 -10
- package/flowquery-py/src/compute/__init__.py +0 -6
- package/flowquery-py/src/compute/flowquery.py +0 -68
- package/flowquery-py/src/compute/runner.py +0 -64
- package/flowquery-py/src/extensibility.py +0 -52
- package/flowquery-py/src/graph/__init__.py +0 -31
- package/flowquery-py/src/graph/data.py +0 -136
- package/flowquery-py/src/graph/database.py +0 -141
- package/flowquery-py/src/graph/hops.py +0 -43
- package/flowquery-py/src/graph/node.py +0 -143
- package/flowquery-py/src/graph/node_data.py +0 -26
- package/flowquery-py/src/graph/node_reference.py +0 -50
- package/flowquery-py/src/graph/pattern.py +0 -115
- package/flowquery-py/src/graph/pattern_expression.py +0 -67
- package/flowquery-py/src/graph/patterns.py +0 -42
- package/flowquery-py/src/graph/physical_node.py +0 -41
- package/flowquery-py/src/graph/physical_relationship.py +0 -36
- package/flowquery-py/src/graph/relationship.py +0 -193
- package/flowquery-py/src/graph/relationship_data.py +0 -36
- package/flowquery-py/src/graph/relationship_match_collector.py +0 -85
- package/flowquery-py/src/graph/relationship_reference.py +0 -21
- package/flowquery-py/src/io/__init__.py +0 -5
- package/flowquery-py/src/io/command_line.py +0 -108
- package/flowquery-py/src/parsing/__init__.py +0 -17
- package/flowquery-py/src/parsing/alias.py +0 -20
- package/flowquery-py/src/parsing/alias_option.py +0 -11
- package/flowquery-py/src/parsing/ast_node.py +0 -147
- package/flowquery-py/src/parsing/base_parser.py +0 -84
- package/flowquery-py/src/parsing/components/__init__.py +0 -19
- package/flowquery-py/src/parsing/components/csv.py +0 -8
- package/flowquery-py/src/parsing/components/from_.py +0 -12
- package/flowquery-py/src/parsing/components/headers.py +0 -12
- package/flowquery-py/src/parsing/components/json.py +0 -8
- package/flowquery-py/src/parsing/components/null.py +0 -10
- package/flowquery-py/src/parsing/components/post.py +0 -8
- package/flowquery-py/src/parsing/components/text.py +0 -8
- package/flowquery-py/src/parsing/context.py +0 -50
- package/flowquery-py/src/parsing/data_structures/__init__.py +0 -15
- package/flowquery-py/src/parsing/data_structures/associative_array.py +0 -41
- package/flowquery-py/src/parsing/data_structures/json_array.py +0 -30
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +0 -38
- package/flowquery-py/src/parsing/data_structures/lookup.py +0 -51
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +0 -42
- package/flowquery-py/src/parsing/expressions/__init__.py +0 -61
- package/flowquery-py/src/parsing/expressions/boolean.py +0 -20
- package/flowquery-py/src/parsing/expressions/expression.py +0 -141
- package/flowquery-py/src/parsing/expressions/expression_map.py +0 -26
- package/flowquery-py/src/parsing/expressions/f_string.py +0 -27
- package/flowquery-py/src/parsing/expressions/identifier.py +0 -21
- package/flowquery-py/src/parsing/expressions/number.py +0 -32
- package/flowquery-py/src/parsing/expressions/operator.py +0 -271
- package/flowquery-py/src/parsing/expressions/reference.py +0 -47
- package/flowquery-py/src/parsing/expressions/string.py +0 -27
- package/flowquery-py/src/parsing/functions/__init__.py +0 -127
- package/flowquery-py/src/parsing/functions/aggregate_function.py +0 -60
- package/flowquery-py/src/parsing/functions/async_function.py +0 -65
- package/flowquery-py/src/parsing/functions/avg.py +0 -55
- package/flowquery-py/src/parsing/functions/coalesce.py +0 -43
- package/flowquery-py/src/parsing/functions/collect.py +0 -75
- package/flowquery-py/src/parsing/functions/count.py +0 -79
- package/flowquery-py/src/parsing/functions/date_.py +0 -61
- package/flowquery-py/src/parsing/functions/datetime_.py +0 -62
- package/flowquery-py/src/parsing/functions/duration.py +0 -159
- package/flowquery-py/src/parsing/functions/element_id.py +0 -50
- package/flowquery-py/src/parsing/functions/function.py +0 -68
- package/flowquery-py/src/parsing/functions/function_factory.py +0 -170
- package/flowquery-py/src/parsing/functions/function_metadata.py +0 -148
- package/flowquery-py/src/parsing/functions/functions.py +0 -67
- package/flowquery-py/src/parsing/functions/head.py +0 -39
- package/flowquery-py/src/parsing/functions/id_.py +0 -49
- package/flowquery-py/src/parsing/functions/join.py +0 -49
- package/flowquery-py/src/parsing/functions/keys.py +0 -34
- package/flowquery-py/src/parsing/functions/last.py +0 -39
- package/flowquery-py/src/parsing/functions/localdatetime.py +0 -60
- package/flowquery-py/src/parsing/functions/localtime.py +0 -57
- package/flowquery-py/src/parsing/functions/max_.py +0 -49
- package/flowquery-py/src/parsing/functions/min_.py +0 -49
- package/flowquery-py/src/parsing/functions/nodes.py +0 -48
- package/flowquery-py/src/parsing/functions/predicate_function.py +0 -47
- package/flowquery-py/src/parsing/functions/predicate_sum.py +0 -49
- package/flowquery-py/src/parsing/functions/properties.py +0 -50
- package/flowquery-py/src/parsing/functions/rand.py +0 -28
- package/flowquery-py/src/parsing/functions/range_.py +0 -41
- package/flowquery-py/src/parsing/functions/reducer_element.py +0 -15
- package/flowquery-py/src/parsing/functions/relationships.py +0 -46
- package/flowquery-py/src/parsing/functions/replace.py +0 -39
- package/flowquery-py/src/parsing/functions/round_.py +0 -34
- package/flowquery-py/src/parsing/functions/schema.py +0 -40
- package/flowquery-py/src/parsing/functions/size.py +0 -34
- package/flowquery-py/src/parsing/functions/split.py +0 -54
- package/flowquery-py/src/parsing/functions/string_distance.py +0 -92
- package/flowquery-py/src/parsing/functions/stringify.py +0 -49
- package/flowquery-py/src/parsing/functions/substring.py +0 -76
- package/flowquery-py/src/parsing/functions/sum.py +0 -51
- package/flowquery-py/src/parsing/functions/tail.py +0 -37
- package/flowquery-py/src/parsing/functions/temporal_utils.py +0 -186
- package/flowquery-py/src/parsing/functions/time_.py +0 -57
- package/flowquery-py/src/parsing/functions/timestamp.py +0 -37
- package/flowquery-py/src/parsing/functions/to_float.py +0 -46
- package/flowquery-py/src/parsing/functions/to_integer.py +0 -46
- package/flowquery-py/src/parsing/functions/to_json.py +0 -35
- package/flowquery-py/src/parsing/functions/to_lower.py +0 -37
- package/flowquery-py/src/parsing/functions/to_string.py +0 -41
- package/flowquery-py/src/parsing/functions/trim.py +0 -37
- package/flowquery-py/src/parsing/functions/type_.py +0 -47
- package/flowquery-py/src/parsing/functions/value_holder.py +0 -24
- package/flowquery-py/src/parsing/logic/__init__.py +0 -15
- package/flowquery-py/src/parsing/logic/case.py +0 -28
- package/flowquery-py/src/parsing/logic/else_.py +0 -12
- package/flowquery-py/src/parsing/logic/end.py +0 -8
- package/flowquery-py/src/parsing/logic/then.py +0 -12
- package/flowquery-py/src/parsing/logic/when.py +0 -12
- package/flowquery-py/src/parsing/operations/__init__.py +0 -46
- package/flowquery-py/src/parsing/operations/aggregated_return.py +0 -25
- package/flowquery-py/src/parsing/operations/aggregated_with.py +0 -22
- package/flowquery-py/src/parsing/operations/call.py +0 -73
- package/flowquery-py/src/parsing/operations/create_node.py +0 -35
- package/flowquery-py/src/parsing/operations/create_relationship.py +0 -35
- package/flowquery-py/src/parsing/operations/delete_node.py +0 -29
- package/flowquery-py/src/parsing/operations/delete_relationship.py +0 -29
- package/flowquery-py/src/parsing/operations/group_by.py +0 -148
- package/flowquery-py/src/parsing/operations/limit.py +0 -33
- package/flowquery-py/src/parsing/operations/load.py +0 -148
- package/flowquery-py/src/parsing/operations/match.py +0 -52
- package/flowquery-py/src/parsing/operations/operation.py +0 -69
- package/flowquery-py/src/parsing/operations/order_by.py +0 -114
- package/flowquery-py/src/parsing/operations/projection.py +0 -21
- package/flowquery-py/src/parsing/operations/return_op.py +0 -88
- package/flowquery-py/src/parsing/operations/union.py +0 -115
- package/flowquery-py/src/parsing/operations/union_all.py +0 -17
- package/flowquery-py/src/parsing/operations/unwind.py +0 -42
- package/flowquery-py/src/parsing/operations/where.py +0 -43
- package/flowquery-py/src/parsing/operations/with_op.py +0 -18
- package/flowquery-py/src/parsing/parser.py +0 -1384
- package/flowquery-py/src/parsing/parser_state.py +0 -26
- package/flowquery-py/src/parsing/token_to_node.py +0 -109
- package/flowquery-py/src/tokenization/__init__.py +0 -23
- package/flowquery-py/src/tokenization/keyword.py +0 -54
- package/flowquery-py/src/tokenization/operator.py +0 -29
- package/flowquery-py/src/tokenization/string_walker.py +0 -158
- package/flowquery-py/src/tokenization/symbol.py +0 -19
- package/flowquery-py/src/tokenization/token.py +0 -693
- package/flowquery-py/src/tokenization/token_mapper.py +0 -53
- package/flowquery-py/src/tokenization/token_type.py +0 -21
- package/flowquery-py/src/tokenization/tokenizer.py +0 -214
- package/flowquery-py/src/tokenization/trie.py +0 -125
- package/flowquery-py/src/utils/__init__.py +0 -6
- package/flowquery-py/src/utils/object_utils.py +0 -20
- package/flowquery-py/src/utils/string_utils.py +0 -113
- package/flowquery-py/tests/__init__.py +0 -1
- package/flowquery-py/tests/compute/__init__.py +0 -1
- package/flowquery-py/tests/compute/test_runner.py +0 -4902
- package/flowquery-py/tests/graph/__init__.py +0 -1
- package/flowquery-py/tests/graph/test_create.py +0 -56
- package/flowquery-py/tests/graph/test_data.py +0 -73
- package/flowquery-py/tests/graph/test_match.py +0 -40
- package/flowquery-py/tests/parsing/__init__.py +0 -1
- package/flowquery-py/tests/parsing/test_context.py +0 -34
- package/flowquery-py/tests/parsing/test_expression.py +0 -248
- package/flowquery-py/tests/parsing/test_parser.py +0 -1237
- package/flowquery-py/tests/test_extensibility.py +0 -611
- package/flowquery-py/tests/tokenization/__init__.py +0 -1
- package/flowquery-py/tests/tokenization/test_token_mapper.py +0 -60
- package/flowquery-py/tests/tokenization/test_tokenizer.py +0 -198
- package/flowquery-py/tests/tokenization/test_trie.py +0 -30
- package/flowquery-vscode/.vscode-test.mjs +0 -5
- package/flowquery-vscode/.vscodeignore +0 -13
- package/flowquery-vscode/LICENSE +0 -21
- package/flowquery-vscode/README.md +0 -11
- package/flowquery-vscode/demo/FlowQueryVSCodeDemo.gif +0 -0
- package/flowquery-vscode/eslint.config.mjs +0 -25
- package/flowquery-vscode/extension.js +0 -508
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +0 -1
- package/flowquery-vscode/flowquery-worker.js +0 -66
- package/flowquery-vscode/images/FlowQueryLogoIcon.png +0 -0
- package/flowquery-vscode/jsconfig.json +0 -13
- package/flowquery-vscode/libs/page.css +0 -53
- package/flowquery-vscode/libs/table.css +0 -13
- package/flowquery-vscode/libs/tabs.css +0 -66
- package/flowquery-vscode/package-lock.json +0 -2917
- package/flowquery-vscode/package.json +0 -51
- package/flowquery-vscode/test/extension.test.js +0 -196
- package/flowquery-vscode/test/worker.test.js +0 -25
- package/flowquery-vscode/vsc-extension-quickstart.md +0 -42
- package/jest.config.js +0 -14
- package/misc/apps/RAG/README.md +0 -29
- package/misc/apps/RAG/data/chats.json +0 -302
- package/misc/apps/RAG/data/emails.json +0 -182
- package/misc/apps/RAG/data/events.json +0 -226
- package/misc/apps/RAG/data/files.json +0 -172
- package/misc/apps/RAG/data/users.json +0 -158
- package/misc/apps/RAG/jest.config.js +0 -21
- package/misc/apps/RAG/package.json +0 -48
- package/misc/apps/RAG/public/index.html +0 -18
- package/misc/apps/RAG/src/App.css +0 -42
- package/misc/apps/RAG/src/App.tsx +0 -50
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.css +0 -172
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +0 -380
- package/misc/apps/RAG/src/components/ApiKeySettings.tsx +0 -245
- package/misc/apps/RAG/src/components/ChatContainer.css +0 -67
- package/misc/apps/RAG/src/components/ChatContainer.tsx +0 -242
- package/misc/apps/RAG/src/components/ChatInput.css +0 -23
- package/misc/apps/RAG/src/components/ChatInput.tsx +0 -76
- package/misc/apps/RAG/src/components/ChatMessage.css +0 -160
- package/misc/apps/RAG/src/components/ChatMessage.tsx +0 -286
- package/misc/apps/RAG/src/components/FlowQueryAgent.ts +0 -708
- package/misc/apps/RAG/src/components/FlowQueryRunner.css +0 -113
- package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +0 -371
- package/misc/apps/RAG/src/components/index.ts +0 -28
- package/misc/apps/RAG/src/graph/index.ts +0 -19
- package/misc/apps/RAG/src/graph/initializeGraph.ts +0 -254
- package/misc/apps/RAG/src/index.tsx +0 -29
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +0 -327
- package/misc/apps/RAG/src/prompts/index.ts +0 -10
- package/misc/apps/RAG/src/tests/graph.test.ts +0 -35
- package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +0 -130
- package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +0 -208
- package/misc/apps/RAG/src/utils/Llm.ts +0 -248
- package/misc/apps/RAG/src/utils/index.ts +0 -12
- package/misc/apps/RAG/tsconfig.json +0 -22
- package/misc/apps/RAG/webpack.config.js +0 -43
- package/misc/apps/README.md +0 -1
- package/misc/queries/analyze_catfacts.cql +0 -75
- package/misc/queries/azure_openai_completions.cql +0 -13
- package/misc/queries/azure_openai_models.cql +0 -9
- package/misc/queries/mock_pipeline.cql +0 -84
- package/misc/queries/openai_completions.cql +0 -15
- package/misc/queries/openai_models.cql +0 -13
- package/misc/queries/test.cql +0 -6
- package/misc/queries/tool_inference.cql +0 -24
- package/misc/queries/wisdom.cql +0 -6
- package/misc/queries/wisdom_letter_histogram.cql +0 -8
- package/src/compute/flowquery.ts +0 -46
- package/src/compute/runner.ts +0 -66
- package/src/extensibility.ts +0 -45
- package/src/graph/data.ts +0 -130
- package/src/graph/database.ts +0 -143
- package/src/graph/hops.ts +0 -22
- package/src/graph/node.ts +0 -122
- package/src/graph/node_data.ts +0 -18
- package/src/graph/node_reference.ts +0 -38
- package/src/graph/pattern.ts +0 -110
- package/src/graph/pattern_expression.ts +0 -48
- package/src/graph/patterns.ts +0 -36
- package/src/graph/physical_node.ts +0 -23
- package/src/graph/physical_relationship.ts +0 -23
- package/src/graph/relationship.ts +0 -167
- package/src/graph/relationship_data.ts +0 -31
- package/src/graph/relationship_match_collector.ts +0 -64
- package/src/graph/relationship_reference.ts +0 -25
- package/src/index.browser.ts +0 -46
- package/src/index.node.ts +0 -55
- package/src/index.ts +0 -12
- package/src/io/command_line.ts +0 -74
- package/src/parsing/alias.ts +0 -23
- package/src/parsing/alias_option.ts +0 -5
- package/src/parsing/ast_node.ts +0 -153
- package/src/parsing/base_parser.ts +0 -98
- package/src/parsing/components/csv.ts +0 -9
- package/src/parsing/components/from.ts +0 -12
- package/src/parsing/components/headers.ts +0 -12
- package/src/parsing/components/json.ts +0 -9
- package/src/parsing/components/null.ts +0 -9
- package/src/parsing/components/post.ts +0 -9
- package/src/parsing/components/text.ts +0 -9
- package/src/parsing/context.ts +0 -54
- package/src/parsing/data_structures/associative_array.ts +0 -43
- package/src/parsing/data_structures/json_array.ts +0 -31
- package/src/parsing/data_structures/key_value_pair.ts +0 -37
- package/src/parsing/data_structures/lookup.ts +0 -44
- package/src/parsing/data_structures/range_lookup.ts +0 -36
- package/src/parsing/expressions/boolean.ts +0 -21
- package/src/parsing/expressions/expression.ts +0 -150
- package/src/parsing/expressions/expression_map.ts +0 -22
- package/src/parsing/expressions/f_string.ts +0 -26
- package/src/parsing/expressions/identifier.ts +0 -22
- package/src/parsing/expressions/number.ts +0 -40
- package/src/parsing/expressions/operator.ts +0 -354
- package/src/parsing/expressions/reference.ts +0 -45
- package/src/parsing/expressions/string.ts +0 -34
- package/src/parsing/functions/aggregate_function.ts +0 -58
- package/src/parsing/functions/async_function.ts +0 -64
- package/src/parsing/functions/avg.ts +0 -47
- package/src/parsing/functions/coalesce.ts +0 -49
- package/src/parsing/functions/collect.ts +0 -54
- package/src/parsing/functions/count.ts +0 -54
- package/src/parsing/functions/date.ts +0 -63
- package/src/parsing/functions/datetime.ts +0 -63
- package/src/parsing/functions/duration.ts +0 -143
- package/src/parsing/functions/element_id.ts +0 -51
- package/src/parsing/functions/function.ts +0 -60
- package/src/parsing/functions/function_factory.ts +0 -195
- package/src/parsing/functions/function_metadata.ts +0 -217
- package/src/parsing/functions/functions.ts +0 -70
- package/src/parsing/functions/head.ts +0 -42
- package/src/parsing/functions/id.ts +0 -51
- package/src/parsing/functions/join.ts +0 -40
- package/src/parsing/functions/keys.ts +0 -29
- package/src/parsing/functions/last.ts +0 -42
- package/src/parsing/functions/localdatetime.ts +0 -63
- package/src/parsing/functions/localtime.ts +0 -58
- package/src/parsing/functions/max.ts +0 -37
- package/src/parsing/functions/min.ts +0 -37
- package/src/parsing/functions/nodes.ts +0 -54
- package/src/parsing/functions/predicate_function.ts +0 -48
- package/src/parsing/functions/predicate_sum.ts +0 -47
- package/src/parsing/functions/properties.ts +0 -56
- package/src/parsing/functions/rand.ts +0 -21
- package/src/parsing/functions/range.ts +0 -37
- package/src/parsing/functions/reducer_element.ts +0 -10
- package/src/parsing/functions/relationships.ts +0 -52
- package/src/parsing/functions/replace.ts +0 -38
- package/src/parsing/functions/round.ts +0 -28
- package/src/parsing/functions/schema.ts +0 -39
- package/src/parsing/functions/size.ts +0 -28
- package/src/parsing/functions/split.ts +0 -45
- package/src/parsing/functions/string_distance.ts +0 -83
- package/src/parsing/functions/stringify.ts +0 -37
- package/src/parsing/functions/substring.ts +0 -68
- package/src/parsing/functions/sum.ts +0 -41
- package/src/parsing/functions/tail.ts +0 -39
- package/src/parsing/functions/temporal_utils.ts +0 -180
- package/src/parsing/functions/time.ts +0 -58
- package/src/parsing/functions/timestamp.ts +0 -37
- package/src/parsing/functions/to_float.ts +0 -50
- package/src/parsing/functions/to_integer.ts +0 -50
- package/src/parsing/functions/to_json.ts +0 -28
- package/src/parsing/functions/to_lower.ts +0 -28
- package/src/parsing/functions/to_string.ts +0 -32
- package/src/parsing/functions/trim.ts +0 -28
- package/src/parsing/functions/type.ts +0 -39
- package/src/parsing/functions/value_holder.ts +0 -13
- package/src/parsing/logic/case.ts +0 -26
- package/src/parsing/logic/else.ts +0 -12
- package/src/parsing/logic/end.ts +0 -9
- package/src/parsing/logic/then.ts +0 -12
- package/src/parsing/logic/when.ts +0 -12
- package/src/parsing/operations/aggregated_return.ts +0 -22
- package/src/parsing/operations/aggregated_with.ts +0 -18
- package/src/parsing/operations/call.ts +0 -69
- package/src/parsing/operations/create_node.ts +0 -39
- package/src/parsing/operations/create_relationship.ts +0 -38
- package/src/parsing/operations/delete_node.ts +0 -33
- package/src/parsing/operations/delete_relationship.ts +0 -32
- package/src/parsing/operations/group_by.ts +0 -137
- package/src/parsing/operations/limit.ts +0 -31
- package/src/parsing/operations/load.ts +0 -146
- package/src/parsing/operations/match.ts +0 -54
- package/src/parsing/operations/operation.ts +0 -69
- package/src/parsing/operations/order_by.ts +0 -126
- package/src/parsing/operations/projection.ts +0 -18
- package/src/parsing/operations/return.ts +0 -76
- package/src/parsing/operations/union.ts +0 -114
- package/src/parsing/operations/union_all.ts +0 -16
- package/src/parsing/operations/unwind.ts +0 -36
- package/src/parsing/operations/where.ts +0 -42
- package/src/parsing/operations/with.ts +0 -20
- package/src/parsing/parser.ts +0 -1641
- package/src/parsing/parser_state.ts +0 -25
- package/src/parsing/token_to_node.ts +0 -114
- package/src/tokenization/keyword.ts +0 -50
- package/src/tokenization/operator.ts +0 -25
- package/src/tokenization/string_walker.ts +0 -197
- package/src/tokenization/symbol.ts +0 -15
- package/src/tokenization/token.ts +0 -764
- package/src/tokenization/token_mapper.ts +0 -53
- package/src/tokenization/token_type.ts +0 -16
- package/src/tokenization/tokenizer.ts +0 -250
- package/src/tokenization/trie.ts +0 -117
- package/src/utils/object_utils.ts +0 -17
- package/src/utils/string_utils.ts +0 -114
- package/tests/compute/runner.test.ts +0 -4559
- package/tests/extensibility.test.ts +0 -643
- package/tests/graph/create.test.ts +0 -36
- package/tests/graph/data.test.ts +0 -58
- package/tests/graph/match.test.ts +0 -29
- package/tests/parsing/context.test.ts +0 -27
- package/tests/parsing/expression.test.ts +0 -303
- package/tests/parsing/parser.test.ts +0 -1327
- package/tests/tokenization/token_mapper.test.ts +0 -47
- package/tests/tokenization/tokenizer.test.ts +0 -191
- package/tests/tokenization/trie.test.ts +0 -20
- package/tsconfig.json +0 -19
- package/typedoc.json +0 -16
- package/vscode-settings.json.recommended +0 -16
- package/webpack.config.js +0 -26
package/src/parsing/parser.ts
DELETED
|
@@ -1,1641 +0,0 @@
|
|
|
1
|
-
import Hops from "../graph/hops";
|
|
2
|
-
import Node from "../graph/node";
|
|
3
|
-
import NodeReference from "../graph/node_reference";
|
|
4
|
-
import Pattern from "../graph/pattern";
|
|
5
|
-
import PatternExpression from "../graph/pattern_expression";
|
|
6
|
-
import Relationship from "../graph/relationship";
|
|
7
|
-
import RelationshipReference from "../graph/relationship_reference";
|
|
8
|
-
import Token from "../tokenization/token";
|
|
9
|
-
import ObjectUtils from "../utils/object_utils";
|
|
10
|
-
import Alias from "./alias";
|
|
11
|
-
import { AliasOption } from "./alias_option";
|
|
12
|
-
import ASTNode from "./ast_node";
|
|
13
|
-
import BaseParser from "./base_parser";
|
|
14
|
-
import From from "./components/from";
|
|
15
|
-
import Headers from "./components/headers";
|
|
16
|
-
import Null from "./components/null";
|
|
17
|
-
import Post from "./components/post";
|
|
18
|
-
import Context from "./context";
|
|
19
|
-
import AssociativeArray from "./data_structures/associative_array";
|
|
20
|
-
import JSONArray from "./data_structures/json_array";
|
|
21
|
-
import KeyValuePair from "./data_structures/key_value_pair";
|
|
22
|
-
import Lookup from "./data_structures/lookup";
|
|
23
|
-
import RangeLookup from "./data_structures/range_lookup";
|
|
24
|
-
import Expression from "./expressions/expression";
|
|
25
|
-
import FString from "./expressions/f_string";
|
|
26
|
-
import Identifier from "./expressions/identifier";
|
|
27
|
-
import {
|
|
28
|
-
Contains,
|
|
29
|
-
EndsWith,
|
|
30
|
-
In,
|
|
31
|
-
Is,
|
|
32
|
-
IsNot,
|
|
33
|
-
Not,
|
|
34
|
-
NotContains,
|
|
35
|
-
NotEndsWith,
|
|
36
|
-
NotIn,
|
|
37
|
-
NotStartsWith,
|
|
38
|
-
StartsWith,
|
|
39
|
-
} from "./expressions/operator";
|
|
40
|
-
import Reference from "./expressions/reference";
|
|
41
|
-
import String from "./expressions/string";
|
|
42
|
-
import AggregateFunction from "./functions/aggregate_function";
|
|
43
|
-
import AsyncFunction from "./functions/async_function";
|
|
44
|
-
import Function from "./functions/function";
|
|
45
|
-
import FunctionFactory from "./functions/function_factory";
|
|
46
|
-
import PredicateFunction from "./functions/predicate_function";
|
|
47
|
-
import Case from "./logic/case";
|
|
48
|
-
import Else from "./logic/else";
|
|
49
|
-
import Then from "./logic/then";
|
|
50
|
-
import When from "./logic/when";
|
|
51
|
-
import AggregatedReturn from "./operations/aggregated_return";
|
|
52
|
-
import AggregatedWith from "./operations/aggregated_with";
|
|
53
|
-
import Call from "./operations/call";
|
|
54
|
-
import CreateNode from "./operations/create_node";
|
|
55
|
-
import CreateRelationship from "./operations/create_relationship";
|
|
56
|
-
import DeleteNode from "./operations/delete_node";
|
|
57
|
-
import DeleteRelationship from "./operations/delete_relationship";
|
|
58
|
-
import Limit from "./operations/limit";
|
|
59
|
-
import Load from "./operations/load";
|
|
60
|
-
import Match from "./operations/match";
|
|
61
|
-
import Operation from "./operations/operation";
|
|
62
|
-
import OrderBy, { SortField } from "./operations/order_by";
|
|
63
|
-
import Return from "./operations/return";
|
|
64
|
-
import Union from "./operations/union";
|
|
65
|
-
import UnionAll from "./operations/union_all";
|
|
66
|
-
import Unwind from "./operations/unwind";
|
|
67
|
-
import Where from "./operations/where";
|
|
68
|
-
import With from "./operations/with";
|
|
69
|
-
import ParserState from "./parser_state";
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Main parser for FlowQuery statements.
|
|
73
|
-
*
|
|
74
|
-
* Parses FlowQuery declarative query language statements into an Abstract Syntax Tree (AST).
|
|
75
|
-
* Supports operations like WITH, UNWIND, RETURN, LOAD, WHERE, and LIMIT, along with
|
|
76
|
-
* expressions, functions, data structures, and logical constructs.
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```typescript
|
|
80
|
-
* const parser = new Parser();
|
|
81
|
-
* const ast = parser.parse("unwind [1, 2, 3, 4, 5] as num return num");
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
class Parser extends BaseParser {
|
|
85
|
-
private _state: ParserState = new ParserState();
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Parses a FlowQuery statement into an Abstract Syntax Tree.
|
|
89
|
-
*
|
|
90
|
-
* @param statement - The FlowQuery statement to parse
|
|
91
|
-
* @returns The root AST node containing the parsed structure
|
|
92
|
-
* @throws {Error} If the statement is malformed or contains syntax errors
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```typescript
|
|
96
|
-
* const ast = parser.parse("LOAD JSON FROM 'https://api.adviceslip.com/advice' AS data RETURN data");
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
public parse(statement: string): ASTNode {
|
|
100
|
-
this.tokenize(statement);
|
|
101
|
-
return this._parseTokenized();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private _parseTokenized(isSubQuery: boolean = false): ASTNode {
|
|
105
|
-
const root: ASTNode = new ASTNode();
|
|
106
|
-
let previous: Operation | null = null;
|
|
107
|
-
let operation: Operation | null = null;
|
|
108
|
-
while (!this.token.isEOF()) {
|
|
109
|
-
if (root.childCount() > 0) {
|
|
110
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
111
|
-
} else {
|
|
112
|
-
this.skipWhitespaceAndComments();
|
|
113
|
-
}
|
|
114
|
-
// UNION separates two query pipelines — break and handle after the loop
|
|
115
|
-
if (this.token.isUnion()) {
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
if (this.token.isEOF()) {
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
operation = this.parseOperation();
|
|
122
|
-
if (operation === null && !isSubQuery) {
|
|
123
|
-
throw new Error("Expected one of WITH, UNWIND, RETURN, LOAD, OR CALL");
|
|
124
|
-
} else if (operation === null && isSubQuery) {
|
|
125
|
-
return root;
|
|
126
|
-
}
|
|
127
|
-
if (this._state.returns > 1) {
|
|
128
|
-
throw new Error("Only one RETURN statement is allowed");
|
|
129
|
-
}
|
|
130
|
-
if (previous instanceof Call && !previous.hasYield) {
|
|
131
|
-
throw new Error(
|
|
132
|
-
"CALL operations must have a YIELD clause unless they are the last operation"
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
if (previous !== null) {
|
|
136
|
-
previous.addSibling(operation!);
|
|
137
|
-
} else {
|
|
138
|
-
root.addChild(operation!);
|
|
139
|
-
}
|
|
140
|
-
const where = this.parseWhere();
|
|
141
|
-
if (where !== null) {
|
|
142
|
-
if (operation instanceof Return) {
|
|
143
|
-
(operation as Return).where = where;
|
|
144
|
-
} else {
|
|
145
|
-
operation!.addSibling(where);
|
|
146
|
-
operation = where;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const orderBy = this.parseOrderBy();
|
|
150
|
-
if (orderBy !== null) {
|
|
151
|
-
if (operation instanceof Return) {
|
|
152
|
-
(operation as Return).orderBy = orderBy;
|
|
153
|
-
} else {
|
|
154
|
-
operation!.addSibling(orderBy);
|
|
155
|
-
operation = orderBy;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const limit = this.parseLimit();
|
|
159
|
-
if (limit !== null) {
|
|
160
|
-
if (operation instanceof Return) {
|
|
161
|
-
(operation as Return).limit = limit;
|
|
162
|
-
} else {
|
|
163
|
-
operation!.addSibling(limit);
|
|
164
|
-
operation = limit;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
previous = operation;
|
|
168
|
-
}
|
|
169
|
-
// Handle UNION: wrap left and right pipelines into a Union node
|
|
170
|
-
if (!this.token.isEOF() && this.token.isUnion()) {
|
|
171
|
-
if (!(operation instanceof Return) && !(operation instanceof Call)) {
|
|
172
|
-
throw new Error("Each side of UNION must end with a RETURN or CALL statement");
|
|
173
|
-
}
|
|
174
|
-
const union = this.parseUnion()!;
|
|
175
|
-
union.left = root.firstChild() as Operation;
|
|
176
|
-
// Save and reset parser state for right-side scope
|
|
177
|
-
const state = this._state;
|
|
178
|
-
this._state = new ParserState();
|
|
179
|
-
const right = this._parseTokenized(isSubQuery);
|
|
180
|
-
union.right = right.firstChild() as Operation;
|
|
181
|
-
// Restore parser state
|
|
182
|
-
this._state = state;
|
|
183
|
-
const newRoot = new ASTNode();
|
|
184
|
-
newRoot.addChild(union);
|
|
185
|
-
return newRoot;
|
|
186
|
-
}
|
|
187
|
-
if (
|
|
188
|
-
!(operation instanceof Return) &&
|
|
189
|
-
!(operation instanceof Call) &&
|
|
190
|
-
!(operation instanceof CreateNode) &&
|
|
191
|
-
!(operation instanceof CreateRelationship) &&
|
|
192
|
-
!(operation instanceof DeleteNode) &&
|
|
193
|
-
!(operation instanceof DeleteRelationship)
|
|
194
|
-
) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
"Last statement must be a RETURN, WHERE, CALL, CREATE, or DELETE statement"
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
return root;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private parseOperation(): Operation | null {
|
|
203
|
-
return (
|
|
204
|
-
this.parseWith() ||
|
|
205
|
-
this.parseUnwind() ||
|
|
206
|
-
this.parseReturn() ||
|
|
207
|
-
this.parseLoad() ||
|
|
208
|
-
this.parseCall() ||
|
|
209
|
-
this.parseCreate() ||
|
|
210
|
-
this.parseDelete() ||
|
|
211
|
-
this.parseMatch()
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private parseWith(): With | null {
|
|
216
|
-
if (!this.token.isWith()) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
this.setNextToken();
|
|
220
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
221
|
-
let distinct = false;
|
|
222
|
-
if (this.token.isDistinct()) {
|
|
223
|
-
distinct = true;
|
|
224
|
-
this.setNextToken();
|
|
225
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
226
|
-
}
|
|
227
|
-
const expressions = this.parseExpressions(AliasOption.REQUIRED);
|
|
228
|
-
if (expressions.length === 0) {
|
|
229
|
-
throw new Error("Expected expression");
|
|
230
|
-
}
|
|
231
|
-
if (distinct || expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
232
|
-
return new AggregatedWith(expressions);
|
|
233
|
-
}
|
|
234
|
-
return new With(expressions);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private parseUnwind(): Unwind | null {
|
|
238
|
-
if (!this.token.isUnwind()) {
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
this.setNextToken();
|
|
242
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
243
|
-
const expression: Expression | null = this.parseExpression();
|
|
244
|
-
if (expression === null) {
|
|
245
|
-
throw new Error("Expected expression");
|
|
246
|
-
}
|
|
247
|
-
if (
|
|
248
|
-
!ObjectUtils.isInstanceOfAny(expression.firstChild(), [
|
|
249
|
-
JSONArray,
|
|
250
|
-
Function,
|
|
251
|
-
Reference,
|
|
252
|
-
Lookup,
|
|
253
|
-
RangeLookup,
|
|
254
|
-
])
|
|
255
|
-
) {
|
|
256
|
-
throw new Error("Expected array, function, reference, or lookup.");
|
|
257
|
-
}
|
|
258
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
259
|
-
const alias = this.parseAlias();
|
|
260
|
-
if (alias !== null) {
|
|
261
|
-
expression.setAlias(alias.getAlias());
|
|
262
|
-
} else {
|
|
263
|
-
throw new Error("Expected alias");
|
|
264
|
-
}
|
|
265
|
-
const unwind = new Unwind(expression);
|
|
266
|
-
this._state.variables.set(alias.getAlias(), unwind);
|
|
267
|
-
return unwind;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private parseReturn(): Return | null {
|
|
271
|
-
if (!this.token.isReturn()) {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
this.setNextToken();
|
|
275
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
276
|
-
let distinct = false;
|
|
277
|
-
if (this.token.isDistinct()) {
|
|
278
|
-
distinct = true;
|
|
279
|
-
this.setNextToken();
|
|
280
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
281
|
-
}
|
|
282
|
-
const expressions = this.parseExpressions(AliasOption.OPTIONAL);
|
|
283
|
-
if (expressions.length === 0) {
|
|
284
|
-
throw new Error("Expected expression");
|
|
285
|
-
}
|
|
286
|
-
if (distinct || expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
287
|
-
return new AggregatedReturn(expressions);
|
|
288
|
-
}
|
|
289
|
-
this._state.incrementReturns();
|
|
290
|
-
return new Return(expressions);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private parseWhere(): Where | null {
|
|
294
|
-
this.skipWhitespaceAndComments();
|
|
295
|
-
if (!this.token.isWhere()) {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
299
|
-
this.setNextToken();
|
|
300
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
301
|
-
const expression = this.parseExpression();
|
|
302
|
-
if (expression === null) {
|
|
303
|
-
throw new Error("Expected expression");
|
|
304
|
-
}
|
|
305
|
-
if (ObjectUtils.isInstanceOfAny(expression.firstChild(), [JSONArray, AssociativeArray])) {
|
|
306
|
-
throw new Error("Expected an expression which can be evaluated to a boolean");
|
|
307
|
-
}
|
|
308
|
-
return new Where(expression);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
private parseLoad(): Load | null {
|
|
312
|
-
if (!this.token.isLoad()) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
const load = new Load();
|
|
316
|
-
this.setNextToken();
|
|
317
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
318
|
-
if (!(this.token.isJSON() || this.token.isCSV() || this.token.isText())) {
|
|
319
|
-
throw new Error("Expected JSON, CSV, or TEXT");
|
|
320
|
-
}
|
|
321
|
-
load.addChild(this.token.node);
|
|
322
|
-
this.setNextToken();
|
|
323
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
324
|
-
if (!this.token.isFrom()) {
|
|
325
|
-
throw new Error("Expected FROM");
|
|
326
|
-
}
|
|
327
|
-
this.setNextToken();
|
|
328
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
329
|
-
const from = new From();
|
|
330
|
-
load.addChild(from);
|
|
331
|
-
|
|
332
|
-
// Check if the source is an async function
|
|
333
|
-
const asyncFunc = this.parseAsyncFunction();
|
|
334
|
-
if (asyncFunc !== null) {
|
|
335
|
-
from.addChild(asyncFunc);
|
|
336
|
-
} else {
|
|
337
|
-
const expression = this.parseExpression();
|
|
338
|
-
if (expression === null) {
|
|
339
|
-
throw new Error("Expected expression or async function");
|
|
340
|
-
}
|
|
341
|
-
from.addChild(expression);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
345
|
-
if (this.token.isHeaders()) {
|
|
346
|
-
const headers = new Headers();
|
|
347
|
-
this.setNextToken();
|
|
348
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
349
|
-
const header = this.parseExpression();
|
|
350
|
-
if (header === null) {
|
|
351
|
-
throw new Error("Expected expression");
|
|
352
|
-
}
|
|
353
|
-
headers.addChild(header);
|
|
354
|
-
load.addChild(headers);
|
|
355
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
356
|
-
}
|
|
357
|
-
if (this.token.isPost()) {
|
|
358
|
-
const post = new Post();
|
|
359
|
-
this.setNextToken();
|
|
360
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
361
|
-
const payload = this.parseExpression();
|
|
362
|
-
if (payload === null) {
|
|
363
|
-
throw new Error("Expected expression");
|
|
364
|
-
}
|
|
365
|
-
post.addChild(payload);
|
|
366
|
-
load.addChild(post);
|
|
367
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
368
|
-
}
|
|
369
|
-
const alias = this.parseAlias();
|
|
370
|
-
if (alias !== null) {
|
|
371
|
-
load.addChild(alias);
|
|
372
|
-
this._state.variables.set(alias.getAlias(), load);
|
|
373
|
-
} else {
|
|
374
|
-
throw new Error("Expected alias");
|
|
375
|
-
}
|
|
376
|
-
return load;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private parseCall(): Call | null {
|
|
380
|
-
if (!this.token.isCall()) {
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
383
|
-
this.setNextToken();
|
|
384
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
385
|
-
const asyncFunction = this.parseAsyncFunction();
|
|
386
|
-
if (asyncFunction === null) {
|
|
387
|
-
throw new Error("Expected async function");
|
|
388
|
-
}
|
|
389
|
-
const call = new Call();
|
|
390
|
-
call.function = asyncFunction;
|
|
391
|
-
this.skipWhitespaceAndComments();
|
|
392
|
-
if (this.token.isYield()) {
|
|
393
|
-
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
394
|
-
this.setNextToken();
|
|
395
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
396
|
-
const expressions = this.parseExpressions(AliasOption.OPTIONAL);
|
|
397
|
-
if (expressions.length === 0) {
|
|
398
|
-
throw new Error("Expected at least one expression");
|
|
399
|
-
}
|
|
400
|
-
call.yielded = expressions;
|
|
401
|
-
}
|
|
402
|
-
return call;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
private parseCreate(): CreateNode | CreateRelationship | null {
|
|
406
|
-
if (!this.token.isCreate()) {
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
this.setNextToken();
|
|
410
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
411
|
-
if (!this.token.isVirtual()) {
|
|
412
|
-
throw new Error("Expected VIRTUAL");
|
|
413
|
-
}
|
|
414
|
-
this.setNextToken();
|
|
415
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
416
|
-
const node: Node | null = this.parseNode();
|
|
417
|
-
if (node === null) {
|
|
418
|
-
throw new Error("Expected node definition");
|
|
419
|
-
}
|
|
420
|
-
let relationship: Relationship | null = null;
|
|
421
|
-
if (this.token.isSubtract() && this.peek()?.isOpeningBracket()) {
|
|
422
|
-
this.setNextToken();
|
|
423
|
-
this.setNextToken();
|
|
424
|
-
if (!this.token.isColon()) {
|
|
425
|
-
throw new Error("Expected ':' for relationship type");
|
|
426
|
-
}
|
|
427
|
-
this.setNextToken();
|
|
428
|
-
if (!this.token.isIdentifierOrKeyword()) {
|
|
429
|
-
throw new Error("Expected relationship type identifier");
|
|
430
|
-
}
|
|
431
|
-
const type: string = this.token.value || "";
|
|
432
|
-
this.setNextToken();
|
|
433
|
-
if (!this.token.isClosingBracket()) {
|
|
434
|
-
throw new Error("Expected closing bracket for relationship definition");
|
|
435
|
-
}
|
|
436
|
-
this.setNextToken();
|
|
437
|
-
if (!this.token.isSubtract()) {
|
|
438
|
-
throw new Error("Expected '-' for relationship definition");
|
|
439
|
-
}
|
|
440
|
-
this.setNextToken();
|
|
441
|
-
const target: Node | null = this.parseNode();
|
|
442
|
-
if (target === null) {
|
|
443
|
-
throw new Error("Expected target node definition");
|
|
444
|
-
}
|
|
445
|
-
relationship = new Relationship();
|
|
446
|
-
relationship.type = type;
|
|
447
|
-
relationship.source = node;
|
|
448
|
-
relationship.target = target;
|
|
449
|
-
}
|
|
450
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
451
|
-
if (!this.token.isAs()) {
|
|
452
|
-
throw new Error("Expected AS");
|
|
453
|
-
}
|
|
454
|
-
this.setNextToken();
|
|
455
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
456
|
-
const query: ASTNode | null = this.parseSubQuery();
|
|
457
|
-
if (query === null) {
|
|
458
|
-
throw new Error("Expected sub-query");
|
|
459
|
-
}
|
|
460
|
-
let create: CreateNode | CreateRelationship;
|
|
461
|
-
if (relationship !== null) {
|
|
462
|
-
create = new CreateRelationship(relationship, query);
|
|
463
|
-
} else {
|
|
464
|
-
create = new CreateNode(node, query);
|
|
465
|
-
}
|
|
466
|
-
return create;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
private parseDelete(): DeleteNode | DeleteRelationship | null {
|
|
470
|
-
if (!this.token.isDelete()) {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
this.setNextToken();
|
|
474
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
475
|
-
if (!this.token.isVirtual()) {
|
|
476
|
-
throw new Error("Expected VIRTUAL");
|
|
477
|
-
}
|
|
478
|
-
this.setNextToken();
|
|
479
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
480
|
-
const node: Node | null = this.parseNode();
|
|
481
|
-
if (node === null) {
|
|
482
|
-
throw new Error("Expected node definition");
|
|
483
|
-
}
|
|
484
|
-
let relationship: Relationship | null = null;
|
|
485
|
-
if (this.token.isSubtract() && this.peek()?.isOpeningBracket()) {
|
|
486
|
-
this.setNextToken();
|
|
487
|
-
this.setNextToken();
|
|
488
|
-
if (!this.token.isColon()) {
|
|
489
|
-
throw new Error("Expected ':' for relationship type");
|
|
490
|
-
}
|
|
491
|
-
this.setNextToken();
|
|
492
|
-
if (!this.token.isIdentifierOrKeyword()) {
|
|
493
|
-
throw new Error("Expected relationship type identifier");
|
|
494
|
-
}
|
|
495
|
-
const type: string = this.token.value || "";
|
|
496
|
-
this.setNextToken();
|
|
497
|
-
if (!this.token.isClosingBracket()) {
|
|
498
|
-
throw new Error("Expected closing bracket for relationship definition");
|
|
499
|
-
}
|
|
500
|
-
this.setNextToken();
|
|
501
|
-
if (!this.token.isSubtract()) {
|
|
502
|
-
throw new Error("Expected '-' for relationship definition");
|
|
503
|
-
}
|
|
504
|
-
this.setNextToken();
|
|
505
|
-
const target: Node | null = this.parseNode();
|
|
506
|
-
if (target === null) {
|
|
507
|
-
throw new Error("Expected target node definition");
|
|
508
|
-
}
|
|
509
|
-
relationship = new Relationship();
|
|
510
|
-
relationship.type = type;
|
|
511
|
-
relationship.source = node;
|
|
512
|
-
relationship.target = target;
|
|
513
|
-
}
|
|
514
|
-
let result: DeleteNode | DeleteRelationship;
|
|
515
|
-
if (relationship !== null) {
|
|
516
|
-
result = new DeleteRelationship(relationship);
|
|
517
|
-
} else {
|
|
518
|
-
result = new DeleteNode(node);
|
|
519
|
-
}
|
|
520
|
-
return result;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
private parseMatch(): Match | null {
|
|
524
|
-
let optional = false;
|
|
525
|
-
if (this.token.isOptional()) {
|
|
526
|
-
optional = true;
|
|
527
|
-
this.setNextToken();
|
|
528
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
529
|
-
}
|
|
530
|
-
if (!this.token.isMatch()) {
|
|
531
|
-
if (optional) {
|
|
532
|
-
throw new Error("Expected MATCH after OPTIONAL");
|
|
533
|
-
}
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
this.setNextToken();
|
|
537
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
538
|
-
const patterns: Pattern[] = Array.from(this.parsePatterns());
|
|
539
|
-
if (patterns.length === 0) {
|
|
540
|
-
throw new Error("Expected graph pattern");
|
|
541
|
-
}
|
|
542
|
-
return new Match(patterns, optional);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
private parseNode(): Node | null {
|
|
546
|
-
if (!this.token.isLeftParenthesis()) {
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
549
|
-
this.setNextToken();
|
|
550
|
-
this.skipWhitespaceAndComments();
|
|
551
|
-
let identifier: string | null = null;
|
|
552
|
-
if (this.token.isIdentifierOrKeyword()) {
|
|
553
|
-
identifier = this.token.value || "";
|
|
554
|
-
this.setNextToken();
|
|
555
|
-
}
|
|
556
|
-
this.skipWhitespaceAndComments();
|
|
557
|
-
let label: string | null = null;
|
|
558
|
-
if (!this.token.isColon() && this.peek()?.isIdentifierOrKeyword()) {
|
|
559
|
-
throw new Error("Expected ':' for node label");
|
|
560
|
-
}
|
|
561
|
-
if (this.token.isColon() && !this.peek()?.isIdentifierOrKeyword()) {
|
|
562
|
-
throw new Error("Expected node label identifier");
|
|
563
|
-
}
|
|
564
|
-
if (this.token.isColon() && this.peek()?.isIdentifierOrKeyword()) {
|
|
565
|
-
this.setNextToken();
|
|
566
|
-
label = this.token.value || "";
|
|
567
|
-
this.setNextToken();
|
|
568
|
-
}
|
|
569
|
-
this.skipWhitespaceAndComments();
|
|
570
|
-
let node = new Node();
|
|
571
|
-
node.properties = new Map(this.parseProperties());
|
|
572
|
-
node.label = label!;
|
|
573
|
-
if (identifier !== null && this._state.variables.has(identifier)) {
|
|
574
|
-
let reference = this._state.variables.get(identifier);
|
|
575
|
-
if (
|
|
576
|
-
reference === undefined ||
|
|
577
|
-
(!(reference instanceof Node) &&
|
|
578
|
-
!(reference instanceof Unwind) &&
|
|
579
|
-
!(reference instanceof Expression))
|
|
580
|
-
) {
|
|
581
|
-
throw new Error(`Undefined node reference: ${identifier}`);
|
|
582
|
-
}
|
|
583
|
-
node = new NodeReference(node, reference);
|
|
584
|
-
} else if (identifier !== null) {
|
|
585
|
-
node.identifier = identifier;
|
|
586
|
-
this._state.variables.set(identifier, node);
|
|
587
|
-
}
|
|
588
|
-
if (!this.token.isRightParenthesis()) {
|
|
589
|
-
throw new Error("Expected closing parenthesis for node definition");
|
|
590
|
-
}
|
|
591
|
-
this.setNextToken();
|
|
592
|
-
return node;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
private *parseProperties(): Iterable<[string, Expression]> {
|
|
596
|
-
let parts: number = 0;
|
|
597
|
-
while (true) {
|
|
598
|
-
this.skipWhitespaceAndComments();
|
|
599
|
-
if (!this.token.isOpeningBrace() && parts == 0) {
|
|
600
|
-
return;
|
|
601
|
-
} else if (!this.token.isOpeningBrace() && parts > 0) {
|
|
602
|
-
throw new Error("Expected opening brace");
|
|
603
|
-
}
|
|
604
|
-
this.setNextToken();
|
|
605
|
-
this.skipWhitespaceAndComments();
|
|
606
|
-
if (!this.token.isIdentifier()) {
|
|
607
|
-
throw new Error("Expected identifier");
|
|
608
|
-
}
|
|
609
|
-
const key: string = this.token.value!;
|
|
610
|
-
this.setNextToken();
|
|
611
|
-
this.skipWhitespaceAndComments();
|
|
612
|
-
if (!this.token.isColon()) {
|
|
613
|
-
throw new Error("Expected colon");
|
|
614
|
-
}
|
|
615
|
-
this.setNextToken();
|
|
616
|
-
this.skipWhitespaceAndComments();
|
|
617
|
-
const expression: Expression | null = this.parseExpression();
|
|
618
|
-
if (expression === null) {
|
|
619
|
-
throw new Error("Expected expression");
|
|
620
|
-
}
|
|
621
|
-
this.skipWhitespaceAndComments();
|
|
622
|
-
if (!this.token.isClosingBrace()) {
|
|
623
|
-
throw new Error("Expected closing brace");
|
|
624
|
-
}
|
|
625
|
-
this.setNextToken();
|
|
626
|
-
yield [key, expression];
|
|
627
|
-
this.skipWhitespaceAndComments();
|
|
628
|
-
if (!this.token.isComma()) {
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
this.setNextToken();
|
|
632
|
-
parts++;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
private *parsePatterns(): IterableIterator<Pattern> {
|
|
637
|
-
while (true) {
|
|
638
|
-
let identifier: string | null = null;
|
|
639
|
-
if (this.token.isIdentifier()) {
|
|
640
|
-
identifier = this.token.value || "";
|
|
641
|
-
this.setNextToken();
|
|
642
|
-
this.skipWhitespaceAndComments();
|
|
643
|
-
if (!this.token.isEquals()) {
|
|
644
|
-
throw new Error("Expected '=' for pattern assignment");
|
|
645
|
-
}
|
|
646
|
-
this.setNextToken();
|
|
647
|
-
this.skipWhitespaceAndComments();
|
|
648
|
-
}
|
|
649
|
-
const pattern: Pattern | null = this.parsePattern();
|
|
650
|
-
if (pattern !== null) {
|
|
651
|
-
if (identifier !== null) {
|
|
652
|
-
pattern.identifier = identifier;
|
|
653
|
-
this._state.variables.set(identifier, pattern);
|
|
654
|
-
}
|
|
655
|
-
yield pattern;
|
|
656
|
-
} else {
|
|
657
|
-
break;
|
|
658
|
-
}
|
|
659
|
-
this.skipWhitespaceAndComments();
|
|
660
|
-
if (!this.token.isComma()) {
|
|
661
|
-
break;
|
|
662
|
-
}
|
|
663
|
-
this.setNextToken();
|
|
664
|
-
this.skipWhitespaceAndComments();
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
private parsePattern(): Pattern | null {
|
|
669
|
-
if (!this.token.isLeftParenthesis()) {
|
|
670
|
-
return null;
|
|
671
|
-
}
|
|
672
|
-
const pattern = new Pattern();
|
|
673
|
-
let node = this.parseNode();
|
|
674
|
-
if (node === null) {
|
|
675
|
-
throw new Error("Expected node definition");
|
|
676
|
-
}
|
|
677
|
-
pattern.addElement(node);
|
|
678
|
-
let relationship: Relationship | null = null;
|
|
679
|
-
while (true) {
|
|
680
|
-
relationship = this.parseRelationship();
|
|
681
|
-
if (relationship === null) {
|
|
682
|
-
break;
|
|
683
|
-
}
|
|
684
|
-
pattern.addElement(relationship);
|
|
685
|
-
node = this.parseNode();
|
|
686
|
-
if (node === null) {
|
|
687
|
-
throw new Error("Expected target node definition");
|
|
688
|
-
}
|
|
689
|
-
pattern.addElement(node);
|
|
690
|
-
}
|
|
691
|
-
return pattern;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
private parsePatternExpression(): PatternExpression | null {
|
|
695
|
-
if (!this.token.isLeftParenthesis()) {
|
|
696
|
-
return null;
|
|
697
|
-
}
|
|
698
|
-
const pattern = new PatternExpression();
|
|
699
|
-
let node = this.parseNode();
|
|
700
|
-
if (node === null) {
|
|
701
|
-
throw new Error("Expected node definition");
|
|
702
|
-
}
|
|
703
|
-
pattern.addElement(node);
|
|
704
|
-
let relationship: Relationship | null = null;
|
|
705
|
-
while (true) {
|
|
706
|
-
relationship = this.parseRelationship();
|
|
707
|
-
if (relationship === null) {
|
|
708
|
-
break;
|
|
709
|
-
}
|
|
710
|
-
if (relationship.hops?.multi()) {
|
|
711
|
-
throw new Error("PatternExpression does not support variable-length relationships");
|
|
712
|
-
}
|
|
713
|
-
pattern.addElement(relationship);
|
|
714
|
-
node = this.parseNode();
|
|
715
|
-
if (node === null) {
|
|
716
|
-
throw new Error("Expected target node definition");
|
|
717
|
-
}
|
|
718
|
-
pattern.addElement(node);
|
|
719
|
-
}
|
|
720
|
-
pattern.verify();
|
|
721
|
-
return pattern;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
private parseRelationship(): Relationship | null {
|
|
725
|
-
let direction: "left" | "right" = "right";
|
|
726
|
-
if (this.token.isLessThan() && this.peek()?.isSubtract()) {
|
|
727
|
-
direction = "left";
|
|
728
|
-
this.setNextToken();
|
|
729
|
-
this.setNextToken();
|
|
730
|
-
} else if (this.token.isSubtract()) {
|
|
731
|
-
this.setNextToken();
|
|
732
|
-
} else {
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
if (!this.token.isOpeningBracket()) {
|
|
736
|
-
return null;
|
|
737
|
-
}
|
|
738
|
-
this.setNextToken();
|
|
739
|
-
let variable: string | null = null;
|
|
740
|
-
if (this.token.isIdentifierOrKeyword()) {
|
|
741
|
-
variable = this.token.value || "";
|
|
742
|
-
this.setNextToken();
|
|
743
|
-
}
|
|
744
|
-
if (!this.token.isColon()) {
|
|
745
|
-
throw new Error("Expected ':' for relationship type");
|
|
746
|
-
}
|
|
747
|
-
this.setNextToken();
|
|
748
|
-
if (!this.token.isIdentifierOrKeyword()) {
|
|
749
|
-
throw new Error("Expected relationship type identifier");
|
|
750
|
-
}
|
|
751
|
-
const types: string[] = [this.token.value || ""];
|
|
752
|
-
this.setNextToken();
|
|
753
|
-
while (this.token.isPipe()) {
|
|
754
|
-
this.setNextToken();
|
|
755
|
-
if (this.token.isColon()) {
|
|
756
|
-
this.setNextToken();
|
|
757
|
-
}
|
|
758
|
-
if (!this.token.isIdentifierOrKeyword()) {
|
|
759
|
-
throw new Error("Expected relationship type identifier after '|'");
|
|
760
|
-
}
|
|
761
|
-
types.push(this.token.value || "");
|
|
762
|
-
this.setNextToken();
|
|
763
|
-
}
|
|
764
|
-
const hops: Hops | null = this.parseRelationshipHops();
|
|
765
|
-
const properties: Map<string, Expression> = new Map(this.parseProperties());
|
|
766
|
-
if (!this.token.isClosingBracket()) {
|
|
767
|
-
throw new Error("Expected closing bracket for relationship definition");
|
|
768
|
-
}
|
|
769
|
-
this.setNextToken();
|
|
770
|
-
if (!this.token.isSubtract()) {
|
|
771
|
-
throw new Error("Expected '-' for relationship definition");
|
|
772
|
-
}
|
|
773
|
-
this.setNextToken();
|
|
774
|
-
if (this.token.isGreaterThan()) {
|
|
775
|
-
this.setNextToken();
|
|
776
|
-
}
|
|
777
|
-
let relationship = new Relationship();
|
|
778
|
-
relationship.direction = direction;
|
|
779
|
-
relationship.properties = properties;
|
|
780
|
-
if (variable !== null && this._state.variables.has(variable)) {
|
|
781
|
-
let reference = this._state.variables.get(variable);
|
|
782
|
-
// Resolve through Expression -> Reference -> Relationship (e.g., after WITH)
|
|
783
|
-
if (reference instanceof Expression && reference.firstChild() instanceof Reference) {
|
|
784
|
-
const inner = (reference.firstChild() as Reference).referred;
|
|
785
|
-
if (inner instanceof Relationship) {
|
|
786
|
-
reference = inner;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
if (reference === undefined || !(reference instanceof Relationship)) {
|
|
790
|
-
throw new Error(`Undefined relationship reference: ${variable}`);
|
|
791
|
-
}
|
|
792
|
-
relationship = new RelationshipReference(relationship, reference);
|
|
793
|
-
} else if (variable !== null) {
|
|
794
|
-
relationship.identifier = variable;
|
|
795
|
-
this._state.variables.set(variable, relationship);
|
|
796
|
-
}
|
|
797
|
-
if (hops !== null) {
|
|
798
|
-
relationship.hops = hops;
|
|
799
|
-
}
|
|
800
|
-
relationship.types = types;
|
|
801
|
-
return relationship;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
private parseRelationshipHops(): Hops | null {
|
|
805
|
-
if (!this.token.isMultiply()) {
|
|
806
|
-
return null;
|
|
807
|
-
}
|
|
808
|
-
const hops = new Hops();
|
|
809
|
-
this.setNextToken();
|
|
810
|
-
if (this.token.isNumber()) {
|
|
811
|
-
hops.min = parseInt(this.token.value || "0");
|
|
812
|
-
this.setNextToken();
|
|
813
|
-
if (this.token.isDot()) {
|
|
814
|
-
this.setNextToken();
|
|
815
|
-
if (!this.token.isDot()) {
|
|
816
|
-
throw new Error("Expected '..' for relationship hops");
|
|
817
|
-
}
|
|
818
|
-
this.setNextToken();
|
|
819
|
-
if (!this.token.isNumber()) {
|
|
820
|
-
hops.max = Number.MAX_SAFE_INTEGER;
|
|
821
|
-
} else {
|
|
822
|
-
hops.max = parseInt(this.token.value || "0");
|
|
823
|
-
this.setNextToken();
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
} else {
|
|
827
|
-
hops.min = 0;
|
|
828
|
-
hops.max = Number.MAX_SAFE_INTEGER;
|
|
829
|
-
}
|
|
830
|
-
return hops;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
private parseSubQuery(): ASTNode | null {
|
|
834
|
-
if (!this.token.isOpeningBrace()) {
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
this.setNextToken();
|
|
838
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
839
|
-
const query: ASTNode = this._parseTokenized(true);
|
|
840
|
-
this.skipWhitespaceAndComments();
|
|
841
|
-
if (!this.token.isClosingBrace()) {
|
|
842
|
-
throw new Error("Expected closing brace for sub-query");
|
|
843
|
-
}
|
|
844
|
-
this.setNextToken();
|
|
845
|
-
return query;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
private parseLimit(): Limit | null {
|
|
849
|
-
this.skipWhitespaceAndComments();
|
|
850
|
-
if (!this.token.isLimit()) {
|
|
851
|
-
return null;
|
|
852
|
-
}
|
|
853
|
-
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
854
|
-
this.setNextToken();
|
|
855
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
856
|
-
if (!this.token.isNumber()) {
|
|
857
|
-
throw new Error("Expected number");
|
|
858
|
-
}
|
|
859
|
-
const limit = new Limit(parseInt(this.token.value || "0"));
|
|
860
|
-
this.setNextToken();
|
|
861
|
-
return limit;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
private parseOrderBy(): OrderBy | null {
|
|
865
|
-
this.skipWhitespaceAndComments();
|
|
866
|
-
if (!this.token.isOrder()) {
|
|
867
|
-
return null;
|
|
868
|
-
}
|
|
869
|
-
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
870
|
-
this.setNextToken();
|
|
871
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
872
|
-
if (!this.token.isByKeyword()) {
|
|
873
|
-
throw new Error("Expected BY after ORDER");
|
|
874
|
-
}
|
|
875
|
-
this.setNextToken();
|
|
876
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
877
|
-
const fields: SortField[] = [];
|
|
878
|
-
while (true) {
|
|
879
|
-
const expression: Expression | null = this.parseExpression();
|
|
880
|
-
if (expression === null) {
|
|
881
|
-
throw new Error("Expected expression in ORDER BY");
|
|
882
|
-
}
|
|
883
|
-
this.skipWhitespaceAndComments();
|
|
884
|
-
let direction: "asc" | "desc" = "asc";
|
|
885
|
-
if (this.token.isAsc()) {
|
|
886
|
-
direction = "asc";
|
|
887
|
-
this.setNextToken();
|
|
888
|
-
this.skipWhitespaceAndComments();
|
|
889
|
-
} else if (this.token.isDesc()) {
|
|
890
|
-
direction = "desc";
|
|
891
|
-
this.setNextToken();
|
|
892
|
-
this.skipWhitespaceAndComments();
|
|
893
|
-
}
|
|
894
|
-
fields.push({ direction, expression });
|
|
895
|
-
if (this.token.isComma()) {
|
|
896
|
-
this.setNextToken();
|
|
897
|
-
this.skipWhitespaceAndComments();
|
|
898
|
-
} else {
|
|
899
|
-
break;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return new OrderBy(fields);
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
/**
|
|
906
|
-
* Parses a comma-separated list of expressions with deferred variable
|
|
907
|
-
* registration. Aliases set by earlier expressions in the same clause
|
|
908
|
-
* won't shadow variables needed by later expressions
|
|
909
|
-
* (e.g. `RETURN a.x AS a, a.y AS b`).
|
|
910
|
-
*/
|
|
911
|
-
private parseExpressions(alias_option: AliasOption = AliasOption.NOT_ALLOWED): Expression[] {
|
|
912
|
-
const parsed = Array.from(this._parseExpressions(alias_option));
|
|
913
|
-
for (const [expression, variableName] of parsed) {
|
|
914
|
-
if (variableName !== null) {
|
|
915
|
-
this._state.variables.set(variableName, expression);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
return parsed.map(([expression]) => expression);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
private *_parseExpressions(
|
|
922
|
-
alias_option: AliasOption
|
|
923
|
-
): IterableIterator<[Expression, string | null]> {
|
|
924
|
-
while (true) {
|
|
925
|
-
const expression: Expression | null = this.parseExpression();
|
|
926
|
-
if (expression === null) {
|
|
927
|
-
break;
|
|
928
|
-
}
|
|
929
|
-
let variableName: string | null = null;
|
|
930
|
-
const alias = this.parseAlias();
|
|
931
|
-
if (expression.firstChild() instanceof Reference && alias === null) {
|
|
932
|
-
const reference: Reference = expression.firstChild() as Reference;
|
|
933
|
-
expression.setAlias(reference.identifier);
|
|
934
|
-
variableName = reference.identifier;
|
|
935
|
-
} else if (
|
|
936
|
-
alias_option === AliasOption.REQUIRED &&
|
|
937
|
-
alias === null &&
|
|
938
|
-
!(expression.firstChild() instanceof Reference)
|
|
939
|
-
) {
|
|
940
|
-
throw new Error("Alias required");
|
|
941
|
-
} else if (alias_option === AliasOption.NOT_ALLOWED && alias !== null) {
|
|
942
|
-
throw new Error("Alias not allowed");
|
|
943
|
-
} else if (
|
|
944
|
-
[AliasOption.OPTIONAL, AliasOption.REQUIRED].includes(alias_option) &&
|
|
945
|
-
alias !== null
|
|
946
|
-
) {
|
|
947
|
-
expression.setAlias(alias.getAlias());
|
|
948
|
-
variableName = alias.getAlias();
|
|
949
|
-
}
|
|
950
|
-
yield [expression, variableName];
|
|
951
|
-
this.skipWhitespaceAndComments();
|
|
952
|
-
if (!this.token.isComma()) {
|
|
953
|
-
break;
|
|
954
|
-
}
|
|
955
|
-
this.setNextToken();
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
/**
|
|
960
|
-
* Parse a single operand (without operators).
|
|
961
|
-
* @returns True if an operand was parsed, false otherwise.
|
|
962
|
-
*/
|
|
963
|
-
private parseOperand(expression: Expression): boolean {
|
|
964
|
-
this.skipWhitespaceAndComments();
|
|
965
|
-
if (this.token.isIdentifierOrKeyword() && !this.peek()?.isLeftParenthesis()) {
|
|
966
|
-
const identifier: string = this.token.value || "";
|
|
967
|
-
const reference = new Reference(identifier, this._state.variables.get(identifier));
|
|
968
|
-
this.setNextToken();
|
|
969
|
-
const lookup = this.parseLookup(reference);
|
|
970
|
-
expression.addNode(lookup);
|
|
971
|
-
return true;
|
|
972
|
-
} else if (this.token.isIdentifierOrKeyword() && this.peek()?.isLeftParenthesis()) {
|
|
973
|
-
const func = this.parsePredicateFunction() || this.parseFunction();
|
|
974
|
-
if (func !== null) {
|
|
975
|
-
const lookup = this.parseLookup(func);
|
|
976
|
-
expression.addNode(lookup);
|
|
977
|
-
return true;
|
|
978
|
-
}
|
|
979
|
-
} else if (this.token.isLeftParenthesis() && this.looksLikeNodePattern()) {
|
|
980
|
-
// Possible graph pattern expression
|
|
981
|
-
const pattern = this.parsePatternExpression();
|
|
982
|
-
if (pattern !== null) {
|
|
983
|
-
expression.addNode(pattern);
|
|
984
|
-
return true;
|
|
985
|
-
}
|
|
986
|
-
} else if (this.token.isOperand()) {
|
|
987
|
-
expression.addNode(this.token.node);
|
|
988
|
-
this.setNextToken();
|
|
989
|
-
return true;
|
|
990
|
-
} else if (this.token.isFString()) {
|
|
991
|
-
const f_string = this.parseFString();
|
|
992
|
-
if (f_string === null) {
|
|
993
|
-
throw new Error("Expected f-string");
|
|
994
|
-
}
|
|
995
|
-
expression.addNode(f_string);
|
|
996
|
-
return true;
|
|
997
|
-
} else if (this.token.isLeftParenthesis()) {
|
|
998
|
-
this.setNextToken();
|
|
999
|
-
const sub = this.parseExpression();
|
|
1000
|
-
if (sub === null) {
|
|
1001
|
-
throw new Error("Expected expression");
|
|
1002
|
-
}
|
|
1003
|
-
if (!this.token.isRightParenthesis()) {
|
|
1004
|
-
throw new Error("Expected right parenthesis");
|
|
1005
|
-
}
|
|
1006
|
-
this.setNextToken();
|
|
1007
|
-
const lookup = this.parseLookup(sub);
|
|
1008
|
-
expression.addNode(lookup);
|
|
1009
|
-
return true;
|
|
1010
|
-
} else if (this.token.isOpeningBrace() || this.token.isOpeningBracket()) {
|
|
1011
|
-
const json = this.parseJSON();
|
|
1012
|
-
if (json === null) {
|
|
1013
|
-
throw new Error("Expected JSON object");
|
|
1014
|
-
}
|
|
1015
|
-
const lookup = this.parseLookup(json);
|
|
1016
|
-
expression.addNode(lookup);
|
|
1017
|
-
return true;
|
|
1018
|
-
} else if (this.token.isCase()) {
|
|
1019
|
-
const _case = this.parseCase();
|
|
1020
|
-
if (_case === null) {
|
|
1021
|
-
throw new Error("Expected CASE statement");
|
|
1022
|
-
}
|
|
1023
|
-
expression.addNode(_case);
|
|
1024
|
-
return true;
|
|
1025
|
-
} else if (this.token.isNot()) {
|
|
1026
|
-
const not = new Not();
|
|
1027
|
-
this.setNextToken();
|
|
1028
|
-
// NOT should only bind to the next operand, not the entire expression
|
|
1029
|
-
const tempExpr = new Expression();
|
|
1030
|
-
if (!this.parseOperand(tempExpr)) {
|
|
1031
|
-
throw new Error("Expected expression after NOT");
|
|
1032
|
-
}
|
|
1033
|
-
tempExpr.finish();
|
|
1034
|
-
not.addChild(tempExpr);
|
|
1035
|
-
expression.addNode(not);
|
|
1036
|
-
return true;
|
|
1037
|
-
}
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
/**
|
|
1042
|
-
* Peeks ahead from a left parenthesis to determine whether the
|
|
1043
|
-
* upcoming tokens form a graph-node pattern (e.g. (n:Label), (n),
|
|
1044
|
-
* (:Label), ()) rather than a parenthesised expression (e.g.
|
|
1045
|
-
* (variable.property), (a + b)).
|
|
1046
|
-
*
|
|
1047
|
-
* The heuristic is:
|
|
1048
|
-
* • ( followed by `:` or `)` → node pattern
|
|
1049
|
-
* • ( identifier, then `:` or `{` or `)` → node pattern
|
|
1050
|
-
* • anything else → parenthesised expression
|
|
1051
|
-
*/
|
|
1052
|
-
private looksLikeNodePattern(): boolean {
|
|
1053
|
-
const savedIndex = this.tokenIndex;
|
|
1054
|
-
this.setNextToken(); // skip '('
|
|
1055
|
-
this.skipWhitespaceAndComments();
|
|
1056
|
-
|
|
1057
|
-
if (this.token.isColon() || this.token.isRightParenthesis()) {
|
|
1058
|
-
this.tokenIndex = savedIndex;
|
|
1059
|
-
return true;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (this.token.isIdentifierOrKeyword()) {
|
|
1063
|
-
this.setNextToken(); // skip identifier
|
|
1064
|
-
this.skipWhitespaceAndComments();
|
|
1065
|
-
const result =
|
|
1066
|
-
this.token.isColon() ||
|
|
1067
|
-
this.token.isOpeningBrace() ||
|
|
1068
|
-
this.token.isRightParenthesis();
|
|
1069
|
-
this.tokenIndex = savedIndex;
|
|
1070
|
-
return result;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
this.tokenIndex = savedIndex;
|
|
1074
|
-
return false;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
private parseExpression(): Expression | null {
|
|
1078
|
-
const expression = new Expression();
|
|
1079
|
-
while (true) {
|
|
1080
|
-
if (!this.parseOperand(expression)) {
|
|
1081
|
-
if (expression.nodesAdded()) {
|
|
1082
|
-
throw new Error("Expected operand or left parenthesis");
|
|
1083
|
-
} else {
|
|
1084
|
-
break;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
this.skipWhitespaceAndComments();
|
|
1088
|
-
if (this.token.isOperator()) {
|
|
1089
|
-
if (this.token.isIs()) {
|
|
1090
|
-
expression.addNode(this.parseIsOperator());
|
|
1091
|
-
} else {
|
|
1092
|
-
expression.addNode(this.token.node);
|
|
1093
|
-
}
|
|
1094
|
-
} else if (this.token.isIn()) {
|
|
1095
|
-
expression.addNode(this.parseInOperator());
|
|
1096
|
-
} else if (this.token.isContains()) {
|
|
1097
|
-
expression.addNode(this.parseContainsOperator());
|
|
1098
|
-
} else if (this.token.isStarts()) {
|
|
1099
|
-
expression.addNode(this.parseStartsWithOperator());
|
|
1100
|
-
} else if (this.token.isEnds()) {
|
|
1101
|
-
expression.addNode(this.parseEndsWithOperator());
|
|
1102
|
-
} else if (this.token.isNot()) {
|
|
1103
|
-
const notOp = this.parseNotOperator();
|
|
1104
|
-
if (notOp === null) {
|
|
1105
|
-
break;
|
|
1106
|
-
}
|
|
1107
|
-
expression.addNode(notOp);
|
|
1108
|
-
} else {
|
|
1109
|
-
break;
|
|
1110
|
-
}
|
|
1111
|
-
this.setNextToken();
|
|
1112
|
-
}
|
|
1113
|
-
if (expression.nodesAdded()) {
|
|
1114
|
-
expression.finish();
|
|
1115
|
-
return expression;
|
|
1116
|
-
}
|
|
1117
|
-
return null;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
private parseIsOperator(): Is | IsNot {
|
|
1121
|
-
// Current token is IS. Look ahead for NOT to produce IS NOT.
|
|
1122
|
-
const savedIndex = this.tokenIndex;
|
|
1123
|
-
this.setNextToken();
|
|
1124
|
-
this.skipWhitespaceAndComments();
|
|
1125
|
-
if (this.token.isNot()) {
|
|
1126
|
-
return new IsNot();
|
|
1127
|
-
}
|
|
1128
|
-
// Not IS NOT — restore position to IS so the outer loop's setNextToken advances past it.
|
|
1129
|
-
this.tokenIndex = savedIndex;
|
|
1130
|
-
return new Is();
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
private parseInOperator(): In | NotIn {
|
|
1134
|
-
// Current token is IN. Advance past it so the outer loop's setNextToken moves correctly.
|
|
1135
|
-
return new In();
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
private parseContainsOperator(): Contains {
|
|
1139
|
-
return new Contains();
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
private parseStartsWithOperator(): StartsWith {
|
|
1143
|
-
// Current token is STARTS. Look ahead for WITH.
|
|
1144
|
-
const savedIndex = this.tokenIndex;
|
|
1145
|
-
this.setNextToken();
|
|
1146
|
-
this.skipWhitespaceAndComments();
|
|
1147
|
-
if (this.token.isWith()) {
|
|
1148
|
-
return new StartsWith();
|
|
1149
|
-
}
|
|
1150
|
-
this.tokenIndex = savedIndex;
|
|
1151
|
-
throw new Error("Expected WITH after STARTS");
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
private parseEndsWithOperator(): EndsWith {
|
|
1155
|
-
// Current token is ENDS. Look ahead for WITH.
|
|
1156
|
-
const savedIndex = this.tokenIndex;
|
|
1157
|
-
this.setNextToken();
|
|
1158
|
-
this.skipWhitespaceAndComments();
|
|
1159
|
-
if (this.token.isWith()) {
|
|
1160
|
-
return new EndsWith();
|
|
1161
|
-
}
|
|
1162
|
-
this.tokenIndex = savedIndex;
|
|
1163
|
-
throw new Error("Expected WITH after ENDS");
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
private parseNotOperator(): NotIn | NotContains | NotStartsWith | NotEndsWith | null {
|
|
1167
|
-
// Current token is NOT. Look ahead for IN, CONTAINS, STARTS WITH, or ENDS WITH.
|
|
1168
|
-
const savedIndex = this.tokenIndex;
|
|
1169
|
-
this.setNextToken();
|
|
1170
|
-
this.skipWhitespaceAndComments();
|
|
1171
|
-
if (this.token.isIn()) {
|
|
1172
|
-
return new NotIn();
|
|
1173
|
-
}
|
|
1174
|
-
if (this.token.isContains()) {
|
|
1175
|
-
return new NotContains();
|
|
1176
|
-
}
|
|
1177
|
-
if (this.token.isStarts()) {
|
|
1178
|
-
this.setNextToken();
|
|
1179
|
-
this.skipWhitespaceAndComments();
|
|
1180
|
-
if (this.token.isWith()) {
|
|
1181
|
-
return new NotStartsWith();
|
|
1182
|
-
}
|
|
1183
|
-
this.tokenIndex = savedIndex;
|
|
1184
|
-
return null;
|
|
1185
|
-
}
|
|
1186
|
-
if (this.token.isEnds()) {
|
|
1187
|
-
this.setNextToken();
|
|
1188
|
-
this.skipWhitespaceAndComments();
|
|
1189
|
-
if (this.token.isWith()) {
|
|
1190
|
-
return new NotEndsWith();
|
|
1191
|
-
}
|
|
1192
|
-
this.tokenIndex = savedIndex;
|
|
1193
|
-
return null;
|
|
1194
|
-
}
|
|
1195
|
-
// Not a recognized NOT operator — restore position and let the outer loop break.
|
|
1196
|
-
this.tokenIndex = savedIndex;
|
|
1197
|
-
return null;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
private parseLookup(node: ASTNode): ASTNode {
|
|
1201
|
-
let variable: ASTNode = node;
|
|
1202
|
-
let lookup: Lookup | RangeLookup | null = null;
|
|
1203
|
-
while (true) {
|
|
1204
|
-
if (this.token.isDot()) {
|
|
1205
|
-
this.setNextToken();
|
|
1206
|
-
if (!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
1207
|
-
throw new Error("Expected identifier");
|
|
1208
|
-
}
|
|
1209
|
-
lookup = new Lookup();
|
|
1210
|
-
lookup.index = new Identifier(this.token.value || "");
|
|
1211
|
-
lookup.variable = variable;
|
|
1212
|
-
this.setNextToken();
|
|
1213
|
-
} else if (this.token.isOpeningBracket()) {
|
|
1214
|
-
this.setNextToken();
|
|
1215
|
-
this.skipWhitespaceAndComments();
|
|
1216
|
-
const index = this.parseExpression();
|
|
1217
|
-
let to: Expression | null = null;
|
|
1218
|
-
this.skipWhitespaceAndComments();
|
|
1219
|
-
if (this.token.isColon()) {
|
|
1220
|
-
this.setNextToken();
|
|
1221
|
-
this.skipWhitespaceAndComments();
|
|
1222
|
-
lookup = new RangeLookup();
|
|
1223
|
-
to = this.parseExpression();
|
|
1224
|
-
} else {
|
|
1225
|
-
if (index === null) {
|
|
1226
|
-
throw new Error("Expected expression");
|
|
1227
|
-
}
|
|
1228
|
-
lookup = new Lookup();
|
|
1229
|
-
}
|
|
1230
|
-
this.skipWhitespaceAndComments();
|
|
1231
|
-
if (!this.token.isClosingBracket()) {
|
|
1232
|
-
throw new Error("Expected closing bracket");
|
|
1233
|
-
}
|
|
1234
|
-
this.setNextToken();
|
|
1235
|
-
if (lookup instanceof RangeLookup) {
|
|
1236
|
-
lookup.from = index || new Null();
|
|
1237
|
-
lookup.to = to || new Null();
|
|
1238
|
-
} else if (lookup instanceof Lookup && index !== null) {
|
|
1239
|
-
lookup.index = index;
|
|
1240
|
-
}
|
|
1241
|
-
lookup.variable = variable;
|
|
1242
|
-
} else {
|
|
1243
|
-
break;
|
|
1244
|
-
}
|
|
1245
|
-
variable = lookup || variable;
|
|
1246
|
-
}
|
|
1247
|
-
return variable;
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
private parseCase(): Case | null {
|
|
1251
|
-
if (!this.token.isCase()) {
|
|
1252
|
-
return null;
|
|
1253
|
-
}
|
|
1254
|
-
this.setNextToken();
|
|
1255
|
-
const _case = new Case();
|
|
1256
|
-
let parts: number = 0;
|
|
1257
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1258
|
-
while (true) {
|
|
1259
|
-
const when = this.parseWhen();
|
|
1260
|
-
if (when === null && parts === 0) {
|
|
1261
|
-
throw new Error("Expected WHEN");
|
|
1262
|
-
} else if (when === null && parts > 0) {
|
|
1263
|
-
break;
|
|
1264
|
-
} else if (when !== null) {
|
|
1265
|
-
_case.addChild(when);
|
|
1266
|
-
}
|
|
1267
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1268
|
-
const then = this.parseThen();
|
|
1269
|
-
if (then === null) {
|
|
1270
|
-
throw new Error("Expected THEN");
|
|
1271
|
-
} else {
|
|
1272
|
-
_case.addChild(then);
|
|
1273
|
-
}
|
|
1274
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1275
|
-
parts++;
|
|
1276
|
-
}
|
|
1277
|
-
const _else = this.parseElse();
|
|
1278
|
-
if (_else === null) {
|
|
1279
|
-
throw new Error("Expected ELSE");
|
|
1280
|
-
} else {
|
|
1281
|
-
_case.addChild(_else);
|
|
1282
|
-
}
|
|
1283
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1284
|
-
if (!this.token.isEnd()) {
|
|
1285
|
-
throw new Error("Expected END");
|
|
1286
|
-
}
|
|
1287
|
-
this.setNextToken();
|
|
1288
|
-
return _case;
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
private parseWhen(): When | null {
|
|
1292
|
-
if (!this.token.isWhen()) {
|
|
1293
|
-
return null;
|
|
1294
|
-
}
|
|
1295
|
-
this.setNextToken();
|
|
1296
|
-
const when = new When();
|
|
1297
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1298
|
-
const expression = this.parseExpression();
|
|
1299
|
-
if (expression === null) {
|
|
1300
|
-
throw new Error("Expected expression");
|
|
1301
|
-
}
|
|
1302
|
-
when.addChild(expression);
|
|
1303
|
-
return when;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
private parseThen(): Then | null {
|
|
1307
|
-
if (!this.token.isThen()) {
|
|
1308
|
-
return null;
|
|
1309
|
-
}
|
|
1310
|
-
this.setNextToken();
|
|
1311
|
-
const then = new Then();
|
|
1312
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1313
|
-
const expression = this.parseExpression();
|
|
1314
|
-
if (expression === null) {
|
|
1315
|
-
throw new Error("Expected expression");
|
|
1316
|
-
}
|
|
1317
|
-
then.addChild(expression);
|
|
1318
|
-
return then;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
private parseElse(): Else | null {
|
|
1322
|
-
if (!this.token.isElse()) {
|
|
1323
|
-
return null;
|
|
1324
|
-
}
|
|
1325
|
-
this.setNextToken();
|
|
1326
|
-
const _else = new Else();
|
|
1327
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1328
|
-
const expression = this.parseExpression();
|
|
1329
|
-
if (expression === null) {
|
|
1330
|
-
throw new Error("Expected expression");
|
|
1331
|
-
}
|
|
1332
|
-
_else.addChild(expression);
|
|
1333
|
-
return _else;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
private parseAlias(): Alias | null {
|
|
1337
|
-
this.skipWhitespaceAndComments();
|
|
1338
|
-
if (!this.token.isAs()) {
|
|
1339
|
-
return null;
|
|
1340
|
-
}
|
|
1341
|
-
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
1342
|
-
this.setNextToken();
|
|
1343
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1344
|
-
if ((!this.token.isIdentifier() && !this.token.isKeyword()) || this.token.value === null) {
|
|
1345
|
-
throw new Error("Expected identifier");
|
|
1346
|
-
}
|
|
1347
|
-
const alias = new Alias(this.token.value || "");
|
|
1348
|
-
this.setNextToken();
|
|
1349
|
-
return alias;
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
private parseFunction(): Function | null {
|
|
1353
|
-
if (!this.token.isIdentifier()) {
|
|
1354
|
-
return null;
|
|
1355
|
-
}
|
|
1356
|
-
if (this.token.value === null) {
|
|
1357
|
-
throw new Error("Expected identifier");
|
|
1358
|
-
}
|
|
1359
|
-
if (!this.peek()?.isLeftParenthesis()) {
|
|
1360
|
-
return null;
|
|
1361
|
-
}
|
|
1362
|
-
const func = FunctionFactory.create(this.token.value);
|
|
1363
|
-
if (
|
|
1364
|
-
func instanceof AggregateFunction &&
|
|
1365
|
-
this._state.context.containsType(AggregateFunction)
|
|
1366
|
-
) {
|
|
1367
|
-
throw new Error("Aggregate functions cannot be nested");
|
|
1368
|
-
}
|
|
1369
|
-
this._state.context.push(func);
|
|
1370
|
-
this.setNextToken();
|
|
1371
|
-
this.setNextToken();
|
|
1372
|
-
this.skipWhitespaceAndComments();
|
|
1373
|
-
if (this.token.isDistinct()) {
|
|
1374
|
-
func.distinct = true;
|
|
1375
|
-
this.setNextToken();
|
|
1376
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1377
|
-
}
|
|
1378
|
-
func.parameters = this.parseExpressions(AliasOption.NOT_ALLOWED);
|
|
1379
|
-
this.skipWhitespaceAndComments();
|
|
1380
|
-
if (!this.token.isRightParenthesis()) {
|
|
1381
|
-
throw new Error("Expected right parenthesis");
|
|
1382
|
-
}
|
|
1383
|
-
this.setNextToken();
|
|
1384
|
-
this._state.context.pop();
|
|
1385
|
-
return func;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
/**
|
|
1389
|
-
* Parses an async function call for use in LOAD operations.
|
|
1390
|
-
* Only matches if the identifier is registered as an async data provider.
|
|
1391
|
-
*
|
|
1392
|
-
* @returns An AsyncFunction node if a registered async function is found, otherwise null
|
|
1393
|
-
*/
|
|
1394
|
-
private parseAsyncFunction(): AsyncFunction | null {
|
|
1395
|
-
if (!this.token.isIdentifier()) {
|
|
1396
|
-
return null;
|
|
1397
|
-
}
|
|
1398
|
-
if (this.token.value === null) {
|
|
1399
|
-
return null;
|
|
1400
|
-
}
|
|
1401
|
-
// Only parse as async function if it's registered as an async provider
|
|
1402
|
-
if (!FunctionFactory.isAsyncProvider(this.token.value)) {
|
|
1403
|
-
return null;
|
|
1404
|
-
}
|
|
1405
|
-
if (!this.peek()?.isLeftParenthesis()) {
|
|
1406
|
-
return null;
|
|
1407
|
-
}
|
|
1408
|
-
const asyncFunc = FunctionFactory.createAsync(this.token.value);
|
|
1409
|
-
this.setNextToken(); // skip function name
|
|
1410
|
-
this.setNextToken(); // skip left parenthesis
|
|
1411
|
-
this.skipWhitespaceAndComments();
|
|
1412
|
-
asyncFunc.parameters = this.parseExpressions(AliasOption.NOT_ALLOWED);
|
|
1413
|
-
this.skipWhitespaceAndComments();
|
|
1414
|
-
if (!this.token.isRightParenthesis()) {
|
|
1415
|
-
throw new Error("Expected right parenthesis");
|
|
1416
|
-
}
|
|
1417
|
-
this.setNextToken();
|
|
1418
|
-
return asyncFunc;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
private parsePredicateFunction(): PredicateFunction | null {
|
|
1422
|
-
if (
|
|
1423
|
-
!this.ahead([
|
|
1424
|
-
Token.IDENTIFIER(""),
|
|
1425
|
-
Token.LEFT_PARENTHESIS,
|
|
1426
|
-
Token.IDENTIFIER(""),
|
|
1427
|
-
Token.IN,
|
|
1428
|
-
])
|
|
1429
|
-
) {
|
|
1430
|
-
return null;
|
|
1431
|
-
}
|
|
1432
|
-
if (this.token.value === null) {
|
|
1433
|
-
throw new Error("Expected identifier");
|
|
1434
|
-
}
|
|
1435
|
-
const func = FunctionFactory.createPredicate(this.token.value);
|
|
1436
|
-
this.setNextToken();
|
|
1437
|
-
if (!this.token.isLeftParenthesis()) {
|
|
1438
|
-
throw new Error("Expected left parenthesis");
|
|
1439
|
-
}
|
|
1440
|
-
this.setNextToken();
|
|
1441
|
-
this.skipWhitespaceAndComments();
|
|
1442
|
-
if (!this.token.isIdentifier()) {
|
|
1443
|
-
throw new Error("Expected identifier");
|
|
1444
|
-
}
|
|
1445
|
-
const reference = new Reference(this.token.value);
|
|
1446
|
-
this._state.variables.set(reference.identifier, reference);
|
|
1447
|
-
func.addChild(reference);
|
|
1448
|
-
this.setNextToken();
|
|
1449
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1450
|
-
if (!this.token.isIn()) {
|
|
1451
|
-
throw new Error("Expected IN");
|
|
1452
|
-
}
|
|
1453
|
-
this.setNextToken();
|
|
1454
|
-
this.expectAndSkipWhitespaceAndComments();
|
|
1455
|
-
const expression = this.parseExpression();
|
|
1456
|
-
if (expression === null) {
|
|
1457
|
-
throw new Error("Expected expression");
|
|
1458
|
-
}
|
|
1459
|
-
if (
|
|
1460
|
-
!ObjectUtils.isInstanceOfAny(expression.firstChild(), [
|
|
1461
|
-
JSONArray,
|
|
1462
|
-
Reference,
|
|
1463
|
-
Lookup,
|
|
1464
|
-
Function,
|
|
1465
|
-
])
|
|
1466
|
-
) {
|
|
1467
|
-
throw new Error("Expected array or reference");
|
|
1468
|
-
}
|
|
1469
|
-
func.addChild(expression);
|
|
1470
|
-
this.skipWhitespaceAndComments();
|
|
1471
|
-
if (!this.token.isPipe()) {
|
|
1472
|
-
throw new Error("Expected pipe");
|
|
1473
|
-
}
|
|
1474
|
-
this.setNextToken();
|
|
1475
|
-
const _return = this.parseExpression();
|
|
1476
|
-
if (_return === null) {
|
|
1477
|
-
throw new Error("Expected expression");
|
|
1478
|
-
}
|
|
1479
|
-
func.addChild(_return);
|
|
1480
|
-
const where = this.parseWhere();
|
|
1481
|
-
if (where !== null) {
|
|
1482
|
-
func.addChild(where);
|
|
1483
|
-
}
|
|
1484
|
-
this.skipWhitespaceAndComments();
|
|
1485
|
-
if (!this.token.isRightParenthesis()) {
|
|
1486
|
-
throw new Error("Expected right parenthesis");
|
|
1487
|
-
}
|
|
1488
|
-
this.setNextToken();
|
|
1489
|
-
this._state.variables.delete(reference.identifier);
|
|
1490
|
-
return func;
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
private parseFString(): FString | null {
|
|
1494
|
-
if (!this.token.isFString()) {
|
|
1495
|
-
return null;
|
|
1496
|
-
}
|
|
1497
|
-
const f_string = new FString();
|
|
1498
|
-
while (this.token.isFString()) {
|
|
1499
|
-
if (this.token.value !== null) {
|
|
1500
|
-
f_string.addChild(new String(this.token.value));
|
|
1501
|
-
}
|
|
1502
|
-
this.setNextToken();
|
|
1503
|
-
if (this.token.isOpeningBrace()) {
|
|
1504
|
-
this.setNextToken();
|
|
1505
|
-
const expression = this.parseExpression();
|
|
1506
|
-
if (expression === null) {
|
|
1507
|
-
throw new Error("Expected expression");
|
|
1508
|
-
}
|
|
1509
|
-
f_string.addChild(expression);
|
|
1510
|
-
if (!this.token.isClosingBrace()) {
|
|
1511
|
-
throw new Error("Expected closing brace");
|
|
1512
|
-
}
|
|
1513
|
-
this.setNextToken();
|
|
1514
|
-
} else {
|
|
1515
|
-
break;
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
return f_string;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
private parseJSON(): AssociativeArray | JSONArray {
|
|
1522
|
-
if (this.token.isOpeningBrace()) {
|
|
1523
|
-
const array = this.parseAssociativeArray();
|
|
1524
|
-
if (array === null) {
|
|
1525
|
-
throw new Error("Expected associative array");
|
|
1526
|
-
}
|
|
1527
|
-
return array;
|
|
1528
|
-
} else if (this.token.isOpeningBracket()) {
|
|
1529
|
-
const array = this.parseJSONArray();
|
|
1530
|
-
if (array === null) {
|
|
1531
|
-
throw new Error("Expected JSON array");
|
|
1532
|
-
}
|
|
1533
|
-
return array;
|
|
1534
|
-
}
|
|
1535
|
-
throw new Error("Expected opening brace or bracket");
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
private parseAssociativeArray(): AssociativeArray | null {
|
|
1539
|
-
if (!this.token.isOpeningBrace()) {
|
|
1540
|
-
return null;
|
|
1541
|
-
}
|
|
1542
|
-
const array = new AssociativeArray();
|
|
1543
|
-
this.setNextToken();
|
|
1544
|
-
while (true) {
|
|
1545
|
-
this.skipWhitespaceAndComments();
|
|
1546
|
-
if (this.token.isClosingBrace()) {
|
|
1547
|
-
break;
|
|
1548
|
-
}
|
|
1549
|
-
if (!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
1550
|
-
throw new Error("Expected identifier");
|
|
1551
|
-
}
|
|
1552
|
-
const key = this.token.value;
|
|
1553
|
-
if (key === null) {
|
|
1554
|
-
throw new Error("Expected string");
|
|
1555
|
-
}
|
|
1556
|
-
this.setNextToken();
|
|
1557
|
-
this.skipWhitespaceAndComments();
|
|
1558
|
-
if (!this.token.isColon()) {
|
|
1559
|
-
throw new Error("Expected colon");
|
|
1560
|
-
}
|
|
1561
|
-
this.setNextToken();
|
|
1562
|
-
this.skipWhitespaceAndComments();
|
|
1563
|
-
const value = this.parseExpression();
|
|
1564
|
-
if (value === null) {
|
|
1565
|
-
throw new Error("Expected expression");
|
|
1566
|
-
}
|
|
1567
|
-
array.addKeyValue(new KeyValuePair(key, value));
|
|
1568
|
-
this.skipWhitespaceAndComments();
|
|
1569
|
-
if (this.token.isComma()) {
|
|
1570
|
-
this.setNextToken();
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
this.setNextToken();
|
|
1574
|
-
return array;
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
private parseJSONArray(): JSONArray | null {
|
|
1578
|
-
if (!this.token.isOpeningBracket()) {
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
const array = new JSONArray();
|
|
1582
|
-
this.setNextToken();
|
|
1583
|
-
while (true) {
|
|
1584
|
-
this.skipWhitespaceAndComments();
|
|
1585
|
-
if (this.token.isClosingBracket()) {
|
|
1586
|
-
break;
|
|
1587
|
-
}
|
|
1588
|
-
const value = this.parseExpression();
|
|
1589
|
-
if (value === null) {
|
|
1590
|
-
throw new Error("Expected expression");
|
|
1591
|
-
}
|
|
1592
|
-
array.addValue(value);
|
|
1593
|
-
this.skipWhitespaceAndComments();
|
|
1594
|
-
if (this.token.isComma()) {
|
|
1595
|
-
this.setNextToken();
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
this.setNextToken();
|
|
1599
|
-
return array;
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
private parseUnion(): Union | UnionAll | null {
|
|
1603
|
-
if (!this.token.isUnion()) {
|
|
1604
|
-
return null;
|
|
1605
|
-
}
|
|
1606
|
-
this.setNextToken();
|
|
1607
|
-
this.skipWhitespaceAndComments();
|
|
1608
|
-
let union: Union | UnionAll;
|
|
1609
|
-
if (this.token.isAll()) {
|
|
1610
|
-
union = new UnionAll();
|
|
1611
|
-
this.setNextToken();
|
|
1612
|
-
} else {
|
|
1613
|
-
union = new Union();
|
|
1614
|
-
}
|
|
1615
|
-
return union;
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
private expectAndSkipWhitespaceAndComments(): void {
|
|
1619
|
-
const skipped = this.skipWhitespaceAndComments();
|
|
1620
|
-
if (!skipped) {
|
|
1621
|
-
throw new Error("Expected whitespace or comment");
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
private skipWhitespaceAndComments(): boolean {
|
|
1626
|
-
let skipped: boolean = this.previousToken.isWhitespaceOrComment();
|
|
1627
|
-
while (this.token.isWhitespace() || this.token.isComment()) {
|
|
1628
|
-
this.setNextToken();
|
|
1629
|
-
skipped = true;
|
|
1630
|
-
}
|
|
1631
|
-
return skipped;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
private expectPreviousTokenToBeWhitespaceOrComment(): void {
|
|
1635
|
-
if (!this.previousToken.isWhitespaceOrComment()) {
|
|
1636
|
-
throw new Error("Expected whitespace or comment");
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
export default Parser;
|