flowquery 1.0.15 → 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.
Files changed (333) hide show
  1. package/.github/workflows/python-publish.yml +97 -0
  2. package/dist/compute/runner.d.ts +3 -2
  3. package/dist/compute/runner.d.ts.map +1 -1
  4. package/dist/compute/runner.js +7 -7
  5. package/dist/compute/runner.js.map +1 -1
  6. package/dist/flowquery.min.js +1 -1
  7. package/dist/graph/data.d.ts +31 -0
  8. package/dist/graph/data.d.ts.map +1 -0
  9. package/dist/graph/data.js +110 -0
  10. package/dist/graph/data.js.map +1 -0
  11. package/dist/graph/database.d.ts +20 -0
  12. package/dist/graph/database.d.ts.map +1 -0
  13. package/dist/graph/database.js +77 -0
  14. package/dist/graph/database.js.map +1 -0
  15. package/dist/graph/hops.d.ts +11 -0
  16. package/dist/graph/hops.d.ts.map +1 -0
  17. package/dist/graph/hops.js +25 -0
  18. package/dist/graph/hops.js.map +1 -0
  19. package/dist/graph/node.d.ts +35 -0
  20. package/dist/graph/node.d.ts.map +1 -0
  21. package/dist/graph/node.js +113 -0
  22. package/dist/graph/node.js.map +1 -0
  23. package/dist/graph/node_data.d.ts +11 -0
  24. package/dist/graph/node_data.d.ts.map +1 -0
  25. package/dist/graph/node_data.js +20 -0
  26. package/dist/graph/node_data.js.map +1 -0
  27. package/dist/graph/node_reference.d.ts +10 -0
  28. package/dist/graph/node_reference.d.ts.map +1 -0
  29. package/dist/graph/node_reference.js +52 -0
  30. package/dist/graph/node_reference.js.map +1 -0
  31. package/dist/graph/pattern.d.ts +18 -0
  32. package/dist/graph/pattern.d.ts.map +1 -0
  33. package/dist/graph/pattern.js +114 -0
  34. package/dist/graph/pattern.js.map +1 -0
  35. package/dist/graph/pattern_expression.d.ts +14 -0
  36. package/dist/graph/pattern_expression.d.ts.map +1 -0
  37. package/dist/graph/pattern_expression.js +58 -0
  38. package/dist/graph/pattern_expression.js.map +1 -0
  39. package/dist/graph/patterns.d.ts +11 -0
  40. package/dist/graph/patterns.d.ts.map +1 -0
  41. package/dist/graph/patterns.js +49 -0
  42. package/dist/graph/patterns.js.map +1 -0
  43. package/dist/graph/physical_node.d.ts +10 -0
  44. package/dist/graph/physical_node.d.ts.map +1 -0
  45. package/dist/graph/physical_node.js +40 -0
  46. package/dist/graph/physical_node.js.map +1 -0
  47. package/dist/graph/physical_relationship.d.ts +10 -0
  48. package/dist/graph/physical_relationship.d.ts.map +1 -0
  49. package/dist/graph/physical_relationship.js +40 -0
  50. package/dist/graph/physical_relationship.js.map +1 -0
  51. package/dist/graph/relationship.d.ts +40 -0
  52. package/dist/graph/relationship.d.ts.map +1 -0
  53. package/dist/graph/relationship.js +124 -0
  54. package/dist/graph/relationship.js.map +1 -0
  55. package/dist/graph/relationship_data.d.ts +12 -0
  56. package/dist/graph/relationship_data.d.ts.map +1 -0
  57. package/dist/graph/relationship_data.js +40 -0
  58. package/dist/graph/relationship_data.js.map +1 -0
  59. package/dist/graph/relationship_match_collector.d.ts +19 -0
  60. package/dist/graph/relationship_match_collector.d.ts.map +1 -0
  61. package/dist/graph/relationship_match_collector.js +55 -0
  62. package/dist/graph/relationship_match_collector.js.map +1 -0
  63. package/dist/graph/relationship_reference.d.ts +8 -0
  64. package/dist/graph/relationship_reference.d.ts.map +1 -0
  65. package/dist/graph/relationship_reference.js +37 -0
  66. package/dist/graph/relationship_reference.js.map +1 -0
  67. package/dist/parsing/base_parser.d.ts +1 -0
  68. package/dist/parsing/base_parser.d.ts.map +1 -1
  69. package/dist/parsing/base_parser.js +4 -1
  70. package/dist/parsing/base_parser.js.map +1 -1
  71. package/dist/parsing/context.d.ts +2 -2
  72. package/dist/parsing/context.js +5 -5
  73. package/dist/parsing/expressions/boolean.d.ts +8 -0
  74. package/dist/parsing/expressions/boolean.d.ts.map +1 -0
  75. package/dist/parsing/expressions/boolean.js +26 -0
  76. package/dist/parsing/expressions/boolean.js.map +1 -0
  77. package/dist/parsing/expressions/expression.d.ts +4 -1
  78. package/dist/parsing/expressions/expression.d.ts.map +1 -1
  79. package/dist/parsing/expressions/expression.js +15 -8
  80. package/dist/parsing/expressions/expression.js.map +1 -1
  81. package/dist/parsing/expressions/expression_map.d.ts +1 -0
  82. package/dist/parsing/expressions/expression_map.d.ts.map +1 -1
  83. package/dist/parsing/expressions/expression_map.js +3 -0
  84. package/dist/parsing/expressions/expression_map.js.map +1 -1
  85. package/dist/parsing/expressions/operator.d.ts +1 -1
  86. package/dist/parsing/expressions/operator.d.ts.map +1 -1
  87. package/dist/parsing/expressions/operator.js.map +1 -1
  88. package/dist/parsing/functions/function_factory.d.ts +13 -13
  89. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  90. package/dist/parsing/functions/function_factory.js +20 -18
  91. package/dist/parsing/functions/function_factory.js.map +1 -1
  92. package/dist/parsing/operations/call.d.ts.map +1 -1
  93. package/dist/parsing/operations/call.js +3 -1
  94. package/dist/parsing/operations/call.js.map +1 -1
  95. package/dist/parsing/operations/create_node.d.ts +14 -0
  96. package/dist/parsing/operations/create_node.d.ts.map +1 -0
  97. package/dist/parsing/operations/create_node.js +51 -0
  98. package/dist/parsing/operations/create_node.js.map +1 -0
  99. package/dist/parsing/operations/create_relationship.d.ts +14 -0
  100. package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
  101. package/dist/parsing/operations/create_relationship.js +51 -0
  102. package/dist/parsing/operations/create_relationship.js.map +1 -0
  103. package/dist/parsing/operations/match.d.ts +15 -0
  104. package/dist/parsing/operations/match.d.ts.map +1 -0
  105. package/dist/parsing/operations/match.js +45 -0
  106. package/dist/parsing/operations/match.js.map +1 -0
  107. package/dist/parsing/operations/operation.d.ts +1 -0
  108. package/dist/parsing/operations/operation.d.ts.map +1 -1
  109. package/dist/parsing/operations/operation.js +6 -0
  110. package/dist/parsing/operations/operation.js.map +1 -1
  111. package/dist/parsing/operations/return.d.ts +1 -0
  112. package/dist/parsing/operations/return.d.ts.map +1 -1
  113. package/dist/parsing/operations/return.js +7 -1
  114. package/dist/parsing/operations/return.js.map +1 -1
  115. package/dist/parsing/operations/where.d.ts +1 -1
  116. package/dist/parsing/operations/where.d.ts.map +1 -1
  117. package/dist/parsing/operations/where.js +4 -0
  118. package/dist/parsing/operations/where.js.map +1 -1
  119. package/dist/parsing/parser.d.ts +10 -0
  120. package/dist/parsing/parser.d.ts.map +1 -1
  121. package/dist/parsing/parser.js +344 -5
  122. package/dist/parsing/parser.js.map +1 -1
  123. package/dist/parsing/token_to_node.d.ts.map +1 -1
  124. package/dist/parsing/token_to_node.js +7 -0
  125. package/dist/parsing/token_to_node.js.map +1 -1
  126. package/dist/tokenization/keyword.d.ts +1 -0
  127. package/dist/tokenization/keyword.d.ts.map +1 -1
  128. package/dist/tokenization/keyword.js +1 -0
  129. package/dist/tokenization/keyword.js.map +1 -1
  130. package/dist/tokenization/token.d.ts +4 -0
  131. package/dist/tokenization/token.d.ts.map +1 -1
  132. package/dist/tokenization/token.js +14 -1
  133. package/dist/tokenization/token.js.map +1 -1
  134. package/dist/tokenization/token_type.d.ts +1 -0
  135. package/dist/tokenization/token_type.d.ts.map +1 -1
  136. package/dist/tokenization/token_type.js +1 -0
  137. package/dist/tokenization/token_type.js.map +1 -1
  138. package/dist/tokenization/tokenizer.d.ts +2 -1
  139. package/dist/tokenization/tokenizer.d.ts.map +1 -1
  140. package/dist/tokenization/tokenizer.js +25 -12
  141. package/dist/tokenization/tokenizer.js.map +1 -1
  142. package/docs/flowquery.min.js +1 -1
  143. package/flowquery-py/README.md +166 -0
  144. package/flowquery-py/pyproject.toml +75 -0
  145. package/flowquery-py/setup_env.ps1 +92 -0
  146. package/flowquery-py/setup_env.sh +87 -0
  147. package/flowquery-py/src/__init__.py +34 -0
  148. package/flowquery-py/src/__main__.py +10 -0
  149. package/flowquery-py/src/compute/__init__.py +5 -0
  150. package/flowquery-py/src/compute/runner.py +60 -0
  151. package/flowquery-py/src/extensibility.py +52 -0
  152. package/flowquery-py/src/graph/__init__.py +31 -0
  153. package/flowquery-py/src/graph/data.py +118 -0
  154. package/flowquery-py/src/graph/database.py +82 -0
  155. package/flowquery-py/src/graph/hops.py +43 -0
  156. package/flowquery-py/src/graph/node.py +112 -0
  157. package/flowquery-py/src/graph/node_data.py +26 -0
  158. package/flowquery-py/src/graph/node_reference.py +49 -0
  159. package/flowquery-py/src/graph/pattern.py +125 -0
  160. package/flowquery-py/src/graph/pattern_expression.py +62 -0
  161. package/flowquery-py/src/graph/patterns.py +42 -0
  162. package/flowquery-py/src/graph/physical_node.py +40 -0
  163. package/flowquery-py/src/graph/physical_relationship.py +36 -0
  164. package/flowquery-py/src/graph/relationship.py +135 -0
  165. package/flowquery-py/src/graph/relationship_data.py +33 -0
  166. package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
  167. package/flowquery-py/src/graph/relationship_reference.py +21 -0
  168. package/flowquery-py/src/io/__init__.py +5 -0
  169. package/flowquery-py/src/io/command_line.py +67 -0
  170. package/flowquery-py/src/parsing/__init__.py +17 -0
  171. package/flowquery-py/src/parsing/alias.py +20 -0
  172. package/flowquery-py/src/parsing/alias_option.py +11 -0
  173. package/flowquery-py/src/parsing/ast_node.py +146 -0
  174. package/flowquery-py/src/parsing/base_parser.py +84 -0
  175. package/flowquery-py/src/parsing/components/__init__.py +19 -0
  176. package/flowquery-py/src/parsing/components/csv.py +8 -0
  177. package/flowquery-py/src/parsing/components/from_.py +10 -0
  178. package/flowquery-py/src/parsing/components/headers.py +12 -0
  179. package/flowquery-py/src/parsing/components/json.py +8 -0
  180. package/flowquery-py/src/parsing/components/null.py +10 -0
  181. package/flowquery-py/src/parsing/components/post.py +8 -0
  182. package/flowquery-py/src/parsing/components/text.py +8 -0
  183. package/flowquery-py/src/parsing/context.py +50 -0
  184. package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
  185. package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
  186. package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
  187. package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
  188. package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
  189. package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
  190. package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
  191. package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
  192. package/flowquery-py/src/parsing/expressions/expression.py +138 -0
  193. package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
  194. package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
  195. package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
  196. package/flowquery-py/src/parsing/expressions/number.py +32 -0
  197. package/flowquery-py/src/parsing/expressions/operator.py +169 -0
  198. package/flowquery-py/src/parsing/expressions/reference.py +47 -0
  199. package/flowquery-py/src/parsing/expressions/string.py +27 -0
  200. package/flowquery-py/src/parsing/functions/__init__.py +75 -0
  201. package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
  202. package/flowquery-py/src/parsing/functions/async_function.py +62 -0
  203. package/flowquery-py/src/parsing/functions/avg.py +55 -0
  204. package/flowquery-py/src/parsing/functions/collect.py +75 -0
  205. package/flowquery-py/src/parsing/functions/function.py +68 -0
  206. package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
  207. package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
  208. package/flowquery-py/src/parsing/functions/functions.py +59 -0
  209. package/flowquery-py/src/parsing/functions/join.py +47 -0
  210. package/flowquery-py/src/parsing/functions/keys.py +34 -0
  211. package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
  212. package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
  213. package/flowquery-py/src/parsing/functions/rand.py +28 -0
  214. package/flowquery-py/src/parsing/functions/range_.py +34 -0
  215. package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
  216. package/flowquery-py/src/parsing/functions/replace.py +37 -0
  217. package/flowquery-py/src/parsing/functions/round_.py +32 -0
  218. package/flowquery-py/src/parsing/functions/size.py +32 -0
  219. package/flowquery-py/src/parsing/functions/split.py +47 -0
  220. package/flowquery-py/src/parsing/functions/stringify.py +47 -0
  221. package/flowquery-py/src/parsing/functions/sum.py +51 -0
  222. package/flowquery-py/src/parsing/functions/to_json.py +33 -0
  223. package/flowquery-py/src/parsing/functions/type_.py +47 -0
  224. package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
  225. package/flowquery-py/src/parsing/logic/__init__.py +15 -0
  226. package/flowquery-py/src/parsing/logic/case.py +29 -0
  227. package/flowquery-py/src/parsing/logic/else_.py +12 -0
  228. package/flowquery-py/src/parsing/logic/end.py +8 -0
  229. package/flowquery-py/src/parsing/logic/then.py +12 -0
  230. package/flowquery-py/src/parsing/logic/when.py +10 -0
  231. package/flowquery-py/src/parsing/operations/__init__.py +35 -0
  232. package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
  233. package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
  234. package/flowquery-py/src/parsing/operations/call.py +74 -0
  235. package/flowquery-py/src/parsing/operations/create_node.py +34 -0
  236. package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
  237. package/flowquery-py/src/parsing/operations/group_by.py +130 -0
  238. package/flowquery-py/src/parsing/operations/limit.py +22 -0
  239. package/flowquery-py/src/parsing/operations/load.py +140 -0
  240. package/flowquery-py/src/parsing/operations/match.py +29 -0
  241. package/flowquery-py/src/parsing/operations/operation.py +69 -0
  242. package/flowquery-py/src/parsing/operations/projection.py +21 -0
  243. package/flowquery-py/src/parsing/operations/return_op.py +50 -0
  244. package/flowquery-py/src/parsing/operations/unwind.py +37 -0
  245. package/flowquery-py/src/parsing/operations/where.py +41 -0
  246. package/flowquery-py/src/parsing/operations/with_op.py +18 -0
  247. package/flowquery-py/src/parsing/parser.py +1011 -0
  248. package/flowquery-py/src/parsing/token_to_node.py +109 -0
  249. package/flowquery-py/src/tokenization/__init__.py +23 -0
  250. package/flowquery-py/src/tokenization/keyword.py +48 -0
  251. package/flowquery-py/src/tokenization/operator.py +29 -0
  252. package/flowquery-py/src/tokenization/string_walker.py +158 -0
  253. package/flowquery-py/src/tokenization/symbol.py +19 -0
  254. package/flowquery-py/src/tokenization/token.py +659 -0
  255. package/flowquery-py/src/tokenization/token_mapper.py +52 -0
  256. package/flowquery-py/src/tokenization/token_type.py +21 -0
  257. package/flowquery-py/src/tokenization/tokenizer.py +214 -0
  258. package/flowquery-py/src/tokenization/trie.py +124 -0
  259. package/flowquery-py/src/utils/__init__.py +6 -0
  260. package/flowquery-py/src/utils/object_utils.py +20 -0
  261. package/flowquery-py/src/utils/string_utils.py +113 -0
  262. package/flowquery-py/tests/__init__.py +1 -0
  263. package/flowquery-py/tests/compute/__init__.py +1 -0
  264. package/flowquery-py/tests/compute/test_runner.py +1335 -0
  265. package/flowquery-py/tests/graph/__init__.py +1 -0
  266. package/flowquery-py/tests/graph/test_create.py +56 -0
  267. package/flowquery-py/tests/graph/test_data.py +73 -0
  268. package/flowquery-py/tests/graph/test_match.py +40 -0
  269. package/flowquery-py/tests/parsing/__init__.py +1 -0
  270. package/flowquery-py/tests/parsing/test_context.py +34 -0
  271. package/flowquery-py/tests/parsing/test_expression.py +49 -0
  272. package/flowquery-py/tests/parsing/test_parser.py +674 -0
  273. package/flowquery-py/tests/test_extensibility.py +611 -0
  274. package/flowquery-py/tests/tokenization/__init__.py +1 -0
  275. package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
  276. package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
  277. package/flowquery-py/tests/tokenization/test_trie.py +30 -0
  278. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  279. package/misc/apps/RAG/package.json +1 -1
  280. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
  281. package/misc/apps/RAG/src/components/index.ts +19 -10
  282. package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +21 -26
  283. package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +24 -25
  284. package/misc/apps/RAG/src/plugins/loaders/Form.ts +163 -147
  285. package/misc/apps/RAG/src/plugins/loaders/Llm.ts +103 -90
  286. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +80 -130
  287. package/misc/apps/RAG/src/plugins/loaders/Table.ts +104 -101
  288. package/misc/apps/RAG/src/plugins/loaders/Weather.ts +47 -36
  289. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +89 -78
  290. package/package.json +1 -1
  291. package/src/compute/runner.ts +24 -19
  292. package/src/graph/data.ts +112 -0
  293. package/src/graph/database.ts +63 -0
  294. package/src/graph/hops.ts +22 -0
  295. package/src/graph/node.ts +99 -0
  296. package/src/graph/node_data.ts +18 -0
  297. package/src/graph/node_reference.ts +33 -0
  298. package/src/graph/pattern.ts +101 -0
  299. package/src/graph/pattern_expression.ts +37 -0
  300. package/src/graph/patterns.ts +36 -0
  301. package/src/graph/physical_node.ts +23 -0
  302. package/src/graph/physical_relationship.ts +23 -0
  303. package/src/graph/relationship.ts +116 -0
  304. package/src/graph/relationship_data.ts +27 -0
  305. package/src/graph/relationship_match_collector.ts +58 -0
  306. package/src/graph/relationship_reference.ts +24 -0
  307. package/src/parsing/base_parser.ts +20 -14
  308. package/src/parsing/context.ts +14 -14
  309. package/src/parsing/expressions/boolean.ts +21 -0
  310. package/src/parsing/expressions/expression.ts +34 -26
  311. package/src/parsing/expressions/expression_map.ts +3 -0
  312. package/src/parsing/expressions/operator.ts +19 -1
  313. package/src/parsing/functions/function_factory.ts +45 -45
  314. package/src/parsing/operations/call.ts +3 -1
  315. package/src/parsing/operations/create_node.ts +39 -0
  316. package/src/parsing/operations/create_relationship.ts +38 -0
  317. package/src/parsing/operations/match.ts +31 -0
  318. package/src/parsing/operations/operation.ts +3 -0
  319. package/src/parsing/operations/return.ts +11 -7
  320. package/src/parsing/operations/where.ts +10 -6
  321. package/src/parsing/parser.ts +346 -8
  322. package/src/parsing/token_to_node.ts +6 -0
  323. package/src/tokenization/keyword.ts +41 -40
  324. package/src/tokenization/token.ts +21 -1
  325. package/src/tokenization/token_type.ts +2 -1
  326. package/src/tokenization/tokenizer.ts +52 -31
  327. package/tests/compute/runner.test.ts +660 -6
  328. package/tests/extensibility.test.ts +97 -93
  329. package/tests/graph/create.test.ts +36 -0
  330. package/tests/graph/data.test.ts +58 -0
  331. package/tests/graph/match.test.ts +29 -0
  332. package/tests/parsing/parser.test.ts +276 -8
  333. package/tests/tokenization/tokenizer.test.ts +107 -17
