flowquery 1.0.13 → 1.0.15
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/.editorconfig +21 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +22 -0
- package/dist/flowquery.min.js +1 -1
- package/dist/parsing/expressions/expression_map.d.ts +8 -0
- package/dist/parsing/expressions/expression_map.d.ts.map +1 -0
- package/dist/parsing/expressions/expression_map.js +21 -0
- package/dist/parsing/expressions/expression_map.js.map +1 -0
- package/dist/parsing/functions/async_function.d.ts +16 -2
- package/dist/parsing/functions/async_function.d.ts.map +1 -1
- package/dist/parsing/functions/async_function.js +15 -1
- package/dist/parsing/functions/async_function.js.map +1 -1
- package/dist/parsing/operations/call.d.ts +17 -0
- package/dist/parsing/operations/call.d.ts.map +1 -0
- package/dist/parsing/operations/call.js +103 -0
- package/dist/parsing/operations/call.js.map +1 -0
- package/dist/parsing/operations/load.d.ts +7 -6
- package/dist/parsing/operations/load.d.ts.map +1 -1
- package/dist/parsing/operations/load.js +11 -7
- package/dist/parsing/operations/load.js.map +1 -1
- package/dist/parsing/operations/operation.d.ts +1 -0
- package/dist/parsing/operations/operation.d.ts.map +1 -1
- package/dist/parsing/operations/operation.js +6 -5
- package/dist/parsing/operations/operation.js.map +1 -1
- package/dist/parsing/operations/projection.d.ts +1 -1
- package/dist/parsing/operations/projection.d.ts.map +1 -1
- package/dist/parsing/operations/projection.js.map +1 -1
- package/dist/parsing/parser.d.ts +1 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +148 -99
- package/dist/parsing/parser.js.map +1 -1
- package/dist/parsing/token_to_node.d.ts +2 -2
- package/dist/parsing/token_to_node.d.ts.map +1 -1
- package/dist/parsing/token_to_node.js +12 -12
- package/dist/parsing/token_to_node.js.map +1 -1
- package/dist/tokenization/token.d.ts +5 -1
- package/dist/tokenization/token.d.ts.map +1 -1
- package/dist/tokenization/token.js +17 -5
- package/dist/tokenization/token.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/package.json +1 -1
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +4 -4
- package/misc/apps/RAG/src/plugins/loaders/Form.ts +3 -3
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +4 -3
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +7 -5
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +3 -3
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +4 -3
- package/package.json +12 -2
- package/src/parsing/expressions/expression_map.ts +19 -0
- package/src/parsing/functions/async_function.ts +16 -2
- package/src/parsing/operations/call.ts +67 -0
- package/src/parsing/operations/load.ts +123 -118
- package/src/parsing/operations/operation.ts +14 -13
- package/src/parsing/operations/projection.ts +3 -3
- package/src/parsing/parser.ts +303 -239
- package/src/parsing/token_to_node.ts +67 -50
- package/src/tokenization/token.ts +29 -14
- package/tests/compute/runner.test.ts +277 -165
- package/tests/parsing/parser.test.ts +355 -303
- package/vscode-settings.json.recommended +16 -0
package/src/parsing/parser.ts
CHANGED
|
@@ -1,51 +1,52 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import Token from "../tokenization/token";
|
|
2
|
+
import ObjectUtils from "../utils/object_utils";
|
|
3
|
+
import Alias from "./alias";
|
|
4
|
+
import { AliasOption } from "./alias_option";
|
|
3
5
|
import ASTNode from "./ast_node";
|
|
4
6
|
import BaseParser from "./base_parser";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
7
|
+
import From from "./components/from";
|
|
8
|
+
import Headers from "./components/headers";
|
|
9
|
+
import Null from "./components/null";
|
|
10
|
+
import Post from "./components/post";
|
|
11
|
+
import Context from "./context";
|
|
8
12
|
import AssociativeArray from "./data_structures/associative_array";
|
|
9
13
|
import JSONArray from "./data_structures/json_array";
|
|
10
14
|
import KeyValuePair from "./data_structures/key_value_pair";
|
|
11
|
-
import With from "./operations/with";
|
|
12
|
-
import Unwind from "./operations/unwind";
|
|
13
|
-
import Where from "./operations/where";
|
|
14
|
-
import { AliasOption } from "./alias_option";
|
|
15
|
-
import Load from "./operations/load";
|
|
16
|
-
import From from "./components/from";
|
|
17
|
-
import Alias from "./alias";
|
|
18
|
-
import Headers from "./components/headers";
|
|
19
|
-
import Case from "./logic/case";
|
|
20
|
-
import When from "./logic/when";
|
|
21
|
-
import Else from "./logic/else";
|
|
22
|
-
import Then from "./logic/then";
|
|
23
15
|
import Lookup from "./data_structures/lookup";
|
|
16
|
+
import RangeLookup from "./data_structures/range_lookup";
|
|
17
|
+
import Expression from "./expressions/expression";
|
|
18
|
+
import FString from "./expressions/f_string";
|
|
19
|
+
import Identifier from "./expressions/identifier";
|
|
24
20
|
import { Not } from "./expressions/operator";
|
|
25
21
|
import Reference from "./expressions/reference";
|
|
26
|
-
import
|
|
27
|
-
import Post from './components/post';
|
|
28
|
-
import AggregatedReturn from "./operations/aggregated_return";
|
|
29
|
-
import Context from "./context";
|
|
22
|
+
import String from "./expressions/string";
|
|
30
23
|
import AggregateFunction from "./functions/aggregate_function";
|
|
31
|
-
import
|
|
24
|
+
import AsyncFunction from "./functions/async_function";
|
|
25
|
+
import Function from "./functions/function";
|
|
26
|
+
import FunctionFactory from "./functions/function_factory";
|
|
32
27
|
import PredicateFunction from "./functions/predicate_function";
|
|
33
|
-
import
|
|
34
|
-
import
|
|
35
|
-
import
|
|
28
|
+
import Case from "./logic/case";
|
|
29
|
+
import Else from "./logic/else";
|
|
30
|
+
import Then from "./logic/then";
|
|
31
|
+
import When from "./logic/when";
|
|
32
|
+
import AggregatedReturn from "./operations/aggregated_return";
|
|
36
33
|
import AggregatedWith from "./operations/aggregated_with";
|
|
37
|
-
import
|
|
34
|
+
import Call from "./operations/call";
|
|
38
35
|
import Limit from "./operations/limit";
|
|
39
|
-
import
|
|
40
|
-
import
|
|
36
|
+
import Load from "./operations/load";
|
|
37
|
+
import Operation from "./operations/operation";
|
|
38
|
+
import Return from "./operations/return";
|
|
39
|
+
import Unwind from "./operations/unwind";
|
|
40
|
+
import Where from "./operations/where";
|
|
41
|
+
import With from "./operations/with";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Main parser for FlowQuery statements.
|
|
44
|
-
*
|
|
45
|
+
*
|
|
45
46
|
* Parses FlowQuery declarative query language statements into an Abstract Syntax Tree (AST).
|
|
46
47
|
* Supports operations like WITH, UNWIND, RETURN, LOAD, WHERE, and LIMIT, along with
|
|
47
48
|
* expressions, functions, data structures, and logical constructs.
|
|
48
|
-
*
|
|
49
|
+
*
|
|
49
50
|
* @example
|
|
50
51
|
* ```typescript
|
|
51
52
|
* const parser = new Parser();
|
|
@@ -59,11 +60,11 @@ class Parser extends BaseParser {
|
|
|
59
60
|
|
|
60
61
|
/**
|
|
61
62
|
* Parses a FlowQuery statement into an Abstract Syntax Tree.
|
|
62
|
-
*
|
|
63
|
+
*
|
|
63
64
|
* @param statement - The FlowQuery statement to parse
|
|
64
65
|
* @returns The root AST node containing the parsed structure
|
|
65
66
|
* @throws {Error} If the statement is malformed or contains syntax errors
|
|
66
|
-
*
|
|
67
|
+
*
|
|
67
68
|
* @example
|
|
68
69
|
* ```typescript
|
|
69
70
|
* const ast = parser.parse("LOAD JSON FROM 'https://api.adviceslip.com/advice' AS data RETURN data");
|
|
@@ -74,27 +75,32 @@ class Parser extends BaseParser {
|
|
|
74
75
|
const root: ASTNode = new ASTNode();
|
|
75
76
|
let previous: Operation | null = null;
|
|
76
77
|
let operation: Operation | null = null;
|
|
77
|
-
while(!this.token.isEOF()) {
|
|
78
|
-
if(root.childCount() > 0) {
|
|
78
|
+
while (!this.token.isEOF()) {
|
|
79
|
+
if (root.childCount() > 0) {
|
|
79
80
|
this.expectAndSkipWhitespaceAndComments();
|
|
80
81
|
} else {
|
|
81
82
|
this.skipWhitespaceAndComments();
|
|
82
83
|
}
|
|
83
84
|
operation = this.parseOperation();
|
|
84
|
-
if(operation === null) {
|
|
85
|
-
throw new Error(
|
|
85
|
+
if (operation === null) {
|
|
86
|
+
throw new Error("Expected one of WITH, UNWIND, RETURN, LOAD, OR CALL");
|
|
87
|
+
}
|
|
88
|
+
if (this._returns > 1) {
|
|
89
|
+
throw new Error("Only one RETURN statement is allowed");
|
|
86
90
|
}
|
|
87
|
-
if(
|
|
88
|
-
throw new Error(
|
|
91
|
+
if (previous instanceof Call && !previous.hasYield) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"CALL operations must have a YIELD clause unless they are the last operation"
|
|
94
|
+
);
|
|
89
95
|
}
|
|
90
|
-
if(previous !== null) {
|
|
96
|
+
if (previous !== null) {
|
|
91
97
|
previous.addSibling(operation);
|
|
92
98
|
} else {
|
|
93
99
|
root.addChild(operation);
|
|
94
100
|
}
|
|
95
101
|
const where = this.parseWhere();
|
|
96
|
-
if(where !== null) {
|
|
97
|
-
if(operation instanceof Return) {
|
|
102
|
+
if (where !== null) {
|
|
103
|
+
if (operation instanceof Return) {
|
|
98
104
|
(operation as Return).where = where;
|
|
99
105
|
} else {
|
|
100
106
|
operation.addSibling(where);
|
|
@@ -102,14 +108,14 @@ class Parser extends BaseParser {
|
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
const limit = this.parseLimit();
|
|
105
|
-
if(limit !== null) {
|
|
111
|
+
if (limit !== null) {
|
|
106
112
|
operation.addSibling(limit);
|
|
107
113
|
operation = limit;
|
|
108
114
|
}
|
|
109
115
|
previous = operation;
|
|
110
116
|
}
|
|
111
|
-
if(!(operation instanceof Return)) {
|
|
112
|
-
throw new Error(
|
|
117
|
+
if (!(operation instanceof Return) && !(operation instanceof Call)) {
|
|
118
|
+
throw new Error("Last statement must be a RETURN, WHERE, or a CALL statement");
|
|
113
119
|
}
|
|
114
120
|
return root;
|
|
115
121
|
}
|
|
@@ -119,45 +125,54 @@ class Parser extends BaseParser {
|
|
|
119
125
|
this.parseWith() ||
|
|
120
126
|
this.parseUnwind() ||
|
|
121
127
|
this.parseReturn() ||
|
|
122
|
-
this.parseLoad()
|
|
128
|
+
this.parseLoad() ||
|
|
129
|
+
this.parseCall()
|
|
123
130
|
);
|
|
124
131
|
}
|
|
125
|
-
|
|
132
|
+
|
|
126
133
|
private parseWith(): With | null {
|
|
127
|
-
if(!this.token.isWith()) {
|
|
134
|
+
if (!this.token.isWith()) {
|
|
128
135
|
return null;
|
|
129
136
|
}
|
|
130
137
|
this.setNextToken();
|
|
131
138
|
this.expectAndSkipWhitespaceAndComments();
|
|
132
139
|
const expressions = Array.from(this.parseExpressions(AliasOption.REQUIRED));
|
|
133
|
-
if(expressions.length === 0) {
|
|
134
|
-
throw new Error(
|
|
140
|
+
if (expressions.length === 0) {
|
|
141
|
+
throw new Error("Expected expression");
|
|
135
142
|
}
|
|
136
|
-
if(expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
143
|
+
if (expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
137
144
|
return new AggregatedWith(expressions);
|
|
138
145
|
}
|
|
139
146
|
return new With(expressions);
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
private parseUnwind(): Unwind | null {
|
|
143
|
-
if(!this.token.isUnwind()) {
|
|
150
|
+
if (!this.token.isUnwind()) {
|
|
144
151
|
return null;
|
|
145
152
|
}
|
|
146
153
|
this.setNextToken();
|
|
147
154
|
this.expectAndSkipWhitespaceAndComments();
|
|
148
155
|
const expression: Expression | null = this.parseExpression();
|
|
149
|
-
if(expression === null) {
|
|
150
|
-
throw new Error(
|
|
151
|
-
}
|
|
152
|
-
if(
|
|
153
|
-
|
|
156
|
+
if (expression === null) {
|
|
157
|
+
throw new Error("Expected expression");
|
|
158
|
+
}
|
|
159
|
+
if (
|
|
160
|
+
!ObjectUtils.isInstanceOfAny(expression.firstChild(), [
|
|
161
|
+
JSONArray,
|
|
162
|
+
Function,
|
|
163
|
+
Reference,
|
|
164
|
+
Lookup,
|
|
165
|
+
RangeLookup,
|
|
166
|
+
])
|
|
167
|
+
) {
|
|
168
|
+
throw new Error("Expected array, function, reference, or lookup.");
|
|
154
169
|
}
|
|
155
170
|
this.expectAndSkipWhitespaceAndComments();
|
|
156
171
|
const alias = this.parseAlias();
|
|
157
|
-
if(alias !== null) {
|
|
172
|
+
if (alias !== null) {
|
|
158
173
|
expression.setAlias(alias.getAlias());
|
|
159
174
|
} else {
|
|
160
|
-
throw new Error(
|
|
175
|
+
throw new Error("Expected alias");
|
|
161
176
|
}
|
|
162
177
|
const unwind = new Unwind(expression);
|
|
163
178
|
this.variables.set(alias.getAlias(), unwind);
|
|
@@ -165,16 +180,16 @@ class Parser extends BaseParser {
|
|
|
165
180
|
}
|
|
166
181
|
|
|
167
182
|
private parseReturn(): Return | null {
|
|
168
|
-
if(!this.token.isReturn()) {
|
|
183
|
+
if (!this.token.isReturn()) {
|
|
169
184
|
return null;
|
|
170
185
|
}
|
|
171
186
|
this.setNextToken();
|
|
172
187
|
this.expectAndSkipWhitespaceAndComments();
|
|
173
188
|
const expressions = Array.from(this.parseExpressions(AliasOption.OPTIONAL));
|
|
174
|
-
if(expressions.length === 0) {
|
|
175
|
-
throw new Error(
|
|
189
|
+
if (expressions.length === 0) {
|
|
190
|
+
throw new Error("Expected expression");
|
|
176
191
|
}
|
|
177
|
-
if(expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
192
|
+
if (expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
178
193
|
return new AggregatedReturn(expressions);
|
|
179
194
|
}
|
|
180
195
|
this._returns++;
|
|
@@ -182,120 +197,155 @@ class Parser extends BaseParser {
|
|
|
182
197
|
}
|
|
183
198
|
|
|
184
199
|
private parseWhere(): Where | null {
|
|
185
|
-
if(!this.token.isWhere()) {
|
|
200
|
+
if (!this.token.isWhere()) {
|
|
186
201
|
return null;
|
|
187
202
|
}
|
|
188
203
|
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
189
204
|
this.setNextToken();
|
|
190
205
|
this.expectAndSkipWhitespaceAndComments();
|
|
191
206
|
const expression = this.parseExpression();
|
|
192
|
-
if(expression === null) {
|
|
193
|
-
throw new Error(
|
|
207
|
+
if (expression === null) {
|
|
208
|
+
throw new Error("Expected expression");
|
|
194
209
|
}
|
|
195
|
-
if(ObjectUtils.isInstanceOfAny(expression.firstChild(), [JSONArray, AssociativeArray])) {
|
|
196
|
-
throw new Error(
|
|
210
|
+
if (ObjectUtils.isInstanceOfAny(expression.firstChild(), [JSONArray, AssociativeArray])) {
|
|
211
|
+
throw new Error("Expected an expression which can be evaluated to a boolean");
|
|
197
212
|
}
|
|
198
213
|
return new Where(expression);
|
|
199
214
|
}
|
|
200
215
|
|
|
201
216
|
private parseLoad(): Load | null {
|
|
202
|
-
if(!this.token.isLoad()) {
|
|
217
|
+
if (!this.token.isLoad()) {
|
|
203
218
|
return null;
|
|
204
219
|
}
|
|
205
220
|
const load = new Load();
|
|
206
221
|
this.setNextToken();
|
|
207
222
|
this.expectAndSkipWhitespaceAndComments();
|
|
208
|
-
if(!(this.token.isJSON() || this.token.isCSV() || this.token.isText())) {
|
|
209
|
-
throw new Error(
|
|
223
|
+
if (!(this.token.isJSON() || this.token.isCSV() || this.token.isText())) {
|
|
224
|
+
throw new Error("Expected JSON, CSV, or TEXT");
|
|
210
225
|
}
|
|
211
226
|
load.addChild(this.token.node);
|
|
212
227
|
this.setNextToken();
|
|
213
228
|
this.expectAndSkipWhitespaceAndComments();
|
|
214
|
-
if(!this.token.isFrom()) {
|
|
215
|
-
throw new Error(
|
|
229
|
+
if (!this.token.isFrom()) {
|
|
230
|
+
throw new Error("Expected FROM");
|
|
216
231
|
}
|
|
217
232
|
this.setNextToken();
|
|
218
233
|
this.expectAndSkipWhitespaceAndComments();
|
|
219
234
|
const from = new From();
|
|
220
235
|
load.addChild(from);
|
|
221
|
-
|
|
236
|
+
|
|
222
237
|
// Check if the source is an async function
|
|
223
238
|
const asyncFunc = this.parseAsyncFunction();
|
|
224
239
|
if (asyncFunc !== null) {
|
|
225
240
|
from.addChild(asyncFunc);
|
|
226
241
|
} else {
|
|
227
242
|
const expression = this.parseExpression();
|
|
228
|
-
if(expression === null) {
|
|
229
|
-
throw new Error(
|
|
243
|
+
if (expression === null) {
|
|
244
|
+
throw new Error("Expected expression or async function");
|
|
230
245
|
}
|
|
231
246
|
from.addChild(expression);
|
|
232
247
|
}
|
|
233
|
-
|
|
248
|
+
|
|
234
249
|
this.expectAndSkipWhitespaceAndComments();
|
|
235
|
-
if(this.token.isHeaders()) {
|
|
250
|
+
if (this.token.isHeaders()) {
|
|
236
251
|
const headers = new Headers();
|
|
237
252
|
this.setNextToken();
|
|
238
253
|
this.expectAndSkipWhitespaceAndComments();
|
|
239
254
|
const header = this.parseExpression();
|
|
240
|
-
if(header === null) {
|
|
241
|
-
throw new Error(
|
|
255
|
+
if (header === null) {
|
|
256
|
+
throw new Error("Expected expression");
|
|
242
257
|
}
|
|
243
258
|
headers.addChild(header);
|
|
244
259
|
load.addChild(headers);
|
|
245
260
|
this.expectAndSkipWhitespaceAndComments();
|
|
246
261
|
}
|
|
247
|
-
if(this.token.isPost()) {
|
|
262
|
+
if (this.token.isPost()) {
|
|
248
263
|
const post = new Post();
|
|
249
264
|
this.setNextToken();
|
|
250
265
|
this.expectAndSkipWhitespaceAndComments();
|
|
251
266
|
const payload = this.parseExpression();
|
|
252
|
-
if(payload === null) {
|
|
253
|
-
throw new Error(
|
|
267
|
+
if (payload === null) {
|
|
268
|
+
throw new Error("Expected expression");
|
|
254
269
|
}
|
|
255
270
|
post.addChild(payload);
|
|
256
271
|
load.addChild(post);
|
|
257
272
|
this.expectAndSkipWhitespaceAndComments();
|
|
258
273
|
}
|
|
259
274
|
const alias = this.parseAlias();
|
|
260
|
-
if(alias !== null) {
|
|
275
|
+
if (alias !== null) {
|
|
261
276
|
load.addChild(alias);
|
|
262
277
|
this.variables.set(alias.getAlias(), load);
|
|
263
278
|
} else {
|
|
264
|
-
throw new Error(
|
|
279
|
+
throw new Error("Expected alias");
|
|
265
280
|
}
|
|
266
281
|
return load;
|
|
267
282
|
}
|
|
268
283
|
|
|
284
|
+
private parseCall(): Call | null {
|
|
285
|
+
if (!this.token.isCall()) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
this.setNextToken();
|
|
289
|
+
this.expectAndSkipWhitespaceAndComments();
|
|
290
|
+
const asyncFunction = this.parseAsyncFunction();
|
|
291
|
+
if (asyncFunction === null) {
|
|
292
|
+
throw new Error("Expected async function");
|
|
293
|
+
}
|
|
294
|
+
const call = new Call();
|
|
295
|
+
call.function = asyncFunction;
|
|
296
|
+
this.skipWhitespaceAndComments();
|
|
297
|
+
if (this.token.isYield()) {
|
|
298
|
+
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
299
|
+
this.setNextToken();
|
|
300
|
+
this.expectAndSkipWhitespaceAndComments();
|
|
301
|
+
const expressions = Array.from(this.parseExpressions(AliasOption.OPTIONAL));
|
|
302
|
+
if (expressions.length === 0) {
|
|
303
|
+
throw new Error("Expected at least one expression");
|
|
304
|
+
}
|
|
305
|
+
call.yielded = expressions;
|
|
306
|
+
}
|
|
307
|
+
return call;
|
|
308
|
+
}
|
|
309
|
+
|
|
269
310
|
private parseLimit(): Limit | null {
|
|
270
311
|
this.skipWhitespaceAndComments();
|
|
271
|
-
if(!this.token.isLimit()) {
|
|
312
|
+
if (!this.token.isLimit()) {
|
|
272
313
|
return null;
|
|
273
314
|
}
|
|
274
315
|
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
275
316
|
this.setNextToken();
|
|
276
317
|
this.expectAndSkipWhitespaceAndComments();
|
|
277
|
-
if(!this.token.isNumber()) {
|
|
278
|
-
throw new Error(
|
|
318
|
+
if (!this.token.isNumber()) {
|
|
319
|
+
throw new Error("Expected number");
|
|
279
320
|
}
|
|
280
|
-
const limit = new Limit(parseInt(this.token.value ||
|
|
321
|
+
const limit = new Limit(parseInt(this.token.value || "0"));
|
|
281
322
|
this.setNextToken();
|
|
282
323
|
return limit;
|
|
283
324
|
}
|
|
284
325
|
|
|
285
|
-
private *parseExpressions(
|
|
286
|
-
|
|
326
|
+
private *parseExpressions(
|
|
327
|
+
alias_option: AliasOption = AliasOption.NOT_ALLOWED
|
|
328
|
+
): IterableIterator<Expression> {
|
|
329
|
+
while (true) {
|
|
287
330
|
const expression: Expression | null = this.parseExpression();
|
|
288
|
-
if(expression !== null) {
|
|
331
|
+
if (expression !== null) {
|
|
289
332
|
const alias = this.parseAlias();
|
|
290
|
-
if(expression.firstChild() instanceof Reference && alias === null) {
|
|
333
|
+
if (expression.firstChild() instanceof Reference && alias === null) {
|
|
291
334
|
const reference: Reference = expression.firstChild() as Reference;
|
|
292
335
|
expression.setAlias(reference.identifier);
|
|
293
336
|
this.variables.set(reference.identifier, expression);
|
|
294
|
-
} else if
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
337
|
+
} else if (
|
|
338
|
+
alias_option === AliasOption.REQUIRED &&
|
|
339
|
+
alias === null &&
|
|
340
|
+
!(expression.firstChild() instanceof Reference)
|
|
341
|
+
) {
|
|
342
|
+
throw new Error("Alias required");
|
|
343
|
+
} else if (alias_option === AliasOption.NOT_ALLOWED && alias !== null) {
|
|
344
|
+
throw new Error("Alias not allowed");
|
|
345
|
+
} else if (
|
|
346
|
+
[AliasOption.OPTIONAL, AliasOption.REQUIRED].includes(alias_option) &&
|
|
347
|
+
alias !== null
|
|
348
|
+
) {
|
|
299
349
|
expression.setAlias(alias.getAlias());
|
|
300
350
|
this.variables.set(alias.getAlias(), expression);
|
|
301
351
|
}
|
|
@@ -304,7 +354,7 @@ class Parser extends BaseParser {
|
|
|
304
354
|
break;
|
|
305
355
|
}
|
|
306
356
|
this.skipWhitespaceAndComments();
|
|
307
|
-
if(!this.token.isComma()) {
|
|
357
|
+
if (!this.token.isComma()) {
|
|
308
358
|
break;
|
|
309
359
|
}
|
|
310
360
|
this.setNextToken();
|
|
@@ -313,79 +363,79 @@ class Parser extends BaseParser {
|
|
|
313
363
|
|
|
314
364
|
private parseExpression(): Expression | null {
|
|
315
365
|
const expression = new Expression();
|
|
316
|
-
while(true) {
|
|
366
|
+
while (true) {
|
|
317
367
|
this.skipWhitespaceAndComments();
|
|
318
|
-
if(this.token.isIdentifier() && !this.peek()?.isLeftParenthesis()) {
|
|
319
|
-
const identifier: string = this.token.value ||
|
|
368
|
+
if (this.token.isIdentifier() && !this.peek()?.isLeftParenthesis()) {
|
|
369
|
+
const identifier: string = this.token.value || "";
|
|
320
370
|
const reference = new Reference(identifier, this.variables.get(identifier));
|
|
321
371
|
this.setNextToken();
|
|
322
372
|
const lookup = this.parseLookup(reference);
|
|
323
373
|
expression.addNode(lookup);
|
|
324
|
-
} else if(this.token.isIdentifier() && this.peek()?.isLeftParenthesis()) {
|
|
374
|
+
} else if (this.token.isIdentifier() && this.peek()?.isLeftParenthesis()) {
|
|
325
375
|
const func = this.parsePredicateFunction() || this.parseFunction();
|
|
326
|
-
if(func !== null) {
|
|
376
|
+
if (func !== null) {
|
|
327
377
|
const lookup = this.parseLookup(func);
|
|
328
378
|
expression.addNode(lookup);
|
|
329
379
|
}
|
|
330
|
-
} else if(this.token.isOperand()) {
|
|
380
|
+
} else if (this.token.isOperand()) {
|
|
331
381
|
expression.addNode(this.token.node);
|
|
332
382
|
this.setNextToken();
|
|
333
|
-
} else if(this.token.isFString()) {
|
|
383
|
+
} else if (this.token.isFString()) {
|
|
334
384
|
const f_string = this.parseFString();
|
|
335
|
-
if(f_string === null) {
|
|
336
|
-
throw new Error(
|
|
385
|
+
if (f_string === null) {
|
|
386
|
+
throw new Error("Expected f-string");
|
|
337
387
|
}
|
|
338
388
|
expression.addNode(f_string);
|
|
339
|
-
} else if(this.token.isLeftParenthesis()) {
|
|
389
|
+
} else if (this.token.isLeftParenthesis()) {
|
|
340
390
|
this.setNextToken();
|
|
341
391
|
const sub = this.parseExpression();
|
|
342
|
-
if(sub === null) {
|
|
343
|
-
throw new Error(
|
|
392
|
+
if (sub === null) {
|
|
393
|
+
throw new Error("Expected expression");
|
|
344
394
|
}
|
|
345
|
-
if(!this.token.isRightParenthesis()) {
|
|
346
|
-
throw new Error(
|
|
395
|
+
if (!this.token.isRightParenthesis()) {
|
|
396
|
+
throw new Error("Expected right parenthesis");
|
|
347
397
|
}
|
|
348
398
|
this.setNextToken();
|
|
349
399
|
const lookup = this.parseLookup(sub);
|
|
350
400
|
expression.addNode(lookup);
|
|
351
|
-
} else if(this.token.isOpeningBrace() || this.token.isOpeningBracket()) {
|
|
401
|
+
} else if (this.token.isOpeningBrace() || this.token.isOpeningBracket()) {
|
|
352
402
|
const json = this.parseJSON();
|
|
353
|
-
if(json === null) {
|
|
354
|
-
throw new Error(
|
|
403
|
+
if (json === null) {
|
|
404
|
+
throw new Error("Expected JSON object");
|
|
355
405
|
}
|
|
356
406
|
const lookup = this.parseLookup(json);
|
|
357
407
|
expression.addNode(lookup);
|
|
358
|
-
} else if(this.token.isCase()) {
|
|
408
|
+
} else if (this.token.isCase()) {
|
|
359
409
|
const _case = this.parseCase();
|
|
360
|
-
if(_case === null) {
|
|
361
|
-
throw new Error(
|
|
410
|
+
if (_case === null) {
|
|
411
|
+
throw new Error("Expected CASE statement");
|
|
362
412
|
}
|
|
363
413
|
expression.addNode(_case);
|
|
364
|
-
} else if(this.token.isNot()) {
|
|
414
|
+
} else if (this.token.isNot()) {
|
|
365
415
|
const not = new Not();
|
|
366
416
|
this.setNextToken();
|
|
367
417
|
const sub = this.parseExpression();
|
|
368
|
-
if(sub === null) {
|
|
369
|
-
throw new Error(
|
|
418
|
+
if (sub === null) {
|
|
419
|
+
throw new Error("Expected expression");
|
|
370
420
|
}
|
|
371
421
|
not.addChild(sub);
|
|
372
422
|
expression.addNode(not);
|
|
373
423
|
} else {
|
|
374
|
-
if(expression.nodesAdded()) {
|
|
375
|
-
throw new Error(
|
|
424
|
+
if (expression.nodesAdded()) {
|
|
425
|
+
throw new Error("Expected operand or left parenthesis");
|
|
376
426
|
} else {
|
|
377
427
|
break;
|
|
378
428
|
}
|
|
379
429
|
}
|
|
380
430
|
this.skipWhitespaceAndComments();
|
|
381
|
-
if(this.token.isOperator()) {
|
|
431
|
+
if (this.token.isOperator()) {
|
|
382
432
|
expression.addNode(this.token.node);
|
|
383
433
|
} else {
|
|
384
434
|
break;
|
|
385
435
|
}
|
|
386
436
|
this.setNextToken();
|
|
387
437
|
}
|
|
388
|
-
if(expression.nodesAdded()) {
|
|
438
|
+
if (expression.nodesAdded()) {
|
|
389
439
|
expression.finish();
|
|
390
440
|
return expression;
|
|
391
441
|
}
|
|
@@ -395,42 +445,42 @@ class Parser extends BaseParser {
|
|
|
395
445
|
private parseLookup(node: ASTNode): ASTNode {
|
|
396
446
|
let variable: ASTNode = node;
|
|
397
447
|
let lookup: Lookup | RangeLookup | null = null;
|
|
398
|
-
while(true) {
|
|
399
|
-
if(this.token.isDot()) {
|
|
448
|
+
while (true) {
|
|
449
|
+
if (this.token.isDot()) {
|
|
400
450
|
this.setNextToken();
|
|
401
|
-
if(!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
402
|
-
throw new Error(
|
|
451
|
+
if (!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
452
|
+
throw new Error("Expected identifier");
|
|
403
453
|
}
|
|
404
454
|
lookup = new Lookup();
|
|
405
|
-
lookup.index = new Identifier(this.token.value ||
|
|
455
|
+
lookup.index = new Identifier(this.token.value || "");
|
|
406
456
|
lookup.variable = variable;
|
|
407
457
|
this.setNextToken();
|
|
408
|
-
} else if(this.token.isOpeningBracket()) {
|
|
458
|
+
} else if (this.token.isOpeningBracket()) {
|
|
409
459
|
this.setNextToken();
|
|
410
460
|
this.skipWhitespaceAndComments();
|
|
411
461
|
const index = this.parseExpression();
|
|
412
462
|
let to: Expression | null = null;
|
|
413
463
|
this.skipWhitespaceAndComments();
|
|
414
|
-
if(this.token.isColon()) {
|
|
464
|
+
if (this.token.isColon()) {
|
|
415
465
|
this.setNextToken();
|
|
416
466
|
this.skipWhitespaceAndComments();
|
|
417
467
|
lookup = new RangeLookup();
|
|
418
468
|
to = this.parseExpression();
|
|
419
469
|
} else {
|
|
420
|
-
if(index === null) {
|
|
421
|
-
throw new Error(
|
|
470
|
+
if (index === null) {
|
|
471
|
+
throw new Error("Expected expression");
|
|
422
472
|
}
|
|
423
473
|
lookup = new Lookup();
|
|
424
474
|
}
|
|
425
475
|
this.skipWhitespaceAndComments();
|
|
426
|
-
if(!this.token.isClosingBracket()) {
|
|
427
|
-
throw new Error(
|
|
476
|
+
if (!this.token.isClosingBracket()) {
|
|
477
|
+
throw new Error("Expected closing bracket");
|
|
428
478
|
}
|
|
429
479
|
this.setNextToken();
|
|
430
|
-
if(lookup instanceof RangeLookup) {
|
|
480
|
+
if (lookup instanceof RangeLookup) {
|
|
431
481
|
lookup.from = index || new Null();
|
|
432
482
|
lookup.to = to || new Null();
|
|
433
|
-
} else if(lookup instanceof Lookup && index !== null) {
|
|
483
|
+
} else if (lookup instanceof Lookup && index !== null) {
|
|
434
484
|
lookup.index = index;
|
|
435
485
|
}
|
|
436
486
|
lookup.variable = variable;
|
|
@@ -443,26 +493,26 @@ class Parser extends BaseParser {
|
|
|
443
493
|
}
|
|
444
494
|
|
|
445
495
|
private parseCase(): Case | null {
|
|
446
|
-
if(!this.token.isCase()) {
|
|
496
|
+
if (!this.token.isCase()) {
|
|
447
497
|
return null;
|
|
448
498
|
}
|
|
449
499
|
this.setNextToken();
|
|
450
500
|
const _case = new Case();
|
|
451
501
|
let parts: number = 0;
|
|
452
502
|
this.expectAndSkipWhitespaceAndComments();
|
|
453
|
-
while(true) {
|
|
503
|
+
while (true) {
|
|
454
504
|
const when = this.parseWhen();
|
|
455
|
-
if(when === null && parts === 0) {
|
|
456
|
-
throw new Error(
|
|
457
|
-
} else if(when === null && parts > 0) {
|
|
505
|
+
if (when === null && parts === 0) {
|
|
506
|
+
throw new Error("Expected WHEN");
|
|
507
|
+
} else if (when === null && parts > 0) {
|
|
458
508
|
break;
|
|
459
509
|
} else if (when !== null) {
|
|
460
510
|
_case.addChild(when);
|
|
461
511
|
}
|
|
462
512
|
this.expectAndSkipWhitespaceAndComments();
|
|
463
513
|
const then = this.parseThen();
|
|
464
|
-
if(then === null) {
|
|
465
|
-
throw new Error(
|
|
514
|
+
if (then === null) {
|
|
515
|
+
throw new Error("Expected THEN");
|
|
466
516
|
} else {
|
|
467
517
|
_case.addChild(then);
|
|
468
518
|
}
|
|
@@ -470,59 +520,59 @@ class Parser extends BaseParser {
|
|
|
470
520
|
parts++;
|
|
471
521
|
}
|
|
472
522
|
const _else = this.parseElse();
|
|
473
|
-
if(_else === null) {
|
|
474
|
-
throw new Error(
|
|
523
|
+
if (_else === null) {
|
|
524
|
+
throw new Error("Expected ELSE");
|
|
475
525
|
} else {
|
|
476
526
|
_case.addChild(_else);
|
|
477
527
|
}
|
|
478
528
|
this.expectAndSkipWhitespaceAndComments();
|
|
479
|
-
if(!this.token.isEnd()) {
|
|
480
|
-
throw new Error(
|
|
529
|
+
if (!this.token.isEnd()) {
|
|
530
|
+
throw new Error("Expected END");
|
|
481
531
|
}
|
|
482
532
|
this.setNextToken();
|
|
483
533
|
return _case;
|
|
484
534
|
}
|
|
485
535
|
|
|
486
536
|
private parseWhen(): When | null {
|
|
487
|
-
if(!this.token.isWhen()) {
|
|
537
|
+
if (!this.token.isWhen()) {
|
|
488
538
|
return null;
|
|
489
539
|
}
|
|
490
540
|
this.setNextToken();
|
|
491
541
|
const when = new When();
|
|
492
542
|
this.expectAndSkipWhitespaceAndComments();
|
|
493
543
|
const expression = this.parseExpression();
|
|
494
|
-
if(expression === null) {
|
|
495
|
-
throw new Error(
|
|
544
|
+
if (expression === null) {
|
|
545
|
+
throw new Error("Expected expression");
|
|
496
546
|
}
|
|
497
547
|
when.addChild(expression);
|
|
498
548
|
return when;
|
|
499
549
|
}
|
|
500
550
|
|
|
501
551
|
private parseThen(): Then | null {
|
|
502
|
-
if(!this.token.isThen()) {
|
|
552
|
+
if (!this.token.isThen()) {
|
|
503
553
|
return null;
|
|
504
554
|
}
|
|
505
555
|
this.setNextToken();
|
|
506
556
|
const then = new Then();
|
|
507
557
|
this.expectAndSkipWhitespaceAndComments();
|
|
508
558
|
const expression = this.parseExpression();
|
|
509
|
-
if(expression === null) {
|
|
510
|
-
throw new Error(
|
|
559
|
+
if (expression === null) {
|
|
560
|
+
throw new Error("Expected expression");
|
|
511
561
|
}
|
|
512
562
|
then.addChild(expression);
|
|
513
563
|
return then;
|
|
514
564
|
}
|
|
515
565
|
|
|
516
566
|
private parseElse(): Else | null {
|
|
517
|
-
if(!this.token.isElse()) {
|
|
567
|
+
if (!this.token.isElse()) {
|
|
518
568
|
return null;
|
|
519
569
|
}
|
|
520
570
|
this.setNextToken();
|
|
521
571
|
const _else = new Else();
|
|
522
572
|
this.expectAndSkipWhitespaceAndComments();
|
|
523
573
|
const expression = this.parseExpression();
|
|
524
|
-
if(expression === null) {
|
|
525
|
-
throw new Error(
|
|
574
|
+
if (expression === null) {
|
|
575
|
+
throw new Error("Expected expression");
|
|
526
576
|
}
|
|
527
577
|
_else.addChild(expression);
|
|
528
578
|
return _else;
|
|
@@ -530,47 +580,47 @@ class Parser extends BaseParser {
|
|
|
530
580
|
|
|
531
581
|
private parseAlias(): Alias | null {
|
|
532
582
|
this.skipWhitespaceAndComments();
|
|
533
|
-
if(!this.token.isAs()) {
|
|
583
|
+
if (!this.token.isAs()) {
|
|
534
584
|
return null;
|
|
535
585
|
}
|
|
536
586
|
this.expectPreviousTokenToBeWhitespaceOrComment();
|
|
537
587
|
this.setNextToken();
|
|
538
588
|
this.expectAndSkipWhitespaceAndComments();
|
|
539
|
-
if((!this.token.isIdentifier() && !this.token.isKeyword()) || this.token.value === null) {
|
|
540
|
-
throw new Error(
|
|
589
|
+
if ((!this.token.isIdentifier() && !this.token.isKeyword()) || this.token.value === null) {
|
|
590
|
+
throw new Error("Expected identifier");
|
|
541
591
|
}
|
|
542
|
-
const alias = new Alias(this.token.value ||
|
|
592
|
+
const alias = new Alias(this.token.value || "");
|
|
543
593
|
this.setNextToken();
|
|
544
594
|
return alias;
|
|
545
595
|
}
|
|
546
596
|
|
|
547
597
|
private parseFunction(): Function | null {
|
|
548
|
-
if(!this.token.isIdentifier()) {
|
|
598
|
+
if (!this.token.isIdentifier()) {
|
|
549
599
|
return null;
|
|
550
600
|
}
|
|
551
|
-
if(this.token.value === null) {
|
|
552
|
-
throw new Error(
|
|
601
|
+
if (this.token.value === null) {
|
|
602
|
+
throw new Error("Expected identifier");
|
|
553
603
|
}
|
|
554
|
-
if(!this.peek()?.isLeftParenthesis()) {
|
|
604
|
+
if (!this.peek()?.isLeftParenthesis()) {
|
|
555
605
|
return null;
|
|
556
606
|
}
|
|
557
607
|
const func = FunctionFactory.create(this.token.value);
|
|
558
|
-
if(func instanceof AggregateFunction && this.context.containsType(AggregateFunction)) {
|
|
559
|
-
throw new Error(
|
|
608
|
+
if (func instanceof AggregateFunction && this.context.containsType(AggregateFunction)) {
|
|
609
|
+
throw new Error("Aggregate functions cannot be nested");
|
|
560
610
|
}
|
|
561
611
|
this.context.push(func);
|
|
562
612
|
this.setNextToken();
|
|
563
613
|
this.setNextToken();
|
|
564
614
|
this.skipWhitespaceAndComments();
|
|
565
|
-
if(this.token.isDistinct()) {
|
|
615
|
+
if (this.token.isDistinct()) {
|
|
566
616
|
func.distinct = true;
|
|
567
617
|
this.setNextToken();
|
|
568
618
|
this.expectAndSkipWhitespaceAndComments();
|
|
569
619
|
}
|
|
570
620
|
func.parameters = Array.from(this.parseExpressions(AliasOption.NOT_ALLOWED));
|
|
571
621
|
this.skipWhitespaceAndComments();
|
|
572
|
-
if(!this.token.isRightParenthesis()) {
|
|
573
|
-
throw new Error(
|
|
622
|
+
if (!this.token.isRightParenthesis()) {
|
|
623
|
+
throw new Error("Expected right parenthesis");
|
|
574
624
|
}
|
|
575
625
|
this.setNextToken();
|
|
576
626
|
this.context.pop();
|
|
@@ -580,21 +630,21 @@ class Parser extends BaseParser {
|
|
|
580
630
|
/**
|
|
581
631
|
* Parses an async function call for use in LOAD operations.
|
|
582
632
|
* Only matches if the identifier is registered as an async data provider.
|
|
583
|
-
*
|
|
633
|
+
*
|
|
584
634
|
* @returns An AsyncFunction node if a registered async function is found, otherwise null
|
|
585
635
|
*/
|
|
586
636
|
private parseAsyncFunction(): AsyncFunction | null {
|
|
587
|
-
if(!this.token.isIdentifier()) {
|
|
637
|
+
if (!this.token.isIdentifier()) {
|
|
588
638
|
return null;
|
|
589
639
|
}
|
|
590
|
-
if(this.token.value === null) {
|
|
640
|
+
if (this.token.value === null) {
|
|
591
641
|
return null;
|
|
592
642
|
}
|
|
593
643
|
// Only parse as async function if it's registered as an async provider
|
|
594
|
-
if(!FunctionFactory.isAsyncProvider(this.token.value)) {
|
|
644
|
+
if (!FunctionFactory.isAsyncProvider(this.token.value)) {
|
|
595
645
|
return null;
|
|
596
646
|
}
|
|
597
|
-
if(!this.peek()?.isLeftParenthesis()) {
|
|
647
|
+
if (!this.peek()?.isLeftParenthesis()) {
|
|
598
648
|
return null;
|
|
599
649
|
}
|
|
600
650
|
const asyncFunc = FunctionFactory.createAsync(this.token.value);
|
|
@@ -603,65 +653,79 @@ class Parser extends BaseParser {
|
|
|
603
653
|
this.skipWhitespaceAndComments();
|
|
604
654
|
asyncFunc.parameters = Array.from(this.parseExpressions(AliasOption.NOT_ALLOWED));
|
|
605
655
|
this.skipWhitespaceAndComments();
|
|
606
|
-
if(!this.token.isRightParenthesis()) {
|
|
607
|
-
throw new Error(
|
|
656
|
+
if (!this.token.isRightParenthesis()) {
|
|
657
|
+
throw new Error("Expected right parenthesis");
|
|
608
658
|
}
|
|
609
659
|
this.setNextToken();
|
|
610
660
|
return asyncFunc;
|
|
611
661
|
}
|
|
612
|
-
|
|
662
|
+
|
|
613
663
|
private parsePredicateFunction(): PredicateFunction | null {
|
|
614
|
-
if
|
|
664
|
+
if (
|
|
665
|
+
!this.ahead([
|
|
666
|
+
Token.IDENTIFIER(""),
|
|
667
|
+
Token.LEFT_PARENTHESIS,
|
|
668
|
+
Token.IDENTIFIER(""),
|
|
669
|
+
Token.IN,
|
|
670
|
+
])
|
|
671
|
+
) {
|
|
615
672
|
return null;
|
|
616
673
|
}
|
|
617
|
-
if(this.token.value === null) {
|
|
618
|
-
throw new Error(
|
|
674
|
+
if (this.token.value === null) {
|
|
675
|
+
throw new Error("Expected identifier");
|
|
619
676
|
}
|
|
620
677
|
const func = FunctionFactory.createPredicate(this.token.value);
|
|
621
678
|
this.setNextToken();
|
|
622
|
-
if(!this.token.isLeftParenthesis()) {
|
|
623
|
-
throw new Error(
|
|
679
|
+
if (!this.token.isLeftParenthesis()) {
|
|
680
|
+
throw new Error("Expected left parenthesis");
|
|
624
681
|
}
|
|
625
682
|
this.setNextToken();
|
|
626
683
|
this.skipWhitespaceAndComments();
|
|
627
|
-
if(!this.token.isIdentifier()) {
|
|
628
|
-
throw new Error(
|
|
684
|
+
if (!this.token.isIdentifier()) {
|
|
685
|
+
throw new Error("Expected identifier");
|
|
629
686
|
}
|
|
630
687
|
const reference = new Reference(this.token.value);
|
|
631
688
|
this.variables.set(reference.identifier, reference);
|
|
632
689
|
func.addChild(reference);
|
|
633
690
|
this.setNextToken();
|
|
634
691
|
this.expectAndSkipWhitespaceAndComments();
|
|
635
|
-
if(!this.token.isIn()) {
|
|
636
|
-
throw new Error(
|
|
692
|
+
if (!this.token.isIn()) {
|
|
693
|
+
throw new Error("Expected IN");
|
|
637
694
|
}
|
|
638
695
|
this.setNextToken();
|
|
639
696
|
this.expectAndSkipWhitespaceAndComments();
|
|
640
697
|
const expression = this.parseExpression();
|
|
641
|
-
if(expression === null) {
|
|
642
|
-
throw new Error(
|
|
643
|
-
}
|
|
644
|
-
if(
|
|
645
|
-
|
|
698
|
+
if (expression === null) {
|
|
699
|
+
throw new Error("Expected expression");
|
|
700
|
+
}
|
|
701
|
+
if (
|
|
702
|
+
!ObjectUtils.isInstanceOfAny(expression.firstChild(), [
|
|
703
|
+
JSONArray,
|
|
704
|
+
Reference,
|
|
705
|
+
Lookup,
|
|
706
|
+
Function,
|
|
707
|
+
])
|
|
708
|
+
) {
|
|
709
|
+
throw new Error("Expected array or reference");
|
|
646
710
|
}
|
|
647
711
|
func.addChild(expression);
|
|
648
712
|
this.skipWhitespaceAndComments();
|
|
649
|
-
if(!this.token.isPipe()) {
|
|
650
|
-
throw new Error(
|
|
713
|
+
if (!this.token.isPipe()) {
|
|
714
|
+
throw new Error("Expected pipe");
|
|
651
715
|
}
|
|
652
716
|
this.setNextToken();
|
|
653
717
|
const _return = this.parseExpression();
|
|
654
|
-
if(_return === null) {
|
|
655
|
-
throw new Error(
|
|
718
|
+
if (_return === null) {
|
|
719
|
+
throw new Error("Expected expression");
|
|
656
720
|
}
|
|
657
721
|
func.addChild(_return);
|
|
658
722
|
const where = this.parseWhere();
|
|
659
|
-
if(where !== null) {
|
|
723
|
+
if (where !== null) {
|
|
660
724
|
func.addChild(where);
|
|
661
725
|
}
|
|
662
726
|
this.skipWhitespaceAndComments();
|
|
663
|
-
if(!this.token.isRightParenthesis()) {
|
|
664
|
-
throw new Error(
|
|
727
|
+
if (!this.token.isRightParenthesis()) {
|
|
728
|
+
throw new Error("Expected right parenthesis");
|
|
665
729
|
}
|
|
666
730
|
this.setNextToken();
|
|
667
731
|
this.variables.delete(reference.identifier);
|
|
@@ -669,24 +733,24 @@ class Parser extends BaseParser {
|
|
|
669
733
|
}
|
|
670
734
|
|
|
671
735
|
private parseFString(): FString | null {
|
|
672
|
-
if(!this.token.isFString()) {
|
|
736
|
+
if (!this.token.isFString()) {
|
|
673
737
|
return null;
|
|
674
738
|
}
|
|
675
739
|
const f_string = new FString();
|
|
676
|
-
while(this.token.isFString()) {
|
|
677
|
-
if(this.token.value !== null) {
|
|
740
|
+
while (this.token.isFString()) {
|
|
741
|
+
if (this.token.value !== null) {
|
|
678
742
|
f_string.addChild(new String(this.token.value));
|
|
679
743
|
}
|
|
680
744
|
this.setNextToken();
|
|
681
|
-
if(this.token.isOpeningBrace()) {
|
|
745
|
+
if (this.token.isOpeningBrace()) {
|
|
682
746
|
this.setNextToken();
|
|
683
747
|
const expression = this.parseExpression();
|
|
684
|
-
if(expression === null) {
|
|
685
|
-
throw new Error(
|
|
748
|
+
if (expression === null) {
|
|
749
|
+
throw new Error("Expected expression");
|
|
686
750
|
}
|
|
687
751
|
f_string.addChild(expression);
|
|
688
|
-
if(!this.token.isClosingBrace()) {
|
|
689
|
-
throw new Error(
|
|
752
|
+
if (!this.token.isClosingBrace()) {
|
|
753
|
+
throw new Error("Expected closing brace");
|
|
690
754
|
}
|
|
691
755
|
this.setNextToken();
|
|
692
756
|
} else {
|
|
@@ -697,54 +761,54 @@ class Parser extends BaseParser {
|
|
|
697
761
|
}
|
|
698
762
|
|
|
699
763
|
private parseJSON(): AssociativeArray | JSONArray {
|
|
700
|
-
if(this.token.isOpeningBrace()) {
|
|
764
|
+
if (this.token.isOpeningBrace()) {
|
|
701
765
|
const array = this.parseAssociativeArray();
|
|
702
|
-
if(array === null) {
|
|
703
|
-
throw new Error(
|
|
766
|
+
if (array === null) {
|
|
767
|
+
throw new Error("Expected associative array");
|
|
704
768
|
}
|
|
705
769
|
return array;
|
|
706
|
-
} else if(this.token.isOpeningBracket()) {
|
|
770
|
+
} else if (this.token.isOpeningBracket()) {
|
|
707
771
|
const array = this.parseJSONArray();
|
|
708
|
-
if(array === null) {
|
|
709
|
-
throw new Error(
|
|
772
|
+
if (array === null) {
|
|
773
|
+
throw new Error("Expected JSON array");
|
|
710
774
|
}
|
|
711
775
|
return array;
|
|
712
776
|
}
|
|
713
|
-
throw new Error(
|
|
777
|
+
throw new Error("Expected opening brace or bracket");
|
|
714
778
|
}
|
|
715
779
|
|
|
716
780
|
private parseAssociativeArray(): AssociativeArray | null {
|
|
717
|
-
if(!this.token.isOpeningBrace()) {
|
|
781
|
+
if (!this.token.isOpeningBrace()) {
|
|
718
782
|
return null;
|
|
719
783
|
}
|
|
720
784
|
const array = new AssociativeArray();
|
|
721
785
|
this.setNextToken();
|
|
722
|
-
while(true) {
|
|
786
|
+
while (true) {
|
|
723
787
|
this.skipWhitespaceAndComments();
|
|
724
|
-
if(this.token.isClosingBrace()) {
|
|
788
|
+
if (this.token.isClosingBrace()) {
|
|
725
789
|
break;
|
|
726
790
|
}
|
|
727
|
-
if(!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
728
|
-
throw new Error(
|
|
791
|
+
if (!this.token.isIdentifier() && !this.token.isKeyword()) {
|
|
792
|
+
throw new Error("Expected identifier");
|
|
729
793
|
}
|
|
730
794
|
const key = this.token.value;
|
|
731
|
-
if(key === null) {
|
|
732
|
-
throw new Error(
|
|
795
|
+
if (key === null) {
|
|
796
|
+
throw new Error("Expected string");
|
|
733
797
|
}
|
|
734
798
|
this.setNextToken();
|
|
735
799
|
this.skipWhitespaceAndComments();
|
|
736
|
-
if(!this.token.isColon()) {
|
|
737
|
-
throw new Error(
|
|
800
|
+
if (!this.token.isColon()) {
|
|
801
|
+
throw new Error("Expected colon");
|
|
738
802
|
}
|
|
739
803
|
this.setNextToken();
|
|
740
804
|
this.skipWhitespaceAndComments();
|
|
741
805
|
const value = this.parseExpression();
|
|
742
|
-
if(value === null) {
|
|
743
|
-
throw new Error(
|
|
806
|
+
if (value === null) {
|
|
807
|
+
throw new Error("Expected expression");
|
|
744
808
|
}
|
|
745
809
|
array.addKeyValue(new KeyValuePair(key, value));
|
|
746
810
|
this.skipWhitespaceAndComments();
|
|
747
|
-
if(this.token.isComma()) {
|
|
811
|
+
if (this.token.isComma()) {
|
|
748
812
|
this.setNextToken();
|
|
749
813
|
}
|
|
750
814
|
}
|
|
@@ -753,23 +817,23 @@ class Parser extends BaseParser {
|
|
|
753
817
|
}
|
|
754
818
|
|
|
755
819
|
private parseJSONArray(): JSONArray | null {
|
|
756
|
-
if(!this.token.isOpeningBracket()) {
|
|
820
|
+
if (!this.token.isOpeningBracket()) {
|
|
757
821
|
return null;
|
|
758
822
|
}
|
|
759
823
|
const array = new JSONArray();
|
|
760
824
|
this.setNextToken();
|
|
761
|
-
while(true) {
|
|
825
|
+
while (true) {
|
|
762
826
|
this.skipWhitespaceAndComments();
|
|
763
|
-
if(this.token.isClosingBracket()) {
|
|
827
|
+
if (this.token.isClosingBracket()) {
|
|
764
828
|
break;
|
|
765
829
|
}
|
|
766
830
|
const value = this.parseExpression();
|
|
767
|
-
if(value === null) {
|
|
768
|
-
throw new Error(
|
|
831
|
+
if (value === null) {
|
|
832
|
+
throw new Error("Expected expression");
|
|
769
833
|
}
|
|
770
834
|
array.addValue(value);
|
|
771
835
|
this.skipWhitespaceAndComments();
|
|
772
|
-
if(this.token.isComma()) {
|
|
836
|
+
if (this.token.isComma()) {
|
|
773
837
|
this.setNextToken();
|
|
774
838
|
}
|
|
775
839
|
}
|
|
@@ -779,14 +843,14 @@ class Parser extends BaseParser {
|
|
|
779
843
|
|
|
780
844
|
private expectAndSkipWhitespaceAndComments(): void {
|
|
781
845
|
const skipped = this.skipWhitespaceAndComments();
|
|
782
|
-
if(!skipped) {
|
|
783
|
-
throw new Error(
|
|
846
|
+
if (!skipped) {
|
|
847
|
+
throw new Error("Expected whitespace or comment");
|
|
784
848
|
}
|
|
785
849
|
}
|
|
786
850
|
|
|
787
851
|
private skipWhitespaceAndComments(): boolean {
|
|
788
852
|
let skipped: boolean = this.previousToken.isWhitespaceOrComment();
|
|
789
|
-
while(this.token.isWhitespace() || this.token.isComment()) {
|
|
853
|
+
while (this.token.isWhitespace() || this.token.isComment()) {
|
|
790
854
|
this.setNextToken();
|
|
791
855
|
skipped = true;
|
|
792
856
|
}
|
|
@@ -794,10 +858,10 @@ class Parser extends BaseParser {
|
|
|
794
858
|
}
|
|
795
859
|
|
|
796
860
|
private expectPreviousTokenToBeWhitespaceOrComment(): void {
|
|
797
|
-
if(!this.previousToken.isWhitespaceOrComment()) {
|
|
798
|
-
throw new Error(
|
|
861
|
+
if (!this.previousToken.isWhitespaceOrComment()) {
|
|
862
|
+
throw new Error("Expected whitespace or comment");
|
|
799
863
|
}
|
|
800
864
|
}
|
|
801
865
|
}
|
|
802
866
|
|
|
803
|
-
export default Parser;
|
|
867
|
+
export default Parser;
|