flowquery 1.0.46 → 1.0.47

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 (413) hide show
  1. package/dist/index.d.ts +0 -7
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -4
  4. package/dist/index.js.map +1 -1
  5. package/package.json +4 -1
  6. package/.editorconfig +0 -21
  7. package/.gitattributes +0 -3
  8. package/.github/workflows/npm-publish.yml +0 -32
  9. package/.github/workflows/python-publish.yml +0 -143
  10. package/.github/workflows/release.yml +0 -107
  11. package/.husky/pre-commit +0 -28
  12. package/.prettierrc +0 -22
  13. package/CODE_OF_CONDUCT.md +0 -10
  14. package/FlowQueryLogoIcon.png +0 -0
  15. package/SECURITY.md +0 -14
  16. package/SUPPORT.md +0 -13
  17. package/docs/flowquery.min.js +0 -1
  18. package/docs/index.html +0 -105
  19. package/flowquery-py/CONTRIBUTING.md +0 -127
  20. package/flowquery-py/README.md +0 -67
  21. package/flowquery-py/misc/data/test.json +0 -10
  22. package/flowquery-py/misc/data/users.json +0 -242
  23. package/flowquery-py/notebooks/TestFlowQuery.ipynb +0 -440
  24. package/flowquery-py/pyproject.toml +0 -121
  25. package/flowquery-py/setup_env.ps1 +0 -92
  26. package/flowquery-py/setup_env.sh +0 -87
  27. package/flowquery-py/src/__init__.py +0 -38
  28. package/flowquery-py/src/__main__.py +0 -10
  29. package/flowquery-py/src/compute/__init__.py +0 -6
  30. package/flowquery-py/src/compute/flowquery.py +0 -68
  31. package/flowquery-py/src/compute/runner.py +0 -64
  32. package/flowquery-py/src/extensibility.py +0 -52
  33. package/flowquery-py/src/graph/__init__.py +0 -31
  34. package/flowquery-py/src/graph/data.py +0 -136
  35. package/flowquery-py/src/graph/database.py +0 -141
  36. package/flowquery-py/src/graph/hops.py +0 -43
  37. package/flowquery-py/src/graph/node.py +0 -143
  38. package/flowquery-py/src/graph/node_data.py +0 -26
  39. package/flowquery-py/src/graph/node_reference.py +0 -50
  40. package/flowquery-py/src/graph/pattern.py +0 -115
  41. package/flowquery-py/src/graph/pattern_expression.py +0 -67
  42. package/flowquery-py/src/graph/patterns.py +0 -42
  43. package/flowquery-py/src/graph/physical_node.py +0 -41
  44. package/flowquery-py/src/graph/physical_relationship.py +0 -36
  45. package/flowquery-py/src/graph/relationship.py +0 -193
  46. package/flowquery-py/src/graph/relationship_data.py +0 -36
  47. package/flowquery-py/src/graph/relationship_match_collector.py +0 -85
  48. package/flowquery-py/src/graph/relationship_reference.py +0 -21
  49. package/flowquery-py/src/io/__init__.py +0 -5
  50. package/flowquery-py/src/io/command_line.py +0 -108
  51. package/flowquery-py/src/parsing/__init__.py +0 -17
  52. package/flowquery-py/src/parsing/alias.py +0 -20
  53. package/flowquery-py/src/parsing/alias_option.py +0 -11
  54. package/flowquery-py/src/parsing/ast_node.py +0 -147
  55. package/flowquery-py/src/parsing/base_parser.py +0 -84
  56. package/flowquery-py/src/parsing/components/__init__.py +0 -19
  57. package/flowquery-py/src/parsing/components/csv.py +0 -8
  58. package/flowquery-py/src/parsing/components/from_.py +0 -12
  59. package/flowquery-py/src/parsing/components/headers.py +0 -12
  60. package/flowquery-py/src/parsing/components/json.py +0 -8
  61. package/flowquery-py/src/parsing/components/null.py +0 -10
  62. package/flowquery-py/src/parsing/components/post.py +0 -8
  63. package/flowquery-py/src/parsing/components/text.py +0 -8
  64. package/flowquery-py/src/parsing/context.py +0 -50
  65. package/flowquery-py/src/parsing/data_structures/__init__.py +0 -15
  66. package/flowquery-py/src/parsing/data_structures/associative_array.py +0 -41
  67. package/flowquery-py/src/parsing/data_structures/json_array.py +0 -30
  68. package/flowquery-py/src/parsing/data_structures/key_value_pair.py +0 -38
  69. package/flowquery-py/src/parsing/data_structures/lookup.py +0 -51
  70. package/flowquery-py/src/parsing/data_structures/range_lookup.py +0 -42
  71. package/flowquery-py/src/parsing/expressions/__init__.py +0 -61
  72. package/flowquery-py/src/parsing/expressions/boolean.py +0 -20
  73. package/flowquery-py/src/parsing/expressions/expression.py +0 -141
  74. package/flowquery-py/src/parsing/expressions/expression_map.py +0 -26
  75. package/flowquery-py/src/parsing/expressions/f_string.py +0 -27
  76. package/flowquery-py/src/parsing/expressions/identifier.py +0 -21
  77. package/flowquery-py/src/parsing/expressions/number.py +0 -32
  78. package/flowquery-py/src/parsing/expressions/operator.py +0 -271
  79. package/flowquery-py/src/parsing/expressions/reference.py +0 -47
  80. package/flowquery-py/src/parsing/expressions/string.py +0 -27
  81. package/flowquery-py/src/parsing/functions/__init__.py +0 -127
  82. package/flowquery-py/src/parsing/functions/aggregate_function.py +0 -60
  83. package/flowquery-py/src/parsing/functions/async_function.py +0 -65
  84. package/flowquery-py/src/parsing/functions/avg.py +0 -55
  85. package/flowquery-py/src/parsing/functions/coalesce.py +0 -43
  86. package/flowquery-py/src/parsing/functions/collect.py +0 -75
  87. package/flowquery-py/src/parsing/functions/count.py +0 -79
  88. package/flowquery-py/src/parsing/functions/date_.py +0 -61
  89. package/flowquery-py/src/parsing/functions/datetime_.py +0 -62
  90. package/flowquery-py/src/parsing/functions/duration.py +0 -159
  91. package/flowquery-py/src/parsing/functions/element_id.py +0 -50
  92. package/flowquery-py/src/parsing/functions/function.py +0 -68
  93. package/flowquery-py/src/parsing/functions/function_factory.py +0 -170
  94. package/flowquery-py/src/parsing/functions/function_metadata.py +0 -148
  95. package/flowquery-py/src/parsing/functions/functions.py +0 -67
  96. package/flowquery-py/src/parsing/functions/head.py +0 -39
  97. package/flowquery-py/src/parsing/functions/id_.py +0 -49
  98. package/flowquery-py/src/parsing/functions/join.py +0 -49
  99. package/flowquery-py/src/parsing/functions/keys.py +0 -34
  100. package/flowquery-py/src/parsing/functions/last.py +0 -39
  101. package/flowquery-py/src/parsing/functions/localdatetime.py +0 -60
  102. package/flowquery-py/src/parsing/functions/localtime.py +0 -57
  103. package/flowquery-py/src/parsing/functions/max_.py +0 -49
  104. package/flowquery-py/src/parsing/functions/min_.py +0 -49
  105. package/flowquery-py/src/parsing/functions/nodes.py +0 -48
  106. package/flowquery-py/src/parsing/functions/predicate_function.py +0 -47
  107. package/flowquery-py/src/parsing/functions/predicate_sum.py +0 -49
  108. package/flowquery-py/src/parsing/functions/properties.py +0 -50
  109. package/flowquery-py/src/parsing/functions/rand.py +0 -28
  110. package/flowquery-py/src/parsing/functions/range_.py +0 -41
  111. package/flowquery-py/src/parsing/functions/reducer_element.py +0 -15
  112. package/flowquery-py/src/parsing/functions/relationships.py +0 -46
  113. package/flowquery-py/src/parsing/functions/replace.py +0 -39
  114. package/flowquery-py/src/parsing/functions/round_.py +0 -34
  115. package/flowquery-py/src/parsing/functions/schema.py +0 -40
  116. package/flowquery-py/src/parsing/functions/size.py +0 -34
  117. package/flowquery-py/src/parsing/functions/split.py +0 -54
  118. package/flowquery-py/src/parsing/functions/string_distance.py +0 -92
  119. package/flowquery-py/src/parsing/functions/stringify.py +0 -49
  120. package/flowquery-py/src/parsing/functions/substring.py +0 -76
  121. package/flowquery-py/src/parsing/functions/sum.py +0 -51
  122. package/flowquery-py/src/parsing/functions/tail.py +0 -37
  123. package/flowquery-py/src/parsing/functions/temporal_utils.py +0 -186
  124. package/flowquery-py/src/parsing/functions/time_.py +0 -57
  125. package/flowquery-py/src/parsing/functions/timestamp.py +0 -37
  126. package/flowquery-py/src/parsing/functions/to_float.py +0 -46
  127. package/flowquery-py/src/parsing/functions/to_integer.py +0 -46
  128. package/flowquery-py/src/parsing/functions/to_json.py +0 -35
  129. package/flowquery-py/src/parsing/functions/to_lower.py +0 -37
  130. package/flowquery-py/src/parsing/functions/to_string.py +0 -41
  131. package/flowquery-py/src/parsing/functions/trim.py +0 -37
  132. package/flowquery-py/src/parsing/functions/type_.py +0 -47
  133. package/flowquery-py/src/parsing/functions/value_holder.py +0 -24
  134. package/flowquery-py/src/parsing/logic/__init__.py +0 -15
  135. package/flowquery-py/src/parsing/logic/case.py +0 -28
  136. package/flowquery-py/src/parsing/logic/else_.py +0 -12
  137. package/flowquery-py/src/parsing/logic/end.py +0 -8
  138. package/flowquery-py/src/parsing/logic/then.py +0 -12
  139. package/flowquery-py/src/parsing/logic/when.py +0 -12
  140. package/flowquery-py/src/parsing/operations/__init__.py +0 -46
  141. package/flowquery-py/src/parsing/operations/aggregated_return.py +0 -25
  142. package/flowquery-py/src/parsing/operations/aggregated_with.py +0 -22
  143. package/flowquery-py/src/parsing/operations/call.py +0 -73
  144. package/flowquery-py/src/parsing/operations/create_node.py +0 -35
  145. package/flowquery-py/src/parsing/operations/create_relationship.py +0 -35
  146. package/flowquery-py/src/parsing/operations/delete_node.py +0 -29
  147. package/flowquery-py/src/parsing/operations/delete_relationship.py +0 -29
  148. package/flowquery-py/src/parsing/operations/group_by.py +0 -148
  149. package/flowquery-py/src/parsing/operations/limit.py +0 -33
  150. package/flowquery-py/src/parsing/operations/load.py +0 -148
  151. package/flowquery-py/src/parsing/operations/match.py +0 -52
  152. package/flowquery-py/src/parsing/operations/operation.py +0 -69
  153. package/flowquery-py/src/parsing/operations/order_by.py +0 -114
  154. package/flowquery-py/src/parsing/operations/projection.py +0 -21
  155. package/flowquery-py/src/parsing/operations/return_op.py +0 -88
  156. package/flowquery-py/src/parsing/operations/union.py +0 -115
  157. package/flowquery-py/src/parsing/operations/union_all.py +0 -17
  158. package/flowquery-py/src/parsing/operations/unwind.py +0 -42
  159. package/flowquery-py/src/parsing/operations/where.py +0 -43
  160. package/flowquery-py/src/parsing/operations/with_op.py +0 -18
  161. package/flowquery-py/src/parsing/parser.py +0 -1384
  162. package/flowquery-py/src/parsing/parser_state.py +0 -26
  163. package/flowquery-py/src/parsing/token_to_node.py +0 -109
  164. package/flowquery-py/src/tokenization/__init__.py +0 -23
  165. package/flowquery-py/src/tokenization/keyword.py +0 -54
  166. package/flowquery-py/src/tokenization/operator.py +0 -29
  167. package/flowquery-py/src/tokenization/string_walker.py +0 -158
  168. package/flowquery-py/src/tokenization/symbol.py +0 -19
  169. package/flowquery-py/src/tokenization/token.py +0 -693
  170. package/flowquery-py/src/tokenization/token_mapper.py +0 -53
  171. package/flowquery-py/src/tokenization/token_type.py +0 -21
  172. package/flowquery-py/src/tokenization/tokenizer.py +0 -214
  173. package/flowquery-py/src/tokenization/trie.py +0 -125
  174. package/flowquery-py/src/utils/__init__.py +0 -6
  175. package/flowquery-py/src/utils/object_utils.py +0 -20
  176. package/flowquery-py/src/utils/string_utils.py +0 -113
  177. package/flowquery-py/tests/__init__.py +0 -1
  178. package/flowquery-py/tests/compute/__init__.py +0 -1
  179. package/flowquery-py/tests/compute/test_runner.py +0 -4902
  180. package/flowquery-py/tests/graph/__init__.py +0 -1
  181. package/flowquery-py/tests/graph/test_create.py +0 -56
  182. package/flowquery-py/tests/graph/test_data.py +0 -73
  183. package/flowquery-py/tests/graph/test_match.py +0 -40
  184. package/flowquery-py/tests/parsing/__init__.py +0 -1
  185. package/flowquery-py/tests/parsing/test_context.py +0 -34
  186. package/flowquery-py/tests/parsing/test_expression.py +0 -248
  187. package/flowquery-py/tests/parsing/test_parser.py +0 -1237
  188. package/flowquery-py/tests/test_extensibility.py +0 -611
  189. package/flowquery-py/tests/tokenization/__init__.py +0 -1
  190. package/flowquery-py/tests/tokenization/test_token_mapper.py +0 -60
  191. package/flowquery-py/tests/tokenization/test_tokenizer.py +0 -198
  192. package/flowquery-py/tests/tokenization/test_trie.py +0 -30
  193. package/flowquery-vscode/.vscode-test.mjs +0 -5
  194. package/flowquery-vscode/.vscodeignore +0 -13
  195. package/flowquery-vscode/LICENSE +0 -21
  196. package/flowquery-vscode/README.md +0 -11
  197. package/flowquery-vscode/demo/FlowQueryVSCodeDemo.gif +0 -0
  198. package/flowquery-vscode/eslint.config.mjs +0 -25
  199. package/flowquery-vscode/extension.js +0 -508
  200. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +0 -1
  201. package/flowquery-vscode/flowquery-worker.js +0 -66
  202. package/flowquery-vscode/images/FlowQueryLogoIcon.png +0 -0
  203. package/flowquery-vscode/jsconfig.json +0 -13
  204. package/flowquery-vscode/libs/page.css +0 -53
  205. package/flowquery-vscode/libs/table.css +0 -13
  206. package/flowquery-vscode/libs/tabs.css +0 -66
  207. package/flowquery-vscode/package-lock.json +0 -2917
  208. package/flowquery-vscode/package.json +0 -51
  209. package/flowquery-vscode/test/extension.test.js +0 -196
  210. package/flowquery-vscode/test/worker.test.js +0 -25
  211. package/flowquery-vscode/vsc-extension-quickstart.md +0 -42
  212. package/jest.config.js +0 -14
  213. package/misc/apps/RAG/README.md +0 -29
  214. package/misc/apps/RAG/data/chats.json +0 -302
  215. package/misc/apps/RAG/data/emails.json +0 -182
  216. package/misc/apps/RAG/data/events.json +0 -226
  217. package/misc/apps/RAG/data/files.json +0 -172
  218. package/misc/apps/RAG/data/users.json +0 -158
  219. package/misc/apps/RAG/jest.config.js +0 -21
  220. package/misc/apps/RAG/package.json +0 -48
  221. package/misc/apps/RAG/public/index.html +0 -18
  222. package/misc/apps/RAG/src/App.css +0 -42
  223. package/misc/apps/RAG/src/App.tsx +0 -50
  224. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.css +0 -172
  225. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +0 -380
  226. package/misc/apps/RAG/src/components/ApiKeySettings.tsx +0 -245
  227. package/misc/apps/RAG/src/components/ChatContainer.css +0 -67
  228. package/misc/apps/RAG/src/components/ChatContainer.tsx +0 -242
  229. package/misc/apps/RAG/src/components/ChatInput.css +0 -23
  230. package/misc/apps/RAG/src/components/ChatInput.tsx +0 -76
  231. package/misc/apps/RAG/src/components/ChatMessage.css +0 -160
  232. package/misc/apps/RAG/src/components/ChatMessage.tsx +0 -286
  233. package/misc/apps/RAG/src/components/FlowQueryAgent.ts +0 -708
  234. package/misc/apps/RAG/src/components/FlowQueryRunner.css +0 -113
  235. package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +0 -371
  236. package/misc/apps/RAG/src/components/index.ts +0 -28
  237. package/misc/apps/RAG/src/graph/index.ts +0 -19
  238. package/misc/apps/RAG/src/graph/initializeGraph.ts +0 -254
  239. package/misc/apps/RAG/src/index.tsx +0 -29
  240. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +0 -327
  241. package/misc/apps/RAG/src/prompts/index.ts +0 -10
  242. package/misc/apps/RAG/src/tests/graph.test.ts +0 -35
  243. package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +0 -130
  244. package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +0 -208
  245. package/misc/apps/RAG/src/utils/Llm.ts +0 -248
  246. package/misc/apps/RAG/src/utils/index.ts +0 -12
  247. package/misc/apps/RAG/tsconfig.json +0 -22
  248. package/misc/apps/RAG/webpack.config.js +0 -43
  249. package/misc/apps/README.md +0 -1
  250. package/misc/queries/analyze_catfacts.cql +0 -75
  251. package/misc/queries/azure_openai_completions.cql +0 -13
  252. package/misc/queries/azure_openai_models.cql +0 -9
  253. package/misc/queries/mock_pipeline.cql +0 -84
  254. package/misc/queries/openai_completions.cql +0 -15
  255. package/misc/queries/openai_models.cql +0 -13
  256. package/misc/queries/test.cql +0 -6
  257. package/misc/queries/tool_inference.cql +0 -24
  258. package/misc/queries/wisdom.cql +0 -6
  259. package/misc/queries/wisdom_letter_histogram.cql +0 -8
  260. package/src/compute/flowquery.ts +0 -46
  261. package/src/compute/runner.ts +0 -66
  262. package/src/extensibility.ts +0 -45
  263. package/src/graph/data.ts +0 -130
  264. package/src/graph/database.ts +0 -143
  265. package/src/graph/hops.ts +0 -22
  266. package/src/graph/node.ts +0 -122
  267. package/src/graph/node_data.ts +0 -18
  268. package/src/graph/node_reference.ts +0 -38
  269. package/src/graph/pattern.ts +0 -110
  270. package/src/graph/pattern_expression.ts +0 -48
  271. package/src/graph/patterns.ts +0 -36
  272. package/src/graph/physical_node.ts +0 -23
  273. package/src/graph/physical_relationship.ts +0 -23
  274. package/src/graph/relationship.ts +0 -167
  275. package/src/graph/relationship_data.ts +0 -31
  276. package/src/graph/relationship_match_collector.ts +0 -64
  277. package/src/graph/relationship_reference.ts +0 -25
  278. package/src/index.browser.ts +0 -46
  279. package/src/index.node.ts +0 -55
  280. package/src/index.ts +0 -12
  281. package/src/io/command_line.ts +0 -74
  282. package/src/parsing/alias.ts +0 -23
  283. package/src/parsing/alias_option.ts +0 -5
  284. package/src/parsing/ast_node.ts +0 -153
  285. package/src/parsing/base_parser.ts +0 -98
  286. package/src/parsing/components/csv.ts +0 -9
  287. package/src/parsing/components/from.ts +0 -12
  288. package/src/parsing/components/headers.ts +0 -12
  289. package/src/parsing/components/json.ts +0 -9
  290. package/src/parsing/components/null.ts +0 -9
  291. package/src/parsing/components/post.ts +0 -9
  292. package/src/parsing/components/text.ts +0 -9
  293. package/src/parsing/context.ts +0 -54
  294. package/src/parsing/data_structures/associative_array.ts +0 -43
  295. package/src/parsing/data_structures/json_array.ts +0 -31
  296. package/src/parsing/data_structures/key_value_pair.ts +0 -37
  297. package/src/parsing/data_structures/lookup.ts +0 -44
  298. package/src/parsing/data_structures/range_lookup.ts +0 -36
  299. package/src/parsing/expressions/boolean.ts +0 -21
  300. package/src/parsing/expressions/expression.ts +0 -150
  301. package/src/parsing/expressions/expression_map.ts +0 -22
  302. package/src/parsing/expressions/f_string.ts +0 -26
  303. package/src/parsing/expressions/identifier.ts +0 -22
  304. package/src/parsing/expressions/number.ts +0 -40
  305. package/src/parsing/expressions/operator.ts +0 -354
  306. package/src/parsing/expressions/reference.ts +0 -45
  307. package/src/parsing/expressions/string.ts +0 -34
  308. package/src/parsing/functions/aggregate_function.ts +0 -58
  309. package/src/parsing/functions/async_function.ts +0 -64
  310. package/src/parsing/functions/avg.ts +0 -47
  311. package/src/parsing/functions/coalesce.ts +0 -49
  312. package/src/parsing/functions/collect.ts +0 -54
  313. package/src/parsing/functions/count.ts +0 -54
  314. package/src/parsing/functions/date.ts +0 -63
  315. package/src/parsing/functions/datetime.ts +0 -63
  316. package/src/parsing/functions/duration.ts +0 -143
  317. package/src/parsing/functions/element_id.ts +0 -51
  318. package/src/parsing/functions/function.ts +0 -60
  319. package/src/parsing/functions/function_factory.ts +0 -195
  320. package/src/parsing/functions/function_metadata.ts +0 -217
  321. package/src/parsing/functions/functions.ts +0 -70
  322. package/src/parsing/functions/head.ts +0 -42
  323. package/src/parsing/functions/id.ts +0 -51
  324. package/src/parsing/functions/join.ts +0 -40
  325. package/src/parsing/functions/keys.ts +0 -29
  326. package/src/parsing/functions/last.ts +0 -42
  327. package/src/parsing/functions/localdatetime.ts +0 -63
  328. package/src/parsing/functions/localtime.ts +0 -58
  329. package/src/parsing/functions/max.ts +0 -37
  330. package/src/parsing/functions/min.ts +0 -37
  331. package/src/parsing/functions/nodes.ts +0 -54
  332. package/src/parsing/functions/predicate_function.ts +0 -48
  333. package/src/parsing/functions/predicate_sum.ts +0 -47
  334. package/src/parsing/functions/properties.ts +0 -56
  335. package/src/parsing/functions/rand.ts +0 -21
  336. package/src/parsing/functions/range.ts +0 -37
  337. package/src/parsing/functions/reducer_element.ts +0 -10
  338. package/src/parsing/functions/relationships.ts +0 -52
  339. package/src/parsing/functions/replace.ts +0 -38
  340. package/src/parsing/functions/round.ts +0 -28
  341. package/src/parsing/functions/schema.ts +0 -39
  342. package/src/parsing/functions/size.ts +0 -28
  343. package/src/parsing/functions/split.ts +0 -45
  344. package/src/parsing/functions/string_distance.ts +0 -83
  345. package/src/parsing/functions/stringify.ts +0 -37
  346. package/src/parsing/functions/substring.ts +0 -68
  347. package/src/parsing/functions/sum.ts +0 -41
  348. package/src/parsing/functions/tail.ts +0 -39
  349. package/src/parsing/functions/temporal_utils.ts +0 -180
  350. package/src/parsing/functions/time.ts +0 -58
  351. package/src/parsing/functions/timestamp.ts +0 -37
  352. package/src/parsing/functions/to_float.ts +0 -50
  353. package/src/parsing/functions/to_integer.ts +0 -50
  354. package/src/parsing/functions/to_json.ts +0 -28
  355. package/src/parsing/functions/to_lower.ts +0 -28
  356. package/src/parsing/functions/to_string.ts +0 -32
  357. package/src/parsing/functions/trim.ts +0 -28
  358. package/src/parsing/functions/type.ts +0 -39
  359. package/src/parsing/functions/value_holder.ts +0 -13
  360. package/src/parsing/logic/case.ts +0 -26
  361. package/src/parsing/logic/else.ts +0 -12
  362. package/src/parsing/logic/end.ts +0 -9
  363. package/src/parsing/logic/then.ts +0 -12
  364. package/src/parsing/logic/when.ts +0 -12
  365. package/src/parsing/operations/aggregated_return.ts +0 -22
  366. package/src/parsing/operations/aggregated_with.ts +0 -18
  367. package/src/parsing/operations/call.ts +0 -69
  368. package/src/parsing/operations/create_node.ts +0 -39
  369. package/src/parsing/operations/create_relationship.ts +0 -38
  370. package/src/parsing/operations/delete_node.ts +0 -33
  371. package/src/parsing/operations/delete_relationship.ts +0 -32
  372. package/src/parsing/operations/group_by.ts +0 -137
  373. package/src/parsing/operations/limit.ts +0 -31
  374. package/src/parsing/operations/load.ts +0 -146
  375. package/src/parsing/operations/match.ts +0 -54
  376. package/src/parsing/operations/operation.ts +0 -69
  377. package/src/parsing/operations/order_by.ts +0 -126
  378. package/src/parsing/operations/projection.ts +0 -18
  379. package/src/parsing/operations/return.ts +0 -76
  380. package/src/parsing/operations/union.ts +0 -114
  381. package/src/parsing/operations/union_all.ts +0 -16
  382. package/src/parsing/operations/unwind.ts +0 -36
  383. package/src/parsing/operations/where.ts +0 -42
  384. package/src/parsing/operations/with.ts +0 -20
  385. package/src/parsing/parser.ts +0 -1641
  386. package/src/parsing/parser_state.ts +0 -25
  387. package/src/parsing/token_to_node.ts +0 -114
  388. package/src/tokenization/keyword.ts +0 -50
  389. package/src/tokenization/operator.ts +0 -25
  390. package/src/tokenization/string_walker.ts +0 -197
  391. package/src/tokenization/symbol.ts +0 -15
  392. package/src/tokenization/token.ts +0 -764
  393. package/src/tokenization/token_mapper.ts +0 -53
  394. package/src/tokenization/token_type.ts +0 -16
  395. package/src/tokenization/tokenizer.ts +0 -250
  396. package/src/tokenization/trie.ts +0 -117
  397. package/src/utils/object_utils.ts +0 -17
  398. package/src/utils/string_utils.ts +0 -114
  399. package/tests/compute/runner.test.ts +0 -4559
  400. package/tests/extensibility.test.ts +0 -643
  401. package/tests/graph/create.test.ts +0 -36
  402. package/tests/graph/data.test.ts +0 -58
  403. package/tests/graph/match.test.ts +0 -29
  404. package/tests/parsing/context.test.ts +0 -27
  405. package/tests/parsing/expression.test.ts +0 -303
  406. package/tests/parsing/parser.test.ts +0 -1327
  407. package/tests/tokenization/token_mapper.test.ts +0 -47
  408. package/tests/tokenization/tokenizer.test.ts +0 -191
  409. package/tests/tokenization/trie.test.ts +0 -20
  410. package/tsconfig.json +0 -19
  411. package/typedoc.json +0 -16
  412. package/vscode-settings.json.recommended +0 -16
  413. package/webpack.config.js +0 -26
