flowquery 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +30 -0
- package/.github/workflows/release.yml +84 -0
- package/CODE_OF_CONDUCT.md +10 -0
- package/FlowQueryLogoIcon.png +0 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/SECURITY.md +14 -0
- package/SUPPORT.md +13 -0
- package/docs/flowquery.min.js +1 -0
- package/docs/index.html +105 -0
- package/flowquery-vscode/.vscode-test.mjs +5 -0
- package/flowquery-vscode/.vscodeignore +13 -0
- package/flowquery-vscode/LICENSE +21 -0
- package/flowquery-vscode/README.md +11 -0
- package/flowquery-vscode/demo/FlowQueryVSCodeDemo.gif +0 -0
- package/flowquery-vscode/eslint.config.mjs +25 -0
- package/flowquery-vscode/extension.js +508 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -0
- package/flowquery-vscode/flowquery-worker.js +66 -0
- package/flowquery-vscode/images/FlowQueryLogoIcon.png +0 -0
- package/flowquery-vscode/jsconfig.json +13 -0
- package/flowquery-vscode/libs/page.css +53 -0
- package/flowquery-vscode/libs/table.css +13 -0
- package/flowquery-vscode/libs/tabs.css +66 -0
- package/flowquery-vscode/package-lock.json +2917 -0
- package/flowquery-vscode/package.json +51 -0
- package/flowquery-vscode/test/extension.test.js +196 -0
- package/flowquery-vscode/test/worker.test.js +25 -0
- package/flowquery-vscode/vsc-extension-quickstart.md +42 -0
- package/jest.config.js +11 -0
- package/package.json +28 -0
- package/queries/analyze_catfacts.cql +75 -0
- package/queries/azure_openai_completions.cql +13 -0
- package/queries/azure_openai_models.cql +9 -0
- package/queries/mock_pipeline.cql +84 -0
- package/queries/openai_completions.cql +15 -0
- package/queries/openai_models.cql +13 -0
- package/queries/test.cql +6 -0
- package/queries/tool_inference.cql +24 -0
- package/queries/wisdom.cql +6 -0
- package/queries/wisdom_letter_histogram.cql +8 -0
- package/src/compute/runner.ts +65 -0
- package/src/index.browser.ts +11 -0
- package/src/index.ts +12 -0
- package/src/io/command_line.ts +74 -0
- package/src/parsing/alias.ts +23 -0
- package/src/parsing/alias_option.ts +5 -0
- package/src/parsing/ast_node.ts +153 -0
- package/src/parsing/base_parser.ts +92 -0
- package/src/parsing/components/csv.ts +9 -0
- package/src/parsing/components/from.ts +12 -0
- package/src/parsing/components/headers.ts +12 -0
- package/src/parsing/components/json.ts +9 -0
- package/src/parsing/components/null.ts +9 -0
- package/src/parsing/components/post.ts +9 -0
- package/src/parsing/components/text.ts +9 -0
- package/src/parsing/context.ts +48 -0
- package/src/parsing/data_structures/associative_array.ts +43 -0
- package/src/parsing/data_structures/json_array.ts +31 -0
- package/src/parsing/data_structures/key_value_pair.ts +37 -0
- package/src/parsing/data_structures/lookup.ts +40 -0
- package/src/parsing/data_structures/range_lookup.ts +36 -0
- package/src/parsing/expressions/expression.ts +142 -0
- package/src/parsing/expressions/f_string.ts +26 -0
- package/src/parsing/expressions/identifier.ts +22 -0
- package/src/parsing/expressions/number.ts +40 -0
- package/src/parsing/expressions/operator.ts +179 -0
- package/src/parsing/expressions/reference.ts +42 -0
- package/src/parsing/expressions/string.ts +34 -0
- package/src/parsing/functions/aggregate_function.ts +58 -0
- package/src/parsing/functions/avg.ts +37 -0
- package/src/parsing/functions/collect.ts +44 -0
- package/src/parsing/functions/function.ts +60 -0
- package/src/parsing/functions/function_factory.ts +66 -0
- package/src/parsing/functions/join.ts +26 -0
- package/src/parsing/functions/predicate_function.ts +44 -0
- package/src/parsing/functions/predicate_function_factory.ts +15 -0
- package/src/parsing/functions/predicate_sum.ts +29 -0
- package/src/parsing/functions/rand.ts +13 -0
- package/src/parsing/functions/range.ts +18 -0
- package/src/parsing/functions/reducer_element.ts +10 -0
- package/src/parsing/functions/replace.ts +19 -0
- package/src/parsing/functions/round.ts +17 -0
- package/src/parsing/functions/size.ts +17 -0
- package/src/parsing/functions/split.ts +26 -0
- package/src/parsing/functions/stringify.ts +26 -0
- package/src/parsing/functions/sum.ts +31 -0
- package/src/parsing/functions/to_json.ts +17 -0
- package/src/parsing/functions/value_holder.ts +13 -0
- package/src/parsing/logic/case.ts +26 -0
- package/src/parsing/logic/else.ts +12 -0
- package/src/parsing/logic/end.ts +9 -0
- package/src/parsing/logic/then.ts +12 -0
- package/src/parsing/logic/when.ts +12 -0
- package/src/parsing/operations/aggregated_return.ts +18 -0
- package/src/parsing/operations/aggregated_with.ts +18 -0
- package/src/parsing/operations/group_by.ts +124 -0
- package/src/parsing/operations/limit.ts +22 -0
- package/src/parsing/operations/load.ts +92 -0
- package/src/parsing/operations/operation.ts +65 -0
- package/src/parsing/operations/projection.ts +18 -0
- package/src/parsing/operations/return.ts +43 -0
- package/src/parsing/operations/unwind.ts +32 -0
- package/src/parsing/operations/where.ts +38 -0
- package/src/parsing/operations/with.ts +20 -0
- package/src/parsing/parser.ts +762 -0
- package/src/parsing/token_to_node.ts +91 -0
- package/src/tokenization/keyword.ts +43 -0
- package/src/tokenization/operator.ts +25 -0
- package/src/tokenization/string_walker.ts +194 -0
- package/src/tokenization/symbol.ts +15 -0
- package/src/tokenization/token.ts +633 -0
- package/src/tokenization/token_mapper.ts +53 -0
- package/src/tokenization/token_type.ts +15 -0
- package/src/tokenization/tokenizer.ts +229 -0
- package/src/tokenization/trie.ts +117 -0
- package/src/utils/object_utils.ts +17 -0
- package/src/utils/string_utils.ts +114 -0
- package/tests/compute/runner.test.ts +498 -0
- package/tests/parsing/context.test.ts +27 -0
- package/tests/parsing/expression.test.ts +40 -0
- package/tests/parsing/parser.test.ts +434 -0
- package/tests/tokenization/token_mapper.test.ts +47 -0
- package/tests/tokenization/tokenizer.test.ts +67 -0
- package/tests/tokenization/trie.test.ts +20 -0
- package/tsconfig.json +15 -0
- package/typedoc.json +16 -0
- package/webpack.config.js +26 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import Parser from "../../src/parsing/parser";
|
|
2
|
+
|
|
3
|
+
test('Test Parser', () => {
|
|
4
|
+
const parser = new Parser();
|
|
5
|
+
const ast = parser.parse('RETURN 1, 2, 3');
|
|
6
|
+
expect(ast.print()).toBe(
|
|
7
|
+
'ASTNode\n' +
|
|
8
|
+
'- Return\n' +
|
|
9
|
+
'-- Expression\n' +
|
|
10
|
+
'--- Number (1)\n' +
|
|
11
|
+
'-- Expression\n' +
|
|
12
|
+
'--- Number (2)\n' +
|
|
13
|
+
'-- Expression\n' +
|
|
14
|
+
'--- Number (3)'
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('Test Parser with function', () => {
|
|
19
|
+
const parser = new Parser();
|
|
20
|
+
const ast = parser.parse('RETURN f()');
|
|
21
|
+
expect(ast.print()).toBe(
|
|
22
|
+
'ASTNode\n' +
|
|
23
|
+
'- Return\n' +
|
|
24
|
+
'-- Expression\n' +
|
|
25
|
+
'--- Function (f)'
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Test Parser with associative array', () => {
|
|
30
|
+
const parser = new Parser();
|
|
31
|
+
const ast = parser.parse('RETURN {a: 1, b: 2}');
|
|
32
|
+
expect(ast.print()).toBe(
|
|
33
|
+
'ASTNode\n' +
|
|
34
|
+
'- Return\n' +
|
|
35
|
+
'-- Expression\n' +
|
|
36
|
+
'--- AssociativeArray\n' +
|
|
37
|
+
'---- KeyValuePair\n' +
|
|
38
|
+
'----- String (a)\n' +
|
|
39
|
+
'----- Expression\n' +
|
|
40
|
+
'------ Number (1)\n' +
|
|
41
|
+
'---- KeyValuePair\n' +
|
|
42
|
+
'----- String (b)\n' +
|
|
43
|
+
'----- Expression\n' +
|
|
44
|
+
'------ Number (2)'
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('Test Parser with JSON array', () => {
|
|
49
|
+
const parser = new Parser();
|
|
50
|
+
const ast = parser.parse('RETURN [1, 2]');
|
|
51
|
+
expect(ast.print()).toBe(
|
|
52
|
+
'ASTNode\n' +
|
|
53
|
+
'- Return\n' +
|
|
54
|
+
'-- Expression\n' +
|
|
55
|
+
'--- JSONArray\n' +
|
|
56
|
+
'---- Expression\n' +
|
|
57
|
+
'----- Number (1)\n' +
|
|
58
|
+
'---- Expression\n' +
|
|
59
|
+
'----- Number (2)'
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('Test Parser with nested associative array', () => {
|
|
64
|
+
const parser = new Parser();
|
|
65
|
+
const ast = parser.parse('RETURN {a:{}}');
|
|
66
|
+
expect(ast.print()).toBe(
|
|
67
|
+
'ASTNode\n' +
|
|
68
|
+
'- Return\n' +
|
|
69
|
+
'-- Expression\n' +
|
|
70
|
+
'--- AssociativeArray\n' +
|
|
71
|
+
'---- KeyValuePair\n' +
|
|
72
|
+
'----- String (a)\n' +
|
|
73
|
+
'----- Expression\n' +
|
|
74
|
+
'------ AssociativeArray'
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('Test Parser with multiple operations', () => {
|
|
79
|
+
const parser = new Parser();
|
|
80
|
+
const ast = parser.parse('WITH 1 AS n RETURN n');
|
|
81
|
+
expect(ast.print()).toBe(
|
|
82
|
+
'ASTNode\n' +
|
|
83
|
+
'- With\n' +
|
|
84
|
+
'-- Expression (n)\n' +
|
|
85
|
+
'--- Number (1)\n' +
|
|
86
|
+
'- Return\n' +
|
|
87
|
+
'-- Expression (n)\n' +
|
|
88
|
+
'--- Reference (n)'
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('Test Parser with multiple operations and comments', () => {
|
|
93
|
+
const parser = new Parser();
|
|
94
|
+
const ast = parser.parse('WITH 1 AS n /* comment */ RETURN n');
|
|
95
|
+
expect(ast.print()).toBe(
|
|
96
|
+
'ASTNode\n' +
|
|
97
|
+
'- With\n' +
|
|
98
|
+
'-- Expression (n)\n' +
|
|
99
|
+
'--- Number (1)\n' +
|
|
100
|
+
'- Return\n' +
|
|
101
|
+
'-- Expression (n)\n' +
|
|
102
|
+
'--- Reference (n)'
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('Test Parser with multiple operations including UNWIND', () => {
|
|
107
|
+
const parser = new Parser();
|
|
108
|
+
const ast = parser.parse('UNWIND [1, 2, 3] AS n RETURN n');
|
|
109
|
+
expect(ast.print()).toBe(
|
|
110
|
+
'ASTNode\n' +
|
|
111
|
+
'- Unwind\n' +
|
|
112
|
+
'-- Expression (n)\n' +
|
|
113
|
+
'--- JSONArray\n' +
|
|
114
|
+
'---- Expression\n' +
|
|
115
|
+
'----- Number (1)\n' +
|
|
116
|
+
'---- Expression\n' +
|
|
117
|
+
'----- Number (2)\n' +
|
|
118
|
+
'---- Expression\n' +
|
|
119
|
+
'----- Number (3)\n' +
|
|
120
|
+
'- Return\n' +
|
|
121
|
+
'-- Expression (n)\n' +
|
|
122
|
+
'--- Reference (n)'
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('Test Unwind with invalid expression', () => {
|
|
127
|
+
const parser = new Parser();
|
|
128
|
+
expect(() => parser.parse('UNWIND 1 AS n RETURN n')).toThrow('Expected array, function, reference, or lookup.');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('Test Unwind with invalid alias', () => {
|
|
132
|
+
const parser = new Parser();
|
|
133
|
+
expect(() => parser.parse('UNWIND [1, 2, 3] AS 1 RETURN n')).toThrow('Expected identifier');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Test Unwind with missing alias', () => {
|
|
137
|
+
const parser = new Parser();
|
|
138
|
+
expect(() => parser.parse('UNWIND [1, 2, 3] RETURN n')).toThrow('Expected alias');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('Test statement with where clause', () => {
|
|
142
|
+
const parser = new Parser();
|
|
143
|
+
const ast = parser.parse('with 1 as n where n > 0 return n');
|
|
144
|
+
expect(ast.print()).toBe(
|
|
145
|
+
'ASTNode\n' +
|
|
146
|
+
'- With\n' +
|
|
147
|
+
'-- Expression (n)\n' +
|
|
148
|
+
'--- Number (1)\n' +
|
|
149
|
+
'- Where\n' +
|
|
150
|
+
'-- Expression\n' +
|
|
151
|
+
'--- GreaterThan\n' +
|
|
152
|
+
'---- Reference (n)\n' +
|
|
153
|
+
'---- Number (0)\n' +
|
|
154
|
+
'- Return\n' +
|
|
155
|
+
'-- Expression (n)\n' +
|
|
156
|
+
'--- Reference (n)'
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('Test lookup', () => {
|
|
161
|
+
const parser = new Parser();
|
|
162
|
+
const ast = parser.parse('return {a: 1}.a');
|
|
163
|
+
expect(ast.print()).toBe(
|
|
164
|
+
'ASTNode\n' +
|
|
165
|
+
'- Return\n' +
|
|
166
|
+
'-- Expression\n' +
|
|
167
|
+
'--- Lookup\n' +
|
|
168
|
+
'---- Identifier (a)\n' +
|
|
169
|
+
'---- AssociativeArray\n' +
|
|
170
|
+
'----- KeyValuePair\n' +
|
|
171
|
+
'------ String (a)\n' +
|
|
172
|
+
'------ Expression\n' +
|
|
173
|
+
'------- Number (1)'
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('Test lookup as part of expression', () => {
|
|
178
|
+
const parser = new Parser();
|
|
179
|
+
const ast = parser.parse('return {a: 1}.a + 1');
|
|
180
|
+
expect(ast.print()).toBe(
|
|
181
|
+
'ASTNode\n' +
|
|
182
|
+
'- Return\n' +
|
|
183
|
+
'-- Expression\n' +
|
|
184
|
+
'--- Add\n' +
|
|
185
|
+
'---- Lookup\n' +
|
|
186
|
+
'----- Identifier (a)\n' +
|
|
187
|
+
'----- AssociativeArray\n' +
|
|
188
|
+
'------ KeyValuePair\n' +
|
|
189
|
+
'------- String (a)\n' +
|
|
190
|
+
'------- Expression\n' +
|
|
191
|
+
'-------- Number (1)\n' +
|
|
192
|
+
'---- Number (1)'
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('Test lookup with nested associative array', () => {
|
|
197
|
+
const parser = new Parser();
|
|
198
|
+
const ast = parser.parse('return {a: {b: 1}}.a.b');
|
|
199
|
+
const _return = ast.firstChild();
|
|
200
|
+
expect(ast.print()).toBe(
|
|
201
|
+
'ASTNode\n' +
|
|
202
|
+
'- Return\n' +
|
|
203
|
+
'-- Expression\n' +
|
|
204
|
+
'--- Lookup\n' +
|
|
205
|
+
'---- Identifier (b)\n' +
|
|
206
|
+
'---- Lookup\n' +
|
|
207
|
+
'----- Identifier (a)\n' +
|
|
208
|
+
'----- AssociativeArray\n' +
|
|
209
|
+
'------ KeyValuePair\n' +
|
|
210
|
+
'------- String (a)\n' +
|
|
211
|
+
'------- Expression\n' +
|
|
212
|
+
'-------- AssociativeArray\n' +
|
|
213
|
+
'--------- KeyValuePair\n' +
|
|
214
|
+
'---------- String (b)\n' +
|
|
215
|
+
'---------- Expression\n' +
|
|
216
|
+
'----------- Number (1)'
|
|
217
|
+
);
|
|
218
|
+
expect(_return.firstChild().value()).toBe(1);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('Test lookup with JSON array', () => {
|
|
222
|
+
const parser = new Parser();
|
|
223
|
+
const ast = parser.parse('return [1, 2][1]');
|
|
224
|
+
const _return = ast.firstChild();
|
|
225
|
+
expect(ast.print()).toBe(
|
|
226
|
+
'ASTNode\n' +
|
|
227
|
+
'- Return\n' +
|
|
228
|
+
'-- Expression\n' +
|
|
229
|
+
'--- Lookup\n' +
|
|
230
|
+
'---- Expression\n' +
|
|
231
|
+
'----- Number (1)\n' +
|
|
232
|
+
'---- JSONArray\n' +
|
|
233
|
+
'----- Expression\n' +
|
|
234
|
+
'------ Number (1)\n' +
|
|
235
|
+
'----- Expression\n' +
|
|
236
|
+
'------ Number (2)'
|
|
237
|
+
);
|
|
238
|
+
expect(_return.firstChild().value()).toBe(2);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('Test load with post', () => {
|
|
242
|
+
const parser = new Parser();
|
|
243
|
+
const ast = parser.parse('load json from "https://jsonplaceholder.typicode.com/posts" post {userId: 1} as data return data');
|
|
244
|
+
expect(ast.print()).toBe(
|
|
245
|
+
'ASTNode\n' +
|
|
246
|
+
'- Load\n' +
|
|
247
|
+
'-- JSON\n' +
|
|
248
|
+
'-- From\n' +
|
|
249
|
+
'--- Expression\n' +
|
|
250
|
+
'---- String (https://jsonplaceholder.typicode.com/posts)\n' +
|
|
251
|
+
'-- Post\n' +
|
|
252
|
+
'--- Expression\n' +
|
|
253
|
+
'---- AssociativeArray\n' +
|
|
254
|
+
'----- KeyValuePair\n' +
|
|
255
|
+
'------ String (userId)\n' +
|
|
256
|
+
'------ Expression\n' +
|
|
257
|
+
'------- Number (1)\n' +
|
|
258
|
+
'-- Alias (data)\n' +
|
|
259
|
+
'- Return\n' +
|
|
260
|
+
'-- Expression (data)\n' +
|
|
261
|
+
'--- Reference (data)'
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('Test nested aggregate functions', () => {
|
|
266
|
+
expect(() => {
|
|
267
|
+
const parser = new Parser();
|
|
268
|
+
parser.parse('RETURN sum(sum(1))');
|
|
269
|
+
}).toThrow('Aggregate functions cannot be nested');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('Test with and return with renamed variable', () => {
|
|
273
|
+
const parser = new Parser();
|
|
274
|
+
const ast = parser.parse('WITH 1 AS n RETURN n AS m');
|
|
275
|
+
expect(ast.print()).toBe(
|
|
276
|
+
'ASTNode\n' +
|
|
277
|
+
'- With\n' +
|
|
278
|
+
'-- Expression (n)\n' +
|
|
279
|
+
'--- Number (1)\n' +
|
|
280
|
+
'- Return\n' +
|
|
281
|
+
'-- Expression (m)\n' +
|
|
282
|
+
'--- Reference (n)'
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test('Test with and return with variable lookup', () => {
|
|
287
|
+
const parser = new Parser();
|
|
288
|
+
const ast = parser.parse('WITH {a: n} AS obj RETURN obj.a');
|
|
289
|
+
expect(ast.print()).toBe(
|
|
290
|
+
'ASTNode\n' +
|
|
291
|
+
'- With\n' +
|
|
292
|
+
'-- Expression (obj)\n' +
|
|
293
|
+
'--- AssociativeArray\n' +
|
|
294
|
+
'---- KeyValuePair\n' +
|
|
295
|
+
'----- String (a)\n' +
|
|
296
|
+
'----- Expression\n' +
|
|
297
|
+
'------ Reference (n)\n' +
|
|
298
|
+
'- Return\n' +
|
|
299
|
+
'-- Expression\n' +
|
|
300
|
+
'--- Lookup\n' +
|
|
301
|
+
'---- Identifier (a)\n' +
|
|
302
|
+
'---- Reference (obj)'
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('Test unwind', () => {
|
|
307
|
+
const parser = new Parser();
|
|
308
|
+
const ast = parser.parse('WITH [1, 2, 4] as n unwind n as i return i');
|
|
309
|
+
expect(ast.print()).toBe(
|
|
310
|
+
'ASTNode\n' +
|
|
311
|
+
'- With\n' +
|
|
312
|
+
'-- Expression (n)\n' +
|
|
313
|
+
'--- JSONArray\n' +
|
|
314
|
+
'---- Expression\n' +
|
|
315
|
+
'----- Number (1)\n' +
|
|
316
|
+
'---- Expression\n' +
|
|
317
|
+
'----- Number (2)\n' +
|
|
318
|
+
'---- Expression\n' +
|
|
319
|
+
'----- Number (4)\n' +
|
|
320
|
+
'- Unwind\n' +
|
|
321
|
+
'-- Expression (i)\n' +
|
|
322
|
+
'--- Reference (n)\n' +
|
|
323
|
+
'- Return\n' +
|
|
324
|
+
'-- Expression (i)\n' +
|
|
325
|
+
'--- Reference (i)'
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('Test predicate function', () => {
|
|
330
|
+
const parser = new Parser();
|
|
331
|
+
const ast = parser.parse('RETURN sum(n in [1, 2, 3] | n where n > 1)');
|
|
332
|
+
expect(ast.print()).toBe(
|
|
333
|
+
'ASTNode\n' +
|
|
334
|
+
'- Return\n' +
|
|
335
|
+
'-- Expression\n' +
|
|
336
|
+
'--- PredicateFunction (sum)\n' +
|
|
337
|
+
'---- Reference (n)\n' +
|
|
338
|
+
'---- Expression\n' +
|
|
339
|
+
'----- JSONArray\n' +
|
|
340
|
+
'------ Expression\n' +
|
|
341
|
+
'------- Number (1)\n' +
|
|
342
|
+
'------ Expression\n' +
|
|
343
|
+
'------- Number (2)\n' +
|
|
344
|
+
'------ Expression\n' +
|
|
345
|
+
'------- Number (3)\n' +
|
|
346
|
+
'---- Expression\n' +
|
|
347
|
+
'----- Reference (n)\n' +
|
|
348
|
+
'---- Where\n' +
|
|
349
|
+
'----- Expression\n' +
|
|
350
|
+
'------ GreaterThan\n' +
|
|
351
|
+
'------- Reference (n)\n' +
|
|
352
|
+
'------- Number (1)'
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('Test case statement', () => {
|
|
357
|
+
const parser = new Parser();
|
|
358
|
+
const ast = parser.parse('RETURN CASE WHEN 1 THEN 2 ELSE 3 END');
|
|
359
|
+
expect(ast.print()).toBe(
|
|
360
|
+
'ASTNode\n' +
|
|
361
|
+
'- Return\n' +
|
|
362
|
+
'-- Expression\n' +
|
|
363
|
+
'--- Case\n' +
|
|
364
|
+
'---- When\n' +
|
|
365
|
+
'----- Expression\n' +
|
|
366
|
+
'------ Number (1)\n' +
|
|
367
|
+
'---- Then\n' +
|
|
368
|
+
'----- Expression\n' +
|
|
369
|
+
'------ Number (2)\n' +
|
|
370
|
+
'---- Else\n' +
|
|
371
|
+
'----- Expression\n' +
|
|
372
|
+
'------ Number (3)'
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('Test functions with wrong number of arguments', () => {
|
|
377
|
+
expect(() => (new Parser()).parse('RETURN range(1)')).toThrow('Function range expected 2 parameters, but got 1');
|
|
378
|
+
expect(() => (new Parser()).parse('RETURN range(1, 2, 3)')).toThrow('Function range expected 2 parameters, but got 3');
|
|
379
|
+
expect(() => (new Parser()).parse('RETURN avg(1, 2, 3)')).toThrow('Function avg expected 1 parameters, but got 3');
|
|
380
|
+
expect(() => (new Parser()).parse('RETURN sum(1, 2)')).toThrow('Function sum expected 1 parameters, but got 2');
|
|
381
|
+
expect(() => (new Parser()).parse('RETURN split("a", "b", "c")')).toThrow('Function split expected 2 parameters, but got 3');
|
|
382
|
+
expect(() => (new Parser()).parse('RETURN size(1, 2)')).toThrow('Function size expected 1 parameters, but got 2');
|
|
383
|
+
expect(() => (new Parser()).parse('RETURN round(1, 2)')).toThrow('Function round expected 1 parameters, but got 2');
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test('Test non-well formed statements', () => {
|
|
387
|
+
expect(() => (new Parser()).parse('return 1 return 1')).toThrow('Only one RETURN statement is allowed');
|
|
388
|
+
expect(() => (new Parser()).parse('return 1 with 1 as n')).toThrow('Last statement must be a RETURN or a WHERE statement');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('Test associative array with backtick string', () => {
|
|
392
|
+
const parser = new Parser();
|
|
393
|
+
const ast = parser.parse('RETURN {`key`: `value`}');
|
|
394
|
+
expect(ast.print()).toBe(
|
|
395
|
+
'ASTNode\n' +
|
|
396
|
+
'- Return\n' +
|
|
397
|
+
'-- Expression\n' +
|
|
398
|
+
'--- AssociativeArray\n' +
|
|
399
|
+
'---- KeyValuePair\n' +
|
|
400
|
+
'----- String (key)\n' +
|
|
401
|
+
'----- Expression\n' +
|
|
402
|
+
'------ Reference (value)'
|
|
403
|
+
);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('Test limit', () => {
|
|
407
|
+
const parser = new Parser();
|
|
408
|
+
const ast = parser.parse('unwind range(1, 10) as n limit 5 return n');
|
|
409
|
+
expect(ast.print()).toBe(
|
|
410
|
+
'ASTNode\n' +
|
|
411
|
+
'- Unwind\n' +
|
|
412
|
+
'-- Expression (n)\n' +
|
|
413
|
+
'--- Function (range)\n' +
|
|
414
|
+
'---- Expression\n' +
|
|
415
|
+
'----- Number (1)\n' +
|
|
416
|
+
'---- Expression\n' +
|
|
417
|
+
'----- Number (10)\n' +
|
|
418
|
+
'- Limit\n' +
|
|
419
|
+
'- Return\n' +
|
|
420
|
+
'-- Expression (n)\n' +
|
|
421
|
+
'--- Reference (n)'
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('Test return -2', () => {
|
|
426
|
+
const parser = new Parser();
|
|
427
|
+
const ast = parser.parse('return -2');
|
|
428
|
+
expect(ast.print()).toBe(
|
|
429
|
+
'ASTNode\n' +
|
|
430
|
+
'- Return\n' +
|
|
431
|
+
'-- Expression\n' +
|
|
432
|
+
'--- Number (-2)'
|
|
433
|
+
);
|
|
434
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import TokenMapper from "../../src/tokenization/token_mapper";
|
|
2
|
+
import Symbol from "../../src/tokenization/symbol";
|
|
3
|
+
import Keyword from "../../src/tokenization/keyword";
|
|
4
|
+
import Operator from "../../src/tokenization/operator";
|
|
5
|
+
|
|
6
|
+
test('mapper with Symbols', () => {
|
|
7
|
+
const mapper = new TokenMapper(Symbol);
|
|
8
|
+
expect(mapper.map(Symbol.LEFT_PARENTHESIS)).toBeDefined();
|
|
9
|
+
expect(mapper.map(Symbol.RIGHT_PARENTHESIS)).toBeDefined();
|
|
10
|
+
expect(mapper.map(Symbol.COMMA)).toBeDefined();
|
|
11
|
+
expect(mapper.map(Symbol.DOT)).toBeDefined();
|
|
12
|
+
expect(mapper.map(Symbol.COLON)).toBeDefined();
|
|
13
|
+
|
|
14
|
+
expect(mapper.map(Operator.ADD)).toBeUndefined();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('mapper with Keywords', () => {
|
|
18
|
+
const mapper = new TokenMapper(Keyword);
|
|
19
|
+
expect(mapper.map(Keyword.MATCH)).toBeDefined();
|
|
20
|
+
expect(mapper.map(Keyword.RETURN)).toBeDefined();
|
|
21
|
+
expect(mapper.map(Keyword.WHERE)).toBeDefined();
|
|
22
|
+
|
|
23
|
+
expect(mapper.map('not_a_keyword')).toBeUndefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('mapper with Operators', () => {
|
|
27
|
+
const mapper = new TokenMapper(Operator);
|
|
28
|
+
expect(mapper.map(Operator.GREATER_THAN_OR_EQUAL)).toBeDefined();
|
|
29
|
+
expect(mapper.map(Operator.ADD.valueOf())).toBeDefined();
|
|
30
|
+
expect(mapper.map(Operator.SUBTRACT)).toBeDefined();
|
|
31
|
+
expect(mapper.map(Operator.NOT)).toBeDefined();
|
|
32
|
+
expect(mapper.map(Operator.EQUALS)).toBeDefined();
|
|
33
|
+
expect(mapper.map(Operator.NOT_EQUALS)).toBeDefined();
|
|
34
|
+
expect(mapper.map(Operator.LESS_THAN)).toBeDefined();
|
|
35
|
+
expect(mapper.map(Operator.LESS_THAN_OR_EQUAL)).toBeDefined();
|
|
36
|
+
|
|
37
|
+
expect(mapper.map(Operator.GREATER_THAN_OR_EQUAL + "1")).toBeDefined();
|
|
38
|
+
|
|
39
|
+
expect(mapper.map('i_s_n_o_t_an_operator')).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('mapper with mixed types', () => {
|
|
43
|
+
const mapper = new TokenMapper(Symbol);
|
|
44
|
+
expect(mapper.map(Symbol.LEFT_PARENTHESIS)).toBeDefined();
|
|
45
|
+
expect(mapper.map(Symbol.RIGHT_PARENTHESIS)).toBeDefined();
|
|
46
|
+
expect(mapper.map(Symbol.COMMA)).toBeDefined();
|
|
47
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Tokenizer from '../../src/tokenization/tokenizer';
|
|
2
|
+
|
|
3
|
+
test('Tokenizer.tokenize() should return an array of tokens', () => {
|
|
4
|
+
const tokenizer = new Tokenizer('MATCH (n:Person) RETURN n');
|
|
5
|
+
const tokens = tokenizer.tokenize();
|
|
6
|
+
expect(tokens).toBeDefined();
|
|
7
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('Tokenizer.tokenize() should handle escaped quotes', () => {
|
|
11
|
+
const tokenizer = new Tokenizer('return "hello \\"world"');
|
|
12
|
+
const tokens = tokenizer.tokenize();
|
|
13
|
+
expect(tokens).toBeDefined();
|
|
14
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Test predicate function', () => {
|
|
18
|
+
const tokenizer = new Tokenizer('RETURN sum(n in [1, 2, 3] | n where n > 1)');
|
|
19
|
+
const tokens = tokenizer.tokenize();
|
|
20
|
+
expect(tokens).toBeDefined();
|
|
21
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('Test f-string', () => {
|
|
25
|
+
const tokenizer = new Tokenizer('RETURN f"hello {world}"');
|
|
26
|
+
const tokens = tokenizer.tokenize();
|
|
27
|
+
expect(tokens).toBeDefined();
|
|
28
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('Test', () => {
|
|
32
|
+
const tokenizer = new Tokenizer('WITH 1 AS n RETURN n');
|
|
33
|
+
const tokens = tokenizer.tokenize();
|
|
34
|
+
expect(tokens).toBeDefined();
|
|
35
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('Test associative array with backtick string', () => {
|
|
39
|
+
const tokenizer = new Tokenizer('RETURN {`key`: `value`}');
|
|
40
|
+
const tokens = tokenizer.tokenize();
|
|
41
|
+
expect(tokens).toBeDefined();
|
|
42
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('Test limit', () => {
|
|
46
|
+
const tokenizer = new Tokenizer('unwind range(1, 10) as n limit 5 return n');
|
|
47
|
+
const tokens = tokenizer.tokenize();
|
|
48
|
+
expect(tokens).toBeDefined();
|
|
49
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Test return -2', () => {
|
|
53
|
+
const tokenizer = new Tokenizer('return [:-2], -2');
|
|
54
|
+
const tokens = tokenizer.tokenize();
|
|
55
|
+
expect(tokens).toBeDefined();
|
|
56
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('Test range with function', () => {
|
|
60
|
+
const tokenizer = new Tokenizer(`
|
|
61
|
+
with range(1,10) as data
|
|
62
|
+
return range(0, size(data)-1) as indices
|
|
63
|
+
`);
|
|
64
|
+
const tokens = tokenizer.tokenize();
|
|
65
|
+
expect(tokens).toBeDefined();
|
|
66
|
+
expect(tokens.length).toBeGreaterThan(0);
|
|
67
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Token from "../../src/tokenization/token";
|
|
2
|
+
import Keyword from "../../src/tokenization/keyword";
|
|
3
|
+
import Trie from "../../src/tokenization/trie";
|
|
4
|
+
|
|
5
|
+
test('Trie', () => {
|
|
6
|
+
let trie = new Trie();
|
|
7
|
+
for(const key of Object.keys(Keyword)) {
|
|
8
|
+
const token: Token | undefined = Token.method(key);
|
|
9
|
+
if(token !== undefined && token.value !== null) {
|
|
10
|
+
trie.insert(token);
|
|
11
|
+
expect(trie.find(key)).toBeDefined();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
expect(trie.find('not_a_keyword')).toBeUndefined();
|
|
15
|
+
expect(trie.find('not_a_operator')).toBeUndefined();
|
|
16
|
+
expect(trie.find('not_a_keyword_or_operator')).toBeUndefined();
|
|
17
|
+
expect(trie.find('')).toBeUndefined();
|
|
18
|
+
expect(trie.find(' ')).toBeUndefined();
|
|
19
|
+
expect(trie.find('a')).toBeUndefined();
|
|
20
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* Visit https://aka.ms/tsconfig to read more about this file */
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src", // Optional, but recommended to specify the root directory
|
|
6
|
+
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ /* Modules */
|
|
7
|
+
"module": "commonjs", /* Specify what module code is generated. */
|
|
8
|
+
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
|
9
|
+
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
|
10
|
+
"strict": true,
|
|
11
|
+
"types": ["node", "jest"],
|
|
12
|
+
"sourceMap": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
package/typedoc.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entryPoints": ["src/index.ts"],
|
|
3
|
+
"out": "docs/api",
|
|
4
|
+
"exclude": ["**/node_modules/**", "**/*.test.ts"],
|
|
5
|
+
"excludePrivate": true,
|
|
6
|
+
"excludeProtected": false,
|
|
7
|
+
"excludeExternals": true,
|
|
8
|
+
"readme": "README.md",
|
|
9
|
+
"name": "FlowQuery API Documentation",
|
|
10
|
+
"includeVersion": true,
|
|
11
|
+
"disableSources": false,
|
|
12
|
+
"navigation": {
|
|
13
|
+
"includeCategories": true,
|
|
14
|
+
"includeGroups": true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
entry: './src/index.browser.ts', // Use your entry point here
|
|
5
|
+
output: {
|
|
6
|
+
filename: 'flowquery.min.js', // The output file for the browser
|
|
7
|
+
path: path.resolve(__dirname, 'dist'),
|
|
8
|
+
library: 'FlowQuery', // Expose your module as a global variable
|
|
9
|
+
libraryTarget: 'umd', // Ensure compatibility with various module systems
|
|
10
|
+
libraryExport: 'default', // Export only the default export
|
|
11
|
+
},
|
|
12
|
+
resolve: {
|
|
13
|
+
extensions: ['.ts', '.js'],
|
|
14
|
+
},
|
|
15
|
+
module: {
|
|
16
|
+
rules: [
|
|
17
|
+
{
|
|
18
|
+
test: /\.ts$/,
|
|
19
|
+
use: 'ts-loader',
|
|
20
|
+
exclude: /node_modules/,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
mode: 'production', // Use 'development' for unminified output
|
|
25
|
+
target: 'web', // Ensure the build is targeted for browsers
|
|
26
|
+
};
|