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,1011 @@
1
+ """Main parser for FlowQuery statements."""
2
+
3
+ from typing import Dict, Iterator, List, Optional
4
+
5
+ from ..tokenization.token import Token
6
+ from ..utils.object_utils import ObjectUtils
7
+ from .alias import Alias
8
+ from .alias_option import AliasOption
9
+ from .ast_node import ASTNode
10
+ from .base_parser import BaseParser
11
+ from .context import Context
12
+ from .components.from_ import From
13
+ from .components.headers import Headers
14
+ from .components.null import Null
15
+ from .components.post import Post
16
+ from .data_structures.associative_array import AssociativeArray
17
+ from .data_structures.json_array import JSONArray
18
+ from .data_structures.key_value_pair import KeyValuePair
19
+ from .data_structures.lookup import Lookup
20
+ from .data_structures.range_lookup import RangeLookup
21
+ from .expressions.expression import Expression
22
+ from .expressions.f_string import FString
23
+ from .expressions.identifier import Identifier
24
+ from .expressions.operator import Not
25
+ from .expressions.reference import Reference
26
+ from .expressions.string import String
27
+ from .functions.aggregate_function import AggregateFunction
28
+ from .functions.async_function import AsyncFunction
29
+ from .functions.function import Function
30
+ from .functions.function_factory import FunctionFactory
31
+ from .functions.predicate_function import PredicateFunction
32
+ from .logic.case import Case
33
+ from .logic.when import When
34
+ from .logic.then import Then
35
+ from .logic.else_ import Else
36
+ from .operations.aggregated_return import AggregatedReturn
37
+ from .operations.aggregated_with import AggregatedWith
38
+ from .operations.call import Call
39
+ from .operations.limit import Limit
40
+ from .operations.load import Load
41
+ from .operations.match import Match
42
+ from .operations.operation import Operation
43
+ from .operations.return_op import Return
44
+ from .operations.unwind import Unwind
45
+ from .operations.where import Where
46
+ from .operations.with_op import With
47
+ from ..graph.node import Node
48
+ from ..graph.node_reference import NodeReference
49
+ from ..graph.pattern import Pattern
50
+ from ..graph.pattern_expression import PatternExpression
51
+ from ..graph.relationship import Relationship
52
+ from .operations.create_node import CreateNode
53
+ from .operations.create_relationship import CreateRelationship
54
+
55
+
56
+ class Parser(BaseParser):
57
+ """Main parser for FlowQuery statements.
58
+
59
+ Parses FlowQuery declarative query language statements into an Abstract Syntax Tree (AST).
60
+ Supports operations like WITH, UNWIND, RETURN, LOAD, WHERE, and LIMIT, along with
61
+ expressions, functions, data structures, and logical constructs.
62
+
63
+ Example:
64
+ parser = Parser()
65
+ ast = parser.parse("unwind [1, 2, 3, 4, 5] as num return num")
66
+ """
67
+
68
+ def __init__(self, tokens: Optional[List[Token]] = None):
69
+ super().__init__(tokens)
70
+ self._variables: Dict[str, ASTNode] = {}
71
+ self._context = Context()
72
+ self._returns = 0
73
+
74
+ def parse(self, statement: str) -> ASTNode:
75
+ """Parses a FlowQuery statement into an Abstract Syntax Tree.
76
+
77
+ Args:
78
+ statement: The FlowQuery statement to parse
79
+
80
+ Returns:
81
+ The root AST node containing the parsed structure
82
+
83
+ Raises:
84
+ ValueError: If the statement is malformed or contains syntax errors
85
+ """
86
+ self.tokenize(statement)
87
+ return self._parse_tokenized()
88
+
89
+ def _parse_tokenized(self, is_sub_query: bool = False) -> ASTNode:
90
+ root = ASTNode()
91
+ previous: Optional[Operation] = None
92
+ operation: Optional[Operation] = None
93
+
94
+ while not self.token.is_eof():
95
+ if root.child_count() > 0:
96
+ self._expect_and_skip_whitespace_and_comments()
97
+ else:
98
+ self._skip_whitespace_and_comments()
99
+
100
+ operation = self._parse_operation()
101
+ if operation is None and not is_sub_query:
102
+ raise ValueError("Expected one of WITH, UNWIND, RETURN, LOAD, OR CALL")
103
+ elif operation is None and is_sub_query:
104
+ return root
105
+
106
+ if self._returns > 1:
107
+ raise ValueError("Only one RETURN statement is allowed")
108
+
109
+ if isinstance(previous, Call) and not previous.has_yield:
110
+ raise ValueError(
111
+ "CALL operations must have a YIELD clause unless they are the last operation"
112
+ )
113
+
114
+ if previous is not None:
115
+ previous.add_sibling(operation)
116
+ else:
117
+ root.add_child(operation)
118
+
119
+ where = self._parse_where()
120
+ if where is not None:
121
+ if isinstance(operation, Return):
122
+ operation.where = where
123
+ else:
124
+ operation.add_sibling(where)
125
+ operation = where
126
+
127
+ limit = self._parse_limit()
128
+ if limit is not None:
129
+ operation.add_sibling(limit)
130
+ operation = limit
131
+
132
+ previous = operation
133
+
134
+ if not isinstance(operation, (Return, Call, CreateNode, CreateRelationship)):
135
+ raise ValueError("Last statement must be a RETURN, WHERE, CALL, or CREATE statement")
136
+
137
+ return root
138
+
139
+ def _parse_operation(self) -> Optional[Operation]:
140
+ return (
141
+ self._parse_with() or
142
+ self._parse_unwind() or
143
+ self._parse_return() or
144
+ self._parse_load() or
145
+ self._parse_call() or
146
+ self._parse_match() or
147
+ self._parse_create()
148
+ )
149
+
150
+ def _parse_with(self) -> Optional[With]:
151
+ if not self.token.is_with():
152
+ return None
153
+ self.set_next_token()
154
+ self._expect_and_skip_whitespace_and_comments()
155
+ expressions = list(self._parse_expressions(AliasOption.REQUIRED))
156
+ if len(expressions) == 0:
157
+ raise ValueError("Expected expression")
158
+ if any(expr.has_reducers() for expr in expressions):
159
+ return AggregatedWith(expressions)
160
+ return With(expressions)
161
+
162
+ def _parse_unwind(self) -> Optional[Unwind]:
163
+ if not self.token.is_unwind():
164
+ return None
165
+ self.set_next_token()
166
+ self._expect_and_skip_whitespace_and_comments()
167
+ expression = self._parse_expression()
168
+ if expression is None:
169
+ raise ValueError("Expected expression")
170
+ if not ObjectUtils.is_instance_of_any(
171
+ expression.first_child(),
172
+ [JSONArray, Function, Reference, Lookup, RangeLookup]
173
+ ):
174
+ raise ValueError("Expected array, function, reference, or lookup.")
175
+ self._expect_and_skip_whitespace_and_comments()
176
+ alias = self._parse_alias()
177
+ if alias is not None:
178
+ expression.set_alias(alias.get_alias())
179
+ else:
180
+ raise ValueError("Expected alias")
181
+ unwind = Unwind(expression)
182
+ self._variables[alias.get_alias()] = unwind
183
+ return unwind
184
+
185
+ def _parse_return(self) -> Optional[Return]:
186
+ if not self.token.is_return():
187
+ return None
188
+ self.set_next_token()
189
+ self._expect_and_skip_whitespace_and_comments()
190
+ expressions = list(self._parse_expressions(AliasOption.OPTIONAL))
191
+ if len(expressions) == 0:
192
+ raise ValueError("Expected expression")
193
+ if any(expr.has_reducers() for expr in expressions):
194
+ return AggregatedReturn(expressions)
195
+ self._returns += 1
196
+ return Return(expressions)
197
+
198
+ def _parse_where(self) -> Optional[Where]:
199
+ if not self.token.is_where():
200
+ return None
201
+ self._expect_previous_token_to_be_whitespace_or_comment()
202
+ self.set_next_token()
203
+ self._expect_and_skip_whitespace_and_comments()
204
+ expression = self._parse_expression()
205
+ if expression is None:
206
+ raise ValueError("Expected expression")
207
+ if ObjectUtils.is_instance_of_any(
208
+ expression.first_child(),
209
+ [JSONArray, AssociativeArray]
210
+ ):
211
+ raise ValueError("Expected an expression which can be evaluated to a boolean")
212
+ return Where(expression)
213
+
214
+ def _parse_load(self) -> Optional[Load]:
215
+ if not self.token.is_load():
216
+ return None
217
+ load = Load()
218
+ self.set_next_token()
219
+ self._expect_and_skip_whitespace_and_comments()
220
+ if not (self.token.is_json() or self.token.is_csv() or self.token.is_text()):
221
+ raise ValueError("Expected JSON, CSV, or TEXT")
222
+ load.add_child(self.token.node)
223
+ self.set_next_token()
224
+ self._expect_and_skip_whitespace_and_comments()
225
+ if not self.token.is_from():
226
+ raise ValueError("Expected FROM")
227
+ self.set_next_token()
228
+ self._expect_and_skip_whitespace_and_comments()
229
+ from_node = From()
230
+ load.add_child(from_node)
231
+
232
+ # Check if source is async function
233
+ async_func = self._parse_async_function()
234
+ if async_func is not None:
235
+ from_node.add_child(async_func)
236
+ else:
237
+ expression = self._parse_expression()
238
+ if expression is None:
239
+ raise ValueError("Expected expression or async function")
240
+ from_node.add_child(expression)
241
+
242
+ self._expect_and_skip_whitespace_and_comments()
243
+ if self.token.is_headers():
244
+ headers = Headers()
245
+ self.set_next_token()
246
+ self._expect_and_skip_whitespace_and_comments()
247
+ header = self._parse_expression()
248
+ if header is None:
249
+ raise ValueError("Expected expression")
250
+ headers.add_child(header)
251
+ load.add_child(headers)
252
+ self._expect_and_skip_whitespace_and_comments()
253
+
254
+ if self.token.is_post():
255
+ post = Post()
256
+ self.set_next_token()
257
+ self._expect_and_skip_whitespace_and_comments()
258
+ payload = self._parse_expression()
259
+ if payload is None:
260
+ raise ValueError("Expected expression")
261
+ post.add_child(payload)
262
+ load.add_child(post)
263
+ self._expect_and_skip_whitespace_and_comments()
264
+
265
+ alias = self._parse_alias()
266
+ if alias is not None:
267
+ load.add_child(alias)
268
+ self._variables[alias.get_alias()] = load
269
+ else:
270
+ raise ValueError("Expected alias")
271
+ return load
272
+
273
+ def _parse_call(self) -> Optional[Call]:
274
+ if not self.token.is_call():
275
+ return None
276
+ self.set_next_token()
277
+ self._expect_and_skip_whitespace_and_comments()
278
+ async_function = self._parse_async_function()
279
+ if async_function is None:
280
+ raise ValueError("Expected async function")
281
+ call = Call()
282
+ call.function = async_function
283
+ self._skip_whitespace_and_comments()
284
+ if self.token.is_yield():
285
+ self._expect_previous_token_to_be_whitespace_or_comment()
286
+ self.set_next_token()
287
+ self._expect_and_skip_whitespace_and_comments()
288
+ expressions = list(self._parse_expressions(AliasOption.OPTIONAL))
289
+ if len(expressions) == 0:
290
+ raise ValueError("Expected at least one expression")
291
+ call.yielded = expressions
292
+ return call
293
+
294
+ def _parse_match(self) -> Optional[Match]:
295
+ if not self.token.is_match():
296
+ return None
297
+ self.set_next_token()
298
+ self._expect_and_skip_whitespace_and_comments()
299
+ patterns = list(self._parse_patterns())
300
+ if len(patterns) == 0:
301
+ raise ValueError("Expected graph pattern")
302
+ return Match(patterns)
303
+
304
+ def _parse_create(self) -> Optional[Operation]:
305
+ """Parse CREATE VIRTUAL statement for nodes and relationships."""
306
+ if not self.token.is_create():
307
+ return None
308
+ self.set_next_token()
309
+ self._expect_and_skip_whitespace_and_comments()
310
+ if not self.token.is_virtual():
311
+ raise ValueError("Expected VIRTUAL")
312
+ self.set_next_token()
313
+ self._expect_and_skip_whitespace_and_comments()
314
+
315
+ node = self._parse_node()
316
+ if node is None:
317
+ raise ValueError("Expected node definition")
318
+
319
+ relationship: Optional[Relationship] = None
320
+ if self.token.is_subtract() and self.peek() and self.peek().is_opening_bracket():
321
+ self.set_next_token() # skip -
322
+ self.set_next_token() # skip [
323
+ if not self.token.is_colon():
324
+ raise ValueError("Expected ':' for relationship type")
325
+ self.set_next_token()
326
+ if not self.token.is_identifier():
327
+ raise ValueError("Expected relationship type identifier")
328
+ rel_type = self.token.value or ""
329
+ self.set_next_token()
330
+ if not self.token.is_closing_bracket():
331
+ raise ValueError("Expected closing bracket for relationship definition")
332
+ self.set_next_token()
333
+ if not self.token.is_subtract():
334
+ raise ValueError("Expected '-' for relationship definition")
335
+ self.set_next_token()
336
+ # Skip optional direction indicator '>'
337
+ if self.token.is_greater_than():
338
+ self.set_next_token()
339
+ target = self._parse_node()
340
+ if target is None:
341
+ raise ValueError("Expected target node definition")
342
+ relationship = Relationship()
343
+ relationship.type = rel_type
344
+
345
+ self._expect_and_skip_whitespace_and_comments()
346
+ if not self.token.is_as():
347
+ raise ValueError("Expected AS")
348
+ self.set_next_token()
349
+ self._expect_and_skip_whitespace_and_comments()
350
+
351
+ query = self._parse_sub_query()
352
+ if query is None:
353
+ raise ValueError("Expected sub-query")
354
+
355
+ if relationship is not None:
356
+ return CreateRelationship(relationship, query)
357
+ else:
358
+ return CreateNode(node, query)
359
+
360
+ def _parse_sub_query(self) -> Optional[ASTNode]:
361
+ """Parse a sub-query enclosed in braces."""
362
+ if not self.token.is_opening_brace():
363
+ return None
364
+ self.set_next_token()
365
+ self._expect_and_skip_whitespace_and_comments()
366
+ query = self._parse_tokenized(is_sub_query=True)
367
+ self._skip_whitespace_and_comments()
368
+ if not self.token.is_closing_brace():
369
+ raise ValueError("Expected closing brace for sub-query")
370
+ self.set_next_token()
371
+ return query
372
+
373
+ def _parse_patterns(self) -> Iterator[Pattern]:
374
+ while True:
375
+ identifier: Optional[str] = None
376
+ if self.token.is_identifier():
377
+ identifier = self.token.value
378
+ self.set_next_token()
379
+ self._skip_whitespace_and_comments()
380
+ if not self.token.is_equals():
381
+ raise ValueError("Expected '=' for pattern assignment")
382
+ self.set_next_token()
383
+ self._skip_whitespace_and_comments()
384
+ pattern = self._parse_pattern()
385
+ if pattern is not None:
386
+ if identifier is not None:
387
+ pattern.identifier = identifier
388
+ self._variables[identifier] = pattern
389
+ yield pattern
390
+ else:
391
+ break
392
+ self._skip_whitespace_and_comments()
393
+ if not self.token.is_comma():
394
+ break
395
+ self.set_next_token()
396
+ self._skip_whitespace_and_comments()
397
+
398
+ def _parse_pattern(self) -> Optional[Pattern]:
399
+ if not self.token.is_left_parenthesis():
400
+ return None
401
+ pattern = Pattern()
402
+ node = self._parse_node()
403
+ if node is None:
404
+ raise ValueError("Expected node definition")
405
+ pattern.add_element(node)
406
+ while True:
407
+ relationship = self._parse_relationship()
408
+ if relationship is None:
409
+ break
410
+ pattern.add_element(relationship)
411
+ node = self._parse_node()
412
+ if node is None:
413
+ raise ValueError("Expected target node definition")
414
+ pattern.add_element(node)
415
+ return pattern
416
+
417
+ def _parse_pattern_expression(self) -> Optional[PatternExpression]:
418
+ """Parse a pattern expression for WHERE clauses.
419
+
420
+ PatternExpression is used to test if a graph pattern exists.
421
+ It must start with a NodeReference (referencing an existing variable).
422
+ """
423
+ if not self.token.is_left_parenthesis():
424
+ return None
425
+ pattern = PatternExpression()
426
+ node = self._parse_node()
427
+ if node is None:
428
+ raise ValueError("Expected node definition")
429
+ if not isinstance(node, NodeReference):
430
+ raise ValueError("PatternExpression must start with a NodeReference")
431
+ pattern.add_element(node)
432
+ while True:
433
+ relationship = self._parse_relationship()
434
+ if relationship is None:
435
+ break
436
+ if relationship.hops and relationship.hops.multi():
437
+ raise ValueError("PatternExpression does not support variable-length relationships")
438
+ pattern.add_element(relationship)
439
+ node = self._parse_node()
440
+ if node is None:
441
+ raise ValueError("Expected target node definition")
442
+ pattern.add_element(node)
443
+ return pattern
444
+
445
+ def _parse_node(self) -> Optional[Node]:
446
+ if not self.token.is_left_parenthesis():
447
+ return None
448
+ self.set_next_token()
449
+ self._skip_whitespace_and_comments()
450
+ identifier: Optional[str] = None
451
+ if self.token.is_identifier():
452
+ identifier = self.token.value
453
+ self.set_next_token()
454
+ self._skip_whitespace_and_comments()
455
+ label: Optional[str] = None
456
+ peek = self.peek()
457
+ if not self.token.is_colon() and peek is not None and peek.is_identifier():
458
+ raise ValueError("Expected ':' for node label")
459
+ if self.token.is_colon() and (peek is None or not peek.is_identifier()):
460
+ raise ValueError("Expected node label identifier")
461
+ if self.token.is_colon() and peek is not None and peek.is_identifier():
462
+ self.set_next_token()
463
+ label = self.token.value
464
+ self.set_next_token()
465
+ self._skip_whitespace_and_comments()
466
+ node = Node()
467
+ node.label = label
468
+ if label is not None and identifier is not None:
469
+ node.identifier = identifier
470
+ self._variables[identifier] = node
471
+ elif identifier is not None:
472
+ reference = self._variables.get(identifier)
473
+ from ..graph.node_reference import NodeReference
474
+ if reference is None or not isinstance(reference, Node):
475
+ raise ValueError(f"Undefined node reference: {identifier}")
476
+ node = NodeReference(node, reference)
477
+ if not self.token.is_right_parenthesis():
478
+ raise ValueError("Expected closing parenthesis for node definition")
479
+ self.set_next_token()
480
+ return node
481
+
482
+ def _parse_relationship(self) -> Optional[Relationship]:
483
+ if self.token.is_less_than() and self.peek() is not None and self.peek().is_subtract():
484
+ self.set_next_token()
485
+ self.set_next_token()
486
+ elif self.token.is_subtract():
487
+ self.set_next_token()
488
+ else:
489
+ return None
490
+ if not self.token.is_opening_bracket():
491
+ return None
492
+ self.set_next_token()
493
+ variable: Optional[str] = None
494
+ if self.token.is_identifier():
495
+ variable = self.token.value
496
+ self.set_next_token()
497
+ if not self.token.is_colon():
498
+ raise ValueError("Expected ':' for relationship type")
499
+ self.set_next_token()
500
+ if not self.token.is_identifier():
501
+ raise ValueError("Expected relationship type identifier")
502
+ rel_type: str = self.token.value or ""
503
+ self.set_next_token()
504
+ hops = self._parse_relationship_hops()
505
+ if not self.token.is_closing_bracket():
506
+ raise ValueError("Expected closing bracket for relationship definition")
507
+ self.set_next_token()
508
+ if not self.token.is_subtract():
509
+ raise ValueError("Expected '-' for relationship definition")
510
+ self.set_next_token()
511
+ if self.token.is_greater_than():
512
+ self.set_next_token()
513
+ relationship = Relationship()
514
+ if rel_type is not None and variable is not None:
515
+ relationship.identifier = variable
516
+ self._variables[variable] = relationship
517
+ elif variable is not None:
518
+ reference = self._variables.get(variable)
519
+ from ..graph.relationship_reference import RelationshipReference
520
+ if reference is None or not isinstance(reference, Relationship):
521
+ raise ValueError(f"Undefined relationship reference: {variable}")
522
+ relationship = RelationshipReference(relationship, reference)
523
+ if hops is not None:
524
+ relationship.hops = hops
525
+ relationship.type = rel_type
526
+ return relationship
527
+
528
+ def _parse_relationship_hops(self):
529
+ import sys
530
+ from ..graph.hops import Hops
531
+ if not self.token.is_multiply():
532
+ return None
533
+ hops = Hops()
534
+ self.set_next_token()
535
+ if self.token.is_number():
536
+ hops.min = int(self.token.value or "0")
537
+ self.set_next_token()
538
+ if self.token.is_dot():
539
+ self.set_next_token()
540
+ if not self.token.is_dot():
541
+ raise ValueError("Expected '..' for relationship hops")
542
+ self.set_next_token()
543
+ if not self.token.is_number():
544
+ raise ValueError("Expected number for relationship hops")
545
+ hops.max = int(self.token.value or "0")
546
+ self.set_next_token()
547
+ else:
548
+ # Just * without numbers means unbounded
549
+ hops.min = 0
550
+ hops.max = sys.maxsize
551
+ return hops
552
+
553
+ def _parse_limit(self) -> Optional[Limit]:
554
+ self._skip_whitespace_and_comments()
555
+ if not self.token.is_limit():
556
+ return None
557
+ self._expect_previous_token_to_be_whitespace_or_comment()
558
+ self.set_next_token()
559
+ self._expect_and_skip_whitespace_and_comments()
560
+ if not self.token.is_number():
561
+ raise ValueError("Expected number")
562
+ limit = Limit(int(self.token.value or "0"))
563
+ self.set_next_token()
564
+ return limit
565
+
566
+ def _parse_expressions(
567
+ self, alias_option: AliasOption = AliasOption.NOT_ALLOWED
568
+ ) -> Iterator[Expression]:
569
+ while True:
570
+ expression = self._parse_expression()
571
+ if expression is not None:
572
+ alias = self._parse_alias()
573
+ if isinstance(expression.first_child(), Reference) and alias is None:
574
+ reference = expression.first_child()
575
+ expression.set_alias(reference.identifier)
576
+ self._variables[reference.identifier] = expression
577
+ elif (alias_option == AliasOption.REQUIRED and
578
+ alias is None and
579
+ not isinstance(expression.first_child(), Reference)):
580
+ raise ValueError("Alias required")
581
+ elif alias_option == AliasOption.NOT_ALLOWED and alias is not None:
582
+ raise ValueError("Alias not allowed")
583
+ elif alias_option in (AliasOption.OPTIONAL, AliasOption.REQUIRED) and alias is not None:
584
+ expression.set_alias(alias.get_alias())
585
+ self._variables[alias.get_alias()] = expression
586
+ yield expression
587
+ else:
588
+ break
589
+ self._skip_whitespace_and_comments()
590
+ if not self.token.is_comma():
591
+ break
592
+ self.set_next_token()
593
+
594
+ def _parse_expression(self) -> Optional[Expression]:
595
+ expression = Expression()
596
+ while True:
597
+ self._skip_whitespace_and_comments()
598
+ if self.token.is_identifier() and (self.peek() is None or not self.peek().is_left_parenthesis()):
599
+ identifier = self.token.value or ""
600
+ reference = Reference(identifier, self._variables.get(identifier))
601
+ self.set_next_token()
602
+ lookup = self._parse_lookup(reference)
603
+ expression.add_node(lookup)
604
+ elif self.token.is_identifier() and self.peek() is not None and self.peek().is_left_parenthesis():
605
+ func = self._parse_predicate_function() or self._parse_function()
606
+ if func is not None:
607
+ lookup = self._parse_lookup(func)
608
+ expression.add_node(lookup)
609
+ elif self.token.is_left_parenthesis() and self.peek() is not None and self.peek().is_identifier():
610
+ # Possible graph pattern expression
611
+ pattern = self._parse_pattern_expression()
612
+ if pattern is not None:
613
+ expression.add_node(pattern)
614
+ elif self.token.is_operand():
615
+ expression.add_node(self.token.node)
616
+ self.set_next_token()
617
+ elif self.token.is_f_string():
618
+ f_string = self._parse_f_string()
619
+ if f_string is None:
620
+ raise ValueError("Expected f-string")
621
+ expression.add_node(f_string)
622
+ elif self.token.is_left_parenthesis():
623
+ self.set_next_token()
624
+ sub = self._parse_expression()
625
+ if sub is None:
626
+ raise ValueError("Expected expression")
627
+ if not self.token.is_right_parenthesis():
628
+ raise ValueError("Expected right parenthesis")
629
+ self.set_next_token()
630
+ lookup = self._parse_lookup(sub)
631
+ expression.add_node(lookup)
632
+ elif self.token.is_opening_brace() or self.token.is_opening_bracket():
633
+ json = self._parse_json()
634
+ if json is None:
635
+ raise ValueError("Expected JSON object")
636
+ lookup = self._parse_lookup(json)
637
+ expression.add_node(lookup)
638
+ elif self.token.is_case():
639
+ case = self._parse_case()
640
+ if case is None:
641
+ raise ValueError("Expected CASE statement")
642
+ expression.add_node(case)
643
+ elif self.token.is_not():
644
+ not_node = Not()
645
+ self.set_next_token()
646
+ sub = self._parse_expression()
647
+ if sub is None:
648
+ raise ValueError("Expected expression")
649
+ not_node.add_child(sub)
650
+ expression.add_node(not_node)
651
+ else:
652
+ if expression.nodes_added():
653
+ raise ValueError("Expected operand or left parenthesis")
654
+ else:
655
+ break
656
+ self._skip_whitespace_and_comments()
657
+ if self.token.is_operator():
658
+ expression.add_node(self.token.node)
659
+ else:
660
+ break
661
+ self.set_next_token()
662
+
663
+ if expression.nodes_added():
664
+ expression.finish()
665
+ return expression
666
+ return None
667
+
668
+ def _parse_lookup(self, node: ASTNode) -> ASTNode:
669
+ variable = node
670
+ lookup = None
671
+ while True:
672
+ if self.token.is_dot():
673
+ self.set_next_token()
674
+ if not self.token.is_identifier() and not self.token.is_keyword():
675
+ raise ValueError("Expected identifier")
676
+ lookup = Lookup()
677
+ lookup.index = Identifier(self.token.value or "")
678
+ lookup.variable = variable
679
+ self.set_next_token()
680
+ elif self.token.is_opening_bracket():
681
+ self.set_next_token()
682
+ self._skip_whitespace_and_comments()
683
+ index = self._parse_expression()
684
+ to = None
685
+ self._skip_whitespace_and_comments()
686
+ if self.token.is_colon():
687
+ self.set_next_token()
688
+ self._skip_whitespace_and_comments()
689
+ lookup = RangeLookup()
690
+ to = self._parse_expression()
691
+ else:
692
+ if index is None:
693
+ raise ValueError("Expected expression")
694
+ lookup = Lookup()
695
+ self._skip_whitespace_and_comments()
696
+ if not self.token.is_closing_bracket():
697
+ raise ValueError("Expected closing bracket")
698
+ self.set_next_token()
699
+ if isinstance(lookup, RangeLookup):
700
+ lookup.from_ = index or Null()
701
+ lookup.to = to or Null()
702
+ elif isinstance(lookup, Lookup) and index is not None:
703
+ lookup.index = index
704
+ lookup.variable = variable
705
+ else:
706
+ break
707
+ variable = lookup or variable
708
+ return variable
709
+
710
+ def _parse_case(self) -> Optional[Case]:
711
+ if not self.token.is_case():
712
+ return None
713
+ self.set_next_token()
714
+ case = Case()
715
+ parts = 0
716
+ self._expect_and_skip_whitespace_and_comments()
717
+ while True:
718
+ when = self._parse_when()
719
+ if when is None and parts == 0:
720
+ raise ValueError("Expected WHEN")
721
+ elif when is None and parts > 0:
722
+ break
723
+ elif when is not None:
724
+ case.add_child(when)
725
+ self._expect_and_skip_whitespace_and_comments()
726
+ then = self._parse_then()
727
+ if then is None:
728
+ raise ValueError("Expected THEN")
729
+ else:
730
+ case.add_child(then)
731
+ self._expect_and_skip_whitespace_and_comments()
732
+ parts += 1
733
+ else_ = self._parse_else()
734
+ if else_ is None:
735
+ raise ValueError("Expected ELSE")
736
+ else:
737
+ case.add_child(else_)
738
+ self._expect_and_skip_whitespace_and_comments()
739
+ if not self.token.is_end():
740
+ raise ValueError("Expected END")
741
+ self.set_next_token()
742
+ return case
743
+
744
+ def _parse_when(self) -> Optional[When]:
745
+ if not self.token.is_when():
746
+ return None
747
+ self.set_next_token()
748
+ when = When()
749
+ self._expect_and_skip_whitespace_and_comments()
750
+ expression = self._parse_expression()
751
+ if expression is None:
752
+ raise ValueError("Expected expression")
753
+ when.add_child(expression)
754
+ return when
755
+
756
+ def _parse_then(self) -> Optional[Then]:
757
+ if not self.token.is_then():
758
+ return None
759
+ self.set_next_token()
760
+ then = Then()
761
+ self._expect_and_skip_whitespace_and_comments()
762
+ expression = self._parse_expression()
763
+ if expression is None:
764
+ raise ValueError("Expected expression")
765
+ then.add_child(expression)
766
+ return then
767
+
768
+ def _parse_else(self) -> Optional[Else]:
769
+ if not self.token.is_else():
770
+ return None
771
+ self.set_next_token()
772
+ else_ = Else()
773
+ self._expect_and_skip_whitespace_and_comments()
774
+ expression = self._parse_expression()
775
+ if expression is None:
776
+ raise ValueError("Expected expression")
777
+ else_.add_child(expression)
778
+ return else_
779
+
780
+ def _parse_alias(self) -> Optional[Alias]:
781
+ self._skip_whitespace_and_comments()
782
+ if not self.token.is_as():
783
+ return None
784
+ self._expect_previous_token_to_be_whitespace_or_comment()
785
+ self.set_next_token()
786
+ self._expect_and_skip_whitespace_and_comments()
787
+ if not self.token.is_identifier():
788
+ raise ValueError("Expected identifier")
789
+ alias = Alias(self.token.value or "")
790
+ self.set_next_token()
791
+ return alias
792
+
793
+ def _parse_predicate_function(self) -> Optional[PredicateFunction]:
794
+ """Parse a predicate function like sum(n in [...] | n where condition)."""
795
+ # Lookahead: identifier ( identifier in
796
+ if not self.ahead([
797
+ Token.IDENTIFIER(""),
798
+ Token.LEFT_PARENTHESIS,
799
+ Token.IDENTIFIER(""),
800
+ Token.IN,
801
+ ]):
802
+ return None
803
+ if self.token.value is None:
804
+ raise ValueError("Expected identifier")
805
+ func = FunctionFactory.create_predicate(self.token.value)
806
+ self.set_next_token()
807
+ if not self.token.is_left_parenthesis():
808
+ raise ValueError("Expected left parenthesis")
809
+ self.set_next_token()
810
+ self._skip_whitespace_and_comments()
811
+ if not self.token.is_identifier():
812
+ raise ValueError("Expected identifier")
813
+ reference = Reference(self.token.value)
814
+ self._variables[reference.identifier] = reference
815
+ func.add_child(reference)
816
+ self.set_next_token()
817
+ self._expect_and_skip_whitespace_and_comments()
818
+ if not self.token.is_in():
819
+ raise ValueError("Expected IN")
820
+ self.set_next_token()
821
+ self._expect_and_skip_whitespace_and_comments()
822
+ expression = self._parse_expression()
823
+ if expression is None:
824
+ raise ValueError("Expected expression")
825
+ if not ObjectUtils.is_instance_of_any(expression.first_child(), [
826
+ JSONArray,
827
+ Reference,
828
+ Lookup,
829
+ Function,
830
+ ]):
831
+ raise ValueError("Expected array or reference")
832
+ func.add_child(expression)
833
+ self._skip_whitespace_and_comments()
834
+ if not self.token.is_pipe():
835
+ raise ValueError("Expected pipe")
836
+ self.set_next_token()
837
+ return_expr = self._parse_expression()
838
+ if return_expr is None:
839
+ raise ValueError("Expected expression")
840
+ func.add_child(return_expr)
841
+ where = self._parse_where()
842
+ if where is not None:
843
+ func.add_child(where)
844
+ self._skip_whitespace_and_comments()
845
+ if not self.token.is_right_parenthesis():
846
+ raise ValueError("Expected right parenthesis")
847
+ self.set_next_token()
848
+ del self._variables[reference.identifier]
849
+ return func
850
+
851
+ def _parse_function(self) -> Optional[Function]:
852
+ if not self.token.is_identifier():
853
+ return None
854
+ name = self.token.value or ""
855
+ if not self.peek() or not self.peek().is_left_parenthesis():
856
+ return None
857
+
858
+ try:
859
+ func = FunctionFactory.create(name)
860
+ except ValueError:
861
+ raise ValueError(f"Unknown function: {name}")
862
+
863
+ # Check for nested aggregate functions
864
+ if isinstance(func, AggregateFunction) and self._context.contains_type(AggregateFunction):
865
+ raise ValueError("Aggregate functions cannot be nested")
866
+
867
+ self._context.push(func)
868
+ self.set_next_token() # skip function name
869
+ self.set_next_token() # skip left parenthesis
870
+ self._skip_whitespace_and_comments()
871
+
872
+ # Check for DISTINCT keyword
873
+ if self.token.is_distinct():
874
+ func.distinct = True
875
+ self.set_next_token()
876
+ self._expect_and_skip_whitespace_and_comments()
877
+
878
+ params = list(self._parse_function_parameters())
879
+ func.parameters = params
880
+
881
+ if not self.token.is_right_parenthesis():
882
+ raise ValueError("Expected right parenthesis")
883
+ self.set_next_token()
884
+ self._context.pop()
885
+ return func
886
+
887
+ def _parse_async_function(self) -> Optional[AsyncFunction]:
888
+ if not self.token.is_identifier():
889
+ return None
890
+ name = self.token.value or ""
891
+ if not FunctionFactory.is_async_provider(name):
892
+ return None
893
+ self.set_next_token()
894
+ if not self.token.is_left_parenthesis():
895
+ raise ValueError("Expected left parenthesis")
896
+ self.set_next_token()
897
+
898
+ func = FunctionFactory.create_async(name)
899
+ params = list(self._parse_function_parameters())
900
+ func.parameters = params
901
+
902
+ if not self.token.is_right_parenthesis():
903
+ raise ValueError("Expected right parenthesis")
904
+ self.set_next_token()
905
+ return func
906
+
907
+ def _parse_function_parameters(self) -> Iterator[ASTNode]:
908
+ while True:
909
+ self._skip_whitespace_and_comments()
910
+ if self.token.is_right_parenthesis():
911
+ break
912
+ expr = self._parse_expression()
913
+ if expr is not None:
914
+ yield expr
915
+ self._skip_whitespace_and_comments()
916
+ if not self.token.is_comma():
917
+ break
918
+ self.set_next_token()
919
+
920
+ def _parse_json(self) -> Optional[ASTNode]:
921
+ if self.token.is_opening_brace():
922
+ return self._parse_associative_array()
923
+ elif self.token.is_opening_bracket():
924
+ return self._parse_json_array()
925
+ return None
926
+
927
+ def _parse_associative_array(self) -> AssociativeArray:
928
+ if not self.token.is_opening_brace():
929
+ raise ValueError("Expected opening brace")
930
+ self.set_next_token()
931
+ array = AssociativeArray()
932
+ while True:
933
+ self._skip_whitespace_and_comments()
934
+ if self.token.is_closing_brace():
935
+ break
936
+ if not self.token.is_identifier() and not self.token.is_string() and not self.token.is_keyword():
937
+ raise ValueError("Expected key identifier or string")
938
+ key = self.token.value or ""
939
+ self.set_next_token()
940
+ self._skip_whitespace_and_comments()
941
+ if not self.token.is_colon():
942
+ raise ValueError("Expected colon")
943
+ self.set_next_token()
944
+ self._skip_whitespace_and_comments()
945
+ value = self._parse_expression()
946
+ if value is None:
947
+ raise ValueError("Expected value")
948
+ array.add_key_value(KeyValuePair(key, value))
949
+ self._skip_whitespace_and_comments()
950
+ if not self.token.is_comma():
951
+ break
952
+ self.set_next_token()
953
+ if not self.token.is_closing_brace():
954
+ raise ValueError("Expected closing brace")
955
+ self.set_next_token()
956
+ return array
957
+
958
+ def _parse_json_array(self) -> JSONArray:
959
+ if not self.token.is_opening_bracket():
960
+ raise ValueError("Expected opening bracket")
961
+ self.set_next_token()
962
+ array = JSONArray()
963
+ while True:
964
+ self._skip_whitespace_and_comments()
965
+ if self.token.is_closing_bracket():
966
+ break
967
+ value = self._parse_expression()
968
+ if value is None:
969
+ break
970
+ array.add_value(value)
971
+ self._skip_whitespace_and_comments()
972
+ if not self.token.is_comma():
973
+ break
974
+ self.set_next_token()
975
+ if not self.token.is_closing_bracket():
976
+ raise ValueError("Expected closing bracket")
977
+ self.set_next_token()
978
+ return array
979
+
980
+ def _parse_f_string(self) -> Optional[FString]:
981
+ if not self.token.is_f_string():
982
+ return None
983
+ f_string = FString()
984
+ while self.token.is_f_string() or self.token.is_opening_brace():
985
+ if self.token.is_f_string():
986
+ f_string.add_child(String(self.token.value or ""))
987
+ self.set_next_token()
988
+ elif self.token.is_opening_brace():
989
+ self.set_next_token()
990
+ expr = self._parse_expression()
991
+ if expr is not None:
992
+ f_string.add_child(expr)
993
+ if self.token.is_closing_brace():
994
+ self.set_next_token()
995
+ return f_string
996
+
997
+ def _skip_whitespace_and_comments(self) -> bool:
998
+ skipped: bool = self.previous_token.is_whitespace_or_comment() if self.previous_token else False
999
+ while self.token.is_whitespace_or_comment():
1000
+ self.set_next_token()
1001
+ skipped = True
1002
+ return skipped
1003
+
1004
+ def _expect_and_skip_whitespace_and_comments(self) -> None:
1005
+ skipped = self._skip_whitespace_and_comments()
1006
+ if not skipped:
1007
+ raise ValueError("Expected whitespace")
1008
+
1009
+ def _expect_previous_token_to_be_whitespace_or_comment(self) -> None:
1010
+ if not self.previous_token.is_whitespace_or_comment():
1011
+ raise ValueError("Expected previous token to be whitespace or comment")