@@ -1,1384 +0,0 @@
1
- """Main parser for FlowQuery statements."""
2
-
3
- import sys
4
- from typing import Dict, Iterator, List, Optional, Tuple, cast
5
-
6
- from ..graph.hops import Hops
7
- from ..graph.node import Node
8
- from ..graph.node_reference import NodeReference
9
- from ..graph.pattern import Pattern
10
- from ..graph.pattern_expression import PatternExpression
11
- from ..graph.relationship import Relationship
12
- from ..graph.relationship_reference import RelationshipReference
13
- from ..tokenization.token import Token
14
- from ..utils.object_utils import ObjectUtils
15
- from .alias import Alias
16
- from .alias_option import AliasOption
17
- from .ast_node import ASTNode
18
- from .base_parser import BaseParser
19
- from .components.from_ import From
20
- from .components.headers import Headers
21
- from .components.null import Null
22
- from .components.post import Post
23
- from .data_structures.associative_array import AssociativeArray
24
- from .data_structures.json_array import JSONArray
25
- from .data_structures.key_value_pair import KeyValuePair
26
- from .data_structures.lookup import Lookup
27
- from .data_structures.range_lookup import RangeLookup
28
- from .expressions.expression import Expression
29
- from .expressions.f_string import FString
30
- from .expressions.identifier import Identifier
31
- from .expressions.operator import (
32
- Contains,
33
- EndsWith,
34
- In,
35
- Is,
36
- IsNot,
37
- Not,
38
- NotContains,
39
- NotEndsWith,
40
- NotIn,
41
- NotStartsWith,
42
- StartsWith,
43
- )
44
- from .expressions.reference import Reference
45
- from .expressions.string import String
46
- from .functions.aggregate_function import AggregateFunction
47
- from .functions.async_function import AsyncFunction
48
- from .functions.function import Function
49
- from .functions.function_factory import FunctionFactory
50
- from .functions.predicate_function import PredicateFunction
51
- from .logic.case import Case
52
- from .logic.else_ import Else
53
- from .logic.then import Then
54
- from .logic.when import When
55
- from .operations.aggregated_return import AggregatedReturn
56
- from .operations.aggregated_with import AggregatedWith
57
- from .operations.call import Call
58
- from .operations.create_node import CreateNode
59
- from .operations.create_relationship import CreateRelationship
60
- from .operations.delete_node import DeleteNode
61
- from .operations.delete_relationship import DeleteRelationship
62
- from .operations.limit import Limit
63
- from .operations.load import Load
64
- from .operations.match import Match
65
- from .operations.operation import Operation
66
- from .operations.order_by import OrderBy, SortField
67
- from .operations.return_op import Return
68
- from .operations.union import Union
69
- from .operations.union_all import UnionAll
70
- from .operations.unwind import Unwind
71
- from .operations.where import Where
72
- from .operations.with_op import With
73
- from .parser_state import ParserState
74
-
75
-
76
- class Parser(BaseParser):
77
- """Main parser for FlowQuery statements.
78
-
79
- Parses FlowQuery declarative query language statements into an Abstract Syntax Tree (AST).
80
- Supports operations like WITH, UNWIND, RETURN, LOAD, WHERE, and LIMIT, along with
81
- expressions, functions, data structures, and logical constructs.
82
-
83
- Example:
84
- parser = Parser()
85
- ast = parser.parse("unwind [1, 2, 3, 4, 5] as num return num")
86
- """
87
-
88
- def __init__(self, tokens: Optional[List[Token]] = None):
89
- super().__init__(tokens)
90
- self._state = ParserState()
91
-
92
- def parse(self, statement: str) -> ASTNode:
93
- """Parses a FlowQuery statement into an Abstract Syntax Tree.
94
-
95
- Args:
96
- statement: The FlowQuery statement to parse
97
-
98
- Returns:
99
- The root AST node containing the parsed structure
100
-
101
- Raises:
102
- ValueError: If the statement is malformed or contains syntax errors
103
- """
104
- self.tokenize(statement)
105
- return self._parse_tokenized()
106
-
107
- def _parse_tokenized(self, is_sub_query: bool = False) -> ASTNode:
108
- root = ASTNode()
109
- previous: Optional[Operation] = None
110
- operation: Optional[Operation] = None
111
-
112
- while not self.token.is_eof():
113
- if root.child_count() > 0:
114
- self._expect_and_skip_whitespace_and_comments()
115
- else:
116
- self._skip_whitespace_and_comments()
117
-
118
- # UNION separates two query pipelines — break and handle after the loop
119
- if self.token.is_union():
120
- break
121
-
122
- if self.token.is_eof():
123
- break
124
-
125
- operation = self._parse_operation()
126
- if operation is None and not is_sub_query:
127
- raise ValueError("Expected one of WITH, UNWIND, RETURN, LOAD, OR CALL")
128
- elif operation is None and is_sub_query:
129
- return root
130
-
131
- if self._state.returns > 1:
132
- raise ValueError("Only one RETURN statement is allowed")
133
-
134
- if isinstance(previous, Call) and not previous.has_yield:
135
- raise ValueError(
136
- "CALL operations must have a YIELD clause unless they are the last operation"
137
- )
138
-
139
- if previous is not None:
140
- previous.add_sibling(operation)
141
- else:
142
- root.add_child(operation)
143
-
144
- where = self._parse_where()
145
- if where is not None:
146
- if isinstance(operation, Return):
147
- operation.where = where
148
- else:
149
- operation.add_sibling(where)
150
- operation = where
151
-
152
- order_by = self._parse_order_by()
153
- if order_by is not None:
154
- if isinstance(operation, Return):
155
- operation.order_by = order_by
156
- else:
157
- operation.add_sibling(order_by)
158
- operation = order_by
159
-
160
- limit = self._parse_limit()
161
- if limit is not None:
162
- if isinstance(operation, Return):
163
- operation.limit = limit
164
- else:
165
- operation.add_sibling(limit)
166
- operation = limit
167
-
168
- previous = operation
169
-
170
- # Handle UNION: wrap left and right pipelines into a Union node
171
- if not self.token.is_eof() and self.token.is_union():
172
- if not isinstance(operation, (Return, Call)):
173
- raise ValueError(
174
- "Each side of UNION must end with a RETURN or CALL statement"
175
- )
176
- union = self._parse_union()
177
- assert union is not None
178
- union.left = root.first_child() # type: ignore[assignment]
179
- # Save and reset parser state for right-side scope
180
- state: ParserState = self._state
181
- self._state = ParserState()
182
- right_root = self._parse_tokenized(is_sub_query)
183
- union.right = right_root.first_child() # type: ignore[assignment]
184
- # Restore parser state
185
- self._state = state
186
- new_root = ASTNode()
187
- new_root.add_child(union)
188
- return new_root
189
-
190
- if not isinstance(operation, (Return, Call, CreateNode, CreateRelationship, DeleteNode, DeleteRelationship)):
191
- raise ValueError("Last statement must be a RETURN, WHERE, CALL, CREATE, or DELETE statement")
192
-
193
- return root
194
-
195
- def _parse_operation(self) -> Optional[Operation]:
196
- return (
197
- self._parse_with() or
198
- self._parse_unwind() or
199
- self._parse_return() or
200
- self._parse_load() or
201
- self._parse_call() or
202
- self._parse_match() or
203
- self._parse_create() or
204
- self._parse_delete()
205
- )
206
-
207
- def _parse_with(self) -> Optional[With]:
208
- if not self.token.is_with():
209
- return None
210
- self.set_next_token()
211
- self._expect_and_skip_whitespace_and_comments()
212
- distinct = False
213
- if self.token.is_distinct():
214
- distinct = True
215
- self.set_next_token()
216
- self._expect_and_skip_whitespace_and_comments()
217
- expressions = self._parse_expressions(AliasOption.REQUIRED)
218
- if len(expressions) == 0:
219
- raise ValueError("Expected expression")
220
- if distinct or any(expr.has_reducers() for expr in expressions):
221
- return AggregatedWith(expressions) # type: ignore[return-value]
222
- return With(expressions)
223
-
224
- def _parse_unwind(self) -> Optional[Unwind]:
225
- if not self.token.is_unwind():
226
- return None
227
- self.set_next_token()
228
- self._expect_and_skip_whitespace_and_comments()
229
- expression = self._parse_expression()
230
- if expression is None:
231
- raise ValueError("Expected expression")
232
- if not ObjectUtils.is_instance_of_any(
233
- expression.first_child(),
234
- [JSONArray, Function, Reference, Lookup, RangeLookup]
235
- ):
236
- raise ValueError("Expected array, function, reference, or lookup.")
237
- self._expect_and_skip_whitespace_and_comments()
238
- alias = self._parse_alias()
239
- if alias is not None:
240
- expression.set_alias(alias.get_alias())
241
- else:
242
- raise ValueError("Expected alias")
243
- unwind = Unwind(expression)
244
- self._state.variables[alias.get_alias()] = unwind
245
- return unwind
246
-
247
- def _parse_return(self) -> Optional[Return]:
248
- if not self.token.is_return():
249
- return None
250
- self.set_next_token()
251
- self._expect_and_skip_whitespace_and_comments()
252
- distinct = False
253
- if self.token.is_distinct():
254
- distinct = True
255
- self.set_next_token()
256
- self._expect_and_skip_whitespace_and_comments()
257
- expressions = self._parse_expressions(AliasOption.OPTIONAL)
258
- if len(expressions) == 0:
259
- raise ValueError("Expected expression")
260
- if distinct or any(expr.has_reducers() for expr in expressions):
261
- return AggregatedReturn(expressions)
262
- self._state.increment_returns()
263
- return Return(expressions)
264
-
265
- def _parse_where(self) -> Optional[Where]:
266
- self._skip_whitespace_and_comments()
267
- if not self.token.is_where():
268
- return None
269
- self._expect_previous_token_to_be_whitespace_or_comment()
270
- self.set_next_token()
271
- self._expect_and_skip_whitespace_and_comments()
272
- expression = self._parse_expression()
273
- if expression is None:
274
- raise ValueError("Expected expression")
275
- if ObjectUtils.is_instance_of_any(
276
- expression.first_child(),
277
- [JSONArray, AssociativeArray]
278
- ):
279
- raise ValueError("Expected an expression which can be evaluated to a boolean")
280
- return Where(expression)
281
-
282
- def _parse_load(self) -> Optional[Load]:
283
- if not self.token.is_load():
284
- return None
285
- load = Load()
286
- self.set_next_token()
287
- self._expect_and_skip_whitespace_and_comments()
288
- if not (self.token.is_json() or self.token.is_csv() or self.token.is_text()):
289
- raise ValueError("Expected JSON, CSV, or TEXT")
290
- load.add_child(self.token.node)
291
- self.set_next_token()
292
- self._expect_and_skip_whitespace_and_comments()
293
- if not self.token.is_from():
294
- raise ValueError("Expected FROM")
295
- self.set_next_token()
296
- self._expect_and_skip_whitespace_and_comments()
297
- from_node = From()
298
- load.add_child(from_node)
299
-
300
- # Check if source is async function
301
- async_func = self._parse_async_function()
302
- if async_func is not None:
303
- from_node.add_child(async_func)
304
- else:
305
- expression = self._parse_expression()
306
- if expression is None:
307
- raise ValueError("Expected expression or async function")
308
- from_node.add_child(expression)
309
-
310
- self._expect_and_skip_whitespace_and_comments()
311
- if self.token.is_headers():
312
- headers = Headers()
313
- self.set_next_token()
314
- self._expect_and_skip_whitespace_and_comments()
315
- header = self._parse_expression()
316
- if header is None:
317
- raise ValueError("Expected expression")
318
- headers.add_child(header)
319
- load.add_child(headers)
320
- self._expect_and_skip_whitespace_and_comments()
321
-
322
- if self.token.is_post():
323
- post = Post()
324
- self.set_next_token()
325
- self._expect_and_skip_whitespace_and_comments()
326
- payload = self._parse_expression()
327
- if payload is None:
328
- raise ValueError("Expected expression")
329
- post.add_child(payload)
330
- load.add_child(post)
331
- self._expect_and_skip_whitespace_and_comments()
332
-
333
- alias = self._parse_alias()
334
- if alias is not None:
335
- load.add_child(alias)
336
- self._state.variables[alias.get_alias()] = load
337
- else:
338
- raise ValueError("Expected alias")
339
- return load
340
-
341
- def _parse_call(self) -> Optional[Call]:
342
- if not self.token.is_call():
343
- return None
344
- self.set_next_token()
345
- self._expect_and_skip_whitespace_and_comments()
346
- async_function = self._parse_async_function()
347
- if async_function is None:
348
- raise ValueError("Expected async function")
349
- call = Call()
350
- call.function = async_function
351
- self._skip_whitespace_and_comments()
352
- if self.token.is_yield():
353
- self._expect_previous_token_to_be_whitespace_or_comment()
354
- self.set_next_token()
355
- self._expect_and_skip_whitespace_and_comments()
356
- expressions = self._parse_expressions(AliasOption.OPTIONAL)
357
- if len(expressions) == 0:
358
- raise ValueError("Expected at least one expression")
359
- call.yielded = expressions # type: ignore[assignment]
360
- return call
361
-
362
- def _parse_match(self) -> Optional[Match]:
363
- optional = False
364
- if self.token.is_optional():
365
- optional = True
366
- self.set_next_token()
367
- self._expect_and_skip_whitespace_and_comments()
368
- if not self.token.is_match():
369
- if optional:
370
- raise ValueError("Expected MATCH after OPTIONAL")
371
- return None
372
- self.set_next_token()
373
- self._expect_and_skip_whitespace_and_comments()
374
- patterns = list(self._parse_patterns())
375
- if len(patterns) == 0:
376
- raise ValueError("Expected graph pattern")
377
- return Match(patterns, optional)
378
-
379
- def _parse_create(self) -> Optional[Operation]:
380
- """Parse CREATE VIRTUAL statement for nodes and relationships."""
381
- if not self.token.is_create():
382
- return None
383
- self.set_next_token()
384
- self._expect_and_skip_whitespace_and_comments()
385
- if not self.token.is_virtual():
386
- raise ValueError("Expected VIRTUAL")
387
- self.set_next_token()
388
- self._expect_and_skip_whitespace_and_comments()
389
-
390
- node = self._parse_node()
391
- if node is None:
392
- raise ValueError("Expected node definition")
393
-
394
- relationship: Optional[Relationship] = None
395
- if self.token.is_subtract() and self.peek() and self.peek().is_opening_bracket():
396
- self.set_next_token() # skip -
397
- self.set_next_token() # skip [
398
- if not self.token.is_colon():
399
- raise ValueError("Expected ':' for relationship type")
400
- self.set_next_token()
401
- if not self.token.is_identifier_or_keyword():
402
- raise ValueError("Expected relationship type identifier")
403
- rel_type = self.token.value or ""
404
- self.set_next_token()
405
- if not self.token.is_closing_bracket():
406
- raise ValueError("Expected closing bracket for relationship definition")
407
- self.set_next_token()
408
- if not self.token.is_subtract():
409
- raise ValueError("Expected '-' for relationship definition")
410
- self.set_next_token()
411
- # Skip optional direction indicator '>'
412
- if self.token.is_greater_than():
413
- self.set_next_token()
414
- target = self._parse_node()
415
- if target is None:
416
- raise ValueError("Expected target node definition")
417
- relationship = Relationship()
418
- relationship.type = rel_type
419
- relationship.source = node
420
- relationship.target = target
421
-
422
- self._expect_and_skip_whitespace_and_comments()
423
- if not self.token.is_as():
424
- raise ValueError("Expected AS")
425
- self.set_next_token()
426
- self._expect_and_skip_whitespace_and_comments()
427
-
428
- query = self._parse_sub_query()
429
- if query is None:
430
- raise ValueError("Expected sub-query")
431
-
432
- if relationship is not None:
433
- return CreateRelationship(relationship, query)
434
- else:
435
- return CreateNode(node, query)
436
-
437
- def _parse_delete(self) -> Optional[Operation]:
438
- """Parse DELETE VIRTUAL statement for nodes and relationships."""
439
- if not self.token.is_delete():
440
- return None
441
- self.set_next_token()
442
- self._expect_and_skip_whitespace_and_comments()
443
- if not self.token.is_virtual():
444
- raise ValueError("Expected VIRTUAL")
445
- self.set_next_token()
446
- self._expect_and_skip_whitespace_and_comments()
447
-
448
- node = self._parse_node()
449
- if node is None:
450
- raise ValueError("Expected node definition")
451
-
452
- relationship: Optional[Relationship] = None
453
- if self.token.is_subtract() and self.peek() and self.peek().is_opening_bracket():
454
- self.set_next_token() # skip -
455
- self.set_next_token() # skip [
456
- if not self.token.is_colon():
457
- raise ValueError("Expected ':' for relationship type")
458
- self.set_next_token()
459
- if not self.token.is_identifier_or_keyword():
460
- raise ValueError("Expected relationship type identifier")
461
- rel_type = self.token.value or ""
462
- self.set_next_token()
463
- if not self.token.is_closing_bracket():
464
- raise ValueError("Expected closing bracket for relationship definition")
465
- self.set_next_token()
466
- if not self.token.is_subtract():
467
- raise ValueError("Expected '-' for relationship definition")
468
- self.set_next_token()
469
- # Skip optional direction indicator '>'
470
- if self.token.is_greater_than():
471
- self.set_next_token()
472
- target = self._parse_node()
473
- if target is None:
474
- raise ValueError("Expected target node definition")
475
- relationship = Relationship()
476
- relationship.type = rel_type
477
- relationship.source = node
478
- relationship.target = target
479
-
480
- if relationship is not None:
481
- return DeleteRelationship(relationship)
482
- else:
483
- return DeleteNode(node)
484
-
485
- def _parse_union(self) -> Optional[Union]:
486
- """Parse a UNION or UNION ALL keyword."""
487
- if not self.token.is_union():
488
- return None
489
- self.set_next_token()
490
- self._skip_whitespace_and_comments()
491
- if self.token.is_all():
492
- union: Union = UnionAll()
493
- self.set_next_token()
494
- else:
495
- union = Union()
496
- return union
497
-
498
- def _parse_sub_query(self) -> Optional[ASTNode]:
499
- """Parse a sub-query enclosed in braces."""
500
- if not self.token.is_opening_brace():
501
- return None
502
- self.set_next_token()
503
- self._expect_and_skip_whitespace_and_comments()
504
- query = self._parse_tokenized(is_sub_query=True)
505
- self._skip_whitespace_and_comments()
506
- if not self.token.is_closing_brace():
507
- raise ValueError("Expected closing brace for sub-query")
508
- self.set_next_token()
509
- return query
510
-
511
- def _parse_patterns(self) -> Iterator[Pattern]:
512
- while True:
513
- identifier: Optional[str] = None
514
- if self.token.is_identifier():
515
- identifier = self.token.value
516
- self.set_next_token()
517
- self._skip_whitespace_and_comments()
518
- if not self.token.is_equals():
519
- raise ValueError("Expected '=' for pattern assignment")
520
- self.set_next_token()
521
- self._skip_whitespace_and_comments()
522
- pattern = self._parse_pattern()
523
- if pattern is not None:
524
- if identifier is not None:
525
- pattern.identifier = identifier
526
- self._state.variables[identifier] = pattern
527
- yield pattern
528
- else:
529
- break
530
- self._skip_whitespace_and_comments()
531
- if not self.token.is_comma():
532
- break
533
- self.set_next_token()
534
- self._skip_whitespace_and_comments()
535
-
536
- def _parse_pattern(self) -> Optional[Pattern]:
537
- if not self.token.is_left_parenthesis():
538
- return None
539
- pattern = Pattern()
540
- node = self._parse_node()
541
- if node is None:
542
- raise ValueError("Expected node definition")
543
- pattern.add_element(node)
544
- while True:
545
- relationship = self._parse_relationship()
546
- if relationship is None:
547
- break
548
- pattern.add_element(relationship)
549
- node = self._parse_node()
550
- if node is None:
551
- raise ValueError("Expected target node definition")
552
- pattern.add_element(node)
553
- return pattern
554
-
555
- def _parse_pattern_expression(self) -> Optional[PatternExpression]:
556
- """Parse a pattern expression for WHERE clauses.
557
-
558
- PatternExpression is used to test if a graph pattern exists.
559
- It must start with a NodeReference (referencing an existing variable).
560
- """
561
- if not self.token.is_left_parenthesis():
562
- return None
563
- pattern = PatternExpression()
564
- node = self._parse_node()
565
- if node is None:
566
- raise ValueError("Expected node definition")
567
- pattern.add_element(node)
568
- while True:
569
- relationship = self._parse_relationship()
570
- if relationship is None:
571
- break
572
- if relationship.hops and relationship.hops.multi():
573
- raise ValueError("PatternExpression does not support variable-length relationships")
574
- pattern.add_element(relationship)
575
- node = self._parse_node()
576
- if node is None:
577
- raise ValueError("Expected target node definition")
578
- pattern.add_element(node)
579
- pattern.verify()
580
- return pattern
581
-
582
- def _parse_node(self) -> Optional[Node]:
583
- if not self.token.is_left_parenthesis():
584
- return None
585
- self.set_next_token()
586
- self._skip_whitespace_and_comments()
587
- identifier: Optional[str] = None
588
- if self.token.is_identifier_or_keyword():
589
- identifier = self.token.value
590
- self.set_next_token()
591
- self._skip_whitespace_and_comments()
592
- label: Optional[str] = None
593
- peek = self.peek()
594
- if not self.token.is_colon() and peek is not None and peek.is_identifier_or_keyword():
595
- raise ValueError("Expected ':' for node label")
596
- if self.token.is_colon() and (peek is None or not peek.is_identifier_or_keyword()):
597
- raise ValueError("Expected node label identifier")
598
- if self.token.is_colon() and peek is not None and peek.is_identifier_or_keyword():
599
- self.set_next_token()
600
- label = cast(str, self.token.value) # Guaranteed by is_identifier check
601
- self.set_next_token()
602
- self._skip_whitespace_and_comments()
603
- node = Node()
604
- node.label = label
605
- node.properties = dict(self._parse_properties())
606
- if identifier is not None and identifier in self._state.variables:
607
- reference = self._state.variables.get(identifier)
608
- if reference is None or (
609
- not isinstance(reference, Node)
610
- and not isinstance(reference, Unwind)
611
- and not isinstance(reference, Expression)
612
- ):
613
- raise ValueError(f"Undefined node reference: {identifier}")
614
- node = NodeReference(node, reference)
615
- elif identifier is not None:
616
- node.identifier = identifier
617
- self._state.variables[identifier] = node
618
- if not self.token.is_right_parenthesis():
619
- raise ValueError("Expected closing parenthesis for node definition")
620
- self.set_next_token()
621
- return node
622
-
623
- def _parse_relationship(self) -> Optional[Relationship]:
624
- direction = "right"
625
- if self.token.is_less_than() and self.peek() is not None and self.peek().is_subtract():
626
- direction = "left"
627
- self.set_next_token()
628
- self.set_next_token()
629
- elif self.token.is_subtract():
630
- self.set_next_token()
631
- else:
632
- return None
633
- if not self.token.is_opening_bracket():
634
- return None
635
- self.set_next_token()
636
- variable: Optional[str] = None
637
- if self.token.is_identifier_or_keyword():
638
- variable = self.token.value
639
- self.set_next_token()
640
- if not self.token.is_colon():
641
- raise ValueError("Expected ':' for relationship type")
642
- self.set_next_token()
643
- if not self.token.is_identifier_or_keyword():
644
- raise ValueError("Expected relationship type identifier")
645
- rel_types: List[str] = [self.token.value or ""]
646
- self.set_next_token()
647
- while self.token.is_pipe():
648
- self.set_next_token()
649
- if self.token.is_colon():
650
- self.set_next_token()
651
- if not self.token.is_identifier_or_keyword():
652
- raise ValueError("Expected relationship type identifier after '|'")
653
- rel_types.append(self.token.value or "")
654
- self.set_next_token()
655
- hops = self._parse_relationship_hops()
656
- properties: Dict[str, Expression] = dict(self._parse_properties())
657
- if not self.token.is_closing_bracket():
658
- raise ValueError("Expected closing bracket for relationship definition")
659
- self.set_next_token()
660
- if not self.token.is_subtract():
661
- raise ValueError("Expected '-' for relationship definition")
662
- self.set_next_token()
663
- if self.token.is_greater_than():
664
- self.set_next_token()
665
- relationship = Relationship()
666
- relationship.direction = direction
667
- relationship.properties = properties
668
- if variable is not None and variable in self._state.variables:
669
- reference = self._state.variables.get(variable)
670
- # Resolve through Expression -> Reference -> Relationship (e.g., after WITH)
671
- first = reference.first_child() if isinstance(reference, Expression) else None
672
- if isinstance(first, Reference):
673
- inner = first.referred
674
- if isinstance(inner, Relationship):
675
- reference = inner
676
- if reference is None or not isinstance(reference, Relationship):
677
- raise ValueError(f"Undefined relationship reference: {variable}")
678
- relationship = RelationshipReference(relationship, reference)
679
- elif variable is not None:
680
- relationship.identifier = variable
681
- self._state.variables[variable] = relationship
682
- if hops is not None:
683
- relationship.hops = hops
684
- relationship.types = rel_types
685
- return relationship
686
-
687
- def _parse_properties(self) -> Iterator[Tuple[str, Expression]]:
688
- parts: int = 0
689
- while True:
690
- self._skip_whitespace_and_comments()
691
- if not self.token.is_opening_brace() and parts == 0:
692
- return
693
- elif not self.token.is_opening_brace() and parts > 0:
694
- raise ValueError("Expected opening brace")
695
- self.set_next_token()
696
- self._skip_whitespace_and_comments()
697
- if not self.token.is_identifier():
698
- raise ValueError("Expected identifier")
699
- key: str = self.token.value or ""
700
- self.set_next_token()
701
- self._skip_whitespace_and_comments()
702
- if not self.token.is_colon():
703
- raise ValueError("Expected colon")
704
- self.set_next_token()
705
- self._skip_whitespace_and_comments()
706
- expression = self._parse_expression()
707
- if expression is None:
708
- raise ValueError("Expected expression")
709
- self._skip_whitespace_and_comments()
710
- if not self.token.is_closing_brace():
711
- raise ValueError("Expected closing brace")
712
- self.set_next_token()
713
- yield (key, expression)
714
- self._skip_whitespace_and_comments()
715
- if not self.token.is_comma():
716
- break
717
- self.set_next_token()
718
- parts += 1
719
-
720
- def _parse_relationship_hops(self) -> Optional[Hops]:
721
- if not self.token.is_multiply():
722
- return None
723
- hops = Hops()
724
- self.set_next_token()
725
- if self.token.is_number():
726
- hops.min = int(self.token.value or "0")
727
- self.set_next_token()
728
- if self.token.is_dot():
729
- self.set_next_token()
730
- if not self.token.is_dot():
731
- raise ValueError("Expected '..' for relationship hops")
732
- self.set_next_token()
733
- if not self.token.is_number():
734
- hops.max = sys.maxsize
735
- else:
736
- hops.max = int(self.token.value or "0")
737
- self.set_next_token()
738
- else:
739
- # Just * without numbers means unbounded
740
- hops.min = 0
741
- hops.max = sys.maxsize
742
- return hops
743
-
744
- def _parse_limit(self) -> Optional[Limit]:
745
- self._skip_whitespace_and_comments()
746
- if not self.token.is_limit():
747
- return None
748
- self._expect_previous_token_to_be_whitespace_or_comment()
749
- self.set_next_token()
750
- self._expect_and_skip_whitespace_and_comments()
751
- if not self.token.is_number():
752
- raise ValueError("Expected number")
753
- limit = Limit(int(self.token.value or "0"))
754
- self.set_next_token()
755
- return limit
756
-
757
- def _parse_order_by(self) -> Optional[OrderBy]:
758
- self._skip_whitespace_and_comments()
759
- if not self.token.is_order():
760
- return None
761
- self._expect_previous_token_to_be_whitespace_or_comment()
762
- self.set_next_token()
763
- self._expect_and_skip_whitespace_and_comments()
764
- if not self.token.is_by():
765
- raise ValueError("Expected BY after ORDER")
766
- self.set_next_token()
767
- self._expect_and_skip_whitespace_and_comments()
768
- fields: list[SortField] = []
769
- while True:
770
- expression = self._parse_expression()
771
- if expression is None:
772
- raise ValueError("Expected expression in ORDER BY")
773
- self._skip_whitespace_and_comments()
774
- direction = "asc"
775
- if self.token.is_asc():
776
- direction = "asc"
777
- self.set_next_token()
778
- self._skip_whitespace_and_comments()
779
- elif self.token.is_desc():
780
- direction = "desc"
781
- self.set_next_token()
782
- self._skip_whitespace_and_comments()
783
- fields.append(SortField(expression, direction))
784
- if self.token.is_comma():
785
- self.set_next_token()
786
- self._skip_whitespace_and_comments()
787
- else:
788
- break
789
- return OrderBy(fields)
790
-
791
- def _parse_expressions(
792
- self, alias_option: AliasOption = AliasOption.NOT_ALLOWED
793
- ) -> List[Expression]:
794
- """Parse a comma-separated list of expressions with deferred variable
795
- registration. Aliases set by earlier expressions in the same clause
796
- won't shadow variables needed by later expressions
797
- (e.g. ``RETURN a.x AS a, a.y AS b``)."""
798
- parsed = list(self.__parse_expressions(alias_option))
799
- for expression, variable_name in parsed:
800
- if variable_name is not None:
801
- self._state.variables[variable_name] = expression
802
- return [expression for expression, _ in parsed]
803
-
804
- def __parse_expressions(
805
- self, alias_option: AliasOption
806
- ) -> Iterator[Tuple[Expression, Optional[str]]]:
807
- while True:
808
- expression = self._parse_expression()
809
- if expression is not None:
810
- variable_name: Optional[str] = None
811
- alias = self._parse_alias()
812
- if isinstance(expression.first_child(), Reference) and alias is None:
813
- reference = expression.first_child()
814
- assert isinstance(reference, Reference) # For type narrowing
815
- expression.set_alias(reference.identifier)
816
- variable_name = reference.identifier
817
- elif (alias_option == AliasOption.REQUIRED and
818
- alias is None and
819
- not isinstance(expression.first_child(), Reference)):
820
- raise ValueError("Alias required")
821
- elif alias_option == AliasOption.NOT_ALLOWED and alias is not None:
822
- raise ValueError("Alias not allowed")
823
- elif alias_option in (AliasOption.OPTIONAL, AliasOption.REQUIRED) and alias is not None:
824
- expression.set_alias(alias.get_alias())
825
- variable_name = alias.get_alias()
826
- yield expression, variable_name
827
- else:
828
- break
829
- self._skip_whitespace_and_comments()
830
- if not self.token.is_comma():
831
- break
832
- self.set_next_token()
833
-
834
- def _parse_operand(self, expression: Expression) -> bool:
835
- """Parse a single operand (without operators). Returns True if an operand was parsed."""
836
- self._skip_whitespace_and_comments()
837
- if self.token.is_identifier_or_keyword() and (self.peek() is None or not self.peek().is_left_parenthesis()):
838
- identifier = self.token.value or ""
839
- reference = Reference(identifier, self._state.variables.get(identifier))
840
- self.set_next_token()
841
- lookup = self._parse_lookup(reference)
842
- expression.add_node(lookup)
843
- return True
844
- elif self.token.is_identifier_or_keyword() and self.peek() is not None and self.peek().is_left_parenthesis():
845
- func = self._parse_predicate_function() or self._parse_function()
846
- if func is not None:
847
- lookup = self._parse_lookup(func)
848
- expression.add_node(lookup)
849
- return True
850
- elif (
851
- self.token.is_left_parenthesis()
852
- and self._looks_like_node_pattern()
853
- ):
854
- # Possible graph pattern expression
855
- pattern = self._parse_pattern_expression()
856
- if pattern is not None:
857
- expression.add_node(pattern)
858
- return True
859
- elif self.token.is_operand():
860
- expression.add_node(self.token.node)
861
- self.set_next_token()
862
- return True
863
- elif self.token.is_f_string():
864
- f_string = self._parse_f_string()
865
- if f_string is None:
866
- raise ValueError("Expected f-string")
867
- expression.add_node(f_string)
868
- return True
869
- elif self.token.is_left_parenthesis():
870
- self.set_next_token()
871
- sub = self._parse_expression()
872
- if sub is None:
873
- raise ValueError("Expected expression")
874
- if not self.token.is_right_parenthesis():
875
- raise ValueError("Expected right parenthesis")
876
- self.set_next_token()
877
- lookup = self._parse_lookup(sub)
878
- expression.add_node(lookup)
879
- return True
880
- elif self.token.is_opening_brace() or self.token.is_opening_bracket():
881
- json = self._parse_json()
882
- if json is None:
883
- raise ValueError("Expected JSON object")
884
- lookup = self._parse_lookup(json)
885
- expression.add_node(lookup)
886
- return True
887
- elif self.token.is_case():
888
- case = self._parse_case()
889
- if case is None:
890
- raise ValueError("Expected CASE statement")
891
- expression.add_node(case)
892
- return True
893
- elif self.token.is_not():
894
- not_node = Not()
895
- self.set_next_token()
896
- # NOT should only bind to the next operand, not the entire expression
897
- # Create a temporary expression to parse just one operand
898
- temp_expr = Expression()
899
- if not self._parse_operand(temp_expr):
900
- raise ValueError("Expected expression after NOT")
901
- temp_expr.finish()
902
- not_node.add_child(temp_expr)
903
- expression.add_node(not_node)
904
- return True
905
- return False
906
-
907
- def _parse_expression(self) -> Optional[Expression]:
908
- expression = Expression()
909
- while True:
910
- if not self._parse_operand(expression):
911
- if expression.nodes_added():
912
- raise ValueError("Expected operand or left parenthesis")
913
- else:
914
- break
915
- self._skip_whitespace_and_comments()
916
- if self.token.is_operator():
917
- if self.token.is_is():
918
- expression.add_node(self._parse_is_operator())
919
- else:
920
- expression.add_node(self.token.node)
921
- elif self.token.is_in():
922
- expression.add_node(self._parse_in_operator())
923
- elif self.token.is_contains():
924
- expression.add_node(self._parse_contains_operator())
925
- elif self.token.is_starts():
926
- expression.add_node(self._parse_starts_with_operator())
927
- elif self.token.is_ends():
928
- expression.add_node(self._parse_ends_with_operator())
929
- elif self.token.is_not():
930
- not_op = self._parse_not_operator()
931
- if not_op is None:
932
- break
933
- expression.add_node(not_op)
934
- else:
935
- break
936
- self.set_next_token()
937
-
938
- if expression.nodes_added():
939
- expression.finish()
940
- return expression
941
- return None
942
-
943
- def _looks_like_node_pattern(self) -> bool:
944
- """Peek ahead from a left parenthesis to determine whether the
945
- upcoming tokens form a graph-node pattern (e.g. (n:Label), (n),
946
- (:Label), ()) rather than a parenthesised expression (e.g.
947
- (variable.property), (a + b)).
948
- """
949
- saved_index = self._token_index
950
- self.set_next_token() # skip '('
951
- self._skip_whitespace_and_comments()
952
-
953
- if self.token.is_colon() or self.token.is_right_parenthesis():
954
- self._token_index = saved_index
955
- return True
956
-
957
- if self.token.is_identifier_or_keyword():
958
- self.set_next_token() # skip identifier
959
- self._skip_whitespace_and_comments()
960
- result = (
961
- self.token.is_colon()
962
- or self.token.is_opening_brace()
963
- or self.token.is_right_parenthesis()
964
- )
965
- self._token_index = saved_index
966
- return result
967
-
968
- self._token_index = saved_index
969
- return False
970
-
971
- def _parse_is_operator(self) -> ASTNode:
972
- """Parse IS or IS NOT operator."""
973
- # Current token is IS. Look ahead for NOT to produce IS NOT.
974
- saved_index = self._token_index
975
- self.set_next_token()
976
- self._skip_whitespace_and_comments()
977
- if self.token.is_not():
978
- return IsNot()
979
- # Not IS NOT — restore position to IS so the outer loop's set_next_token advances past it.
980
- self._token_index = saved_index
981
- return Is()
982
-
983
- def _parse_in_operator(self) -> In:
984
- """Parse IN operator."""
985
- # Current token is IN. Advance past it so the outer loop's set_next_token moves correctly.
986
- return In()
987
-
988
- def _parse_contains_operator(self) -> Contains:
989
- """Parse CONTAINS operator."""
990
- return Contains()
991
-
992
- def _parse_starts_with_operator(self) -> StartsWith:
993
- """Parse STARTS WITH operator."""
994
- # Current token is STARTS. Look ahead for WITH.
995
- saved_index = self._token_index
996
- self.set_next_token()
997
- self._skip_whitespace_and_comments()
998
- if self.token.is_with():
999
- return StartsWith()
1000
- self._token_index = saved_index
1001
- raise ValueError("Expected WITH after STARTS")
1002
-
1003
- def _parse_ends_with_operator(self) -> EndsWith:
1004
- """Parse ENDS WITH operator."""
1005
- # Current token is ENDS. Look ahead for WITH.
1006
- saved_index = self._token_index
1007
- self.set_next_token()
1008
- self._skip_whitespace_and_comments()
1009
- if self.token.is_with():
1010
- return EndsWith()
1011
- self._token_index = saved_index
1012
- raise ValueError("Expected WITH after ENDS")
1013
-
1014
- def _parse_not_operator(self) -> NotIn | NotContains | NotStartsWith | NotEndsWith | None:
1015
- """Parse NOT IN, NOT CONTAINS, NOT STARTS WITH, or NOT ENDS WITH operator."""
1016
- saved_index = self._token_index
1017
- self.set_next_token()
1018
- self._skip_whitespace_and_comments()
1019
- if self.token.is_in():
1020
- return NotIn()
1021
- if self.token.is_contains():
1022
- return NotContains()
1023
- if self.token.is_starts():
1024
- self.set_next_token()
1025
- self._skip_whitespace_and_comments()
1026
- if self.token.is_with():
1027
- return NotStartsWith()
1028
- self._token_index = saved_index
1029
- return None
1030
- if self.token.is_ends():
1031
- self.set_next_token()
1032
- self._skip_whitespace_and_comments()
1033
- if self.token.is_with():
1034
- return NotEndsWith()
1035
- self._token_index = saved_index
1036
- return None
1037
- # Not a recognized NOT operator — restore position and let the outer loop break.
1038
- self._token_index = saved_index
1039
- return None
1040
-
1041
- def _parse_lookup(self, node: ASTNode) -> ASTNode:
1042
- variable = node
1043
- lookup: Lookup | RangeLookup | None = None
1044
- while True:
1045
- if self.token.is_dot():
1046
- self.set_next_token()
1047
- if not self.token.is_identifier() and not self.token.is_keyword():
1048
- raise ValueError("Expected identifier")
1049
- lookup = Lookup()
1050
- lookup.index = Identifier(self.token.value or "")
1051
- lookup.variable = variable
1052
- self.set_next_token()
1053
- elif self.token.is_opening_bracket():
1054
- self.set_next_token()
1055
- self._skip_whitespace_and_comments()
1056
- index = self._parse_expression()
1057
- to = None
1058
- self._skip_whitespace_and_comments()
1059
- if self.token.is_colon():
1060
- self.set_next_token()
1061
- self._skip_whitespace_and_comments()
1062
- lookup = RangeLookup()
1063
- to = self._parse_expression()
1064
- else:
1065
- if index is None:
1066
- raise ValueError("Expected expression")
1067
- lookup = Lookup()
1068
- self._skip_whitespace_and_comments()
1069
- if not self.token.is_closing_bracket():
1070
- raise ValueError("Expected closing bracket")
1071
- self.set_next_token()
1072
- if isinstance(lookup, RangeLookup):
1073
- lookup.from_ = index or Null()
1074
- lookup.to = to or Null()
1075
- elif isinstance(lookup, Lookup) and index is not None:
1076
- lookup.index = index
1077
- lookup.variable = variable
1078
- else:
1079
- break
1080
- variable = lookup or variable
1081
- return variable
1082
-
1083
- def _parse_case(self) -> Optional[Case]:
1084
- if not self.token.is_case():
1085
- return None
1086
- self.set_next_token()
1087
- case = Case()
1088
- parts = 0
1089
- self._expect_and_skip_whitespace_and_comments()
1090
- while True:
1091
- when = self._parse_when()
1092
- if when is None and parts == 0:
1093
- raise ValueError("Expected WHEN")
1094
- elif when is None and parts > 0:
1095
- break
1096
- elif when is not None:
1097
- case.add_child(when)
1098
- self._expect_and_skip_whitespace_and_comments()
1099
- then = self._parse_then()
1100
- if then is None:
1101
- raise ValueError("Expected THEN")
1102
- else:
1103
- case.add_child(then)
1104
- self._expect_and_skip_whitespace_and_comments()
1105
- parts += 1
1106
- else_ = self._parse_else()
1107
- if else_ is None:
1108
- raise ValueError("Expected ELSE")
1109
- else:
1110
- case.add_child(else_)
1111
- self._expect_and_skip_whitespace_and_comments()
1112
- if not self.token.is_end():
1113
- raise ValueError("Expected END")
1114
- self.set_next_token()
1115
- return case
1116
-
1117
- def _parse_when(self) -> Optional[When]:
1118
- if not self.token.is_when():
1119
- return None
1120
- self.set_next_token()
1121
- when = When()
1122
- self._expect_and_skip_whitespace_and_comments()
1123
- expression = self._parse_expression()
1124
- if expression is None:
1125
- raise ValueError("Expected expression")
1126
- when.add_child(expression)
1127
- return when
1128
-
1129
- def _parse_then(self) -> Optional[Then]:
1130
- if not self.token.is_then():
1131
- return None
1132
- self.set_next_token()
1133
- then = Then()
1134
- self._expect_and_skip_whitespace_and_comments()
1135
- expression = self._parse_expression()
1136
- if expression is None:
1137
- raise ValueError("Expected expression")
1138
- then.add_child(expression)
1139
- return then
1140
-
1141
- def _parse_else(self) -> Optional[Else]:
1142
- if not self.token.is_else():
1143
- return None
1144
- self.set_next_token()
1145
- else_ = Else()
1146
- self._expect_and_skip_whitespace_and_comments()
1147
- expression = self._parse_expression()
1148
- if expression is None:
1149
- raise ValueError("Expected expression")
1150
- else_.add_child(expression)
1151
- return else_
1152
-
1153
- def _parse_alias(self) -> Optional[Alias]:
1154
- self._skip_whitespace_and_comments()
1155
- if not self.token.is_as():
1156
- return None
1157
- self._expect_previous_token_to_be_whitespace_or_comment()
1158
- self.set_next_token()
1159
- self._expect_and_skip_whitespace_and_comments()
1160
- if not self.token.is_identifier_or_keyword():
1161
- raise ValueError("Expected identifier")
1162
- alias = Alias(self.token.value or "")
1163
- self.set_next_token()
1164
- return alias
1165
-
1166
- def _parse_predicate_function(self) -> Optional[PredicateFunction]:
1167
- """Parse a predicate function like sum(n in [...] | n where condition)."""
1168
- # Lookahead: identifier ( identifier in
1169
- if not self.ahead([
1170
- Token.IDENTIFIER(""),
1171
- Token.LEFT_PARENTHESIS(),
1172
- Token.IDENTIFIER(""),
1173
- Token.IN(),
1174
- ]):
1175
- return None
1176
- if self.token.value is None:
1177
- raise ValueError("Expected identifier")
1178
- func = FunctionFactory.create_predicate(self.token.value)
1179
- self.set_next_token()
1180
- if not self.token.is_left_parenthesis():
1181
- raise ValueError("Expected left parenthesis")
1182
- self.set_next_token()
1183
- self._skip_whitespace_and_comments()
1184
- if not self.token.is_identifier():
1185
- raise ValueError("Expected identifier")
1186
- reference = Reference(self.token.value)
1187
- self._state.variables[reference.identifier] = reference
1188
- func.add_child(reference)
1189
- self.set_next_token()
1190
- self._expect_and_skip_whitespace_and_comments()
1191
- if not self.token.is_in():
1192
- raise ValueError("Expected IN")
1193
- self.set_next_token()
1194
- self._expect_and_skip_whitespace_and_comments()
1195
- expression = self._parse_expression()
1196
- if expression is None:
1197
- raise ValueError("Expected expression")
1198
- if not ObjectUtils.is_instance_of_any(expression.first_child(), [
1199
- JSONArray,
1200
- Reference,
1201
- Lookup,
1202
- Function,
1203
- ]):
1204
- raise ValueError("Expected array or reference")
1205
- func.add_child(expression)
1206
- self._skip_whitespace_and_comments()
1207
- if not self.token.is_pipe():
1208
- raise ValueError("Expected pipe")
1209
- self.set_next_token()
1210
- return_expr = self._parse_expression()
1211
- if return_expr is None:
1212
- raise ValueError("Expected expression")
1213
- func.add_child(return_expr)
1214
- where = self._parse_where()
1215
- if where is not None:
1216
- func.add_child(where)
1217
- self._skip_whitespace_and_comments()
1218
- if not self.token.is_right_parenthesis():
1219
- raise ValueError("Expected right parenthesis")
1220
- self.set_next_token()
1221
- del self._state.variables[reference.identifier]
1222
- return func
1223
-
1224
- def _parse_function(self) -> Optional[Function]:
1225
- if not self.token.is_identifier():
1226
- return None
1227
- name = self.token.value or ""
1228
- if not self.peek() or not self.peek().is_left_parenthesis():
1229
- return None
1230
-
1231
- try:
1232
- func = FunctionFactory.create(name)
1233
- except ValueError:
1234
- raise ValueError(f"Unknown function: {name}")
1235
-
1236
- # Check for nested aggregate functions
1237
- if isinstance(func, AggregateFunction) and self._state.context.contains_type(AggregateFunction):
1238
- raise ValueError("Aggregate functions cannot be nested")
1239
-
1240
- self._state.context.push(func)
1241
- self.set_next_token() # skip function name
1242
- self.set_next_token() # skip left parenthesis
1243
- self._skip_whitespace_and_comments()
1244
-
1245
- # Check for DISTINCT keyword
1246
- if self.token.is_distinct():
1247
- func.distinct = True
1248
- self.set_next_token()
1249
- self._expect_and_skip_whitespace_and_comments()
1250
-
1251
- params = list(self._parse_function_parameters())
1252
- func.parameters = params
1253
-
1254
- if not self.token.is_right_parenthesis():
1255
- raise ValueError("Expected right parenthesis")
1256
- self.set_next_token()
1257
- self._state.context.pop()
1258
- return func
1259
-
1260
- def _parse_async_function(self) -> Optional[AsyncFunction]:
1261
- if not self.token.is_identifier():
1262
- return None
1263
- name = self.token.value or ""
1264
- if not FunctionFactory.is_async_provider(name):
1265
- return None
1266
- self.set_next_token()
1267
- if not self.token.is_left_parenthesis():
1268
- raise ValueError("Expected left parenthesis")
1269
- self.set_next_token()
1270
-
1271
- func = FunctionFactory.create_async(name)
1272
- params = list(self._parse_function_parameters())
1273
- func.parameters = params
1274
-
1275
- if not self.token.is_right_parenthesis():
1276
- raise ValueError("Expected right parenthesis")
1277
- self.set_next_token()
1278
- return func
1279
-
1280
- def _parse_function_parameters(self) -> Iterator[ASTNode]:
1281
- while True:
1282
- self._skip_whitespace_and_comments()
1283
- if self.token.is_right_parenthesis():
1284
- break
1285
- expr = self._parse_expression()
1286
- if expr is not None:
1287
- yield expr
1288
- self._skip_whitespace_and_comments()
1289
- if not self.token.is_comma():
1290
- break
1291
- self.set_next_token()
1292
-
1293
- def _parse_json(self) -> Optional[ASTNode]:
1294
- if self.token.is_opening_brace():
1295
- return self._parse_associative_array()
1296
- elif self.token.is_opening_bracket():
1297
- return self._parse_json_array()
1298
- return None
1299
-
1300
- def _parse_associative_array(self) -> AssociativeArray:
1301
- if not self.token.is_opening_brace():
1302
- raise ValueError("Expected opening brace")
1303
- self.set_next_token()
1304
- array = AssociativeArray()
1305
- while True:
1306
- self._skip_whitespace_and_comments()
1307
- if self.token.is_closing_brace():
1308
- break
1309
- if not self.token.is_identifier() and not self.token.is_string() and not self.token.is_keyword():
1310
- raise ValueError("Expected key identifier or string")
1311
- key = self.token.value or ""
1312
- self.set_next_token()
1313
- self._skip_whitespace_and_comments()
1314
- if not self.token.is_colon():
1315
- raise ValueError("Expected colon")
1316
- self.set_next_token()
1317
- self._skip_whitespace_and_comments()
1318
- value = self._parse_expression()
1319
- if value is None:
1320
- raise ValueError("Expected value")
1321
- array.add_key_value(KeyValuePair(key, value))
1322
- self._skip_whitespace_and_comments()
1323
- if not self.token.is_comma():
1324
- break
1325
- self.set_next_token()
1326
- if not self.token.is_closing_brace():
1327
- raise ValueError("Expected closing brace")
1328
- self.set_next_token()
1329
- return array
1330
-
1331
- def _parse_json_array(self) -> JSONArray:
1332
- if not self.token.is_opening_bracket():
1333
- raise ValueError("Expected opening bracket")
1334
- self.set_next_token()
1335
- array = JSONArray()
1336
- while True:
1337
- self._skip_whitespace_and_comments()
1338
- if self.token.is_closing_bracket():
1339
- break
1340
- value = self._parse_expression()
1341
- if value is None:
1342
- break
1343
- array.add_value(value)
1344
- self._skip_whitespace_and_comments()
1345
- if not self.token.is_comma():
1346
- break
1347
- self.set_next_token()
1348
- if not self.token.is_closing_bracket():
1349
- raise ValueError("Expected closing bracket")
1350
- self.set_next_token()
1351
- return array
1352
-
1353
- def _parse_f_string(self) -> Optional[FString]:
1354
- if not self.token.is_f_string():
1355
- return None
1356
- f_string = FString()
1357
- while self.token.is_f_string() or self.token.is_opening_brace():
1358
- if self.token.is_f_string():
1359
- f_string.add_child(String(self.token.value or ""))
1360
- self.set_next_token()
1361
- elif self.token.is_opening_brace():
1362
- self.set_next_token()
1363
- expr = self._parse_expression()
1364
- if expr is not None:
1365
- f_string.add_child(expr)
1366
- if self.token.is_closing_brace():
1367
- self.set_next_token()
1368
- return f_string
1369
-
1370
- def _skip_whitespace_and_comments(self) -> bool:
1371
- skipped: bool = self.previous_token.is_whitespace_or_comment() if self.previous_token else False
1372
- while self.token.is_whitespace_or_comment():
1373
- self.set_next_token()
1374
- skipped = True
1375
- return skipped
1376
-
1377
- def _expect_and_skip_whitespace_and_comments(self) -> None:
1378
- skipped = self._skip_whitespace_and_comments()
1379
- if not skipped:
1380
- raise ValueError("Expected whitespace")
1381
-
1382
- def _expect_previous_token_to_be_whitespace_or_comment(self) -> None:
1383
- if not self.previous_token.is_whitespace_or_comment():
1384
- raise ValueError("Expected previous token to be whitespace or comment")