flowquery 1.0.16 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) 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/operator.d.ts +1 -1
  82. package/dist/parsing/expressions/operator.d.ts.map +1 -1
  83. package/dist/parsing/expressions/operator.js.map +1 -1
  84. package/dist/parsing/functions/function_factory.d.ts +13 -13
  85. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  86. package/dist/parsing/functions/function_factory.js +20 -18
  87. package/dist/parsing/functions/function_factory.js.map +1 -1
  88. package/dist/parsing/operations/create_node.d.ts +14 -0
  89. package/dist/parsing/operations/create_node.d.ts.map +1 -0
  90. package/dist/parsing/operations/create_node.js +51 -0
  91. package/dist/parsing/operations/create_node.js.map +1 -0
  92. package/dist/parsing/operations/create_relationship.d.ts +14 -0
  93. package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
  94. package/dist/parsing/operations/create_relationship.js +51 -0
  95. package/dist/parsing/operations/create_relationship.js.map +1 -0
  96. package/dist/parsing/operations/match.d.ts +15 -0
  97. package/dist/parsing/operations/match.d.ts.map +1 -0
  98. package/dist/parsing/operations/match.js +45 -0
  99. package/dist/parsing/operations/match.js.map +1 -0
  100. package/dist/parsing/operations/operation.d.ts +1 -0
  101. package/dist/parsing/operations/operation.d.ts.map +1 -1
  102. package/dist/parsing/operations/operation.js +6 -0
  103. package/dist/parsing/operations/operation.js.map +1 -1
  104. package/dist/parsing/operations/return.d.ts +1 -0
  105. package/dist/parsing/operations/return.d.ts.map +1 -1
  106. package/dist/parsing/operations/return.js +7 -1
  107. package/dist/parsing/operations/return.js.map +1 -1
  108. package/dist/parsing/operations/where.d.ts +1 -1
  109. package/dist/parsing/operations/where.d.ts.map +1 -1
  110. package/dist/parsing/operations/where.js +4 -0
  111. package/dist/parsing/operations/where.js.map +1 -1
  112. package/dist/parsing/parser.d.ts +10 -0
  113. package/dist/parsing/parser.d.ts.map +1 -1
  114. package/dist/parsing/parser.js +344 -5
  115. package/dist/parsing/parser.js.map +1 -1
  116. package/dist/parsing/token_to_node.d.ts.map +1 -1
  117. package/dist/parsing/token_to_node.js +7 -0
  118. package/dist/parsing/token_to_node.js.map +1 -1
  119. package/dist/tokenization/keyword.d.ts +1 -0
  120. package/dist/tokenization/keyword.d.ts.map +1 -1
  121. package/dist/tokenization/keyword.js +1 -0
  122. package/dist/tokenization/keyword.js.map +1 -1
  123. package/dist/tokenization/token.d.ts +4 -0
  124. package/dist/tokenization/token.d.ts.map +1 -1
  125. package/dist/tokenization/token.js +14 -1
  126. package/dist/tokenization/token.js.map +1 -1
  127. package/dist/tokenization/token_type.d.ts +1 -0
  128. package/dist/tokenization/token_type.d.ts.map +1 -1
  129. package/dist/tokenization/token_type.js +1 -0
  130. package/dist/tokenization/token_type.js.map +1 -1
  131. package/dist/tokenization/tokenizer.d.ts +2 -1
  132. package/dist/tokenization/tokenizer.d.ts.map +1 -1
  133. package/dist/tokenization/tokenizer.js +25 -12
  134. package/dist/tokenization/tokenizer.js.map +1 -1
  135. package/docs/flowquery.min.js +1 -1
  136. package/flowquery-py/README.md +166 -0
  137. package/flowquery-py/pyproject.toml +75 -0
  138. package/flowquery-py/setup_env.ps1 +92 -0
  139. package/flowquery-py/setup_env.sh +87 -0
  140. package/flowquery-py/src/__init__.py +34 -0
  141. package/flowquery-py/src/__main__.py +10 -0
  142. package/flowquery-py/src/compute/__init__.py +5 -0
  143. package/flowquery-py/src/compute/runner.py +60 -0
  144. package/flowquery-py/src/extensibility.py +52 -0
  145. package/flowquery-py/src/graph/__init__.py +31 -0
  146. package/flowquery-py/src/graph/data.py +118 -0
  147. package/flowquery-py/src/graph/database.py +82 -0
  148. package/flowquery-py/src/graph/hops.py +43 -0
  149. package/flowquery-py/src/graph/node.py +112 -0
  150. package/flowquery-py/src/graph/node_data.py +26 -0
  151. package/flowquery-py/src/graph/node_reference.py +49 -0
  152. package/flowquery-py/src/graph/pattern.py +125 -0
  153. package/flowquery-py/src/graph/pattern_expression.py +62 -0
  154. package/flowquery-py/src/graph/patterns.py +42 -0
  155. package/flowquery-py/src/graph/physical_node.py +40 -0
  156. package/flowquery-py/src/graph/physical_relationship.py +36 -0
  157. package/flowquery-py/src/graph/relationship.py +135 -0
  158. package/flowquery-py/src/graph/relationship_data.py +33 -0
  159. package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
  160. package/flowquery-py/src/graph/relationship_reference.py +21 -0
  161. package/flowquery-py/src/io/__init__.py +5 -0
  162. package/flowquery-py/src/io/command_line.py +67 -0
  163. package/flowquery-py/src/parsing/__init__.py +17 -0
  164. package/flowquery-py/src/parsing/alias.py +20 -0
  165. package/flowquery-py/src/parsing/alias_option.py +11 -0
  166. package/flowquery-py/src/parsing/ast_node.py +146 -0
  167. package/flowquery-py/src/parsing/base_parser.py +84 -0
  168. package/flowquery-py/src/parsing/components/__init__.py +19 -0
  169. package/flowquery-py/src/parsing/components/csv.py +8 -0
  170. package/flowquery-py/src/parsing/components/from_.py +10 -0
  171. package/flowquery-py/src/parsing/components/headers.py +12 -0
  172. package/flowquery-py/src/parsing/components/json.py +8 -0
  173. package/flowquery-py/src/parsing/components/null.py +10 -0
  174. package/flowquery-py/src/parsing/components/post.py +8 -0
  175. package/flowquery-py/src/parsing/components/text.py +8 -0
  176. package/flowquery-py/src/parsing/context.py +50 -0
  177. package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
  178. package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
  179. package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
  180. package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
  181. package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
  182. package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
  183. package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
  184. package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
  185. package/flowquery-py/src/parsing/expressions/expression.py +138 -0
  186. package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
  187. package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
  188. package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
  189. package/flowquery-py/src/parsing/expressions/number.py +32 -0
  190. package/flowquery-py/src/parsing/expressions/operator.py +169 -0
  191. package/flowquery-py/src/parsing/expressions/reference.py +47 -0
  192. package/flowquery-py/src/parsing/expressions/string.py +27 -0
  193. package/flowquery-py/src/parsing/functions/__init__.py +75 -0
  194. package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
  195. package/flowquery-py/src/parsing/functions/async_function.py +62 -0
  196. package/flowquery-py/src/parsing/functions/avg.py +55 -0
  197. package/flowquery-py/src/parsing/functions/collect.py +75 -0
  198. package/flowquery-py/src/parsing/functions/function.py +68 -0
  199. package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
  200. package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
  201. package/flowquery-py/src/parsing/functions/functions.py +59 -0
  202. package/flowquery-py/src/parsing/functions/join.py +47 -0
  203. package/flowquery-py/src/parsing/functions/keys.py +34 -0
  204. package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
  205. package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
  206. package/flowquery-py/src/parsing/functions/rand.py +28 -0
  207. package/flowquery-py/src/parsing/functions/range_.py +34 -0
  208. package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
  209. package/flowquery-py/src/parsing/functions/replace.py +37 -0
  210. package/flowquery-py/src/parsing/functions/round_.py +32 -0
  211. package/flowquery-py/src/parsing/functions/size.py +32 -0
  212. package/flowquery-py/src/parsing/functions/split.py +47 -0
  213. package/flowquery-py/src/parsing/functions/stringify.py +47 -0
  214. package/flowquery-py/src/parsing/functions/sum.py +51 -0
  215. package/flowquery-py/src/parsing/functions/to_json.py +33 -0
  216. package/flowquery-py/src/parsing/functions/type_.py +47 -0
  217. package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
  218. package/flowquery-py/src/parsing/logic/__init__.py +15 -0
  219. package/flowquery-py/src/parsing/logic/case.py +29 -0
  220. package/flowquery-py/src/parsing/logic/else_.py +12 -0
  221. package/flowquery-py/src/parsing/logic/end.py +8 -0
  222. package/flowquery-py/src/parsing/logic/then.py +12 -0
  223. package/flowquery-py/src/parsing/logic/when.py +10 -0
  224. package/flowquery-py/src/parsing/operations/__init__.py +35 -0
  225. package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
  226. package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
  227. package/flowquery-py/src/parsing/operations/call.py +74 -0
  228. package/flowquery-py/src/parsing/operations/create_node.py +34 -0
  229. package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
  230. package/flowquery-py/src/parsing/operations/group_by.py +130 -0
  231. package/flowquery-py/src/parsing/operations/limit.py +22 -0
  232. package/flowquery-py/src/parsing/operations/load.py +140 -0
  233. package/flowquery-py/src/parsing/operations/match.py +29 -0
  234. package/flowquery-py/src/parsing/operations/operation.py +69 -0
  235. package/flowquery-py/src/parsing/operations/projection.py +21 -0
  236. package/flowquery-py/src/parsing/operations/return_op.py +50 -0
  237. package/flowquery-py/src/parsing/operations/unwind.py +37 -0
  238. package/flowquery-py/src/parsing/operations/where.py +41 -0
  239. package/flowquery-py/src/parsing/operations/with_op.py +18 -0
  240. package/flowquery-py/src/parsing/parser.py +1011 -0
  241. package/flowquery-py/src/parsing/token_to_node.py +109 -0
  242. package/flowquery-py/src/tokenization/__init__.py +23 -0
  243. package/flowquery-py/src/tokenization/keyword.py +48 -0
  244. package/flowquery-py/src/tokenization/operator.py +29 -0
  245. package/flowquery-py/src/tokenization/string_walker.py +158 -0
  246. package/flowquery-py/src/tokenization/symbol.py +19 -0
  247. package/flowquery-py/src/tokenization/token.py +659 -0
  248. package/flowquery-py/src/tokenization/token_mapper.py +52 -0
  249. package/flowquery-py/src/tokenization/token_type.py +21 -0
  250. package/flowquery-py/src/tokenization/tokenizer.py +214 -0
  251. package/flowquery-py/src/tokenization/trie.py +124 -0
  252. package/flowquery-py/src/utils/__init__.py +6 -0
  253. package/flowquery-py/src/utils/object_utils.py +20 -0
  254. package/flowquery-py/src/utils/string_utils.py +113 -0
  255. package/flowquery-py/tests/__init__.py +1 -0
  256. package/flowquery-py/tests/compute/__init__.py +1 -0
  257. package/flowquery-py/tests/compute/test_runner.py +1335 -0
  258. package/flowquery-py/tests/graph/__init__.py +1 -0
  259. package/flowquery-py/tests/graph/test_create.py +56 -0
  260. package/flowquery-py/tests/graph/test_data.py +73 -0
  261. package/flowquery-py/tests/graph/test_match.py +40 -0
  262. package/flowquery-py/tests/parsing/__init__.py +1 -0
  263. package/flowquery-py/tests/parsing/test_context.py +34 -0
  264. package/flowquery-py/tests/parsing/test_expression.py +49 -0
  265. package/flowquery-py/tests/parsing/test_parser.py +674 -0
  266. package/flowquery-py/tests/test_extensibility.py +611 -0
  267. package/flowquery-py/tests/tokenization/__init__.py +1 -0
  268. package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
  269. package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
  270. package/flowquery-py/tests/tokenization/test_trie.py +30 -0
  271. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  272. package/misc/apps/RAG/package.json +1 -1
  273. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
  274. package/misc/apps/RAG/src/components/index.ts +19 -10
  275. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +70 -140
  276. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +12 -0
  277. package/package.json +1 -1
  278. package/src/compute/runner.ts +24 -19
  279. package/src/graph/data.ts +112 -0
  280. package/src/graph/database.ts +63 -0
  281. package/src/graph/hops.ts +22 -0
  282. package/src/graph/node.ts +99 -0
  283. package/src/graph/node_data.ts +18 -0
  284. package/src/graph/node_reference.ts +33 -0
  285. package/src/graph/pattern.ts +101 -0
  286. package/src/graph/pattern_expression.ts +37 -0
  287. package/src/graph/patterns.ts +36 -0
  288. package/src/graph/physical_node.ts +23 -0
  289. package/src/graph/physical_relationship.ts +23 -0
  290. package/src/graph/relationship.ts +116 -0
  291. package/src/graph/relationship_data.ts +27 -0
  292. package/src/graph/relationship_match_collector.ts +58 -0
  293. package/src/graph/relationship_reference.ts +24 -0
  294. package/src/parsing/base_parser.ts +20 -14
  295. package/src/parsing/context.ts +14 -14
  296. package/src/parsing/expressions/boolean.ts +21 -0
  297. package/src/parsing/expressions/expression.ts +34 -26
  298. package/src/parsing/expressions/operator.ts +19 -1
  299. package/src/parsing/functions/function_factory.ts +45 -45
  300. package/src/parsing/operations/create_node.ts +39 -0
  301. package/src/parsing/operations/create_relationship.ts +38 -0
  302. package/src/parsing/operations/match.ts +31 -0
  303. package/src/parsing/operations/operation.ts +3 -0
  304. package/src/parsing/operations/return.ts +11 -7
  305. package/src/parsing/operations/where.ts +10 -6
  306. package/src/parsing/parser.ts +346 -8
  307. package/src/parsing/token_to_node.ts +6 -0
  308. package/src/tokenization/keyword.ts +41 -40
  309. package/src/tokenization/token.ts +21 -1
  310. package/src/tokenization/token_type.ts +2 -1
  311. package/src/tokenization/tokenizer.ts +52 -31
  312. package/tests/compute/runner.test.ts +654 -0
  313. package/tests/extensibility.test.ts +97 -93
  314. package/tests/graph/create.test.ts +36 -0
  315. package/tests/graph/data.test.ts +58 -0
  316. package/tests/graph/match.test.ts +29 -0
  317. package/tests/parsing/parser.test.ts +273 -2
  318. package/tests/tokenization/tokenizer.test.ts +90 -0