@@ -0,0 +1,674 @@
1
+ """Tests for the FlowQuery parser."""
2
+
3
+ import pytest
4
+ from typing import AsyncIterator
5
+ from flowquery.parsing.parser import Parser
6
+ from flowquery.parsing.functions.async_function import AsyncFunction
7
+ from flowquery.parsing.functions.function_metadata import FunctionDef
8
+
9
+
10
+ # Test async function for CALL operation parsing test
11
+ # Named with underscore prefix to prevent pytest from trying to collect it as a test class
12
+ @FunctionDef({
13
+ "description": "Asynchronous function for testing CALL operation",
14
+ "category": "async",
15
+ "parameters": [],
16
+ "output": {"description": "Yields test values", "type": "any"},
17
+ })
18
+ class _Test(AsyncFunction):
19
+ """Async function for CALL operation testing, registered as 'test'."""
20
+
21
+ def __init__(self):
22
+ super().__init__("test") # Register as 'test'
23
+ self._expected_parameter_count = 0
24
+
25
+ async def generate(self) -> AsyncIterator:
26
+ yield 1
27
+ yield 2
28
+ yield 3
29
+
30
+
31
+ class TestParser:
32
+ """Test cases for the Parser class."""
33
+
34
+ def test_parser_basic(self):
35
+ """Test basic parser functionality."""
36
+ parser = Parser()
37
+ ast = parser.parse("RETURN 1, 2, 3")
38
+ expected = (
39
+ "ASTNode\n"
40
+ "- Return\n"
41
+ "-- Expression\n"
42
+ "--- Number (1)\n"
43
+ "-- Expression\n"
44
+ "--- Number (2)\n"
45
+ "-- Expression\n"
46
+ "--- Number (3)"
47
+ )
48
+ assert ast.print() == expected
49
+
50
+ def test_parser_with_function(self):
51
+ """Test parser with function."""
52
+ parser = Parser()
53
+ ast = parser.parse("RETURN rand()")
54
+ expected = (
55
+ "ASTNode\n"
56
+ "- Return\n"
57
+ "-- Expression\n"
58
+ "--- Function (rand)"
59
+ )
60
+ assert ast.print() == expected
61
+
62
+ def test_parser_with_associative_array(self):
63
+ """Test parser with associative array."""
64
+ parser = Parser()
65
+ ast = parser.parse("RETURN {a: 1, b: 2}")
66
+ expected = (
67
+ "ASTNode\n"
68
+ "- Return\n"
69
+ "-- Expression\n"
70
+ "--- AssociativeArray\n"
71
+ "---- KeyValuePair\n"
72
+ "----- String (a)\n"
73
+ "----- Expression\n"
74
+ "------ Number (1)\n"
75
+ "---- KeyValuePair\n"
76
+ "----- String (b)\n"
77
+ "----- Expression\n"
78
+ "------ Number (2)"
79
+ )
80
+ assert ast.print() == expected
81
+
82
+ def test_parser_with_json_array(self):
83
+ """Test parser with JSON array."""
84
+ parser = Parser()
85
+ ast = parser.parse("RETURN [1, 2]")
86
+ expected = (
87
+ "ASTNode\n"
88
+ "- Return\n"
89
+ "-- Expression\n"
90
+ "--- JSONArray\n"
91
+ "---- Expression\n"
92
+ "----- Number (1)\n"
93
+ "---- Expression\n"
94
+ "----- Number (2)"
95
+ )
96
+ assert ast.print() == expected
97
+
98
+ def test_parser_with_nested_associative_array(self):
99
+ """Test parser with nested associative array."""
100
+ parser = Parser()
101
+ ast = parser.parse("RETURN {a:{}}")
102
+ expected = (
103
+ "ASTNode\n"
104
+ "- Return\n"
105
+ "-- Expression\n"
106
+ "--- AssociativeArray\n"
107
+ "---- KeyValuePair\n"
108
+ "----- String (a)\n"
109
+ "----- Expression\n"
110
+ "------ AssociativeArray"
111
+ )
112
+ assert ast.print() == expected
113
+
114
+ def test_parser_with_multiple_operations(self):
115
+ """Test parser with multiple operations."""
116
+ parser = Parser()
117
+ ast = parser.parse("WITH 1 AS n RETURN n")
118
+ expected = (
119
+ "ASTNode\n"
120
+ "- With\n"
121
+ "-- Expression (n)\n"
122
+ "--- Number (1)\n"
123
+ "- Return\n"
124
+ "-- Expression (n)\n"
125
+ "--- Reference (n)"
126
+ )
127
+ assert ast.print() == expected
128
+
129
+ def test_parser_with_comments(self):
130
+ """Test parser with comments."""
131
+ parser = Parser()
132
+ ast = parser.parse("WITH 1 AS n /* comment */ RETURN n")
133
+ expected = (
134
+ "ASTNode\n"
135
+ "- With\n"
136
+ "-- Expression (n)\n"
137
+ "--- Number (1)\n"
138
+ "- Return\n"
139
+ "-- Expression (n)\n"
140
+ "--- Reference (n)"
141
+ )
142
+ assert ast.print() == expected
143
+
144
+ def test_parser_with_unwind(self):
145
+ """Test parser with UNWIND."""
146
+ parser = Parser()
147
+ ast = parser.parse("UNWIND [1, 2, 3] AS n RETURN n")
148
+ expected = (
149
+ "ASTNode\n"
150
+ "- Unwind\n"
151
+ "-- Expression (n)\n"
152
+ "--- JSONArray\n"
153
+ "---- Expression\n"
154
+ "----- Number (1)\n"
155
+ "---- Expression\n"
156
+ "----- Number (2)\n"
157
+ "---- Expression\n"
158
+ "----- Number (3)\n"
159
+ "- Return\n"
160
+ "-- Expression (n)\n"
161
+ "--- Reference (n)"
162
+ )
163
+ assert ast.print() == expected
164
+
165
+ def test_unwind_with_invalid_expression(self):
166
+ """Test Unwind with invalid expression."""
167
+ parser = Parser()
168
+ with pytest.raises(Exception, match="Expected array, function, reference, or lookup"):
169
+ parser.parse("UNWIND 1 AS n RETURN n")
170
+
171
+ def test_unwind_with_invalid_alias(self):
172
+ """Test Unwind with invalid alias."""
173
+ parser = Parser()
174
+ with pytest.raises(Exception, match="Expected identifier"):
175
+ parser.parse("UNWIND [1, 2, 3] AS 1 RETURN n")
176
+
177
+ def test_unwind_with_missing_alias(self):
178
+ """Test Unwind with missing alias."""
179
+ parser = Parser()
180
+ with pytest.raises(Exception, match="Expected alias"):
181
+ parser.parse("UNWIND [1, 2, 3] RETURN n")
182
+
183
+ def test_statement_with_where_clause(self):
184
+ """Test statement with where clause."""
185
+ parser = Parser()
186
+ ast = parser.parse("with 1 as n where n > 0 return n")
187
+ expected = (
188
+ "ASTNode\n"
189
+ "- With\n"
190
+ "-- Expression (n)\n"
191
+ "--- Number (1)\n"
192
+ "- Where\n"
193
+ "-- Expression\n"
194
+ "--- GreaterThan\n"
195
+ "---- Reference (n)\n"
196
+ "---- Number (0)\n"
197
+ "- Return\n"
198
+ "-- Expression (n)\n"
199
+ "--- Reference (n)"
200
+ )
201
+ assert ast.print() == expected
202
+
203
+ def test_lookup(self):
204
+ """Test lookup expression."""
205
+ parser = Parser()
206
+ ast = parser.parse("return {a: 1}.a")
207
+ expected = (
208
+ "ASTNode\n"
209
+ "- Return\n"
210
+ "-- Expression\n"
211
+ "--- Lookup\n"
212
+ "---- Identifier (a)\n"
213
+ "---- AssociativeArray\n"
214
+ "----- KeyValuePair\n"
215
+ "------ String (a)\n"
216
+ "------ Expression\n"
217
+ "------- Number (1)"
218
+ )
219
+ assert ast.print() == expected
220
+
221
+ def test_lookup_as_part_of_expression(self):
222
+ """Test lookup as part of expression."""
223
+ parser = Parser()
224
+ ast = parser.parse("return {a: 1}.a + 1")
225
+ expected = (
226
+ "ASTNode\n"
227
+ "- Return\n"
228
+ "-- Expression\n"
229
+ "--- Add\n"
230
+ "---- Lookup\n"
231
+ "----- Identifier (a)\n"
232
+ "----- AssociativeArray\n"
233
+ "------ KeyValuePair\n"
234
+ "------- String (a)\n"
235
+ "------- Expression\n"
236
+ "-------- Number (1)\n"
237
+ "---- Number (1)"
238
+ )
239
+ assert ast.print() == expected
240
+
241
+ def test_lookup_with_nested_associative_array(self):
242
+ """Test lookup with nested associative array."""
243
+ parser = Parser()
244
+ ast = parser.parse("return {a: {b: 1}}.a.b")
245
+ expected = (
246
+ "ASTNode\n"
247
+ "- Return\n"
248
+ "-- Expression\n"
249
+ "--- Lookup\n"
250
+ "---- Identifier (b)\n"
251
+ "---- Lookup\n"
252
+ "----- Identifier (a)\n"
253
+ "----- AssociativeArray\n"
254
+ "------ KeyValuePair\n"
255
+ "------- String (a)\n"
256
+ "------- Expression\n"
257
+ "-------- AssociativeArray\n"
258
+ "--------- KeyValuePair\n"
259
+ "---------- String (b)\n"
260
+ "---------- Expression\n"
261
+ "----------- Number (1)"
262
+ )
263
+ assert ast.print() == expected
264
+ _return = ast.first_child()
265
+ assert _return.first_child().value() == 1
266
+
267
+ def test_lookup_with_json_array(self):
268
+ """Test lookup with JSON array."""
269
+ parser = Parser()
270
+ ast = parser.parse("return [1, 2][1]")
271
+ expected = (
272
+ "ASTNode\n"
273
+ "- Return\n"
274
+ "-- Expression\n"
275
+ "--- Lookup\n"
276
+ "---- Expression\n"
277
+ "----- Number (1)\n"
278
+ "---- JSONArray\n"
279
+ "----- Expression\n"
280
+ "------ Number (1)\n"
281
+ "----- Expression\n"
282
+ "------ Number (2)"
283
+ )
284
+ assert ast.print() == expected
285
+ _return = ast.first_child()
286
+ assert _return.first_child().value() == 2
287
+
288
+ def test_load_with_post(self):
289
+ """Test load with post."""
290
+ parser = Parser()
291
+ ast = parser.parse(
292
+ 'load json from "https://jsonplaceholder.typicode.com/posts" post {userId: 1} as data return data'
293
+ )
294
+ expected = (
295
+ "ASTNode\n"
296
+ "- Load\n"
297
+ "-- JSON\n"
298
+ "-- From\n"
299
+ "--- Expression\n"
300
+ "---- String (https://jsonplaceholder.typicode.com/posts)\n"
301
+ "-- Post\n"
302
+ "--- Expression\n"
303
+ "---- AssociativeArray\n"
304
+ "----- KeyValuePair\n"
305
+ "------ String (userId)\n"
306
+ "------ Expression\n"
307
+ "------- Number (1)\n"
308
+ "-- Alias (data)\n"
309
+ "- Return\n"
310
+ "-- Expression (data)\n"
311
+ "--- Reference (data)"
312
+ )
313
+ assert ast.print() == expected
314
+
315
+ def test_nested_aggregate_functions(self):
316
+ """Test nested aggregate functions."""
317
+ parser = Parser()
318
+ with pytest.raises(Exception, match="Aggregate functions cannot be nested"):
319
+ parser.parse("RETURN sum(sum(1))")
320
+
321
+ def test_with_and_return_with_renamed_variable(self):
322
+ """Test with and return with renamed variable."""
323
+ parser = Parser()
324
+ ast = parser.parse("WITH 1 AS n RETURN n AS m")
325
+ expected = (
326
+ "ASTNode\n"
327
+ "- With\n"
328
+ "-- Expression (n)\n"
329
+ "--- Number (1)\n"
330
+ "- Return\n"
331
+ "-- Expression (m)\n"
332
+ "--- Reference (n)"
333
+ )
334
+ assert ast.print() == expected
335
+
336
+ def test_with_and_return_with_variable_lookup(self):
337
+ """Test with and return with variable lookup."""
338
+ parser = Parser()
339
+ ast = parser.parse("WITH {a: n} AS obj RETURN obj.a")
340
+ expected = (
341
+ "ASTNode\n"
342
+ "- With\n"
343
+ "-- Expression (obj)\n"
344
+ "--- AssociativeArray\n"
345
+ "---- KeyValuePair\n"
346
+ "----- String (a)\n"
347
+ "----- Expression\n"
348
+ "------ Reference (n)\n"
349
+ "- Return\n"
350
+ "-- Expression\n"
351
+ "--- Lookup\n"
352
+ "---- Identifier (a)\n"
353
+ "---- Reference (obj)"
354
+ )
355
+ assert ast.print() == expected
356
+
357
+ def test_unwind(self):
358
+ """Test unwind."""
359
+ parser = Parser()
360
+ ast = parser.parse("WITH [1, 2, 4] as n unwind n as i return i")
361
+ expected = (
362
+ "ASTNode\n"
363
+ "- With\n"
364
+ "-- Expression (n)\n"
365
+ "--- JSONArray\n"
366
+ "---- Expression\n"
367
+ "----- Number (1)\n"
368
+ "---- Expression\n"
369
+ "----- Number (2)\n"
370
+ "---- Expression\n"
371
+ "----- Number (4)\n"
372
+ "- Unwind\n"
373
+ "-- Expression (i)\n"
374
+ "--- Reference (n)\n"
375
+ "- Return\n"
376
+ "-- Expression (i)\n"
377
+ "--- Reference (i)"
378
+ )
379
+ assert ast.print() == expected
380
+
381
+ def test_predicate_function(self):
382
+ """Test predicate function."""
383
+ parser = Parser()
384
+ ast = parser.parse("RETURN sum(n in [1, 2, 3] | n where n > 1)")
385
+ expected = (
386
+ "ASTNode\n"
387
+ "- Return\n"
388
+ "-- Expression\n"
389
+ "--- PredicateFunction (sum)\n"
390
+ "---- Reference (n)\n"
391
+ "---- Expression\n"
392
+ "----- JSONArray\n"
393
+ "------ Expression\n"
394
+ "------- Number (1)\n"
395
+ "------ Expression\n"
396
+ "------- Number (2)\n"
397
+ "------ Expression\n"
398
+ "------- Number (3)\n"
399
+ "---- Expression\n"
400
+ "----- Reference (n)\n"
401
+ "---- Where\n"
402
+ "----- Expression\n"
403
+ "------ GreaterThan\n"
404
+ "------- Reference (n)\n"
405
+ "------- Number (1)"
406
+ )
407
+ assert ast.print() == expected
408
+
409
+ def test_case_statement(self):
410
+ """Test case statement."""
411
+ parser = Parser()
412
+ ast = parser.parse("RETURN CASE WHEN 1 THEN 2 ELSE 3 END")
413
+ expected = (
414
+ "ASTNode\n"
415
+ "- Return\n"
416
+ "-- Expression\n"
417
+ "--- Case\n"
418
+ "---- When\n"
419
+ "----- Expression\n"
420
+ "------ Number (1)\n"
421
+ "---- Then\n"
422
+ "----- Expression\n"
423
+ "------ Number (2)\n"
424
+ "---- Else\n"
425
+ "----- Expression\n"
426
+ "------ Number (3)"
427
+ )
428
+ assert ast.print() == expected
429
+
430
+ def test_functions_with_wrong_number_of_arguments(self):
431
+ """Test functions with wrong number of arguments."""
432
+ parser = Parser()
433
+ with pytest.raises(Exception, match="Function range expected 2 parameters, but got 1"):
434
+ parser.parse("RETURN range(1)")
435
+ with pytest.raises(Exception, match="Function range expected 2 parameters, but got 3"):
436
+ parser.parse("RETURN range(1, 2, 3)")
437
+ with pytest.raises(Exception, match="Function avg expected 1 parameters, but got 3"):
438
+ parser.parse("RETURN avg(1, 2, 3)")
439
+ with pytest.raises(Exception, match="Function size expected 1 parameters, but got 2"):
440
+ parser.parse("RETURN size(1, 2)")
441
+ with pytest.raises(Exception, match="Function round expected 1 parameters, but got 2"):
442
+ parser.parse("RETURN round(1, 2)")
443
+
444
+ def test_non_well_formed_statements(self):
445
+ """Test non-well formed statements."""
446
+ parser = Parser()
447
+ with pytest.raises(Exception, match="Only one RETURN statement is allowed"):
448
+ parser.parse("return 1 return 1")
449
+ # Note: Python implementation throws "Only one RETURN" for this case too
450
+ with pytest.raises(Exception, match="Only one RETURN statement is allowed"):
451
+ parser.parse("return 1 with 1 as n")
452
+
453
+ def test_associative_array_with_backtick_string(self):
454
+ """Test associative array with backtick string."""
455
+ parser = Parser()
456
+ ast = parser.parse("RETURN {`key`: `value`}")
457
+ expected = (
458
+ "ASTNode\n"
459
+ "- Return\n"
460
+ "-- Expression\n"
461
+ "--- AssociativeArray\n"
462
+ "---- KeyValuePair\n"
463
+ "----- String (key)\n"
464
+ "----- Expression\n"
465
+ "------ Reference (value)"
466
+ )
467
+ assert ast.print() == expected
468
+
469
+ def test_limit(self):
470
+ """Test limit."""
471
+ parser = Parser()
472
+ ast = parser.parse("unwind range(1, 10) as n limit 5 return n")
473
+ expected = (
474
+ "ASTNode\n"
475
+ "- Unwind\n"
476
+ "-- Expression (n)\n"
477
+ "--- Function (range)\n"
478
+ "---- Expression\n"
479
+ "----- Number (1)\n"
480
+ "---- Expression\n"
481
+ "----- Number (10)\n"
482
+ "- Limit\n"
483
+ "- Return\n"
484
+ "-- Expression (n)\n"
485
+ "--- Reference (n)"
486
+ )
487
+ assert ast.print() == expected
488
+
489
+ def test_return_negative_number(self):
490
+ """Test return -2."""
491
+ parser = Parser()
492
+ ast = parser.parse("return -2")
493
+ expected = (
494
+ "ASTNode\n"
495
+ "- Return\n"
496
+ "-- Expression\n"
497
+ "--- Number (-2)"
498
+ )
499
+ assert ast.print() == expected
500
+
501
+ def test_call_operation(self):
502
+ """Test call operation."""
503
+ parser = Parser()
504
+ ast = parser.parse("CALL test() YIELD result RETURN result")
505
+ expected = (
506
+ "ASTNode\n"
507
+ "- Call\n"
508
+ "-- Expression (result)\n"
509
+ "--- Reference (result)\n"
510
+ "- Return\n"
511
+ "-- Expression (result)\n"
512
+ "--- Reference (result)"
513
+ )
514
+ assert ast.print() == expected
515
+
516
+ def test_f_string(self):
517
+ """Test f-string."""
518
+ parser = Parser()
519
+ ast = parser.parse("with 1 as value RETURN f'Value is: {value}.'")
520
+ expected = (
521
+ "ASTNode\n"
522
+ "- With\n"
523
+ "-- Expression (value)\n"
524
+ "--- Number (1)\n"
525
+ "- Return\n"
526
+ "-- Expression\n"
527
+ "--- FString\n"
528
+ "---- String (Value is: )\n"
529
+ "---- Expression\n"
530
+ "----- Reference (value)\n"
531
+ "---- String (.)"
532
+ )
533
+ assert ast.print() == expected
534
+
535
+ def test_not_equal_operator(self):
536
+ """Test not equal operator."""
537
+ parser = Parser()
538
+ ast = parser.parse("RETURN 1 <> 2")
539
+ expected = (
540
+ "ASTNode\n"
541
+ "- Return\n"
542
+ "-- Expression\n"
543
+ "--- NotEquals\n"
544
+ "---- Number (1)\n"
545
+ "---- Number (2)"
546
+ )
547
+ assert ast.print() == expected
548
+
549
+ def test_equal_operator(self):
550
+ """Test equal operator."""
551
+ parser = Parser()
552
+ ast = parser.parse("RETURN 1 = 2")
553
+ expected = (
554
+ "ASTNode\n"
555
+ "- Return\n"
556
+ "-- Expression\n"
557
+ "--- Equals\n"
558
+ "---- Number (1)\n"
559
+ "---- Number (2)"
560
+ )
561
+ assert ast.print() == expected
562
+
563
+ def test_not_operator(self):
564
+ """Test not operator."""
565
+ parser = Parser()
566
+ ast = parser.parse("RETURN NOT true")
567
+ expected = (
568
+ "ASTNode\n"
569
+ "- Return\n"
570
+ "-- Expression\n"
571
+ "--- Not\n"
572
+ "---- Expression\n"
573
+ "----- Boolean"
574
+ )
575
+ assert ast.print() == expected
576
+
577
+ def test_create_node_operation(self):
578
+ """Test create node operation."""
579
+ parser = Parser()
580
+ ast = parser.parse(
581
+ """
582
+ CREATE VIRTUAL (:Person) AS {
583
+ unwind range(1, 3) AS id
584
+ return id, f'Person {id}' AS name
585
+ }
586
+ """
587
+ )
588
+ expected = (
589
+ "ASTNode\n"
590
+ "- CreateNode"
591
+ )
592
+ assert ast.print() == expected
593
+
594
+ def test_match_operation(self):
595
+ """Test match operation."""
596
+ parser = Parser()
597
+ ast = parser.parse("MATCH (n:Person) RETURN n")
598
+ expected = (
599
+ "ASTNode\n"
600
+ "- Match\n"
601
+ "- Return\n"
602
+ "-- Expression (n)\n"
603
+ "--- Reference (n)"
604
+ )
605
+ assert ast.print() == expected
606
+
607
+ def test_create_relationship_operation(self):
608
+ """Test create relationship operation."""
609
+ parser = Parser()
610
+ ast = parser.parse(
611
+ """
612
+ CREATE VIRTUAL (:Person)-[:KNOWS]-(:Person) AS {
613
+ unwind [
614
+ {from_id: 1, to_id: 2, since: '2020-01-01'},
615
+ {from_id: 2, to_id: 3, since: '2021-01-01'}
616
+ ] AS pair
617
+ return pair.from_id AS left_id, pair.to_id AS right_id
618
+ }
619
+ """
620
+ )
621
+ expected = (
622
+ "ASTNode\n"
623
+ "- CreateRelationship"
624
+ )
625
+ assert ast.print() == expected
626
+
627
+ def test_match_with_graph_pattern_including_relationships(self):
628
+ """Test match with graph pattern including relationships."""
629
+ parser = Parser()
630
+ ast = parser.parse("MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a, b")
631
+ expected = (
632
+ "ASTNode\n"
633
+ "- Match\n"
634
+ "- Return\n"
635
+ "-- Expression (a)\n"
636
+ "--- Reference (a)\n"
637
+ "-- Expression (b)\n"
638
+ "--- Reference (b)"
639
+ )
640
+ assert ast.print() == expected
641
+
642
+ def test_parse_relationship_with_hops(self):
643
+ """Test parse relationship with hops."""
644
+ parser = Parser()
645
+ ast = parser.parse("MATCH (a:Test)-[:KNOWS*1..3]->(b:Test) RETURN a, b")
646
+ expected = (
647
+ "ASTNode\n"
648
+ "- Match\n"
649
+ "- Return\n"
650
+ "-- Expression (a)\n"
651
+ "--- Reference (a)\n"
652
+ "-- Expression (b)\n"
653
+ "--- Reference (b)"
654
+ )
655
+ assert ast.print() == expected
656
+
657
+ def test_parse_statement_with_graph_pattern_in_where_clause(self):
658
+ """Test parse statement with graph pattern in where clause."""
659
+ parser = Parser()
660
+ ast = parser.parse("MATCH (a:Person) WHERE (a)-[:KNOWS]->(:Person) RETURN a")
661
+ expected = (
662
+ "ASTNode\n"
663
+ "- Match\n"
664
+ "- Where\n"
665
+ "-- Expression\n"
666
+ "--- PatternExpression\n"
667
+ "---- NodeReference\n"
668
+ "---- Relationship\n"
669
+ "---- Node\n"
670
+ "- Return\n"
671
+ "-- Expression (a)\n"
672
+ "--- Reference (a)"
673
+ )
674
+ assert ast.print() == expected