@@ -0,0 +1,21 @@
1
+ """Token type enumeration for FlowQuery tokenization."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class TokenType(Enum):
7
+ """Enumeration of all token types in FlowQuery."""
8
+
9
+ KEYWORD = "KEYWORD"
10
+ BOOLEAN = "BOOLEAN"
11
+ OPERATOR = "OPERATOR"
12
+ UNARY_OPERATOR = "UNARY_OPERATOR"
13
+ IDENTIFIER = "IDENTIFIER"
14
+ STRING = "STRING"
15
+ F_STRING = "F-STRING"
16
+ BACKTICK_STRING = "BACKTICK_STRING"
17
+ NUMBER = "NUMBER"
18
+ SYMBOL = "SYMBOL"
19
+ WHITESPACE = "WHITESPACE"
20
+ COMMENT = "COMMENT"
21
+ EOF = "EOF"
@@ -0,0 +1,214 @@
1
+ """Tokenizes FlowQuery input strings into a sequence of tokens."""
2
+
3
+ from typing import List, Optional, Iterator, Callable
4
+
5
+ from ..utils.string_utils import StringUtils
6
+ from .keyword import Keyword
7
+ from .operator import Operator
8
+ from .string_walker import StringWalker
9
+ from .symbol import Symbol
10
+ from .token import Token
11
+ from .token_mapper import TokenMapper
12
+
13
+
14
+ class Tokenizer:
15
+ """Tokenizes FlowQuery input strings into a sequence of tokens.
16
+
17
+ The tokenizer performs lexical analysis, breaking down the input text into
18
+ meaningful tokens such as keywords, identifiers, operators, strings, numbers,
19
+ and symbols. It handles comments, whitespace, and f-strings.
20
+
21
+ Example:
22
+ tokenizer = Tokenizer("WITH x = 1 RETURN x")
23
+ tokens = tokenizer.tokenize()
24
+ """
25
+
26
+ def __init__(self, input_: str):
27
+ """Creates a new Tokenizer instance for the given input.
28
+
29
+ Args:
30
+ input_: The FlowQuery input string to tokenize
31
+ """
32
+ self._walker = StringWalker(input_)
33
+ self._keywords = TokenMapper(Keyword)
34
+ self._symbols = TokenMapper(Symbol)
35
+ self._operators = TokenMapper(Operator)
36
+
37
+ def tokenize(self) -> List[Token]:
38
+ """Tokenizes the input string into an array of tokens.
39
+
40
+ Returns:
41
+ An array of Token objects representing the tokenized input
42
+
43
+ Raises:
44
+ ValueError: If an unrecognized token is encountered
45
+ """
46
+ tokens: List[Token] = []
47
+ last: Optional[Token] = None
48
+
49
+ while not self._walker.is_at_end:
50
+ tokens.extend(self._f_string())
51
+ last = self._get_last_non_whitespace_or_non_comment_token(tokens) or last
52
+ token = self._get_next_token(last)
53
+ if token is None:
54
+ raise ValueError(f"Unrecognized token at position {self._walker.position}")
55
+ token.position = self._walker.position
56
+ tokens.append(token)
57
+
58
+ return tokens
59
+
60
+ def _get_last_non_whitespace_or_non_comment_token(self, tokens: List[Token]) -> Optional[Token]:
61
+ if len(tokens) == 0:
62
+ return None
63
+ if not tokens[-1].is_whitespace_or_comment():
64
+ return tokens[-1]
65
+ return None
66
+
67
+ def _get_next_token(self, last: Optional[Token] = None) -> Optional[Token]:
68
+ if self._walker.is_at_end:
69
+ return Token.EOF
70
+ return (
71
+ self._comment() or
72
+ self._whitespace() or
73
+ self._lookup(self._keywords) or
74
+ self._lookup(self._operators, last, self._skip_minus) or
75
+ self._boolean() or
76
+ self._identifier() or
77
+ self._string() or
78
+ self._number() or
79
+ self._lookup(self._symbols)
80
+ )
81
+
82
+ def _comment(self) -> Optional[Token]:
83
+ start_position = self._walker.position
84
+ if self._walker.check_for_single_comment() or self._walker.check_for_multi_line_comment():
85
+ uncommented = StringUtils.uncomment(self._walker.get_string(start_position))
86
+ return Token.COMMENT(uncommented)
87
+ return None
88
+
89
+ def _boolean(self) -> Optional[Token]:
90
+ start_position = self._walker.position
91
+ if self._walker.check_for_string("TRUE"):
92
+ return Token.BOOLEAN(self._walker.get_string(start_position).upper())
93
+ if self._walker.check_for_string("FALSE"):
94
+ return Token.BOOLEAN(self._walker.get_string(start_position).upper())
95
+ return None
96
+
97
+ def _identifier(self) -> Optional[Token]:
98
+ start_position = self._walker.position
99
+ if self._walker.check_for_under_score() or self._walker.check_for_letter():
100
+ while (not self._walker.is_at_end and
101
+ (self._walker.check_for_letter() or
102
+ self._walker.check_for_digit() or
103
+ self._walker.check_for_under_score())):
104
+ pass
105
+ return Token.IDENTIFIER(self._walker.get_string(start_position))
106
+ return None
107
+
108
+ def _string(self) -> Optional[Token]:
109
+ start_position = self._walker.position
110
+ quote_char = self._walker.check_for_quote()
111
+ if quote_char is None:
112
+ return None
113
+
114
+ while not self._walker.is_at_end:
115
+ if self._walker.escaped(quote_char):
116
+ self._walker.move_next()
117
+ self._walker.move_next()
118
+ continue
119
+ if self._walker.check_for_string(quote_char):
120
+ value = self._walker.get_string(start_position)
121
+ if quote_char == Symbol.BACKTICK.value:
122
+ return Token.BACKTICK_STRING(value, quote_char)
123
+ return Token.STRING(value, quote_char)
124
+ self._walker.move_next()
125
+
126
+ raise ValueError(f"Unterminated string at position {start_position}")
127
+
128
+ def _f_string(self) -> Iterator[Token]:
129
+ if not self._walker.check_for_f_string_start():
130
+ return
131
+
132
+ self._walker.move_next() # skip the f
133
+ position = self._walker.position
134
+ quote_char = self._walker.check_for_quote()
135
+ if quote_char is None:
136
+ return
137
+
138
+ while not self._walker.is_at_end:
139
+ if self._walker.escaped(quote_char) or self._walker.escaped_brace():
140
+ self._walker.move_next()
141
+ self._walker.move_next()
142
+ continue
143
+
144
+ if self._walker.opening_brace():
145
+ yield Token.F_STRING(self._walker.get_string(position), quote_char)
146
+ position = self._walker.position
147
+ yield Token.OPENING_BRACE
148
+ self._walker.move_next() # skip the opening brace
149
+ position = self._walker.position
150
+
151
+ while not self._walker.is_at_end and not self._walker.closing_brace():
152
+ token = self._get_next_token()
153
+ if token is not None:
154
+ yield token
155
+ else:
156
+ break
157
+ if self._walker.closing_brace():
158
+ yield Token.CLOSING_BRACE
159
+ self._walker.move_next() # skip the closing brace
160
+ position = self._walker.position
161
+ break
162
+
163
+ if self._walker.check_for_string(quote_char):
164
+ yield Token.F_STRING(self._walker.get_string(position), quote_char)
165
+ return
166
+
167
+ self._walker.move_next()
168
+
169
+ def _whitespace(self) -> Optional[Token]:
170
+ found_whitespace = False
171
+ while not self._walker.is_at_end and self._walker.check_for_whitespace():
172
+ self._walker.move_next()
173
+ found_whitespace = True
174
+ return Token.WHITESPACE if found_whitespace else None
175
+
176
+ def _number(self) -> Optional[Token]:
177
+ start_position = self._walker.position
178
+ if self._walker.check_for_string("-") or self._walker.check_for_digit():
179
+ while not self._walker.is_at_end and self._walker.check_for_digit():
180
+ pass
181
+ if self._walker.check_for_string(Symbol.DOT.value):
182
+ decimal_digits = 0
183
+ while not self._walker.is_at_end and self._walker.check_for_digit():
184
+ decimal_digits += 1
185
+ if decimal_digits == 0:
186
+ self._walker.move_previous()
187
+ number_str = self._walker.get_string(start_position)
188
+ return Token.NUMBER(number_str)
189
+ return None
190
+
191
+ def _lookup(
192
+ self,
193
+ mapper: TokenMapper,
194
+ last: Optional[Token] = None,
195
+ skip: Optional[Callable[[Optional[Token], Token], bool]] = None
196
+ ) -> Optional[Token]:
197
+ token = mapper.map(self._walker.get_remaining_string())
198
+ if token is not None and token.value is not None:
199
+ if token.can_be_identifier and self._walker.word_continuation(token.value):
200
+ return None
201
+ if skip and last and skip(last, token):
202
+ return None
203
+ self._walker.move_by(len(token.value))
204
+ if mapper.last_found is not None:
205
+ token.case_sensitive_value = mapper.last_found
206
+ return token
207
+ return None
208
+
209
+ def _skip_minus(self, last: Optional[Token], current: Token) -> bool:
210
+ if last is None:
211
+ return False
212
+ if (last.is_keyword() or last.is_comma() or last.is_colon()) and current.is_negation():
213
+ return True
214
+ return False
@@ -0,0 +1,124 @@
1
+ """Trie (prefix tree) data structure for efficient keyword and operator lookup."""
2
+
3
+ from __future__ import annotations
4
+ from typing import TYPE_CHECKING, Optional
5
+
6
+ if TYPE_CHECKING:
7
+ from .token import Token
8
+
9
+
10
+ class TrieNode:
11
+ """Represents a node in a Trie data structure.
12
+
13
+ Each node can have children nodes (one per character) and may contain a token
14
+ if the path to this node represents a complete word.
15
+ """
16
+
17
+ def __init__(self):
18
+ self._children: dict[str, TrieNode] = {}
19
+ self._token: Optional[Token] = None
20
+
21
+ def map(self, char: str) -> TrieNode:
22
+ if char not in self._children:
23
+ self._children[char] = TrieNode()
24
+ return self._children[char]
25
+
26
+ def retrieve(self, char: str) -> Optional[TrieNode]:
27
+ return self._children.get(char)
28
+
29
+ @property
30
+ def token(self) -> Optional[Token]:
31
+ return self._token
32
+
33
+ @token.setter
34
+ def token(self, token: Token) -> None:
35
+ self._token = token
36
+
37
+ def is_end_of_word(self) -> bool:
38
+ return self._token is not None
39
+
40
+ def no_children(self) -> bool:
41
+ return len(self._children) == 0
42
+
43
+
44
+ class Trie:
45
+ """Trie (prefix tree) data structure for efficient keyword and operator lookup.
46
+
47
+ Used during tokenization to quickly match input strings against known keywords
48
+ and operators. Supports case-insensitive matching and tracks the longest match found.
49
+
50
+ Example:
51
+ trie = Trie()
52
+ trie.insert(Token.WITH)
53
+ found = trie.find("WITH")
54
+ """
55
+
56
+ def __init__(self):
57
+ self._root = TrieNode()
58
+ self._max_length = 0
59
+ self._last_found: Optional[str] = None
60
+
61
+ def insert(self, token: Token) -> None:
62
+ """Inserts a token into the trie.
63
+
64
+ Args:
65
+ token: The token to insert
66
+
67
+ Raises:
68
+ ValueError: If the token value is None or empty
69
+ """
70
+ if token.value is None or len(token.value) == 0:
71
+ raise ValueError("Token value cannot be null or empty")
72
+
73
+ current_node = self._root
74
+ for char in token.value:
75
+ current_node = current_node.map(char.lower())
76
+
77
+ if len(token.value) > self._max_length:
78
+ self._max_length = len(token.value)
79
+
80
+ current_node.token = token
81
+
82
+ def find(self, value: str) -> Optional[Token]:
83
+ """Finds a token by searching for the longest matching prefix in the trie.
84
+
85
+ Args:
86
+ value: The string value to search for
87
+
88
+ Returns:
89
+ The token if found, None otherwise
90
+ """
91
+ if len(value) == 0:
92
+ return None
93
+
94
+ index = 0
95
+ current: Optional[TrieNode] = None
96
+ found: Optional[Token] = None
97
+ self._last_found = None
98
+
99
+ while True:
100
+ next_node = (current or self._root).retrieve(value[index].lower())
101
+ if next_node is None:
102
+ break
103
+ current = next_node
104
+ if current.is_end_of_word():
105
+ found = current.token
106
+ self._last_found = value[:index + 1]
107
+ index += 1
108
+ if index >= len(value) or index > self._max_length:
109
+ break
110
+
111
+ if current is not None and current.is_end_of_word():
112
+ found = current.token
113
+ self._last_found = value[:index]
114
+
115
+ return found
116
+
117
+ @property
118
+ def last_found(self) -> Optional[str]:
119
+ """Gets the last matched string from the most recent find operation.
120
+
121
+ Returns:
122
+ The last found string, or None if no match was found
123
+ """
124
+ return self._last_found
@@ -0,0 +1,6 @@
1
+ """Utils module for FlowQuery."""
2
+
3
+ from .string_utils import StringUtils
4
+ from .object_utils import ObjectUtils
5
+
6
+ __all__ = ["StringUtils", "ObjectUtils"]
@@ -0,0 +1,20 @@
1
+ """Utility class for object-related operations."""
2
+
3
+ from typing import Any, List, Type
4
+
5
+
6
+ class ObjectUtils:
7
+ """Utility class for object-related operations."""
8
+
9
+ @staticmethod
10
+ def is_instance_of_any(obj: Any, classes: List[Type]) -> bool:
11
+ """Checks if an object is an instance of any of the provided classes.
12
+
13
+ Args:
14
+ obj: The object to check
15
+ classes: Array of class constructors to test against
16
+
17
+ Returns:
18
+ True if the object is an instance of any class, False otherwise
19
+ """
20
+ return any(isinstance(obj, cls) for cls in classes)
@@ -0,0 +1,113 @@
1
+ """Utility class for string manipulation and validation."""
2
+
3
+
4
+ class StringUtils:
5
+ """Utility class for string manipulation and validation.
6
+
7
+ Provides methods for handling quoted strings, comments, escape sequences,
8
+ and identifier validation.
9
+ """
10
+
11
+ quotes = ['"', "'", '`']
12
+ letters = 'abcdefghijklmnopqrstuvwxyz'
13
+ digits = '0123456789'
14
+ whitespace = ' \t\n\r'
15
+ word_valid_chars = letters + letters.upper() + digits + '_'
16
+
17
+ @staticmethod
18
+ def unquote(s: str) -> str:
19
+ """Removes surrounding quotes from a string.
20
+
21
+ Args:
22
+ s: The string to unquote
23
+
24
+ Returns:
25
+ The unquoted string
26
+ """
27
+ if len(s) == 0:
28
+ return s
29
+ if len(s) == 1 and s in StringUtils.quotes:
30
+ return ''
31
+ first = s[0]
32
+ last = s[-1]
33
+ if first in StringUtils.quotes and first == last:
34
+ return s[1:-1]
35
+ if last in StringUtils.quotes and first != last:
36
+ return s[:-1]
37
+ if first in StringUtils.quotes and first != last:
38
+ return s[1:]
39
+ return s
40
+
41
+ @staticmethod
42
+ def uncomment(s: str) -> str:
43
+ """Removes comment markers from a string.
44
+
45
+ Args:
46
+ s: The comment string
47
+
48
+ Returns:
49
+ The string without comment markers
50
+ """
51
+ if len(s) < 2:
52
+ return s
53
+ if s[0] == '/' and s[1] == '/':
54
+ return s[2:]
55
+ if s[0] == '/' and s[1] == '*' and s[-2] == '*' and s[-1] == '/':
56
+ return s[2:-2]
57
+ return s
58
+
59
+ @staticmethod
60
+ def remove_escaped_quotes(s: str, quote_char: str) -> str:
61
+ """Removes escape sequences before quotes in a string.
62
+
63
+ Args:
64
+ s: The string to process
65
+ quote_char: The quote character that was escaped
66
+
67
+ Returns:
68
+ The string with escape sequences removed
69
+ """
70
+ unescaped = ''
71
+ i = 0
72
+ while i < len(s):
73
+ if i < len(s) - 1 and s[i] == '\\' and s[i + 1] == quote_char:
74
+ i += 1
75
+ unescaped += s[i]
76
+ i += 1
77
+ return unescaped
78
+
79
+ @staticmethod
80
+ def remove_escaped_braces(s: str) -> str:
81
+ """Removes escaped braces ({{ and }}) from f-strings.
82
+
83
+ Args:
84
+ s: The string to process
85
+
86
+ Returns:
87
+ The string with escaped braces resolved
88
+ """
89
+ unescaped = ''
90
+ i = 0
91
+ while i < len(s):
92
+ if i < len(s) - 1 and ((s[i] == '{' and s[i + 1] == '{') or (s[i] == '}' and s[i + 1] == '}')):
93
+ i += 1
94
+ unescaped += s[i]
95
+ i += 1
96
+ return unescaped
97
+
98
+ @staticmethod
99
+ def can_be_identifier(s: str) -> bool:
100
+ """Checks if a string is a valid identifier.
101
+
102
+ Args:
103
+ s: The string to validate
104
+
105
+ Returns:
106
+ True if the string can be used as an identifier, false otherwise
107
+ """
108
+ lower = s.lower()
109
+ if len(lower) == 0:
110
+ return False
111
+ if lower[0] not in StringUtils.letters and lower[0] != '_':
112
+ return False
113
+ return all(char in StringUtils.word_valid_chars for char in lower)
@@ -0,0 +1 @@
1
+ """Tests package for FlowQuery."""
@@ -0,0 +1 @@
1
+ """Compute tests package."""